diff options
author | Jerry Zhang <zhangjerry@google.com> | 2017-04-20 04:23:38 +0300 |
---|---|---|
committer | Felipe Balbi <felipe.balbi@linux.intel.com> | 2017-06-02 11:22:31 +0300 |
commit | 222155de45573e978cda988b7efc7d4e7b9a8ff9 (patch) | |
tree | adcb3c45cb941485794fa7be6a40155a3b7e7d7f /drivers/usb/gadget/function | |
parent | 8a8b161df5ce06ef5a315899f83978e765be09e8 (diff) | |
download | linux-222155de45573e978cda988b7efc7d4e7b9a8ff9.tar.xz |
usb: gadget: function: f_fs: Let ffs_epfile_ioctl wait for enable.
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>
Diffstat (limited to 'drivers/usb/gadget/function')
-rw-r--r-- | drivers/usb/gadget/function/f_fs.c | 93 |
1 files changed, 54 insertions, 39 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 71dd27c0d7f2..a24f9bf9c1c0 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -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); |