summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2011-05-16 10:21:51 +0200
committerGerd Hoffmann <kraxel@redhat.com>2011-05-26 11:55:02 +0200
commit7d6c6de7c3a79581d663f9503e4ff2de4df56c3e (patch)
treee56818dd1dad77a90dd51a46f23960e298abd46d
parent7e94052c9109c7cdc471ce9f7ba07d3b5adf7a7a (diff)
downloadqemu-7d6c6de7c3a79581d663f9503e4ff2de4df56c3e.tar.gz
qemu-7d6c6de7c3a79581d663f9503e4ff2de4df56c3e.tar.bz2
qemu-7d6c6de7c3a79581d663f9503e4ff2de4df56c3e.zip
usb-linux: split large xfers
Add support for splitting large transfers into multiple smaller ones. This is needed for the upcoming EHCI emulation which allows guests to submit requests up to 20k in size. The linux kernel allows 16k max size though. Based on a patch from David Ahern, see http://www.mail-archive.com/qemu-devel@nongnu.org/msg30337.html Cc: David Ahern <daahern@cisco.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-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;
+ }
}
}