UBUNTU: SAUCE: staging: comedi: Add module parameters for default buffer size
[linux-flexiantxendom0.git] / drivers / staging / comedi / comedi_fops.c
index aced00e..57742b1 100644 (file)
@@ -55,17 +55,40 @@ 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);
+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, 0444);
+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
@@ -83,7 +106,7 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
 static int do_chaninfo_ioctl(struct comedi_device *dev,
                             struct comedi_chaninfo __user *arg);
 static int do_bufinfo_ioctl(struct comedi_device *dev,
-                           struct comedi_bufinfo __user *arg);
+                           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,
@@ -101,7 +124,7 @@ static int do_insn_ioctl(struct comedi_device *dev,
 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);
 
@@ -169,7 +192,8 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
                break;
        case COMEDI_BUFINFO:
                rc = do_bufinfo_ioctl(dev,
-                                     (struct comedi_bufinfo __user *)arg);
+                                     (struct comedi_bufinfo __user *)arg,
+                                     file);
                break;
        case COMEDI_LOCK:
                rc = do_lock_ioctl(dev, arg, file);
@@ -382,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;
@@ -536,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,
@@ -563,7 +588,7 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
 
   */
 static int do_bufinfo_ioctl(struct comedi_device *dev,
-                           struct comedi_bufinfo __user *arg)
+                           struct comedi_bufinfo __user *arg, void *file)
 {
        struct comedi_bufinfo bi;
        struct comedi_subdevice *s;
@@ -576,6 +601,10 @@ static int do_bufinfo_ioctl(struct comedi_device *dev,
                return -EINVAL;
 
        s = dev->subdevices + bi.subdevice;
+
+       if (s->lock && s->lock != file)
+               return -EACCES;
+
        async = s->async;
 
        if (!async) {
@@ -584,8 +613,17 @@ static int do_bufinfo_ioctl(struct comedi_device *dev,
                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);
@@ -604,6 +642,7 @@ static int do_bufinfo_ioctl(struct comedi_device *dev,
                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;
@@ -655,7 +694,7 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
        }
 
        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;
@@ -894,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);
@@ -1256,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
@@ -1397,7 +1455,21 @@ static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
        return ret;
 }
 
-static 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;
@@ -1411,15 +1483,13 @@ static 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;
@@ -1427,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) {
@@ -1493,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) {
@@ -1543,9 +1628,15 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
        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);
@@ -1576,6 +1667,19 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
        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;
@@ -1588,25 +1692,15 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
                        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) {
@@ -1645,9 +1739,15 @@ static ssize_t comedi_read(struct file *file, char __user *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);
@@ -1703,11 +1803,11 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
                                retval = -EAGAIN;
                                break;
                        }
+                       schedule();
                        if (signal_pending(current)) {
                                retval = -ERESTARTSYS;
                                break;
                        }
-                       schedule();
                        if (!s->busy) {
                                retval = 0;
                                break;
@@ -1827,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++;
 
@@ -1840,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);
 
@@ -1878,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);
 }
@@ -1897,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;
@@ -2018,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;
@@ -2129,9 +2248,8 @@ int comedi_alloc_board_minor(struct device *hardware_device)
                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);
@@ -2230,10 +2348,9 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev,
                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);