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

嵌入式Linux

开发平台:

C/C++

  1. /*
  2.  * main.c -- the bare scull char module (v2.1)
  3.  */
  4. #ifndef __KERNEL__
  5. #  define __KERNEL__
  6. #endif
  7. #ifndef MODULE
  8. #  define MODULE
  9. #endif
  10. #include <linux/module.h>
  11. #include <linux/kernel.h> /* printk() */
  12. #include <linux/malloc.h> /* kmalloc() */
  13. #include <linux/fs.h>     /* everything... */
  14. #include <linux/errno.h>  /* error codes */
  15. #include <linux/types.h>  /* size_t */
  16. #include <linux/proc_fs.h>
  17. #include <linux/fcntl.h>        /* O_ACCMODE */
  18. #include <asm/system.h>   /* cli(), *_flags */
  19. #include <asm/segment.h>  /* memcpy and such */
  20. #include "scull.h"        /* local definitions */
  21. #include "sysdep-2.1.h"
  22. /*
  23.  * I don't use static symbols here, because register_symtab is called
  24.  */
  25. int scull_major =   SCULL_MAJOR;
  26. int scull_nr_devs = SCULL_NR_DEVS;    /* number of bare scull devices */
  27. int scull_quantum = SCULL_QUANTUM;
  28. int scull_qset =    SCULL_QSET;
  29. /* v2.1 wants parameters to be declared as such */
  30. MODULE_PARM(scull_major,"i");
  31. MODULE_PARM(scull_nr_devs,"i");
  32. MODULE_PARM(scull_quantum,"i");
  33. MODULE_PARM(scull_qset,"i");
  34. Scull_Dev *scull_devices; /* allocated in init_module */
  35. /*
  36.  * Different minors behave differently, so let's use multiple fops
  37.  */
  38. struct file_operations *scull_fop_array[]={
  39.     &scull_fops,      /* type 0 */
  40.     &scull_priv_fops, /* type 1 */
  41.     &scull_pipe_fops, /* type 2 */
  42.     &scull_sngl_fops, /* type 3 */
  43.     &scull_user_fops, /* type 4 */
  44.     &scull_wusr_fops  /* type 5 */
  45. };
  46. #define SCULL_MAX_TYPE 5
  47. int scull_trim(Scull_Dev *dev)
  48. {
  49.     Scull_Dev *next, *dptr;
  50.     int qset = dev->qset;   /* "dev" is not-null */
  51.     int i;
  52.     if (dev->usage)
  53.       return -EBUSY; /* scull_open ignores this error and goes on */
  54.     for (dptr = dev; dptr; dptr = next) { /* all the list items */
  55.         if (dptr->data) {
  56.             for (i = 0; i < qset; i++)
  57.                 if (dptr->data[i])
  58.                     kfree(dptr->data[i]);
  59.             kfree(dptr->data);
  60.             dptr->data=NULL;
  61.         }
  62.         next=dptr->next;
  63.         if (dptr != dev) kfree(dptr); /* all of them but the first */
  64.     }
  65.     dev->size = 0;
  66.     dev->quantum = scull_quantum;
  67.     dev->qset = scull_qset;
  68.     dev->next = NULL;
  69.     return 0;
  70. }
  71. #ifdef SCULL_USE_PROC /* don't waste space if unused */
  72. /*
  73.  * The proc filesystem: function to read and entry
  74.  */
  75. int scull_read_procmem(char *buf, char **start, off_t offset,
  76.                    int len, int unused)
  77. {
  78.     int i, j, quantum, qset;
  79.     Scull_Dev *d;
  80.     #define LIMIT (PAGE_SIZE-80) /* don't print any more after this size */
  81.     len=0;
  82.     for(i=0; i<scull_nr_devs; i++) {
  83.         d=&scull_devices[i];
  84.         quantum=d->quantum;  /* retrieve the features of each device */
  85.         qset=d->qset;
  86.         len += sprintf(buf+len,"nDevice %i: qset %i, q %i, sz %lin",
  87.                        i, qset, quantum, d->size);
  88.         for (; d; d=d->next) { /* scan the list */
  89.             if (len > LIMIT) return len;
  90.             len += sprintf(buf+len,"  item at %p, qset at %pn",d,d->data);
  91.             if (d->data && !d->next) /* dump only the last item - save space */
  92.             for (j=0; j<qset; j++) {
  93.                 if (len > LIMIT) return len;
  94.                 if (d->data[j])
  95.                     len += sprintf(buf+len,"    % 4i:%8pn",j,d->data[j]);
  96.             }
  97.         }
  98.     }
  99.     return len;
  100. }
  101. struct proc_dir_entry scull_proc_entry = {
  102.         0,                 /* low_ino: the inode -- dynamic */
  103.         8, "scullmem",     /* len of name and name */
  104.         S_IFREG | S_IRUGO, /* mode */
  105.         1, 0, 0,           /* nlinks, owner, group */
  106.         0, NULL,           /* size - unused; operations -- use default */
  107.         &scull_read_procmem,   /* function used to read data */
  108.         /* nothing more */
  109.     };
  110. #endif /* SCULL_USE_PROC */
  111. /*
  112.  * Open and close
  113.  */
  114. int scull_open (struct inode *inode, struct file *filp)
  115. {
  116.     int type = TYPE(inode->i_rdev);
  117.     int num = NUM(inode->i_rdev);
  118.     Scull_Dev *dev; /* device information */
  119.     /* manage peculiar types first */
  120.     if (type) {
  121.         if (type > SCULL_MAX_TYPE) return -ENODEV;
  122.         filp->f_op = scull_fop_array[type];
  123.         return filp->f_op->open(inode, filp); /* dispatch to specific open */
  124.     }
  125.     /* type 0, check the device number */
  126.     if (num >= scull_nr_devs) return -ENODEV;
  127.     dev = &scull_devices[num];
  128.     /* now trim to 0 the length of the device if open was write-only */
  129.     if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
  130.         scull_trim(dev); /* ignore errors */
  131.     /* and use filp->private_data to point to the device data */
  132.     filp->private_data = dev;
  133.     MOD_INC_USE_COUNT;
  134.     return 0;          /* success */
  135. }
  136. release_t scull_release (struct inode *inode, struct file *filp)
  137. {
  138.     MOD_DEC_USE_COUNT;
  139.     release_return(0);
  140. }
  141. /*
  142.  * Follow the list 
  143.  */
  144. Scull_Dev *scull_follow(Scull_Dev *dev, int n)
  145. {
  146.     while (n--) {
  147.         if (!dev->next) {
  148.             dev->next = kmalloc(sizeof(Scull_Dev), GFP_KERNEL);
  149.             memset(dev->next, 0, sizeof(Scull_Dev));
  150.         }
  151.         dev = dev->next;
  152.         continue;
  153.     }
  154.     return dev;
  155. }
  156. /*
  157.  * Data management: read and write
  158.  */
  159. read_write_t scull_read (struct inode *inode, struct file *filp,
  160.                 char *buf, count_t count)
  161. {
  162.     Scull_Dev *dev = filp->private_data; /* the first listitem */
  163.     int quantum = dev->quantum;
  164.     int qset = dev->qset;
  165.     int itemsize = quantum * qset; /* how many bytes in the listitem */
  166.     unsigned long f_pos = (unsigned long)(filp->f_pos);
  167.     int item, s_pos, q_pos, rest;
  168.     if (f_pos > dev->size)
  169.         return 0;
  170.     if (f_pos + count > dev->size)
  171.         count = dev->size - f_pos;
  172.     /* find listitem, qset index and offset in the quantum */
  173.     item = f_pos / itemsize;
  174.     rest = f_pos % itemsize;
  175.     s_pos = rest / quantum; q_pos = rest % quantum;
  176.     /* follow the list up to the right position (defined elsewhere) */
  177.     dev = scull_follow(dev, item);
  178.     if (!dev->data)
  179.         return 0; /* don't fill holes */
  180.     if (!dev->data[s_pos])
  181.         return 0;
  182.     if (count > quantum - q_pos)
  183.         count = quantum - q_pos; /* read only up to the end of this quantum */
  184.     dev->usage++; /* the following call may sleep */
  185.     copy_to_user(buf, dev->data[s_pos]+q_pos, count);
  186.     dev->usage--;
  187.     filp->f_pos += count;
  188.     return count;
  189. }
  190. read_write_t scull_write (struct inode *inode, struct file *filp,
  191.                 const char *buf, count_t count)
  192. {
  193.     Scull_Dev *dev = filp->private_data;
  194.     Scull_Dev *dptr;
  195.     int quantum = dev->quantum;
  196.     int qset = dev->qset;
  197.     int itemsize = quantum * qset;
  198.     unsigned long f_pos = (unsigned long)(filp->f_pos);
  199.     int item, s_pos, q_pos, rest;
  200.     /* find listitem, qset index and offset in the quantum */
  201.     item = f_pos / itemsize;
  202.     rest = f_pos % itemsize;
  203.     s_pos = rest / quantum; q_pos = rest % quantum;
  204.     /* follow the list up to the right position */
  205.     dptr = scull_follow(dev, item);
  206.     if (!dptr->data) {
  207.         dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
  208.         if (!dptr->data)
  209.             return -ENOMEM;
  210.         memset(dptr->data, 0, qset * sizeof(char *));
  211.     }
  212.     if (!dptr->data[s_pos]) {
  213.         dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
  214.         if (!dptr->data[s_pos])
  215.             return -ENOMEM;
  216.     }
  217.     if (count > quantum - q_pos)
  218.         count = quantum - q_pos; /* write only up to the end of this quantum */
  219.     dev->usage++; /* the following call may sleep */
  220.     copy_from_user(dptr->data[s_pos]+q_pos, buf, count);
  221.     dev->usage--;
  222.     /* update the size */
  223.     if (dev->size < f_pos + count)
  224.         dev-> size = f_pos + count;
  225.     filp->f_pos += count;
  226.     return count;
  227. }
  228. /*
  229.  * The ioctl() implementation
  230.  */
  231. int scull_ioctl (struct inode *inode, struct file *filp,
  232.                  unsigned int cmd, unsigned long arg)
  233. {
  234.     int tmp, size = _IOC_SIZE(cmd); /* the size bitfield in cmd */
  235.     /*
  236.      * extract the type and number bitfields, and don't decode
  237.      * wrong cmds: return EINVAL before verify_area()
  238.      */
  239.     if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -EINVAL;
  240.     if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -EINVAL;
  241.     /*
  242.      * the direction is a bitmask, and VERIFY_WRITE catches R/W
  243.      * transfers. `Type' is user-oriented, while
  244.      * verify_area is kernel-oriented, so the concept of "read" and
  245.      * "write" is reversed
  246.      */
  247.     if ((_IOC_DIR(cmd) & _IOC_READ) &&
  248.         verify_area_20(VERIFY_WRITE, (void *)arg, size))
  249.         return -EINVAL;
  250.     else if ((_IOC_DIR(cmd) & _IOC_WRITE) &&
  251.              verify_area_20(VERIFY_READ, (void *)arg, size))
  252.         return -EINVAL;
  253.     
  254.     switch(cmd) {
  255.       case SCULL_IOCRESET:
  256.         scull_quantum = SCULL_QUANTUM;
  257.         scull_qset = SCULL_QSET;
  258.         break;
  259.         
  260.       case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
  261.         GET_USER_RET(scull_quantum,(int *)arg, -EFAULT);
  262.         break;
  263.       case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
  264.         scull_quantum = arg;
  265.         break;
  266.       case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
  267.         PUT_USER_RET(scull_quantum, (int *)arg, -EFAULT);
  268.         break;
  269.       case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */
  270.         return scull_quantum;
  271.       case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */
  272.         tmp = scull_quantum;
  273.         GET_USER_RET(scull_quantum,(int *)arg, -EFAULT);
  274.         PUT_USER_RET(tmp, (int *)arg, -EFAULT);
  275.         break;
  276.       case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */
  277.         tmp = scull_quantum;
  278.         scull_quantum = arg;
  279.         return tmp;
  280.         
  281.       case SCULL_IOCSQSET:
  282.         GET_USER_RET(scull_qset, (int *)arg, -EFAULT);
  283.         break;
  284.       case SCULL_IOCTQSET:
  285.         scull_qset = arg;
  286.         break;
  287.       case SCULL_IOCGQSET:
  288.         PUT_USER_RET(scull_qset, (int *)arg, -EFAULT);
  289.         break;
  290.       case SCULL_IOCQQSET:
  291.         return scull_qset;
  292.       case SCULL_IOCXQSET:
  293.         tmp = scull_qset;
  294.         GET_USER_RET(scull_qset, (int *)arg, -EFAULT);
  295.         PUT_USER_RET(tmp, (int *)arg, -EFAULT);
  296.         break;
  297.       case SCULL_IOCHQSET:
  298.         tmp = scull_qset;
  299.         scull_quantum = arg;
  300.         return tmp;
  301.         /*
  302.          * The following two change the buffer size for scullpipe.
  303.          * The scullpipe device uses this same ioctl method, just to
  304.          * write less code. Actually, it's the same driver, isn't it?
  305.          */
  306.       case SCULL_P_IOCTSIZE:
  307.         scull_p_buffer = arg;
  308.         break;
  309.       case SCULL_P_IOCQSIZE:
  310.         return scull_p_buffer;
  311.       default:  /* redundant, as cmd was checked against MAXNR */
  312.         return -EINVAL;
  313.     }
  314.     return 0;
  315. }
  316. /*
  317.  * The "extended" operations -- only seek
  318.  */
  319. lseek_t scull_lseek (struct inode *inode, struct file *filp,
  320.                  lseek_off_t off, int whence)
  321. {
  322.     Scull_Dev *dev = filp->private_data;
  323.     long newpos;
  324.     switch(whence) {
  325.       case 0: /* SEEK_SET */
  326.         newpos = off;
  327.         break;
  328.       case 1: /* SEEK_CUR */
  329.         newpos = filp->f_pos + off;
  330.         break;
  331.       case 2: /* SEEK_END */
  332.         newpos = dev->size + off;
  333.         break;
  334.       default: /* can't happen */
  335.         return -EINVAL;
  336.     }
  337.     if (newpos<0) return -EINVAL;
  338.     filp->f_pos = newpos;
  339.     return newpos;
  340. }
  341. /*
  342.  * The different file operations
  343.  */
  344. struct file_operations scull_fops = {
  345.     scull_lseek,
  346.     scull_read,
  347.     scull_write,
  348.     NULL,          /* scull_readdir */
  349.     NULL,          /* scull_select */
  350.     scull_ioctl,
  351.     NULL,          /* scull_mmap */
  352.     scull_open,
  353.     scull_release,
  354.                    /* nothing more, fill with NULLs */
  355. };
  356. /*
  357.  * Finally, the module stuff
  358.  */
  359. int init_module(void)
  360. {
  361.     int result, i;
  362.     /*
  363.      * Register your major, and accept a dynamic number
  364.      */
  365.     result = register_chrdev(scull_major, "scull", &scull_fops);
  366.     if (result < 0) {
  367.         printk(KERN_WARNING "scull: can't get major %dn",scull_major);
  368.         return result;
  369.     }
  370.     if (scull_major == 0) scull_major = result; /* dynamic */
  371.     /* 
  372.      * allocate the devices -- we can't have them static, as the number
  373.      * can be specified at load time
  374.      */
  375.     scull_devices = kmalloc(scull_nr_devs * sizeof (Scull_Dev), GFP_KERNEL);
  376.     if (!scull_devices) {
  377.         result = -ENOMEM;
  378.         goto fail_malloc;
  379.     }
  380.     memset(scull_devices, 0, scull_nr_devs * sizeof (Scull_Dev));
  381.     for (i=0; i < scull_nr_devs; i++) {
  382.         scull_devices[i].quantum = scull_quantum;
  383.         scull_devices[i].qset = scull_qset;
  384.     }
  385.     /* At this point call the init function for any friend device */
  386.     if ( (result = scull_p_init()) )
  387.         goto fail_pipe;
  388.     if ( (result = scull_access_init()) )
  389.         goto fail_access;
  390.     /* ... */
  391. #ifndef SCULL_DEBUG
  392.     REGISTER_SYMTAB(NULL); /* otherwise, leave global symbols visible */
  393. #endif
  394. #ifdef SCULL_USE_PROC /* only when available */
  395.     /* this is the last line in init_module */
  396.     proc_register_dynamic(&proc_root, &scull_proc_entry);
  397. #endif
  398.     return 0; /* succeed */
  399.   fail_access: scull_p_cleanup();
  400.   fail_pipe:   kfree(scull_devices);
  401.   fail_malloc: unregister_chrdev(scull_major, "scull");
  402.     return result;
  403. }
  404. void cleanup_module(void)
  405. {
  406.     int i;
  407.     unregister_chrdev(scull_major, "scull");
  408. #ifdef SCULL_USE_PROC
  409.     proc_unregister(&proc_root, scull_proc_entry.low_ino);
  410. #endif
  411.     for (i=0; i<scull_nr_devs; i++)
  412.         scull_trim(scull_devices+i);
  413.     kfree(scull_devices);
  414.     /* and call the cleanup functions for friend devices */
  415.     scull_p_cleanup();
  416.     scull_access_cleanup();
  417. }