aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@android.com>2011-02-10 11:54:53 -0500
committerMike Lockwood <lockwood@android.com>2011-02-10 12:21:02 -0500
commitf35c505ce33f5cb97dca9532c510740a666161e4 (patch)
tree624803ae38dbe8817da66d79ced18ab6e01c9b6c
parent99648ea95eaa167e3cb7bc37403fdc7d5da0e56c (diff)
downloadqemu-f35c505ce33f5cb97dca9532c510740a666161e4.tar.gz
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>
-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);