]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
staging: comedi: refactor sysfs files in comedi_fops.c
authorH Hartley Sweeten <hartleys@visionengravers.com>
Wed, 9 May 2012 16:20:08 +0000 (09:20 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 9 May 2012 20:39:33 +0000 (13:39 -0700)
Refactor the sysfs attributes and functions to remove
the need for the forward declarations and use the
DEVICE_ATTR macro to define them.

Instead of individually creating sysfs device attribute
files, wrap them in an attribute_group and use the
sysfs_create_group function to create them.

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: Mori Hess <fmhess@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/comedi_fops.c

index 06fc6569c859ff354125aa32648804bdfe5fd800..44ca1feebe24e5b6dfd0ac1419414f2e329e0686 100644 (file)
@@ -129,15 +129,295 @@ static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
 static int comedi_fasync(int fd, struct file *file, int on);
 
 static int is_device_busy(struct comedi_device *dev);
+
 static int resize_async_buffer(struct comedi_device *dev,
                               struct comedi_subdevice *s,
-                              struct comedi_async *async, unsigned new_size);
+                              struct comedi_async *async, unsigned new_size)
+{
+       int retval;
+
+       if (new_size > async->max_bufsize)
+               return -EPERM;
+
+       if (s->busy) {
+               DPRINTK("subdevice is busy, cannot resize buffer\n");
+               return -EBUSY;
+       }
+       if (async->mmap_count) {
+               DPRINTK("subdevice is mmapped, cannot resize buffer\n");
+               return -EBUSY;
+       }
+
+       if (!async->prealloc_buf)
+               return -EINVAL;
+
+       /* make sure buffer is an integral number of pages
+        * (we round up) */
+       new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
+
+       retval = comedi_buf_alloc(dev, s, new_size);
+       if (retval < 0)
+               return retval;
+
+       if (s->buf_change) {
+               retval = s->buf_change(dev, s, new_size);
+               if (retval < 0)
+                       return retval;
+       }
+
+       DPRINTK("comedi%i subd %d buffer resized to %i bytes\n",
+               dev->minor, (int)(s - dev->subdevices), async->prealloc_bufsz);
+       return 0;
+}
+
+/* sysfs attribute files */
+
+static const unsigned bytes_per_kibi = 1024;
+
+static ssize_t show_max_read_buffer_kb(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+       ssize_t retval;
+       struct comedi_device_file_info *info = dev_get_drvdata(dev);
+       unsigned max_buffer_size_kb = 0;
+       struct comedi_subdevice *const read_subdevice =
+           comedi_get_read_subdevice(info);
+
+       mutex_lock(&info->device->mutex);
+       if (read_subdevice &&
+           (read_subdevice->subdev_flags & SDF_CMD_READ) &&
+           read_subdevice->async) {
+               max_buffer_size_kb = read_subdevice->async->max_bufsize /
+                   bytes_per_kibi;
+       }
+       retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
+       mutex_unlock(&info->device->mutex);
+
+       return retval;
+}
+
+static ssize_t store_max_read_buffer_kb(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct comedi_device_file_info *info = dev_get_drvdata(dev);
+       unsigned int new_max_size_kb;
+       unsigned int new_max_size;
+       int ret;
+       struct comedi_subdevice *const read_subdevice =
+           comedi_get_read_subdevice(info);
+
+       ret = kstrtouint(buf, 10, &new_max_size_kb);
+       if (ret)
+               return ret;
+       if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
+               return -EINVAL;
+       new_max_size = new_max_size_kb * bytes_per_kibi;
+
+       mutex_lock(&info->device->mutex);
+       if (read_subdevice == NULL ||
+           (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
+           read_subdevice->async == NULL) {
+               mutex_unlock(&info->device->mutex);
+               return -EINVAL;
+       }
+       read_subdevice->async->max_bufsize = new_max_size;
+       mutex_unlock(&info->device->mutex);
+
+       return count;
+}
+
+static DEVICE_ATTR(max_read_buffer_kb, S_IRUGO | S_IWUSR,
+               show_max_read_buffer_kb, store_max_read_buffer_kb);
+
+static ssize_t show_read_buffer_kb(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       ssize_t retval;
+       struct comedi_device_file_info *info = dev_get_drvdata(dev);
+       unsigned buffer_size_kb = 0;
+       struct comedi_subdevice *const read_subdevice =
+           comedi_get_read_subdevice(info);
+
+       mutex_lock(&info->device->mutex);
+       if (read_subdevice &&
+           (read_subdevice->subdev_flags & SDF_CMD_READ) &&
+           read_subdevice->async) {
+               buffer_size_kb = read_subdevice->async->prealloc_bufsz /
+                   bytes_per_kibi;
+       }
+       retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
+       mutex_unlock(&info->device->mutex);
+
+       return retval;
+}
+
+static ssize_t store_read_buffer_kb(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct comedi_device_file_info *info = dev_get_drvdata(dev);
+       unsigned int new_size_kb;
+       unsigned int new_size;
+       int retval;
+       int ret;
+       struct comedi_subdevice *const read_subdevice =
+           comedi_get_read_subdevice(info);
+
+       ret = kstrtouint(buf, 10, &new_size_kb);
+       if (ret)
+               return ret;
+       if (new_size_kb > (UINT_MAX / bytes_per_kibi))
+               return -EINVAL;
+       new_size = new_size_kb * bytes_per_kibi;
+
+       mutex_lock(&info->device->mutex);
+       if (read_subdevice == NULL ||
+           (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
+           read_subdevice->async == NULL) {
+               mutex_unlock(&info->device->mutex);
+               return -EINVAL;
+       }
+       retval = resize_async_buffer(info->device, read_subdevice,
+                                    read_subdevice->async, new_size);
+       mutex_unlock(&info->device->mutex);
+
+       if (retval < 0)
+               return retval;
+       return count;
+}
 
-/* declarations for sysfs attribute files */
-static struct device_attribute dev_attr_max_read_buffer_kb;
-static struct device_attribute dev_attr_read_buffer_kb;
-static struct device_attribute dev_attr_max_write_buffer_kb;
-static struct device_attribute dev_attr_write_buffer_kb;
+static DEVICE_ATTR(read_buffer_kb, S_IRUGO | S_IWUSR | S_IWGRP,
+               show_read_buffer_kb, store_read_buffer_kb);
+
+static ssize_t show_max_write_buffer_kb(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       ssize_t retval;
+       struct comedi_device_file_info *info = dev_get_drvdata(dev);
+       unsigned max_buffer_size_kb = 0;
+       struct comedi_subdevice *const write_subdevice =
+           comedi_get_write_subdevice(info);
+
+       mutex_lock(&info->device->mutex);
+       if (write_subdevice &&
+           (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
+           write_subdevice->async) {
+               max_buffer_size_kb = write_subdevice->async->max_bufsize /
+                   bytes_per_kibi;
+       }
+       retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
+       mutex_unlock(&info->device->mutex);
+
+       return retval;
+}
+
+static ssize_t store_max_write_buffer_kb(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count)
+{
+       struct comedi_device_file_info *info = dev_get_drvdata(dev);
+       unsigned int new_max_size_kb;
+       unsigned int new_max_size;
+       int ret;
+       struct comedi_subdevice *const write_subdevice =
+           comedi_get_write_subdevice(info);
+
+       ret = kstrtouint(buf, 10, &new_max_size_kb);
+       if (ret)
+               return ret;
+       if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
+               return -EINVAL;
+       new_max_size = new_max_size_kb * bytes_per_kibi;
+
+       mutex_lock(&info->device->mutex);
+       if (write_subdevice == NULL ||
+           (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
+           write_subdevice->async == NULL) {
+               mutex_unlock(&info->device->mutex);
+               return -EINVAL;
+       }
+       write_subdevice->async->max_bufsize = new_max_size;
+       mutex_unlock(&info->device->mutex);
+
+       return count;
+}
+
+static DEVICE_ATTR(max_write_buffer_kb, S_IRUGO | S_IWUSR,
+               show_max_write_buffer_kb, store_max_write_buffer_kb);
+
+static ssize_t show_write_buffer_kb(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       ssize_t retval;
+       struct comedi_device_file_info *info = dev_get_drvdata(dev);
+       unsigned buffer_size_kb = 0;
+       struct comedi_subdevice *const write_subdevice =
+           comedi_get_write_subdevice(info);
+
+       mutex_lock(&info->device->mutex);
+       if (write_subdevice &&
+           (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
+           write_subdevice->async) {
+               buffer_size_kb = write_subdevice->async->prealloc_bufsz /
+                   bytes_per_kibi;
+       }
+       retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
+       mutex_unlock(&info->device->mutex);
+
+       return retval;
+}
+
+static ssize_t store_write_buffer_kb(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct comedi_device_file_info *info = dev_get_drvdata(dev);
+       unsigned int new_size_kb;
+       unsigned int new_size;
+       int retval;
+       int ret;
+       struct comedi_subdevice *const write_subdevice =
+           comedi_get_write_subdevice(info);
+
+       ret = kstrtouint(buf, 10, &new_size_kb);
+       if (ret)
+               return ret;
+       if (new_size_kb > (UINT_MAX / bytes_per_kibi))
+               return -EINVAL;
+       new_size = ((uint64_t) new_size_kb) * bytes_per_kibi;
+
+       mutex_lock(&info->device->mutex);
+       if (write_subdevice == NULL ||
+           (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
+           write_subdevice->async == NULL) {
+               mutex_unlock(&info->device->mutex);
+               return -EINVAL;
+       }
+       retval = resize_async_buffer(info->device, write_subdevice,
+                                    write_subdevice->async, new_size);
+       mutex_unlock(&info->device->mutex);
+
+       if (retval < 0)
+               return retval;
+       return count;
+}
+
+static DEVICE_ATTR(write_buffer_kb, S_IRUGO | S_IWUSR | S_IWGRP,
+               show_write_buffer_kb, store_write_buffer_kb);
+
+static struct attribute *comedi_attrs[] = {
+       &dev_attr_max_read_buffer_kb.attr,
+       &dev_attr_read_buffer_kb.attr,
+       &dev_attr_max_write_buffer_kb.attr,
+       &dev_attr_write_buffer_kb.attr,
+       NULL
+};
+
+static const struct attribute_group comedi_sysfs_files = {
+       .attrs  = comedi_attrs,
+};
 
 static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
                                  unsigned long arg)
@@ -1937,804 +2217,453 @@ ok:
 
        dev->use_count++;
 
-       mutex_unlock(&dev->mutex);
-
-       return 0;
-}
-
-static int comedi_close(struct inode *inode, struct file *file)
-{
-       const unsigned minor = iminor(inode);
-       struct comedi_subdevice *s = NULL;
-       int i;
-       struct comedi_device_file_info *dev_file_info;
-       struct comedi_device *dev;
-       dev_file_info = comedi_get_device_file_info(minor);
-
-       if (dev_file_info == NULL)
-               return -ENODEV;
-       dev = dev_file_info->device;
-       if (dev == NULL)
-               return -ENODEV;
-
-       mutex_lock(&dev->mutex);
-
-       if (dev->subdevices) {
-               for (i = 0; i < dev->n_subdevices; i++) {
-                       s = dev->subdevices + i;
-
-                       if (s->busy == file)
-                               do_cancel(dev, s);
-                       if (s->lock == file)
-                               s->lock = NULL;
-               }
-       }
-       if (dev->attached && dev->use_count == 1 && dev->close)
-               dev->close(dev);
-
-       module_put(THIS_MODULE);
-       if (dev->attached)
-               module_put(dev->driver->module);
-
-       dev->use_count--;
-
-       mutex_unlock(&dev->mutex);
-
-       if (file->f_flags & FASYNC)
-               comedi_fasync(-1, file, 0);
-
-       return 0;
-}
-
-static int comedi_fasync(int fd, struct file *file, int on)
-{
-       const unsigned minor = iminor(file->f_dentry->d_inode);
-       struct comedi_device_file_info *dev_file_info;
-       struct comedi_device *dev;
-       dev_file_info = comedi_get_device_file_info(minor);
-
-       if (dev_file_info == NULL)
-               return -ENODEV;
-       dev = dev_file_info->device;
-       if (dev == NULL)
-               return -ENODEV;
-
-       return fasync_helper(fd, file, on, &dev->async_queue);
-}
-
-const struct file_operations comedi_fops = {
-       .owner = THIS_MODULE,
-       .unlocked_ioctl = comedi_unlocked_ioctl,
-       .compat_ioctl = comedi_compat_ioctl,
-       .open = comedi_open,
-       .release = comedi_close,
-       .read = comedi_read,
-       .write = comedi_write,
-       .mmap = comedi_mmap,
-       .poll = comedi_poll,
-       .fasync = comedi_fasync,
-       .llseek = noop_llseek,
-};
-
-struct class *comedi_class;
-static struct cdev comedi_cdev;
-
-static void comedi_cleanup_legacy_minors(void)
-{
-       unsigned i;
-
-       for (i = 0; i < comedi_num_legacy_minors; i++)
-               comedi_free_board_minor(i);
-}
-
-static int __init comedi_init(void)
-{
-       int i;
-       int retval;
-
-       printk(KERN_INFO "comedi: version " COMEDI_RELEASE
-              " - http://www.comedi.org\n");
-
-       if (comedi_num_legacy_minors < 0 ||
-           comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
-               printk(KERN_ERR "comedi: error: invalid value for module "
-                      "parameter \"comedi_num_legacy_minors\".  Valid values "
-                      "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
-               return -EINVAL;
-       }
-
-       /*
-        * comedi is unusable if both comedi_autoconfig and
-        * comedi_num_legacy_minors are zero, so we might as well adjust the
-        * defaults in that case
-        */
-       if (comedi_autoconfig == 0 && comedi_num_legacy_minors == 0)
-               comedi_num_legacy_minors = 16;
-
-       memset(comedi_file_info_table, 0,
-              sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
-
-       retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
-                                       COMEDI_NUM_MINORS, "comedi");
-       if (retval)
-               return -EIO;
-       cdev_init(&comedi_cdev, &comedi_fops);
-       comedi_cdev.owner = THIS_MODULE;
-       kobject_set_name(&comedi_cdev.kobj, "comedi");
-       if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
-               unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
-                                        COMEDI_NUM_MINORS);
-               return -EIO;
-       }
-       comedi_class = class_create(THIS_MODULE, "comedi");
-       if (IS_ERR(comedi_class)) {
-               printk(KERN_ERR "comedi: failed to create class");
-               cdev_del(&comedi_cdev);
-               unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
-                                        COMEDI_NUM_MINORS);
-               return PTR_ERR(comedi_class);
-       }
-
-       /* XXX requires /proc interface */
-       comedi_proc_init();
-
-       /* create devices files for legacy/manual use */
-       for (i = 0; i < comedi_num_legacy_minors; i++) {
-               int minor;
-               minor = comedi_alloc_board_minor(NULL);
-               if (minor < 0) {
-                       comedi_cleanup_legacy_minors();
-                       cdev_del(&comedi_cdev);
-                       unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
-                                                COMEDI_NUM_MINORS);
-                       return minor;
-               }
-       }
-
-       return 0;
-}
-
-static void __exit comedi_cleanup(void)
-{
-       int i;
-
-       comedi_cleanup_legacy_minors();
-       for (i = 0; i < COMEDI_NUM_MINORS; ++i)
-               BUG_ON(comedi_file_info_table[i]);
-
-       class_destroy(comedi_class);
-       cdev_del(&comedi_cdev);
-       unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
-
-       comedi_proc_cleanup();
-}
-
-module_init(comedi_init);
-module_exit(comedi_cleanup);
-
-void comedi_error(const struct comedi_device *dev, const char *s)
-{
-       printk(KERN_ERR "comedi%d: %s: %s\n", dev->minor,
-              dev->driver->driver_name, s);
-}
-EXPORT_SYMBOL(comedi_error);
-
-void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
-{
-       struct comedi_async *async = s->async;
-       unsigned runflags = 0;
-       unsigned runflags_mask = 0;
-
-       /* DPRINTK("comedi_event 0x%x\n",mask); */
-
-       if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
-               return;
-
-       if (s->
-           async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
-                            COMEDI_CB_OVERFLOW)) {
-               runflags_mask |= SRF_RUNNING;
-       }
-       /* remember if an error event has occurred, so an error
-        * can be returned the next time the user does a read() */
-       if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
-               runflags_mask |= SRF_ERROR;
-               runflags |= SRF_ERROR;
-       }
-       if (runflags_mask) {
-               /*sets SRF_ERROR and SRF_RUNNING together atomically */
-               comedi_set_subdevice_runflags(s, runflags_mask, runflags);
-       }
-
-       if (async->cb_mask & s->async->events) {
-               if (comedi_get_subdevice_runflags(s) & SRF_USER) {
-                       wake_up_interruptible(&async->wait_head);
-                       if (s->subdev_flags & SDF_CMD_READ)
-                               kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
-                       if (s->subdev_flags & SDF_CMD_WRITE)
-                               kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
-               } else {
-                       if (async->cb_func)
-                               async->cb_func(s->async->events, async->cb_arg);
-               }
-       }
-       s->async->events = 0;
-}
-EXPORT_SYMBOL(comedi_event);
-
-unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
-{
-       unsigned long flags;
-       unsigned runflags;
+       mutex_unlock(&dev->mutex);
 
-       spin_lock_irqsave(&s->spin_lock, flags);
-       runflags = s->runflags;
-       spin_unlock_irqrestore(&s->spin_lock, flags);
-       return runflags;
+       return 0;
 }
