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

嵌入式Linux

开发平台:

C/C++

  1. /*
  2.  * main.c -- the bare scull char module
  3.  *
  4.  *********/
  5. #ifndef __KERNEL__
  6. #  define __KERNEL__
  7. #endif
  8. #ifndef MODULE
  9. #  define MODULE
  10. #endif
  11. #include <linux/config.h>
  12. #include <linux/module.h>
  13. #include <linux/kernel.h>   /* printk() */
  14. #include <linux/malloc.h>   /* kmalloc() */
  15. #include <linux/fs.h>       /* everything... */
  16. #include <linux/errno.h>    /* error codes */
  17. #include <linux/types.h>    /* size_t */
  18. #include <linux/proc_fs.h>
  19. #include <linux/fcntl.h>    /* O_ACCMODE */
  20. #include <asm/system.h>     /* cli(), *_flags */
  21. #include "scull.h"          /* local definitions */
  22. /*
  23.  * I don't use static symbols here, because we export no symbols
  24.  */
  25. int scull_major =   SCULL_MAJOR;
  26. int scull_nr_devs = SCULL_NR_DEVS;    /* number of bare scull devices */
  27. int scull_quantum = SCULL_QUANTUM;
  28. int scull_qset =    SCULL_QSET;
  29. MODULE_PARM(scull_major,"i");
  30. MODULE_PARM(scull_nr_devs,"i");
  31. MODULE_PARM(scull_quantum,"i");
  32. MODULE_PARM(scull_qset,"i");
  33. MODULE_AUTHOR("Alessandro Rubini");
  34. Scull_Dev *scull_devices; /* allocated in scull_init_module */
  35. /*
  36.  * Different minors behave differently, so let's use multiple fops
  37.  */
  38. struct file_operations *scull_fop_array[]={
  39.     &scull_fops,      /* type 0 */
  40.     &scull_priv_fops, /* type 1 */
  41.     &scull_pipe_fops, /* type 2 */
  42.     &scull_sngl_fops, /* type 3 */
  43.     &scull_user_fops, /* type 4 */
  44.     &scull_wusr_fops  /* type 5 */
  45. };
  46. #define SCULL_MAX_TYPE 5
  47. int scull_trim(Scull_Dev *dev)
  48. {
  49.     Scull_Dev *next, *dptr;
  50.     int qset = dev->qset;   /* "dev" is not-null */
  51.     int i;
  52.     for (dptr = dev; dptr; dptr = next) { /* all the list items */
  53.         if (dptr->data) {
  54.             for (i = 0; i < qset; i++)
  55.                 if (dptr->data[i])
  56.                     kfree(dptr->data[i]);
  57.             kfree(dptr->data);
  58.             dptr->data=NULL;
  59.         }
  60.         next=dptr->next;
  61.         if (dptr != dev) kfree(dptr); /* all of them but the first */
  62.     }
  63.     dev->size = 0;
  64.     dev->quantum = scull_quantum;
  65.     dev->qset = scull_qset;
  66.     dev->next = NULL;
  67.     return 0;
  68. }
  69. #ifdef SCULL_DEBUG /* use proc only if debugging */
  70. /*
  71.  * The proc filesystem: function to read and entry
  72.  */
  73. int scull_read_procmem(char *buf, char **start, off_t offset,
  74.                    int count, int *eof, void *data)
  75. {
  76.     int i, j, len = 0;
  77.     int limit = count - 80; /* Don't print more than this */
  78.     for (i = 0; i < scull_nr_devs && len <= limit; i++) {
  79.         Scull_Dev *d = &scull_devices[i];
  80.         if (down_interruptible(&d->sem))
  81.                 return -ERESTARTSYS;
  82.         len += sprintf(buf+len,"nDevice %i: qset %i, q %i, sz %lin",
  83.                        i, d->qset, d->quantum, d->size);
  84.         for (; d && len <= limit; d = d->next) { /* scan the list */
  85.             len += sprintf(buf+len, "  item at %p, qset at %pn", d, d->data);
  86.             if (d->data && !d->next) /* dump only the last item - save space */
  87.                 for (j = 0; j < d->qset; j++) {
  88.                     if (d->data[j])
  89.                         len += sprintf(buf+len,"    % 4i: %8pn",j,d->data[j]);
  90.                 }
  91.         }
  92.         up(&scull_devices[i].sem);
  93.     }
  94.     *eof = 1;
  95.     return len;
  96. }
  97. #ifdef USE_PROC_REGISTER
  98. static int scull_get_info(char *buf, char **start, off_t offset,
  99.                 int len, int unused)
  100. {
  101.     int eof = 0;
  102.     return scull_read_procmem (buf, start, offset, len, &eof, NULL);
  103. }
  104. struct proc_dir_entry scull_proc_entry = {
  105.         namelen:    8,
  106.         name:       "scullmem",
  107.         mode:       S_IFREG | S_IRUGO,
  108.         nlink:      1,
  109.         get_info:   scull_get_info,
  110. };
  111. static void scull_create_proc()
  112. {
  113.     proc_register_dynamic(&proc_root, &scull_proc_entry);
  114. }
  115. static void scull_remove_proc()
  116. {
  117.     proc_unregister(&proc_root, scull_proc_entry.low_ino);
  118. }
  119. #else  /* no USE_PROC_REGISTER - modern world */
  120. static void scull_create_proc()
  121. {
  122.     create_proc_read_entry("scullmem", 0 /* default mode */,
  123.                            NULL /* parent dir */, scull_read_procmem,
  124.                            NULL /* client data */);
  125. }
  126. static void scull_remove_proc()
  127. {
  128.     /* no problem if it was not registered */
  129.     remove_proc_entry("scullmem", NULL /* parent dir */);
  130. }
  131. #endif /* USE_PROC_REGISTER */
  132. #endif /* SCULL_DEBUG */
  133. /*
  134.  * Open and close
  135.  */
  136. /* In scull_open, the fop_array is used according to TYPE(dev) */
  137. int scull_open(struct inode *inode, struct file *filp)
  138. {
  139.     Scull_Dev *dev; /* device information */
  140.     int num = NUM(inode->i_rdev);
  141.     int type = TYPE(inode->i_rdev);
  142.     /*
  143.      * the type and num values are only valid if we are not using devfs.
  144.      * However, since we use them to retrieve the device pointer, we
  145.      * don't need them with devfs as filp->private_data is already
  146.      * initialized
  147.      */
  148.     /*
  149.      * If private data is not valid, we are not using devfs
  150.      * so use the type (from minor nr.) to select a new f_op
  151.      */
  152.     if (!filp->private_data && type) {
  153.         if (type > SCULL_MAX_TYPE) return -ENODEV;
  154.         filp->f_op = scull_fop_array[type];
  155.         return filp->f_op->open(inode, filp); /* dispatch to specific open */
  156.     }
  157.     /* type 0, check the device number (unless private_data valid) */
  158.     dev = (Scull_Dev *)filp->private_data;
  159.     if (!dev) {
  160.         if (num >= scull_nr_devs) return -ENODEV;
  161.         dev = &scull_devices[num];
  162.         filp->private_data = dev; /* for other methods */
  163.     }
  164.     MOD_INC_USE_COUNT;  /* Before we maybe sleep */
  165.     /* now trim to 0 the length of the device if open was write-only */
  166.     if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
  167.         if (down_interruptible(&dev->sem)) {
  168.             MOD_DEC_USE_COUNT;
  169.             return -ERESTARTSYS;
  170.         }
  171.         scull_trim(dev); /* ignore errors */
  172.         up(&dev->sem);
  173.     }
  174.     return 0;          /* success */
  175. }
  176. int scull_release(struct inode *inode, struct file *filp)
  177. {
  178.     MOD_DEC_USE_COUNT;
  179.     return 0;
  180. }
  181. /*
  182.  * Follow the list 
  183.  */
  184. Scull_Dev *scull_follow(Scull_Dev *dev, int n)
  185. {
  186.     while (n--) {
  187.         if (!dev->next) {
  188.             dev->next = kmalloc(sizeof(Scull_Dev), GFP_KERNEL);
  189.             memset(dev->next, 0, sizeof(Scull_Dev));
  190.         }
  191.         dev = dev->next;
  192.         continue;
  193.     }
  194.     return dev;
  195. }
  196. /*
  197.  * Data management: read and write
  198.  */
  199. ssize_t scull_read(struct file *filp, char *buf, size_t count,
  200.                 loff_t *f_pos)
  201. {
  202.     Scull_Dev *dev = filp->private_data; /* the first listitem */
  203.     Scull_Dev *dptr;
  204.     int quantum = dev->quantum;
  205.     int qset = dev->qset;
  206.     int itemsize = quantum * qset; /* how many bytes in the listitem */
  207.     int item, s_pos, q_pos, rest;
  208.     ssize_t ret = 0;
  209.     if (down_interruptible(&dev->sem))
  210.             return -ERESTARTSYS;
  211.     if (*f_pos >= dev->size)
  212.         goto out;
  213.     if (*f_pos + count > dev->size)
  214.         count = dev->size - *f_pos;
  215.     /* find listitem, qset index, and offset in the quantum */
  216.     item = (long)*f_pos / itemsize;
  217.     rest = (long)*f_pos % itemsize;
  218.     s_pos = rest / quantum; q_pos = rest % quantum;
  219.     /* follow the list up to the right position (defined elsewhere) */
  220.     dptr = scull_follow(dev, item);
  221.     if (!dptr->data)
  222.         goto out; /* don't fill holes */
  223.     if (!dptr->data[s_pos])
  224.         goto out;
  225.     /* read only up to the end of this quantum */
  226.     if (count > quantum - q_pos)
  227.         count = quantum - q_pos;
  228.     if (copy_to_user(buf, dptr->data[s_pos]+q_pos, count)) {
  229.         ret = -EFAULT;
  230. goto out;
  231.     }
  232.     *f_pos += count;
  233.     ret = count;
  234.  out:
  235.     up(&dev->sem);
  236.     return ret;
  237. }
  238. ssize_t scull_write(struct file *filp, const char *buf, size_t count,
  239.                 loff_t *f_pos)
  240. {
  241.     Scull_Dev *dev = filp->private_data;
  242.     Scull_Dev *dptr;
  243.     int quantum = dev->quantum;
  244.     int qset = dev->qset;
  245.     int itemsize = quantum * qset;
  246.     int item, s_pos, q_pos, rest;
  247.     ssize_t ret = -ENOMEM; /* value used in "goto out" statements */
  248.     if (down_interruptible(&dev->sem))
  249.             return -ERESTARTSYS;
  250.     /* find listitem, qset index and offset in the quantum */
  251.     item = (long)*f_pos / itemsize;
  252.     rest = (long)*f_pos % itemsize;
  253.     s_pos = rest / quantum; q_pos = rest % quantum;
  254.     /* follow the list up to the right position */
  255.     dptr = scull_follow(dev, item);
  256.     if (!dptr->data) {
  257.         dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
  258.         if (!dptr->data)
  259.             goto out;
  260.         memset(dptr->data, 0, qset * sizeof(char *));
  261.     }
  262.     if (!dptr->data[s_pos]) {
  263.         dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
  264.         if (!dptr->data[s_pos])
  265.             goto out;
  266.     }
  267.     /* write only up to the end of this quantum */
  268.     if (count > quantum - q_pos)
  269.         count = quantum - q_pos;
  270.     if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) {
  271.         ret = -EFAULT;
  272. goto out;
  273.     }
  274.     *f_pos += count;
  275.     ret = count;
  276.     /* update the size */
  277.     if (dev->size < *f_pos)
  278.         dev-> size = *f_pos;
  279.   out:
  280.     up(&dev->sem);
  281.     return ret;
  282. }
  283. /*
  284.  * The ioctl() implementation
  285.  *
  286.  * This is done twice, once the 2.2 way, followed by the 2.0 way.  One
  287.  * would not normally do things in this manner, but we wanted to illustrate
  288.  * both ways...
  289.  */
  290. #ifndef LINUX_20
  291. int scull_ioctl(struct inode *inode, struct file *filp,
  292.                  unsigned int cmd, unsigned long arg)
  293. {
  294.     int err = 0, tmp;
  295.     int ret = 0;
  296.     
  297.     /*
  298.      * extract the type and number bitfields, and don't decode
  299.      * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
  300.      */
  301.     if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
  302.     if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
  303.     /*
  304.      * the direction is a bitmask, and VERIFY_WRITE catches R/W
  305.      * transfers. `Type' is user-oriented, while
  306.      * access_ok is kernel-oriented, so the concept of "read" and
  307.      * "write" is reversed
  308.      */
  309.     if (_IOC_DIR(cmd) & _IOC_READ)
  310.         err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
  311.     else if (_IOC_DIR(cmd) & _IOC_WRITE)
  312.         err =  !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
  313.     if (err) return -EFAULT;
  314.     switch(cmd) {
  315. #ifdef SCULL_DEBUG
  316.       case SCULL_IOCHARDRESET:
  317.          /*
  318.           * reset the counter to 1, to allow unloading in case
  319.           * of problems. Use 1, not 0, because the invoking
  320.           * process has the device open.
  321.           */
  322.          while (MOD_IN_USE)
  323.              MOD_DEC_USE_COUNT;
  324.          MOD_INC_USE_COUNT;
  325.          /* don't break: fall through and reset things */
  326. #endif /* SCULL_DEBUG */
  327.       case SCULL_IOCRESET:
  328.         scull_quantum = SCULL_QUANTUM;
  329.         scull_qset = SCULL_QSET;
  330.         break;
  331.         
  332.       case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
  333.         if (! capable (CAP_SYS_ADMIN))
  334.             return -EPERM;
  335.         ret = __get_user(scull_quantum, (int *)arg);
  336.         break;
  337.       case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
  338.         if (! capable (CAP_SYS_ADMIN))
  339.             return -EPERM;
  340.         scull_quantum = arg;
  341.         break;
  342.       case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
  343.         ret = __put_user(scull_quantum, (int *)arg);
  344.         break;
  345.       case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */
  346.         return scull_quantum;
  347.       case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */
  348.         if (! capable (CAP_SYS_ADMIN))
  349.             return -EPERM;
  350.         tmp = scull_quantum;
  351.         ret = __get_user(scull_quantum, (int *)arg);
  352.         if (ret == 0)
  353.             ret = __put_user(tmp, (int *)arg);
  354.         break;
  355.       case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */
  356.         if (! capable (CAP_SYS_ADMIN))
  357.             return -EPERM;
  358.         tmp = scull_quantum;
  359.         scull_quantum = arg;
  360.         return tmp;
  361.         
  362.       case SCULL_IOCSQSET:
  363.         if (! capable (CAP_SYS_ADMIN))
  364.             return -EPERM;
  365.         ret = __get_user(scull_qset, (int *)arg);
  366.         break;
  367.       case SCULL_IOCTQSET:
  368.         if (! capable (CAP_SYS_ADMIN))
  369.             return -EPERM;
  370.         scull_qset = arg;
  371.         break;
  372.       case SCULL_IOCGQSET:
  373.         ret = __put_user(scull_qset, (int *)arg);
  374.         break;
  375.       case SCULL_IOCQQSET:
  376.         return scull_qset;
  377.       case SCULL_IOCXQSET:
  378.         if (! capable (CAP_SYS_ADMIN))
  379.             return -EPERM;
  380.         tmp = scull_qset;
  381.         ret = __get_user(scull_qset, (int *)arg);
  382.         if (ret == 0)
  383.             ret = put_user(tmp, (int *)arg);
  384.         break;
  385.       case SCULL_IOCHQSET:
  386.         if (! capable (CAP_SYS_ADMIN))
  387.             return -EPERM;
  388.         tmp = scull_qset;
  389.         scull_qset = arg;
  390.         return tmp;
  391.         /*
  392.          * The following two change the buffer size for scullpipe.
  393.          * The scullpipe device uses this same ioctl method, just to
  394.          * write less code. Actually, it's the same driver, isn't it?
  395.          */
  396.       case SCULL_P_IOCTSIZE:
  397.         scull_p_buffer = arg;
  398.         break;
  399.       case SCULL_P_IOCQSIZE:
  400.         return scull_p_buffer;
  401.       default:  /* redundant, as cmd was checked against MAXNR */
  402.         return -ENOTTY;
  403.     }
  404.     return ret;
  405. }
  406. #else  /* LINUX_20 */
  407. int scull_ioctl(struct inode *inode, struct file *filp,
  408.                  unsigned int cmd, unsigned long arg)
  409. {
  410.     int err = 0, tmp;
  411.     
  412.     /*
  413.      * extract the type and number bitfields, and don't decode
  414.      * wrong cmds: return ENOTTY before verify_area()
  415.      */
  416.     if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
  417.     if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
  418.     /*
  419.      * the direction is a bitmask, and VERIFY_WRITE catches R/W
  420.      * transfers. `Type' is user-oriented, while
  421.      * verify_area is kernel-oriented, so the concept of "read" and
  422.      * "write" is reversed
  423.      */
  424.     if (_IOC_DIR(cmd) & _IOC_READ)
  425.         err = verify_area(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
  426.     else if (_IOC_DIR(cmd) & _IOC_WRITE)
  427.         err =  verify_area(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
  428.     if (err) return err;
  429.     switch(cmd) {
  430.       case SCULL_IOCRESET:
  431.         scull_quantum = SCULL_QUANTUM;
  432.         scull_qset = SCULL_QSET;
  433.         break;
  434.         
  435.       case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
  436.         scull_quantum = get_user((int *)arg);
  437.         break;
  438.       case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
  439.         scull_quantum = arg;
  440.         break;
  441.       case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
  442.         put_user(scull_quantum, (int *)arg);
  443.         break;
  444.       case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */
  445.         return scull_quantum;
  446.       case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */
  447.         tmp = scull_quantum;
  448.         scull_quantum = get_user((int *)arg);
  449.         put_user(tmp, (int *)arg);
  450.         break;
  451.       case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */
  452.         tmp = scull_quantum;
  453.         scull_quantum = arg;
  454.         return tmp;
  455.         
  456.       case SCULL_IOCSQSET:
  457.         scull_qset = get_user((int *)arg);
  458.         break;
  459.       case SCULL_IOCTQSET:
  460.         scull_qset = arg;
  461.         break;
  462.       case SCULL_IOCGQSET:
  463.         put_user(scull_qset, (int *)arg);
  464.         break;
  465.       case SCULL_IOCQQSET:
  466.         return scull_qset;
  467.       case SCULL_IOCXQSET:
  468.         tmp = scull_qset;
  469.         scull_qset = get_user((int *)arg);
  470.         put_user(tmp, (int *)arg);
  471.         break;
  472.       case SCULL_IOCHQSET:
  473.         tmp = scull_qset;
  474.         scull_quantum = arg;
  475.         return tmp;
  476.         /*
  477.          * The following two change the buffer size for scullpipe.
  478.          * The scullpipe device uses this same ioctl method, just to
  479.          * write less code. Actually, it's the same driver, isn't it?
  480.          */
  481.       case SCULL_P_IOCTSIZE:
  482.         scull_p_buffer = arg;
  483.         break;
  484.       case SCULL_P_IOCQSIZE:
  485.         return scull_p_buffer;
  486.       default:  /* redundant, as cmd was checked against MAXNR */
  487.         return -ENOTTY;
  488.     }
  489.     return 0;
  490. }
  491. #endif /* LINUX_20 */
  492. /*
  493.  * The "extended" operations -- only seek
  494.  */
  495. loff_t scull_llseek(struct file *filp, loff_t off, int whence)
  496. {
  497.     Scull_Dev *dev = filp->private_data;
  498.     loff_t newpos;
  499.     switch(whence) {
  500.       case 0: /* SEEK_SET */
  501.         newpos = off;
  502.         break;
  503.       case 1: /* SEEK_CUR */
  504.         newpos = filp->f_pos + off;
  505.         break;
  506.       case 2: /* SEEK_END */
  507.         newpos = dev->size + off;
  508.         break;
  509.       default: /* can't happen */
  510.         return -EINVAL;
  511.     }
  512.     if (newpos<0) return -EINVAL;
  513.     filp->f_pos = newpos;
  514.     return newpos;
  515. }
  516. /*
  517.  * The following wrappers are meant to make things work with 2.0 kernels
  518.  */
  519. #ifdef LINUX_20
  520. int scull_lseek_20(struct inode *ino, struct file *f,
  521.                 off_t offset, int whence)
  522. {
  523.     return (int)scull_llseek(f, offset, whence);
  524. }
  525. int scull_read_20(struct inode *ino, struct file *f, char *buf, int count)
  526. {
  527.     return (int)scull_read(f, buf, count, &f->f_pos);
  528. }
  529. int scull_write_20(struct inode *ino, struct file *f, const char *b, int c)
  530. {
  531.     return (int)scull_write(f, b, c, &f->f_pos);
  532. }
  533. void scull_release_20(struct inode *ino, struct file *f)
  534. {
  535.     scull_release(ino, f);
  536. }
  537. /* Redefine "real" names to the 2.0 ones */
  538. #define scull_llseek scull_lseek_20
  539. #define scull_read scull_read_20
  540. #define scull_write scull_write_20
  541. #define scull_release scull_release_20
  542. #define llseek lseek
  543. #endif  /* LINUX_20 */
  544. struct file_operations scull_fops = {
  545.     llseek:     scull_llseek,
  546.     read:       scull_read,
  547.     write:      scull_write,
  548.     ioctl:      scull_ioctl,
  549.     open:       scull_open,
  550.     release:    scull_release,
  551. };
  552. /*
  553.  * Finally, the module stuff
  554.  */
  555. #ifdef CONFIG_DEVFS_FS
  556. devfs_handle_t scull_devfs_dir;
  557. static char devname[4];
  558. #endif
  559. /*
  560.  * The cleanup function is used to handle initialization failures as well.
  561.  * Thefore, it must be careful to work correctly even if some of the items
  562.  * have not been initialized
  563.  */
  564. void scull_cleanup_module(void)
  565. {
  566.     int i;
  567. #ifndef CONFIG_DEVFS_FS
  568.     /* cleanup_module is never called if registering failed */
  569.     unregister_chrdev(scull_major, "scull");
  570. #endif
  571.     scull_remove_proc();
  572.     if (scull_devices) {
  573.         for (i=0; i<scull_nr_devs; i++) {
  574.             scull_trim(scull_devices+i);
  575.             /* the following line is only used for devfs */
  576.             devfs_unregister(scull_devices[i].handle);
  577.         }
  578.         kfree(scull_devices);
  579.     }
  580.     /* and call the cleanup functions for friend devices */
  581.     scull_p_cleanup();
  582.     scull_access_cleanup();
  583.     /* once again, only for devfs */
  584.     devfs_unregister(scull_devfs_dir);
  585. }
  586. int scull_init_module(void)
  587. {
  588.     int result, i;
  589.     SET_MODULE_OWNER(&scull_fops);
  590. #ifdef CONFIG_DEVFS_FS
  591.     /* If we have devfs, create /dev/scull to put files in there */
  592.     scull_devfs_dir = devfs_mk_dir(NULL, "scull", NULL);
  593.     if (!scull_devfs_dir) return -EBUSY; /* problem */
  594. #else /* no devfs, do it the "classic" way  */    
  595.     /*
  596.      * Register your major, and accept a dynamic number. This is the
  597.      * first thing to do, in order to avoid releasing other module's
  598.      * fops in scull_cleanup_module()
  599.      */
  600.     result = register_chrdev(scull_major, "scull", &scull_fops);
  601.     if (result < 0) {
  602.         printk(KERN_WARNING "scull: can't get major %dn",scull_major);
  603.         return result;
  604.     }
  605.     if (scull_major == 0) scull_major = result; /* dynamic */
  606. #endif /* CONFIG_DEVFS_FS */
  607.     /* 
  608.      * allocate the devices -- we can't have them static, as the number
  609.      * can be specified at load time
  610.      */
  611.     scull_devices = kmalloc(scull_nr_devs * sizeof(Scull_Dev), GFP_KERNEL);
  612.     if (!scull_devices) {
  613.         result = -ENOMEM;
  614.         goto fail;
  615.     }
  616.     memset(scull_devices, 0, scull_nr_devs * sizeof(Scull_Dev));
  617.     for (i=0; i < scull_nr_devs; i++) {
  618.         scull_devices[i].quantum = scull_quantum;
  619.         scull_devices[i].qset = scull_qset;
  620.         sema_init(&scull_devices[i].sem, 1);
  621. #ifdef CONFIG_DEVFS_FS
  622.         sprintf(devname, "%i", i);
  623.         devfs_register(scull_devfs_dir, devname,
  624.                        DEVFS_FL_AUTO_DEVNUM,
  625.                        0, 0, S_IFCHR | S_IRUGO | S_IWUGO,
  626.                        &scull_fops,
  627.                        scull_devices+i);
  628. #endif  
  629.     }
  630.     /* At this point call the init function for any friend device */
  631.     if ( (result = scull_p_init()) )
  632.         goto fail;
  633.     if ( (result = scull_access_init()) )
  634.         goto fail;
  635.     /* ... */
  636. #ifndef SCULL_DEBUG
  637.     EXPORT_NO_SYMBOLS; /* otherwise, leave global symbols visible */
  638. #endif
  639. #ifdef SCULL_DEBUG /* only when debugging */
  640.     scull_create_proc();
  641. #endif
  642.     return 0; /* succeed */
  643.   fail:
  644.     scull_cleanup_module();
  645.     return result;
  646. }
  647. module_init(scull_init_module);
  648. module_exit(scull_cleanup_module);