staging: comedi: return error on "write" if no command set up
[deliverable/linux.git] / drivers / staging / comedi / comedi_fops.c
index 7b4af519e17e1a92e9b6d594ea95fad753af5a36..2a2b5a06ed0e97bfc8d0cef935f3db7f5ca7eb16 100644 (file)
@@ -2303,11 +2303,13 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
 {
        struct comedi_subdevice *s;
        struct comedi_async *async;
-       int n, m, count = 0, retval = 0;
+       unsigned int n, m;
+       ssize_t count = 0;
+       int retval = 0;
        DECLARE_WAITQUEUE(wait, current);
        struct comedi_file *cfp = file->private_data;
        struct comedi_device *dev = cfp->dev;
-       bool on_wait_queue = false;
+       bool become_nonbusy = false;
        bool attach_locked;
        unsigned int old_detach_count;
 
@@ -2329,9 +2331,12 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
        }
 
        async = s->async;
-
-       if (!s->busy || !nbytes)
+       if (!nbytes)
+               goto out;
+       if (!s->busy) {
+               retval = -EINVAL;
                goto out;
+       }
        if (s->busy != file) {
                retval = -EACCES;
                goto out;
@@ -2342,61 +2347,24 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
        }
 
        add_wait_queue(&async->wait_head, &wait);
-       on_wait_queue = true;
-       while (nbytes > 0 && !retval) {
+       while (count == 0 && !retval) {
                unsigned runflags;
+               unsigned int wp, n1, n2;
 
                set_current_state(TASK_INTERRUPTIBLE);
 
                runflags = comedi_get_subdevice_runflags(s);
                if (!comedi_is_runflags_running(runflags)) {
-                       if (count == 0) {
-                               struct comedi_subdevice *new_s;
-
-                               if (comedi_is_runflags_in_error(runflags))
-                                       retval = -EPIPE;
-                               else
-                                       retval = 0;
-                               /*
-                                * To avoid deadlock, cannot acquire dev->mutex
-                                * while dev->attach_lock is held.  Need to
-                                * remove task from the async wait queue before
-                                * releasing dev->attach_lock, as it might not
-                                * be valid afterwards.
-                                */
-                               remove_wait_queue(&async->wait_head, &wait);
-                               on_wait_queue = false;
-                               up_read(&dev->attach_lock);
-                               attach_locked = false;
-                               mutex_lock(&dev->mutex);
-                               /*
-                                * Become non-busy unless things have changed
-                                * behind our back.  Checking dev->detach_count
-                                * is unchanged ought to be sufficient (unless
-                                * there have been 2**32 detaches in the
-                                * meantime!), but check the subdevice pointer
-                                * as well just in case.
-                                */
-                               new_s = comedi_file_write_subdevice(file);
-                               if (dev->attached &&
-                                   old_detach_count == dev->detach_count &&
-                                   s == new_s && new_s->async == async)
-                                       do_become_nonbusy(dev, s);
-                               mutex_unlock(&dev->mutex);
-                       }
+                       if (comedi_is_runflags_in_error(runflags))
+                               retval = -EPIPE;
+                       become_nonbusy = true;
                        break;
                }
 
-               n = nbytes;
-
-               m = n;
-               if (async->buf_write_ptr + m > async->prealloc_bufsz)
-                       m = async->prealloc_bufsz - async->buf_write_ptr;
+               /* Allocate all free buffer space. */
                comedi_buf_write_alloc(s, async->prealloc_bufsz);
-               if (m > comedi_buf_write_n_allocated(s))
-                       m = comedi_buf_write_n_allocated(s);
-               if (m < n)
-                       n = m;
+               m = comedi_buf_write_n_allocated(s);
+               n = min_t(size_t, m, nbytes);
 
                if (n == 0) {
                        if (file->f_flags & O_NONBLOCK) {
@@ -2408,8 +2376,10 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
                                retval = -ERESTARTSYS;
                                break;
                        }
-                       if (!s->busy)
+                       if (!s->busy) {
+                               retval = -EINVAL;
                                break;
+                       }
                        if (s->busy != file) {
                                retval = -EACCES;
                                break;
@@ -2421,8 +2391,14 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
                        continue;
                }
 
-               m = copy_from_user(async->prealloc_buf + async->buf_write_ptr,
-                                  buf, n);
+               wp = async->buf_write_ptr;
+               n1 = min(n, async->prealloc_bufsz - wp);
+               n2 = n - n1;
+               m = copy_from_user(async->prealloc_buf + wp, buf, n1);
+               if (m)
+                       m += n2;
+               else if (n2)
+                       m = copy_from_user(async->prealloc_buf, buf + n1, n2);
                if (m) {
                        n -= m;
                        retval = -EFAULT;
@@ -2433,12 +2409,38 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
                nbytes -= n;
 
                buf += n;
-               break;          /* makes device work like a pipe */
        }
-out:
-       if (on_wait_queue)
-               remove_wait_queue(&async->wait_head, &wait);
+       remove_wait_queue(&async->wait_head, &wait);
        set_current_state(TASK_RUNNING);
+       if (become_nonbusy && count == 0) {
+               struct comedi_subdevice *new_s;
+
+               /*
+                * To avoid deadlock, cannot acquire dev->mutex
+                * while dev->attach_lock is held.
+                */
+               up_read(&dev->attach_lock);
+               attach_locked = false;
+               mutex_lock(&dev->mutex);
+               /*
+                * Check device hasn't become detached behind our back.
+                * Checking dev->detach_count is unchanged ought to be
+                * sufficient (unless there have been 2**32 detaches in the
+                * meantime!), but check the subdevice pointer as well just in
+                * case.
+                *
+                * Also check the subdevice is still in a suitable state to
+                * become non-busy in case it changed behind our back.
+                */
+               new_s = comedi_file_write_subdevice(file);
+               if (dev->attached && old_detach_count == dev->detach_count &&
+                   s == new_s && new_s->async == async && s->busy == file &&
+                   (async->cmd.flags & CMDF_WRITE) &&
+                   !comedi_is_subdevice_running(s))
+                       do_become_nonbusy(dev, s);
+               mutex_unlock(&dev->mutex);
+       }
+out:
        if (attach_locked)
                up_read(&dev->attach_lock);
 
This page took 0.034687 seconds and 5 git commands to generate.