Parallel port driver

      //
      // lpv.c
      //
      // Create the device with:
      // mknod /dev/lpv c 32 0
      //

      #include <linux/module.h>
      #include <linux/mm.h>
      #include <linux/errno.h>
      #include <linux/kernel.h>
      #include <linux/major.h>
      #include <linux/sched.h>
      #include <linux/malloc.h>
      #include <linux/ioport.h>
      #include <linux/fcntl.h>
      #include <linux/delay.h>
      #include <time.h>
      #include <asm/io.h>
      #include <asm/segment.h>
      #include <asm/system.h>
      #include <sys/ioctl.h>
      #include "lpv.h"

      int lpv_busy = LPV_FREE;
      int irq7 = 7;
      int irq7_flag = 0;

      struct wait_queue *lpv_wait_q;

      // Interrupt Handler
      void lpv_irq7(int irq7) {

      // Just wake up the read...
      wake_up(&lpv_wait_q);

      }

      static int lpv_open(struct inode * inode, struct file * file)
      {
      unsigned int minor = MINOR(inode->i_rdev);
      int ret_code;

      if (minor != 0)
      return -ENODEV;
      if (lpv_busy == LPV_BUSY)
      return -EBUSY;

      lpv_busy = LPV_BUSY;

      // Register irq7
      cli();
      ret_code = request_irq(irq7,(void *)lpv_irq7,
      SA_INTERRUPT,"lpv",NULL);
      if(ret_code) {
      printk("lpv: unable to use irq7.\n");
      } else {
      printk("lpv: irq7 registered.\n");
      }

      // Port 0x21 -> interrupt mask (write 0 to bit 7 to
      // enable irq7 - Do not modify the others!!)
      outb(inb(0x21)&(~0x80),0x21);

      // Tell the PC the irq has been processed
      outb(0x20,0x20);

      // Set LPV_0 to 1 (to get Vcc for the irq7)
      outb(inb(LPV)|0x01,LPV);

      sti();

      printk("lpv: open concluded.\n");

      return 0;
      }

      static int lpv_read(struct inode * inode, struct file * file,
      char * buf, int count)
      {

      if (count != 1) return -EINVAL;

      // Sleep until interrupt wakes it up
      interruptible_sleep_on(&lpv_wait_q);

      return 0;
      }

      static int lpv_write(struct inode * inode, struct file * file,
      const char * buf, int count)
      {
      char c;
      const char *temp;

      temp=buf;

      while(count>0){
      c=get_user(temp); /* Get next char */
      outb(c|0x01,LPV); /* Send it to the LPV */
      count--;
      temp++;
      }

      return temp-buf;

      }

      static int lpv_ioctl(struct inode * inode, struct file * file,
      unsigned int cmd, unsigned long arg)
      {
      switch(cmd) {
      case LPV_IRQ:
      // Toggle bit 4 of Control Port
      // 1: irq enabled, 0: disabled
      if(arg==LPV_ENABLE) {
      outb(inb(LPVC)|0x10,LPVC);
      printk("lpv: irq7 enabled.\n");
      return 0;
      }
      else if(arg==LPV_DISABLE) {
      outb(inb(LPVC)&(~0x10),LPVC);
      printk("lpv: irq7 disabled.\n");
      return 0;
      }
      else {
      return -EINVAL;
      }
      break;
      default:
      return -EINVAL;
      }
      }

      static void lpv_release(struct inode * inode, struct file * file)
      {
      cli();

      // Port 0x21 -> interrupt mask (write 1 to bit 7 to
      // disable irq7 - Do not modify the others!!)
      outb(inb(0x21)|(0x80),0x21);

      free_irq(irq7,NULL);

      sti();

      lpv_busy = LPV_FREE;

      printk("lpv: device released.\n");

      }

      static struct file_operations lpv_fops = {
      NULL, /* seek */
      lpv_read, /* read */
      lpv_write, /* write */
      NULL, /* readdir */
      NULL, /* select */
      lpv_ioctl, /* control */
      NULL, /* mmap */
      lpv_open, /* open */
      lpv_release /* release */
      };

      int init_module( void)
      {
      // Debug
      printk("lpv: init_module called.\n");

      // Initialize the chip
      outb(0, LPV);

      // Register the device driver with the system
      if (register_chrdev(LPV_MAJOR, "lpv", &lpv_fops)) {
      printk(KERN_ERR "lpv: register_chrdev failed.\n");
      return -EIO;
      }
      else
      // Debug
      printk("lpv: driver registered.\n");

      return 0;
      }

      void cleanup_module( void)
      {
      if (lpv_busy)
      // Debug
      printk("lpv: device busy, remove delayed.\n");
      if (unregister_chrdev(LPV_MAJOR, "lpv") != 0) {
      // Debug
      printk("lpv: cleanup_module failed.\n");
      }
      else
      // Debug
      printk("lpv: cleanup_module succeeded.\n");

      }