-EXPORT_SYMBOL(comedi_get_subdevice_runflags);
 
-static int is_device_busy(struct comedi_device *dev)
+static int comedi_close(struct inode *inode, struct file *file)
 {
-       struct comedi_subdevice *s;
+       const unsigned minor = iminor(inode);
+       struct comedi_subdevice *s = NULL;
        int i;
+       struct comedi_device_file_info *dev_file_info;
+       struct comedi_device *dev;
+       dev_file_info = comedi_get_device_file_info(minor);
 
-       if (!dev->attached)
-               return 0;
+       if (dev_file_info == NULL)
+               return -ENODEV;
+       dev = dev_file_info->device;
+       if (dev == NULL)
+               return -ENODEV;
 
-       for (i = 0; i < dev->n_subdevices; i++) {
-               s = dev->subdevices + i;
-               if (s->busy)
-                       return 1;
-               if (s->async && s->async->mmap_count)
-                       return 1;
+       mutex_lock(&dev->mutex);
+
+       if (dev->subdevices) {
+               for (i = 0; i < dev->n_subdevices; i++) {
+                       s = dev->subdevices + i;
+
+                       if (s->busy == file)
+                               do_cancel(dev, s);
+                       if (s->lock == file)
+                               s->lock = NULL;
+               }
        }
+       if (dev->attached && dev->use_count == 1 && dev->close)
+               dev->close(dev);
+
+       module_put(THIS_MODULE);
+       if (dev->attached)
+               module_put(dev->driver->module);
+
+       dev->use_count--;
+
+       mutex_unlock(&dev->mutex);
+
+       if (file->f_flags & FASYNC)
+               comedi_fasync(-1, file, 0);
 
        return 0;
 }
 
-static void comedi_device_init(struct comedi_device *dev)
+static int comedi_fasync(int fd, struct file *file, int on)
 {
-       memset(dev, 0, sizeof(struct comedi_device));
-       spin_lock_init(&dev->spinlock);
-       mutex_init(&dev->mutex);
-       dev->minor = -1;
-}
+       const unsigned minor = iminor(file->f_dentry->d_inode);
+       struct comedi_device_file_info *dev_file_info;
+       struct comedi_device *dev;
+       dev_file_info = comedi_get_device_file_info(minor);
 
-static void comedi_device_cleanup(struct comedi_device *dev)
-{
+       if (dev_file_info == NULL)
+               return -ENODEV;
+       dev = dev_file_info->device;
        if (dev == NULL)
-               return;
-       mutex_lock(&dev->mutex);
-       comedi_device_detach(dev);
-       mutex_unlock(&dev->mutex);
-       mutex_destroy(&dev->mutex);
+               return -ENODEV;
+
+       return fasync_helper(fd, file, on, &dev->async_queue);
 }
 
-int comedi_alloc_board_minor(struct device *hardware_device)
+const struct file_operations comedi_fops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = comedi_unlocked_ioctl,
+       .compat_ioctl = comedi_compat_ioctl,
+       .open = comedi_open,
+       .release = comedi_close,
+       .read = comedi_read,
+       .write = comedi_write,
+       .mmap = comedi_mmap,
+       .poll = comedi_poll,
+       .fasync = comedi_fasync,
+       .llseek = noop_llseek,
+};
+
+struct class *comedi_class;
+static struct cdev comedi_cdev;
+
+static void comedi_cleanup_legacy_minors(void)
 {
-       struct comedi_device_file_info *info;
-       struct device *csdev;
        unsigned i;
-       int retval;
 
-       info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
-       if (info == NULL)
-               return -ENOMEM;
-       info->device = kzalloc(sizeof(struct comedi_device), GFP_KERNEL);
-       if (info->device == NULL) {
-               kfree(info);
-               return -ENOMEM;
-       }
-       info->hardware_device = hardware_device;
-       comedi_device_init(info->device);
-       spin_lock(&comedi_file_info_table_lock);
-       for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
-               if (comedi_file_info_table[i] == NULL) {
-                       comedi_file_info_table[i] = info;
-                       break;
-               }
-       }
-       spin_unlock(&comedi_file_info_table_lock);
-       if (i == COMEDI_NUM_BOARD_MINORS) {
-               comedi_device_cleanup(info->device);
-               kfree(info->device);
-               kfree(info);
-               printk(KERN_ERR
-                      "comedi: error: "
-                      "ran out of minor numbers for board device files.\n");
-               return -EBUSY;
-       }
-       info->device->minor = i;
-       csdev = device_create(comedi_class, hardware_device,
-                             MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i", i);
-       if (!IS_ERR(csdev))
-               info->device->class_dev = csdev;
-       dev_set_drvdata(csdev, info);
-       retval = device_create_file(csdev, &dev_attr_max_read_buffer_kb);
-       if (retval) {
-               printk(KERN_ERR
-                      "comedi: "
-                      "failed to create sysfs attribute file \"%s\".\n",
-                      dev_attr_max_read_buffer_kb.attr.name);
-               comedi_free_board_minor(i);
-               return retval;
-       }
-       retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
-       if (retval) {
-               printk(KERN_ERR
-                      "comedi: "
-                      "failed to create sysfs attribute file \"%s\".\n",
-                      dev_attr_read_buffer_kb.attr.name);
-               comedi_free_board_minor(i);
-               return retval;
-       }
-       retval = device_create_file(csdev, &dev_attr_max_write_buffer_kb);
-       if (retval) {
-               printk(KERN_ERR
-                      "comedi: "
-                      "failed to create sysfs attribute file \"%s\".\n",
-                      dev_attr_max_write_buffer_kb.attr.name);
-               comedi_free_board_minor(i);
-               return retval;
-       }
-       retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
-       if (retval) {
-               printk(KERN_ERR
-                      "comedi: "
-                      "failed to create sysfs attribute file \"%s\".\n",
-                      dev_attr_write_buffer_kb.attr.name);
+       for (i = 0; i < comedi_num_legacy_minors; i++)
                comedi_free_board_minor(i);
-               return retval;
-       }
-       return i;
 }
 
