mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-20 04:21:14 +09:00
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>
This commit is contained in:
committed by
Colin Cross
parent
01acfb833c
commit
290c1aaa58
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (dev->error) {
|
||||
r = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
fail:
|
||||
requeue_req:
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
Reference in New Issue
Block a user