From ed3a328db9d9a027092edc2c17348068e3271cef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 24 Nov 2010 12:50:00 +0100 Subject: usb-linux: introduce a usb_linux_alt_setting function The next patch in this series introduces multiple ways to get the alt setting dependent upon usb_fs_type, it is cleaner to put this into its own function. Note that this patch also changes the assumed alt setting in case of an error getting the alt setting to be 0 (a sane default) rather then the interface numberwhich makes no sense. Signed-off-by: Hans de Goede --- usb-linux.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/usb-linux.c b/usb-linux.c index 1f33c2c230..353e1b1232 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -823,13 +823,35 @@ usbdevfs: return configuration; } +static uint8_t usb_linux_get_alt_setting(USBHostDevice *s, + uint8_t configuration, uint8_t interface) +{ + uint8_t alt_setting; + struct usb_ctrltransfer ct; + int ret; + + ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE; + ct.bRequest = USB_REQ_GET_INTERFACE; + ct.wValue = 0; + ct.wIndex = interface; + ct.wLength = 1; + ct.data = &alt_setting; + ct.timeout = 50; + ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct); + if (ret < 0) { + /* Assume alt 0 on error */ + return 0; + } + + return alt_setting; +} + /* returns 1 on problem encountered or 0 for success */ static int usb_linux_update_endp_table(USBHostDevice *s) { uint8_t *descriptors; uint8_t devep, type, configuration, alt_interface; - struct usb_ctrltransfer ct; - int interface, ret, length, i; + int interface, length, i; i = usb_linux_get_configuration(s); if (i < 0) @@ -858,19 +880,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s) } interface = descriptors[i + 2]; - - ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE; - ct.bRequest = USB_REQ_GET_INTERFACE; - ct.wValue = 0; - ct.wIndex = interface; - ct.wLength = 1; - ct.data = &alt_interface; - ct.timeout = 50; - - ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct); - if (ret < 0) { - alt_interface = interface; - } + alt_interface = usb_linux_get_alt_setting(s, configuration, interface); /* the current interface descriptor is the active interface * and has endpoints */ -- cgit v1.2.3 From c43831fb47e4ee51967870c7b5deb08789b0874c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 24 Nov 2010 12:57:59 +0100 Subject: usb-linux: Get the alt. setting from sysfs rather then asking the dev At least one device I have lies when receiving a USB_REQ_GET_INTERFACE, always returning 0 even if the alternate setting is different. This is likely caused because in practice this control message is never used as the operating system's usb stack knows which alternate setting it has told the device to get into, and thus this ctrl message does not get tested by device manufacturers. When usb_fs_type == USB_FS_SYS, the active alt. setting can be read directly from sysfs, which allows using this device through qemu's usb redirection. More in general it seems a good idea to not send needless control msg's to devices, esp. as the code in question is called every time a set_interface is done. Which happens multiple times during virtual machine startup, and when device drivers are activating the usb device. Signed-off-by: Hans de Goede --- usb-linux.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/usb-linux.c b/usb-linux.c index 353e1b1232..f4601e6e63 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -830,6 +830,24 @@ static uint8_t usb_linux_get_alt_setting(USBHostDevice *s, struct usb_ctrltransfer ct; int ret; + if (usb_fs_type == USB_FS_SYS) { + char device_name[64], line[1024]; + int alt_setting; + + sprintf(device_name, "%d-%d:%d.%d", s->bus_num, s->devpath, + (int)configuration, (int)interface); + + if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting", + device_name)) { + goto usbdevfs; + } + if (sscanf(line, "%d", &alt_setting) != 1) { + goto usbdevfs; + } + return alt_setting; + } + +usbdevfs: ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE; ct.bRequest = USB_REQ_GET_INTERFACE; ct.wValue = 0; -- cgit v1.2.3 From 060dc841d117e2a2868ef50d0d30e01c90051a6f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 26 Nov 2010 11:41:08 +0100 Subject: usb-linux: Add support for buffering iso usb packets Currently we are submitting iso packets to the host one at a time, as we receive them from the emulated host controller. This has 2 problems: 1) If we were fast enough to submit every packet in time for the next host host controller usb frame, we would be generating 1000 hardware interrupts per second on the host 2) We are not fast enough to submit every packet in time for the next host host controller usb frame, causing us to not submit iso urbs in some usb frames which causes devices with an endpoint with an interval of 1 ms (so every frame) to loose data. This causes for example ubs-1.1 webcams to not work properly (usb-2.0 is not supported at all atm). This patch fixes both problems by changing the iso packet pass through handling to buffer packets. This version only does so for iso input packets (webcams, audio in) I'm working on a second patch extending this to iso output packets (audio out). This patch makes use of the linux batching of iso packets in one urb. When an iso in packet gets received from the emulated host controller, it immediately submits 3 urbs with 32 iso in packets each. This causes the host to only get an hw interrupt every 32 packets dropping the interrupt rate to 32 interrupts per second and gives it a queue of urbs to work from once the first 32 iso in packets have been received to make sure no packets are dropped. Besides submitting a whole bunch or urbs as soon as the first urb is received, effectively creating a buffer inside the kernel, this patch also gets rid of the asynchroneous completion for iso in urbs. Instead they are only marked as complete in the fd write callback (which usbfs uses to signal complete urbs). These complete packets then get consumed by returning them synchroneously to the emulated host controller when it submits an iso in packet for the ep in question. When no complete packets are ready (which happens when the stream is starting) a 0 length packet gets returned to the emulated host controller. With this patch I've several usb-1.1 webcams working well with usb pass through, where as without this patch none of them work. Signed-off-by: Hans de Goede --- usb-linux.c | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 226 insertions(+), 17 deletions(-) diff --git a/usb-linux.c b/usb-linux.c index f4601e6e63..a68603df76 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -92,9 +92,17 @@ static char *usb_host_device_path; static int usb_fs_type; /* endpoint association data */ +#define ISO_FRAME_DESC_PER_URB 32 +#define ISO_URB_COUNT 3 + +typedef struct AsyncURB AsyncURB; + struct endp_data { uint8_t type; uint8_t halted; + AsyncURB *iso_urb; + int iso_urb_idx; + int max_packet_size; }; enum { @@ -175,19 +183,48 @@ static void set_halt(USBHostDevice *s, int ep) s->endp_table[ep - 1].halted = 1; } +static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb) +{ + s->endp_table[ep - 1].iso_urb = iso_urb; +} + +static AsyncURB *get_iso_urb(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].iso_urb; +} + +static void set_iso_urb_idx(USBHostDevice *s, int ep, int i) +{ + s->endp_table[ep - 1].iso_urb_idx = i; +} + +static int get_iso_urb_idx(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].iso_urb_idx; +} + +static int get_max_packet_size(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].max_packet_size; +} + /* * Async URB state. - * We always allocate one isoc descriptor even for bulk transfers + * We always allocate iso packet descriptors even for bulk transfers * to simplify allocation and casts. */ -typedef struct AsyncURB +struct AsyncURB { struct usbdevfs_urb urb; - struct usbdevfs_iso_packet_desc isocpd; + struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB]; + /* For regular async urbs */ USBPacket *packet; USBHostDevice *hdev; -} AsyncURB; + + /* For buffered iso handling */ + int iso_frame_idx; /* -1 means in flight */ +}; static AsyncURB *async_alloc(void) { @@ -244,11 +281,21 @@ static void async_complete(void *opaque) return; } - p = aurb->packet; - DPRINTF("husb: async completed. aurb %p status %d alen %d\n", aurb, aurb->urb.status, aurb->urb.actual_length); + /* If this is a buffered iso urb mark it as complete and don't do + anything else (it is handled further in usb_host_handle_iso_data) */ + if (aurb->iso_frame_idx == -1) { + if (aurb->urb.status == -EPIPE) { + set_halt(s, aurb->urb.endpoint & 0xf); + } + aurb->iso_frame_idx = 0; + continue; + } + + p = aurb->packet; + if (p) { switch (aurb->urb.status) { case 0: @@ -415,34 +462,181 @@ static void usb_host_handle_destroy(USBDevice *dev) static int usb_linux_update_endp_table(USBHostDevice *s); +/* iso data is special, we need to keep enough urbs in flight to make sure + that the controller never runs out of them, otherwise the device will + likely suffer a buffer underrun / overrun. */ +static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, uint8_t ep, int in) +{ + AsyncURB *aurb; + int i, j, len = get_max_packet_size(s, ep); + + aurb = qemu_mallocz(ISO_URB_COUNT * sizeof(*aurb)); + for (i = 0; i < ISO_URB_COUNT; i++) { + aurb[i].urb.endpoint = ep; + aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len; + aurb[i].urb.buffer = qemu_malloc(aurb[i].urb.buffer_length); + aurb[i].urb.type = USBDEVFS_URB_TYPE_ISO; + aurb[i].urb.flags = USBDEVFS_URB_ISO_ASAP; + aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB; + for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++) + aurb[i].urb.iso_frame_desc[j].length = len; + if (in) { + aurb[i].urb.endpoint |= 0x80; + /* Mark as fully consumed (idle) */ + aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB; + } + } + set_iso_urb(s, ep, aurb); + + return aurb; +} + +static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) +{ + AsyncURB *aurb; + int i, ret, killed = 0, free = 1; + + aurb = get_iso_urb(s, ep); + if (!aurb) { + return; + } + + for (i = 0; i < ISO_URB_COUNT; i++) { + /* in flight? */ + if (aurb[i].iso_frame_idx == -1) { + ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]); + if (ret < 0) { + printf("husb: discard isoc in urb failed errno %d\n", errno); + free = 0; + continue; + } + killed++; + } + } + + /* Make sure any urbs we've killed are reaped before we free them */ + if (killed) { + async_complete(s); + } + + for (i = 0; i < ISO_URB_COUNT; i++) { + qemu_free(aurb[i].urb.buffer); + } + + if (free) + qemu_free(aurb); + else + printf("husb: leaking iso urbs because of discard failure\n"); + set_iso_urb(s, ep, NULL); +} + +static int urb_status_to_usb_ret(int status) +{ + switch (status) { + case -EPIPE: + return USB_RET_STALL; + default: + return USB_RET_NAK; + } +} + +static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p) +{ + AsyncURB *aurb; + int i, j, ret, len = 0; + + aurb = get_iso_urb(s, p->devep); + if (!aurb) { + aurb = usb_host_alloc_iso(s, p->devep, 1); + } + + i = get_iso_urb_idx(s, p->devep); + j = aurb[i].iso_frame_idx; + if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { + /* Check urb status */ + if (aurb[i].urb.status) { + len = urb_status_to_usb_ret(aurb[i].urb.status); + /* Move to the next urb */ + aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1; + /* Check frame status */ + } else if (aurb[i].urb.iso_frame_desc[j].status) { + len = urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status); + /* Check the frame fits */ + } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) { + printf("husb: error received isoc data is larger then packet\n"); + len = USB_RET_NAK; + /* All good copy data over */ + } else { + len = aurb[i].urb.iso_frame_desc[j].actual_length; + memcpy(p->data, + aurb[i].urb.buffer + + j * aurb[i].urb.iso_frame_desc[0].length, + len); + } + aurb[i].iso_frame_idx++; + if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { + i = (i + 1) % ISO_URB_COUNT; + set_iso_urb_idx(s, p->devep, i); + } + } + + /* (Re)-submit all fully consumed urbs */ + for (i = 0; i < ISO_URB_COUNT; i++) { + if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { + ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]); + if (ret < 0) { + printf("husb error submitting isoc urb %d: %d\n", i, errno); + if (len == 0) { + switch(errno) { + case ETIMEDOUT: + len = USB_RET_NAK; + case EPIPE: + default: + len = USB_RET_STALL; + } + } + break; + } + aurb[i].iso_frame_idx = -1; + } + } + + return len; +} + static int usb_host_handle_data(USBHostDevice *s, USBPacket *p) { struct usbdevfs_urb *urb; AsyncURB *aurb; int ret; - - aurb = async_alloc(); - aurb->hdev = s; - aurb->packet = p; - - urb = &aurb->urb; + uint8_t ep; if (p->pid == USB_TOKEN_IN) { - urb->endpoint = p->devep | 0x80; + ep = p->devep | 0x80; } else { - urb->endpoint = p->devep; + ep = p->devep; } if (is_halted(s, p->devep)) { - ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint); + ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &ep); if (ret < 0) { DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n", - urb->endpoint, errno); + ep, errno); return USB_RET_NAK; } clear_halt(s, p->devep); } + if (is_isoc(s, p->devep) && p->pid == USB_TOKEN_IN) + return usb_host_handle_iso_data(s, p); + + aurb = async_alloc(); + aurb->hdev = s; + aurb->packet = p; + + urb = &aurb->urb; + + urb->endpoint = ep; urb->buffer = p->data; urb->buffer_length = p->len; @@ -515,7 +709,13 @@ static int usb_host_set_config(USBHostDevice *s, int config) static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) { struct usbdevfs_setinterface si; - int ret; + int i, ret; + + for (i = 1; i < MAX_ENDPOINTS; i++) { + if (is_isoc(s, i)) { + usb_host_stop_n_free_iso(s, i); + } + } si.interface = iface; si.altsetting = alt; @@ -927,6 +1127,8 @@ static int usb_linux_update_endp_table(USBHostDevice *s) break; case 0x01: type = USBDEVFS_URB_TYPE_ISO; + s->endp_table[(devep & 0xf) - 1].max_packet_size = + descriptors[i + 4] + (descriptors[i + 5] << 8); break; case 0x02: type = USBDEVFS_URB_TYPE_BULK; @@ -1049,12 +1251,19 @@ fail: static int usb_host_close(USBHostDevice *dev) { + int i; + if (dev->fd == -1) { return -1; } qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); dev->closing = 1; + for (i = 1; i < MAX_ENDPOINTS; i++) { + if (is_isoc(dev, i)) { + usb_host_stop_n_free_iso(dev, i); + } + } async_complete(dev); dev->closing = 0; usb_device_detach(&dev->dev); -- cgit v1.2.3 From a0b5fece8afe7deca08cbca97e2a4015d7f0038e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 26 Nov 2010 14:56:17 +0100 Subject: usb-linux: Refuse packets for endpoints which are not in the usb descriptor If an endpoint is not in the usb descriptor we've no idea what kind of endpoint it is and thus how to handle it, refuse packages in this case. Signed-off-by: Hans de Goede --- usb-linux.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/usb-linux.c b/usb-linux.c index a68603df76..6aef7a5db0 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -94,6 +94,7 @@ static int usb_fs_type; /* endpoint association data */ #define ISO_FRAME_DESC_PER_URB 32 #define ISO_URB_COUNT 3 +#define INVALID_EP_TYPE 255 typedef struct AsyncURB AsyncURB; @@ -168,6 +169,11 @@ static int is_isoc(USBHostDevice *s, int ep) return s->endp_table[ep - 1].type == USBDEVFS_URB_TYPE_ISO; } +static int is_valid(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].type != INVALID_EP_TYPE; +} + static int is_halted(USBHostDevice *s, int ep) { return s->endp_table[ep - 1].halted; @@ -611,6 +617,10 @@ static int usb_host_handle_data(USBHostDevice *s, USBPacket *p) int ret; uint8_t ep; + if (!is_valid(s, p->devep)) { + return USB_RET_NAK; + } + if (p->pid == USB_TOKEN_IN) { ep = p->devep | 0x80; } else { @@ -1071,6 +1081,9 @@ static int usb_linux_update_endp_table(USBHostDevice *s) uint8_t devep, type, configuration, alt_interface; int interface, length, i; + for (i = 0; i < MAX_ENDPOINTS; i++) + s->endp_table[i].type = INVALID_EP_TYPE; + i = usb_linux_get_configuration(s); if (i < 0) return 1; -- cgit v1.2.3 From 975f29984da4e25f2647d014ec3f4cf688c60e4d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 26 Nov 2010 14:59:35 +0100 Subject: usb-linux: Refuse iso packets when max packet size is 0 (alt setting 0) Refuse iso usb packets when then max packet size for the endpoint is 0, this avoids an abort in usb_host_alloc_iso() caused by trying to qemu_malloc a 0 bytes large buffer. --- usb-linux.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/usb-linux.c b/usb-linux.c index 6aef7a5db0..4c42fe181a 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -549,7 +549,11 @@ static int urb_status_to_usb_ret(int status) static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p) { AsyncURB *aurb; - int i, j, ret, len = 0; + int i, j, ret, max_packet_size, len = 0; + + max_packet_size = get_max_packet_size(s, p->devep); + if (max_packet_size == 0) + return USB_RET_NAK; aurb = get_iso_urb(s, p->devep); if (!aurb) { -- cgit v1.2.3 From 3a4854b372e0ca1a619d33531efa02477674e93f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 26 Nov 2010 15:02:16 +0100 Subject: usb-linux: We only need to keep track of 15 endpoints Currently we reserve room for endpoint data for 16 endpoints, but given that we only use endpoint data for endpoints 1-15, and always index the array with the endpoint-number - 1, 15 is enough. Signed-off-by: Hans de Goede --- usb-linux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/usb-linux.c b/usb-linux.c index 4c42fe181a..4498b16b07 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -78,7 +78,7 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath, #define USBPROCBUS_PATH "/proc/bus/usb" #define PRODUCT_NAME_SZ 32 -#define MAX_ENDPOINTS 16 +#define MAX_ENDPOINTS 15 #define USBDEVBUS_PATH "/dev/bus/usb" #define USBSYSBUS_PATH "/sys/bus/usb" @@ -725,7 +725,7 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) struct usbdevfs_setinterface si; int i, ret; - for (i = 1; i < MAX_ENDPOINTS; i++) { + for (i = 1; i <= MAX_ENDPOINTS; i++) { if (is_isoc(s, i)) { usb_host_stop_n_free_iso(s, i); } @@ -1276,7 +1276,7 @@ static int usb_host_close(USBHostDevice *dev) qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); dev->closing = 1; - for (i = 1; i < MAX_ENDPOINTS; i++) { + for (i = 1; i <= MAX_ENDPOINTS; i++) { if (is_isoc(dev, i)) { usb_host_stop_n_free_iso(dev, i); } -- cgit v1.2.3 From bb6d5498c6756eba3d0779c7753fc8830a8a9078 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 26 Nov 2010 19:11:03 +0100 Subject: usb-linux: Add support for buffering iso out usb packets Extend the iso buffering code to also buffer iso out packets, this fixes for example using usb speakers with usb redirection. Signed-off-by: Hans de Goede --- usb-linux.c | 152 ++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 51 deletions(-) diff --git a/usb-linux.c b/usb-linux.c index 4498b16b07..730eac2ca6 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -101,8 +101,10 @@ typedef struct AsyncURB AsyncURB; struct endp_data { uint8_t type; uint8_t halted; + uint8_t iso_started; AsyncURB *iso_urb; int iso_urb_idx; + int iso_buffer_used; int max_packet_size; }; @@ -189,6 +191,21 @@ static void set_halt(USBHostDevice *s, int ep) s->endp_table[ep - 1].halted = 1; } +static int is_iso_started(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].iso_started; +} + +static void clear_iso_started(USBHostDevice *s, int ep) +{ + s->endp_table[ep - 1].iso_started = 0; +} + +static void set_iso_started(USBHostDevice *s, int ep) +{ + s->endp_table[ep - 1].iso_started = 1; +} + static void set_iso_urb(USBHostDevice *s, int ep, AsyncURB *iso_urb) { s->endp_table[ep - 1].iso_urb = iso_urb; @@ -209,6 +226,16 @@ static int get_iso_urb_idx(USBHostDevice *s, int ep) return s->endp_table[ep - 1].iso_urb_idx; } +static void set_iso_buffer_used(USBHostDevice *s, int ep, int i) +{ + s->endp_table[ep - 1].iso_buffer_used = i; +} + +static int get_iso_buffer_used(USBHostDevice *s, int ep) +{ + return s->endp_table[ep - 1].iso_buffer_used; +} + static int get_max_packet_size(USBHostDevice *s, int ep) { return s->endp_table[ep - 1].max_packet_size; @@ -534,6 +561,8 @@ static void usb_host_stop_n_free_iso(USBHostDevice *s, uint8_t ep) else printf("husb: leaking iso urbs because of discard failure\n"); set_iso_urb(s, ep, NULL); + set_iso_urb_idx(s, ep, 0); + clear_iso_started(s, ep); } static int urb_status_to_usb_ret(int status) @@ -546,10 +575,10 @@ static int urb_status_to_usb_ret(int status) } } -static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p) +static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) { AsyncURB *aurb; - int i, j, ret, max_packet_size, len = 0; + int i, j, ret, max_packet_size, offset, len = 0; max_packet_size = get_max_packet_size(s, p->devep); if (max_packet_size == 0) @@ -557,57 +586,88 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p) aurb = get_iso_urb(s, p->devep); if (!aurb) { - aurb = usb_host_alloc_iso(s, p->devep, 1); + aurb = usb_host_alloc_iso(s, p->devep, in); } i = get_iso_urb_idx(s, p->devep); j = aurb[i].iso_frame_idx; if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { - /* Check urb status */ - if (aurb[i].urb.status) { - len = urb_status_to_usb_ret(aurb[i].urb.status); - /* Move to the next urb */ - aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1; - /* Check frame status */ - } else if (aurb[i].urb.iso_frame_desc[j].status) { - len = urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status); - /* Check the frame fits */ - } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) { - printf("husb: error received isoc data is larger then packet\n"); - len = USB_RET_NAK; - /* All good copy data over */ + if (in) { + /* Check urb status */ + if (aurb[i].urb.status) { + len = urb_status_to_usb_ret(aurb[i].urb.status); + /* Move to the next urb */ + aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1; + /* Check frame status */ + } else if (aurb[i].urb.iso_frame_desc[j].status) { + len = urb_status_to_usb_ret( + aurb[i].urb.iso_frame_desc[j].status); + /* Check the frame fits */ + } else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) { + printf("husb: received iso data is larger then packet\n"); + len = USB_RET_NAK; + /* All good copy data over */ + } else { + len = aurb[i].urb.iso_frame_desc[j].actual_length; + memcpy(p->data, + aurb[i].urb.buffer + + j * aurb[i].urb.iso_frame_desc[0].length, + len); + } } else { - len = aurb[i].urb.iso_frame_desc[j].actual_length; - memcpy(p->data, - aurb[i].urb.buffer + - j * aurb[i].urb.iso_frame_desc[0].length, - len); + len = p->len; + offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep); + + /* Check the frame fits */ + if (len > max_packet_size) { + printf("husb: send iso data is larger then max packet size\n"); + return USB_RET_NAK; + } + + /* All good copy data over */ + memcpy(aurb[i].urb.buffer + offset, p->data, len); + aurb[i].urb.iso_frame_desc[j].length = len; + offset += len; + set_iso_buffer_used(s, p->devep, offset); + + /* Start the stream once we have buffered enough data */ + if (!is_iso_started(s, p->devep) && i == 1 && j == 8) { + set_iso_started(s, p->devep); + } } aurb[i].iso_frame_idx++; if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { i = (i + 1) % ISO_URB_COUNT; set_iso_urb_idx(s, p->devep, i); } + } else { + if (in) { + set_iso_started(s, p->devep); + } else { + DPRINTF("hubs: iso out error no free buffer, dropping packet\n"); + } } - /* (Re)-submit all fully consumed urbs */ - for (i = 0; i < ISO_URB_COUNT; i++) { - if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { - ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]); - if (ret < 0) { - printf("husb error submitting isoc urb %d: %d\n", i, errno); - if (len == 0) { - switch(errno) { - case ETIMEDOUT: - len = USB_RET_NAK; - case EPIPE: - default: - len = USB_RET_STALL; + if (is_iso_started(s, p->devep)) { + /* (Re)-submit all fully consumed / filled urbs */ + for (i = 0; i < ISO_URB_COUNT; i++) { + if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { + ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]); + if (ret < 0) { + printf("husb error submitting iso urb %d: %d\n", i, errno); + if (!in || len == 0) { + switch(errno) { + case ETIMEDOUT: + len = USB_RET_NAK; + case EPIPE: + default: + len = USB_RET_STALL; + } } + break; } - break; + aurb[i].iso_frame_idx = -1; } - aurb[i].iso_frame_idx = -1; } } @@ -641,8 +701,9 @@ static int usb_host_handle_data(USBHostDevice *s, USBPacket *p) clear_halt(s, p->devep); } - if (is_isoc(s, p->devep) && p->pid == USB_TOKEN_IN) - return usb_host_handle_iso_data(s, p); + if (is_isoc(s, p->devep)) { + return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); + } aurb = async_alloc(); aurb->hdev = s; @@ -653,19 +714,8 @@ static int usb_host_handle_data(USBHostDevice *s, USBPacket *p) urb->endpoint = ep; urb->buffer = p->data; urb->buffer_length = p->len; - - if (is_isoc(s, p->devep)) { - /* Setup ISOC transfer */ - urb->type = USBDEVFS_URB_TYPE_ISO; - urb->flags = USBDEVFS_URB_ISO_ASAP; - urb->number_of_packets = 1; - urb->iso_frame_desc[0].length = p->len; - } else { - /* Setup bulk transfer */ - urb->type = USBDEVFS_URB_TYPE_BULK; - } - - urb->usercontext = s; + urb->type = USBDEVFS_URB_TYPE_BULK; + urb->usercontext = s; ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); -- cgit v1.2.3 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 ++++++ hw/usb.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/usb.c b/hw/usb.c index 82a6217a0b..d8c0a75c3a 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 diff --git a/hw/usb.h b/hw/usb.h index d3d755db7b..22bb3385ba 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -167,7 +167,7 @@ struct USBDevice { int32_t state; uint8_t setup_buf[8]; - uint8_t data_buf[1024]; + uint8_t data_buf[4096]; int32_t remote_wakeup; int32_t setup_state; int32_t setup_len; -- cgit v1.2.3 From ddf6583f88e29b2ec47fa81010c80868bfff7c83 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 14 Dec 2010 18:19:47 +0100 Subject: uhci: switch to QTAILQ --- hw/usb-uhci.c | 63 ++++++++++++++--------------------------------------------- 1 file changed, 15 insertions(+), 48 deletions(-) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 346db3e3d8..2de0cf2e5c 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -113,7 +113,7 @@ static void dump_data(const uint8_t *data, int len) {} */ typedef struct UHCIAsync { USBPacket packet; - struct UHCIAsync *next; + QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; uint32_t token; int8_t valid; @@ -145,8 +145,7 @@ typedef struct UHCIState { uint32_t pending_int_mask; /* Active packets */ - UHCIAsync *async_pending; - UHCIAsync *async_pool; + QTAILQ_HEAD(,UHCIAsync) async_pending; uint8_t num_ports_vmstate; } UHCIState; @@ -172,7 +171,6 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) async->token = 0; async->done = 0; async->isoc = 0; - async->next = NULL; return async; } @@ -184,24 +182,12 @@ static void uhci_async_free(UHCIState *s, UHCIAsync *async) static void uhci_async_link(UHCIState *s, UHCIAsync *async) { - async->next = s->async_pending; - s->async_pending = async; + QTAILQ_INSERT_HEAD(&s->async_pending, async, next); } static void uhci_async_unlink(UHCIState *s, UHCIAsync *async) { - UHCIAsync *curr = s->async_pending; - UHCIAsync **prev = &s->async_pending; - - while (curr) { - if (curr == async) { - *prev = curr->next; - return; - } - - prev = &curr->next; - curr = curr->next; - } + QTAILQ_REMOVE(&s->async_pending, async, next); } static void uhci_async_cancel(UHCIState *s, UHCIAsync *async) @@ -220,11 +206,10 @@ static void uhci_async_cancel(UHCIState *s, UHCIAsync *async) */ static UHCIAsync *uhci_async_validate_begin(UHCIState *s) { - UHCIAsync *async = s->async_pending; + UHCIAsync *async; - while (async) { + QTAILQ_FOREACH(async, &s->async_pending, next) { async->valid--; - async = async->next; } return NULL; } @@ -234,47 +219,30 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s) */ static void uhci_async_validate_end(UHCIState *s) { - UHCIAsync *curr = s->async_pending; - UHCIAsync **prev = &s->async_pending; - UHCIAsync *next; + UHCIAsync *curr, *n; - while (curr) { + QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { if (curr->valid > 0) { - prev = &curr->next; - curr = curr->next; continue; } - - next = curr->next; - - /* Unlink */ - *prev = next; - + uhci_async_unlink(s, curr); uhci_async_cancel(s, curr); - - curr = next; } } static void uhci_async_cancel_all(UHCIState *s) { - UHCIAsync *curr = s->async_pending; - UHCIAsync *next; - - while (curr) { - next = curr->next; + UHCIAsync *curr, *n; + QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { + uhci_async_unlink(s, curr); uhci_async_cancel(s, curr); - - curr = next; } - - s->async_pending = NULL; } static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token) { - UHCIAsync *async = s->async_pending; + UHCIAsync *async; UHCIAsync *match = NULL; int count = 0; @@ -291,7 +259,7 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token * If we ever do we'd want to optimize this algorithm. */ - while (async) { + QTAILQ_FOREACH(async, &s->async_pending, next) { if (async->token == token) { /* Good match */ match = async; @@ -301,8 +269,6 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token break; } } - - async = async->next; count++; } @@ -1137,6 +1103,7 @@ static int usb_uhci_common_initfn(UHCIState *s) s->expire_time = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / FRAME_TIMER_FREQ); s->num_ports_vmstate = NB_PORTS; + QTAILQ_INIT(&s->async_pending); qemu_register_reset(uhci_reset, s); -- cgit v1.2.3 From 7b5a44c546d9b836859c48bcfb7f1d4ef57ac3e1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 15 Dec 2010 10:26:15 +0100 Subject: uhci: keep uhci state pointer in async packet struct. Signed-off-by: Gerd Hoffmann --- hw/usb-uhci.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 2de0cf2e5c..2b63b3f9e0 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -106,6 +106,8 @@ static void dump_data(const uint8_t *data, int len) static void dump_data(const uint8_t *data, int len) {} #endif +typedef struct UHCIState UHCIState; + /* * Pending async transaction. * 'packet' must be the first field because completion @@ -113,6 +115,7 @@ static void dump_data(const uint8_t *data, int len) {} */ typedef struct UHCIAsync { USBPacket packet; + UHCIState *uhci; QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; uint32_t token; @@ -127,7 +130,7 @@ typedef struct UHCIPort { uint16_t ctrl; } UHCIPort; -typedef struct UHCIState { +struct UHCIState { PCIDevice dev; USBBus bus; uint16_t cmd; /* cmd register */ @@ -147,7 +150,7 @@ typedef struct UHCIState { /* Active packets */ QTAILQ_HEAD(,UHCIAsync) async_pending; uint8_t num_ports_vmstate; -} UHCIState; +}; typedef struct UHCI_TD { uint32_t link; @@ -166,6 +169,7 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) UHCIAsync *async = qemu_malloc(sizeof(UHCIAsync)); memset(&async->packet, 0, sizeof(async->packet)); + async->uhci = s; async->valid = 0; async->td = 0; async->token = 0; @@ -830,8 +834,8 @@ done: static void uhci_async_complete(USBPacket *packet, void *opaque) { - UHCIState *s = opaque; - UHCIAsync *async = (UHCIAsync *) packet; + UHCIAsync *async = container_of(packet, UHCIAsync, packet); + UHCIState *s = async->uhci; DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token); -- cgit v1.2.3 From 9066df13a3db0b8c4c283fb6c6772da3d507c9b7 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 15 Dec 2010 11:47:19 +0100 Subject: ohci: get ohci state via container_of() Signed-off-by: Gerd Hoffmann --- hw/usb-ohci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 0ad4f555d0..7678cdba11 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -577,7 +577,7 @@ static void ohci_process_lists(OHCIState *ohci, int completion); static void ohci_async_complete_packet(USBPacket *packet, void *opaque) { - OHCIState *ohci = opaque; + OHCIState *ohci = container_of(packet, OHCIState, usb_packet); #ifdef DEBUG_PACKET DPRINTF("Async packet complete\n"); #endif -- cgit v1.2.3 From 5dc1672b279344b5e4e1ba5526a8e45466b953a8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 16 Dec 2010 13:23:13 +0100 Subject: musb: get musb state via container_of() --- hw/usb-musb.c | 54 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 15bc549a85..30148e718d 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -267,7 +267,16 @@ static USBPortOps musb_port_ops = { .detach = musb_detach, }; -typedef struct { +typedef struct MUSBPacket MUSBPacket; +typedef struct MUSBEndPoint MUSBEndPoint; + +struct MUSBPacket { + USBPacket p; + MUSBEndPoint *ep; + int dir; +}; + +struct MUSBEndPoint { uint16_t faddr[2]; uint8_t haddr[2]; uint8_t hport[2]; @@ -284,7 +293,7 @@ typedef struct { int fifolen[2]; int fifostart[2]; int fifoaddr[2]; - USBPacket packey[2]; + MUSBPacket packey[2]; int status[2]; int ext_size[2]; @@ -294,7 +303,7 @@ typedef struct { MUSBState *musb; USBCallback *delayed_cb[2]; QEMUTimer *intv_timer[2]; -} MUSBEndPoint; +}; struct MUSBState { qemu_irq *irqs; @@ -321,7 +330,9 @@ struct MUSBState { /* Duplicating the world since 2008!... probably we should have 32 * logical, single endpoints instead. */ MUSBEndPoint ep[16]; -} *musb_init(qemu_irq *irqs) +}; + +struct MUSBState *musb_init(qemu_irq *irqs) { MUSBState *s = qemu_mallocz(sizeof(*s)); int i; @@ -488,14 +499,14 @@ static inline void musb_cb_tick0(void *opaque) { MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - ep->delayed_cb[0](&ep->packey[0], opaque); + ep->delayed_cb[0](&ep->packey[0].p, opaque); } static inline void musb_cb_tick1(void *opaque) { MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - ep->delayed_cb[1](&ep->packey[1], opaque); + ep->delayed_cb[1](&ep->packey[1].p, opaque); } #define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) @@ -587,17 +598,19 @@ static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->delayed_cb[dir] = cb; cb = dir ? musb_schedule1_cb : musb_schedule0_cb; - ep->packey[dir].pid = pid; + ep->packey[dir].p.pid = pid; /* A wild guess on the FADDR semantics... */ - ep->packey[dir].devaddr = ep->faddr[idx]; - ep->packey[dir].devep = ep->type[idx] & 0xf; - ep->packey[dir].data = (void *) ep->buf[idx]; - ep->packey[dir].len = len; - ep->packey[dir].complete_cb = cb; - ep->packey[dir].complete_opaque = ep; + ep->packey[dir].p.devaddr = ep->faddr[idx]; + ep->packey[dir].p.devep = ep->type[idx] & 0xf; + ep->packey[dir].p.data = (void *) ep->buf[idx]; + ep->packey[dir].p.len = len; + ep->packey[dir].p.complete_cb = cb; + ep->packey[dir].p.complete_opaque = ep; + ep->packey[dir].ep = ep; + ep->packey[dir].dir = dir; if (s->port.dev) - ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir]); + ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir].p); else ret = USB_RET_NODEV; @@ -607,7 +620,7 @@ static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep, } ep->status[dir] = ret; - usb_packet_complete(&ep->packey[dir]); + usb_packet_complete(&ep->packey[dir].p); } static void musb_tx_packet_complete(USBPacket *packey, void *opaque) @@ -821,14 +834,14 @@ static void musb_rx_req(MUSBState *s, int epnum) /* If we already have a packet, which didn't fit into the * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ - if (ep->packey[1].pid == USB_TOKEN_IN && ep->status[1] >= 0 && + if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && (ep->fifostart[1]) + ep->rxcount < - ep->packey[1].len) { + ep->packey[1].p.len) { TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); ep->fifostart[1] += ep->rxcount; ep->fifolen[1] = 0; - ep->rxcount = MIN(ep->packey[0].len - (ep->fifostart[1]), + ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]), ep->maxp[1]); ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; @@ -866,10 +879,11 @@ static void musb_rx_req(MUSBState *s, int epnum) #ifdef SETUPLEN_HACK /* Why should *we* do that instead of Linux? */ if (!epnum) { - if (ep->packey[0].devaddr == 2) + if (ep->packey[0].p.devaddr == 2) { total = MIN(s->setup_len, 8); - else + } else { total = MIN(s->setup_len, 64); + } s->setup_len -= total; } #endif -- cgit v1.2.3 From 13a9a0d3e253e272744b523e39642c9b6d564f4d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 16 Dec 2010 17:03:44 +0100 Subject: usb: move complete callback to port ops --- hw/usb-hub.c | 14 ++++++++++++++ hw/usb-msd.c | 4 ++-- hw/usb-musb.c | 27 +++++++++------------------ hw/usb-ohci.c | 7 ++----- hw/usb-uhci.c | 7 +++---- hw/usb.h | 7 +++---- usb-linux.c | 2 +- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 3dd31ba31f..e0588f891d 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -256,6 +256,19 @@ static void usb_hub_wakeup(USBDevice *dev) } } +static void usb_hub_complete(USBDevice *dev, USBPacket *packet) +{ + USBHubState *s = dev->port->opaque; + + /* + * Just pass it along upstream for now. + * + * If we ever inplement usb 2.0 split transactions this will + * become a little more complicated ... + */ + usb_packet_complete(&s->dev, packet); +} + static void usb_hub_handle_attach(USBDevice *dev) { USBHubState *s = DO_UPCAST(USBHubState, dev, dev); @@ -524,6 +537,7 @@ static USBPortOps usb_hub_port_ops = { .attach = usb_hub_attach, .detach = usb_hub_detach, .wakeup = usb_hub_wakeup, + .complete = usb_hub_complete, }; static int usb_hub_initfn(USBDevice *dev) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 947fd3f83c..93f4b78f3c 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -241,7 +241,7 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, s->mode = USB_MSDM_CSW; } s->packet = NULL; - usb_packet_complete(p); + usb_packet_complete(&s->dev, p); } else if (s->data_len == 0) { s->mode = USB_MSDM_CSW; } @@ -257,7 +257,7 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, usb_packet_complete returns. */ DPRINTF("Packet complete %p\n", p); s->packet = NULL; - usb_packet_complete(p); + usb_packet_complete(&s->dev, p); } } } diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 30148e718d..b30caeb4e8 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -261,10 +261,12 @@ static void musb_attach(USBPort *port); static void musb_detach(USBPort *port); +static void musb_schedule_cb(USBDevice *dev, USBPacket *p); static USBPortOps musb_port_ops = { .attach = musb_attach, .detach = musb_detach, + .complete = musb_schedule_cb, }; typedef struct MUSBPacket MUSBPacket; @@ -511,9 +513,11 @@ static inline void musb_cb_tick1(void *opaque) #define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) -static inline void musb_schedule_cb(USBPacket *packey, void *opaque, int dir) +static inline void musb_schedule_cb(USBDevice *dev, USBPacket *packey) { - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; + MUSBPacket *p = container_of(packey, MUSBPacket, p); + MUSBEndPoint *ep = p->ep; + int dir = p->dir; int timeout = 0; if (ep->status[dir] == USB_RET_NAK) @@ -521,25 +525,15 @@ static inline void musb_schedule_cb(USBPacket *packey, void *opaque, int dir) else if (ep->interrupt[dir]) timeout = 8; else - return musb_cb_tick(opaque); + return musb_cb_tick(ep); if (!ep->intv_timer[dir]) - ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, opaque); + ep->intv_timer[dir] = qemu_new_timer_ns(vm_clock, musb_cb_tick, ep); qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock_ns(vm_clock) + muldiv64(timeout, get_ticks_per_sec(), 8000)); } -static void musb_schedule0_cb(USBPacket *packey, void *opaque) -{ - return musb_schedule_cb(packey, opaque, 0); -} - -static void musb_schedule1_cb(USBPacket *packey, void *opaque) -{ - return musb_schedule_cb(packey, opaque, 1); -} - static int musb_timeout(int ttype, int speed, int val) { #if 1 @@ -596,7 +590,6 @@ static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->type[idx] >> 6, ep->interval[idx]); ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; ep->delayed_cb[dir] = cb; - cb = dir ? musb_schedule1_cb : musb_schedule0_cb; ep->packey[dir].p.pid = pid; /* A wild guess on the FADDR semantics... */ @@ -604,8 +597,6 @@ static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->packey[dir].p.devep = ep->type[idx] & 0xf; ep->packey[dir].p.data = (void *) ep->buf[idx]; ep->packey[dir].p.len = len; - ep->packey[dir].p.complete_cb = cb; - ep->packey[dir].p.complete_opaque = ep; ep->packey[dir].ep = ep; ep->packey[dir].dir = dir; @@ -620,7 +611,7 @@ static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep, } ep->status[dir] = ret; - usb_packet_complete(&ep->packey[dir].p); + usb_packet_complete(s->port.dev, &ep->packey[dir].p); } static void musb_tx_packet_complete(USBPacket *packey, void *opaque) diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 7678cdba11..8090c17c63 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -575,7 +575,7 @@ static void ohci_copy_iso_td(OHCIState *ohci, static void ohci_process_lists(OHCIState *ohci, int completion); -static void ohci_async_complete_packet(USBPacket *packet, void *opaque) +static void ohci_async_complete_packet(USBDevice *dev, USBPacket *packet) { OHCIState *ohci = container_of(packet, OHCIState, usb_packet); #ifdef DEBUG_PACKET @@ -748,8 +748,6 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); ohci->usb_packet.data = ohci->usb_buf; ohci->usb_packet.len = len; - ohci->usb_packet.complete_cb = ohci_async_complete_packet; - ohci->usb_packet.complete_opaque = ohci; ret = dev->info->handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; @@ -946,8 +944,6 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); ohci->usb_packet.data = ohci->usb_buf; ohci->usb_packet.len = len; - ohci->usb_packet.complete_cb = ohci_async_complete_packet; - ohci->usb_packet.complete_opaque = ohci; ret = dev->info->handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; @@ -1665,6 +1661,7 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={ static USBPortOps ohci_port_ops = { .attach = ohci_attach, .detach = ohci_detach, + .complete = ohci_async_complete_packet, }; static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 2b63b3f9e0..a65e0b3af6 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -642,7 +642,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) return ret; } -static void uhci_async_complete(USBPacket * packet, void *opaque); +static void uhci_async_complete(USBDevice *dev, USBPacket *packet); static void uhci_process_frame(UHCIState *s); /* return -1 if fatal error (frame must be stopped) @@ -795,8 +795,6 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in async->packet.devep = (td->token >> 15) & 0xf; async->packet.data = async->buffer; async->packet.len = max_len; - async->packet.complete_cb = uhci_async_complete; - async->packet.complete_opaque = s; switch(pid) { case USB_TOKEN_OUT: @@ -832,7 +830,7 @@ done: return len; } -static void uhci_async_complete(USBPacket *packet, void *opaque) +static void uhci_async_complete(USBDevice *dev, USBPacket *packet) { UHCIAsync *async = container_of(packet, UHCIAsync, packet); UHCIState *s = async->uhci; @@ -1083,6 +1081,7 @@ static USBPortOps uhci_port_ops = { .attach = uhci_attach, .detach = uhci_detach, .wakeup = uhci_wakeup, + .complete = uhci_async_complete, }; static int usb_uhci_common_initfn(UHCIState *s) diff --git a/hw/usb.h b/hw/usb.h index 22bb3385ba..7e46141fed 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -235,6 +235,7 @@ typedef struct USBPortOps { void (*attach)(USBPort *port); void (*detach)(USBPort *port); void (*wakeup)(USBDevice *dev); + void (*complete)(USBDevice *dev, USBPacket *p); } USBPortOps; /* USB port on which a device can be connected */ @@ -259,8 +260,6 @@ struct USBPacket { uint8_t *data; int len; /* Internal use by the USB layer. */ - USBCallback *complete_cb; - void *complete_opaque; USBCallback *cancel_cb; void *cancel_opaque; }; @@ -278,9 +277,9 @@ static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel, /* Notify the controller that an async packet is complete. This should only be called for packets previously deferred with usb_defer_packet, and should never be called from within handle_packet. */ -static inline void usb_packet_complete(USBPacket *p) +static inline void usb_packet_complete(USBDevice *dev, USBPacket *p) { - p->complete_cb(p, p->complete_opaque); + dev->port->ops->complete(dev, p); } /* Cancel an active packet. The packed must have been deferred with diff --git a/usb-linux.c b/usb-linux.c index 730eac2ca6..36a01ea0de 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -348,7 +348,7 @@ static void async_complete(void *opaque) break; } - usb_packet_complete(p); + usb_packet_complete(&s->dev, p); } async_free(aurb); -- cgit v1.2.3 From ef0bdf77d7070494692cbccd80c4c8f08c85c240 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 4 May 2011 16:49:56 +0200 Subject: usb: mass storage fix Initialize scsi_len with zero when starting a new request, so any stuff leftover from the previous request is cleared out. This may happen in case the data returned by the scsi command doesn't fit into the buffer provided by the guest. Signed-off-by: Gerd Hoffmann --- hw/usb-msd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 93f4b78f3c..bd1c3a415f 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -364,6 +364,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) DPRINTF("Command tag 0x%x flags %08x len %d data %d\n", s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue = 0; + s->scsi_len = 0; s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0); /* ??? Should check that USB and SCSI data transfer directions match. */ -- cgit v1.2.3