summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/f_mtp.c
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@android.com>2011-02-10 11:54:53 -0500
committerColin Cross <ccross@android.com>2011-06-14 09:09:53 -0700
commit292b96396c25c465f7f17d5c0d0e182a6b7a4e26 (patch)
treeac74aebc2e9e3987bf79f5cb1efce9db81658f64 /drivers/usb/gadget/f_mtp.c
parentaecca43f09d029eb98bec88b6da3b3c6897e42f5 (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/gadget/f_mtp.c')
-rw-r--r--drivers/usb/gadget/f_mtp.c21
1 files changed, 9 insertions, 12 deletions
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index d560fbcf4f5..8128b203e76 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);