#include #if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS) # define MODVERSIONS #endif #include /* printk() */ #include #include #include /* everything... */ #include /* error codes */ #include /* size_t */ #include #include #include /* O_ACCMODE */ #include #include #include #include #include #include #include #include #include #include #include #define __KERNEL_SYSCALLS__ #include asmlinkage long (*s_ioperm)(unsigned long from, unsigned long num, int turn_on); #include #include #include /* cli(), *_flags */ #include /* memcpy and such */ #include #include #include #include "kernel26compat.h" #include "svgalib_helper.h" #include "vgaversion.h" #include "i810.h" #include "interrupt.h" #include "displaystart.h" int debug=0; static int all_devices=0; int num_devices=0; static char *sdev_id="svgalib_helper"; static struct sh_pci_device *sh_pci_devs[MAX_NR_DEVICES]; static int irqs[MAX_NR_DEVICES]; #if (defined CONFIG_DEVFS_FS) && (!defined KERNEL_2_6) static devfs_handle_t devfs_handle; #endif #if defined(KERNEL_2_6) && !defined(CONFIG_DEVFS_FS) struct K_CLASS *svgalib_helper_class; #endif static int check_io_range(int port, int device) { return 1; } static struct pci_dev *get_pci_dev(int pcipos, int minor) { if(minor>=num_devices) return NULL; if(minor>0) { return sh_pci_devs[minor]->dev; } else { if(pcipos>0 && pciposdev; } return NULL; } static int get_dev(int pcipos, int minor) { if(minor>=num_devices) return 0; if(minor>0) { return minor; } else { if(pcipos>=num_devices || pcipos<1) return 0; return pcipos; } } static volatile int vsync=0; static wait_queue_head_t vsync_wait; static irqreturn_t vsync_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct sh_pci_device *dev = (struct sh_pci_device *)dev_id; if((char *)dev==sdev_id) { if(!vga_test_vsync(dev)) { return IRQ_NONE; } vga_ack_vsync(dev); vsync=0; wake_up_interruptible(&vsync_wait); } else { if(!dev->test_vsync(dev)) { return IRQ_NONE; } dev->ack_vsync(dev); dev->vsync=0; wake_up_interruptible(&dev->vsync_wait); #ifndef NO_TASK wake_up_interruptible(&dev->startad_wait); #endif } return IRQ_HANDLED; } #ifndef NO_TASK static void task_startad(void *data) { struct sh_pci_device *dev = (struct sh_pci_device *)data; int i; i=dev->dev->irq; dev->enable_vsync(dev); interruptible_sleep_on(&dev->vsync_wait); dev->ack_vsync(dev); set_displaystart(dev); dev->startad=-1; } #endif #define GET_IOSTR \ get_user(iostr.port, &user_iostr->port); \ get_user(iostr.length, &user_iostr->length); \ get_user(iostr.string, &user_iostr->string); #define GET_IOV \ get_user(iov.port, &user_iov->port); \ get_user(iov.val, &user_iov->val); #define PUT_IOV \ put_user(iov.val, &user_iov->val); #define GET_PCIV \ get_user(pciv.pcipos, &user_pciv->pcipos); \ get_user(pciv.address, &user_pciv->address); \ get_user(pciv.val, &user_pciv->val); #define PUT_PCIV \ put_user(pciv.val, &user_pciv->val); static int svgalib_helper_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { io_t iov, *user_iov=(io_t *)arg; pcic_t pciv, *user_pciv=(pcic_t *)arg; int minor = my_minor(inode->i_rdev); struct pci_dev *pdev; io_string_t iostr, *user_iostr=(io_string_t *)arg; int i=0, ret; u8 pb; u16 pw; u32 pl; unsigned char *outb_str; if(_IOC_TYPE(cmd)!=SVGAHELPER_MAGIC) { return -EINVAL; } ret=0; switch(_IOC_NR(cmd)) { case _IOC_NR(SVGAHELPER_REPOUTB): GET_IOSTR; if (iostr.length>4096) return -EINVAL; if ( (outb_str = kmalloc(iostr.length, GFP_KERNEL )) == NULL ) return -ENOMEM; if(copy_from_user(outb_str,iostr.string,iostr.length)) return -EPERM; if(check_io_range(iostr.port,minor)) { for(i=0; i5)) return -EINVAL; pciv.val=sh_pci_devs[i]->len[pciv.address]; PUT_PCIV; break; case _IOC_NR(SVGAHELPER_PCIOUTB): GET_PCIV; pdev = get_pci_dev(pciv.pcipos, minor); if(!pdev) return -EINVAL; pb=pciv.val; pci_write_config_byte(pdev, pciv.address, pb); break; case _IOC_NR(SVGAHELPER_PCIOUTW): GET_PCIV; pdev = get_pci_dev(pciv.pcipos, minor); if(!pdev) return -EINVAL; pw=pciv.val; pci_write_config_word(pdev, pciv.address, pw); break; case _IOC_NR(SVGAHELPER_PCIOUTL): GET_PCIV; pdev = get_pci_dev(pciv.pcipos, minor); if(!pdev) return -EINVAL; pl=pciv.val; pci_write_config_dword(pdev, pciv.address, pl); break; #ifdef __386__ case _IOC_NR(SVGAHELPER_I810GTT): i=i810_make_gtt(); copy_to_user((char *)arg, &i, sizeof(unsigned int)); break; case _IOC_NR(SVGAHELPER_I810GTTE): copy_from_user(&i, (char *)arg, sizeof(unsigned int)); if(idev->irq; if(i==0 || i==-1 || i==255) return -EINVAL; sh_pci_devs[minor]->vsync=1; sh_pci_devs[minor]->enable_vsync(sh_pci_devs[minor]); wait_event_interruptible(sh_pci_devs[minor]->vsync_wait, !sh_pci_devs[minor]->vsync); if(sh_pci_devs[minor]->vsync) sh_pci_devs[minor]->ack_vsync(sh_pci_devs[minor]); if(sh_pci_devs[minor]->vsync) return -ERESTARTSYS; } else { int i; vsync=1; i=0; while(irqs[i]!=-1) request_irq(irqs[i++], vsync_interrupt, SA_SHIRQ, "svgalib_helper", sdev_id); vga_enable_vsync((void *)sdev_id); wait_event_interruptible(vsync_wait, !vsync); i=0; if(vsync) vga_ack_vsync((void *)sdev_id); while(irqs[i]!=-1) free_irq(irqs[i++], sdev_id); if(vsync) return -ERESTARTSYS; } break; #if 0 case _IOC_NR(SVGAHELPER_IOPERM): if(minor) return -EPERM; #if 1 i=0; if(!capable(CAP_SYS_RAWIO)) { i=1; cap_raise(current->cap_effective, CAP_SYS_RAWIO); } s_ioperm=sys_call_table[__NR_ioperm]; s_ioperm(0,0x400,1); if(i) { cap_lower(current->cap_effective, CAP_SYS_RAWIO); } #else if(!ioperm) { struct thread_struct * t = ¤t->thread; struct tss_struct * tss; unsigned long *bitmap; ioperm=1; bitmap = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); if (!bitmap) return -ENOMEM; memset(bitmap, 0, IO_BITMAP_BYTES); t->io_bitmap_ptr = bitmap; tss = &per_cpu(init_tss, get_cpu()); t->io_bitmap_max = IO_BITMAP_BYTES; tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET_LAZY; put_cpu(); } #endif break; #endif #ifndef NO_TASK case _IOC_NR(SVGAHELPER_SETDISPLAYSTART): if(minor) { if(sh_pci_devs[minor]->startad==-1) schedule_task(&sh_pci_devs[minor]->tq_startad); sh_pci_devs[minor]->startad=arg; } break; #endif #ifdef SVGA_HELPER_VIRTUAL case _IOC_NR(SVGAHELPER_VIRT_TO_PHYS): return dhahelper_virt_to_phys((dhahelper_vmi_t *)arg); case _IOC_NR(SVGAHELPER_VIRT_TO_BUS): return dhahelper_virt_to_bus((dhahelper_vmi_t *)arg); case _IOC_NR(SVGAHELPER_ALLOC_PA): return dhahelper_alloc_pa((dhahelper_mem_t *)arg); case _IOC_NR(SVGAHELPER_FREE_PA): return dhahelper_free_pa((dhahelper_mem_t *)arg); case _IOC_NR(SVGAHELPER_LOCK_MEM): return dhahelper_lock_mem((dhahelper_mem_t *)arg); case _IOC_NR(SVGAHELPER_UNLOCK_MEM): return dhahelper_unlock_mem((dhahelper_mem_t *)arg); default: #endif return -EINVAL; } return ret; } static int svgalib_helper_open( struct inode *inode, struct file * filp) { int minor = my_minor(inode->i_rdev); if(minor>=num_devices) return -ENODEV; if(minor) { int i=sh_pci_devs[minor]->dev->irq; sh_pci_devs[minor]->opencount++; if(sh_pci_devs[minor]->opencount==1 && i!=0 && i!=-1 && i!=255) request_irq(i, vsync_interrupt, SA_SHIRQ, "svgalib_helper", sh_pci_devs[minor]); } #ifndef KERNEL_2_6 MOD_INC_USE_COUNT; #else try_module_get(THIS_MODULE); #endif return 0; } static int svgalib_helper_release( struct inode *inode, struct file *filp) { int minor = my_minor(inode->i_rdev); if(minor) { int i=sh_pci_devs[minor]->dev->irq; sh_pci_devs[minor]->opencount--; if(sh_pci_devs[minor]->opencount==0 && i!=0 && i!=-1 && i!=255) { sh_pci_devs[minor]->disable_vsync(sh_pci_devs[minor]); free_irq(i, sh_pci_devs[minor]); } } #ifndef KERNEL_2_6 MOD_DEC_USE_COUNT; #else module_put(THIS_MODULE); #endif return 0; } int remap_cache(struct vm_area_struct *vma, unsigned long off) { vma->vm_pgoff = off >> PAGE_SHIFT; /* This is an IO map - tell maydump to skip this VMA */ vma->vm_flags |= VM_IO; #if defined(__sparc_v9__) vma->vm_flags |= (VM_SHM | VM_LOCKED); if (my_io_remap_page_range(vma, vma->vm_start, off, vma->vm_end - vma->vm_start, vma->vm_page_prot, 0)) return -EAGAIN; #else #if defined(__mc68000__) #if defined(CONFIG_SUN3) pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE; #else if (CPU_IS_020_OR_030) pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030; if (CPU_IS_040_OR_060) { pgprot_val(vma->vm_page_prot) &= _CACHEMASK040; /* Use no-cache mode, serialized */ pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S; } #endif #elif defined(__powerpc__) pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED; #elif defined(__alpha__) /* Caching is off in the I/O space quadrant by design. */ #elif defined(__i386__) || defined(__x86_64__) if (boot_cpu_data.x86 > 3) pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; #elif defined(__arm__) || defined(__mips__) vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); #elif defined(__sh__) pgprot_val(vma->vm_page_prot) &= ~_PAGE_CACHABLE; #elif defined(__hppa__) pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; #elif defined(__ia64__) vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); #else #warning What do we have to do here?? #endif if (my_io_remap_page_range(vma, vma->vm_start, off, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; #endif /* !__sparc_v9__ */ return 0; } int check_mem(int card, unsigned long start, unsigned long len) { int j; unsigned long rstart, rlen; #ifdef __alpha__ int type; #endif if (start+len < start || len >= 0x40000000) return -3; rstart=start; rlen=len; #ifdef __alpha__ type = start>>32; switch(type) { case 2: start = start & 0xffffffff; start = start >> 5; len = len >> 5; break; case 3: start = start & 0xffffffff; break; default: return -1; } #endif if(!card) { if( (start<0x110000) && (start+len<0x110000) ) return 0; for(j=1;jmem[j]) if((start>=sh_pci_devs[card]->mem[j])&& (start+len<=sh_pci_devs[card]->mem[j]+sh_pci_devs[card]->len[j])) { return 0; } } return -2; } static int svgalib_helper_mmap(struct file *filp, struct vm_area_struct *vma) { unsigned long start=vma->vm_start; unsigned long end=vma->vm_end; unsigned long minor = my_minor(filp->f_dentry->d_inode->i_rdev); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) unsigned long ofs=vma->vm_pgoff*PAGE_SIZE; #else unsigned long ofs=vma->vm_offset; #endif if(check_mem(minor, ofs, end-start)) return -EPERM; if(remap_cache(vma, ofs)) return -EAGAIN; return 0; } struct file_operations svgalib_helper_fops = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) .owner = THIS_MODULE, #endif .ioctl = svgalib_helper_ioctl, .mmap = svgalib_helper_mmap, .open = svgalib_helper_open, .release = svgalib_helper_release, }; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) #define base_address(i) dev->resource[i].start #else #define base_address(i) (dev->base_address[i]&PCI_BASE_ADDRESS_MEM_MASK) #endif int init_module(void) { int result, i, j; struct pci_dev *dev=NULL; char name[255]; #ifdef CONFIG_DEVFS_FS # ifndef KERNEL_2_6 devfs_handle_t slave_handle; # endif #endif /* * Register your major, and accept a dynamic number */ printk(KERN_INFO "svgalib_helper: Initializing, version %s\n", versionstr); result = devfs_register_chrdev(SVGALIB_HELPER_MAJOR, "svgalib_helper", &svgalib_helper_fops); if (result < 0) { printk(KERN_WARNING "svgalib_helper: can't get major %d\n",SVGALIB_HELPER_MAJOR); return result; } if((sh_pci_devs[0]=kmalloc(sizeof(struct sh_pci_device),GFP_KERNEL))==NULL) { goto nomem_error; } memset(sh_pci_devs[0],0,sizeof(struct sh_pci_device)); num_devices=1; for(i=1;idev=dev; pci_read_config_word(dev,0,&sh_pci_devs[num_devices]->vendor); pci_read_config_word(dev,2,&sh_pci_devs[num_devices]->id); pci_read_config_byte(dev,8,&sh_pci_devs[num_devices]->revision); printk(KERN_DEBUG "svgalib_helper: device%d: vendor:%.4x id:%.4x\n",num_devices, sh_pci_devs[num_devices]->vendor,sh_pci_devs[num_devices]->id); for(i=0;i<6;i++){ unsigned int t; int len; pci_read_config_dword(dev,16+4*i,&result); if(result) { pci_write_config_dword(dev,16+4*i,0xffffffff); pci_read_config_dword(dev,16+4*i,&t); pci_write_config_dword(dev,16+4*i,result); len = ~(t&~0xf)+1; if (len){ sh_pci_devs[num_devices]->mem[i]=result&~0xf; sh_pci_devs[num_devices]->flags[i]=0x80 | (result&0xf); sh_pci_devs[num_devices]->len[i]=len; sh_pci_devs[num_devices]->mask[i]=t&~0xf; printk(KERN_DEBUG "device%d: region%d, base=%.8x len=%d type=%d\n", num_devices,i, result&(~0xf), len, result&0xf); } } } vga_init_vsync(sh_pci_devs[num_devices]); init_waitqueue_head(&sh_pci_devs[num_devices]->vsync_wait); #ifndef NO_TASK init_waitqueue_head(&sh_pci_devs[num_devices]->startad_wait); sh_pci_devs[num_devices]->tq_startad.routine=task_startad; sh_pci_devs[num_devices]->tq_startad.data=sh_pci_devs[num_devices]; sh_pci_devs[num_devices]->tq_startad.sync=0; sh_pci_devs[num_devices]->startad=-1; #endif sh_pci_devs[num_devices]->opencount=0; sprintf(name, "svga%d", num_devices); SLH_SYSFS_ADD_DEVICE(name, num_devices); num_devices++; } } j=0; for(i=1; idev->irq; if(k>0 && k<255) { for(l=0;l0) { irqs[j]=k; j++; } } } irqs[j]=-1; init_waitqueue_head(&vsync_wait); #ifndef KERNEL_2_6 EXPORT_NO_SYMBOLS; #endif return 0; /* succeed */ nomem_error: for(i=0;i"); MODULE_DESCRIPTION("Generic hardware access to vga cards");