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

嵌入式Linux

开发平台:

C/C++

  1. /*
  2.  * silly.c -- Simple Tool for Unloading and Printing ISA Data
  3.  *
  4.  * Tested with 1.2 on the x86
  5.  * Tested with 2.0 on the x86
  6.  * This module won't work on the sparc, where there's no concept of I/O space
  7.  *
  8.  */
  9. #ifndef __KERNEL__
  10. #  define __KERNEL__
  11. #endif
  12. #ifndef MODULE
  13. #  define MODULE
  14. #endif
  15. #ifdef __sparc__
  16. #  error "This module can't run on the Sparc platform"
  17. #else
  18. #define __NO_VERSION__ /* don't define kernel_verion in module.h */
  19. #include <linux/module.h>
  20. #include <linux/version.h>
  21. char kernel_version [] = UTS_RELEASE;
  22. #include <linux/sched.h>
  23. #include <linux/kernel.h> /* printk() */
  24. #include <linux/fs.h>     /* everything... */
  25. #include <linux/errno.h>  /* error codes */
  26. #include <linux/tqueue.h>
  27. #include <linux/malloc.h>
  28. #include <linux/mm.h>
  29. #include <linux/ioport.h>
  30. #include <asm/io.h>
  31. #include <asm/segment.h>  /* memcpy to/from fs */
  32. #include "sysdep.h" /* count_t for portability 2.0/2.1 */
  33. int silly_major = 0;
  34. static int lines=25, columns=80; /* can be changed at load time */
  35. /*
  36.  * The devices access the 640k-1M memory.
  37.  * minor 0 uses readb/writeb
  38.  * minor 1 uses readw/writew
  39.  * minor 2 uses readl/writel
  40.  * minor 3 uses memcpy_fromio()/memcpy_toio()
  41.  * minor 4 uses readl/writel over VGA mem (0xb8000-0xc0000)
  42.  * minor 5 only drops letters in the VGA buffer
  43.  */
  44. static int silly_joke_write(int count);
  45. int silly_open (struct inode *inode, struct file *filp)
  46. {
  47.     MOD_INC_USE_COUNT;
  48.     return 0;
  49. }
  50. void silly_release (struct inode *inode, struct file *filp)
  51. {
  52.     MOD_DEC_USE_COUNT;
  53. }
  54. enum silly_modes {M_8=0, M_16, M_32, M_memcpy, M_vga, M_joke};
  55. read_write_t silly_read (struct inode *inode, struct file *filp,
  56.                 char *buf, count_t count)
  57. {
  58.     int retval;
  59.     int mode = MINOR(inode->i_rdev);
  60.     unsigned long add = 0xA0000 + filp->f_pos;
  61.     unsigned char *kbuf, *ptr;
  62.     if (mode == M_joke) return 0;  /* no read on /dev/silliest */
  63.     if (mode == M_vga) {
  64.         add = 0xB8000 + filp->f_pos; /* range: 0xB8000-0xC0000 */
  65.         if (add + count > 0xC0000)
  66.             count = 0xC0000 - add;
  67.         mode = M_32; /* and fall back to normal xfer */
  68.     }
  69.     else 
  70.         if (add + count > 0x100000) /* range: 0xA0000-0x100000 */
  71.             count = 0x100000 - add;
  72.     /*
  73.      * too big an f_pos (caused by a malicious lseek())
  74.      * would result in a negative count
  75.      */
  76.     if (count < 0) return 0;
  77.     kbuf = kmalloc(count, GFP_KERNEL);
  78.     if (!kbuf) return -ENOMEM;
  79.     ptr=kbuf;
  80.     retval=count;
  81.     /*
  82.      * kbuf is aligned, but the reads might not. In order not to
  83.      * drive me mad with unaligned leading and trailing bytes,
  84.      * I downgrade the `mode' if unaligned xfers are requested.
  85.      */
  86.     if (mode==M_32 && ((add | count) & 3))
  87.         mode = M_16;
  88.     if (mode==M_16 && ((add | count) & 1))
  89.         mode = M_8;
  90.     switch(mode) {
  91.       case M_32: 
  92.         while (count >= 4) {
  93.             *(u32 *)ptr = readl(add);
  94.             add+=4; count-=4; ptr+=4;
  95.         }
  96.         break;
  97.             
  98.       case M_16: 
  99.         while (count >= 2) {
  100.             *(u16 *)ptr = readw(add);
  101.             add+=2; count-=2; ptr+=2;
  102.         }
  103.         break;
  104.             
  105.       case M_8: 
  106.         while (count) {
  107.             *ptr = readb(add);
  108.             add++; count--; ptr++;
  109.         }
  110.         break;
  111.       case M_memcpy:
  112.         memcpy_fromio(ptr, add, count);
  113.         break;
  114.       default:
  115.         return -EINVAL;
  116.     }
  117.     if (retval > 0)
  118.         memcpy_tofs(buf, kbuf, retval);
  119.     kfree(kbuf);
  120.     filp->f_pos += retval;
  121.     return retval;
  122. }
  123. read_write_t silly_write (struct inode *inode, struct file *filp,
  124.                 const char *buf, count_t count)
  125. {
  126.     int retval;
  127.     int mode = MINOR(inode->i_rdev);
  128.     unsigned long add = 0xA0000 + filp->f_pos;
  129.     unsigned char *kbuf, *ptr;
  130.     /*
  131.      * Writing is dangerous.
  132.      * Allow root-only, independently of device permissions
  133.      */
  134.     if (!suser()) return -EPERM;
  135.     if (mode == M_joke)
  136.         return silly_joke_write(count);
  137.     if (mode == M_vga) {
  138.         add = 0xB8000 + filp->f_pos; /* range: 0xB8000-0xC0000 */
  139.         if (add + count > 0xC0000)
  140.             count = 0xC0000 - add;
  141.         mode = M_32; /* and fall back to normal xfer */
  142.     }
  143.     else 
  144.         if (add + count > 0x100000) /* range: 0xA0000-0x100000 */
  145.             count = 0x100000 - add;
  146.     /*
  147.      * too big an f_pos (caused by a malicious lseek())
  148.      * results in a negative count
  149.      */
  150.     if (count < 0) return 0;
  151.     kbuf = kmalloc(count, GFP_KERNEL);
  152.     if (!kbuf) return -ENOMEM;
  153.     ptr=kbuf;
  154.     retval=count;
  155.     /*
  156.      * kbuf is aligned, but the writes might not. In order not to
  157.      * drive me mad with unaligned leading and trailing bytes,
  158.      * I downgrade the `mode' if unaligned xfers are requested.
  159.      */
  160.     if (mode==M_32 && ((add | count) & 3))
  161.         mode = M_16;
  162.     if (mode==M_16 && ((add | count) & 1))
  163.         mode = M_8;
  164.     memcpy_fromfs(kbuf, buf, count);
  165.     ptr=kbuf;
  166.     switch(mode) {
  167.       case M_32: 
  168.         while (count >= 4) {
  169.             writel(*(u32 *)ptr, add);
  170.             add+=4; count-=4; ptr+=4;
  171.         }
  172.         break;
  173.             
  174.       case M_16: 
  175.         while (count >= 2) {
  176.             writel(*(u16 *)ptr, add);
  177.             add+=2; count-=2; ptr+=2;
  178.         }
  179.         break;
  180.             
  181.       case M_8: 
  182.         while (count) {
  183.             writeb(*ptr, add);
  184.             add++; count--; ptr++;
  185.         }
  186.         break;
  187.       case M_memcpy:
  188.         memcpy_toio(add, ptr, count);
  189.         break;
  190.       default:
  191.         return -EINVAL;
  192.     }
  193.     filp->f_pos += retval;
  194.     return retval;
  195. }
  196. int silly_select (struct inode *inode, struct file *filp,
  197.                   int mode, select_table *table)
  198. {
  199.     return mode==SEL_EX ? 0 : 1; /* readable, writable, not-exceptionable */
  200. }
  201. /*
  202.  * Dropping letters: use the timer queue to drop 10 rows per second.
  203.  * Put the writing process to sleep, and don't manage more than 1 writer.
  204.  *
  205.  * Note that this isn't meant to show how to access the text buffer:
  206.  * it must *not* be accessed this way. Look at selection.c for information
  207.  * about the text buffer.
  208.  */
  209. struct wait_queue *jokeq;
  210. struct tq_struct silly_task;
  211. void silly_timerfn(void *ptr)
  212. {
  213.     unsigned long place = (unsigned long)ptr;
  214.     char *ch, *ch2;
  215.     static int time;
  216.     ch = (char *)(0xb8000 + place*2);  /* two bytes every char-cell */
  217.     ch2 = ch + columns * 2;
  218.     if (place >= lines*columns || readb(ch) == ' ' || readb(ch2) != ' ') {
  219.         /* printk("not goodn"); */
  220.         wake_up_interruptible(&jokeq);
  221.         return;
  222.     }
  223.     /* don't do it everytime, only ten times per second */
  224.     if (time++ >= HZ/10) {
  225.         time = 0;
  226.         /* ("in queue: place %4i (%i,%i) -- %x %xn", place, place%columns,
  227.                place/columns, (int)ch, (int)ch2); */
  228.         place += columns;
  229.         writeb(readb(ch),ch2); writeb(' ',ch); /* down one */
  230.         silly_task.data = (void *)place;
  231.     }
  232.     queue_task(&silly_task, &tq_timer);
  233.     return;
  234. }    
  235. static int silly_joke_write(int count)
  236. {
  237.     int i, j;
  238.     unsigned long place;
  239.     char *ch, *ch2;
  240.     
  241.     for (i=0; i<count; i++) {
  242.         /* choose a place with any algorithm */
  243.         place = (unsigned int)(jiffies * 443) % (lines * columns);
  244.         for (j=0; j<50; j++) { /* try 50 times */
  245.             ch = (char *)(0xb8000 + place*2);
  246.             ch2 = ch + 2*columns;
  247.             if (*ch == ' ' || *ch2 != ' ') {
  248.                 place *= 331; place %= (lines * columns);
  249.                 continue;
  250.             }
  251.             break;
  252.         }
  253.         silly_task.routine = silly_timerfn;
  254.         silly_task.data = (void *)place;
  255.         queue_task(&silly_task, &tq_timer);
  256.         interruptible_sleep_on(&jokeq);
  257.         if (current->signal & ~current->blocked)
  258.             return -ERESTARTSYS;
  259.     }
  260.     return i;
  261. }
  262. /*
  263.  * Done, the rest is normal
  264.  */
  265. struct file_operations silly_fops = {
  266.     NULL,          /* silly_lseek */
  267.     silly_read,
  268.     silly_write,
  269.     NULL,          /* silly_readdir */
  270.     silly_select,
  271.     NULL,          /* silly_ioctl */
  272.     NULL,          /* silly_mmap */
  273.     silly_open,
  274.     silly_release,
  275.     NULL,          /* silly_fsync */
  276.     NULL,          /* silly_fasync */
  277.                    /* nothing more, fill with NULLs */
  278. };
  279. int init_module(void)
  280. {
  281.     int result = register_chrdev(silly_major, "silly", &silly_fops);
  282.     if (result < 0) {
  283.         printk(KERN_INFO "silly: can't get major numbern");
  284.         return result;
  285.     }
  286.     if (silly_major == 0) silly_major = result; /* dynamic */
  287.     return 0;
  288. }
  289. void cleanup_module(void)
  290. {
  291.     unregister_chrdev(silly_major, "silly");
  292. }
  293. #endif /* __386__ */