From 19f3322379c25a235eb1ec6335676549109fa625 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 2 Feb 2011 17:46:00 +0100 Subject: usb: control buffer fixes Windows allows control transfers to pass up to 4k of data, so raise our control buffer size to 4k. For control out transfers the usb core code copies the control request data to a buffer before calling the device's handle_control callback. Add a check for overflowing the buffer before copying the data. Signed-off-by: Hans de Goede --- hw/usb.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'hw/usb.c') diff --git a/hw/usb.c b/hw/usb.c index 82a6217..d8c0a75 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -93,6 +93,12 @@ static int do_token_setup(USBDevice *s, USBPacket *p) s->setup_len = ret; s->setup_state = SETUP_STATE_DATA; } else { + if (s->setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", + s->setup_len, sizeof(s->data_buf)); + return USB_RET_STALL; + } if (s->setup_len == 0) s->setup_state = SETUP_STATE_ACK; else -- cgit v1.1 From 007fd62f4d3959f2a61abe61a34a54c9f99560b0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 2 Feb 2011 16:33:13 +0100 Subject: usb: Pass the packet to the device's handle_control callback This allows using the generic usb_generic_handle_packet function from device code which does ASYNC control requests (such as the linux host pass through code). Signed-off-by: Hans de Goede --- hw/usb.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'hw/usb.c') diff --git a/hw/usb.c b/hw/usb.c index d8c0a75..f503b7a 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -82,9 +82,9 @@ static int do_token_setup(USBDevice *s, USBPacket *p) request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - + if (s->setup_buf[0] & USB_DIR_IN) { - ret = s->info->handle_control(s, request, value, index, + ret = s->info->handle_control(s, p, request, value, index, s->setup_len, s->data_buf); if (ret < 0) return ret; @@ -123,9 +123,12 @@ static int do_token_in(USBDevice *s, USBPacket *p) switch(s->setup_state) { case SETUP_STATE_ACK: if (!(s->setup_buf[0] & USB_DIR_IN)) { - s->setup_state = SETUP_STATE_IDLE; - ret = s->info->handle_control(s, request, value, index, + ret = s->info->handle_control(s, p, request, value, index, s->setup_len, s->data_buf); + if (ret == USB_RET_ASYNC) { + return USB_RET_ASYNC; + } + s->setup_state = SETUP_STATE_IDLE; if (ret > 0) return 0; return ret; -- cgit v1.1 From 50b7963e72da6c31c2bebd435aeefd2966cd94ee Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 2 Feb 2011 17:36:29 +0100 Subject: usb-linux: use usb_generic_handle_packet() Make the linux usb host passthrough code use the usb_generic_handle_packet() function, rather then the curent DYI code. This removes 200 lines of almost identical code. Signed-off-by: Hans de Goede --- hw/usb.c | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) (limited to 'hw/usb.c') diff --git a/hw/usb.c b/hw/usb.c index f503b7a..60027c6 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -63,9 +63,10 @@ void usb_wakeup(USBDevice *dev) protocol) */ -#define SETUP_STATE_IDLE 0 -#define SETUP_STATE_DATA 1 -#define SETUP_STATE_ACK 2 +#define SETUP_STATE_IDLE 0 +#define SETUP_STATE_SETUP 1 +#define SETUP_STATE_DATA 2 +#define SETUP_STATE_ACK 3 static int do_token_setup(USBDevice *s, USBPacket *p) { @@ -86,6 +87,10 @@ static int do_token_setup(USBDevice *s, USBPacket *p) if (s->setup_buf[0] & USB_DIR_IN) { ret = s->info->handle_control(s, p, request, value, index, s->setup_len, s->data_buf); + if (ret == USB_RET_ASYNC) { + s->setup_state = SETUP_STATE_SETUP; + return USB_RET_ASYNC; + } if (ret < 0) return ret; @@ -241,6 +246,36 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p) } } +/* ctrl complete function for devices which use usb_generic_handle_packet and + may return USB_RET_ASYNC from their handle_control callback. Device code + which does this *must* call this function instead of the normal + usb_packet_complete to complete their async control packets. */ +void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) +{ + if (p->len < 0) { + s->setup_state = SETUP_STATE_IDLE; + } + + switch (s->setup_state) { + case SETUP_STATE_SETUP: + if (p->len < s->setup_len) { + s->setup_len = p->len; + } + s->setup_state = SETUP_STATE_DATA; + p->len = 8; + break; + + case SETUP_STATE_ACK: + s->setup_state = SETUP_STATE_IDLE; + p->len = 0; + break; + + default: + break; + } + usb_packet_complete(s, p); +} + /* XXX: fix overflow */ int set_usb_string(uint8_t *buf, const char *str) { -- cgit v1.1 From 53aa8c0e2af473050fa765533a8d69f3450788ab Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 12 May 2011 13:20:39 +0200 Subject: usb: add usb_handle_packet Add a usb_handle_packet function, put it into use everywhere. Right now it just calls dev->info->handle_packet(), that will change in future patches though. Signed-off-by: Gerd Hoffmann --- hw/usb.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'hw/usb.c') diff --git a/hw/usb.c b/hw/usb.c index 60027c6..966cb0f 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -297,9 +297,22 @@ int set_usb_string(uint8_t *buf, const char *str) void usb_send_msg(USBDevice *dev, int msg) { USBPacket p; + int ret; + memset(&p, 0, sizeof(p)); p.pid = msg; - dev->info->handle_packet(dev, &p); - + ret = usb_handle_packet(dev, &p); /* This _must_ be synchronous */ + assert(ret != USB_RET_ASYNC); +} + +/* Hand over a packet to a device for processing. Return value + USB_RET_ASYNC indicates the processing isn't finished yet, the + driver will call usb_packet_complete() when done processing it. */ +int usb_handle_packet(USBDevice *dev, USBPacket *p) +{ + int ret; + + ret = dev->info->handle_packet(dev, p); + return ret; } -- cgit v1.1 From 4ff658fb6c4f1cb7f771b16f808547e4f5767d02 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 12 May 2011 13:48:13 +0200 Subject: usb: keep track of packet owner. Keep track of the device which owns the usb packet for async processing. Signed-off-by: Gerd Hoffmann --- hw/usb.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'hw/usb.c') diff --git a/hw/usb.c b/hw/usb.c index 966cb0f..8a9a7fc 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -313,6 +313,38 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) { int ret; + assert(p->owner == NULL); ret = dev->info->handle_packet(dev, p); + if (ret == USB_RET_ASYNC) { + if (p->owner == NULL) { + p->owner = dev; + } else { + /* We'll end up here when usb_handle_packet is called + * recursively due to a hub being in the chain. Nothing + * to do. Leave p->owner pointing to the device, not the + * hub. */; + } + } return ret; } + +/* Notify the controller that an async packet is complete. This should only + be called for packets previously deferred by returning USB_RET_ASYNC from + handle_packet. */ +void usb_packet_complete(USBDevice *dev, USBPacket *p) +{ + /* Note: p->owner != dev is possible in case dev is a hub */ + assert(p->owner != NULL); + dev->port->ops->complete(dev, p); + p->owner = NULL; +} + +/* Cancel an active packet. The packed must have been deferred by + returning USB_RET_ASYNC from handle_packet, and not yet + completed. */ +void usb_cancel_packet(USBPacket * p) +{ + assert(p->owner != NULL); + p->cancel_cb(p, p->cancel_opaque); + p->owner = NULL; +} -- cgit v1.1 From eb5e680ae5a72b999946e5618c501648367734a8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 16 May 2011 10:34:53 +0200 Subject: usb: move cancel callback to USBDeviceInfo Remove the cancel callback from the USBPacket struct, move it over to USBDeviceInfo. Zap usb_defer_packet() which is obsolete now. Signed-off-by: Gerd Hoffmann --- hw/usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw/usb.c') diff --git a/hw/usb.c b/hw/usb.c index 8a9a7fc..4a39cbc 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -345,6 +345,6 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) void usb_cancel_packet(USBPacket * p) { assert(p->owner != NULL); - p->cancel_cb(p, p->cancel_opaque); + p->owner->info->cancel_packet(p->owner, p); p->owner = NULL; } -- cgit v1.1