diff options
author | Mike Lockwood <lockwood@android.com> | 2011-02-10 11:54:53 -0500 |
---|---|---|
committer | Mike Lockwood <lockwood@android.com> | 2011-02-10 12:21:02 -0500 |
commit | f35c505ce33f5cb97dca9532c510740a666161e4 (patch) | |
tree | 624803ae38dbe8817da66d79ced18ab6e01c9b6c /drivers/usb | |
parent | 99648ea95eaa167e3cb7bc37403fdc7d5da0e56c (diff) |
USB: gadget: f_mtp: Don't block in mtp_send_event
We used to wait for the previous interrupt packet to complete before sending
the next packet. But unfortunately the previous packet will not complete
until USB is disconnected if the host is not listening on the interrupt
endpoint (which is the case with libmtp on Linux and Mac).
To avoid hanging indefinitely in this case, we now simply return -EBUSY
if the previous interrupt packet has not completed yet.
Signed-off-by: Mike Lockwood <lockwood@android.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/f_mtp.c | 21 |
1 files changed, 9 insertions, 12 deletions
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index d560fbcf4f58..8128b203e76f 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -92,7 +92,6 @@ struct mtp_dev { wait_queue_head_t read_wq; wait_queue_head_t write_wq; - wait_queue_head_t intr_wq; struct usb_request *rx_req[RX_REQ_MAX]; struct usb_request *intr_req; int rx_done; @@ -373,12 +372,11 @@ static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req) { struct mtp_dev *dev = _mtp_dev; - DBG(dev->cdev, "mtp_complete_intr status: %d actual: %d\n", req->status, req->actual); + DBG(dev->cdev, "mtp_complete_intr status: %d actual: %d\n", + req->status, req->actual); dev->intr_busy = 0; if (req->status != 0) dev->state = STATE_ERROR; - - wake_up(&dev->intr_wq); } static int __init create_bulk_endpoints(struct mtp_dev *dev, @@ -798,13 +796,15 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) if (length < 0 || length > INTR_BUFFER_SIZE) return -EINVAL; - - /* wait for a request to complete */ - ret = wait_event_interruptible(dev->intr_wq, !dev->intr_busy || dev->state == STATE_OFFLINE); - if (ret < 0) - return ret; if (dev->state == STATE_OFFLINE) return -ENODEV; + /* unfortunately an interrupt request might hang indefinitely if the host + * is not listening on the interrupt endpoint, so instead of waiting, + * we just fail if the endpoint is busy. + */ + if (dev->intr_busy) + return -EBUSY; + req = dev->intr_req; if (copy_from_user(req->buf, (void __user *)event->data, length)) return -EFAULT; @@ -1016,7 +1016,6 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) mtp_request_free(dev->intr_req, dev->ep_intr); dev->state = STATE_OFFLINE; spin_unlock_irq(&dev->lock); - wake_up(&dev->intr_wq); misc_deregister(&mtp_device); kfree(_mtp_dev); @@ -1180,7 +1179,6 @@ static void mtp_function_disable(struct usb_function *f) /* readers may be blocked waiting for us to go online */ wake_up(&dev->read_wq); - wake_up(&dev->intr_wq); VDBG(cdev, "%s disabled\n", dev->function.name); } @@ -1208,7 +1206,6 @@ static int mtp_bind_config(struct usb_configuration *c) spin_lock_init(&dev->lock); init_waitqueue_head(&dev->read_wq); init_waitqueue_head(&dev->write_wq); - init_waitqueue_head(&dev->intr_wq); atomic_set(&dev->open_excl, 0); atomic_set(&dev->ioctl_excl, 0); INIT_LIST_HEAD(&dev->tx_idle); |