staging: comedi: check s->async for poll(), read() and write()
[pandora-kernel.git] / drivers / staging / comedi / comedi_fops.c
index 21d8c1c..a023f52 100644 (file)
@@ -136,6 +136,11 @@ 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) {
+               if (minor >= COMEDI_NUM_BOARD_MINORS) {
+                       /* Device config not appropriate on non-board minors. */
+                       rc = -ENOTTY;
+                       goto done;
+               }
                rc = do_devconfig_ioctl(dev,
                                        (struct comedi_devconfig __user *)arg);
                goto done;
@@ -280,7 +285,7 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
        if (ret == 0) {
                if (!try_module_get(dev->driver->module)) {
                        comedi_device_detach(dev);
-                       return -ENOSYS;
+                       ret = -ENOSYS;
                }
        }
 
@@ -671,7 +676,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;
@@ -843,7 +848,7 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
                                ret = -EAGAIN;
                                break;
                        }
-                       ret = s->async->inttrig(dev, s, insn->data[0]);
+                       ret = s->async->inttrig(dev, s, data[0]);
                        if (ret >= 0)
                                ret = 1;
                        break;
@@ -1088,7 +1093,6 @@ static int do_cmd_ioctl(struct comedi_device *dev,
                goto cleanup;
        }
 
-       kfree(async->cmd.chanlist);
        async->cmd = user_cmd;
        async->cmd.data = NULL;
        /* load channel/gain list */
@@ -1432,7 +1436,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;
@@ -1446,15 +1464,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;
@@ -1462,6 +1478,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) {
@@ -1528,11 +1553,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,7 +1574,7 @@ static unsigned int comedi_poll(struct file *file, poll_table * wait)
 
        mask = 0;
        read_subdev = comedi_get_read_subdevice(dev_file_info);
-       if (read_subdev) {
+       if (read_subdev && read_subdev->async) {
                poll_wait(file, &read_subdev->async->wait_head, wait);
                if (!read_subdev->busy
                    || comedi_buf_read_n_available(read_subdev->async) > 0
@@ -1553,7 +1584,7 @@ static unsigned int comedi_poll(struct file *file, poll_table * wait)
                }
        }
        write_subdev = comedi_get_write_subdevice(dev_file_info);
-       if (write_subdev) {
+       if (write_subdev && write_subdev->async) {
                poll_wait(file, &write_subdev->async->wait_head, wait);
                comedi_buf_write_alloc(write_subdev->async,
                                       write_subdev->async->prealloc_bufsz);
@@ -1578,9 +1609,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);
@@ -1589,7 +1626,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
        }
 
        s = comedi_get_write_subdevice(dev_file_info);
-       if (s == NULL) {
+       if (s == NULL || s->async == NULL) {
                retval = -EIO;
                goto done;
        }
@@ -1640,11 +1677,11 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
                                retval = -EAGAIN;
                                break;
                        }
+                       schedule();
                        if (signal_pending(current)) {
                                retval = -ERESTARTSYS;
                                break;
                        }
-                       schedule();
                        if (!s->busy)
                                break;
                        if (s->busy != file) {
@@ -1683,9 +1720,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);
@@ -1694,7 +1737,7 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
        }
 
        s = comedi_get_read_subdevice(dev_file_info);
-       if (s == NULL) {
+       if (s == NULL || s->async == NULL) {
                retval = -EIO;
                goto done;
        }
@@ -1741,11 +1784,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;
@@ -1794,6 +1837,8 @@ void do_become_nonbusy(struct comedi_device *dev, struct comedi_subdevice *s)
        if (async) {
                comedi_reset_async_buf(async);
                async->inttrig = NULL;
+               kfree(async->cmd.chanlist);
+               async->cmd.chanlist = NULL;
        } else {
                printk(KERN_ERR
                       "BUG: (?) do_become_nonbusy called with async=0\n");
@@ -1885,11 +1930,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);
 
@@ -1923,10 +1974,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);
 }
@@ -2156,6 +2212,7 @@ int comedi_alloc_board_minor(struct device *hardware_device)
                kfree(info);
                return -ENOMEM;
        }
+       info->hardware_device = hardware_device;
        comedi_device_init(info->device);
        spin_lock_irqsave(&comedi_file_info_table_lock, flags);
        for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
@@ -2244,6 +2301,23 @@ void comedi_free_board_minor(unsigned minor)
        }
 }
 
+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;
+}
+
 int comedi_alloc_subdevice_minor(struct comedi_device *dev,
                                 struct comedi_subdevice *s)
 {