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

嵌入式Linux

开发平台:

C/C++

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