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

嵌入式Linux

开发平台:

C/C++

  1. /* -*- C -*-
  2.  * main.c -- the bare scullv 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 "scullv.h"        /* local definitions */
  31. /*
  32.  * I don't use static symbols here, because register_symtab is called
  33.  */
  34. int scullv_major =   SCULLV_MAJOR;
  35. int scullv_devs =    SCULLV_DEVS;    /* number of bare scullv devices */
  36. int scullv_qset =    SCULLV_QSET;
  37. int scullv_order =   SCULLV_ORDER;
  38. MODULE_PARM(scullv_major, "i");
  39. MODULE_PARM(scullv_devs, "i");
  40. MODULE_PARM(scullv_qset, "i");
  41. MODULE_PARM(scullv_order, "i");
  42. MODULE_AUTHOR("Alessandro Rubini");
  43. ScullV_Dev *scullv_devices; /* allocated in scullv_init */
  44. int scullv_trim(ScullV_Dev *dev);
  45. #ifdef SCULLV_USE_PROC /* don't waste space if unused */
  46. /*
  47.  * The proc filesystem: function to read and entry
  48.  */
  49. void scullv_proc_offset(char *buf, char **start, off_t *offset, int *len)
  50. {
  51.     if (*offset == 0)
  52.         return;
  53.     if (*offset >= *len) {      /* Not there yet */
  54.         *offset -= *len;
  55.         *len = 0;
  56.     }
  57.     else {                      /* We're into the interesting stuff now */
  58.         *start = buf + *offset;
  59.         *offset = 0;
  60.     }
  61. }
  62. int scullv_read_procmem(char *buf, char **start, off_t offset,
  63.                    int count, int *eof, void *data)
  64. {
  65.     int i, j, order, qset, len = 0;
  66.     int limit = count - 80; /* Don't print more than this */
  67.     ScullV_Dev *d;
  68.     *start = buf;
  69.     for(i=0; i<scullv_devs; i++) {
  70.         d=&scullv_devices[i];
  71.         if (down_interruptible (&d->sem))
  72.                 return -ERESTARTSYS;
  73.         qset=d->qset;  /* retrieve the features of each device */
  74.         order=d->order;
  75.         len += sprintf(buf+len,"nDevice %i: qset %i, order %i, sz %lin",
  76.                        i, qset, order, (long)(d->size));
  77.         for (; d; d=d->next) { /* scan the list */
  78.             len += sprintf(buf+len,"  item at %p, qset at %pn",d,d->data);
  79.             scullv_proc_offset (buf, start, &offset, &len);
  80.             if (len > limit)
  81.                 goto out;
  82.             if (d->data && !d->next) /* dump only the last item - save space */
  83.                 for (j=0; j<qset; j++) {
  84.                     if (d->data[j])
  85.                         len += sprintf(buf+len,"    % 4i:%8pn",j,d->data[j]);
  86.                     scullv_proc_offset (buf, start, &offset, &len);
  87.                     if (len > limit)
  88.                         goto out;
  89. }
  90.         }
  91.     out:
  92.         up (&scullv_devices[i].sem);
  93. if (len > limit)
  94.     break;
  95.     }
  96.     *eof = 1;
  97.     return len;
  98. }
  99. #ifdef USE_PROC_REGISTER
  100. static int scullv_get_info (char *buf, char **start, off_t offset,
  101.                 int len, int unused)
  102. {
  103.     int eof = 0;
  104.     return scullv_read_procmem(buf, start, offset, len, &eof, NULL);
  105. }
  106. struct proc_dir_entry scullv_proc_entry = {
  107.         0,                 /* low_ino: the inode -- dynamic */
  108.         9, "scullvmem",     /* len of name and name */
  109.         S_IFREG | S_IRUGO, /* mode */
  110.         1, 0, 0,           /* nlinks, owner, group */
  111.         0, NULL,           /* size - unused; operations -- use default */
  112.         scullv_get_info,   /* function used to read data */
  113.         /* nothing more */
  114.     };
  115. static inline void create_proc_read_entry (const char *name, mode_t mode,
  116.                 struct proc_dir_entry *base, void *read_func, void *data)
  117. {
  118.     proc_register_dynamic (&proc_root, &scullv_proc_entry);
  119. }
  120. static inline void remove_proc_entry (char *name, void *parent)
  121. {
  122.     proc_unregister (&proc_root, scullv_proc_entry.low_ino);
  123. }
  124. #endif /* USE_PROC_REGISTER */
  125. #endif /* SCULLV_USE_PROC */
  126. /*
  127.  * Open and close
  128.  */
  129. int scullv_open (struct inode *inode, struct file *filp)
  130. {
  131.     int num = MINOR(inode->i_rdev);
  132.     ScullV_Dev *dev; /* device information */
  133.     /*  check the device number */
  134.     if (num >= scullv_devs) return -ENODEV;
  135.     dev = &scullv_devices[num];
  136.     /* now trim to 0 the length of the device if open was write-only */
  137.      if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
  138.         if (down_interruptible (&dev->sem))
  139.             return -ERESTARTSYS;
  140.         scullv_trim(dev); /* ignore errors */
  141.         up (&dev->sem);
  142.     }
  143.     /* and use filp->private_data to point to the device data */
  144.     filp->private_data = dev;
  145.     MOD_INC_USE_COUNT;
  146.     return 0;          /* success */
  147. }
  148. int scullv_release (struct inode *inode, struct file *filp)
  149. {
  150.     MOD_DEC_USE_COUNT;
  151.     return 0;
  152. }
  153. /*
  154.  * Follow the list 
  155.  */
  156. ScullV_Dev *scullv_follow(ScullV_Dev *dev, int n)
  157. {
  158.     while (n--) {
  159.         if (!dev->next) {
  160.             dev->next = kmalloc(sizeof(ScullV_Dev), GFP_KERNEL);
  161.             memset(dev->next, 0, sizeof(ScullV_Dev));
  162.         }
  163.         dev = dev->next;
  164.         continue;
  165.     }
  166.     return dev;
  167. }
  168. /*
  169.  * Data management: read and write
  170.  */
  171. ssize_t scullv_read (struct file *filp, char *buf, size_t count,
  172.                 loff_t *f_pos)
  173. {
  174.     ScullV_Dev *dev = filp->private_data; /* the first listitem */
  175.     ScullV_Dev *dptr;
  176.     int quantum = PAGE_SIZE << dev->order;
  177.     int qset = dev->qset;
  178.     int itemsize = quantum * qset; /* how many bytes in the listitem */
  179.     int item, s_pos, q_pos, rest;
  180.     ssize_t retval = 0;
  181.     if (down_interruptible (&dev->sem))
  182.             return -ERESTARTSYS;
  183.     if (*f_pos > dev->size) 
  184.         goto nothing;
  185.     if (*f_pos + count > dev->size)
  186.         count = dev->size - *f_pos;
  187.     /* find listitem, qset index, and offset in the quantum */
  188.     item = ((long) *f_pos) / itemsize;
  189.     rest = ((long) *f_pos) % itemsize;
  190.     s_pos = rest / quantum; q_pos = rest % quantum;
  191.     /* follow the list up to the right position (defined elsewhere) */
  192.     dptr = scullv_follow(dev, item);
  193.     if (!dptr->data)
  194.         goto nothing; /* don't fill holes */
  195.     if (!dptr->data[s_pos])
  196.         goto nothing;
  197.     if (count > quantum - q_pos)
  198.         count = quantum - q_pos; /* read only up to the end of this quantum */
  199.     if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) {
  200.         retval = -EFAULT;
  201.         goto nothing;
  202.     }
  203.     up (&dev->sem);
  204.     *f_pos += count;
  205.     return count;
  206.   nothing:
  207.     up (&dev->sem);
  208.     return retval;
  209. }
  210. ssize_t scullv_write (struct file *filp, const char *buf, size_t count,
  211.                 loff_t *f_pos)
  212. {
  213.     ScullV_Dev *dev = filp->private_data;
  214.     ScullV_Dev *dptr;
  215.     int quantum = PAGE_SIZE << dev->order;
  216.     int qset = dev->qset;
  217.     int itemsize = quantum * qset;
  218.     int item, s_pos, q_pos, rest;
  219.     ssize_t retval = -ENOMEM; /* our most likely error */
  220.     if (down_interruptible (&dev->sem))
  221.             return -ERESTARTSYS;
  222.     /* find listitem, qset index and offset in the quantum */
  223.     item = ((long) *f_pos) / itemsize;
  224.     rest = ((long) *f_pos) % itemsize;
  225.     s_pos = rest / quantum; q_pos = rest % quantum;
  226.     /* follow the list up to the right position */
  227.     dptr = scullv_follow(dev, item);
  228.     if (!dptr->data) {
  229.         dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL);
  230.         if (!dptr->data)
  231.             goto nomem;
  232.         memset(dptr->data, 0, qset * sizeof(char *));
  233.     }
  234.     /* Allocate a quantum using virtual addresses */
  235.     if (!dptr->data[s_pos]) {
  236.         dptr->data[s_pos] =
  237.     (void *)vmalloc(PAGE_SIZE << dptr->order);
  238.         if (!dptr->data[s_pos])
  239.             goto nomem;
  240.         memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order);
  241.     }
  242.     if (count > quantum - q_pos)
  243.         count = quantum - q_pos; /* write only up to the end of this quantum */
  244.     if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) {
  245.         retval = -EFAULT;
  246.         goto nomem;
  247.     }
  248.     *f_pos += count;
  249.  
  250.     /* update the size */
  251.     if (dev->size < *f_pos)
  252.         dev->size = *f_pos;
  253.     up (&dev->sem);
  254.     return count;
  255.   nomem:
  256.     up (&dev->sem);
  257.     return retval;
  258. }
  259. /*
  260.  * The ioctl() implementation
  261.  */
  262. int scullv_ioctl (struct inode *inode, struct file *filp,
  263.                  unsigned int cmd, unsigned long arg)
  264. {
  265.     int err= 0, ret = 0, tmp;
  266.     /* don't even decode wrong cmds: better returning  ENOTTY than EFAULT */
  267.     if (_IOC_TYPE(cmd) != SCULLV_IOC_MAGIC) return -ENOTTY;
  268.     if (_IOC_NR(cmd) > SCULLV_IOC_MAXNR) return -ENOTTY;
  269.     /*
  270.      * the type is a bitmask, and VERIFY_WRITE catches R/W
  271.      * transfers. Note that the type is user-oriented, while
  272.      * verify_area is kernel-oriented, so the concept of "read" and
  273.      * "write" is reversed
  274.      */
  275.     if (_IOC_DIR(cmd) & _IOC_READ)
  276.         err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
  277.     else if (_IOC_DIR(cmd) & _IOC_WRITE)
  278.         err =  !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
  279.     if (err) return -EFAULT;
  280.     switch(cmd) {
  281.       case SCULLV_IOCRESET:
  282.         scullv_qset = SCULLV_QSET;
  283.         scullv_order = SCULLV_ORDER;
  284.         break;
  285.         
  286.       case SCULLV_IOCSORDER: /* Set: arg points to the value */
  287.         ret = __GET_USER(scullv_order, (int *) arg);
  288.         break;
  289.       case SCULLV_IOCTORDER: /* Tell: arg is the value */
  290.         scullv_order = arg;
  291.         break;
  292.       case SCULLV_IOCGORDER: /* Get: arg is pointer to result */
  293.         ret = __PUT_USER (scullv_order, (int *) arg);
  294.         break;
  295.       case SCULLV_IOCQORDER: /* Query: return it (it's positive) */
  296.         return scullv_order;
  297.       case SCULLV_IOCXORDER: /* eXchange: use arg as pointer */
  298.         tmp = scullv_order;
  299.         ret = __GET_USER(scullv_order, (int *) arg);
  300.         if (ret == 0)
  301.             ret = __PUT_USER(tmp, (int *) arg);
  302.         break;
  303.       case SCULLV_IOCHORDER: /* sHift: like Tell + Query */
  304.         tmp = scullv_order;
  305.         scullv_order = arg;
  306.         return tmp;
  307.         
  308.       case SCULLV_IOCSQSET:
  309.         ret = __GET_USER(scullv_qset, (int *) arg);
  310.         break;
  311.       case SCULLV_IOCTQSET:
  312.         scullv_qset = arg;
  313.         break;
  314.       case SCULLV_IOCGQSET:
  315.         ret = __PUT_USER(scullv_qset, (int *)arg);
  316.         break;
  317.       case SCULLV_IOCQQSET:
  318.         return scullv_qset;
  319.       case SCULLV_IOCXQSET:
  320.         tmp = scullv_qset;
  321.         ret = __GET_USER(scullv_qset, (int *) arg);
  322.         if (ret == 0)
  323.             ret = __PUT_USER(tmp, (int *)arg);
  324.         break;
  325.       case SCULLV_IOCHQSET:
  326.         tmp = scullv_qset;
  327.         scullv_qset = arg;
  328.         return tmp;
  329.       default:  /* redundant, as cmd was checked against MAXNR */
  330.         return -ENOTTY;
  331.     }
  332.     return ret;
  333. }
  334. /*
  335.  * The "extended" operations
  336.  */
  337. loff_t scullv_llseek (struct file *filp, loff_t off, int whence)
  338. {
  339.     ScullV_Dev *dev = filp->private_data;
  340.     long newpos;
  341.     switch(whence) {
  342.       case 0: /* SEEK_SET */
  343.         newpos = off;
  344.         break;
  345.       case 1: /* SEEK_CUR */
  346.         newpos = filp->f_pos + off;
  347.         break;
  348.       case 2: /* SEEK_END */
  349.         newpos = dev->size + off;
  350.         break;
  351.       default: /* can't happen */
  352.         return -EINVAL;
  353.     }
  354.     if (newpos<0) return -EINVAL;
  355.     filp->f_pos = newpos;
  356.     return newpos;
  357. }
  358.  
  359. /*
  360.  * Mmap *is* available, but confined in a different file
  361.  */
  362. #ifndef LINUX_20
  363. extern int scullv_mmap(struct file *filp, struct vm_area_struct *vma);
  364. #else
  365. extern int scullv_mmap(struct inode *inode, struct file *filp,
  366. struct vm_area_struct *vma);
  367. #endif
  368. /*
  369.  * The 2.0 wrappers
  370.  */
  371. #ifdef LINUX_20
  372. int scullv_lseek_20 (struct inode *ino, struct file *f,
  373.                 off_t offset, int whence)
  374. {
  375.     return (int)scullv_llseek(f, offset, whence);
  376. }
  377. int scullv_read_20 (struct inode *ino, struct file *f, char *buf, int count)
  378. {
  379.     return (int)scullv_read(f, buf, count, &f->f_pos);
  380. }
  381. int scullv_write_20 (struct inode *ino, struct file *f, const char *b, int c)
  382. {
  383.     return (int)scullv_write(f, b, c, &f->f_pos);
  384. }
  385. void scullv_release_20 (struct inode *ino, struct file *f)
  386. {
  387.     scullv_release(ino, f);
  388. }
  389. #define scullv_llseek scullv_lseek_20
  390. #define scullv_read scullv_read_20
  391. #define scullv_write scullv_write_20
  392. #define scullv_release scullv_release_20
  393. #define llseek lseek
  394. #endif /* LINUX_20 */
  395. /*
  396.  * The fops
  397.  */
  398. struct file_operations scullv_fops = {
  399.     llseek: scullv_llseek,
  400.     read: scullv_read,
  401.     write: scullv_write,
  402.     ioctl: scullv_ioctl,
  403.     mmap: scullv_mmap,
  404.     open: scullv_open,
  405.     release: scullv_release,
  406. };
  407. int scullv_trim(ScullV_Dev *dev)
  408. {
  409.     ScullV_Dev *next, *dptr;
  410.     int qset = dev->qset;   /* "dev" is not-null */
  411.     int i;
  412.     if (dev->vmas) /* don't trim: there are active mappings */
  413.         return -EBUSY;
  414.     for (dptr = dev; dptr; dptr = next) { /* all the list items */
  415.         if (dptr->data) {
  416.             /* Release the quantum-set */
  417.             for (i = 0; i < qset; i++)
  418.                 if (dptr->data[i])
  419.                     vfree(dptr->data[i]);
  420.             kfree(dptr->data);
  421.             dptr->data=NULL;
  422.         }
  423.         next=dptr->next;
  424.         if (dptr != dev) kfree(dptr); /* all of them but the first */
  425.     }
  426.     dev->size = 0;
  427.     dev->qset = scullv_qset;
  428.     dev->order = scullv_order;
  429.     dev->next = NULL;
  430.     return 0;
  431. }
  432. /*
  433.  * Finally, the module stuff
  434.  */
  435. int scullv_init(void)
  436. {
  437.     int result, i;
  438.     SET_MODULE_OWNER(&scullv_fops);
  439.     /*
  440.      * Register your major, and accept a dynamic number
  441.      */
  442.     result = register_chrdev(scullv_major, "scullv", &scullv_fops);
  443.     if (result < 0) return result;
  444.     if (scullv_major == 0) scullv_major = result; /* dynamic */
  445.     /* 
  446.      * allocate the devices -- we can't have them static, as the number
  447.      * can be specified at load time
  448.      */
  449.     scullv_devices = kmalloc(scullv_devs * sizeof (ScullV_Dev), GFP_KERNEL);
  450.     if (!scullv_devices) {
  451.         result = -ENOMEM;
  452.         goto fail_malloc;
  453.     }
  454.     memset(scullv_devices, 0, scullv_devs * sizeof (ScullV_Dev));
  455.     for (i=0; i < scullv_devs; i++) {
  456.         scullv_devices[i].order = scullv_order;
  457.         scullv_devices[i].qset = scullv_qset;
  458.         sema_init (&scullv_devices[i].sem, 1);
  459.     }
  460. #ifdef SCULLV_USE_PROC /* only when available */
  461.     create_proc_read_entry("scullvmem", 0, NULL, scullv_read_procmem, NULL);
  462. #endif
  463.     return 0; /* succeed */
  464.   fail_malloc:
  465.     unregister_chrdev(scullv_major, "scullv");
  466.     return result;
  467. }
  468. void scullv_cleanup(void)
  469. {
  470.     int i;
  471.     unregister_chrdev(scullv_major, "scullv");
  472. #ifdef SCULLV_USE_PROC
  473.     remove_proc_entry("scullvmem", 0);
  474. #endif
  475.     for (i=0; i<scullv_devs; i++)
  476.         scullv_trim(scullv_devices+i);
  477.     kfree(scullv_devices);
  478. }
  479. module_init(scullv_init);
  480. module_exit(scullv_cleanup);