/* Compile and load the GFN driver module by using the commands, cc -Wall -O1 -c -DMODULE -D__KERNEL__ gfn4.c rmmod gfn - to remove the existing one insmod gfn.o */ #include #include #include #include #define GFN_MAJOR 60 #define BASE 0x378 #define DATAPORT (BASE) #define STATPORT (BASE+1) #define CONPORT (BASE+2) #define IRQENBIT 0x10 #define IRQBIT 0x40 #define MYIRQ 7 static DECLARE_WAIT_QUEUE_HEAD(my_wait_queue); static int waiting_for_irq; static void isr(int irq, void *dev_id, struct pt_regs *regs) { if(irq != MYIRQ) { printk("Stray Interrupt from IRQ %d\n",irq); return; } outb(0, CONPORT); // Disable further interrupts from Parallel port if(!waiting_for_irq) { printk("unexpected Interrupt from %d\n",irq); return; } waiting_for_irq = 0; wake_up_interruptible(&my_wait_queue); printk("ISR: Done Interrupt from IRQ %d\n",irq); } static ssize_t gfn_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { u8 data; if (copy_from_user(&data, buf, sizeof(data))) return -EFAULT; printk("Data from user %x hex\n", data); outb(data, DATAPORT); return 0; } static ssize_t gfn_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { u8 data; printk("READ: Process going to Sleep ...\n"); waiting_for_irq = 1; //enable_irq(MYIRQ); outb(IRQENBIT, CONPORT); // Enable Interrupt from Parallel port interruptible_sleep_on(&my_wait_queue); printk("Woken up. data = %x hex\n", data); // for debugging only data = inb(STATPORT); // read the printer status port // transfer data from kernel address space to user address space if(copy_to_user( buffer, &data, sizeof(u8))) return -EFAULT; return 0; } static int gfn_open(struct inode * inode, struct file * file) { printk("GFN open: Usage = %d\n", MOD_IN_USE); MOD_INC_USE_COUNT; return 0; } static int gfn_release(struct inode * inode, struct file * file) { MOD_DEC_USE_COUNT; printk("GFN release: Usage = %d\n", MOD_IN_USE); return 0; } struct file_operations gfn_fops = { owner: THIS_MODULE, open: gfn_open, release: gfn_release, write: gfn_write, read: gfn_read }; int init_module(void) { if (register_chrdev(GFN_MAJOR,"gfn",&gfn_fops)) { printk("gfn: Failed to get major %d\n", GFN_MAJOR); return -EIO; } if(request_irq(MYIRQ, isr, SA_INTERRUPT, "gfn", NULL) ) { printk("mkscc: Failed to get IRQ %d\n",MYIRQ); return -EIO; } outb(0, CONPORT); // Disable parallel port Interrupt outb(0, DATAPORT); printk("Registered device gfn: major %d\n",GFN_MAJOR); return 0; } void cleanup_module(void) { free_irq(MYIRQ, NULL); printk("Freed resources: MOD_IN_USE = %d\n", MOD_IN_USE); unregister_chrdev(GFN_MAJOR,"gfn"); printk("Unregistered device gfn: major %d\n",GFN_MAJOR); }