UBUNTU: SAUCE: staging: comedi: Add module parameters for default buffer size
[linux-flexiantxendom0.git] / drivers / staging / comedi / comedi_fops.c
index 135c80a..57742b1 100644 (file)
 #include <linux/io.h>
 #include <linux/uaccess.h>
 
-/* #include "kvmem.h" */
+#include "internal.h"
 
 MODULE_AUTHOR("http://www.comedi.org");
 MODULE_DESCRIPTION("Comedi core module");
 MODULE_LICENSE("GPL");
 
+#define DEFAULT_BUF_MAXSIZE_KB 64
+#define DEFAULT_BUF_SIZE_KB 64
+
 #ifdef CONFIG_COMEDI_DEBUG
 int comedi_debug;
 EXPORT_SYMBOL(comedi_debug);
-module_param(comedi_debug, int, 0644);
+module_param(comedi_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(comedi_debug,
+                "enable comedi core and driver debugging if non-zero (default 0)"
+               );
 #endif
 
 int comedi_autoconfig = 1;
-module_param(comedi_autoconfig, bool, 0444);
-
-int comedi_num_legacy_minors;
-module_param(comedi_num_legacy_minors, int, 0444);
+module_param(comedi_autoconfig, bool, S_IRUGO);
+MODULE_PARM_DESC(comedi_autoconfig,
+                "enable drivers to auto-configure comedi devices (default 1)");
+
+static int comedi_num_legacy_minors;
+module_param(comedi_num_legacy_minors, int, S_IRUGO);
+MODULE_PARM_DESC(comedi_num_legacy_minors,
+                "number of comedi minor devices to reserve for non-auto-configured devices (default 0)"
+               );
+
+unsigned int comedi_default_buf_size_kb = DEFAULT_BUF_SIZE_KB;
+module_param(comedi_default_buf_size_kb, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(comedi_default_buf_size_kb,
+                "default asynchronous buffer size in KiB (default "
+                __MODULE_STRING(DEFAULT_BUF_SIZE_KB) ")");
+
+unsigned int comedi_default_buf_maxsize_kb = DEFAULT_BUF_MAXSIZE_KB;
+module_param(comedi_default_buf_maxsize_kb, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(comedi_default_buf_maxsize_kb,
+                "default maximum size of asynchronous buffer in KiB (default "
+                __MODULE_STRING(DEFAULT_BUF_MAXSIZE_KB) ")");
 
 static DEFINE_SPINLOCK(comedi_file_info_table_lock);
 static struct comedi_device_file_info
 *comedi_file_info_table[COMEDI_NUM_MINORS];
 
 static int do_devconfig_ioctl(struct comedi_device *dev,
-                             struct comedi_devconfig *arg);
-static int do_bufconfig_ioctl(struct comedi_device *dev, void *arg);
+                             struct comedi_devconfig __user *arg);
+static int do_bufconfig_ioctl(struct comedi_device *dev,
+                             struct comedi_bufconfig __user *arg);
 static int do_devinfo_ioctl(struct comedi_device *dev,
-                           struct comedi_devinfo *arg, struct file *file);
+                           struct comedi_devinfo __user *arg,
+                           struct file *file);
 static int do_subdinfo_ioctl(struct comedi_device *dev,
-                            struct comedi_subdinfo *arg, void *file);
+                            struct comedi_subdinfo __user *arg, void *file);
 static int do_chaninfo_ioctl(struct comedi_device *dev,
-                            struct comedi_chaninfo *arg);
-static int do_bufinfo_ioctl(struct comedi_device *dev, void *arg);
-static int do_cmd_ioctl(struct comedi_device *dev, void *arg, void *file);
+                            struct comedi_chaninfo __user *arg);
+static int do_bufinfo_ioctl(struct comedi_device *dev,
+                           struct comedi_bufinfo __user *arg, void *file);
+static int do_cmd_ioctl(struct comedi_device *dev,
+                       struct comedi_cmd __user *arg, void *file);
 static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
                         void *file);
 static int do_unlock_ioctl(struct comedi_device *dev, unsigned int arg,
                           void *file);
 static int do_cancel_ioctl(struct comedi_device *dev, unsigned int arg,
                           void *file);