-void comedi_free_board_minor(unsigned minor)
+static int __init comedi_init(void)
 {
-       struct comedi_device_file_info *info;
+       int i;
+       int retval;
 
-       BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
-       spin_lock(&comedi_file_info_table_lock);
-       info = comedi_file_info_table[minor];
-       comedi_file_info_table[minor] = NULL;
-       spin_unlock(&comedi_file_info_table_lock);
+       printk(KERN_INFO "comedi: version " COMEDI_RELEASE
+              " - http://www.comedi.org\n");
 
-       if (info) {
-               struct comedi_device *dev = info->device;
-               if (dev) {
-                       if (dev->class_dev) {
-                               device_destroy(comedi_class,
-                                              MKDEV(COMEDI_MAJOR, dev->minor));
-                       }
-                       comedi_device_cleanup(dev);
-                       kfree(dev);
-               }
-               kfree(info);
+       if (comedi_num_legacy_minors < 0 ||
+           comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
+               printk(KERN_ERR "comedi: error: invalid value for module "
+                      "parameter \"comedi_num_legacy_minors\".  Valid values "
+                      "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
+               return -EINVAL;
        }
-}
-
-int comedi_find_board_minor(struct device *hardware_device)
-{
-       int minor;
-       struct comedi_device_file_info *info;
 
-       for (minor = 0; minor < COMEDI_NUM_BOARD_MINORS; minor++) {
-               spin_lock(&comedi_file_info_table_lock);
-               info = comedi_file_info_table[minor];
-               if (info && info->hardware_device == hardware_device) {
-                       spin_unlock(&comedi_file_info_table_lock);
-                       return minor;
-               }
-               spin_unlock(&comedi_file_info_table_lock);
-       }
-       return -ENODEV;
-}
+       /*
+        * comedi is unusable if both comedi_autoconfig and
+        * comedi_num_legacy_minors are zero, so we might as well adjust the
+        * defaults in that case
+        */
+       if (comedi_autoconfig == 0 && comedi_num_legacy_minors == 0)
+               comedi_num_legacy_minors = 16;
 
-int comedi_alloc_subdevice_minor(struct comedi_device *dev,
-                                struct comedi_subdevice *s)
-{
-       struct comedi_device_file_info *info;
-       struct device *csdev;
-       unsigned i;
-       int retval;
+       memset(comedi_file_info_table, 0,
+              sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
 
-       info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
-       if (info == NULL)
-               return -ENOMEM;
-       info->device = dev;
-       info->read_subdevice = s;
-       info->write_subdevice = s;
-       spin_lock(&comedi_file_info_table_lock);
-       for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) {
-               if (comedi_file_info_table[i] == NULL) {
-                       comedi_file_info_table[i] = info;
-                       break;
-               }
-       }
-       spin_unlock(&comedi_file_info_table_lock);
-       if (i == COMEDI_NUM_MINORS) {
-               kfree(info);
-               printk(KERN_ERR
-                      "comedi: error: "
-                      "ran out of minor numbers for board device files.\n");
-               return -EBUSY;
-       }
-       s->minor = i;
-       csdev = device_create(comedi_class, dev->class_dev,
-                             MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
-                             dev->minor, (int)(s - dev->subdevices));
-       if (!IS_ERR(csdev))
-               s->class_dev = csdev;
-       dev_set_drvdata(csdev, info);
-       retval = device_create_file(csdev, &dev_attr_max_read_buffer_kb);
-       if (retval) {
-               printk(KERN_ERR
-                      "comedi: "
-                      "failed to create sysfs attribute file \"%s\".\n",
-                      dev_attr_max_read_buffer_kb.attr.name);
-               comedi_free_subdevice_minor(s);
-               return retval;
-       }
-       retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
-       if (retval) {
-               printk(KERN_ERR
-                      "comedi: "
-                      "failed to create sysfs attribute file \"%s\".\n",
-                      dev_attr_read_buffer_kb.attr.name);
-               comedi_free_subdevice_minor(s);
-               return retval;
+       retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+                                       COMEDI_NUM_MINORS, "comedi");
+       if (retval)
+               return -EIO;
+       cdev_init(&comedi_cdev, &comedi_fops);
+       comedi_cdev.owner = THIS_MODULE;
+       kobject_set_name(&comedi_cdev.kobj, "comedi");
+       if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
+               unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+                                        COMEDI_NUM_MINORS);
+               return -EIO;
        }
-       retval = device_create_file(csdev, &dev_attr_max_write_buffer_kb);
-       if (retval) {
-               printk(KERN_ERR
-                      "comedi: "
-                      "failed to create sysfs attribute file \"%s\".\n",
-                      dev_attr_max_write_buffer_kb.attr.name);
-               comedi_free_subdevice_minor(s);
-               return retval;
+       comedi_class = class_create(THIS_MODULE, "comedi");
+       if (IS_ERR(comedi_class)) {
+               printk(KERN_ERR "comedi: failed to create class");
+               cdev_del(&comedi_cdev);
+               unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+                                        COMEDI_NUM_MINORS);
+               return PTR_ERR(comedi_class);
        }
-       retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
-       if (retval) {
-               printk(KERN_ERR
-                      "comedi: "
-                      "failed to create sysfs attribute file \"%s\".\n",
-                      dev_attr_write_buffer_kb.attr.name);
-               comedi_free_subdevice_minor(s);
-               return retval;
+
+       /* XXX requires /proc interface */
+       comedi_proc_init();
+
+       /* create devices files for legacy/manual use */
+       for (i = 0; i < comedi_num_legacy_minors; i++) {
+               int minor;
+               minor = comedi_alloc_board_minor(NULL);
+               if (minor < 0) {
+                       comedi_cleanup_legacy_minors();
+                       cdev_del(&comedi_cdev);
+                       unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+                                                COMEDI_NUM_MINORS);
+                       return minor;
+               }
        }
-       return i;
+
+       return 0;
 }
 
