/*----------------------------------------------------------------*/ /* Support FPGA POST LED PORT 80h WRITE Driver */ /*----------------------------------------------------------------*/ /* # insmod hwdd_sfpga_port80_drv.ko # mknod /dev/hwdd_sfpga_port80 c 80 0 # chmod +x /dev/hwdd_sfpga_port80 */ //#include #include //#include #include #include /* for wmb/rmb */ #include #include /* printk() */ #include /* everything... */ #include /* error codes */ #include /* udelay */ #include #include #include #include #include #include /* copy_from/to_user */ #define NUM_PORTS 1 /* 1 port - port 80 only! */ #define SFPGA_PORT80_MAJOR 80 #define HWDD_SFPGA_PORT80 "hwdd_sfpga_port80" static unsigned short sfpga_port80_base = 0x80; // port 80h MODULE_AUTHOR("NetApp Inc."); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Support FPGA POST LED Port80 Write Driver"); MODULE_VERSION("1.0"); int hwdd_sfpga_port80_open (struct inode *inode, struct file *filp) { return 0; } int hwdd_sfpga_port80_release (struct inode *inode, struct file *filp) { return 0; } ssize_t hwdd_sfpga_port80_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { int retval = count; unsigned char *kbuf, *ptr; kbuf = kmalloc(count, GFP_KERNEL); if (!kbuf) return -ENOMEM; if (copy_from_user(kbuf, buf, count)) { kfree(kbuf); return -EFAULT; } ptr = kbuf; // success, assign now while (count--) { outb(*(ptr++), sfpga_port80_base); /* enforce non-optimization/non-caching at compiler and CPU level and ensure the completion of the writes in the necessary order, may not require on x86 arch (controller side) since writes (unlike reads) outside the processor are not reordered.*/ wmb(); // order the write! - check if smp_wmb() is required! ??? } kfree(kbuf); return retval; } /* This port is write-only port as of now - this fn. will not be used as of writing */ /* Writes should be checked on the connected LEDs */ ssize_t hwdd_sfpga_port80_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { return 0; /* int retval = count; unsigned char *kbuf = kmalloc(count, GFP_KERNEL), *ptr; if (!kbuf) return -ENOMEM; ptr = kbuf; while (count--) { *(ptr++) = inb(sfpga_port80_base); rmb(); // // order the read! - check if required! } if ((retval > 0) && copy_to_user(buf, kbuf, retval)) retval = -EFAULT; kfree(kbuf); return retval; */ } struct file_operations hwdd_sfpga_port80_fops = { .owner = THIS_MODULE, .read = hwdd_sfpga_port80_read, .write = hwdd_sfpga_port80_write, .open = hwdd_sfpga_port80_open, .release = hwdd_sfpga_port80_release, }; /* private fn */ static int hwdd_sfpga_port80_init(void) { int result; printk(KERN_INFO "registering the device "); /* register the device */ result = register_chrdev(SFPGA_PORT80_MAJOR, HWDD_SFPGA_PORT80, &hwdd_sfpga_port80_fops); if (result < 0) { printk(KERN_INFO "hwdd_sfpga_port80: can't get major number\n"); return result; } /* request for IO port - request_region */ /* this is not a mem IO so no need to do request_mem_region and ioremap. */ printk(KERN_INFO "Requesting the IO port region for access at I/O port address 0x%X\n", sfpga_port80_base); if (! request_region(sfpga_port80_base, NUM_PORTS, HWDD_SFPGA_PORT80)) { printk(KERN_INFO "hwdd_sfpga_port80: since I/O port 0x%X is reserved by DMA1 engine at bootup, request region\n", sfpga_port80_base); printk(KERN_INFO "... may fail, but DMA1 never uses it, so the port is available for access\n"); //return -ENODEV; } return 0; } /* private fn */ static void hwdd_sfpga_port80_cleanup(void) { unregister_chrdev(SFPGA_PORT80_MAJOR, "sfpga_port80"); release_region(sfpga_port80_base, NUM_PORTS); } module_init(hwdd_sfpga_port80_init); module_exit(hwdd_sfpga_port80_cleanup);