On Wed, Apr 19 2017, Jerry Zhang wrote:
> 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
Acked-by: Michal Nazarewicz
> ---
> 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(>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(>ffs->eps_lock);
> + return -ESHUTDOWN;
> + }
>
> - spin_unlock_irq(>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(>ffs->eps_lock);
> + ret = copy_to_user((void *)value, desc, desc->bLength);
> + if (ret)
> + ret = -EFAULT;
> + return ret;