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.

Signed-off-by: Jerry Zhang <zhangje...@google.com>
---
Changelog since v2:
- Updated doc to account for -ESHUTDOWN

Changelog since v1:
- Guarded against epfile->ep changing after taking lock

 drivers/usb/gadget/function/f_fs.c  | 93 +++++++++++++++++++++----------------
 include/uapi/linux/usb/functionfs.h |  7 +--
 2 files changed, 58 insertions(+), 42 deletions(-)

diff --git a/drivers/usb/gadget/function/f_fs.c 
b/drivers/usb/gadget/function/f_fs.c
index a0085571824d..04425e6b457a 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1190,6 +1190,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();
@@ -1197,50 +1198,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);
 
diff --git a/include/uapi/linux/usb/functionfs.h 
b/include/uapi/linux/usb/functionfs.h
index b2a31a55a612..297bca230dd0 100644
--- a/include/uapi/linux/usb/functionfs.h
+++ b/include/uapi/linux/usb/functionfs.h
@@ -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)
-- 
2.12.2.816.g2cccc81164-goog

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to