short.c
上传用户:wudi5211
上传日期:2010-01-21
资源大小:607k
文件大小:20k
源码类别:

嵌入式Linux

开发平台:

C/C++

  1. /*
  2.  * short.c -- Simple Hardware Operations and Raw Tests
  3.  * short.c -- also a brief example of interrupt handling ("short int")
  4.  *
  5.  * $Id: short.c,v 1.20 2001/03/16 21:04:49 rubini Exp $
  6.  * 
  7.  *********/
  8. #ifndef __KERNEL__
  9. #  define __KERNEL__
  10. #endif
  11. #ifndef MODULE
  12. #  define MODULE
  13. #endif
  14. #include <linux/config.h>
  15. #include <linux/module.h>
  16. #include <linux/sched.h>
  17. #include <linux/kernel.h> /* printk() */
  18. #include <linux/fs.h>     /* everything... */
  19. #include <linux/errno.h>  /* error codes */
  20. #include <linux/delay.h>  /* udelay */
  21. #include <linux/malloc.h>
  22. #include <linux/mm.h>
  23. #include <linux/ioport.h>
  24. #include <linux/interrupt.h>
  25. #include <linux/tqueue.h>
  26. #include <asm/io.h>
  27. #include "sysdep.h"
  28. #define SHORT_NR_PORTS 8 /* use 8 ports by default */
  29. /*
  30.  * all of the parameters have no "short_" prefix, to save typing when
  31.  * specifying them at load time
  32.  */
  33. static int major = 0; /* dynamic by default */
  34. MODULE_PARM(major, "i");
  35. static int use_mem = 0; /* default is I/O-mapped */
  36. MODULE_PARM(use_mem, "i");
  37. /* default is the first printer port on PC's. "short_base" is there too
  38.    because it's what we want to use in the code */
  39. static unsigned long base = 0x378;
  40. unsigned long short_base = 0;
  41. MODULE_PARM(base, "l");
  42. /* Since short_base is vremapped in case use_mem==1, remember the phys addr. */
  43. unsigned long short_phys;
  44. /* The interrupt line is undefined by default. "short_irq" is as above */
  45. static int irq = -1;
  46. volatile int short_irq = -1;
  47. MODULE_PARM(irq, "i");
  48. static int probe = 0; /* select at load time how to probe irq line */
  49. MODULE_PARM(probe, "i");
  50. static int bh = 0; /* select at load time whether a bottom-half is used */
  51. MODULE_PARM(bh, "i");
  52. static int tasklet = 0; /* select whether a tasklet is used */
  53. MODULE_PARM(tasklet, "i");
  54. static int share = 0; /* select at load time whether install a shared irq */
  55. MODULE_PARM(share, "i");
  56. MODULE_AUTHOR ("Alessandro Rubini");
  57. unsigned long short_buffer = 0;
  58. unsigned long volatile short_head;
  59. volatile unsigned long short_tail;
  60. DECLARE_WAIT_QUEUE_HEAD(short_queue);
  61. /* Set up our tasklet if we're doing that. */
  62. #ifdef HAVE_TASKLETS
  63. void short_do_tasklet (unsigned long);
  64. DECLARE_TASKLET (short_tasklet, short_do_tasklet, 0);
  65. #endif
  66. /*
  67.  * Atomicly increment an index into short_buffer
  68.  */
  69. static inline void short_incr_bp(volatile unsigned long *index, int delta)
  70. {
  71.     unsigned long new = *index + delta;
  72.     barrier ();  /* Don't optimize these two together */
  73.     *index = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new;
  74. }
  75. /*
  76.  * The devices with low minor numbers write/read burst of data to/from
  77.  * specific I/O ports (by default the parallel ones).
  78.  * 
  79.  * The device with 128 as minor number returns ascii strings telling
  80.  * when interrupts have been received. Writing to the device toggles
  81.  * 00/FF on the parallel data lines. If there is a loopback wire, this
  82.  * generates interrupts.  
  83.  */
  84. int short_open (struct inode *inode, struct file *filp)
  85. {
  86.     extern struct file_operations short_i_fops;
  87.     MOD_INC_USE_COUNT;
  88.     if (MINOR(inode->i_rdev) & 0x80) {
  89.         filp->f_op = &short_i_fops; /* the interrupt-driven node */
  90.     }
  91.     return 0;
  92. }
  93. #ifdef LINUX_20
  94. void short_release (struct inode *inode, struct file *filp)
  95. {
  96.     MOD_DEC_USE_COUNT;
  97. }
  98. #else
  99. int short_release (struct inode *inode, struct file *filp)
  100. {
  101.     MOD_DEC_USE_COUNT;
  102.     return 0;
  103. }
  104. #endif
  105. /* first, the port-oriented device */
  106. enum short_modes {SHORT_DEFAULT=0, SHORT_PAUSE, SHORT_STRING, SHORT_MEMORY};
  107. ssize_t do_short_read (struct inode *inode, struct file *filp, char *buf,
  108.                 size_t count, loff_t *f_pos)
  109. {
  110.     int retval = count;
  111.     unsigned long address = short_base + (MINOR(inode->i_rdev)&0x0f);
  112.     int mode = (MINOR(inode->i_rdev)&0x70) >> 4;
  113.     unsigned char *kbuf=kmalloc(count, GFP_KERNEL), *ptr;
  114.     
  115.     if (!kbuf) return -ENOMEM;
  116.     ptr=kbuf;
  117.     if (use_mem)
  118. mode = SHORT_MEMORY;
  119.     switch(mode) {
  120.       case SHORT_STRING:
  121.         insb(address, ptr, count);
  122. rmb();
  123.         break;
  124.       case SHORT_DEFAULT:
  125.         while (count--) {
  126.             *(ptr++) = inb(address);
  127.     rmb();
  128. }
  129.         break;
  130.       case SHORT_MEMORY:
  131.         while (count--) {
  132.             *(ptr++) = readb(address);
  133.     rmb();
  134. }
  135.         break;
  136.       case SHORT_PAUSE:
  137.         while (count--) {
  138.             *(ptr++) = inb_p(address);
  139.     rmb();
  140. }
  141.         break;
  142.       default: /* no more modes defined by now */
  143.         retval = -EINVAL;
  144.         break;
  145.     }
  146.     if ( (retval > 0) && copy_to_user(buf, kbuf, retval))
  147. retval = -EFAULT;
  148.     kfree(kbuf);
  149.     return retval;
  150. }
  151. /*
  152.  * Version-specific methods for the fops structure.
  153.  */
  154. #ifdef LINUX_20
  155. int short_read(struct inode *inode, struct file *filp, char *buf, int count)
  156. {
  157.     return do_short_read(inode, filp, buf, count, &filp->f_pos);
  158. }
  159. #else
  160. ssize_t short_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
  161. {
  162.     return do_short_read(filp->f_dentry->d_inode, filp, buf, count, f_pos);
  163. }
  164. #endif
  165. ssize_t do_short_write (struct inode *inode, struct file *filp, const char *buf,
  166.                 size_t count, loff_t *f_pos)
  167. {
  168.     int retval = count;
  169.     unsigned long address = short_base + (MINOR(inode->i_rdev)&0x0f);
  170.     int mode = (MINOR(inode->i_rdev)&0x70) >> 4;
  171.     unsigned char *kbuf=kmalloc(count, GFP_KERNEL), *ptr;
  172.     if (!kbuf) return -ENOMEM;
  173.     if (copy_from_user(kbuf, buf, count))
  174. return -EFAULT;
  175.     ptr=kbuf;
  176.     if (use_mem)
  177. mode = SHORT_MEMORY;
  178.     switch(mode) {
  179.       case SHORT_PAUSE:
  180.         while (count--) {
  181.             outb_p(*(ptr++), address);
  182.     wmb();
  183. }
  184.         break;
  185.       case SHORT_STRING:
  186.         outsb(address, ptr, count);
  187. wmb();
  188.         break;
  189.       case SHORT_DEFAULT:
  190.         while (count--) {
  191.             outb(*(ptr++), address);
  192.     wmb();
  193. }
  194.         break;
  195.       case SHORT_MEMORY:
  196.         while (count--) {
  197.             writeb(*(ptr++), address);
  198.     wmb();
  199. }
  200.         break;
  201.       default: /* no more modes defined by now */
  202.         retval = -EINVAL;
  203.         break;
  204.     }
  205.     kfree(kbuf);
  206.     return retval;
  207. }
  208. #ifdef LINUX_20
  209. int short_write(struct inode *inode, struct file *filp, const char *buf,
  210.                 int count)
  211. {
  212.     return do_short_write(inode, filp, buf, count, &filp->f_pos);
  213. }
  214. #else
  215. ssize_t short_write(struct file *filp, const char *buf, size_t count,
  216.                 loff_t *f_pos)
  217. {
  218.     return do_short_write(filp->f_dentry->d_inode, filp, buf, count, f_pos);
  219. }
  220. #endif
  221. #ifdef __USE_OLD_SELECT__
  222. int short_poll (struct inode *inode, struct file *filp,
  223.                   int mode, select_table *table)
  224. {
  225.     return mode==SEL_EX ? 0 : 1; /* readable, writable, not-exceptionable */
  226. }
  227. #define poll select
  228. #else /* Use poll */
  229. unsigned int short_poll(struct file *filp, poll_table *wait)
  230. {
  231.     return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
  232. }
  233. #endif /* __USE_OLD_SELECT__ */
  234. struct file_operations short_fops = {
  235.     read: short_read,
  236.     write: short_write,
  237.     poll: short_poll,
  238.     open: short_open,
  239.     release: short_release,
  240. };
  241. /* then,  the interrupt-related device */
  242. ssize_t short_i_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
  243. {
  244.     int count0;
  245.     while (short_head == short_tail) {
  246.         interruptible_sleep_on(&short_queue);
  247.         if (signal_pending (current))  /* a signal arrived */
  248.           return -ERESTARTSYS; /* tell the fs layer to handle it */
  249.         /* else, loop */
  250.     }
  251.     /* count0 is the number of readable data bytes */
  252.     count0 = short_head - short_tail;
  253.     if (count0 < 0) /* wrapped */
  254.         count0 = short_buffer + PAGE_SIZE - short_tail;
  255.     if (count0 < count) count = count0;
  256.     if (copy_to_user(buf, (char *)short_tail, count))
  257. return -EFAULT;
  258.     short_incr_bp (&short_tail, count);
  259.     return count;
  260. }
  261. ssize_t short_i_write (struct file *filp, const char *buf, size_t count,
  262.                 loff_t *f_pos)
  263. {
  264.     int written = 0, odd = *f_pos & 1;
  265.     unsigned long address = short_base; /* output to the parallel data latch */
  266.     if (use_mem) {
  267. while (written < count)
  268.     writeb(0xff * ((++written + odd) & 1), address);
  269.     } else {
  270. while (written < count)
  271.     outb(0xff * ((++written + odd) & 1), address);
  272.     }
  273.     *f_pos += count;
  274.     return written;
  275. }
  276. /*
  277.  * 2.0 wrappers.
  278.  */
  279. #ifdef LINUX_20
  280. int short_i_read_20 (struct inode *inode, struct file *filp, char *buf,
  281.                 int count)
  282. {
  283.     return short_i_read (filp, buf, count, &filp->f_pos);
  284. }
  285. int short_i_write_20 (struct inode *inode, struct file *filp, const char *buf,
  286.                 int count)
  287. {
  288.     return short_i_write (filp, buf, count, &filp->f_pos);
  289. }
  290. #define short_i_read  short_i_read_20
  291. #define short_i_write short_i_write_20
  292. #endif /* LINUX_20 */
  293. struct file_operations short_i_fops = {
  294.     read: short_i_read,
  295.     write: short_i_write,
  296.     open: short_open,
  297.     release: short_release,
  298. };
  299. void short_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  300. {
  301.     struct timeval tv;
  302.     int written;
  303.     do_gettimeofday(&tv);
  304.     /* Write a 16 byte record. Assume PAGE_SIZE is a multiple of 16 */
  305.     written = sprintf((char *)short_head,"%08u.%06un",
  306.                           (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
  307.     short_incr_bp(&short_head, written);
  308.     wake_up_interruptible(&short_queue); /* awake any reading process */
  309. }
  310. /*
  311.  * The following two functions are equivalent to the previous one,
  312.  * but split in top and bottom half. First, a few needed variables
  313.  */
  314. #define NR_TIMEVAL 512 /* length of the array of time values */
  315. struct timeval tv_data[NR_TIMEVAL]; /* too lazy to allocate it */
  316. volatile struct timeval *tv_head=tv_data;
  317. volatile struct timeval *tv_tail=tv_data;
  318. static struct tq_struct short_task; /* 0 by now, filled by short_init code */
  319. int short_bh_count = 0;
  320. /*
  321.  * Increment a circular buffer pointer in a way that nobody sees
  322.  * an intermediate value.
  323.  */
  324. static inline void short_incr_tv(volatile struct timeval **tvp)
  325. {
  326.     if (*tvp == (tv_data + NR_TIMEVAL - 1))
  327.         *tvp = tv_data;  /* Wrap */
  328.     else
  329.         (*tvp)++;
  330. }
  331. void short_do_tasklet (unsigned long unused)
  332. {
  333.     int savecount = short_bh_count, written;
  334.     short_bh_count = 0; /* we have already been removed from the queue */
  335.     /*
  336.      * The bottom half reads the tv array, filled by the top half,
  337.      * and prints it to the circular text buffer, which is then consumed
  338.      * by reading processes
  339.      */
  340.     /* First write the number of interrupts that occurred before this bh */
  341.     written = sprintf((char *)short_head,"bh after %6in",savecount);
  342.     short_incr_bp(&short_head, written);
  343.     /*
  344.      * Then, write the time values. Write exactly 16 bytes at a time,
  345.      * so it aligns with PAGE_SIZE
  346.      */
  347.     do {
  348.         written = sprintf((char *)short_head,"%08u.%06un",
  349. (int)(tv_tail->tv_sec % 100000000),
  350. (int)(tv_tail->tv_usec));
  351. short_incr_bp(&short_head, written);
  352. short_incr_tv(&tv_tail);
  353.     } while (tv_tail != tv_head);
  354.     wake_up_interruptible(&short_queue); /* awake any reading process */
  355. }
  356. void short_bh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  357. {
  358.     /* cast to stop 'volatile' warning */
  359.     do_gettimeofday((struct timeval *) tv_head);
  360.     short_incr_tv(&tv_head);
  361.     /* Queue the bh. Don't care for multiple enqueueing */
  362.     queue_task(&short_task, &tq_immediate);
  363.     mark_bh(IMMEDIATE_BH);
  364.     short_bh_count++; /* record that an interrupt arrived */
  365. }
  366. #ifdef HAVE_TASKLETS
  367. /*
  368.  * Tasklet top half
  369.  */
  370. void short_tl_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  371. {
  372.     do_gettimeofday((struct timeval *) tv_head); /* cast to stop 'volatile' warning */
  373.     short_incr_tv(&tv_head);
  374.     tasklet_schedule(&short_tasklet);
  375.     short_bh_count++; /* record that an interrupt arrived */
  376. }
  377. #endif /* HAVE_TASKLETS */
  378. void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  379. {
  380.     int value, written;
  381.     struct timeval tv;
  382.     /* If it wasn't short, return immediately */
  383.     value = inb(short_base);
  384.     if (!(value & 0x80)) return;
  385.         
  386.     /* clear the interrupting bit */
  387.     outb(value & 0x7F, short_base);
  388.     /* the rest is unchanged */
  389.     do_gettimeofday(&tv);
  390.     written = sprintf((char *)short_head,"%08u.%06un",
  391.                           (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
  392.     short_incr_bp(&short_head, written);
  393.     wake_up_interruptible(&short_queue); /* awake any reading process */
  394. }
  395. void short_kernelprobe(void)
  396. {
  397.     int count = 0;
  398.     do {
  399.         unsigned long mask;
  400.         mask = probe_irq_on();
  401.         outb_p(0x10,short_base+2); /* enable reporting */
  402.         outb_p(0x00,short_base);   /* clear the bit */
  403.         outb_p(0xFF,short_base);   /* set the bit: interrupt! */
  404.         outb_p(0x00,short_base+2); /* disable reporting */
  405.         udelay(5);  /* give it some time */
  406.         short_irq = probe_irq_off(mask);
  407.         if (short_irq == 0) { /* none of them? */
  408.             printk(KERN_INFO "short: no irq reported by proben");
  409.             short_irq = -1;
  410.         }
  411.         /*
  412.          * if more than one line has been activated, the result is
  413.          * negative. We should service the interrupt (no need for lpt port)
  414.          * and loop over again. Loop at most five times, then give up
  415.          */
  416.     } while (short_irq < 0 && count++ < 5);
  417.     if (short_irq < 0)
  418.         printk("short: probe failed %i times, giving upn", count);
  419. }
  420. void short_probing(int irq, void *dev_id, struct pt_regs *regs)
  421. {
  422.     if (short_irq == 0) short_irq = irq;    /* found */
  423.     if (short_irq != irq) short_irq = -irq; /* ambiguous */
  424. }
  425. void short_selfprobe(void)
  426. {
  427.     int trials[] = {3, 5, 7, 9, 0};
  428.     int tried[]  = {0, 0, 0, 0, 0};
  429.     int i, count = 0;
  430.      /*
  431.       * install the probing handler for all possible lines. Remember
  432.       * the result (0 for success, or -EBUSY) in order to only free
  433.       * what has been acquired
  434.       */
  435.     for (i=0; trials[i]; i++)
  436.         tried[i] = request_irq(trials[i], short_probing,
  437.                                SA_INTERRUPT, "short probe", NULL);
  438.     do {
  439.         short_irq = 0; /* none got, yet */
  440.         outb_p(0x10,short_base+2); /* enable */
  441.         outb_p(0x00,short_base);
  442.         outb_p(0xFF,short_base); /* toggle the bit */
  443.         outb_p(0x00,short_base+2); /* disable */
  444.         udelay(5);  /* give it some time */
  445.         /* the value has been set by the handler */
  446.         if (short_irq == 0) { /* none of them? */
  447.             printk(KERN_INFO "short: no irq reported by proben");
  448.         }
  449.         /*
  450.          * If more than one line has been activated, the result is
  451.          * negative. We should service the interrupt (but the lpt port
  452.          * doesn't need it) and loop over again. Do it at most 5 times
  453.          */
  454.     } while (short_irq <=0 && count++ < 5);
  455.     /* end of loop, uninstall the handler */
  456.     for (i=0; trials[i]; i++)
  457.         if (tried[i] == 0)
  458.             free_irq(trials[i], NULL);
  459.     if (short_irq < 0)
  460.         printk("short: probe failed %i times, giving upn", count);
  461. }
  462. /* Two wrappers, to use non-page-aligned ioremap() on 2.0 */
  463. /* Remap a not (necessarily) aligned port region */
  464. void *short_remap(unsigned long phys_addr)
  465. {
  466.     /* The code comes mainly from arch/any/mm/ioremap.c */
  467.     unsigned long offset, last_addr, size;
  468.     last_addr = phys_addr + SHORT_NR_PORTS - 1;
  469.     offset = phys_addr & ~PAGE_MASK;
  470.     
  471.     /* Adjust the begin and end to remap a full page */
  472.     phys_addr &= PAGE_MASK;
  473.     size = PAGE_ALIGN(last_addr) - phys_addr;
  474.     return ioremap(phys_addr, size) + offset;
  475. }
  476. /* Unmap a region obtained with short_remap */
  477. void short_unmap(void *virt_add)
  478. {
  479.     iounmap((void *)((unsigned long)virt_add & PAGE_MASK));
  480. }
  481. /* Finally, init and cleanup */
  482. int short_init(void)
  483. {
  484.     int result;
  485.     /*
  486.      * first, sort out the base/short_base ambiguity: we'd better
  487.      * use short_base in the code, for clarity, but allow setting
  488.      * just "base" at load time. Same for "irq".
  489.      */
  490.     short_base = base;
  491.     short_irq = irq;
  492.     /* Set up owner pointers.*/
  493.     SET_MODULE_OWNER(&short_fops);
  494.     SET_MODULE_OWNER(&short_i_fops);
  495.     /* Get our needed resources. */
  496.     if (!use_mem) {
  497. result = check_region(short_base, SHORT_NR_PORTS);
  498. if (result) {
  499.     printk(KERN_INFO "short: can't get I/O port address 0x%lxn",
  500.    short_base);
  501.     return result;
  502. }
  503. request_region(short_base, SHORT_NR_PORTS, "short");
  504.     } else {
  505. result = check_mem_region(short_base, SHORT_NR_PORTS);
  506. if (result) {
  507.     printk(KERN_INFO "short: can't get I/O mem address 0x%lxn",
  508.    short_base);
  509.     return result;
  510. }
  511. request_mem_region(short_base, SHORT_NR_PORTS, "short");
  512. /* also, ioremap it */
  513. short_phys = short_base;
  514. short_base = (unsigned long)short_remap(short_base);
  515. /* Hmm... we should check the return value */
  516.     }
  517.     result = register_chrdev(major, "short", &short_fops);
  518.     if (result < 0) {
  519.         printk(KERN_INFO "short: can't get major numbern");
  520.         release_region(short_base,SHORT_NR_PORTS);
  521.         return result;
  522.     }
  523.     if (major == 0) major = result; /* dynamic */
  524.     short_buffer = __get_free_pages(GFP_KERNEL,0); /* never fails */
  525.     short_head = short_tail = short_buffer;
  526.     /*
  527.      * Fill the short_task structure, used for the bottom half handler.
  528.      * The cast is there to prevent warnings about the type of the
  529.      * (unused) argument.
  530.      */
  531.     short_task.routine = (void (*)(void *)) short_do_tasklet;
  532.     short_task.data = NULL; /* unused */
  533.     /*
  534.      * Now we deal with the interrupt: either kernel-based
  535.      * autodetection, DIY detection or default number
  536.      */
  537.     if (short_irq < 0 && probe == 1)
  538.         short_kernelprobe();
  539.     if (short_irq < 0 && probe == 2)
  540.         short_selfprobe();
  541.     if (short_irq < 0) /* not yet specified: force the default on */
  542.         switch(short_base) {
  543.           case 0x378: short_irq = 7; break;
  544.           case 0x278: short_irq = 2; break;
  545.           case 0x3bc: short_irq = 5; break;
  546.         }
  547.     /*
  548.      * If shared has been specified, installed the shared handler
  549.      * instead of the normal one. Do it first, before a -EBUSY will
  550.      * force short_irq to -1.
  551.      */
  552.     if (short_irq >= 0 && share > 0) {
  553.         result = request_irq(short_irq, short_sh_interrupt,
  554.                              SA_SHIRQ | SA_INTERRUPT,"short",
  555.                              short_sh_interrupt);
  556.         if (result) {
  557.             printk(KERN_INFO "short: can't get assigned irq %in", short_irq);
  558.             short_irq = -1;
  559.         }
  560.         else { /* actually enable it -- assume this *is* a parallel port */
  561.             outb(0x10,short_base+2);
  562.         }
  563.         return 0; /* the rest of the function only installs handlers */
  564.     }
  565.     if (short_irq >= 0) {
  566.         result = request_irq(short_irq, short_interrupt,
  567.                              SA_INTERRUPT, "short", NULL);
  568.         if (result) {
  569.             printk(KERN_INFO "short: can't get assigned irq %in",
  570.                    short_irq);
  571.             short_irq = -1;
  572.         }
  573.         else { /* actually enable it -- assume this *is* a parallel port */
  574.             outb(0x10,short_base+2);
  575.         }
  576.     }
  577.     /*
  578.      * Ok, now change the interrupt handler if using top/bottom halves
  579.      * has been requested
  580.      */
  581.     if (short_irq >= 0 && (bh + tasklet) > 0) {
  582.         free_irq(short_irq,NULL);
  583.         result = request_irq(short_irq,
  584. #ifdef HAVE_TASKLETS
  585.                         tasklet ? short_tl_interrupt :
  586. #endif
  587.                         short_bh_interrupt,
  588.                         SA_INTERRUPT,"short-bh", NULL);
  589.         if (result) {
  590.             printk(KERN_INFO "short-bh: can't get assigned irq %in",
  591.                    short_irq);
  592.             short_irq = -1;
  593.         }
  594.     }
  595.     return 0;
  596. }
  597. void short_cleanup(void)
  598. {
  599.     if (short_irq >= 0) {
  600.         outb(0x0, short_base + 2);   /* disable the interrupt */
  601.         if (!share) free_irq(short_irq, NULL);
  602.         else free_irq(short_irq, short_sh_interrupt);
  603.     }
  604.         
  605.     unregister_chrdev(major, "short");
  606.     if (use_mem) {
  607. short_unmap((void *)short_base);
  608. release_mem_region(short_base, SHORT_NR_PORTS);
  609.     } else {
  610. release_region(short_base,SHORT_NR_PORTS);
  611.     }
  612.     if (short_buffer) free_page(short_buffer);
  613. }
  614. module_init(short_init);
  615. module_exit(short_cleanup);