I am writing a simple driver that hooks the parallel port irq and when written to, enables the port for generating interrupts (the pins 9&10 are wired together) and then toggles the high bit once for every byte in the written input. This means that if I do:
echo "0123456789" >> /dev/foobar
I expect to toggle that bit 10 times, and generate 10 interrupts. My ISR is a simple counter that bumps a number every time it is called. I am seeing that I am missing about 85% or more of my interrupts. Sometimes I get 2/10 but more often only 1/10. I have tried udelay()-ing between the port toggles but no go, and of course that is not really a solution. I am not trying to protect the counter but nothing else is accessing it. I would expect to see *some* losses here, but not that many. What is going on? Code follows. Thanks loads Eric ///////////////////////////////-----------cut here------------------------ #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/ioport.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <linux/seq_file.h> #include <linux/interrupt.h> MODULE_LICENSE("GPL"); #define NUM_DEVICES (1) #define DEVNAME "foobar" #define FOOBAR_MAJOR (248) #define FOOBAR_MINOR (0) int foobar_init(void); void foobar_exit(void); int foobar_release(struct inode *, struct file*); ssize_t foobar_read(struct file *, char __user *, size_t, loff_t *); ssize_t foobar_write(struct file *, const char __user *, size_t, loff_t *); int foobar_open(struct inode * inode, struct file * file); void set_port_and_irq(void); static irqreturn_t irqhandler(int irq, void * data); int foobar_set_irq(void); int port_address = -1; module_param(port_address, int, 0); unsigned int irq = 0; module_param(irq, uint, 0); int irqavail = -1; size_t chars_read = 0; static struct file_operations foobar_i_fops = { .owner = THIS_MODULE, .read = foobar_read, .write = foobar_write, .open = foobar_open, .release = foobar_release, }; struct resource * region = NULL; static unsigned long mask; static unsigned long probe; static dev_t g_dev; unsigned dev_id = (unsigned)&foobar_i_fops; int foobar_init(void) { int result = 0; result = register_chrdev(0, DEVNAME, &foobar_i_fops); g_dev = MKDEV(result, FOOBAR_MINOR); if(result < 0) { printk(KERN_WARNING "foobar: can't get device number, returning %d", result); return result; } printk(KERN_WARNING "foobar: Module %s got device number %d, minor is %d, result is %d\n", THIS_MODULE->name, MAJOR(g_dev), MINOR(g_dev), result); g_dev = MKDEV(MAJOR(g_dev), FOOBAR_MINOR + NUM_DEVICES); set_port_and_irq(); region = request_region(port_address, 1, DEVNAME); printk(KERN_WARNING "Port address is 0x%x, IRQ is %d, region is 0x%p\n", port_address, irq, region); if(region == 0) { printk(KERN_WARNING "Can't get region"); return -ENODEV; } mask = probe_irq_on(); return 0; } void foobar_exit(void) { int major = MAJOR(g_dev); release_region(port_address, 1); printk(KERN_WARNING "unregister_chrdev(%d) called for %s, dev # is %d\n", major, DEVNAME, g_dev); unregister_chrdev(major, DEVNAME); } int foobar_open(struct inode * inode, struct file * file) { irqavail = request_irq(irq, irqhandler, IRQF_SHARED, DEVNAME, (void*)dev_id); printk(KERN_WARNING "IRQ:%d\tPort Address:0x%x\tAvailable:%s\n", irq, port_address, irqavail == 0 ? "true" : "false"); return irqavail; } int foobar_release(struct inode * inode, struct file * filp) { printk(KERN_WARNING "foobar_release() is called, fil is 0x%p, inode is 0x%p\n", filp, inode); if(irqavail == 0) { printk(KERN_WARNING "Freeing irq %d, dev_id is 0x%x\n", irq, dev_id); free_irq(irq, (void*)dev_id); } else printk(KERN_WARNING "NOT freeing irq %d, dev_id is 0x%x\n", irq, dev_id); probe = probe_irq_off(mask); return 0; } ssize_t foobar_read(struct file * filp, char __user * buf, size_t count, loff_t * f_pos) { if(chars_read == 0) { printk(KERN_WARNING "Read is called, nothing to return."); return 0; } sprintf(buf, "Chars>%d\n", chars_read); chars_read = 0; printk(KERN_WARNING "Read is called, returning %s, length is %d\n", buf, strlen(buf)); return count; } ssize_t foobar_write(struct file * filp, const char __user * buf, size_t count, loff_t *f_pos) { size_t idx; unsigned long address = (unsigned long)port_address; printk(KERN_WARNING "Write is called, count is %d, address is 0x%lx, buffer is %s\n", count, address, buf); outb_p(0x10, address + 2); for(idx = 0; idx < strlen(buf); ++idx) { outb_p(0x00, address); udelay(100); outb_p(0xff, address); udelay(100); } outb_p(0x00, address + 2); udelay(5); printk(KERN_WARNING "probe_irq_off(0x%lx) returned 0x%lx", mask, probe); if(f_pos) * f_pos = 0; return count; } static irqreturn_t irqhandler(int irq, void * data) { chars_read++; printk(KERN_WARNING "Interrupt is fired, IRQ is %d, data is 0x%p, chars %d\n", irq, data, chars_read); return IRQ_HANDLED; } void set_port_and_irq(void) { if(irq < 0) switch(port_address) { case 0x378: irq = 7; break; case 0x278: irq = 2; break; } switch(irq) { case 2: port_address = 0x278; break; case 7: port_address = 0x378; break; } } module_init(foobar_init); module_exit(foobar_exit);