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