/* this is a character driver for the PCI arb card*/ /* Programming note: Most programs are only called at one point, the main() function. This one is a kernel module, which has every function called externally, starting with pci_arb_init() pci_arb_exit() which are called when the module is loaded and unloaded. After the module is loaded, an asocciated /dev/ file is created (in the "load" script) which application code will use to access the device. Loading the module registers these functions with the kernel so they will be called when application code manipulates the device file. arb_llseek() arb_read() arb_write() arb_ioctl() arb_open() arb_release() These functions are also registered with the kernel when the module is loaded. They are called by the pci controller when it needs to initialize the hardware. They also set up (and remove) the mapping from hardware address space to programming memory space probe() remove() which are called from the pci controller and are used to initialize the device, */ #include #include #include #include #include #include #include MODULE_LICENSE("GPL"); /* This driver will only work on a system with kernel 2.6 or greater I used Fedora Core V.5 To compile this driver, the kernel source tree must be installed and the kernel recompiled with it. http://download.fedora.redhat.com/pub/fedora/linux/core/5/source/SRPMS/ Create the following symbolic link to wherever the source tree is installed: in /lib/modules/2.6.15-1.2054_FC5/ build -> ../../../home/me/rpmbuild/BUILD/kernel-2.6.15/linux-2.6.15.i686/ */ /* useful IO fucnctions: note that the address is treated as u32* for the purposes of pointer arithmetic, so ioread32(base_addr + 0x1) will read from pci address xxxxx004, which is interpreted by the FPGA as address 0x1 void iowrite32(u32 data,void *addr); u32 ioread32(void *addr); void ioread32_rep(void *addr, void *buf, unsigned long count); iowrite32_rep(void *addr, const void *buf, unsigned long count); void memset_io(void *addr, u8 value, unsigned int count); void memcpy_fromio(void *dest, void *source, unsigned int count); void memcpy_toio(void *dest, void *source, unsigned int count); See Linux Device Drivers 3rd Edition, Rubini - pub. O'Reilly for an explanation of the workings of a PCI driver. */ /*list of devices supported by this driver, list terminated by 0 */ static struct pci_device_id ids[] = { { PCI_DEVICE(0xf00d, 0x0bad), }, { 0, } }; //register driver with hot-plug controller MODULE_DEVICE_TABLE(pci, ids); //start all globals with pci_arb to help avoid //name collision with the kernel u32 pci_arb_baseaddress; u32 pci_arb_mem_length; u32* pci_arb_mem_ptr; struct resource *pci_arb_memregion; dev_t pci_arb_system_device; int pci_arb_hardware_present; struct cdev pci_arb_cdev; /*probe() is called when the system detects the addition of a PCI device or the device is present when the driver module is loaded. The current hardware can't be hot-plugged but this driver should work with a cardbus version of the hardware the function is responsable for: -checking to see that the BIOS has assigned the card an address for its io memory region. This is mainly a problem in development, as the base address is forgotten if the FPGA is reprogrammed and BIOS does not reassign the base address on a soft reboot. -requesting the use of the io memory space from the kernel -remapping the io memory space to program memory space -telling the driver that the hardware is present */ static int probe(struct pci_dev *dev, const struct pci_device_id *id) { u32 addressend; pci_enable_device(dev); pci_arb_baseaddress = pci_resource_start(dev, 0); addressend = pci_resource_end(dev, 0); pci_arb_mem_length = addressend - pci_arb_baseaddress + 1; if(pci_arb_mem_length != 0x2000 || pci_arb_baseaddress == 0){ printk("<0>PCI memory bad... base:%x length:%x\n", pci_arb_baseaddress,pci_arb_mem_length); goto mem_wrong_size; } pci_arb_memregion = request_mem_region(pci_arb_baseaddress, pci_arb_mem_length, "PCI AWG"); if(pci_arb_memregion == NULL){ printk("<0>memregion allocation failed\n"); goto memregion_alloc_failed; } pci_arb_mem_ptr = (u32*)ioremap(pci_arb_baseaddress, pci_arb_mem_length); if(pci_arb_mem_ptr == NULL){ printk("<0>PCI memory not remapped\n"); goto iomem_remap_failed; } pci_arb_hardware_present = 1; return 0; iomem_remap_failed: release_mem_region(pci_arb_baseaddress,pci_arb_mem_length); memregion_alloc_failed: mem_wrong_size: return -1; } static void remove(struct pci_dev *dev) { pci_arb_hardware_present = 0; if(pci_arb_mem_ptr != NULL) iounmap(pci_arb_mem_ptr); if(pci_arb_memregion != NULL) release_mem_region(pci_arb_baseaddress,pci_arb_mem_length); } /* The following functions are called when the device file (/dev/ARB_DEV0) is accessed by the application code note that you can't directly access user memory space from kernel space or vice-versa, so pointer dereferencing has to be replaced with the copy_to_user() and copy_from_user() functions The read/write interface is an awful hack to allow addressing with a character mode driver, and it should be replaced by proper use of llseek() */ loff_t arb_llseek(struct file *filp, loff_t a, int b){ return 0; } unsigned int read_data; ssize_t arb_read(struct file *filp, char __user *buffer, size_t length,loff_t *a){ unsigned char rbuff[2]; rbuff[0] = read_data >> 8; rbuff[1] = read_data & 0xff; if(length == 2){ if(0 == copy_to_user(buffer,rbuff,2)) return 2; } return 0; } ssize_t arb_write(struct file *filp,const char __user *buffer, size_t length, loff_t *a){ unsigned char packet[4]; u32 addr,data; if(pci_arb_hardware_present){ if(length == 4){; if(copy_from_user(packet,buffer,length)!= 0) return -2; addr = 0x7ff & ((packet[0] << 8) | packet[1]); data = (packet[2] << 8) | packet[3]; iowrite32(1,pci_arb_mem_ptr+0x0);//enable fpga ibus iowrite32(data,pci_arb_mem_ptr+addr); iowrite32(0,pci_arb_mem_ptr+0x0);//disable fpga ibus return length; } if(length == 2){ if(copy_from_user(packet,buffer,length)!= 0) return -2; addr = 0x7ff & ((packet[0] << 8) | packet[1]); iowrite32(1,pci_arb_mem_ptr+0x0);//enable fpga ibus read_data = ioread32(pci_arb_mem_ptr+addr); iowrite32(0,pci_arb_mem_ptr+0x0);//disable fpga ibus return length; } return -1; } else { printk("<0> Attempted write to non-existant pci_arb hardware"); return 0; } } int arb_ioctl(struct inode *inode,struct file *filp, unsigned int a,unsigned long b){ return 0; } int arb_open(struct inode *inodep, struct file *filp){ if(!pci_arb_hardware_present) return -1; return 0; } int arb_release(struct inode *inodep, struct file *filp){ return 0; } /* end device driver access functions */ //defines function pointers for use by pci controller static struct pci_driver pci_arb_driver = { .name = "pci_arb", .id_table = ids, .probe = probe, .remove = remove, }; //defines function pointers for use by applications struct file_operations arb_fops = { .owner = THIS_MODULE, .llseek = arb_llseek, .read = arb_read, .write = arb_write, .ioctl = arb_ioctl, .open = arb_open, .release = arb_release, }; static int __init pci_arb_init(void) { int err; pci_arb_hardware_present = 0; //assume there is no hardware //probe() will update this //register character driver number- let system assign major number err = alloc_chrdev_region(&pci_arb_system_device,0,1,"ARB_DEV"); if(err) { printk("<0>Error %d creating pci_arb_system_device ARB_DEV", err); return err; } //register driver functions cdev_init(&pci_arb_cdev,&arb_fops); pci_arb_cdev.owner = THIS_MODULE; pci_arb_cdev.ops = &arb_fops; err = cdev_add (&pci_arb_cdev, pci_arb_system_device, 1); if (err) { printk("<0>Error %d adding pci_arb_cdev", err); return err; } err = pci_register_driver(&pci_arb_driver); return err; } static void __exit pci_arb_exit(void) { pci_unregister_driver(&pci_arb_driver); cdev_del(&pci_arb_cdev); unregister_chrdev_region(pci_arb_system_device, 1); } //define the functions used on module loading and unloading module_init(pci_arb_init); module_exit(pci_arb_exit);