summaryrefslogtreecommitdiff
path: root/usb-linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'usb-linux.c')
-rw-r--r--usb-linux.c64
1 files changed, 41 insertions, 23 deletions
diff --git a/usb-linux.c b/usb-linux.c
index 5e9c5e4135..4edcdc451e 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -89,6 +89,9 @@ static int usb_fs_type;
#define ISO_URB_COUNT 3
#define INVALID_EP_TYPE 255
+/* devio.c limits single requests to 16k */
+#define MAX_USBFS_BUFFER_SIZE 16384
+
typedef struct AsyncURB AsyncURB;
struct endp_data {
@@ -229,6 +232,7 @@ struct AsyncURB
/* For regular async urbs */
USBPacket *packet;
+ int more; /* large transfer, more urbs follow */
/* For buffered iso handling */
int iso_frame_idx; /* -1 means in flight */
@@ -291,7 +295,7 @@ static void async_complete(void *opaque)
if (p) {
switch (aurb->urb.status) {
case 0:
- p->len = aurb->urb.actual_length;
+ p->len += aurb->urb.actual_length;
break;
case -EPIPE:
@@ -306,7 +310,7 @@ static void async_complete(void *opaque)
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
usb_generic_async_ctrl_complete(&s->dev, p);
- } else {
+ } else if (!aurb->more) {
usb_packet_complete(&s->dev, p);
}
}
@@ -646,7 +650,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
struct usbdevfs_urb *urb;
AsyncURB *aurb;
- int ret;
+ int ret, rem;
+ uint8_t *pbuf;
uint8_t ep;
if (!is_valid(s, p->devep)) {
@@ -673,32 +678,45 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
}
- aurb = async_alloc(s);
- aurb->packet = p;
+ rem = p->len;
+ pbuf = p->data;
+ p->len = 0;
+ while (rem) {
+ aurb = async_alloc(s);
+ aurb->packet = p;
- urb = &aurb->urb;
+ urb = &aurb->urb;
+ urb->endpoint = ep;
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ urb->usercontext = s;
+ urb->buffer = pbuf;
- urb->endpoint = ep;
- urb->buffer = p->data;
- urb->buffer_length = p->len;
- urb->type = USBDEVFS_URB_TYPE_BULK;
- urb->usercontext = s;
+ if (rem > MAX_USBFS_BUFFER_SIZE) {
+ urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
+ aurb->more = 1;
+ } else {
+ urb->buffer_length = rem;
+ aurb->more = 0;
+ }
+ pbuf += urb->buffer_length;
+ rem -= urb->buffer_length;
- ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+ ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
- DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n",
- urb->endpoint, p->len, aurb);
+ DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
+ urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
- if (ret < 0) {
- DPRINTF("husb: submit failed. errno %d\n", errno);
- async_free(aurb);
+ if (ret < 0) {
+ DPRINTF("husb: submit failed. errno %d\n", errno);
+ async_free(aurb);
- switch(errno) {
- case ETIMEDOUT:
- return USB_RET_NAK;
- case EPIPE:
- default:
- return USB_RET_STALL;
+ switch(errno) {
+ case ETIMEDOUT:
+ return USB_RET_NAK;
+ case EPIPE:
+ default:
+ return USB_RET_STALL;
+ }
}
}