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

嵌入式Linux

开发平台:

C/C++

  1. /* -*- C -*-
  2.  * main.c -- the bare scullc char module
  3.  *
  4.  * $Id: _main.c.in,v 1.17 2001/03/16 20:27:26 corbet Exp $
  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. #include <linux/config.h>
  17. #include <linux/module.h>
  18. /* modversion stuff: no #ifdef needed if 2.0 support is not needed */
  19. #ifdef CONFIG_MODVERSIONS
  20. #  include <linux/modversions.h>
  21. #endif
  22. #include <linux/kernel.h> /* printk() */
  23. #include <linux/malloc.h> /* kmalloc() */
  24. #include <linux/fs.h>     /* everything... */
  25. #include <linux/errno.h>  /* error codes */
  26. #include <linux/types.h>  /* size_t */
  27. #include <linux/proc_fs.h>
  28. #include <linux/fcntl.h>        /* O_ACCMODE */
  29. #include <asm/system.h>   /* cli(), *_flags */
  30. #include "scullc.h"        /* local definitions */
  31. /* Kmem caches were not available in 2.0. Disallow compilation in that case */
  32. #ifdef LINUX_20
  33. #  error "Kmem_cache functions are not available in Linux-2.0"
  34. #else
  35. #  if defined(LINUX_22) && LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
  36. #    error "This module needs at least 2.2.18 to run"
  37. #  endif
  38. /* leave the #ifdef open, so 2.0 won't get other errors */
  39. /*
  40.  * I don't use static symbols here, because register_symtab is called
  41.  */
  42. int scullc_major =   SCULLC_MAJOR;
  43. int scullc_devs =    SCULLC_DEVS;    /* number of bare scullc devices */
  44. int scullc_qset =    SCULLC_QSET;
  45. int scullc_quantum = SCULLC_QUANTUM;
  46. MODULE_PARM(scullc_major, "i");
  47. MODULE_PARM(scullc_devs, "i");
  48. MODULE_PARM(scullc_qset, "i");
  49. MODULE_PARM(scullc_quantum, "i");
  50. MODULE_AUTHOR("Alessandro Rubini");
  51. ScullC_Dev *scullc_devices; /* allocated in scullc_init */
  52. int scullc_trim(ScullC_Dev *dev);
  53. /* declare one cache pointer: use it for all devices */
  54. kmem_cache_t *scullc_cache;
  55. #ifdef SCULLC_USE_PROC /* don't waste space if unused */
  56. /*
  57.  * The proc filesystem: function to read and entry
  58.  */
  59. void scullc_proc_offset(char *buf, char **start, off_t *offset, int *len)
  60. {
  61.     if (*offset == 0)
  62.         return;
  63.     if (*offset >= *len) {      /* Not there yet */
  64.         *offset -= *len;
  65.         *len = 0;
  66.     }
  67.     else {                      /* We're into the interesting stuff now */
  68.         *start = buf + *offset;
  69.         *offset = 0;
  70.     }
  71. }
  72. int scullc_read_procmem(char *buf, char **start, off_t offset,
  73.                    int count, int *eof, void *data)
  74. {
  75.     int i, j, quantum, qset, len = 0;
  76.     int limit = count - 80; /* Don't print more than this */
  77.     ScullC_Dev *d;
  78.     *start = buf;
  79.     for(i=0; i<scullc_devs; i++) {
  80.         d=&scullc_devices[i];
  81.         if (down_interruptible (&d->sem))
  82.                 return -ERESTARTSYS;
  83.         qset=d->qset;  /* retrieve the features of each device */
  84.         quantum=d->quantum;
  85.         len += sprintf(buf+len,"nDevice %i: qset %i, quantum %i, sz %lin",
  86.                        i, qset, quantum, (long)(d->size));
  87.         for (; d; d=d->next) { /* scan the list */
  88.             len += sprintf(buf+len,"  item at %p, qset at %pn",d,d->data);
  89.             scullc_proc_offset (buf, start, &offset, &len);
  90.             if (len > limit)
  91.                 goto out;
  92.             if (d->data && !d->next) /* dump only the last item - save space */
  93.                 for (j=0; j<qset; j++) {
  94.                     if (d->data[j])
  95.                         len += sprintf(buf+len,"    % 4i:%8pn",j,d->data[j]);
  96.                     scullc_proc_offset (buf, start, &offset, &len);
  97.                     if (len > limit)
  98.                         goto out;
  99. }
  100.         }
  101.     out:
  102.         up (&scullc_devices[i].sem);
  103. if (len > limit)
  104.     break;
  105.     }
  106.     *eof = 1;
  107.     return len;
  108. }
  109. #ifdef USE_PROC_REGISTER
  110. static int scullc_get_info (char *buf, char **start, off_t offset,
  111.                 int len, int unused)
  112. {
  113.     int eof = 0;
  114.     return scullc_read_procmem(buf, start, offset, len, &eof, NULL);
  115. }
  116. struct proc_dir_entry scullc_proc_entry = {
  117.         0,                 /* low_ino: the inode -- dynamic */
  118.         9, "scullcmem",     /* len of name and name */
  119.         S_IFREG | S_IRUGO, /* mode */
  120.         1, 0, 0,           /* nlinks, owner, group */
  121.         0, NULL,           /* size - unused; operations -- use default */
  122.         scullc_get_info,   /* function used to read data */
  123.         /* nothing more */
  124.     };
  125. static inline void create_proc_read_entry (const char *name, mode_t mode,
  126.                 struct proc_dir_entry *base, void *read_func, void *data)
  127. {
  128.     proc_register_dynamic (&proc_root, &scullc_proc_entry);
  129. }
  130. static inline void remove_proc_entry (char *name, void *parent)
  131. {
  132.     proc_unregister (&proc_root, scullc_proc_entry.low_ino);
  133. }
  134. #endif /* USE_PROC_REGISTER */
  135. #endif /* SCULLC_USE_PROC */
  136. /*
  137.  * Open and close
  138.  */
  139. int scullc_open (struct inode *inode, struct file *filp)
  140. {
  141.     int num = MINOR(inode->i_rdev);
  142.     ScullC_Dev *dev; /* device information */
  143.     /*  check the device number */
  144.     if (num >= scullc_devs) return -ENODEV;
  145.     dev = &scullc_devices[num];
  146.     /* now trim to 0 the length of the device if open was write-only */
  147.      if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
  148.         if (down_interruptible (&dev->sem))
  149.             return -ERESTARTSYS;
  150.         scullc_trim(dev); /* ignore errors */
  151.         up (&dev->sem);
  152.     }
  153.     /* and use filp->private_data to point to the device data */
  154.     filp->private_data = dev;
  155.     MOD_INC_USE_COUNT;
  156.     return 0;          /* success */
  157. }
  158. int scullc_release (struct inode *inode, struct file *filp)
  159. {
  160.     MOD_DEC_USE_COUNT;
  161.     return 0;
  162. }
  163. /*
  164.  * Follow the list 
  165.  */
  166. ScullC_Dev *scullc_follow(ScullC_Dev *dev, int n)
  167. {
  168.     while (n--) {
  169.         if (!dev->next) {
  170.             dev->next = kmalloc(sizeof(ScullC_Dev), GFP_KERNEL);
  171.             memset(dev->next, 0, sizeof(ScullC_Dev));
  172.         }
  173.         dev = dev->next;
  174.         continue;
  175.     }
  176.     return dev;
  177. }
  178. /*
  179.  * Data management: read and write
  180.  */
  181. ssize_t scullc_read (struct file *filp, char *buf, size_t count,
  182.                 loff_t *f_pos)
  183. {
  184.     ScullC_Dev *dev = filp->private_data; /* the first listitem */
  185.     ScullC_Dev *dptr;
  186.     int quantum = dev->quantum;
  187.     int qset = dev->qset;
  188.     int itemsize = quantum * qset; /* how many bytes in the listitem */
  189.     int item, s_pos, q_pos, rest;
  190.     ssize_t retval = 0;
  191.     if (down_interruptible (&dev->sem))
  192.             return -ERESTARTSYS;
  193.     if (*f_pos > dev->size) 
  194.         goto nothing;
  195.     if (*f_pos + count > dev->size)
  196.         count = dev->size - *f_pos;
  197.     /* find listitem, qset index, and offset in the quantum */
  198.     item = ((long) *f_pos) / itemsize;
  199.     rest = ((long) *f_pos) % itemsize;
  200.     s_pos = rest / quantum; q_pos = rest % quantum;
  201.     /* follow the list up to the right position (defined elsewhere) */
  202.     dptr = scullc_follow(dev, item);
  203.     if (!dptr->data)
  204.         goto nothing; /* don't fill holes */
  205.     if (!dptr->data[s_pos])
  206.         goto nothing;
  207.     if (count > quantum - q_pos)
  208.         count = quantum - q_pos; /* read only up to the end of this quantum */
  209.     if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) {
  210.         retval = -EFAULT;
  211.         goto nothing;
  212.     }
  213.     up (&dev->sem);
  214.     *f_pos += count;
  215.     return count;
  216.   nothing:
  217.     up (&dev->sem);
  218.     return retval;
  219. }
  220. ssize_t scullc_write (struct file *filp, const char *buf, size_t count,
  221.                 loff_t *f_pos)
  222. {
  223.     ScullC_Dev *dev = filp->private_data;
  224.     ScullC_Dev *dptr;
  225.     int quantum = dev->quantum;
  226.     int qset = dev->qset;
  227.     int itemsize = quantum * qset;
  228.     int item, s_pos, q_pos, rest;
  229.     ssize_t retval = -ENOMEM; /* our most likely error */
  230.     if (down_interruptible (&dev->sem))
  231.             return -ERESTARTSYS;
  232.     /* find listitem, qset index and offset in the quantum */
  233.     item = ((long) *f_pos) / itemsize;
  234.     rest = ((long) *f_pos) % itemsize;
  235.     s_pos = rest / quantum; q_pos = rest % quantum;
  236.     /* follow the list up to the right position */
  237.     dptr = scullc_follow(dev, item);
  238.     if (!dptr->data) {
  239.         dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL);
  240.         if (!dptr->data)
  241.             goto nomem;
  242.         memset(dptr->data, 0, qset * sizeof(char *));
  243.     }
  244.     /* Allocate a quantum using the memory cache */
  245.     if (!dptr->data[s_pos]) {
  246.         dptr->data[s_pos] =
  247.     kmem_cache_alloc(scullc_cache, GFP_KERNEL);
  248.         if (!dptr->data[s_pos])
  249.             goto nomem;
  250.         memset(dptr->data[s_pos], 0, scullc_quantum);
  251.     }
  252.     if (count > quantum - q_pos)
  253.         count = quantum - q_pos; /* write only up to the end of this quantum */
  254.     if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) {
  255.         retval = -EFAULT;
  256.         goto nomem;
  257.     }
  258.     *f_pos += count;
  259.  
  260.     /* update the size */
  261.     if (dev->size < *f_pos)
  262.         dev->size = *f_pos;
  263.     up (&dev->sem);
  264.     return count;
  265.   nomem:
  266.     up (&dev->sem);
  267.     return retval;
  268. }
  269. /*
  270.  * The ioctl() implementation
  271.  */
  272. int scullc_ioctl (struct inode *inode, struct file *filp,
  273.                  unsigned int cmd, unsigned long arg)
  274. {
  275.     int err= 0, ret = 0, tmp;
  276.     /* don't even decode wrong cmds: better returning  ENOTTY than EFAULT */
  277.     if (_IOC_TYPE(cmd) != SCULLC_IOC_MAGIC) return -ENOTTY;
  278.     if (_IOC_NR(cmd) > SCULLC_IOC_MAXNR) return -ENOTTY;
  279.     /*
  280.      * the type is a bitmask, and VERIFY_WRITE catches R/W
  281.      * transfers. Note that the type is user-oriented, while
  282.      * verify_area is kernel-oriented, so the concept of "read" and
  283.      * "write" is reversed
  284.      */
  285.     if (_IOC_DIR(cmd) & _IOC_READ)
  286.         err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
  287.     else if (_IOC_DIR(cmd) & _IOC_WRITE)
  288.         err =  !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
  289.     if (err) return -EFAULT;
  290.     switch(cmd) {
  291.       case SCULLC_IOCRESET:
  292.         scullc_qset = SCULLC_QSET;
  293.         scullc_quantum = SCULLC_QUANTUM;
  294.         break;
  295.         
  296.       case SCULLC_IOCSQUANTUM: /* Set: arg points to the value */
  297.         ret = __GET_USER(scullc_quantum, (int *) arg);
  298.         break;
  299.       case SCULLC_IOCTQUANTUM: /* Tell: arg is the value */
  300.         scullc_quantum = arg;
  301.         break;
  302.       case SCULLC_IOCGQUANTUM: /* Get: arg is pointer to result */
  303.         ret = __PUT_USER (scullc_quantum, (int *) arg);
  304.         break;
  305.       case SCULLC_IOCQQUANTUM: /* Query: return it (it's positive) */
  306.         return scullc_quantum;
  307.       case SCULLC_IOCXQUANTUM: /* eXchange: use arg as pointer */
  308.         tmp = scullc_quantum;
  309.         ret = __GET_USER(scullc_quantum, (int *) arg);
  310.         if (ret == 0)
  311.             ret = __PUT_USER(tmp, (int *) arg);
  312.         break;
  313.       case SCULLC_IOCHQUANTUM: /* sHift: like Tell + Query */
  314.         tmp = scullc_quantum;
  315.         scullc_quantum = arg;
  316.         return tmp;
  317.       case SCULLC_IOCSQSET:
  318.         ret = __GET_USER(scullc_qset, (int *) arg);
  319.         break;
  320.       case SCULLC_IOCTQSET:
  321.         scullc_qset = arg;
  322.         break;
  323.       case SCULLC_IOCGQSET:
  324.         ret = __PUT_USER(scullc_qset, (int *)arg);
  325.         break;
  326.       case SCULLC_IOCQQSET:
  327.         return scullc_qset;
  328.       case SCULLC_IOCXQSET:
  329.         tmp = scullc_qset;
  330.         ret = __GET_USER(scullc_qset, (int *) arg);
  331.         if (ret == 0)
  332.             ret = __PUT_USER(tmp, (int *)arg);
  333.         break;
  334.       case SCULLC_IOCHQSET:
  335.         tmp = scullc_qset;
  336.         scullc_qset = arg;
  337.         return tmp;
  338.       default:  /* redundant, as cmd was checked against MAXNR */
  339.         return -ENOTTY;
  340.     }
  341.     return ret;
  342. }
  343. /*
  344.  * The "extended" operations
  345.  */
  346. loff_t scullc_llseek (struct file *filp, loff_t off, int whence)
  347. {
  348.     ScullC_Dev *dev = filp->private_data;
  349.     long newpos;
  350.     switch(whence) {
  351.       case 0: /* SEEK_SET */
  352.         newpos = off;
  353.         break;
  354.       case 1: /* SEEK_CUR */
  355.         newpos = filp->f_pos + off;
  356.         break;
  357.       case 2: /* SEEK_END */
  358.         newpos = dev->size + off;
  359.         break;
  360.       default: /* can't happen */
  361.         return -EINVAL;
  362.     }
  363.     if (newpos<0) return -EINVAL;
  364.     filp->f_pos = newpos;
  365.     return newpos;
  366. }
  367.  
  368. /*
  369.  * The 2.0 wrappers
  370.  */
  371. #ifdef LINUX_20
  372. int scullc_lseek_20 (struct inode *ino, struct file *f,
  373.                 off_t offset, int whence)
  374. {
  375.     return (int)scullc_llseek(f, offset, whence);
  376. }
  377. int scullc_read_20 (struct inode *ino, struct file *f, char *buf, int count)
  378. {
  379.     return (int)scullc_read(f, buf, count, &f->f_pos);
  380. }
  381. int scullc_write_20 (struct inode *ino, struct file *f, const char *b, int c)
  382. {
  383.     return (int)scullc_write(f, b, c, &f->f_pos);
  384. }
  385. void scullc_release_20 (struct inode *ino, struct file *f)
  386. {
  387.     scullc_release(ino, f);
  388. }
  389. #define scullc_llseek scullc_lseek_20
  390. #define scullc_read scullc_read_20
  391. #define scullc_write scullc_write_20
  392. #define scullc_release scullc_release_20
  393. #define llseek lseek
  394. #endif /* LINUX_20 */
  395. /*
  396.  * The fops
  397.  */
  398. struct file_operations scullc_fops = {
  399.     llseek: scullc_llseek,
  400.     read: scullc_read,
  401.     write: scullc_write,
  402.     ioctl: scullc_ioctl,
  403.     open: scullc_open,
  404.     release: scullc_release,
  405. };
  406. int scullc_trim(ScullC_Dev *dev)
  407. {
  408.     ScullC_Dev *next, *dptr;
  409.     int qset = dev->qset;   /* "dev" is not-null */
  410.     int i;
  411.     if (dev->vmas) /* don't trim: there are active mappings */
  412.         return -EBUSY;
  413.     for (dptr = dev; dptr; dptr = next) { /* all the list items */
  414.         if (dptr->data) {
  415.             for (i = 0; i < qset; i++)
  416.                 if (dptr->data[i])
  417.                     kmem_cache_free(scullc_cache, dptr->data[i]);
  418.             kfree(dptr->data);
  419.             dptr->data=NULL;
  420.         }
  421.         next=dptr->next;
  422.         if (dptr != dev) kfree(dptr); /* all of them but the first */
  423.     }
  424.     dev->size = 0;
  425.     dev->qset = scullc_qset;
  426.     dev->quantum = scullc_quantum;
  427.     dev->next = NULL;
  428.     return 0;
  429. }
  430. /*
  431.  * Finally, the module stuff
  432.  */
  433. int scullc_init(void)
  434. {
  435.     int result, i;
  436.     SET_MODULE_OWNER(&scullc_fops);
  437.     /*
  438.      * Register your major, and accept a dynamic number
  439.      */
  440.     result = register_chrdev(scullc_major, "scullc", &scullc_fops);
  441.     if (result < 0) return result;
  442.     if (scullc_major == 0) scullc_major = result; /* dynamic */
  443.     /* 
  444.      * allocate the devices -- we can't have them static, as the number
  445.      * can be specified at load time
  446.      */
  447.     scullc_devices = kmalloc(scullc_devs * sizeof (ScullC_Dev), GFP_KERNEL);
  448.     if (!scullc_devices) {
  449.         result = -ENOMEM;
  450.         goto fail_malloc;
  451.     }
  452.     memset(scullc_devices, 0, scullc_devs * sizeof (ScullC_Dev));
  453.     for (i=0; i < scullc_devs; i++) {
  454.         scullc_devices[i].quantum = scullc_quantum;
  455.         scullc_devices[i].qset = scullc_qset;
  456.         sema_init (&scullc_devices[i].sem, 1);
  457.     }
  458.     /* init_module: create a cache for our quanta */
  459.     scullc_cache =
  460. kmem_cache_create("scullc", scullc_quantum,
  461.   0, SLAB_HWCACHE_ALIGN,
  462.   NULL, NULL); /* no ctor/dtor */
  463.     if (!scullc_cache) {
  464.         result = -ENOMEM;
  465.         goto fail_malloc2;
  466.     }
  467. #ifdef SCULLC_USE_PROC /* only when available */
  468.     create_proc_read_entry("scullcmem", 0, NULL, scullc_read_procmem, NULL);
  469. #endif
  470.     return 0; /* succeed */
  471.   fail_malloc2:
  472.     kfree(scullc_devices);
  473.   fail_malloc:
  474.     unregister_chrdev(scullc_major, "scullc");
  475.     return result;
  476. }
  477. void scullc_cleanup(void)
  478. {
  479.     int i;
  480.     unregister_chrdev(scullc_major, "scullc");
  481. #ifdef SCULLC_USE_PROC
  482.     remove_proc_entry("scullcmem", 0);
  483. #endif
  484.     for (i=0; i<scullc_devs; i++)
  485.         scullc_trim(scullc_devices+i);
  486.     kfree(scullc_devices);
  487.     /* cleanup_module: release the cache of our quanta */
  488.     kmem_cache_destroy(scullc_cache);
  489. }
  490. #endif /* not linux-2.0 */
  491. module_init(scullc_init);
  492. module_exit(scullc_cleanup);