summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/function/f_fs.c
diff options
context:
space:
mode:
authorJerry Zhang <zhangjerry@google.com>2017-04-20 03:23:38 +0200
committerFelipe Balbi <felipe.balbi@linux.intel.com>2017-06-02 10:22:31 +0200
commit222155de45573e978cda988b7efc7d4e7b9a8ff9 (patch)
treeadcb3c45cb941485794fa7be6a40155a3b7e7d7f /drivers/usb/gadget/function/f_fs.c
parentusb: gadget: remove redundant self assignment (diff)
downloadlinux-222155de45573e978cda988b7efc7d4e7b9a8ff9.tar.xz
linux-222155de45573e978cda988b7efc7d4e7b9a8ff9.zip
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/f_fs.c')
-rw-r--r--drivers/usb/gadget/function/f_fs.c93
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);