summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@android.com>2009-12-19 18:22:09 -0500
committerColin Cross <ccross@android.com>2011-06-14 09:08:54 -0700
commita9e8c44fc578157d2124d66e1c72266df6391f47 (patch)
tree1cee0bb81d9ce7eba00ecae655c816ec30a3b2f6 /drivers/usb
parent83814ea997cb03a65c7baf1c41699963724af932 (diff)
USB: gadget: adb: Queue read requests with length specified by client.
Previously we queued 4K requests rather than the count passed into read(). Signed-off-by: Mike Lockwood <lockwood@android.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/f_adb.c144
1 files changed, 50 insertions, 94 deletions
diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c
index 04dd4513940..7186cb601c2 100644
--- a/drivers/usb/gadget/f_adb.c
+++ b/drivers/usb/gadget/f_adb.c
@@ -34,8 +34,7 @@
#define BULK_BUFFER_SIZE 4096
-/* number of rx and tx requests to allocate */
-#define RX_REQ_MAX 4
+/* number of tx requests to allocate */
#define TX_REQ_MAX 4
static const char shortname[] = "android_adb";
@@ -56,16 +55,11 @@ struct adb_dev {
atomic_t open_excl;
struct list_head tx_idle;
- struct list_head rx_idle;
- struct list_head rx_done;
wait_queue_head_t read_wq;
wait_queue_head_t write_wq;
-
- /* the request we're currently reading from */
- struct usb_request *read_req;
- unsigned char *read_buf;
- unsigned read_count;
+ struct usb_request *rx_req;
+ int rx_done;
};
static struct usb_interface_descriptor adb_interface_desc = {
@@ -217,12 +211,9 @@ static void adb_complete_out(struct usb_ep *ep, struct usb_request *req)
{
struct adb_dev *dev = _adb_dev;
- if (req->status != 0) {
+ dev->rx_done = 1;
+ if (req->status != 0)
dev->error = 1;
- req_put(dev, &dev->rx_idle, req);
- } else {
- req_put(dev, &dev->rx_done, req);
- }
wake_up(&dev->read_wq);
}
@@ -255,13 +246,11 @@ static int __init create_bulk_endpoints(struct adb_dev *dev,
dev->ep_out = ep;
/* now allocate requests for our endpoints */
- for (i = 0; i < RX_REQ_MAX; i++) {
- req = adb_request_new(dev->ep_out, BULK_BUFFER_SIZE);
- if (!req)
- goto fail;
- req->complete = adb_complete_out;
- req_put(dev, &dev->rx_idle, req);
- }
+ req = adb_request_new(dev->ep_out, BULK_BUFFER_SIZE);
+ if (!req)
+ goto fail;
+ req->complete = adb_complete_out;
+ dev->rx_req = req;
for (i = 0; i < TX_REQ_MAX; i++) {
req = adb_request_new(dev->ep_in, BULK_BUFFER_SIZE);
@@ -289,6 +278,9 @@ static ssize_t adb_read(struct file *fp, char __user *buf,
DBG(cdev, "adb_read(%d)\n", count);
+ if (count > BULK_BUFFER_SIZE)
+ return -EINVAL;
+
if (_lock(&dev->read_excl))
return -EBUSY;
@@ -302,79 +294,46 @@ static ssize_t adb_read(struct file *fp, char __user *buf,
return ret;
}
}
+ if (dev->error) {
+ r = -EIO;
+ goto done;
+ }
- while (count > 0) {
- if (dev->error) {
- DBG(cdev, "adb_read dev->error\n");
- r = -EIO;
- break;
- }
-
- /* if we have idle read requests, get them queued */
- while ((req = req_get(dev, &dev->rx_idle))) {
requeue_req:
- req->length = BULK_BUFFER_SIZE;
- ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC);
-
- if (ret < 0) {
- r = -EIO;
- dev->error = 1;
- req_put(dev, &dev->rx_idle, req);
- goto fail;
- } else {
- DBG(cdev, "rx %p queue\n", req);
- }
- }
-
- /* if we have data pending, give it to userspace */
- if (dev->read_count > 0) {
- if (dev->read_count < count)
- xfer = dev->read_count;
- else
- xfer = count;
-
- if (copy_to_user(buf, dev->read_buf, xfer)) {
- r = -EFAULT;
- break;
- }
- dev->read_buf += xfer;
- dev->read_count -= xfer;
- buf += xfer;
- count -= xfer;
-
- /* if we've emptied the buffer, release the request */
- if (dev->read_count == 0) {
- req_put(dev, &dev->rx_idle, dev->read_req);
- dev->read_req = 0;
- }
- continue;
- }
-
- /* wait for a request to complete */
- req = 0;
- ret = wait_event_interruptible(dev->read_wq,
- ((req = req_get(dev, &dev->rx_done)) || dev->error));
- if (req != 0) {
- /* if we got a 0-len one we need to put it back into
- ** service. if we made it the current read req we'd
- ** be stuck forever
- */
- if (req->actual == 0)
- goto requeue_req;
-
- dev->read_req = req;
- dev->read_count = req->actual;
- dev->read_buf = req->buf;
- DBG(cdev, "rx %p %d\n", req, req->actual);
- }
-
- if (ret < 0) {
- r = ret;
- break;
- }
+ /* queue a request */
+ req = dev->rx_req;
+ req->length = count;
+ dev->rx_done = 0;
+ ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC);
+ if (ret < 0) {
+ DBG(cdev, "adb_read: failed to queue req %p (%d)\n", req, ret);
+ r = -EIO;
+ dev->error = 1;
+ goto done;
+ } else {
+ DBG(cdev, "rx %p queue\n", req);
}
-fail:
+ /* wait for a request to complete */
+ ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+ if (ret < 0) {
+ dev->error = 1;
+ r = ret;
+ goto done;
+ }
+ if (!dev->error) {
+ /* If we got a 0-len packet, throw it back and try again. */
+ if (req->actual == 0)
+ goto requeue_req;
+
+ DBG(cdev, "rx %p %d\n", req, req->actual);
+ xfer = (req->actual < count) ? req->actual : count;
+ if (copy_to_user(buf, req->buf, xfer))
+ r = -EFAULT;
+ } else
+ r = -EIO;
+
+done:
_unlock(&dev->read_excl);
DBG(cdev, "adb_read returning %d\n", r);
return r;
@@ -560,8 +519,7 @@ adb_function_unbind(struct usb_configuration *c, struct usb_function *f)
spin_lock_irq(&dev->lock);
- while ((req = req_get(dev, &dev->rx_idle)))
- adb_request_free(req, dev->ep_out);
+ adb_request_free(dev->rx_req, dev->ep_out);
while ((req = req_get(dev, &dev->tx_idle)))
adb_request_free(req, dev->ep_in);
@@ -641,8 +599,6 @@ static int adb_bind_config(struct usb_configuration *c)
atomic_set(&dev->read_excl, 0);
atomic_set(&dev->write_excl, 0);
- INIT_LIST_HEAD(&dev->rx_idle);
- INIT_LIST_HEAD(&dev->rx_done);
INIT_LIST_HEAD(&dev->tx_idle);
dev->cdev = c->cdev;