usb: f_fs: check quirk to pad epout buf size when not aligned to maxpacketsize
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / usb / gadget / f_fs.c
index 774e8b8..3494043 100644 (file)
@@ -753,78 +753,71 @@ static ssize_t ffs_epfile_io(struct file *file,
                             char __user *buf, size_t len, int read)
 {
        struct ffs_epfile *epfile = file->private_data;
+       struct usb_gadget *gadget = epfile->ffs->gadget;
        struct ffs_ep *ep;
        char *data = NULL;
-       ssize_t ret;
+       ssize_t ret, data_len;
        int halt;
 
-       goto first_try;
-       do {
-               spin_unlock_irq(&epfile->ffs->eps_lock);
-               mutex_unlock(&epfile->mutex);
+       /* Are we still active? */
+       if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
+               ret = -ENODEV;
+               goto error;
+       }
 
-first_try:
-               /* Are we still active? */
-               if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
-                       ret = -ENODEV;
+       /* Wait for endpoint to be enabled */
+       ep = epfile->ep;
+       if (!ep) {
+               if (file->f_flags & O_NONBLOCK) {
+                       ret = -EAGAIN;
                        goto error;
                }
 
-               /* Wait for endpoint to be enabled */
-               ep = epfile->ep;
-               if (!ep) {
-                       if (file->f_flags & O_NONBLOCK) {
-                               ret = -EAGAIN;
-                               goto error;
-                       }
-
-                       if (wait_event_interruptible(epfile->wait,
-                                                    (ep = epfile->ep))) {
-                               ret = -EINTR;
-                               goto error;
-                       }
-               }
-
-               /* Do we halt? */
-               halt = !read == !epfile->in;
-               if (halt && epfile->isoc) {
-                       ret = -EINVAL;
+               ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
+               if (ret) {
+                       ret = -EINTR;
                        goto error;
                }
+       }
 
-               /* Allocate & copy */
-               if (!halt && !data) {
-                       data = kzalloc(len, GFP_KERNEL);
-                       if (unlikely(!data))
-                               return -ENOMEM;
+       /* Do we halt? */
+       halt = !read == !epfile->in;
+       if (halt && epfile->isoc) {
+               ret = -EINVAL;
+               goto error;
+       }
 
-                       if (!read &&
-                           unlikely(__copy_from_user(data, buf, len))) {
-                               ret = -EFAULT;
-                               goto error;
-                       }
-               }
+       /* Allocate & copy */
+       if (!halt) {
+               /*
+                * Controller may require buffer size to be aligned to
+                * maxpacketsize of an out endpoint.
+                */
+               data_len = read ? usb_ep_align_maybe(gadget, ep->ep, len) : len;
+
+               data = kmalloc(data_len, GFP_KERNEL);
+               if (unlikely(!data))
+                       return -ENOMEM;
 
-               /* We will be using request */
-               ret = ffs_mutex_lock(&epfile->mutex,
-                                    file->f_flags & O_NONBLOCK);
-               if (unlikely(ret))
+               if (!read && unlikely(copy_from_user(data, buf, len))) {
+                       ret = -EFAULT;
                        goto error;
+               }
+       }
 
-               /*
-                * We're called from user space, we can use _irq rather then
-                * _irqsave
-                */
-               spin_lock_irq(&epfile->ffs->eps_lock);
+       /* We will be using request */
+       ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK);
+       if (unlikely(ret))
+               goto error;
 
-               /*
-                * While we were acquiring mutex endpoint got disabled
-                * or changed?
-                */
-       } while (unlikely(epfile->ep != ep));
+       spin_lock_irq(&epfile->ffs->eps_lock);
 
-       /* Halt */
-       if (unlikely(halt)) {
+       if (epfile->ep != ep) {
+               /* In the meantime, endpoint got disabled or changed. */
+               ret = -ESHUTDOWN;
+               spin_unlock_irq(&epfile->ffs->eps_lock);
+       } else if (halt) {
+               /* Halt */
                if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
                        usb_ep_set_halt(ep->ep);
                spin_unlock_irq(&epfile->ffs->eps_lock);
@@ -837,7 +830,7 @@ first_try:
                req->context  = &done;
                req->complete = ffs_epfile_io_complete;
                req->buf      = data;
-               req->length   = len;
+               req->length   = data_len;
 
                ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
 
@@ -849,9 +842,17 @@ first_try:
                        ret = -EINTR;
                        usb_ep_dequeue(ep->ep, req);
                } else {
+                       /*
+                        * XXX We may end up silently droping data here.
+                        * Since data_len (i.e. req->length) may be bigger
+                        * than len (after being rounded up to maxpacketsize),
+                        * we may end up with more data then user space has
+                        * space for.
+                        */
                        ret = ep->status;
                        if (read && ret > 0 &&
-                           unlikely(copy_to_user(buf, data, ret)))
+                           unlikely(copy_to_user(buf, data,
+                                                 min_t(size_t, ret, len))))
                                ret = -EFAULT;
                }
        }