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