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

嵌入式Linux

开发平台:

C/C++

  1. /*
  2.  * sbull.c -- the Simple Block Utility
  3.  *
  4.  * Tested with 1.2 on the x86
  5.  * Tested with 2.0 on the x86, Sparc
  6.  *
  7.  *********/
  8. #ifndef __KERNEL__
  9. #  define __KERNEL__
  10. #endif
  11. #ifndef MODULE
  12. #  define MODULE
  13. #endif
  14. #define __NO_VERSION__ /* don't define kernel_verion in module.h */
  15. #include <linux/module.h>
  16. #include <linux/version.h>
  17. char kernel_version [] = UTS_RELEASE;
  18. #include <linux/sched.h>
  19. #include <linux/kernel.h> /* printk() */
  20. #include <linux/malloc.h> /* kmalloc() */
  21. #include <linux/fs.h>     /* everything... */
  22. #include <linux/errno.h>  /* error codes */
  23. #include <linux/timer.h>
  24. #include <linux/types.h>  /* size_t */
  25. #include <linux/fcntl.h>        /* O_ACCMODE */
  26. #include <linux/hdreg.h>  /* HDIO_GETGEO */
  27. #include <asm/system.h>   /* cli(), *_flags */
  28. #include <asm/segment.h>  /* put_user */
  29. #include "sysdep.h" /* at least for add_timer */
  30. #define MAJOR_NR sbull_major /* force definitions on in blk.h */
  31. int sbull_major; /* must be declared before including blk.h */
  32. #define DEVICE_NR(device) MINOR(device)   /* sbull has no partition bits */
  33. #define DEVICE_NAME "sbull"               /* name for messaging */
  34. #define DEVICE_INTR sbull_intrptr         /* pointer to the bottom half */
  35. #define DEVICE_NO_RANDOM                  /* no entropy to contribute */
  36. #define DEVICE_OFF(d) /* do-nothing */
  37. #if LINUX_VERSION_CODE < 0x10324 /* 1.3.36 */
  38. #  include <linux/../../drivers/block/blk.h>
  39. #else
  40. #  include <linux/blk.h>
  41. #endif
  42. #include "sbull.h"        /* local definitions */
  43. /*
  44.  * Non-prefixed symbols are static. They are meant to be assigned at
  45.  * load time. Prefixed symbols are not static, so they can be used in
  46.  * debugging. They are hidden anyways by register_symtab() unless
  47.  * SBULL_DEBUG is defined.
  48.  */
  49. static int major    = SBULL_MAJOR;
  50. static int devs     = SBULL_DEVS;
  51. static int rahead   = SBULL_RAHEAD;
  52. static int size     = SBULL_SIZE;
  53. static int blksize  = SBULL_BLKSIZE;
  54. static int hardsect = SBULL_HARDSECT;
  55. int sbull_devs, sbull_rahead, sbull_size;
  56. int sbull_blksize, sbull_hardsect;
  57. /* The following items are obtained through kmalloc() in init_module() */
  58. Sbull_Dev *sbull_devices = NULL;
  59. int *sbull_blksizes = NULL;
  60. int *sbull_sizes = NULL;
  61. int *sbull_hardsects = NULL;
  62. int sbull_revalidate(kdev_t i_rdev);
  63. /*
  64.  * Open and close
  65.  */
  66. int sbull_open (struct inode *inode, struct file *filp)
  67. {
  68.     Sbull_Dev *dev; /* device information */
  69.     int num = MINOR(inode->i_rdev);
  70.     if (num >= sbull_devs) return -ENODEV;
  71.     dev = sbull_devices + num;
  72.     /* kill the timer associated to the device: it might be active */
  73.     del_timer(&dev->timer);
  74.     /* revalidate on first open and fail if no data is there */
  75.     if (!dev->usage) {
  76.         check_disk_change(inode->i_rdev);
  77.         if (!dev->data)
  78.             return -ENOMEM;
  79.     }
  80.     dev->usage++;
  81.     MOD_INC_USE_COUNT;
  82.     return 0;          /* success */
  83. }
  84. void sbull_release (struct inode *inode, struct file *filp)
  85. {
  86.     Sbull_Dev *dev = sbull_devices + MINOR(inode->i_rdev);
  87.     dev->usage--;
  88.     /*
  89.      * If the device is closed for the last time, start a timer
  90.      * to release RAM in half a minute. The function and argument
  91.      * for the timer have been setup in init_module()
  92.      */
  93.     if (!dev->usage) {
  94.         dev->timer.expires = jiffies + 30 * HZ;
  95.         add_timer(&dev->timer);
  96.         /* but flush it right now */
  97.         fsync_dev(inode->i_rdev);
  98.         invalidate_buffers(inode->i_rdev);
  99.     }
  100.     MOD_DEC_USE_COUNT;
  101. }
  102. /*
  103.  * The timer function. As argument it receives the device
  104.  */
  105. void sbull_expires(unsigned long data)
  106. {
  107.     Sbull_Dev *dev = (Sbull_Dev *)data;
  108.     if (dev->usage || !dev->data) {
  109.         printk(KERN_WARNING "sbull: timer mismatch for device %in",
  110.                dev - sbull_devices);
  111.         return;
  112.     }
  113.     PDEBUG("freeing device %in",dev - sbull_devices);
  114.     vfree(dev->data);
  115.     dev->data=0;
  116.     return;
  117. }    
  118. /*
  119.  * The ioctl() implementation
  120.  */
  121. int sbull_ioctl (struct inode *inode, struct file *filp,
  122.                  unsigned int cmd, unsigned long arg)
  123. {
  124.     int err, size;
  125.     struct hd_geometry *geo = (struct hd_geometry *)arg;
  126.     PDEBUG("ioctl 0x%x 0x%lxn", cmd, arg);
  127.     switch(cmd) {
  128.       case BLKGETSIZE:
  129.         /* Return the device size, expressed in sectors */
  130.         if (!arg) return -EINVAL; /* NULL pointer: not valid */
  131.         err=verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
  132.         if (err) return err;
  133.         put_user ( 1024* sbull_sizes[MINOR(inode->i_rdev)]
  134.                    / sbull_hardsects[MINOR(inode->i_rdev)],
  135.                      (long *) arg);
  136.         return 0;
  137.       case BLKFLSBUF: /* flush */
  138.         if (!suser()) return -EACCES; /* only root */
  139.         fsync_dev(inode->i_rdev);
  140.         invalidate_buffers(inode->i_rdev);
  141.         return 0;
  142.       case BLKRAGET: /* return the readahead value */
  143.         if (!arg)  return -EINVAL;
  144.         err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
  145.         if (err) return err;
  146.         put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
  147.         return 0;
  148.       case BLKRASET: /* set the readahead value */
  149.         if (!suser()) return -EACCES;
  150.         if (arg > 0xff) return -EINVAL; /* limit it */
  151.         read_ahead[MAJOR(inode->i_rdev)] = arg;
  152.         return 0;
  153.       case BLKRRPART: /* re-read partition table: can't do it */
  154.         return -EINVAL;
  155.       RO_IOCTLS(inode->i_rdev, arg); /* the default RO operations */
  156.       case HDIO_GETGEO:
  157.         /*
  158.          * get geometry: we have to fake one...  trim the size to a
  159.          * multiple of 64 (32k): tell we have 16 sectors, 4 heads,
  160.          * whatever cylinders. Tell also that data starts at sector. 4.
  161.          */
  162.         size = sbull_size * 1024 / sbull_hardsect;
  163.         size &= ~0x3f; /* multiple of 64 */
  164.         if (geo==NULL) return -EINVAL;
  165.         err = verify_area(VERIFY_WRITE, geo, sizeof(*geo));
  166.         if (err) return err;
  167.         put_user(size >> 6, &geo->cylinders);
  168.         put_user(        4, &geo->heads);
  169.         put_user(       16, &geo->sectors);
  170.         put_user(        4, &geo->start);
  171.         return 0;
  172.     }
  173.     return -EINVAL; /* unknown command */
  174. }
  175. /*
  176.  * Support for removable devices
  177.  */
  178. int sbull_check_change(kdev_t i_rdev)
  179. {
  180.     int minor = MINOR(i_rdev);
  181.     Sbull_Dev *dev = sbull_devices + minor;
  182.     if (minor >= sbull_devs) /* paranoid */
  183.         return 0;
  184.     PDEBUG("check_change for dev %in",minor);
  185.     if (dev->data)
  186.         return 0; /* still valid */
  187.     return 1; /* expired */
  188. }
  189. int sbull_revalidate(kdev_t i_rdev)
  190. {
  191.     Sbull_Dev *dev = sbull_devices + MINOR(i_rdev);
  192.     PDEBUG("revalidate for dev %in",MINOR(i_rdev));
  193.     if (dev->data)
  194.         return 0;
  195.     dev->data = vmalloc(dev->size);
  196.     if (!dev->data)
  197.         return -ENOMEM;
  198.     return 0;
  199. }
  200. /*
  201.  * The file operations
  202.  */
  203. struct file_operations sbull_fops = {
  204.     NULL,          /* lseek: default */
  205.     block_read,
  206.     block_write,
  207.     NULL,          /* sbull_readdir */
  208.     NULL,          /* sbull_select */
  209.     sbull_ioctl,
  210.     NULL,          /* sbull_mmap */
  211.     sbull_open,
  212.     sbull_release,
  213.     block_fsync,
  214.     NULL,          /* sbull_fasync */
  215.     sbull_check_change,
  216.     sbull_revalidate
  217. };
  218. /*
  219.  * Block-driver specific functions
  220.  */
  221. #ifdef SBULL_EMPTY_REQUEST
  222. /*
  223.  * This empty request function just prints the interesting items
  224.  * of the current request. The sectors affected by the request
  225.  * are printed as <first-sector>-<number-of-sectors>.
  226.  */
  227. void sbull_request(void)
  228. {
  229.     while(1) {
  230.         INIT_REQUEST;
  231.         printk("request %p: cmd %i sec %li (nr. %li), next %pn", CURRENT,
  232.                CURRENT->cmd,
  233.                CURRENT->sector,
  234.                CURRENT->current_nr_sectors,
  235.                CURRENT->next);
  236.         end_request(1); /* success */
  237.     }
  238. }
  239. #else
  240. void sbull_request(void)
  241. {
  242.     Sbull_Dev *device;
  243.     u8 *ptr;
  244.     int size;
  245.     while(1) {
  246.         INIT_REQUEST;
  247.         /* Check if the minor number is in range */
  248.         if (DEVICE_NR(CURRENT_DEV) > sbull_devs) {
  249.             static int count = 0;
  250.             if (count++ < 5) /* print the message at most five times */
  251.                 printk(KERN_WARNING "sbull: request for unknown devicen");
  252.             end_request(0);
  253.             continue;
  254.         }
  255.         /* pointer to device structure, from the global array */
  256.         device = sbull_devices + DEVICE_NR(CURRENT_DEV);
  257.         ptr = device->data + CURRENT->sector * sbull_hardsect;
  258.         size = CURRENT->current_nr_sectors * sbull_hardsect;
  259.         if (ptr + size > device->data + sbull_blksize*sbull_size) {
  260.             static int count = 0;
  261.             if (count++ < 5)
  262.                 printk(KERN_WARNING "sbull: request past end of devicen");
  263.             end_request(0);
  264.             continue;
  265.         }
  266.         switch(CURRENT->cmd) {
  267.           case READ:
  268.             memcpy(CURRENT->buffer, ptr, size); /* from sbull to buffer */
  269.             break;
  270.           case WRITE:
  271.             memcpy(ptr, CURRENT->buffer, size); /* from buffer to sbull */
  272.             break;
  273.           default:
  274.             /* can't happen */
  275.             end_request(0);
  276.             continue;
  277.         }
  278.         end_request(1); /* success */
  279.     }
  280. }
  281. #endif /* SBULL_EMPTY_REQUEST */
  282. /*
  283.  * Finally, the module stuff
  284.  */
  285. int init_module(void)
  286. {
  287.     int result, i;
  288.     /*
  289.      * Copy the (static) cfg variables to public prefixed ones to allow
  290.      * snoozing with a debugger.
  291.      */
  292.     sbull_major    = major;
  293.     sbull_devs     = devs;
  294.     sbull_rahead   = rahead;
  295.     sbull_size     = size;
  296.     sbull_blksize  = blksize;
  297.     sbull_hardsect = hardsect;
  298.     /* Hardsect can't be changed :( */
  299.     if (hardsect != 512) {
  300.         printk(KERN_ERR "sbull: can't change hardsect sizen");
  301.         hardsect = sbull_hardsect = 512;
  302.     }
  303.     /*
  304.      * Register your major, and accept a dynamic number
  305.      */
  306.     result = register_blkdev(sbull_major, "sbull", &sbull_fops);
  307.     if (result < 0) {
  308.         printk(KERN_WARNING "sbull: can't get major %dn",sbull_major);
  309.         return result;
  310.     }
  311.     if (sbull_major == 0) sbull_major = result; /* dynamic */
  312.     major = sbull_major; /* Use `major' later on to save typing */
  313.     /*
  314.      * Assign the other needed values: request, rahead, size, blksize,
  315.      * hardsect. All the minor devices feature the same value.
  316.      * Note that `sbull' defines all of them to allow testing non-default
  317.      * values. A real device could well avoid setting values in global
  318.      * arrays if it uses the default values.
  319.      */
  320.     blk_dev[major].request_fn = sbull_request;
  321.     read_ahead[major] = sbull_rahead;
  322.     result = -ENOMEM; /* for the possible errors */
  323.     sbull_sizes = kmalloc(sbull_devs * sizeof(int), GFP_KERNEL);
  324.     if (!sbull_sizes)
  325.         goto fail_malloc;
  326.     for (i=0; i < sbull_devs; i++) /* all the same size */
  327.         sbull_sizes[i] = sbull_size;
  328.     blk_size[major]=sbull_sizes;
  329.     sbull_blksizes = kmalloc(sbull_devs * sizeof(int), GFP_KERNEL);
  330.     if (!sbull_blksizes)
  331.         goto fail_malloc;
  332.     for (i=0; i < sbull_devs; i++) /* all the same blocksize */
  333.         sbull_blksizes[i] = sbull_blksize;
  334.     blksize_size[major]=sbull_blksizes;
  335.     sbull_hardsects = kmalloc(sbull_devs * sizeof(int), GFP_KERNEL);
  336.     if (!sbull_hardsects)
  337.         goto fail_malloc;
  338.     for (i=0; i < sbull_devs; i++) /* all the same hardsect */
  339.         sbull_hardsects[i] = sbull_hardsect;
  340.     hardsect_size[major]=sbull_hardsects;
  341.     
  342.     /* 
  343.      * allocate the devices -- we can't have them static, as the number
  344.      * can be specified at load time
  345.      */
  346.     sbull_devices = kmalloc(sbull_devs * sizeof (Sbull_Dev), GFP_KERNEL);
  347.     if (!sbull_devices)
  348.         goto fail_malloc;
  349.     memset(sbull_devices, 0, sbull_devs * sizeof (Sbull_Dev));
  350.     for (i=0; i < sbull_devs; i++) {
  351.         /* data and usage remain zeroed */
  352.         sbull_devices[i].size = 1024 * sbull_size;
  353.         init_timer(&(sbull_devices[i].timer));
  354.         sbull_devices[i].timer.data = (unsigned long)(sbull_devices+i);
  355.         sbull_devices[i].timer.function = sbull_expires;
  356.     }
  357. #ifndef SBULL_DEBUG
  358.     register_symtab(NULL); /* otherwise, leave global symbols visible */
  359. #endif
  360.     return 0; /* succeed */
  361.   fail_malloc:
  362.     read_ahead[major] = 0;
  363.     if (sbull_sizes) kfree(sbull_sizes);
  364.     blk_size[major] = NULL;
  365.     if (sbull_blksizes) kfree(sbull_blksizes);
  366.     blksize_size[major] = NULL;
  367.     if (sbull_hardsects) kfree(sbull_hardsects);
  368.     hardsect_size[major] = NULL;
  369.     if (sbull_devices) kfree(sbull_devices);
  370.     unregister_chrdev(major, "sbull");
  371.     return result;
  372. }
  373. void cleanup_module(void)
  374. {
  375.     int i;
  376.     /* first of all, flush it all and reset all the data structures */
  377.     for (i=0; i<sbull_devs; i++)
  378.         fsync_dev(MKDEV(sbull_major, i)); /* flush the devices */
  379.     blk_dev[major].request_fn = NULL;
  380.     read_ahead[major] = 0;
  381.     kfree(blk_size[major]);
  382.     blk_size[major] = NULL;
  383.     kfree(blksize_size[major]);
  384.     blksize_size[major] = NULL;
  385.     kfree(hardsect_size[major]);
  386.     hardsect_size[major] = NULL;
  387.     /* finally, the usual cleanup */
  388.     unregister_blkdev(major, "sbull");
  389.     for (i=0; i < sbull_devs; i++) {
  390.         if (sbull_devices[i].data)
  391.             vfree(sbull_devices[i].data);
  392.         del_timer(&sbull_devices[i].timer);
  393.     }
  394.     kfree(sbull_devices);
  395. }