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

嵌入式Linux

开发平台:

C/C++

  1. /*
  2.  * main.c -- scullv: the scull-virtual char driver
  3.  *
  4.  * Tested with 2.0 on the x86, Sparc
  5.  *
  6.  * Mmap is not available on the Sparc, as pgd_offset etc. are not
  7.  * yet exported to modules. Nonetheless, I've been able to run this
  8.  * code by tweaking the loading mechanism.
  9.  */
  10. #ifndef __KERNEL__
  11. #  define __KERNEL__
  12. #endif
  13. #ifndef MODULE
  14. #  define MODULE
  15. #endif
  16. #define __NO_VERSION__ /* don't define kernel_verion in module.h */
  17. #include <linux/module.h>
  18. #include <linux/version.h>
  19. char kernel_version [] = UTS_RELEASE;
  20. #include <linux/kernel.h> /* printk() */
  21. #include <linux/malloc.h> /* kmalloc() */
  22. #include <linux/fs.h>     /* everything... */
  23. #include <linux/errno.h>  /* error codes */
  24. #include <linux/types.h>  /* size_t */
  25. #include <linux/proc_fs.h>
  26. #include <linux/fcntl.h>        /* O_ACCMODE */
  27. #include <asm/system.h>   /* cli(), *_flags */
  28. #include <asm/segment.h>  /* memcpy_* put_user and so on */
  29. #include "scullv.h"        /* local definitions */
  30. /*
  31.  * I don't use static symbols here, because register_symtab is called
  32.  */
  33. int scullv_major =   SCULLV_MAJOR;
  34. int scullv_devs =    SCULLV_DEVS;    /* number of bare scullv devices */
  35. int scullv_order =   SCULLV_ORDER;
  36. int scullv_qset =    SCULLV_QSET;
  37. ScullV_Dev *scullv_devices; /* allocated in init_module */
  38. int scullv_trim(ScullV_Dev *dev);
  39. #ifdef SCULLV_USE_PROC /* don't waste space if unused */
  40. /*
  41.  * The proc filesystem: function to read and entry
  42.  */
  43. int scullv_read_procmem(char *buf, char **start, off_t offset,
  44.                    int len, int unused)
  45. {
  46.     int i, j, order, qset;
  47.     ScullV_Dev *d;
  48.     #define LIMIT (PAGE_SIZE-80) /* don't print any more after this size */
  49.     len=0;
  50.     for(i=0; i<scullv_devs; i++) {
  51.         d=&scullv_devices[i];
  52.         order=d->order;  /* retrieve the features of each device */
  53.         qset=d->qset;
  54.         len += sprintf(buf+len,"nDevice %i: qset %i, order %i, sz %lin",
  55.                        i, qset, order, (long)(d->size));
  56.         for (; d; d=d->next) { /* scan the list */
  57.             if (len > LIMIT) return len;
  58.             len += sprintf(buf+len,"  item at %p, qset at %pn",d,d->data);
  59.             if (d->data && !d->next) /* dump only the last item - save space */
  60.                 for (j=0; j<qset; j++) {
  61.                     if (len > LIMIT) return len;
  62.                     if (d->data[j])
  63.                         len += sprintf(buf+len,"    % 4i:%8pn",j,d->data[j]);
  64.                 }
  65.         }
  66.     }
  67.     return len;
  68. }
  69. struct proc_dir_entry scullv_proc_entry = {
  70.         0,                 /* low_ino: the inode -- dynamic */
  71.         9, "scullvmem",    /* len of name and name */
  72.         S_IFREG | S_IRUGO, /* mode */
  73.         1, 0, 0,           /* nlinks, owner, group */
  74.         0, NULL,           /* size - unused; operations -- use default */
  75.         &scullv_read_procmem,   /* function used to read data */
  76.         /* nothing more */
  77.     };
  78. #endif /* SCULLV_USE_PROC */
  79. /*
  80.  * Open and close
  81.  */
  82. int scullv_open (struct inode *inode, struct file *filp)
  83. {
  84.     int num = MINOR(inode->i_rdev);
  85.     ScullV_Dev *dev; /* device information */
  86.     /*  check the device number */
  87.     if (num >= scullv_devs) return -ENODEV;
  88.     dev = &scullv_devices[num];
  89.     /* now trim to 0 the length of the device if open was write-only */
  90.     if ( (filp->f_flags & O_ACCMODE) == O_WRONLY)
  91.         scullv_trim(dev);
  92.     /* and use filp->private_data to point to the device data */
  93.     filp->private_data = dev;
  94.     MOD_INC_USE_COUNT;
  95.     return 0;          /* success */
  96. }
  97. void scullv_release (struct inode *inode, struct file *filp)
  98. {
  99.     MOD_DEC_USE_COUNT;
  100. }
  101. /*
  102.  * Follow the list 
  103.  */
  104. ScullV_Dev *scullv_follow(ScullV_Dev *dev, int n)
  105. {
  106.     while (n--) {
  107.         if (!dev->next) {
  108.             dev->next = kmalloc(sizeof(ScullV_Dev), GFP_KERNEL);
  109.             memset(dev->next, 0, sizeof(ScullV_Dev));
  110.         }
  111.         dev = dev->next;
  112.         continue;
  113.     }
  114.     return dev;
  115. }
  116. /*
  117.  * Data management: read and write
  118.  */
  119. read_write_t scullv_read (struct inode *inode, struct file *filp,
  120.                 char *buf, count_t count)
  121. {
  122.     ScullV_Dev *dev = filp->private_data; /* the first listitem */
  123.     int quantum = PAGE_SIZE << dev->order;
  124.     int qset = dev->qset;
  125.     int itemsize = quantum * qset; /* how many bytes in the listitem */
  126.     unsigned long f_pos = (unsigned long)(filp->f_pos);
  127.     int item, s_pos, q_pos, rest;
  128.     if (f_pos > dev->size)
  129.         return 0;
  130.     if (f_pos + count > dev->size)
  131.         count = dev->size - f_pos;
  132.     /* find listitem, qset index and offset in the quantum */
  133.     item = f_pos / itemsize;
  134.     rest = f_pos % itemsize;
  135.     s_pos = rest / quantum; q_pos = rest % quantum;
  136.     /* follow the list up to the right position (defined elsewhere) */
  137.     dev = scullv_follow(dev, item);
  138.     if (!dev->data)
  139.         return 0; /* don't fill holes */
  140.     if (!dev->data[s_pos])
  141.         return 0;
  142.     if (count > quantum - q_pos)
  143.         count = quantum - q_pos; /* read only up to the end of this quantum */
  144.     memcpy_tofs(buf, dev->data[s_pos]+q_pos, count);
  145.     filp->f_pos += count;
  146.     return count;
  147. }
  148. read_write_t scullv_write (struct inode *inode, struct file *filp,
  149.                 const char *buf, count_t count)
  150. {
  151.     ScullV_Dev *dev = filp->private_data;
  152.     ScullV_Dev *dptr;
  153.     int order = dev->order;
  154.     int quantum = PAGE_SIZE << order;
  155.     int qset = dev->qset;
  156.     int itemsize = quantum * qset;
  157.     unsigned long f_pos = (unsigned long)(filp->f_pos);
  158.     int item, s_pos, q_pos, rest;
  159.     /* find listitem, qset index and offset in the quantum */
  160.     item = f_pos / itemsize;
  161.     rest = f_pos % itemsize;
  162.     s_pos = rest / quantum; q_pos = rest % quantum;
  163.     /* follow the list up to the right position */
  164.     dptr = scullv_follow(dev, item);
  165.     if (!dptr->data) {
  166.         dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL);
  167.         if (!dptr->data)
  168.             return -ENOMEM;
  169.         memset(dptr->data, 0, qset * sizeof(char *));
  170.     }
  171.     /* Allocate a quantum using virtual addresses */
  172.     if (!dptr->data[s_pos]) {
  173.         dptr->data[s_pos] = (void *)vmalloc(PAGE_SIZE << order);
  174.         if (!dptr->data[s_pos])
  175.             return -ENOMEM;
  176.     }
  177.     if (count > quantum - q_pos)
  178.         count = quantum - q_pos; /* write only up to the end of this quantum */
  179.     memcpy_fromfs(dptr->data[s_pos]+q_pos, buf, count);
  180.     /* update the size */
  181.     if (dev->size < f_pos + count)
  182.         dev-> size = f_pos + count;
  183.     filp->f_pos += count;
  184.     return count;
  185. }
  186. /*
  187.  * The ioctl() implementation
  188.  */
  189. int scullv_ioctl (struct inode *inode, struct file *filp,
  190.                  unsigned int cmd, unsigned long arg)
  191. {
  192.     int err= 0, tmp, size = _IOC_SIZE(cmd);
  193.     /* don't even decode wrong cmds: better returning  EINVAL than EFAULT */
  194.     if (_IOC_TYPE(cmd) != SCULLV_IOC_MAGIC) return -EINVAL;
  195.     if (_IOC_NR(cmd) > SCULLV_IOC_MAXNR) return -EINVAL;
  196.     /*
  197.      * the type is a bitmask, and VERIFY_WRITE catches R/W
  198.      * transfers. Note that the type is user-oriented, while
  199.      * verify_area is kernel-oriented, so the concept of "read" and
  200.      * "write" is reversed
  201.      */
  202.     if (_IOC_TYPE(cmd) & _IOC_READ)
  203.         err = verify_area(VERIFY_WRITE, (void *)arg, size);
  204.     else if (_IOC_TYPE(cmd) & _IOC_WRITE)
  205.         err =  verify_area(VERIFY_READ, (void *)arg, size);
  206.     if (err) return err;
  207.     switch(cmd) {
  208. #ifdef SCULLV_DEBUG
  209.       case SCULLV_IOCHARDRESET:
  210.         /*
  211.          * reset the counter to 1, to allow unloading in case of problems.
  212.          * Use 1, not 0, because the invoking file is still to be closed.
  213.          */
  214.         mod_use_count_ = 1;
  215.         /* don't break: fall through */
  216. #endif
  217.       case SCULLV_IOCRESET:
  218.         scullv_order = SCULLV_ORDER;
  219.         scullv_qset = SCULLV_QSET;
  220.         break;
  221.         
  222.       case SCULLV_IOCSORDER: /* Set: arg points to the value */
  223.         scullv_order = get_user((int *)arg);
  224.         break;
  225.       case SCULLV_IOCTORDER: /* Tell: arg is the value */
  226.         scullv_order = arg;
  227.         break;
  228.       case SCULLV_IOCGORDER: /* Get: arg is pointer to result */
  229.         put_user(scullv_order, (int *)arg);
  230.         break;
  231.       case SCULLV_IOCQORDER: /* Query: return it (it's positive) */
  232.         return scullv_order;
  233.       case SCULLV_IOCXORDER: /* eXchange: use arg as pointer */
  234.         tmp = scullv_order;
  235.         scullv_order = get_user((int *)arg);
  236.         put_user(tmp, (int *)arg);
  237.         break;
  238.       case SCULLV_IOCHORDER: /* sHift: like Tell + Query */
  239.         tmp = scullv_order;
  240.         scullv_order = arg;
  241.         return tmp;
  242.         
  243.       case SCULLV_IOCSQSET:
  244.         scullv_qset = get_user((int *)arg);
  245.         break;
  246.       case SCULLV_IOCTQSET:
  247.         scullv_qset = arg;
  248.         break;
  249.       case SCULLV_IOCGQSET:
  250.         put_user(scullv_qset, (int *)arg);
  251.         break;
  252.       case SCULLV_IOCQQSET:
  253.         return scullv_qset;
  254.       case SCULLV_IOCXQSET:
  255.         tmp = scullv_qset;
  256.         scullv_qset = get_user((int *)arg);
  257.         put_user(tmp, (int *)arg);
  258.         break;
  259.       case SCULLV_IOCHQSET:
  260.         tmp = scullv_qset;
  261.         scullv_order = arg;
  262.         return tmp;
  263.       default:  /* redundant, as cmd was checked against MAXNR */
  264.         return -EINVAL;
  265.     }
  266.     return 0;
  267. }
  268. /*
  269.  * The "extended" operations -- only seek
  270.  */
  271. int scullv_lseek (struct inode *inode, struct file *filp,
  272.                  off_t off, int whence)
  273. {
  274.     ScullV_Dev *dev = filp->private_data;
  275.     long newpos;
  276.     switch(whence) {
  277.       case 0: /* SEEK_SET */
  278.         newpos = off;
  279.         break;
  280.       case 1: /* SEEK_CUR */
  281.         newpos = filp->f_pos + off;
  282.         break;
  283.       case 2: /* SEEK_END */
  284.         newpos = dev->size + off;
  285.         break;
  286.       default: /* can't happen */
  287.         return -EINVAL;
  288.     }
  289.     if (newpos<0) return -EINVAL;
  290.     filp->f_pos = newpos;
  291.     return newpos;
  292. }
  293. /*
  294.  * Mmap *is* available, but confined in a different file
  295.  */
  296. extern int scullv_mmap(struct inode *inode, struct file *filp,
  297.                        struct vm_area_struct *vma);
  298. /*
  299.  * The different file operations
  300.  */
  301. struct file_operations scullv_fops = {
  302.     scullv_lseek,
  303.     scullv_read,
  304.     scullv_write,
  305.     NULL,          /* scullv_readdir */
  306.     NULL,          /* scullv_select */
  307.     scullv_ioctl,
  308.     scullv_mmap,
  309.     scullv_open,
  310.     scullv_release,
  311.     NULL,          /* scullv_fsync */
  312.     NULL,          /* scullv_fasync */
  313.                    /* nothing more, fill with NULLs */
  314. };
  315. int scullv_trim(ScullV_Dev *dev)
  316. {
  317.     ScullV_Dev *next, *dptr;
  318.     int qset = dev->qset;   /* "dev" is not-null */
  319.     int i;
  320.     if (dev->vmas)
  321.         return -EBUSY;  /* don't free: it's mem-mapped */
  322.     for (dptr = dev; dptr; dptr = next) { /* all the list items */
  323.         if (dptr->data) {
  324.             /* Release the quantum-set */
  325.             for (i = 0; i < qset; i++)
  326.                 if (dptr->data[i])
  327.                     vfree(dptr->data[i]);
  328.             kfree(dptr->data);
  329.             dptr->data=NULL;
  330.         }
  331.         next=dptr->next;
  332.         if (dptr != dev) kfree(dptr); /* all of them but the first */
  333.     }
  334.     dev->size = 0;
  335.     dev->order = scullv_order;
  336.     dev->qset = scullv_qset;
  337.     dev->next = NULL;
  338.     return 0;
  339. }
  340. /*
  341.  * Finally, the module stuff
  342.  */
  343. int init_module(void)
  344. {
  345.     int result, i;
  346.     /*
  347.      * Register your major, and accept a dynamic number
  348.      */
  349.     result = register_chrdev(scullv_major, "scullv", &scullv_fops);
  350.     if (result < 0) return result;
  351.     if (scullv_major == 0) scullv_major = result; /* dynamic */
  352.     /* 
  353.      * allocate the devices -- we can't have them static, as the number
  354.      * can be specified at load time
  355.      */
  356.     scullv_devices = kmalloc(scullv_devs * sizeof (ScullV_Dev), GFP_KERNEL);
  357.     if (!scullv_devices) {
  358.         result = -ENOMEM;
  359.         goto fail_malloc;
  360.     }
  361.     memset(scullv_devices, 0, scullv_devs * sizeof (ScullV_Dev));
  362.     for (i=0; i < scullv_devs; i++) {
  363.         scullv_devices[i].order = scullv_order;
  364.         scullv_devices[i].qset = scullv_qset;
  365.     }
  366. #ifdef SCULLV_USE_PROC /* only when available */
  367.     /* this is the last line in init_module */
  368.     proc_register_dynamic(&proc_root, &scullv_proc_entry);
  369. #endif
  370.     return 0; /* succeed */
  371.   fail_malloc: unregister_chrdev(scullv_major, "scullv");
  372.     return result;
  373. }
  374. void cleanup_module(void)
  375. {
  376.     int i;
  377.     unregister_chrdev(scullv_major, "scullv");
  378. #ifdef SCULLV_USE_PROC
  379.     proc_unregister(&proc_root, scullv_proc_entry.low_ino);
  380. #endif
  381.     for (i=0; i<scullv_devs; i++)
  382.         scullv_trim(scullv_devices+i);
  383.     kfree(scullv_devices);
  384. }