-void comedi_free_subdevice_minor(struct comedi_subdevice *s)
+static void __exit comedi_cleanup(void)
 {
-       struct comedi_device_file_info *info;
-
-       if (s == NULL)
-               return;
-       if (s->minor < 0)
-               return;
+       int i;
 
-       BUG_ON(s->minor >= COMEDI_NUM_MINORS);
-       BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
+       comedi_cleanup_legacy_minors();
+       for (i = 0; i < COMEDI_NUM_MINORS; ++i)
+               BUG_ON(comedi_file_info_table[i]);
 
-       spin_lock(&comedi_file_info_table_lock);
-       info = comedi_file_info_table[s->minor];
-       comedi_file_info_table[s->minor] = NULL;
-       spin_unlock(&comedi_file_info_table_lock);
+       class_destroy(comedi_class);
+       cdev_del(&comedi_cdev);
+       unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
 
-       if (s->class_dev) {
-               device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
-               s->class_dev = NULL;
-       }
-       kfree(info);
+       comedi_proc_cleanup();
 }
 
-struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
-{
-       struct comedi_device_file_info *info;
+module_init(comedi_init);
+module_exit(comedi_cleanup);
 
-       BUG_ON(minor >= COMEDI_NUM_MINORS);
-       spin_lock(&comedi_file_info_table_lock);
-       info = comedi_file_info_table[minor];
-       spin_unlock(&comedi_file_info_table_lock);
-       return info;
+void comedi_error(const struct comedi_device *dev, const char *s)
+{
+       printk(KERN_ERR "comedi%d: %s: %s\n", dev->minor,
+              dev->driver->driver_name, s);
 }
-EXPORT_SYMBOL_GPL(comedi_get_device_file_info);
+EXPORT_SYMBOL(comedi_error);
 
-static int resize_async_buffer(struct comedi_device *dev,
-                              struct comedi_subdevice *s,
-                              struct comedi_async *async, unsigned new_size)
+void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
 {
-       int retval;
+       struct comedi_async *async = s->async;
+       unsigned runflags = 0;
+       unsigned runflags_mask = 0;
 
-       if (new_size > async->max_bufsize)
-               return -EPERM;
+       /* DPRINTK("comedi_event 0x%x\n",mask); */
 
-       if (s->busy) {
-               DPRINTK("subdevice is busy, cannot resize buffer\n");
-               return -EBUSY;
+       if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
+               return;
+
+       if (s->
+           async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
+                            COMEDI_CB_OVERFLOW)) {
+               runflags_mask |= SRF_RUNNING;
        }
-       if (async->mmap_count) {
-               DPRINTK("subdevice is mmapped, cannot resize buffer\n");
-               return -EBUSY;
+       /* remember if an error event has occurred, so an error
+        * can be returned the next time the user does a read() */
+       if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
+               runflags_mask |= SRF_ERROR;
+               runflags |= SRF_ERROR;
        }
-
-       if (!async->prealloc_buf)
-               return -EINVAL;
-
-       /* make sure buffer is an integral number of pages
-        * (we round up) */
-       new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
-
-       retval = comedi_buf_alloc(dev, s, new_size);
-       if (retval < 0)
-               return retval;
-
-       if (s->buf_change) {
-               retval = s->buf_change(dev, s, new_size);
-               if (retval < 0)
-                       return retval;
+       if (runflags_mask) {
+               /*sets SRF_ERROR and SRF_RUNNING together atomically */
+               comedi_set_subdevice_runflags(s, runflags_mask, runflags);
        }
 
-       DPRINTK("comedi%i subd %d buffer resized to %i bytes\n",
-               dev->minor, (int)(s - dev->subdevices), async->prealloc_bufsz);
-       return 0;
+       if (async->cb_mask & s->async->events) {
+               if (comedi_get_subdevice_runflags(s) & SRF_USER) {
+                       wake_up_interruptible(&async->wait_head);
+                       if (s->subdev_flags & SDF_CMD_READ)
+                               kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+                       if (s->subdev_flags & SDF_CMD_WRITE)
+                               kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
+               } else {
+                       if (async->cb_func)
+                               async->cb_func(s->async->events, async->cb_arg);
+               }
+       }
+       s->async->events = 0;
 }
+EXPORT_SYMBOL(comedi_event);
 
-/* sysfs attribute files */
-
-static const unsigned bytes_per_kibi = 1024;
-
-static ssize_t show_max_read_buffer_kb(struct device *dev,
-                                      struct device_attribute *attr, char *buf)
+unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
 {
-       ssize_t retval;
-       struct comedi_device_file_info *info = dev_get_drvdata(dev);
-       unsigned max_buffer_size_kb = 0;
-       struct comedi_subdevice *const read_subdevice =
-           comedi_get_read_subdevice(info);
-
-       mutex_lock(&info->device->mutex);
-       if (read_subdevice &&
-           (read_subdevice->subdev_flags & SDF_CMD_READ) &&
-           read_subdevice->async) {
-               max_buffer_size_kb = read_subdevice->async->max_bufsize /
-                   bytes_per_kibi;
-       }
-       retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
-       mutex_unlock(&info->device->mutex);
+       unsigned long flags;
+       unsigned runflags;
 
-       return retval;
+       spin_lock_irqsave(&s->spin_lock, flags);
+       runflags = s->runflags;
+       spin_unlock_irqrestore(&s->spin_lock, flags);
+       return runflags;
 }
+EXPORT_SYMBOL(comedi_get_subdevice_runflags);
 
-static ssize_t store_max_read_buffer_kb(struct device *dev,
-                                       struct device_attribute *attr,
-                                       const char *buf, size_t count)
+static int is_device_busy(struct comedi_device *dev)
 {
-       struct comedi_device_file_info *info = dev_get_drvdata(dev);
-       unsigned int new_max_size_kb;
-       unsigned int new_max_size;
-       int ret;
-       struct comedi_subdevice *const read_subdevice =
-           comedi_get_read_subdevice(info);
+       struct comedi_subdevice *s;
+       int i;
 
-       ret = kstrtouint(buf, 10, &new_max_size_kb);
-       if (ret)
-               return ret;
-       if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
-               return -EINVAL;
-       new_max_size = new_max_size_kb * bytes_per_kibi;
+       if (!dev->attached)
+               return 0;
 
-       mutex_lock(&info->device->mutex);
-       if (read_subdevice == NULL ||
-           (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
-           read_subdevice->async == NULL) {
-               mutex_unlock(&info->device->mutex);
-               return -EINVAL;
+       for (i = 0; i < dev->n_subdevices; i++) {
+               s = dev->subdevices + i;
+               if (s->busy)
+                       return 1;
+               if (s->async && s->async->mmap_count)
+                       return 1;
        }
-       read_subdevice->async->max_bufsize = new_max_size;
-       mutex_unlock(&info->device->mutex);
 
-       return count;
+       return 0;
 }
 
-static struct device_attribute dev_attr_max_read_buffer_kb = {
-       .attr = {
-                .name = "max_read_buffer_kb",
-                .mode = S_IRUGO | S_IWUSR},
-       .show = &show_max_read_buffer_kb,
-       .store = &store_max_read_buffer_kb
-};
+static void comedi_device_init(struct comedi_device *dev)
+{
+       memset(dev, 0, sizeof(struct comedi_device));
+       spin_lock_init(&dev->spinlock);
+       mutex_init(&dev->mutex);
+       dev->minor = -1;
+}
 
-static ssize_t show_read_buffer_kb(struct device *dev,
-                                  struct device_attribute *attr, char *buf)
+static void comedi_device_cleanup(struct comedi_device *dev)
 {
-       ssize_t retval;
-       struct comedi_device_file_info *info = dev_get_drvdata(dev);
-       unsigned buffer_size_kb = 0;
-       struct comedi_subdevice *const read_subdevice =
-           comedi_get_read_subdevice(info);
+       if (dev == NULL)
+               return;
+       mutex_lock(&dev->mutex);
+       comedi_device_detach(dev);
+       mutex_unlock(&dev->mutex);
+       mutex_destroy(&dev->mutex);
+}
+
+int comedi_alloc_board_minor(struct device *hardware_device)
+{
+       struct comedi_device_file_info *info;
+       struct device *csdev;
+       unsigned i;
+       int retval;
+
+       info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
+       if (info == NULL)
+               return -ENOMEM;
+       info->device = kzalloc(sizeof(struct comedi_device), GFP_KERNEL);
+       if (info->device == NULL) {
+               kfree(info);
+               return -ENOMEM;
+       }
+       info->hardware_device = hardware_device;
+       comedi_device_init(info->device);
+       spin_lock(&comedi_file_info_table_lock);
+       for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
+               if (comedi_file_info_table[i] == NULL) {
+                       comedi_file_info_table[i] = info;
+                       break;
+               }
+       }
+       spin_unlock(&comedi_file_info_table_lock);
+       if (i == COMEDI_NUM_BOARD_MINORS) {
+               comedi_device_cleanup(info->device);
+               kfree(info->device);
+               kfree(info);
+               printk(KERN_ERR
+                      "comedi: error: "
+                      "ran out of minor numbers for board device files.\n");
+               return -EBUSY;
+       }
+       info->device->minor = i;
+       csdev = device_create(comedi_class, hardware_device,
+                             MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i", i);
+       if (!IS_ERR(csdev))
+               info->device->class_dev = csdev;
+       dev_set_drvdata(csdev, info);
 
-       mutex_lock(&info->device->mutex);
-       if (read_subdevice &&
-           (read_subdevice->subdev_flags & SDF_CMD_READ) &&
-           read_subdevice->async) {
-               buffer_size_kb = read_subdevice->async->prealloc_bufsz /
-                   bytes_per_kibi;
+       retval = sysfs_create_group(&csdev->kobj, &comedi_sysfs_files);
+       if (retval) {
+               printk(KERN_ERR
+                      "comedi: failed to create sysfs attribute files\n");
+               comedi_free_board_minor(i);
+               return retval;
        }
-       retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
-       mutex_unlock(&info->device->mutex);
 
-       return retval;
+       return i;
 }
 
-static ssize_t store_read_buffer_kb(struct device *dev,
-                                   struct device_attribute *attr,
-                                   const char *buf, size_t count)
+void comedi_free_board_minor(unsigned minor)
 {
-       struct comedi_device_file_info *info = dev_get_drvdata(dev);
-       unsigned int new_size_kb;
-       unsigned int new_size;
-       int retval;
-       int ret;
-       struct comedi_subdevice *const read_subdevice =
-           comedi_get_read_subdevice(info);
+       struct comedi_device_file_info *info;
 
-       ret = kstrtouint(buf, 10, &new_size_kb);
-       if (ret)
-               return ret;
-       if (new_size_kb > (UINT_MAX / bytes_per_kibi))
-               return -EINVAL;
-       new_size = new_size_kb * bytes_per_kibi;
+       BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
+       spin_lock(&comedi_file_info_table_lock);
+       info = comedi_file_info_table[minor];
+       comedi_file_info_table[minor] = NULL;
+       spin_unlock(&comedi_file_info_table_lock);
 
-       mutex_lock(&info->device->mutex);
-       if (read_subdevice == NULL ||
-           (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
-           read_subdevice->async == NULL) {
-               mutex_unlock(&info->device->mutex);
-               return -EINVAL;
+       if (info) {
+               struct comedi_device *dev = info->device;
+               if (dev) {
+                       if (dev->class_dev) {
+                               device_destroy(comedi_class,
+                                              MKDEV(COMEDI_MAJOR, dev->minor));
+                       }
+                       comedi_device_cleanup(dev);
+                       kfree(dev);
+               }
+               kfree(info);
        }
-       retval = resize_async_buffer(info->device, read_subdevice,
-                                    read_subdevice->async, new_size);
-       mutex_unlock(&info->device->mutex);
-
-       if (retval < 0)
-               return retval;
-       return count;
 }
 
-static struct device_attribute dev_attr_read_buffer_kb = {
-       .attr = {
-                .name = "read_buffer_kb",
-                .mode = S_IRUGO | S_IWUSR | S_IWGRP},
-       .show = &show_read_buffer_kb,
-       .store = &store_read_buffer_kb
-};
-
-static ssize_t show_max_write_buffer_kb(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
+int comedi_find_board_minor(struct device *hardware_device)
 {
-       ssize_t retval;
-       struct comedi_device_file_info *info = dev_get_drvdata(dev);
-       unsigned max_buffer_size_kb = 0;
-       struct comedi_subdevice *const write_subdevice =
-           comedi_get_write_subdevice(info);
+       int minor;
+       struct comedi_device_file_info *info;
 
-       mutex_lock(&info->device->mutex);
-       if (write_subdevice &&
-           (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
-           write_subdevice->async) {
-               max_buffer_size_kb = write_subdevice->async->max_bufsize /
-                   bytes_per_kibi;
+       for (minor = 0; minor < COMEDI_NUM_BOARD_MINORS; minor++) {
+               spin_lock(&comedi_file_info_table_lock);
+               info = comedi_file_info_table[minor];
+               if (info && info->hardware_device == hardware_device) {
+                       spin_unlock(&comedi_file_info_table_lock);
+                       return minor;
+               }
+               spin_unlock(&comedi_file_info_table_lock);
        }
-       retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
-       mutex_unlock(&info->device->mutex);
-
-       return retval;
+       return -ENODEV;
 }
 
-static ssize_t store_max_write_buffer_kb(struct device *dev,
-                                        struct device_attribute *attr,
-                                        const char *buf, size_t count)
+int comedi_alloc_subdevice_minor(struct comedi_device *dev,
+                                struct comedi_subdevice *s)
 {
-       struct comedi_device_file_info *info = dev_get_drvdata(dev);
-       unsigned int new_max_size_kb;
-       unsigned int new_max_size;
-       int ret;
-       struct comedi_subdevice *const write_subdevice =
-           comedi_get_write_subdevice(info);
+       struct comedi_device_file_info *info;
+       struct device *csdev;
+       unsigned i;
+       int retval;
 
-       ret = kstrtouint(buf, 10, &new_max_size_kb);
-       if (ret)
-               return ret;
-       if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
-               return -EINVAL;
-       new_max_size = new_max_size_kb * bytes_per_kibi;
+       info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
+       if (info == NULL)
+               return -ENOMEM;
+       info->device = dev;
+       info->read_subdevice = s;
+       info->write_subdevice = s;
+       spin_lock(&comedi_file_info_table_lock);
+       for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) {
+               if (comedi_file_info_table[i] == NULL) {
+                       comedi_file_info_table[i] = info;
+                       break;
+               }
+       }
+       spin_unlock(&comedi_file_info_table_lock);
+       if (i == COMEDI_NUM_MINORS) {
+               kfree(info);
+               printk(KERN_ERR
+                      "comedi: error: "
+                      "ran out of minor numbers for board device files.\n");
+               return -EBUSY;
+       }
+       s->minor = i;
+       csdev = device_create(comedi_class, dev->class_dev,
+                             MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
+                             dev->minor, (int)(s - dev->subdevices));
+       if (!IS_ERR(csdev))
+               s->class_dev = csdev;
+       dev_set_drvdata(csdev, info);
 
-       mutex_lock(&info->device->mutex);
-       if (write_subdevice == NULL ||
-           (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
-           write_subdevice->async == NULL) {
-               mutex_unlock(&info->device->mutex);
-               return -EINVAL;
+       retval = sysfs_create_group(&csdev->kobj, &comedi_sysfs_files);
+       if (retval) {
+               printk(KERN_ERR
+                      "comedi: failed to create sysfs attribute files\n");
+               comedi_free_subdevice_minor(s);
+               return retval;
        }
-       write_subdevice->async->max_bufsize = new_max_size;
-       mutex_unlock(&info->device->mutex);
 
-       return count;
+       return i;
 }
 
-static struct device_attribute dev_attr_max_write_buffer_kb = {
-       .attr = {
-                .name = "max_write_buffer_kb",
-                .mode = S_IRUGO | S_IWUSR},
-       .show = &show_max_write_buffer_kb,
-       .store = &store_max_write_buffer_kb
-};
-
-static ssize_t show_write_buffer_kb(struct device *dev,
-                                   struct device_attribute *attr, char *buf)
+void comedi_free_subdevice_minor(struct comedi_subdevice *s)
 {
-       ssize_t retval;
-       struct comedi_device_file_info *info = dev_get_drvdata(dev);
-       unsigned buffer_size_kb = 0;
-       struct comedi_subdevice *const write_subdevice =
-           comedi_get_write_subdevice(info);
-
-       mutex_lock(&info->device->mutex);
-       if (write_subdevice &&
-           (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
-           write_subdevice->async) {
-               buffer_size_kb = write_subdevice->async->prealloc_bufsz /
-                   bytes_per_kibi;
-       }
-       retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
-       mutex_unlock(&info->device->mutex);
+       struct comedi_device_file_info *info;
 
-       return retval;
-}
+       if (s == NULL)
+               return;
+       if (s->minor < 0)
+               return;
 
-static ssize_t store_write_buffer_kb(struct device *dev,
-                                    struct device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       struct comedi_device_file_info *info = dev_get_drvdata(dev);
-       unsigned int new_size_kb;
-       unsigned int new_size;
-       int retval;
-       int ret;
-       struct comedi_subdevice *const write_subdevice =
-           comedi_get_write_subdevice(info);
+       BUG_ON(s->minor >= COMEDI_NUM_MINORS);
+       BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
 
-       ret = kstrtouint(buf, 10, &new_size_kb);
-       if (ret)
-               return ret;
-       if (new_size_kb > (UINT_MAX / bytes_per_kibi))
-               return -EINVAL;
-       new_size = ((uint64_t) new_size_kb) * bytes_per_kibi;
+       spin_lock(&comedi_file_info_table_lock);
+       info = comedi_file_info_table[s->minor];
+       comedi_file_info_table[s->minor] = NULL;
+       spin_unlock(&comedi_file_info_table_lock);
 
-       mutex_lock(&info->device->mutex);
-       if (write_subdevice == NULL ||
-           (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
-           write_subdevice->async == NULL) {
-               mutex_unlock(&info->device->mutex);
-               return -EINVAL;
+       if (s->class_dev) {
+               device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
+               s->class_dev = NULL;
        }
-       retval = resize_async_buffer(info->device, write_subdevice,
-                                    write_subdevice->async, new_size);
-       mutex_unlock(&info->device->mutex);
-
-       if (retval < 0)
-               return retval;
-       return count;
+       kfree(info);
 }
 
-static struct device_attribute dev_attr_write_buffer_kb = {
-       .attr = {
-                .name = "write_buffer_kb",
-                .mode = S_IRUGO | S_IWUSR | S_IWGRP},
-       .show = &show_write_buffer_kb,
-       .store = &store_write_buffer_kb
-};
+struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
+{
+       struct comedi_device_file_info *info;
+
+       BUG_ON(minor >= COMEDI_NUM_MINORS);
+       spin_lock(&comedi_file_info_table_lock);
+       info = comedi_file_info_table[minor];
+       spin_unlock(&comedi_file_info_table_lock);
+       return info;
+}
+EXPORT_SYMBOL_GPL(comedi_get_device_file_info);