usb: gadget: function: f_fs: Let ffs_epfile_ioctl wait for enable.
authorJerry Zhang <zhangjerry@google.com>
Thu, 20 Apr 2017 01:23:38 +0000 (18:23 -0700)
committerFelipe Balbi <felipe.balbi@linux.intel.com>
Fri, 2 Jun 2017 08:22:31 +0000 (11:22 +0300)
This allows users to make an ioctl call as the first action on a
connection. Ex, some functions might want to get endpoint size
before making any i/os.

Previously, calling ioctls before read/write would depending on the
timing of endpoints being enabled.

ESHUTDOWN is now a possible return value and ENODEV is not, so change
docs accordingly.

Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Jerry Zhang <zhangjerry@google.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
drivers/usb/gadget/function/f_fs.c
include/uapi/linux/usb/functionfs.h

index 71dd27c0d7f27daf9d2db08b6a0b72901844ba3f..a24f9bf9c1c0c35f4d24469ac57b72bf68641f09 100644 (file)
@@ -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,64 @@ 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->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);
 
index 062606f02309f814ffd7c9a4e5ef5a0f4f31abe5..f913d08ab7bb0fa732d89b682f3f602f5f648dbc 100644 (file)
@@ -275,13 +275,14 @@ struct usb_functionfs_event {
 #define        FUNCTIONFS_INTERFACE_REVMAP     _IO('g', 128)
 
 /*
- * Returns real bEndpointAddress of an endpoint.  If function is not
- * active returns -ENODEV.
+ * Returns real bEndpointAddress of an endpoint. If endpoint shuts down
+ * during the call, returns -ESHUTDOWN.
  */
 #define        FUNCTIONFS_ENDPOINT_REVMAP      _IO('g', 129)
 
 /*
- * Returns endpoint descriptor. If function is not active returns -ENODEV.
+ * Returns endpoint descriptor. If endpoint shuts down during the call,
+ * returns -ESHUTDOWN.
  */
 #define        FUNCTIONFS_ENDPOINT_DESC        _IOR('g', 130, \
                                             struct usb_endpoint_descriptor)