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

嵌入式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.  * untested (by now) the hardware r/w
  6.  *
  7.  * Tested with 2.0.18 (intel & alpha -- but no irq available for me)
  8.  * This module won't work on the sparc, where there's no concept of I/O space
  9.  * 
  10.  *********/
  11. #ifndef __KERNEL__
  12. #  define __KERNEL__
  13. #endif
  14. #ifndef MODULE
  15. #  define MODULE
  16. #endif
  17. #ifdef __sparc__
  18. #  error "This module can't compile on the Sparc platform"
  19. #else
  20. #include <linux/module.h>
  21. #include <linux/sched.h>
  22. #include <linux/kernel.h> /* printk() */
  23. #include <linux/fs.h>     /* everything... */
  24. #include <linux/errno.h>  /* error codes */
  25. #include <linux/malloc.h>
  26. #include <linux/mm.h>
  27. #include <linux/ioport.h>
  28. #include <linux/interrupt.h>
  29. #include <linux/tqueue.h>
  30. #include <asm/io.h>
  31. #include <asm/segment.h>
  32. #include "sysdep-2.1.h"
  33. int short_major = 0;
  34. #ifdef __alpha__
  35. int short_base = 0x3bc; /* Hmm.... FIXME */
  36. #else
  37. int short_base = 0x378; /* intel: default to lp0 */
  38. #endif
  39. int short_irq = -1;
  40. unsigned long short_buffer = 0;
  41. unsigned long volatile short_head;
  42. volatile unsigned long short_tail;
  43. struct wait_queue *short_queue;
  44. static int probe = 0; /* select at load time how to probe irq line */
  45. static int bh = 0; /* select at load time whether a bottom-half is used */
  46. static int share = 0; /* select at load time whether install a shared irq */
  47. /*
  48.  * The devices with low minor numbers write/read burst of data to/from
  49.  * specific I/O ports (by default the parallel ones).
  50.  * 
  51.  * The device with 128 as minor number returns ascii strings telling
  52.  * when interrupts have been received. Writing to the device toggles
  53.  * 00/FF on the parallel data lines. If there is a loopback wire, this
  54.  * generates interrupts.  
  55.  */
  56. int short_open (struct inode *inode, struct file *filp)
  57. {
  58.     extern struct file_operations short_i_fops;
  59.     MOD_INC_USE_COUNT;
  60.     if (MINOR(inode->i_rdev) & 0x80) {
  61.         filp->f_op = &short_i_fops; /* the interrupt-driven node */
  62.     }
  63.     return 0;
  64. }
  65. release_t short_release (struct inode *inode, struct file *filp)
  66. {
  67.     MOD_DEC_USE_COUNT;
  68.     release_return(0);
  69. }
  70. /* first, the port-oriented device */
  71. enum short_modes {SHORT_DEFAULT=0, SHORT_PAUSE, SHORT_STRING};
  72. read_write_t short_read (struct inode *inode, struct file *filp,
  73.                 char *buf, count_t count)
  74. {
  75.     int retval = count;
  76.     unsigned port = short_base + (MINOR(inode->i_rdev)&0x0f);
  77.     int mode = (MINOR(inode->i_rdev)&0x70) >> 4;
  78.     unsigned char *kbuf=kmalloc(count, GFP_KERNEL), *ptr;
  79.     
  80.     if (!kbuf) return -ENOMEM;
  81.     ptr=kbuf;
  82.     switch(mode) {
  83.       case SHORT_STRING:
  84. #ifndef __alpha__ /* Alpha doesn'e export insb: fall through */
  85.         insb(port, ptr, count);
  86.         break;
  87. #endif
  88.       case SHORT_DEFAULT:
  89.         while (count--)
  90.             *(ptr++) = inb(port);
  91.         break;
  92.       case SHORT_PAUSE:
  93.         while (count--)
  94.             *(ptr++) = inb_p(port);
  95.         break;
  96.       default: /* no more modes defined by now */
  97.         retval = -EINVAL;
  98.         break;
  99.     }
  100.     if (retval > 0)
  101.         copy_to_user(buf, kbuf, retval);
  102.     kfree(kbuf);
  103.     return retval;
  104. }
  105. read_write_t short_write (struct inode *inode, struct file *filp,
  106.                 const char *buf, count_t count)
  107. {
  108.     int retval = count;
  109.     unsigned port = short_base + (MINOR(inode->i_rdev)&0x0f);
  110.     int mode = (MINOR(inode->i_rdev)&0x70) >> 4;
  111.     unsigned char *kbuf=kmalloc(count, GFP_KERNEL), *ptr;
  112.     if (!kbuf) return -ENOMEM;
  113.     copy_from_user(kbuf, buf, count);
  114.     ptr=kbuf;
  115.     switch(mode) {
  116.       case SHORT_PAUSE:
  117.         while (count--)
  118.             outb_p(*(ptr++), port);
  119.         break;
  120.       case SHORT_STRING:
  121. #ifndef __alpha__ /* Alpha doesn't export insb: fall through  */
  122.         outsb(port, ptr, count);
  123.         break;
  124. #endif
  125.       case SHORT_DEFAULT:
  126.         while (count--)
  127.             outb(*(ptr++), port);
  128.         break;
  129.       default: /* no more modes defined by now */
  130.         retval = -EINVAL;
  131.         break;
  132.     }
  133.     kfree(kbuf);
  134.     return retval;
  135. }
  136. #ifdef __USE_OLD_SELECT__
  137. int short_poll (struct inode *inode, struct file *filp,
  138.                   int mode, select_table *table)
  139. {
  140.     return mode==SEL_EX ? 0 : 1; /* readable, writable, not-exceptionable */
  141. }
  142. #else
  143. unsigned int short_poll (struct file *filp, poll_table *table)
  144. {
  145.     return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
  146. }
  147. #endif
  148. struct file_operations short_fops = {
  149.     NULL,          /* short_lseek */
  150.     short_read,
  151.     short_write,
  152.     NULL,          /* short_readdir */
  153.     short_poll,
  154.     NULL,          /* short_ioctl */
  155.     NULL,          /* short_mmap */
  156.     short_open,
  157.     short_release,
  158.     NULL,          /* short_fsync */
  159.     NULL,          /* short_fasync */
  160.                    /* nothing more, fill with NULLs */
  161. };
  162. /* then,  the interrupt-related device */
  163. read_write_t short_i_read (struct inode *inode, struct file *filp,
  164.                 char *buf, count_t count)
  165. {
  166.     int count0;
  167.     while (short_head == short_tail) {
  168.         interruptible_sleep_on(&short_queue);
  169.         if (current->signal & ~current->blocked) /* a signal arrived */
  170.           return -ERESTARTSYS; /* tell the fs layer to handle it */
  171.         /* else, loop */
  172.     }
  173.     /* count0 is the number of readable data bytes */
  174.     count0 = short_head - short_tail;
  175.     if (count0 < 0) /* wrapped */
  176.         count0 = short_buffer + PAGE_SIZE - short_tail;
  177.     if (count0 < count) count = count0;
  178.     copy_to_user(buf, (char *)short_tail, count);
  179.     short_tail += count;
  180.     if (short_tail == short_buffer + PAGE_SIZE)
  181.         short_tail = short_buffer;
  182.     return count;
  183. }
  184. read_write_t short_i_write (struct inode *inode, struct file *filp,
  185.                 const char *buf, count_t count)
  186. {
  187.     int written = 0, odd = filp->f_pos & 1;
  188.     unsigned port = short_base; /* output to the parallel data latch */
  189.     while (written < count)
  190.         outb(0xff * ((++written + odd) & 1), port);
  191.     filp->f_pos += count;
  192.     return written;
  193. }
  194. struct file_operations short_i_fops = {
  195.     NULL,          /* short_i_lseek */
  196.     short_i_read,
  197.     short_i_write,
  198.     NULL,          /* short_i_readdir */
  199.     NULL,          /* short_i_select */
  200.     NULL,          /* short_i_ioctl */
  201.     NULL,          /* short_i_mmap */
  202.     short_open,
  203.     short_release,
  204.     NULL,          /* short_i_fsync */
  205.     NULL,          /* short_i_fasync */
  206.                    /* nothing more, fill with NULLs */
  207. };
  208. /*
  209.  * The interrupt handler has a different prototype whether it
  210.  * is compiled with kernels older or newer than 1.3.70
  211.  */
  212. #if LINUX_VERSION_CODE < VERSION_CODE(1,3,70)
  213. void short_interrupt(int irq, struct pt_regs *regs)
  214. #else
  215. void short_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  216. #endif
  217. {
  218.     struct timeval tv;
  219.     do_gettimeofday(&tv);
  220.     /* Write a 16 byte record. Assume PAGE_SIZE is a multiple of 16 */
  221.     short_head += sprintf((char *)short_head,"%08u.%06un",
  222.                           (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
  223.     if (short_head == short_buffer + PAGE_SIZE)
  224.         short_head = short_buffer; /* wrap */
  225.     wake_up_interruptible(&short_queue); /* awake any reading process */
  226. }
  227. /*
  228.  * The following two functions are equivalent to the previous one,
  229.  * but split in top and bottom half. First, a few needed variables
  230.  */
  231. #define NR_TIMEVAL 512 /* length of the array of time values */
  232. struct timeval tv_data[NR_TIMEVAL]; /* too lazy to allocate it */
  233. volatile struct timeval *tv_head=tv_data;
  234. struct timeval *tv_tail=tv_data;
  235. static struct tq_struct short_task; /* 0 by now, filled by init_module code */
  236. int short_bh_count = 0;
  237. void short_bottom_half(void *unused)
  238. {
  239.     int savecount = short_bh_count;
  240.     short_bh_count = 0; /* we have already been removed from the queue */
  241.     /*
  242.      * The bottom half reads the tv array, filled by the top half,
  243.      * and prints it to the circular text buffer, which is then consumed
  244.      * by reading processes
  245.      */
  246.     /* First write the number of interrupts that occurred before this bh */
  247.     short_head += sprintf((char *)short_head,"bh after %6in",savecount);
  248.     if (short_head == short_buffer + PAGE_SIZE)
  249.         short_head = short_buffer; /* wrap */
  250.     /*
  251.      * Then, write the time values. Write exactly 16 bytes at a time,
  252.      * so it aligns with PAGE_SIZE
  253.      */
  254.     do {
  255.         short_head += sprintf((char *)short_head,"%08u.%06un",
  256.                               (int)(tv_tail->tv_sec % 100000000),
  257.                               (int)(tv_tail->tv_usec));
  258.         if (short_head == short_buffer + PAGE_SIZE)
  259.             short_head = short_buffer; /* wrap */
  260.         
  261.         tv_tail++;
  262.         if (tv_tail == (tv_data + NR_TIMEVAL) ) 
  263.             tv_tail = tv_data; /* wrap */
  264.         
  265.     } while (tv_tail != tv_head);
  266.     wake_up_interruptible(&short_queue); /* awake any reading process */
  267. }
  268. /*
  269.  * use two implementations: before version 1.3.70 you couldn't
  270.  * re-enqueue tasks, and "dev_id" was missing. I like re-enqueueing,
  271.  * so I'd better keep the modern version clean
  272.  */
  273. #if LINUX_VERSION_CODE < VERSION_CODE(1,3,70) /* old */
  274. void short_bh_interrupt(int irq, struct pt_regs *regs)
  275. {
  276.     do_gettimeofday(tv_head);
  277.     tv_head++;
  278.     if (tv_head == (tv_data + NR_TIMEVAL) ) 
  279.         tv_head = tv_data; /* wrap */
  280.     /* Queue the bh. Don't re-enqueue */
  281.     if (!short_bh_count)
  282.         queue_task_irq_off(&short_task, &tq_immediate);
  283.     mark_bh(IMMEDIATE_BH);
  284.     short_bh_count++; /* record that an interrupt arrived */
  285. }
  286. #else /* recent */
  287. void short_bh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  288. {
  289.     do_gettimeofday(tv_head);
  290.     tv_head++;
  291.     if (tv_head == (tv_data + NR_TIMEVAL) ) 
  292.         tv_head = tv_data; /* wrap */
  293.     /* Queue the bh. Don't care for multiple enqueueing */
  294.     queue_task_irq_off(&short_task, &tq_immediate);
  295.     mark_bh(IMMEDIATE_BH);
  296.     short_bh_count++; /* record that an interrupt arrived */
  297. }
  298. #endif
  299. #if LINUX_VERSION_CODE < VERSION_CODE(1,3,70)
  300. void short_sh_interrupt(int irq, struct pt_regs *regs)
  301. {
  302.     void *dev_id = NULL;
  303. #else
  304. void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  305. {
  306. #endif
  307.     int value;
  308.     struct timeval tv;
  309.     /* If it wasn't short, return immediately */
  310.     value = inb(short_base);
  311.     if (!(value & 0x80)) return;
  312.         
  313.     /* clear the interrupting bit */
  314.     outb(value & 0x7F, short_base);
  315.     /* the rest is unchanged */
  316.     do_gettimeofday(&tv);
  317.     short_head += sprintf((char *)short_head,"%08u.%06un",
  318.                           (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
  319.     if (short_head == short_buffer + PAGE_SIZE)
  320.         short_head = short_buffer; /* wrap */
  321.     wake_up_interruptible(&short_queue); /* awake any reading process */
  322. }
  323. #if LINUX_VERSION_CODE >= VERSION_CODE(1,3,30)
  324. void short_kernelprobe(void)
  325. {
  326.     int count = 0;
  327.     do {
  328.         unsigned long mask;
  329.         mask = probe_irq_on();
  330.         outb_p(0x10,short_base+2); /* enable reporting */
  331.         outb_p(0x00,short_base);   /* clear the bit */
  332.         outb_p(0xFF,short_base);   /* set the bit: interrupt! */
  333.         outb_p(0x00,short_base+2); /* disable reporting */
  334.         short_irq = probe_irq_off(mask);
  335.         if (short_irq == 0) { /* none of them? */
  336.             printk(KERN_INFO "short: no irq reported by proben");
  337.             short_irq = -1;
  338.         }
  339.         /*
  340.          * if more than one line has been activated, the result is
  341.          * negative. We should service the interrupt (no need for lpt port)
  342.          * and loop over again. Loop at most five times, then give up
  343.          */
  344.     } while (short_irq < 0 && count++ < 5);
  345.     if (short_irq < 0)
  346.         printk("short: probe failed %i times, giving upn", count);
  347. }
  348. #else
  349. void short_kernelprobe(void)
  350. {
  351.     printk(KERN_WARNING "short: kernel probing not available before 1.3.30n");
  352. }
  353. #endif /* 1.3.30 */
  354. #if LINUX_VERSION_CODE < VERSION_CODE(1,3,70)
  355. void short_probing(int irq, struct pt_regs *regs)
  356. #else
  357. void short_probing(int irq, void *dev_id, struct pt_regs *regs)
  358. #endif
  359. {
  360.     if (short_irq == 0) short_irq = irq;    /* found */
  361.     if (short_irq != irq) short_irq = -irq; /* ambiguous */
  362. }
  363. void short_selfprobe(void)
  364. {
  365.     int trials[] = {3, 5, 7, 9, 0};
  366.     int tried[]  = {0, 0, 0, 0, 0};
  367.     int i, count = 0;
  368.      /*
  369.       * install the probing handler for all possible lines. Remember
  370.       * the result (0 for success, or -EBUSY) in order to only free
  371.       * what has been acquired
  372.       */
  373.     for (i=0; trials[i]; i++)
  374.         tried[i] = request_irq(trials[i], short_probing,
  375.                                SA_INTERRUPT, "short probe", NULL);
  376.     do {
  377.         short_irq = 0; /* none got, yet */
  378.         outb_p(0x10,short_base+2); /* enable */
  379.         outb_p(0x00,short_base);
  380.         outb_p(0xFF,short_base); /* toggle the bit */
  381.         outb_p(0x10,short_base+2); /* disable */
  382.         /* the value has been set by the handler */
  383.         if (short_irq == 0) { /* none of them? */
  384.             printk(KERN_INFO "short: no irq reported by proben");
  385.         }
  386.         /*
  387.          * If more than one line has been activated, the result is
  388.          * negative. We should service the interrupt (but the lpt port
  389.          * doesn't need it) and loop over again. Do it at most 5 times
  390.          */
  391.     } while (short_irq <=0 && count++ < 5);
  392.     /* end of loop, uninstall the handler */
  393.     for (i=0; trials[i]; i++)
  394.         if (tried[i] == 0)
  395.             free_irq(trials[i], NULL);
  396.     if (short_irq < 0)
  397.         printk("short: probe failed %i times, giving upn", count);
  398. }
  399. int init_module(void)
  400. {
  401.     int result = check_region(short_base,4);
  402.     if (result) {
  403.         printk(KERN_INFO "short: can't get I/O address 0x%xn",short_base);
  404.         return result;
  405.     }
  406.     request_region(short_base,4,"short");
  407.     result = register_chrdev(short_major, "short", &short_fops);
  408.     if (result < 0) {
  409.         printk(KERN_INFO "short: can't get major numbern");
  410.         release_region(short_base,4);
  411.         return result;
  412.     }
  413.     if (short_major == 0) short_major = result; /* dynamic */
  414.     short_buffer = __get_free_page(GFP_KERNEL); /* never fails */
  415.     short_head = short_tail = short_buffer;
  416.     /*
  417.      * Fill the short_task structure, used for the bottom half handler
  418.      */
  419.     short_task.routine = short_bottom_half;
  420.     short_task.data = NULL; /* unused */
  421.     /*
  422.      * Now we deal with the interrupt: either kernel-based
  423.      * autodetection, DIY detection or default number
  424.      */
  425.     if (short_irq < 0 && probe == 1)
  426.         short_kernelprobe();
  427.     if (short_irq < 0 && probe == 2)
  428.         short_selfprobe();
  429.     if (short_irq < 0) /* not yet specified: force the default on */
  430.         switch(short_base) {
  431.           case 0x378: short_irq = 7; break;
  432.           case 0x278: short_irq = 2; break;
  433.           case 0x3bc: short_irq = 5; break;
  434.         }
  435.     /*
  436.      * If shared has been specified, installed the shared handler
  437.      * instead of the normal one. Do it first, before a -EBUSY will
  438.      * force short_irq to -1.
  439.      */
  440.     if (short_irq >= 0 && share > 0) {
  441.         free_irq(short_irq,NULL);
  442.         result = request_irq(short_irq, short_sh_interrupt,
  443.                              SA_SHIRQ | SA_INTERRUPT,"short",
  444.                              short_sh_interrupt);
  445.         if (result) {
  446.             printk(KERN_INFO "short: can't get assigned irq %in", short_irq);
  447.             short_irq = -1;
  448.         }
  449.         else { /* actually enable it -- assume this *is* a parallel port */
  450.             outb(0x10,short_base+2);
  451.         }
  452.         return 0; /* the rest of the function only installs handlers */
  453.     }
  454.     if (short_irq >= 0) {
  455.         result = request_irq(short_irq, short_interrupt,
  456.                              SA_INTERRUPT, "short", NULL);
  457.         if (result) {
  458.             printk(KERN_INFO "short: can't get assigned irq %in",
  459.                    short_irq);
  460.             short_irq = -1;
  461.         }
  462.         else { /* actually enable it -- assume this *is* a parallel port */
  463.             outb(0x10,short_base+2);
  464.         }
  465.     }
  466.     /*
  467.      * Ok, now change the interrupt handler if using top/bottom halves
  468.      * has been requested
  469.      */
  470.     if (short_irq >= 0 && bh > 0) {
  471.         free_irq(short_irq,NULL);
  472.         result = request_irq(short_irq, short_bh_interrupt,
  473.                              SA_INTERRUPT,"short-bh", NULL);
  474.         if (result) {
  475.             printk(KERN_INFO "short-bh: can't get assigned irq %in",
  476.                    short_irq);
  477.             short_irq = -1;
  478.         }
  479.     }
  480.     return 0;
  481. }
  482. void cleanup_module(void)
  483. {
  484.     if (short_irq >= 0) {
  485.         if (!share) free_irq(short_irq, NULL);
  486.         else free_irq(short_irq, short_sh_interrupt);
  487.     }
  488.         
  489.     unregister_chrdev(short_major, "short");
  490.     release_region(short_base,4);
  491.     if (short_buffer) free_page(short_buffer);
  492. }
  493. #endif /* __sparc__ */