]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/usb/gadget/function/f_fs.c
Merge tag 'usb-for-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi...
[mirror_ubuntu-artful-kernel.git] / drivers / usb / gadget / function / f_fs.c
index 47dda3450abd9391de37cf19115abfa0b927a402..d21874b35cf6eba0021ef3f66d97a9b226907f6f 100644 (file)
@@ -127,7 +127,6 @@ struct ffs_ep {
 struct ffs_epfile {
        /* Protects ep->ep and ep->req. */
        struct mutex                    mutex;
-       wait_queue_head_t               wait;
 
        struct ffs_data                 *ffs;
        struct ffs_ep                   *ep;    /* P: ffs->eps_lock */
@@ -889,7 +888,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
                if (file->f_flags & O_NONBLOCK)
                        return -EAGAIN;
 
-               ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
+               ret = wait_event_interruptible(
+                               epfile->ffs->wait, (ep = epfile->ep));
                if (ret)
                        return -EINTR;
        }
@@ -1189,6 +1189,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
                             unsigned long value)
 {
        struct ffs_epfile *epfile = file->private_data;
+       struct ffs_ep *ep;
        int ret;
 
        ENTER();
@@ -1196,50 +1197,65 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
        if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
                return -ENODEV;
 
+       /* Wait for endpoint to be enabled */
+       ep = epfile->ep;
+       if (!ep) {
+               if (file->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+
+               ret = wait_event_interruptible(
+                               epfile->ffs->wait, (ep = epfile->ep));
+               if (ret)
+                       return -EINTR;
+       }
+
        spin_lock_irq(&epfile->ffs->eps_lock);
-       if (likely(epfile->ep)) {
-               switch (code) {
-               case FUNCTIONFS_FIFO_STATUS:
-                       ret = usb_ep_fifo_status(epfile->ep->ep);
-                       break;
-               case FUNCTIONFS_FIFO_FLUSH:
-                       usb_ep_fifo_flush(epfile->ep->ep);
-                       ret = 0;
-                       break;
-               case FUNCTIONFS_CLEAR_HALT:
-                       ret = usb_ep_clear_halt(epfile->ep->ep);
-                       break;
-               case FUNCTIONFS_ENDPOINT_REVMAP:
-                       ret = epfile->ep->num;
-                       break;
-               case FUNCTIONFS_ENDPOINT_DESC:
-               {
-                       int desc_idx;
-                       struct usb_endpoint_descriptor *desc;
 
-                       switch (epfile->ffs->gadget->speed) {
-                       case USB_SPEED_SUPER:
-                               desc_idx = 2;
-                               break;
-                       case USB_SPEED_HIGH:
-                               desc_idx = 1;
-                               break;
-                       default:
-                               desc_idx = 0;
-                       }
-                       desc = epfile->ep->descs[desc_idx];
+       /* In the meantime, endpoint got disabled or changed. */
+       if (epfile->ep != ep) {
+               spin_unlock_irq(&epfile->ffs->eps_lock);
+               return -ESHUTDOWN;
+       }
 
-                       spin_unlock_irq(&epfile->ffs->eps_lock);
-                       ret = copy_to_user((void *)value, desc, desc->bLength);
-                       if (ret)
-                               ret = -EFAULT;
-                       return ret;
-               }
+       switch (code) {
+       case FUNCTIONFS_FIFO_STATUS:
+               ret = usb_ep_fifo_status(epfile->ep->ep);
+               break;
+       case FUNCTIONFS_FIFO_FLUSH:
+               usb_ep_fifo_flush(epfile->ep->ep);
+               ret = 0;
+               break;
+       case FUNCTIONFS_CLEAR_HALT:
+               ret = usb_ep_clear_halt(epfile->ep->ep);
+               break;
+       case FUNCTIONFS_ENDPOINT_REVMAP:
+               ret = epfile->ep->num;
+               break;
+       case FUNCTIONFS_ENDPOINT_DESC:
+       {
+               int desc_idx;
+               struct usb_endpoint_descriptor *desc;
+
+               switch (epfile->ffs->gadget->speed) {
+               case USB_SPEED_SUPER:
+                       desc_idx = 2;
+                       break;
+               case USB_SPEED_HIGH:
+                       desc_idx = 1;
+                       break;
                default:
-                       ret = -ENOTTY;
+                       desc_idx = 0;
                }
-       } else {
-               ret = -ENODEV;
+               desc = epfile->ep->descs[desc_idx];
+
+               spin_unlock_irq(&epfile->ffs->eps_lock);
+               ret = copy_to_user((void *)value, desc, desc->bLength);
+               if (ret)
+                       ret = -EFAULT;
+               return ret;
+       }
+       default:
+               ret = -ENOTTY;
        }
        spin_unlock_irq(&epfile->ffs->eps_lock);
 
@@ -1593,7 +1609,8 @@ static void ffs_data_put(struct ffs_data *ffs)
                pr_info("%s(): freeing\n", __func__);
                ffs_data_clear(ffs);
                BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
-                      waitqueue_active(&ffs->ep0req_completion.wait));
+                      waitqueue_active(&ffs->ep0req_completion.wait) ||
+                      waitqueue_active(&ffs->wait));
                kfree(ffs->dev_name);
                kfree(ffs);
        }
@@ -1640,6 +1657,7 @@ static struct ffs_data *ffs_data_new(void)
        mutex_init(&ffs->mutex);
        spin_lock_init(&ffs->eps_lock);
        init_waitqueue_head(&ffs->ev.waitq);
+       init_waitqueue_head(&ffs->wait);
        init_completion(&ffs->ep0req_completion);
 
        /* XXX REVISIT need to update it in some places, or do we? */
@@ -1761,7 +1779,6 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
        for (i = 1; i <= count; ++i, ++epfile) {
                epfile->ffs = ffs;
                mutex_init(&epfile->mutex);
-               init_waitqueue_head(&epfile->wait);
                if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
                        sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
                else
@@ -1786,8 +1803,7 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
        ENTER();
 
        for (; count; --count, ++epfile) {
-               BUG_ON(mutex_is_locked(&epfile->mutex) ||
-                      waitqueue_active(&epfile->wait));
+               BUG_ON(mutex_is_locked(&epfile->mutex));
                if (epfile->dentry) {
                        d_delete(epfile->dentry);
                        dput(epfile->dentry);
@@ -1874,11 +1890,11 @@ static int ffs_func_eps_enable(struct ffs_function *func)
                        break;
                }
 
-               wake_up(&epfile->wait);
-
                ++ep;
                ++epfile;
        }
+
+       wake_up_interruptible(&ffs->wait);
        spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
 
        return ret;