#include #include #include #include #include #include #include #include "vxi.h" #include "pci_io.h" #include "vxi_kernel.h" #include "openlist.h" #include "mutex.h" static int VXIMajorNumber = 0; static int irq; static struct pci_dev *device = NULL; static int num_opened; // The number of times the VXI driver is current open static mutex_t *driverFileMutex = NULL; int vxi_open(struct inode *i, struct file *fp); int vxi_release(struct inode *i, struct file *fp); int vxi_ioctl(struct inode *i, struct file *fp, unsigned int command, unsigned long arg); int vxi_mmap(struct file *fp, struct vm_area_struct *vma); irqreturn_t vxi_tophalf(int irq, void *dev_id, struct pt_regs *regs); struct file_operations VXIFileOps = { open: &vxi_open, release: &vxi_release, ioctl: &vxi_ioctl, mmap: &vxi_mmap }; /* * This function is called when the module loads. It registers the device * and initializes the hardware */ static int vxi_init(void) { int retval = 0; /* Give the kernel our empty symbol table */ /* EXPORT_NO_SYMBOLS is the default starting with Linux 2.6 */ /* EXPORT_NO_SYMBOLS; */ SET_MODULE_OWNER(&VXIFileOps); /* Register a character device for the module */ /* VXI_MAJOR is defined in the makefile and is by default 0 */ if((VXIMajorNumber = register_chrdev(VXI_MAJOR, "vxi", &VXIFileOps)) < 0) { return VXIMajorNumber; } /* First check to see that the hardware is present */ device = PMdetect(); if(!device) { retval = -ENODEV; goto failed_init; } if(init_module_internal() < 0) { retval = -1; goto failed_init; } if(initOpenedList() < 0) { retval = -1; goto failed_init; } driverFileMutex = mutex_construct(); vxi_printk(KERN_INFO "VXI: initialized\n"); /* Finally, return success to the kernel. */ return retval; failed_init: /* unregister the character device we created */ unregister_chrdev(VXIMajorNumber, "vxi"); vxi_printk(KERN_ERR "VXI: failed initialization\n"); return retval; } /* * This function is called when the module is unloaded. It needs to clean * up after the module and prepare it for unloading. */ static void vxi_exit(void) { /* NOTE: flush_scheduled_work is 2.6-only, and waits for everything * in the default workqueue to finish, guaranteeing that we've * serviced everything before closing up */ flush_scheduled_work(); mutex_destruct(driverFileMutex); destroyOpenedList(); unregister_chrdev(VXIMajorNumber, "vxi"); cleanup_module_internal(); vxi_printk(KERN_INFO "VXI: unloaded\n"); } module_init(vxi_init); module_exit(vxi_exit); NIVXICC int vxi_install_interrupt_handler(void *deviceCookie) { pci_enable_device(device); irq = device->irq; if(request_irq(irq, &vxi_tophalf, SA_INTERRUPT|SA_SHIRQ, "vxi", deviceCookie)) { return -1; } return 0; } NIVXICC void vxi_uninstall_interrupt_handler(void *deviceCookie) { free_irq(irq, deviceCookie); } NIVXICC struct pci_dev * vxi_get_device(void) { return device; } int vxi_open(struct inode *i, struct file *fp) { mutex_acquire(driverFileMutex); vxiAddToOpenedList(get_current()->pid, VXI_UNINITED); num_opened++; mutex_release(driverFileMutex); return 0; } int vxi_release(struct inode *i, struct file *fp) { unsigned long pid = get_current()->pid; int state; if(processReleaseSetupMutex(pid)) { return 0; } mutex_acquire(driverFileMutex); state = vxiGetFromOpenedList(pid); if(state == VXI_INITED) { processEndedWithoutClose(pid); } vxiRemoveFromOpenedList(pid); num_opened--; if(num_opened == 0) { resetDriver(); } mutex_release(driverFileMutex); return 0; } /* * This function is called when a process calls ioctl on our device file. * This is where most of the kernel stuff gets done. */ int vxi_ioctl(struct inode *i, struct file *fp, unsigned int command, unsigned long arg) { return vxi_ioctl_internal(command, arg); } void vxi_vma_open(struct vm_area_struct *vma) { /* deprecated in 2.6 */ /* MOD_INC_USE_COUNT; */ if(try_module_get(THIS_MODULE) == 0) { vxi_printk(KERN_ERR "[vxi_vma_open]: couldn't increment reference count\n"); } } void vxi_vma_close(struct vm_area_struct *vma) { /* deprecated in 2.6 */ /* MOD_DEC_USE_COUNT; */ module_put(THIS_MODULE); } struct page * vxi_vma_nopage_shm(struct vm_area_struct *vma, unsigned long address, int *type) { unsigned long offset = address - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT); struct page *pg = virt_to_page(offset); get_page(pg); if(type) { *type = VM_FAULT_MINOR; } return pg; } struct page * vxi_vma_nopage_basic(struct vm_area_struct *vma, unsigned long address, int *type) { return NOPAGE_SIGBUS; } struct vm_operations_struct vxi_vm_ops_shm = { open: &vxi_vma_open, close: &vxi_vma_close, nopage: &vxi_vma_nopage_shm }; struct vm_operations_struct vxi_vm_ops_basic = { open: &vxi_vma_open, close: &vxi_vma_close, nopage: &vxi_vma_nopage_basic }; /* * This function is called when a process calls mmap on our device file. * This is where VXIMapAddress, etc finally get handled. */ int vxi_mmap(struct file *fp, struct vm_area_struct *vma) { unsigned long final_offset; int type; int result; if(vxi_mmap_internal(vma->vm_pgoff << PAGE_SHIFT, vma->vm_end - vma->vm_start, &type, &final_offset) != 0) { return -1; } switch(type) { case 1: vma->vm_flags |= VM_IO | VM_RESERVED; #if defined(REMAP_PFN_RANGE) result = remap_pfn_range(vma, vma->vm_start, final_offset >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot); #elif defined (REMAP_PAGE_RANGE_VMA) result = remap_page_range(vma, vma->vm_start, final_offset, vma->vm_end - vma->vm_start, vma->vm_page_prot); #elif defined(REMAP_PAGE_RANGE) result = remap_page_range( vma->vm_start, final_offset, vma->vm_end - vma->vm_start, vma->vm_page_prot); #endif if(result < 0) { return -EAGAIN; } vma->vm_ops = &vxi_vm_ops_basic; break; case 2: vma->vm_pgoff = final_offset >> PAGE_SHIFT; vma->vm_ops = &vxi_vm_ops_shm; break; default: return -EINVAL; } /* Increment the module usage count */ vxi_vma_open(vma); return 0; } NIVXICC int PCIreadReg(int size, UInt8 regnum, UInt32 *value) { int ret = -1; switch(size) { case 1: ret = _pci_read_config_8(device, regnum, (UInt8 *)value); break; case 2: ret = _pci_read_config_16(device, regnum, (UInt16*)value); break; case 4: ret = _pci_read_config_32(device, regnum, value); break; } return ret; } NIVXICC int PCIwriteReg(int size, UInt8 regnum, UInt32 value) { int ret = -1; switch(size) { case 1: ret = _pci_write_config_8(device, regnum, (UInt8)value); break; case 2: ret = _pci_write_config_16(device, regnum, (UInt16)value); break; case 4: ret = _pci_write_config_32(device, regnum, value); break; } return ret; }