-static int do_cmdtest_ioctl(struct comedi_device *dev, void *arg, void *file);
-static int do_insnlist_ioctl(struct comedi_device *dev, void *arg, void *file);
-static int do_insn_ioctl(struct comedi_device *dev, void *arg, void *file);
+static int do_cmdtest_ioctl(struct comedi_device *dev,
+                           struct comedi_cmd __user *arg, void *file);
+static int do_insnlist_ioctl(struct comedi_device *dev,
+                            struct comedi_insnlist __user *arg, void *file);
+static int do_insn_ioctl(struct comedi_device *dev,
+                        struct comedi_insn __user *arg, void *file);
 static int do_poll_ioctl(struct comedi_device *dev, unsigned int subd,
                         void *file);
 
-extern void do_become_nonbusy(struct comedi_device *dev,
+static void do_become_nonbusy(struct comedi_device *dev,
                              struct comedi_subdevice *s);
 static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
 
@@ -129,7 +159,8 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
        /* Device config is special, because it must work on
         * an unconfigured device. */
        if (cmd == COMEDI_DEVCONFIG) {
-               rc = do_devconfig_ioctl(dev, (void *)arg);
+               rc = do_devconfig_ioctl(dev,
+                                       (struct comedi_devconfig __user *)arg);
                goto done;
        }
 
@@ -141,22 +172,28 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
 
        switch (cmd) {
        case COMEDI_BUFCONFIG:
-               rc = do_bufconfig_ioctl(dev, (void *)arg);
+               rc = do_bufconfig_ioctl(dev,
+                                       (struct comedi_bufconfig __user *)arg);
                break;
        case COMEDI_DEVINFO:
-               rc = do_devinfo_ioctl(dev, (void *)arg, file);
+               rc = do_devinfo_ioctl(dev, (struct comedi_devinfo __user *)arg,
+                                     file);
                break;
        case COMEDI_SUBDINFO:
-               rc = do_subdinfo_ioctl(dev, (void *)arg, file);
+               rc = do_subdinfo_ioctl(dev,
+                                      (struct comedi_subdinfo __user *)arg,
+                                      file);
                break;
        case COMEDI_CHANINFO:
-               rc = do_chaninfo_ioctl(dev, (void *)arg);
+               rc = do_chaninfo_ioctl(dev, (void __user *)arg);
                break;
        case COMEDI_RANGEINFO:
-               rc = do_rangeinfo_ioctl(dev, (void *)arg);
+               rc = do_rangeinfo_ioctl(dev, (void __user *)arg);
                break;
        case COMEDI_BUFINFO:
-               rc = do_bufinfo_ioctl(dev, (void *)arg);
+               rc = do_bufinfo_ioctl(dev,
+                                     (struct comedi_bufinfo __user *)arg,
+                                     file);
                break;
        case COMEDI_LOCK:
                rc = do_lock_ioctl(dev, arg, file);
@@ -168,16 +205,20 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
                rc = do_cancel_ioctl(dev, arg, file);
                break;
        case COMEDI_CMD:
-               rc = do_cmd_ioctl(dev, (void *)arg, file);
+               rc = do_cmd_ioctl(dev, (struct comedi_cmd __user *)arg, file);
                break;
        case COMEDI_CMDTEST:
-               rc = do_cmdtest_ioctl(dev, (void *)arg, file);
+               rc = do_cmdtest_ioctl(dev, (struct comedi_cmd __user *)arg,
+                                     file);
                break;
        case COMEDI_INSNLIST:
-               rc = do_insnlist_ioctl(dev, (void *)arg, file);
+               rc = do_insnlist_ioctl(dev,
+                                      (struct comedi_insnlist __user *)arg,
+                                      file);
                break;
        case COMEDI_INSN:
-               rc = do_insn_ioctl(dev, (void *)arg, file);
+               rc = do_insn_ioctl(dev, (struct comedi_insn __user *)arg,
+                                  file);
                break;
        case COMEDI_POLL:
                rc = do_poll_ioctl(dev, arg, file);
@@ -206,7 +247,7 @@ done:
                none
 */
 static int do_devconfig_ioctl(struct comedi_device *dev,
-                             struct comedi_devconfig *arg)
+                             struct comedi_devconfig __user *arg)
 {
        struct comedi_devconfig it;
        int ret;
@@ -286,7 +327,8 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
                modified bufconfig at arg
 
 */
-static int do_bufconfig_ioctl(struct comedi_device *dev, void *arg)
+static int do_bufconfig_ioctl(struct comedi_device *dev,
+                             struct comedi_bufconfig __user *arg)
 {
        struct comedi_bufconfig bc;
        struct comedi_async *async;
@@ -347,7 +389,8 @@ copyback:
 
 */
 static int do_devinfo_ioctl(struct comedi_device *dev,
-                           struct comedi_devinfo *arg, struct file *file)
+                           struct comedi_devinfo __user *arg,
+                           struct file *file)
 {
        struct comedi_devinfo devinfo;
        const unsigned minor = iminor(file->f_dentry->d_inode);
@@ -363,8 +406,8 @@ static int do_devinfo_ioctl(struct comedi_device *dev,
        /* fill devinfo structure */
        devinfo.version_code = COMEDI_VERSION_CODE;
        devinfo.n_subdevs = dev->n_subdevices;
-       memcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
-       memcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
+       strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
+       strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
 
        if (read_subdev)
                devinfo.read_subdevice = read_subdev - dev->subdevices;
@@ -397,7 +440,7 @@ static int do_devinfo_ioctl(struct comedi_device *dev,
 
 */
 static int do_subdinfo_ioctl(struct comedi_device *dev,
-                            struct comedi_subdinfo *arg, void *file)
+                            struct comedi_subdinfo __user *arg, void *file)
 {
        int ret, i;
        struct comedi_subdinfo *tmp, *us;
@@ -479,7 +522,7 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
 
 */
 static int do_chaninfo_ioctl(struct comedi_device *dev,
-                            struct comedi_chaninfo *arg)
+                            struct comedi_chaninfo __user *arg)
 {
        struct comedi_subdevice *s;
        struct comedi_chaninfo it;
@@ -517,7 +560,8 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
 
                        x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) |
                            (s->range_table_list[i]->length);
-                       put_user(x, it.rangelist + i);
+                       if (put_user(x, it.rangelist + i))
+                               return -EFAULT;
                }
 #if 0
                if (copy_to_user(it.rangelist, s->range_type_list,
@@ -543,7 +587,8 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
     modified bufinfo at arg
 
   */
-static int do_bufinfo_ioctl(struct comedi_device *dev, void *arg)
+static int do_bufinfo_ioctl(struct comedi_device *dev,
+                           struct comedi_bufinfo __user *arg, void *file)
 {
        struct comedi_bufinfo bi;
        struct comedi_subdevice *s;
@@ -556,6 +601,10 @@ static int do_bufinfo_ioctl(struct comedi_device *dev, void *arg)
                return -EINVAL;
 
        s = dev->subdevices + bi.subdevice;
+
+       if (s->lock && s->lock != file)
+               return -EACCES;
+
        async = s->async;
 
        if (!async) {
@@ -564,8 +613,17 @@ static int do_bufinfo_ioctl(struct comedi_device *dev, void *arg)
                bi.buf_read_ptr = 0;
                bi.buf_write_count = 0;
                bi.buf_read_count = 0;
+               bi.bytes_read = 0;
+               bi.bytes_written = 0;
                goto copyback;
        }
+       if (!s->busy) {
+               bi.bytes_read = 0;
+               bi.bytes_written = 0;
+               goto copyback_position;
+       }
+       if (s->busy != file)
+               return -EACCES;
 
        if (bi.bytes_read && (s->subdev_flags & SDF_CMD_READ)) {
                bi.bytes_read = comedi_buf_read_alloc(async, bi.bytes_read);
@@ -584,6 +642,7 @@ static int do_bufinfo_ioctl(struct comedi_device *dev, void *arg)
                comedi_buf_write_free(async, bi.bytes_written);
        }
 
+copyback_position:
        bi.buf_write_count = async->buf_write_count;
        bi.buf_write_ptr = async->buf_write_ptr;
        bi.buf_read_count = async->buf_read_count;
@@ -615,7 +674,8 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
  */
 /* arbitrary limits */
 #define MAX_SAMPLES 256
-static int do_insnlist_ioctl(struct comedi_device *dev, void *arg, void *file)
+static int do_insnlist_ioctl(struct comedi_device *dev,
+                            struct comedi_insnlist __user *arg, void *file)
 {
        struct comedi_insnlist insnlist;
        struct comedi_insn *insns = NULL;
@@ -634,7 +694,7 @@ static int do_insnlist_ioctl(struct comedi_device *dev, void *arg, void *file)
        }
 
        insns =
-           kmalloc(sizeof(struct comedi_insn) * insnlist.n_insns, GFP_KERNEL);
+           kcalloc(insnlist.n_insns, sizeof(struct comedi_insn), GFP_KERNEL);
        if (!insns) {
                DPRINTK("kmalloc failed\n");
                ret = -ENOMEM;
@@ -873,9 +933,28 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
                case INSN_BITS:
                        if (insn->n != 2) {
                                ret = -EINVAL;
-                               break;
+                       } else {
+                               /* Most drivers ignore the base channel in
+                                * insn->chanspec.  Fix this here if
+                                * the subdevice has <= 32 channels.  */
+                               unsigned int shift;
+                               unsigned int orig_mask;
+
+                               orig_mask = data[0];
+                               if (s->n_chan <= 32) {
+                                       shift = CR_CHAN(insn->chanspec);
+                                       if (shift > 0) {
+                                               insn->chanspec = 0;
+                                               data[0] <<= shift;
+                                               data[1] <<= shift;
+                                       }
+                               } else
+                                       shift = 0;
+                               ret = s->insn_bits(dev, s, insn, data);
+                               data[0] = orig_mask;
+                               if (shift > 0)
+                                       data[1] >>= shift;
                        }
-                       ret = s->insn_bits(dev, s, insn, data);
                        break;
                case INSN_CONFIG:
                        ret = check_insn_config_length(insn, data);
@@ -909,7 +988,8 @@ out:
  *     writes:
  *             data (for reads)
  */
-static int do_insn_ioctl(struct comedi_device *dev, void *arg, void *file)
+static int do_insn_ioctl(struct comedi_device *dev,
+                        struct comedi_insn __user *arg, void *file)
 {
        struct comedi_insn insn;
        unsigned int *data = NULL;
@@ -930,8 +1010,9 @@ static int do_insn_ioctl(struct comedi_device *dev, void *arg, void *file)
        if (insn.n > MAX_SAMPLES)
                insn.n = MAX_SAMPLES;
        if (insn.insn & INSN_MASK_WRITE) {
-               if (copy_from_user
-                   (data, insn.data, insn.n * sizeof(unsigned int))) {
+               if (copy_from_user(data,
+                                  insn.data,
+                                  insn.n * sizeof(unsigned int))) {
                        ret = -EFAULT;
                        goto error;
                }
@@ -940,8 +1021,9 @@ static int do_insn_ioctl(struct comedi_device *dev, void *arg, void *file)
        if (ret < 0)
                goto error;
        if (insn.insn & INSN_MASK_READ) {
-               if (copy_to_user
-                   (insn.data, data, insn.n * sizeof(unsigned int))) {
+               if (copy_to_user(insn.data,
+                                data,
+                                insn.n * sizeof(unsigned int))) {
                        ret = -EFAULT;
                        goto error;
                }
@@ -965,30 +1047,16 @@ static void comedi_set_subdevice_runflags(struct comedi_subdevice *s,
        spin_unlock_irqrestore(&s->spin_lock, flags);
 }
 
-/*
-       COMEDI_CMD
-       command ioctl
-
-       arg:
-               pointer to cmd structure
-
-       reads:
-               cmd structure at arg
-               channel/range list
-
-       writes:
-               modified cmd structure at arg
-
-*/
-static int do_cmd_ioctl(struct comedi_device *dev, void *arg, void *file)
+static int do_cmd_ioctl(struct comedi_device *dev,
+                       struct comedi_cmd __user *cmd, void *file)
 {
        struct comedi_cmd user_cmd;
        struct comedi_subdevice *s;
        struct comedi_async *async;
        int ret = 0;
-       unsigned int *chanlist_saver = NULL;
+       unsigned int __user *chanlist_saver = NULL;
 
-       if (copy_from_user(&user_cmd, arg, sizeof(struct comedi_cmd))) {
+       if (copy_from_user(&user_cmd, cmd, sizeof(struct comedi_cmd))) {
                DPRINTK("bad cmd address\n");
                return -EFAULT;
        }
@@ -1063,7 +1131,9 @@ static int do_cmd_ioctl(struct comedi_device *dev, void *arg, void *file)
        }
 
        /* make sure each element in channel/gain list is valid */
-       ret = comedi_check_chanlist(s, async->cmd.chanlist_len, async->cmd.chanlist);
+       ret = comedi_check_chanlist(s,
+                                   async->cmd.chanlist_len,
+                                   async->cmd.chanlist);
        if (ret < 0) {
                DPRINTK("bad chanlist\n");
                goto cleanup;
@@ -1077,7 +1147,7 @@ static int do_cmd_ioctl(struct comedi_device *dev, void *arg, void *file)
                /* restore chanlist pointer before copying back */
                user_cmd.chanlist = chanlist_saver;
                user_cmd.data = NULL;
-               if (copy_to_user(arg, &user_cmd, sizeof(struct comedi_cmd))) {
+               if (copy_to_user(cmd, &user_cmd, sizeof(struct comedi_cmd))) {
                        DPRINTK("fault writing cmd\n");
                        ret = -EFAULT;
                        goto cleanup;
@@ -1127,13 +1197,14 @@ cleanup:
                modified cmd structure at arg
 
 */
-static int do_cmdtest_ioctl(struct comedi_device *dev, void *arg, void *file)
+static int do_cmdtest_ioctl(struct comedi_device *dev,
+                           struct comedi_cmd __user *arg, void *file)
 {
        struct comedi_cmd user_cmd;
        struct comedi_subdevice *s;
        int ret = 0;
        unsigned int *chanlist = NULL;
-       unsigned int *chanlist_saver = NULL;
+       unsigned int __user *chanlist_saver = NULL;
 
        if (copy_from_user(&user_cmd, arg, sizeof(struct comedi_cmd))) {
                DPRINTK("bad cmd address\n");
@@ -1243,10 +1314,10 @@ static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
                s->lock = file;
        spin_unlock_irqrestore(&s->spin_lock, flags);
 
+#if 0
        if (ret < 0)
                return ret;
 
-#if 0
        if (s->lock_f)
                ret = s->lock_f(dev, s);
 #endif
@@ -1384,7 +1455,21 @@ static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
        return ret;
 }
 
-void comedi_unmap(struct vm_area_struct *area)
+
+static void comedi_vm_open(struct vm_area_struct *area)
+{
+       struct comedi_async *async;
+       struct comedi_device *dev;
+
+       async = area->vm_private_data;
+       dev = async->subdevice->device;
+
+       mutex_lock(&dev->mutex);
+       async->mmap_count++;
+       mutex_unlock(&dev->mutex);
+}
+
+static void comedi_vm_close(struct vm_area_struct *area)
 {
        struct comedi_async *async;
        struct comedi_device *dev;
@@ -1398,15 +1483,13 @@ void comedi_unmap(struct vm_area_struct *area)
 }
 
 static struct vm_operations_struct comedi_vm_ops = {
-       .close = comedi_unmap,
+       .open = comedi_vm_open,
+       .close = comedi_vm_close,
 };
 
 static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
 {
        const unsigned minor = iminor(file->f_dentry->d_inode);
-       struct comedi_device_file_info *dev_file_info =
-           comedi_get_device_file_info(minor);
-       struct comedi_device *dev = dev_file_info->device;
        struct comedi_async *async = NULL;
        unsigned long start = vma->vm_start;
        unsigned long size;
@@ -1414,6 +1497,15 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
        int i;
        int retval;
        struct comedi_subdevice *s;
+       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->attached) {
@@ -1480,11 +1572,17 @@ static unsigned int comedi_poll(struct file *file, poll_table * wait)
 {
        unsigned int mask = 0;
        const unsigned minor = iminor(file->f_dentry->d_inode);
-       struct comedi_device_file_info *dev_file_info =
-           comedi_get_device_file_info(minor);
-       struct comedi_device *dev = dev_file_info->device;
        struct comedi_subdevice *read_subdev;
        struct comedi_subdevice *write_subdev;
+       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->attached) {
@@ -1522,17 +1620,23 @@ static unsigned int comedi_poll(struct file *file, poll_table * wait)
        return mask;
 }
 
-static ssize_t comedi_write(struct file *file, const char *buf, size_t nbytes,
-                               loff_t *offset)
+static ssize_t comedi_write(struct file *file, const char __user *buf,
+                           size_t nbytes, loff_t *offset)
 {
        struct comedi_subdevice *s;
        struct comedi_async *async;
        int n, m, count = 0, retval = 0;
        DECLARE_WAITQUEUE(wait, current);
        const unsigned minor = iminor(file->f_dentry->d_inode);
-       struct comedi_device_file_info *dev_file_info =
-           comedi_get_device_file_info(minor);
-       struct comedi_device *dev = dev_file_info->device;
+       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;
 
        if (!dev->attached) {
                DPRINTK("no driver configured on comedi%i\n", dev->minor);
@@ -1563,6 +1667,19 @@ static ssize_t comedi_write(struct file *file, const char *buf, size_t nbytes,
        while (nbytes > 0 && !retval) {
                set_current_state(TASK_INTERRUPTIBLE);
 
+               if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
+                       if (count == 0) {
+                               if (comedi_get_subdevice_runflags(s) &
+                                       SRF_ERROR) {
+                                       retval = -EPIPE;
+                               } else {
+                                       retval = 0;
+                               }
+                               do_become_nonbusy(dev, s);
+                       }
+                       break;
+               }
+
                n = nbytes;
 
                m = n;
@@ -1575,25 +1692,15 @@ static ssize_t comedi_write(struct file *file, const char *buf, size_t nbytes,
                        n = m;
 
                if (n == 0) {
-                       if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
-                               if (comedi_get_subdevice_runflags(s) &
-                                   SRF_ERROR) {
-                                       retval = -EPIPE;
-                               } else {
-                                       retval = 0;
-                               }
-                               do_become_nonbusy(dev, s);
-                               break;
-                       }
                        if (file->f_flags & O_NONBLOCK) {
                                retval = -EAGAIN;
                                break;
                        }
+                       schedule();
                        if (signal_pending(current)) {
                                retval = -ERESTARTSYS;
                                break;
                        }
-                       schedule();
                        if (!s->busy)
                                break;
                        if (s->busy != file) {
@@ -1624,7 +1731,7 @@ done:
        return count ? count : retval;
 }
 
-static ssize_t comedi_read(struct file *file, char *buf, size_t nbytes,
+static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
                                loff_t *offset)
 {
        struct comedi_subdevice *s;
@@ -1632,9 +1739,15 @@ static ssize_t comedi_read(struct file *file, char *buf, size_t nbytes,
        int n, m, count = 0, retval = 0;
        DECLARE_WAITQUEUE(wait, current);
        const unsigned minor = iminor(file->f_dentry->d_inode);
-       struct comedi_device_file_info *dev_file_info =
-           comedi_get_device_file_info(minor);
-       struct comedi_device *dev = dev_file_info->device;
+       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;
 
        if (!dev->attached) {
                DPRINTK("no driver configured on comedi%i\n", dev->minor);
@@ -1690,11 +1803,11 @@ static ssize_t comedi_read(struct file *file, char *buf, size_t nbytes,
                                retval = -EAGAIN;
                                break;
                        }
+                       schedule();
                        if (signal_pending(current)) {
                                retval = -ERESTARTSYS;
                                break;
                        }
-                       schedule();
                        if (!s->busy) {
                                retval = 0;
                                break;
@@ -1814,8 +1927,15 @@ ok:
                }
        }
 
-       if (dev->attached && dev->use_count == 0 && dev->open)
-               dev->open(dev);
+       if (dev->attached && dev->use_count == 0 && dev->open) {
+               int rc = dev->open(dev);
+               if (rc < 0) {
+                       module_put(dev->driver->module);
+                       module_put(THIS_MODULE);
+                       mutex_unlock(&dev->mutex);
+                       return rc;
+               }
+       }
 
        dev->use_count++;
 
@@ -1827,11 +1947,17 @@ ok:
 static int comedi_close(struct inode *inode, struct file *file)
 {
        const unsigned minor = iminor(inode);
-       struct comedi_device_file_info *dev_file_info =
-           comedi_get_device_file_info(minor);
-       struct comedi_device *dev = dev_file_info->device;
        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);
 
@@ -1865,10 +1991,15 @@ static int comedi_close(struct inode *inode, struct file *file)
 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 =
-           comedi_get_device_file_info(minor);
+       struct comedi_device_file_info *dev_file_info;
+       struct comedi_device *dev;
+       dev_file_info = comedi_get_device_file_info(minor);
 
-       struct comedi_device *dev = dev_file_info->device;
+       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);
 }
@@ -1884,6 +2015,7 @@ const struct file_operations comedi_fops = {
        .mmap = comedi_mmap,
        .poll = comedi_poll,
        .fasync = comedi_fasync,
+       .llseek = noop_llseek,
 };
 
 struct class *comedi_class;
@@ -2005,7 +2137,7 @@ void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
                             COMEDI_CB_OVERFLOW)) {
                runflags_mask |= SRF_RUNNING;
        }
-       /* remember if an error event has occured, so an error
+       /* 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;
@@ -2063,7 +2195,7 @@ static int is_device_busy(struct comedi_device *dev)
        return 0;
 }
 
-void comedi_device_init(struct comedi_device *dev)
+static void comedi_device_init(struct comedi_device *dev)
 {
        memset(dev, 0, sizeof(struct comedi_device));
        spin_lock_init(&dev->spinlock);
@@ -2071,7 +2203,7 @@ void comedi_device_init(struct comedi_device *dev)
        dev->minor = -1;
 }
 
-void comedi_device_cleanup(struct comedi_device *dev)
+static void comedi_device_cleanup(struct comedi_device *dev)
 {
        if (dev == NULL)
                return;
@@ -2111,21 +2243,21 @@ int comedi_alloc_board_minor(struct device *hardware_device)
                kfree(info->device);
                kfree(info);
                printk(KERN_ERR
-
-                      "comedi: error: ran out of minor numbers for board device files.\n");
+                      "comedi: error: "
+                      "ran out of minor numbers for board device files.\n");
                return -EBUSY;
        }
        info->device->minor = i;
-       csdev = COMEDI_DEVICE_CREATE(comedi_class, NULL,
-                                    MKDEV(COMEDI_MAJOR, i), NULL,
-                                    hardware_device, "comedi%i", 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",
+                      "comedi: "
+                      "failed to create sysfs attribute file \"%s\".\n",
                       dev_attr_max_read_buffer_kb.attr.name);
                comedi_free_board_minor(i);
                return retval;
@@ -2133,7 +2265,8 @@ int comedi_alloc_board_minor(struct device *hardware_device)
        retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
        if (retval) {
                printk(KERN_ERR
-                      "comedi: failed to create sysfs attribute file \"%s\".\n",
+                      "comedi: "
+                      "failed to create sysfs attribute file \"%s\".\n",
                       dev_attr_read_buffer_kb.attr.name);
                comedi_free_board_minor(i);
                return retval;
@@ -2141,7 +2274,8 @@ int comedi_alloc_board_minor(struct device *hardware_device)
        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",
+                      "comedi: "
+                      "failed to create sysfs attribute file \"%s\".\n",
                       dev_attr_max_write_buffer_kb.attr.name);
                comedi_free_board_minor(i);
                return retval;
@@ -2149,14 +2283,14 @@ int comedi_alloc_board_minor(struct device *hardware_device)
        retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
        if (retval) {
                printk(KERN_ERR
-                      "comedi: failed to create sysfs attribute file \"%s\".\n",
+                      "comedi: "
+                      "failed to create sysfs attribute file \"%s\".\n",
                       dev_attr_write_buffer_kb.attr.name);
                comedi_free_board_minor(i);
                return retval;
        }
        return i;
 }
-EXPORT_SYMBOL_GPL(comedi_alloc_board_minor);
 
 void comedi_free_board_minor(unsigned minor)
 {
@@ -2182,7 +2316,6 @@ void comedi_free_board_minor(unsigned minor)
                kfree(info);
        }
 }
-EXPORT_SYMBOL_GPL(comedi_free_board_minor);
 
 int comedi_alloc_subdevice_minor(struct comedi_device *dev,
                                 struct comedi_subdevice *s)
@@ -2210,21 +2343,22 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev,
        if (i == COMEDI_NUM_MINORS) {
                kfree(info);
                printk(KERN_ERR
-                      "comedi: error: ran out of minor numbers for board device files.\n");
+                      "comedi: error: "
+                      "ran out of minor numbers for board device files.\n");
                return -EBUSY;
        }
        s->minor = i;
-       csdev = COMEDI_DEVICE_CREATE(comedi_class, dev->class_dev,
-                                    MKDEV(COMEDI_MAJOR, i), NULL, NULL,
-                                    "comedi%i_subd%i", dev->minor,
-                                    (int)(s - dev->subdevices));
+       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",
+                      "comedi: "
+                      "failed to create sysfs attribute file \"%s\".\n",
                       dev_attr_max_read_buffer_kb.attr.name);
                comedi_free_subdevice_minor(s);
                return retval;
@@ -2232,7 +2366,8 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev,
        retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
        if (retval) {
                printk(KERN_ERR
-                      "comedi: failed to create sysfs attribute file \"%s\".\n",
+                      "comedi: "
+                      "failed to create sysfs attribute file \"%s\".\n",
                       dev_attr_read_buffer_kb.attr.name);
                comedi_free_subdevice_minor(s);
                return retval;
@@ -2240,7 +2375,8 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev,
        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",
+                      "comedi: "
+                      "failed to create sysfs attribute file \"%s\".\n",
                       dev_attr_max_write_buffer_kb.attr.name);
                comedi_free_subdevice_minor(s);
                return retval;
@@ -2248,7 +2384,8 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev,
        retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
        if (retval) {
                printk(KERN_ERR
-                      "comedi: failed to create sysfs attribute file \"%s\".\n",
+                      "comedi: "
+                      "failed to create sysfs attribute file \"%s\".\n",
                       dev_attr_write_buffer_kb.attr.name);
                comedi_free_subdevice_minor(s);
                return retval;