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

嵌入式Linux

开发平台:

C/C++

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