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

嵌入式Linux

开发平台:

C/C++

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