diff --git a/0119-usb-ehci-Clear-the-portstatus-powner-bit-on-device-d.patch b/0119-usb-ehci-Clear-the-portstatus-powner-bit-on-device-d.patch new file mode 100644 index 0000000..cb41dc0 --- /dev/null +++ b/0119-usb-ehci-Clear-the-portstatus-powner-bit-on-device-d.patch @@ -0,0 +1,35 @@ +From 959f57d34f11daf0da6f73541243934f39dfb2b2 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 13 Jan 2012 14:26:26 +0100 +Subject: [PATCH 119/140] usb-ehci: Clear the portstatus powner bit on device + disconnect + +According to the EHCI spec port ownerhsip should revert to the EHCI controller +on device disconnect. This fixes the problem of a port getting stuck on USB 1 +when using redirection and plugging in a USB 2 device after a USB 1 device +has been redirected. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index a946e1d..69bcc4b 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -764,6 +764,11 @@ static void ehci_detach(USBPort *port) + USBPort *companion = s->companion_ports[port->index]; + companion->ops->detach(companion); + companion->dev = NULL; ++ /* ++ * EHCI spec 4.2.2: "When a disconnect occurs... On the event, ++ * the port ownership is returned immediately to the EHCI controller." ++ */ ++ *portsc &= ~PORTSC_POWNER; + return; + } + +-- +1.7.9.3 + diff --git a/0120-usb-redir-Add-the-posibility-to-filter-out-certain-d.patch b/0120-usb-redir-Add-the-posibility-to-filter-out-certain-d.patch new file mode 100644 index 0000000..c0d0e9f --- /dev/null +++ b/0120-usb-redir-Add-the-posibility-to-filter-out-certain-d.patch @@ -0,0 +1,263 @@ +From bcc4748db3e991fbaa032fe9c0726288a8f1008d Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 12 Jan 2012 16:54:04 +0100 +Subject: [PATCH 120/140] usb-redir: Add the posibility to filter out certain + devices from redirecion + +This patch adds the posibility to filter out certain devices from redirecion. +To use this pass the filter property to -device usb-redir. The filter +property takes a string consisting of filter rules, the format for a rule is: +:::: + +-1 can be used to allow any value for a field. + +Muliple rules can be concatonated using | as a separator. Note that if +a device matches none of the passed in rules, redirecting it will not be +allowed! + +Example: +-device usb-redir,filter='-1:0x0781:0x5567:-1:0|0x08:-1:-1:-1:1' + +This example will deny the Sandisk Cruzer Blade being redirected, as it +has a usb id of 0781:5567, it will allow any other usb mass storage devices, +and it will deny any other devices (the default for devices not matching any +of the rules. + +Signed-off-by: Hans de Goede +--- + configure | 2 +- + usb-redir.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ + 2 files changed, 106 insertions(+), 11 deletions(-) + +diff --git a/configure b/configure +index 7ecf44e..c7e37df 100755 +--- a/configure ++++ b/configure +@@ -2541,7 +2541,7 @@ fi + + # check for usbredirparser for usb network redirection support + if test "$usb_redir" != "no" ; then +- if $pkg_config libusbredirparser >/dev/null 2>&1 ; then ++ if $pkg_config --atleast-version=0.3.3 libusbredirparser >/dev/null 2>&1 ; then + usb_redir="yes" + usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) + usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) +diff --git a/usb-redir.c b/usb-redir.c +index 6e92f14..85f40d6 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + + #include "hw/usb.h" + +@@ -72,6 +73,7 @@ struct USBRedirDevice { + /* Properties */ + CharDriverState *cs; + uint8_t debug; ++ char *filter_str; + /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ + const uint8_t *read_buf; + int read_buf_size; +@@ -84,6 +86,11 @@ struct USBRedirDevice { + struct endp_data endpoint[MAX_ENDPOINTS]; + uint32_t packet_id; + QTAILQ_HEAD(, AsyncURB) asyncq; ++ /* Data for device filtering */ ++ struct usb_redir_device_connect_header device_info; ++ struct usb_redir_interface_info_header interface_info; ++ struct usbredirfilter_rule *filter_rules; ++ int filter_rules_count; + }; + + struct AsyncURB { +@@ -790,6 +797,7 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, + static void usbredir_open_close_bh(void *opaque) + { + USBRedirDevice *dev = opaque; ++ uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; + + usbredir_device_disconnect(dev); + +@@ -820,7 +828,9 @@ static void usbredir_open_close_bh(void *opaque) + dev->parser->interrupt_packet_func = usbredir_interrupt_packet; + dev->read_buf = NULL; + dev->read_buf_size = 0; +- usbredirparser_init(dev->parser, VERSION, NULL, 0, 0); ++ ++ usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); ++ usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0); + usbredirparser_do_write(dev->parser); + } + } +@@ -908,6 +918,17 @@ static int usbredir_initfn(USBDevice *udev) + return -1; + } + ++ if (dev->filter_str) { ++ i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|", ++ &dev->filter_rules, ++ &dev->filter_rules_count); ++ if (i) { ++ qerror_report(QERR_INVALID_PARAMETER_VALUE, "filter", ++ "a usb device filter string"); ++ return -1; ++ } ++ } ++ + dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev); + dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); + +@@ -956,6 +977,44 @@ static void usbredir_handle_destroy(USBDevice *udev) + if (dev->parser) { + usbredirparser_destroy(dev->parser); + } ++ ++ free(dev->filter_rules); ++} ++ ++static int usbredir_check_filter(USBRedirDevice *dev) ++{ ++ if (dev->interface_info.interface_count == 0) { ++ ERROR("No interface info for device\n"); ++ return -1; ++ } ++ ++ if (dev->filter_rules) { ++ if (!usbredirparser_peer_has_cap(dev->parser, ++ usb_redir_cap_connect_device_version)) { ++ ERROR("Device filter specified and peer does not have the " ++ "connect_device_version capability\n"); ++ return -1; ++ } ++ ++ if (usbredirfilter_check( ++ dev->filter_rules, ++ dev->filter_rules_count, ++ dev->device_info.device_class, ++ dev->device_info.device_subclass, ++ dev->device_info.device_protocol, ++ dev->interface_info.interface_class, ++ dev->interface_info.interface_subclass, ++ dev->interface_info.interface_protocol, ++ dev->interface_info.interface_count, ++ dev->device_info.vendor_id, ++ dev->device_info.product_id, ++ dev->device_info.device_version_bcd, ++ 0) != 0) { ++ return -1; ++ } ++ } ++ ++ return 0; + } + + /* +@@ -984,6 +1043,7 @@ static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect) + { + USBRedirDevice *dev = priv; ++ const char *speed; + + if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) { + ERROR("Received device connect while already connected\n"); +@@ -992,26 +1052,48 @@ static void usbredir_device_connect(void *priv, + + switch (device_connect->speed) { + case usb_redir_speed_low: +- DPRINTF("attaching low speed device\n"); ++ speed = "low speed"; + dev->dev.speed = USB_SPEED_LOW; + break; + case usb_redir_speed_full: +- DPRINTF("attaching full speed device\n"); ++ speed = "full speed"; + dev->dev.speed = USB_SPEED_FULL; + break; + case usb_redir_speed_high: +- DPRINTF("attaching high speed device\n"); ++ speed = "high speed"; + dev->dev.speed = USB_SPEED_HIGH; + break; + case usb_redir_speed_super: +- DPRINTF("attaching super speed device\n"); ++ speed = "super speed"; + dev->dev.speed = USB_SPEED_SUPER; + break; + default: +- DPRINTF("attaching unknown speed device, assuming full speed\n"); ++ speed = "unknown speed"; + dev->dev.speed = USB_SPEED_FULL; + } ++ ++ if (usbredirparser_peer_has_cap(dev->parser, ++ usb_redir_cap_connect_device_version)) { ++ INFO("attaching %s device %04x:%04x version %d.%d class %02x\n", ++ speed, device_connect->vendor_id, device_connect->product_id, ++ device_connect->device_version_bcd >> 8, ++ device_connect->device_version_bcd & 0xff, ++ device_connect->device_class); ++ } else { ++ INFO("attaching %s device %04x:%04x class %02x\n", speed, ++ device_connect->vendor_id, device_connect->product_id, ++ device_connect->device_class); ++ } ++ + dev->dev.speedmask = (1 << dev->dev.speed); ++ dev->device_info = *device_connect; ++ ++ if (usbredir_check_filter(dev)) { ++ WARNING("Device %04x:%04x rejected by device filter, not attaching\n", ++ device_connect->vendor_id, device_connect->product_id); ++ return; ++ } ++ + qemu_mod_timer(dev->attach_timer, dev->next_attach_time); + } + +@@ -1038,15 +1120,27 @@ static void usbredir_device_disconnect(void *priv) + for (i = 0; i < MAX_ENDPOINTS; i++) { + QTAILQ_INIT(&dev->endpoint[i].bufpq); + } ++ dev->interface_info.interface_count = 0; + } + + static void usbredir_interface_info(void *priv, + struct usb_redir_interface_info_header *interface_info) + { +- /* The intention is to allow specifying acceptable interface classes +- for redirection on the cmdline and in the future verify this here, +- and disconnect (or never connect) the device if a not accepted +- interface class is detected */ ++ USBRedirDevice *dev = priv; ++ ++ dev->interface_info = *interface_info; ++ ++ /* ++ * If we receive interface info after the device has already been ++ * connected (ie on a set_config), re-check the filter. ++ */ ++ if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) { ++ if (usbredir_check_filter(dev)) { ++ ERROR("Device no longer matches filter after interface info " ++ "change, disconnecting!\n"); ++ usbredir_device_disconnect(dev); ++ } ++ } + } + + static void usbredir_ep_info(void *priv, +@@ -1356,6 +1450,7 @@ static struct USBDeviceInfo usbredir_dev_info = { + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), + DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), ++ DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), + DEFINE_PROP_END_OF_LIST(), + }, + }; +-- +1.7.9.3 + diff --git a/0121-usb-redir-Fix-printing-of-device-version.patch b/0121-usb-redir-Fix-printing-of-device-version.patch new file mode 100644 index 0000000..854641c --- /dev/null +++ b/0121-usb-redir-Fix-printing-of-device-version.patch @@ -0,0 +1,33 @@ +From 6c13e7b9448b10d966bb99c00c5120678ccd2a3f Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Sat, 18 Feb 2012 09:12:14 +0100 +Subject: [PATCH 121/140] usb-redir: Fix printing of device version + +The device version is in bcd format, which requires some special handling to +print. + +Signed-off-by: Hans de Goede +--- + usb-redir.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/usb-redir.c b/usb-redir.c +index 85f40d6..9b804e9 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -1076,8 +1076,10 @@ static void usbredir_device_connect(void *priv, + usb_redir_cap_connect_device_version)) { + INFO("attaching %s device %04x:%04x version %d.%d class %02x\n", + speed, device_connect->vendor_id, device_connect->product_id, +- device_connect->device_version_bcd >> 8, +- device_connect->device_version_bcd & 0xff, ++ ((device_connect->device_version_bcd & 0xf000) >> 12) * 10 + ++ ((device_connect->device_version_bcd & 0x0f00) >> 8), ++ ((device_connect->device_version_bcd & 0x00f0) >> 4) * 10 + ++ ((device_connect->device_version_bcd & 0x000f) >> 0), + device_connect->device_class); + } else { + INFO("attaching %s device %04x:%04x class %02x\n", speed, +-- +1.7.9.3 + diff --git a/0122-usb-redir-Always-clear-device-state-on-filter-reject.patch b/0122-usb-redir-Always-clear-device-state-on-filter-reject.patch new file mode 100644 index 0000000..66ccf68 --- /dev/null +++ b/0122-usb-redir-Always-clear-device-state-on-filter-reject.patch @@ -0,0 +1,64 @@ +From 49a01afb24b925de97074d093fb072bb7de470f9 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Sat, 18 Feb 2012 09:18:57 +0100 +Subject: [PATCH 122/140] usb-redir: Always clear device state on filter + reject + +Always call usbredir_device_disconnect() when usbredir_check_filter() fails +to clean up all the device state (ie received endpoint info). + +Signed-off-by: Hans de Goede +--- + usb-redir.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/usb-redir.c b/usb-redir.c +index 9b804e9..fe3b0a3 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -985,7 +985,7 @@ static int usbredir_check_filter(USBRedirDevice *dev) + { + if (dev->interface_info.interface_count == 0) { + ERROR("No interface info for device\n"); +- return -1; ++ goto error; + } + + if (dev->filter_rules) { +@@ -993,7 +993,7 @@ static int usbredir_check_filter(USBRedirDevice *dev) + usb_redir_cap_connect_device_version)) { + ERROR("Device filter specified and peer does not have the " + "connect_device_version capability\n"); +- return -1; ++ goto error; + } + + if (usbredirfilter_check( +@@ -1010,11 +1010,15 @@ static int usbredir_check_filter(USBRedirDevice *dev) + dev->device_info.product_id, + dev->device_info.device_version_bcd, + 0) != 0) { +- return -1; ++ goto error; + } + } + + return 0; ++ ++error: ++ usbredir_device_disconnect(dev); ++ return -1; + } + + /* +@@ -1140,7 +1144,6 @@ static void usbredir_interface_info(void *priv, + if (usbredir_check_filter(dev)) { + ERROR("Device no longer matches filter after interface info " + "change, disconnecting!\n"); +- usbredir_device_disconnect(dev); + } + } + } +-- +1.7.9.3 + diff --git a/0123-usb-redir-Let-the-usb-host-know-about-our-device-fil.patch b/0123-usb-redir-Let-the-usb-host-know-about-our-device-fil.patch new file mode 100644 index 0000000..9a166bf --- /dev/null +++ b/0123-usb-redir-Let-the-usb-host-know-about-our-device-fil.patch @@ -0,0 +1,93 @@ +From f04315d9210f22e5d7317f1cfb3c076fb93b3c08 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Sun, 19 Feb 2012 09:58:03 +0100 +Subject: [PATCH 123/140] usb-redir: Let the usb-host know about our device + filtering + +libusbredirparser-0.3.4 adds 2 new packets which allows us to notify +the usb-host: +-about the usb device filter we have (if any), so that it knows not the even + try to redirect certain devices +-when we reject a device based on filtering (in case it tries anyways) + +Signed-off-by: Hans de Goede +--- + configure | 2 +- + usb-redir.c | 20 ++++++++++++++++++++ + 2 files changed, 21 insertions(+), 1 deletion(-) + +diff --git a/configure b/configure +index c7e37df..a4848a4 100755 +--- a/configure ++++ b/configure +@@ -2541,7 +2541,7 @@ fi + + # check for usbredirparser for usb network redirection support + if test "$usb_redir" != "no" ; then +- if $pkg_config --atleast-version=0.3.3 libusbredirparser >/dev/null 2>&1 ; then ++ if $pkg_config --atleast-version=0.3.4 libusbredirparser >/dev/null 2>&1 ; then + usb_redir="yes" + usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) + usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) +diff --git a/usb-redir.c b/usb-redir.c +index fe3b0a3..d10d8de 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -106,6 +106,7 @@ struct AsyncURB { + QTAILQ_ENTRY(AsyncURB)next; + }; + ++static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); + static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect); + static void usbredir_device_disconnect(void *priv); +@@ -812,6 +813,7 @@ static void usbredir_open_close_bh(void *opaque) + dev->parser->log_func = usbredir_log; + dev->parser->read_func = usbredir_read; + dev->parser->write_func = usbredir_write; ++ dev->parser->hello_func = usbredir_hello; + dev->parser->device_connect_func = usbredir_device_connect; + dev->parser->device_disconnect_func = usbredir_device_disconnect; + dev->parser->interface_info_func = usbredir_interface_info; +@@ -830,6 +832,7 @@ static void usbredir_open_close_bh(void *opaque) + dev->read_buf_size = 0; + + usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); ++ usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); + usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0); + usbredirparser_do_write(dev->parser); + } +@@ -1018,6 +1021,10 @@ static int usbredir_check_filter(USBRedirDevice *dev) + + error: + usbredir_device_disconnect(dev); ++ if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) { ++ usbredirparser_send_filter_reject(dev->parser); ++ usbredirparser_do_write(dev->parser); ++ } + return -1; + } + +@@ -1043,6 +1050,19 @@ static int usbredir_handle_status(USBRedirDevice *dev, + } + } + ++static void usbredir_hello(void *priv, struct usb_redir_hello_header *h) ++{ ++ USBRedirDevice *dev = priv; ++ ++ /* Try to send the filter info now that we've the usb-host's caps */ ++ if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) && ++ dev->filter_rules) { ++ usbredirparser_send_filter_filter(dev->parser, dev->filter_rules, ++ dev->filter_rules_count); ++ usbredirparser_do_write(dev->parser); ++ } ++} ++ + static void usbredir_device_connect(void *priv, + struct usb_redir_device_connect_header *device_connect) + { +-- +1.7.9.3 + diff --git a/0124-usb-redir-Limit-return-values-returned-by-iso-packet.patch b/0124-usb-redir-Limit-return-values-returned-by-iso-packet.patch new file mode 100644 index 0000000..d35ae08 --- /dev/null +++ b/0124-usb-redir-Limit-return-values-returned-by-iso-packet.patch @@ -0,0 +1,42 @@ +From 91338d1b4df14f7454d1b52200d2ae4eb957fa72 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Sun, 26 Feb 2012 15:28:51 +0100 +Subject: [PATCH 124/140] usb-redir: Limit return values returned by iso + packets + +The usbredir protocol uses a status of usb_redir_stall to indicate that +an iso data stream has stopped (ie because the urbs failed on resubmit), +but iso packets should never return a result of USB_RET_STALL, since iso +endpoints cannot stall. So instead simply always return USB_RET_NAK on +iso stream errors. + +Signed-off-by: Hans de Goede +--- + usb-redir.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/usb-redir.c b/usb-redir.c +index d10d8de..c76e55d 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -441,7 +441,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, + /* Check iso_error for stream errors, otherwise its an underrun */ + status = dev->endpoint[EP2I(ep)].iso_error; + dev->endpoint[EP2I(ep)].iso_error = 0; +- return usbredir_handle_status(dev, status, 0); ++ return status ? USB_RET_NAK : 0; + } + DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep, + isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size); +@@ -449,7 +449,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, + status = isop->status; + if (status != usb_redir_success) { + bufp_free(dev, isop, ep); +- return usbredir_handle_status(dev, status, 0); ++ return USB_RET_NAK; + } + + len = isop->len; +-- +1.7.9.3 + diff --git a/0125-usb-redir-Return-USB_RET_NAK-when-we-ve-no-data-for-.patch b/0125-usb-redir-Return-USB_RET_NAK-when-we-ve-no-data-for-.patch new file mode 100644 index 0000000..516b431 --- /dev/null +++ b/0125-usb-redir-Return-USB_RET_NAK-when-we-ve-no-data-for-.patch @@ -0,0 +1,33 @@ +From ddb24b5063e3b4c90295bd4ddaab3bfc428ae79b Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Sun, 26 Feb 2012 15:51:50 +0100 +Subject: [PATCH 125/140] usb-redir: Return USB_RET_NAK when we've no data for + an interrupt endpoint + +We should return USB_RET_NAK, rather then a 0 sized packet, when we've no data +for an interrupt IN endpoint. + +Signed-off-by: Hans de Goede +--- + usb-redir.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/usb-redir.c b/usb-redir.c +index c76e55d..629c87d 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -558,7 +558,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, + /* Check interrupt_error for stream errors */ + status = dev->endpoint[EP2I(ep)].interrupt_error; + dev->endpoint[EP2I(ep)].interrupt_error = 0; +- return usbredir_handle_status(dev, status, 0); ++ if (status) { ++ return usbredir_handle_status(dev, status, 0); ++ } ++ return USB_RET_NAK; + } + DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, + intp->status, intp->len); +-- +1.7.9.3 + diff --git a/0126-usb-ehci-Handle-ISO-packets-failing-with-an-error-ot.patch b/0126-usb-ehci-Handle-ISO-packets-failing-with-an-error-ot.patch new file mode 100644 index 0000000..7c3c59d --- /dev/null +++ b/0126-usb-ehci-Handle-ISO-packets-failing-with-an-error-ot.patch @@ -0,0 +1,64 @@ +From 7a3acb928b617b33605c779e7df05c2c896844b1 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 20 Feb 2012 16:27:47 +0100 +Subject: [PATCH 126/140] usb-ehci: Handle ISO packets failing with an error + other then NAK + +Before this patch the ehci code was not checking for any other errors other +then USB_RET_NAK. This causes 2 problems: +1) Other errors are not reported to the guest. +2) When transactions with the ITD_XACT_IOC bit set completing with another + error would not result in USBSTS_INT getting set. + +I hit this problem when unplugging devices while iso data was streaming from +the device to the guest. When this happens it takes a while for the guest to +process the unplugging and remove ISO transactions from the ehci schedule, in +the mean time these transactions would complete with a result of USB_RET_NODEV, +which was not handled. This lead to the Linux guest's usb subsystem "hanging", +that is it would no longer see new usb devices getting plugged in and running +for example lsusb would lead to a stuck (D state) lsusb process. This patch +fixes this. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 69bcc4b..a6b6ae5 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1512,11 +1512,27 @@ static int ehci_process_itd(EHCIState *ehci, + /* IN */ + set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); + } +- +- if (itd->transact[i] & ITD_XACT_IOC) { +- ehci_record_interrupt(ehci, USBSTS_INT); ++ } else { ++ switch (ret) { ++ default: ++ fprintf(stderr, "Unexpected iso usb result: %d\n", ret); ++ /* Fall through */ ++ case USB_RET_NODEV: ++ /* 3.3.2: XACTERR is only allowed on IN transactions */ ++ if (dir) { ++ itd->transact[i] |= ITD_XACT_XACTERR; ++ ehci_record_interrupt(ehci, USBSTS_ERRINT); ++ } ++ break; ++ case USB_RET_BABBLE: ++ itd->transact[i] |= ITD_XACT_BABBLE; ++ ehci_record_interrupt(ehci, USBSTS_ERRINT); ++ break; + } + } ++ if (itd->transact[i] & ITD_XACT_IOC) { ++ ehci_record_interrupt(ehci, USBSTS_INT); ++ } + itd->transact[i] &= ~ITD_XACT_ACTIVE; + } + } +-- +1.7.9.3 + diff --git a/0127-usb-ehci-Never-follow-table-entries-with-the-T-bit-s.patch b/0127-usb-ehci-Never-follow-table-entries-with-the-T-bit-s.patch new file mode 100644 index 0000000..e437048 --- /dev/null +++ b/0127-usb-ehci-Never-follow-table-entries-with-the-T-bit-s.patch @@ -0,0 +1,61 @@ +From 82e500c24a026323e0b8b869e227cc68b179fb11 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Mon, 27 Feb 2012 11:44:52 +0100 +Subject: [PATCH 127/140] usb-ehci: Never follow table entries with the T-bit + set + +Before this patch the T-bit was not checked in 2 places, while it should be. + +Once we properly check the T-bit everywhere we no longer need the weird +entry < 0x1000 and entry > 0x1000 checks, so this patch removes them. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index a6b6ae5..37076a9 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1596,8 +1596,7 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async) + int again = 0; + uint32_t entry = ehci_get_fetch_addr(ehci, async); + +- if (entry < 0x1000) { +- DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry); ++ if (NLPTR_TBIT(entry)) { + ehci_set_state(ehci, async, EST_ACTIVE); + goto out; + } +@@ -1705,7 +1704,8 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) + if (q->qh.token & QTD_TOKEN_HALT) { + ehci_set_state(ehci, async, EST_HORIZONTALQH); + +- } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && (q->qh.current_qtd > 0x1000)) { ++ } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && ++ (NLPTR_TBIT(q->qh.current_qtd) == 0)) { + q->qtdaddr = q->qh.current_qtd; + ehci_set_state(ehci, async, EST_FETCHQTD); + +@@ -1784,7 +1784,6 @@ static int ehci_state_advqueue(EHCIQueue *q, int async) + * want data and alt-next qTD is valid + */ + if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && +- (q->qh.altnext_qtd > 0x1000) && + (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) { + q->qtdaddr = q->qh.altnext_qtd; + ehci_set_state(q->ehci, async, EST_FETCHQTD); +@@ -1792,8 +1791,7 @@ static int ehci_state_advqueue(EHCIQueue *q, int async) + /* + * next qTD is valid + */ +- } else if ((q->qh.next_qtd > 0x1000) && +- (NLPTR_TBIT(q->qh.next_qtd) == 0)) { ++ } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) { + q->qtdaddr = q->qh.next_qtd; + ehci_set_state(q->ehci, async, EST_FETCHQTD); + +-- +1.7.9.3 + diff --git a/0128-usb-ehci-split-our-qh-queue-into-async-and-periodic-.patch b/0128-usb-ehci-split-our-qh-queue-into-async-and-periodic-.patch new file mode 100644 index 0000000..6272c84 --- /dev/null +++ b/0128-usb-ehci-split-our-qh-queue-into-async-and-periodic-.patch @@ -0,0 +1,219 @@ +From 0f1e5b8d4f36de8b6b1301740226c9858b5a0318 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 28 Feb 2012 16:34:38 +0100 +Subject: [PATCH 128/140] usb-ehci: split our qh queue into async and periodic + queues + +qhs can be part of both the async and the periodic schedule, as is shown +in later patches in this series it is useful to keep track of the qhs on +a per schedule basis. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 62 ++++++++++++++++++++++++++++++++++----------------------- + 1 file changed, 37 insertions(+), 25 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 37076a9..980cce3 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -347,7 +347,6 @@ enum async_state { + struct EHCIQueue { + EHCIState *ehci; + QTAILQ_ENTRY(EHCIQueue) next; +- bool async_schedule; + uint32_t seen; + uint64_t ts; + +@@ -367,6 +366,8 @@ struct EHCIQueue { + int usb_status; + }; + ++typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; ++ + struct EHCIState { + PCIDevice dev; + USBBus bus; +@@ -410,7 +411,8 @@ struct EHCIState { + USBPort ports[NB_PORTS]; + USBPort *companion_ports[NB_PORTS]; + uint32_t usbsts_pending; +- QTAILQ_HEAD(, EHCIQueue) queues; ++ EHCIQueueHead aqueues; ++ EHCIQueueHead pqueues; + + uint32_t a_fetch_addr; // which address to look at next + uint32_t p_fetch_addr; // which address to look at next +@@ -660,31 +662,34 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr, + + static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) + { ++ EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + EHCIQueue *q; + + q = g_malloc0(sizeof(*q)); + q->ehci = ehci; +- q->async_schedule = async; +- QTAILQ_INSERT_HEAD(&ehci->queues, q, next); ++ QTAILQ_INSERT_HEAD(head, q, next); + trace_usb_ehci_queue_action(q, "alloc"); + return q; + } + +-static void ehci_free_queue(EHCIQueue *q) ++static void ehci_free_queue(EHCIQueue *q, int async) + { ++ EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues; + trace_usb_ehci_queue_action(q, "free"); + if (q->async == EHCI_ASYNC_INFLIGHT) { + usb_cancel_packet(&q->packet); + } +- QTAILQ_REMOVE(&q->ehci->queues, q, next); ++ QTAILQ_REMOVE(head, q, next); + g_free(q); + } + +-static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr) ++static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, ++ int async) + { ++ EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + EHCIQueue *q; + +- QTAILQ_FOREACH(q, &ehci->queues, next) { ++ QTAILQ_FOREACH(q, head, next) { + if (addr == q->qhaddr) { + return q; + } +@@ -692,11 +697,12 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr) + return NULL; + } + +-static void ehci_queues_rip_unused(EHCIState *ehci) ++static void ehci_queues_rip_unused(EHCIState *ehci, int async) + { ++ EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + EHCIQueue *q, *tmp; + +- QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { ++ QTAILQ_FOREACH_SAFE(q, head, next, tmp) { + if (q->seen) { + q->seen = 0; + q->ts = ehci->last_run_ns; +@@ -706,28 +712,30 @@ static void ehci_queues_rip_unused(EHCIState *ehci) + /* allow 0.25 sec idle */ + continue; + } +- ehci_free_queue(q); ++ ehci_free_queue(q, async); + } + } + +-static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev) ++static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) + { ++ EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + EHCIQueue *q, *tmp; + +- QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { ++ QTAILQ_FOREACH_SAFE(q, head, next, tmp) { + if (q->packet.owner != dev) { + continue; + } +- ehci_free_queue(q); ++ ehci_free_queue(q, async); + } + } + +-static void ehci_queues_rip_all(EHCIState *ehci) ++static void ehci_queues_rip_all(EHCIState *ehci, int async) + { ++ EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + EHCIQueue *q, *tmp; + +- QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { +- ehci_free_queue(q); ++ QTAILQ_FOREACH_SAFE(q, head, next, tmp) { ++ ehci_free_queue(q, async); + } + } + +@@ -772,7 +780,8 @@ static void ehci_detach(USBPort *port) + return; + } + +- ehci_queues_rip_device(s, port->dev); ++ ehci_queues_rip_device(s, port->dev, 0); ++ ehci_queues_rip_device(s, port->dev, 1); + + *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); + *portsc |= PORTSC_CSC; +@@ -792,7 +801,8 @@ static void ehci_child_detach(USBPort *port, USBDevice *child) + return; + } + +- ehci_queues_rip_device(s, child); ++ ehci_queues_rip_device(s, child, 0); ++ ehci_queues_rip_device(s, child, 1); + } + + static void ehci_wakeup(USBPort *port) +@@ -890,7 +900,8 @@ static void ehci_reset(void *opaque) + usb_send_msg(devs[i], USB_MSG_RESET); + } + } +- ehci_queues_rip_all(s); ++ ehci_queues_rip_all(s, 0); ++ ehci_queues_rip_all(s, 1); + } + + static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) +@@ -1554,7 +1565,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) + ehci_set_usbsts(ehci, USBSTS_REC); + } + +- ehci_queues_rip_unused(ehci); ++ ehci_queues_rip_unused(ehci, async); + + /* Find the head of the list (4.9.1.1) */ + for(i = 0; i < MAX_QH; i++) { +@@ -1641,7 +1652,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) + int reload; + + entry = ehci_get_fetch_addr(ehci, async); +- q = ehci_find_queue_by_qh(ehci, entry); ++ q = ehci_find_queue_by_qh(ehci, entry, async); + if (NULL == q) { + q = ehci_alloc_queue(ehci, async); + } +@@ -2092,7 +2103,7 @@ static void ehci_advance_state(EHCIState *ehci, + + static void ehci_advance_async_state(EHCIState *ehci) + { +- int async = 1; ++ const int async = 1; + + switch(ehci_get_state(ehci, async)) { + case EST_INACTIVE: +@@ -2149,7 +2160,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) + { + uint32_t entry; + uint32_t list; +- int async = 0; ++ const int async = 0; + + // 4.6 + +@@ -2366,7 +2377,8 @@ static int usb_ehci_initfn(PCIDevice *dev) + } + + s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s); +- QTAILQ_INIT(&s->queues); ++ QTAILQ_INIT(&s->aqueues); ++ QTAILQ_INIT(&s->pqueues); + + qemu_register_reset(ehci_reset, s); + +-- +1.7.9.3 + diff --git a/0129-usb-ehci-always-call-ehci_queues_rip_unused-for-peri.patch b/0129-usb-ehci-always-call-ehci_queues_rip_unused-for-peri.patch new file mode 100644 index 0000000..6305ec4 --- /dev/null +++ b/0129-usb-ehci-always-call-ehci_queues_rip_unused-for-peri.patch @@ -0,0 +1,42 @@ +From 320063f7165c5a5f9ddd5a09a4663bc1a81f5bd6 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 2 Mar 2012 13:52:44 +0100 +Subject: [PATCH 129/140] usb-ehci: always call ehci_queues_rip_unused for + period queues + +Before this patch USB 2 devices with interrupt endpoints were not working +properly. The problem is that to avoid loops we stop processing as soon +as we encounter a queue-head (qh) we've already seen since qhs can be linked +in a circular fashion, this is tracked by the seen flag in our qh struct. + +The resetting of the seen flag is done from ehci_queues_rip_unused which +before this patch was only called when executing the statemachine for the +async schedule. + +But packets for interrupt endpoints are part of the periodic schedule! So what +would happen is that when there were no ctrl or bulk packets for a USB 2 +device with an interrupt endpoint, the async schedule would become non +active, then ehci_queues_rip_unused would no longer get called and when +processing the qhs for the interrupt endpoints from the periodic schedule +their seen bit would still be 1 and they would be skipped. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 980cce3..422afc8 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -2195,6 +2195,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) + ehci_set_fetch_addr(ehci, async,entry); + ehci_set_state(ehci, async, EST_FETCHENTRY); + ehci_advance_state(ehci, async); ++ ehci_queues_rip_unused(ehci, async, 0); + break; + + default: +-- +1.7.9.3 + diff --git a/0130-usb-ehci-Drop-cached-qhs-when-the-doorbell-gets-rung.patch b/0130-usb-ehci-Drop-cached-qhs-when-the-doorbell-gets-rung.patch new file mode 100644 index 0000000..fa1b20d --- /dev/null +++ b/0130-usb-ehci-Drop-cached-qhs-when-the-doorbell-gets-rung.patch @@ -0,0 +1,115 @@ +From 35562fb521547e081e732453a6395fc00d9ee9e4 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 1 Mar 2012 15:20:17 +0100 +Subject: [PATCH 130/140] usb-ehci: Drop cached qhs when the doorbell gets + rung + +The purpose of the IAAD bit / the doorbell is to make the ehci controller +forget about cached qhs, this is mainly used when cancelling transactions, +the qh is unlinked from the async schedule and then the doorbell gets rung, +once the doorbell is acked by the controller the hcd knows that the qh is +no longer in use and that it can do something else with the memory, such +as re-use it for a new qh! But we keep our struct representing this qh around +for circa 250 ms. This allows for a (mightily large) race window where the +following could happen: +-hcd submits a qh at address 0xdeadbeef +-our ehci code sees the qh, sends a request to a usb-device, gets a result + of USB_RET_ASYNC, sets the async_state of the qh to EHCI_ASYNC_INFLIGHT +-hcd unlinks the qh at address 0xdeadbeef +-hcd rings the doorbell, wait for us to ack it +-hcd re-uses the qh at address 0xdeadbeef +-our ehci code sees the qh, looks in the async_queue, sees there already is + a qh at address 0xdeadbeef there with async_state of EHCI_ASYNC_INFLIGHT, + does nothing +-the *original* (which the hcd thinks it has cancelled) transaction finishes +-our ehci code sees the qh on yet another pass through the async list, + looks in the async_queue, sees there already is a qh at address 0xdeadbeef + there with async_state of EHCI_ASYNC_COMPLETED, and finished the transaction + with the results of the *original* transaction. + +Not good (tm), this patch fixes this race by removing all qhs which have not +been seen during the last cycle through the async list immidiately when the +doorbell is rung. + +Note this patch does not fix any actually observed problem, but upon +reading of the EHCI spec it became apparent to me that the above race could +happen and the usb-ehci behavior from before this patch is not good. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 31 ++++++++++++++++--------------- + 1 file changed, 16 insertions(+), 15 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 422afc8..b8ba483 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -697,7 +697,7 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, + return NULL; + } + +-static void ehci_queues_rip_unused(EHCIState *ehci, int async) ++static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) + { + EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + EHCIQueue *q, *tmp; +@@ -708,7 +708,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async) + q->ts = ehci->last_run_ns; + continue; + } +- if (ehci->last_run_ns < q->ts + 250000000) { ++ if (!flush && ehci->last_run_ns < q->ts + 250000000) { + /* allow 0.25 sec idle */ + continue; + } +@@ -1565,7 +1565,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) + ehci_set_usbsts(ehci, USBSTS_REC); + } + +- ehci_queues_rip_unused(ehci, async); ++ ehci_queues_rip_unused(ehci, async, 0); + + /* Find the head of the list (4.9.1.1) */ + for(i = 0; i < MAX_QH; i++) { +@@ -2121,18 +2121,7 @@ static void ehci_advance_async_state(EHCIState *ehci) + break; + } + +- /* If the doorbell is set, the guest wants to make a change to the +- * schedule. The host controller needs to release cached data. +- * (section 4.8.2) +- */ +- if (ehci->usbcmd & USBCMD_IAAD) { +- DPRINTF("ASYNC: doorbell request acknowledged\n"); +- ehci->usbcmd &= ~USBCMD_IAAD; +- ehci_set_interrupt(ehci, USBSTS_IAA); +- break; +- } +- +- /* make sure guest has acknowledged */ ++ /* make sure guest has acknowledged the doorbell interrupt */ + /* TO-DO: is this really needed? */ + if (ehci->usbsts & USBSTS_IAA) { + DPRINTF("IAA status bit still set.\n"); +@@ -2146,6 +2135,18 @@ static void ehci_advance_async_state(EHCIState *ehci) + + ehci_set_state(ehci, async, EST_WAITLISTHEAD); + ehci_advance_state(ehci, async); ++ ++ /* If the doorbell is set, the guest wants to make a change to the ++ * schedule. The host controller needs to release cached data. ++ * (section 4.8.2) ++ */ ++ if (ehci->usbcmd & USBCMD_IAAD) { ++ /* Remove all unseen qhs from the async qhs queue */ ++ ehci_queues_rip_unused(ehci, async, 1); ++ DPRINTF("ASYNC: doorbell request acknowledged\n"); ++ ehci->usbcmd &= ~USBCMD_IAAD; ++ ehci_set_interrupt(ehci, USBSTS_IAA); ++ } + break; + + default: +-- +1.7.9.3 + diff --git a/0131-usb-ehci-Rip-the-queues-when-the-async-or-period-sch.patch b/0131-usb-ehci-Rip-the-queues-when-the-async-or-period-sch.patch new file mode 100644 index 0000000..3f07ca0 --- /dev/null +++ b/0131-usb-ehci-Rip-the-queues-when-the-async-or-period-sch.patch @@ -0,0 +1,44 @@ +From cbb6384d32c4926822ba9216992253deef9ef3b5 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 2 Mar 2012 11:02:04 +0100 +Subject: [PATCH 131/140] usb-ehci: Rip the queues when the async or period + schedule is halted + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index b8ba483..11eded7 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1054,7 +1054,8 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) + + if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) { + qemu_del_timer(s->frame_timer); +- // TODO - should finish out some stuff before setting halt ++ ehci_queues_rip_all(s, 0); ++ ehci_queues_rip_all(s, 1); + ehci_set_usbsts(s, USBSTS_HALT); + } + +@@ -2116,6 +2117,7 @@ static void ehci_advance_async_state(EHCIState *ehci) + + case EST_ACTIVE: + if ( !(ehci->usbcmd & USBCMD_ASE)) { ++ ehci_queues_rip_all(ehci, async); + ehci_clear_usbsts(ehci, USBSTS_ASS); + ehci_set_state(ehci, async, EST_INACTIVE); + break; +@@ -2176,6 +2178,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) + + case EST_ACTIVE: + if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) { ++ ehci_queues_rip_all(ehci, async); + ehci_clear_usbsts(ehci, USBSTS_PSS); + ehci_set_state(ehci, async, EST_INACTIVE); + break; +-- +1.7.9.3 + diff --git a/0132-usb-ehci-Any-packet-completion-except-for-NAK-should.patch b/0132-usb-ehci-Any-packet-completion-except-for-NAK-should.patch new file mode 100644 index 0000000..7107ed8 --- /dev/null +++ b/0132-usb-ehci-Any-packet-completion-except-for-NAK-should.patch @@ -0,0 +1,33 @@ +From 73a9969c47459ee208d6247999823f2a36ee51fe Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 1 Mar 2012 16:34:56 +0100 +Subject: [PATCH 132/140] usb-ehci: Any packet completion except for NAK + should set the interrupt + +As clearly stated in the 2.3.2 of the EHCI spec, any time USBERRINT get +sets then if the td has its IOC bit set USBINT should be set as well. + +This means that for any status except for USB_RET_NAK we should set +USBINT if the IOC bit is set. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 11eded7..bc5f591 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1338,7 +1338,7 @@ err: + q->qh.token ^= QTD_TOKEN_DTOGGLE; + q->qh.token &= ~QTD_TOKEN_ACTIVE; + +- if ((q->usb_status >= 0) && (q->qh.token & QTD_TOKEN_IOC)) { ++ if ((q->usb_status != USB_RET_NAK) && (q->qh.token & QTD_TOKEN_IOC)) { + ehci_record_interrupt(q->ehci, USBSTS_INT); + } + } +-- +1.7.9.3 + diff --git a/0133-usb-ehci-Fix-cerr-tracking.patch b/0133-usb-ehci-Fix-cerr-tracking.patch new file mode 100644 index 0000000..6ae9d8e --- /dev/null +++ b/0133-usb-ehci-Fix-cerr-tracking.patch @@ -0,0 +1,72 @@ +From 6e6bfa88ae3867afd8258b43e3c05cba2585ee37 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 1 Mar 2012 21:43:56 +0100 +Subject: [PATCH 133/140] usb-ehci: Fix cerr tracking + +cerr should only be decremented on errors which cause XactErr to be set, and +when that happens the failing transaction should be retried until cerr reaches +0 and only then should USBSTS_ERRINT be set (and inactive cleared and +USBSTS_INT set if requested). + +Since we don't have any hardware level errors (and in case of redirection +the real hardware has already retried), re-trying makes no sense, so +immediately set cerr to 0 on errors which set XactErr. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 19 ++++++------------- + 1 file changed, 6 insertions(+), 13 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index bc5f591..a3d5c11 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1269,7 +1269,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) + + static void ehci_execute_complete(EHCIQueue *q) + { +- int c_err, reload; ++ int reload; + + assert(q->async != EHCI_ASYNC_INFLIGHT); + q->async = EHCI_ASYNC_NONE; +@@ -1278,15 +1278,10 @@ static void ehci_execute_complete(EHCIQueue *q) + q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status); + + if (q->usb_status < 0) { +-err: +- /* TO-DO: put this is in a function that can be invoked below as well */ +- c_err = get_field(q->qh.token, QTD_TOKEN_CERR); +- c_err--; +- set_field(&q->qh.token, c_err, QTD_TOKEN_CERR); +- + switch(q->usb_status) { + case USB_RET_NODEV: + q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); ++ set_field(&q->qh.token, 0, QTD_TOKEN_CERR); + ehci_record_interrupt(q->ehci, USBSTS_ERRINT); + break; + case USB_RET_STALL: +@@ -1314,15 +1309,13 @@ err: + assert(0); + break; + } ++ } else if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) { ++ q->usb_status = USB_RET_BABBLE; ++ q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); ++ ehci_record_interrupt(q->ehci, USBSTS_ERRINT); + } else { +- // DPRINTF("Short packet condition\n"); + // TODO check 4.12 for splits + +- if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) { +- q->usb_status = USB_RET_BABBLE; +- goto err; +- } +- + if (q->tbytes && q->pid == USB_TOKEN_IN) { + q->tbytes -= q->usb_status; + } else { +-- +1.7.9.3 + diff --git a/0134-usb-ehci-Remove-dead-nakcnt-code.patch b/0134-usb-ehci-Remove-dead-nakcnt-code.patch new file mode 100644 index 0000000..657ab26 --- /dev/null +++ b/0134-usb-ehci-Remove-dead-nakcnt-code.patch @@ -0,0 +1,78 @@ +From 6177c3610b6416a7200ae6c6985f01fccdbdc7e5 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 1 Mar 2012 23:11:13 +0100 +Subject: [PATCH 134/140] usb-ehci: Remove dead nakcnt code + +This patch removes 2 bits of dead nakcnt code: + +1) usb_ehci_execute calls ehci_qh_do_overlay which does: +nakcnt = reload; +and then has a block of code which is conditional on: +if (reload && !nakcnt) { +which ofcourse is never true now as nakcnt == reload. + +2) ehci_state_fetchqh does: +nakcnt = reload; +but before nakcnt is ever used ehci_state_fetchqh is always followed +by a ehci_qh_do_overlay call which also does: +nakcnt = reload; +So doing this from ehci_state_fetchqh is redundant. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 20 -------------------- + 1 file changed, 20 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index a3d5c11..92cdf2a 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1643,7 +1643,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) + { + uint32_t entry; + EHCIQueue *q; +- int reload; + + entry = ehci_get_fetch_addr(ehci, async); + q = ehci_find_queue_by_qh(ehci, entry, async); +@@ -1701,11 +1700,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) + } + #endif + +- reload = get_field(q->qh.epchar, QH_EPCHAR_RL); +- if (reload) { +- set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT); +- } +- + if (q->qh.token & QTD_TOKEN_HALT) { + ehci_set_state(ehci, async, EST_HORIZONTALQH); + +@@ -1865,25 +1859,11 @@ static void ehci_flush_qh(EHCIQueue *q) + static int ehci_state_execute(EHCIQueue *q, int async) + { + int again = 0; +- int reload, nakcnt; +- int smask; + + if (ehci_qh_do_overlay(q) != 0) { + return -1; + } + +- smask = get_field(q->qh.epcap, QH_EPCAP_SMASK); +- +- if (!smask) { +- reload = get_field(q->qh.epchar, QH_EPCHAR_RL); +- nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT); +- if (reload && !nakcnt) { +- ehci_set_state(q->ehci, async, EST_HORIZONTALQH); +- again = 1; +- goto out; +- } +- } +- + // TODO verify enough time remains in the uframe as in 4.4.1.1 + // TODO write back ptr to async list when done or out of time + // TODO Windows does not seem to ever set the MULT field +-- +1.7.9.3 + diff --git a/0135-usb-ehci-Fix-and-simplify-nakcnt-handling.patch b/0135-usb-ehci-Fix-and-simplify-nakcnt-handling.patch new file mode 100644 index 0000000..351f9e4 --- /dev/null +++ b/0135-usb-ehci-Fix-and-simplify-nakcnt-handling.patch @@ -0,0 +1,120 @@ +From 2d9b6cb9bd00ede47635dc4db413f647143d5a1d Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 1 Mar 2012 23:55:11 +0100 +Subject: [PATCH 135/140] usb-ehci: Fix and simplify nakcnt handling + +The nakcnt code in ehci_execute_complete() marked transactions as finished +when a packet completed with a result of USB_RET_NAK, but USB_RET_NAK +means that the device cannot receive / send data at that time and that +the transaction should be retried later, which is also what the usb-uhci +and usb-ohci code does. + +Note that there already was some special code in place to handle this +for interrupt endpoints in the form of doing a return from +ehci_execute_complete() when reload == 0, but that for bulk transactions +this was not handled correctly (where as for example the usb-ccid device does +return USB_RET_NAK for bulk packets). + +Besides that the code in ehci_execute_complete() decrement nakcnt by 1 +on a packet result of USB_RET_NAK, but +-since the transaction got marked as finished, + nakcnt would never be decremented again +-there is no code checking for nakcnt becoming 0 +-there is no use in re-trying the transaction within the same usb frame / + usb-ehci frame-timer call, since the status of emulated devices won't change + as long as the usb-ehci frame-timer is running +So we should simply set the nakcnt to 0 when we get a USB_RET_NAK, thus +claiming that we've tried reload times (or as many times as possible if +reload is 0). + +Besides the code in ehci_execute_complete() handling USB_RET_NAK there +was also code handling it in ehci_state_executing(), which calls +ehci_execute_complete(), and then does its own handling on top of the handling +in ehci_execute_complete(), this code would decrement nakcnt *again* (if not +already 0), or restore the reload value (which was never changed) on success. + +Since the double decrement was wrong to begin with, and is no longer needed +now that we set nakcnt directly to 0 on USB_RET_NAK, and the restore of reload +is not needed either, this patch simply removes all nakcnt handling from +ehci_state_executing(). + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 32 ++++---------------------------- + 1 file changed, 4 insertions(+), 28 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 92cdf2a..aa6fae5 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1269,8 +1269,6 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) + + static void ehci_execute_complete(EHCIQueue *q) + { +- int reload; +- + assert(q->async != EHCI_ASYNC_INFLIGHT); + q->async = EHCI_ASYNC_NONE; + +@@ -1289,16 +1287,8 @@ static void ehci_execute_complete(EHCIQueue *q) + ehci_record_interrupt(q->ehci, USBSTS_ERRINT); + break; + case USB_RET_NAK: +- /* 4.10.3 */ +- reload = get_field(q->qh.epchar, QH_EPCHAR_RL); +- if ((q->pid == USB_TOKEN_IN) && reload) { +- int nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT); +- nakcnt--; +- set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); +- } else if (!reload) { +- return; +- } +- break; ++ set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT); ++ return; /* We're not done yet with this transaction */ + case USB_RET_BABBLE: + q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); + ehci_record_interrupt(q->ehci, USBSTS_ERRINT); +@@ -1331,7 +1321,7 @@ static void ehci_execute_complete(EHCIQueue *q) + q->qh.token ^= QTD_TOKEN_DTOGGLE; + q->qh.token &= ~QTD_TOKEN_ACTIVE; + +- if ((q->usb_status != USB_RET_NAK) && (q->qh.token & QTD_TOKEN_IOC)) { ++ if (q->qh.token & QTD_TOKEN_IOC) { + ehci_record_interrupt(q->ehci, USBSTS_INT); + } + } +@@ -1905,7 +1895,6 @@ out: + static int ehci_state_executing(EHCIQueue *q, int async) + { + int again = 0; +- int reload, nakcnt; + + ehci_execute_complete(q); + if (q->usb_status == USB_RET_ASYNC) { +@@ -1925,21 +1914,8 @@ static int ehci_state_executing(EHCIQueue *q, int async) + // counter decrements to 0 + } + +- reload = get_field(q->qh.epchar, QH_EPCHAR_RL); +- if (reload) { +- nakcnt = get_field(q->qh.altnext_qtd, QH_ALTNEXT_NAKCNT); +- if (q->usb_status == USB_RET_NAK) { +- if (nakcnt) { +- nakcnt--; +- } +- } else { +- nakcnt = reload; +- } +- set_field(&q->qh.altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT); +- } +- + /* 4.10.5 */ +- if ((q->usb_status == USB_RET_NAK) || (q->qh.token & QTD_TOKEN_ACTIVE)) { ++ if (q->usb_status == USB_RET_NAK) { + ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + } else { + ehci_set_state(q->ehci, async, EST_WRITEBACK); +-- +1.7.9.3 + diff --git a/0136-usb-ehci-Remove-dead-isoch_pause-code.patch b/0136-usb-ehci-Remove-dead-isoch_pause-code.patch new file mode 100644 index 0000000..7d47913 --- /dev/null +++ b/0136-usb-ehci-Remove-dead-isoch_pause-code.patch @@ -0,0 +1,114 @@ +From 0e6cd6e6da3d0648204526e8ebd79047f48d009a Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 2 Mar 2012 00:36:50 +0100 +Subject: [PATCH 136/140] usb-ehci: Remove dead isoch_pause code + +I see no value in keeping this around, so lets delete it. + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 60 +++++++++++++++------------------------------------------ + 1 file changed, 15 insertions(+), 45 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index aa6fae5..72c3f2a 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -419,7 +419,6 @@ struct EHCIState { + + USBPacket ipacket; + QEMUSGList isgl; +- int isoch_pause; + + uint64_t last_run_ns; + }; +@@ -886,7 +885,6 @@ static void ehci_reset(void *opaque) + + s->astate = EST_INACTIVE; + s->pstate = EST_INACTIVE; +- s->isoch_pause = -1; + s->attach_poll_counter = 0; + + for(i = 0; i < NB_PORTS; i++) { +@@ -1468,46 +1466,7 @@ static int ehci_process_itd(EHCIState *ehci, + usb_packet_unmap(&ehci->ipacket); + qemu_sglist_destroy(&ehci->isgl); + +-#if 0 +- /* In isoch, there is no facility to indicate a NAK so let's +- * instead just complete a zero-byte transaction. Setting +- * DBERR seems too draconian. +- */ +- +- if (ret == USB_RET_NAK) { +- if (ehci->isoch_pause > 0) { +- DPRINTF("ISOCH: received a NAK but paused so returning\n"); +- ehci->isoch_pause--; +- return 0; +- } else if (ehci->isoch_pause == -1) { +- DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n"); +- // Pause frindex for up to 50 msec waiting for data from +- // remote +- ehci->isoch_pause = 50; +- return 0; +- } else { +- DPRINTF("ISOCH: isoch pause timeout! return 0\n"); +- ret = 0; +- } +- } else { +- DPRINTF("ISOCH: received ACK, clearing pause\n"); +- ehci->isoch_pause = -1; +- } +-#else +- if (ret == USB_RET_NAK) { +- ret = 0; +- } +-#endif +- +- if (ret >= 0) { +- if (!dir) { +- /* OUT */ +- set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH); +- } else { +- /* IN */ +- set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); +- } +- } else { ++ if (ret < 0) { + switch (ret) { + default: + fprintf(stderr, "Unexpected iso usb result: %d\n", ret); +@@ -1523,6 +1482,19 @@ static int ehci_process_itd(EHCIState *ehci, + itd->transact[i] |= ITD_XACT_BABBLE; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + break; ++ case USB_RET_NAK: ++ /* no data for us, so do a zero-length transfer */ ++ ret = 0; ++ break; ++ } ++ } ++ if (ret >= 0) { ++ if (!dir) { ++ /* OUT */ ++ set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH); ++ } else { ++ /* IN */ ++ set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); + } + } + if (itd->transact[i] & ITD_XACT_IOC) { +@@ -2176,9 +2148,7 @@ static void ehci_frame_timer(void *opaque) + + for (i = 0; i < frames; i++) { + if ( !(ehci->usbsts & USBSTS_HALT)) { +- if (ehci->isoch_pause <= 0) { +- ehci->frindex += 8; +- } ++ ehci->frindex += 8; + + if (ehci->frindex > 0x00001fff) { + ehci->frindex = 0; +-- +1.7.9.3 + diff --git a/0137-usb-return-BABBLE-rather-then-NAK-when-we-receive-to.patch b/0137-usb-return-BABBLE-rather-then-NAK-when-we-receive-to.patch new file mode 100644 index 0000000..3743d5e --- /dev/null +++ b/0137-usb-return-BABBLE-rather-then-NAK-when-we-receive-to.patch @@ -0,0 +1,70 @@ +From 9726556968aef62213b80bd4e351a4f7f721f941 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Thu, 1 Mar 2012 17:22:14 +0100 +Subject: [PATCH 137/140] usb: return BABBLE rather then NAK when we receive + too much data + +Signed-off-by: Hans de Goede +--- + usb-linux.c | 8 +++++++- + usb-redir.c | 4 ++-- + 2 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/usb-linux.c b/usb-linux.c +index ab4c693..b2d70f9 100644 +--- a/usb-linux.c ++++ b/usb-linux.c +@@ -390,6 +390,10 @@ static void async_complete(void *opaque) + p->result = USB_RET_STALL; + break; + ++ case -EOVERFLOW: ++ p->result = USB_RET_BABBLE; ++ break; ++ + default: + p->result = USB_RET_NAK; + break; +@@ -718,6 +722,8 @@ static int urb_status_to_usb_ret(int status) + switch (status) { + case -EPIPE: + return USB_RET_STALL; ++ case -EOVERFLOW: ++ return USB_RET_BABBLE; + default: + return USB_RET_NAK; + } +@@ -755,7 +761,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) + } else if (aurb[i].urb.iso_frame_desc[j].actual_length + > p->iov.size) { + printf("husb: received iso data is larger then packet\n"); +- len = USB_RET_NAK; ++ len = USB_RET_BABBLE; + /* All good copy data over */ + } else { + len = aurb[i].urb.iso_frame_desc[j].actual_length; +diff --git a/usb-redir.c b/usb-redir.c +index 629c87d..61860ef 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -457,7 +457,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, + ERROR("received iso data is larger then packet ep %02X (%d > %d)\n", + ep, len, (int)p->iov.size); + bufp_free(dev, isop, ep); +- return USB_RET_NAK; ++ return USB_RET_BABBLE; + } + usb_packet_copy(p, isop->data, len); + bufp_free(dev, isop, ep); +@@ -576,7 +576,7 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, + if (len > p->iov.size) { + ERROR("received int data is larger then packet ep %02X\n", ep); + bufp_free(dev, intp, ep); +- return USB_RET_NAK; ++ return USB_RET_BABBLE; + } + usb_packet_copy(p, intp->data, len); + bufp_free(dev, intp, ep); +-- +1.7.9.3 + diff --git a/0138-usb-add-USB_RET_IOERROR.patch b/0138-usb-add-USB_RET_IOERROR.patch new file mode 100644 index 0000000..0ed82a7 --- /dev/null +++ b/0138-usb-add-USB_RET_IOERROR.patch @@ -0,0 +1,167 @@ +From 2a6bbdddc2aca6af038c42054c3d3a7b09e5ac3a Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Fri, 2 Mar 2012 00:26:23 +0100 +Subject: [PATCH 138/140] usb: add USB_RET_IOERROR + +We already have USB_RET_NAK, but that means that a device does not want +to send/receive right now. But with host / network redirection we can +actually have a transaction fail due to some io error, rather then ie +the device just not having any data atm. + +This patch adds a new error code named USB_RET_IOERROR for this, and uses +it were appropriate. + +Notes: +-Currently all usb-controllers handle this the same as NODEV, but that + may change in the future, OHCI could indicate a CRC error instead for example. +-This patch does not touch hw/usb-musb.c, that is because the code in there + handles STALL and NAK specially and has a if status < 0 generic catch all + for all other errors + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 2 ++ + hw/usb-ohci.c | 2 ++ + hw/usb-uhci.c | 1 + + hw/usb.h | 11 ++++++----- + usb-linux.c | 4 ++-- + usb-redir.c | 9 ++++++--- + 6 files changed, 19 insertions(+), 10 deletions(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index 72c3f2a..ba1b9da 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1275,6 +1275,7 @@ static void ehci_execute_complete(EHCIQueue *q) + + if (q->usb_status < 0) { + switch(q->usb_status) { ++ case USB_RET_IOERROR: + case USB_RET_NODEV: + q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); + set_field(&q->qh.token, 0, QTD_TOKEN_CERR); +@@ -1471,6 +1472,7 @@ static int ehci_process_itd(EHCIState *ehci, + default: + fprintf(stderr, "Unexpected iso usb result: %d\n", ret); + /* Fall through */ ++ case USB_RET_IOERROR: + case USB_RET_NODEV: + /* 3.3.2: XACTERR is only allowed on IN transactions */ + if (dir) { +diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c +index c2981c5..d805497 100644 +--- a/hw/usb-ohci.c ++++ b/hw/usb-ohci.c +@@ -828,6 +828,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, + OHCI_CC_DATAUNDERRUN); + } else { + switch (ret) { ++ case USB_RET_IOERROR: + case USB_RET_NODEV: + OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, + OHCI_CC_DEVICENOTRESPONDING); +@@ -1051,6 +1052,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN); + } else { + switch (ret) { ++ case USB_RET_IOERROR: + case USB_RET_NODEV: + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING); + case USB_RET_NAK: +diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c +index f9e3ea5..a994943 100644 +--- a/hw/usb-uhci.c ++++ b/hw/usb-uhci.c +@@ -751,6 +751,7 @@ out: + break; + return 1; + ++ case USB_RET_IOERROR: + case USB_RET_NODEV: + default: + break; +diff --git a/hw/usb.h b/hw/usb.h +index c6e1870..4010e12 100644 +--- a/hw/usb.h ++++ b/hw/usb.h +@@ -41,11 +41,12 @@ + #define USB_MSG_DETACH 0x101 + #define USB_MSG_RESET 0x102 + +-#define USB_RET_NODEV (-1) +-#define USB_RET_NAK (-2) +-#define USB_RET_STALL (-3) +-#define USB_RET_BABBLE (-4) +-#define USB_RET_ASYNC (-5) ++#define USB_RET_NODEV (-1) ++#define USB_RET_NAK (-2) ++#define USB_RET_STALL (-3) ++#define USB_RET_BABBLE (-4) ++#define USB_RET_IOERROR (-5) ++#define USB_RET_ASYNC (-6) + + #define USB_SPEED_LOW 0 + #define USB_SPEED_FULL 1 +diff --git a/usb-linux.c b/usb-linux.c +index b2d70f9..9f13d1e 100644 +--- a/usb-linux.c ++++ b/usb-linux.c +@@ -395,7 +395,7 @@ static void async_complete(void *opaque) + break; + + default: +- p->result = USB_RET_NAK; ++ p->result = USB_RET_IOERROR; + break; + } + +@@ -725,7 +725,7 @@ static int urb_status_to_usb_ret(int status) + case -EOVERFLOW: + return USB_RET_BABBLE; + default: +- return USB_RET_NAK; ++ return USB_RET_IOERROR; + } + } + +diff --git a/usb-redir.c b/usb-redir.c +index 61860ef..f64443e 100644 +--- a/usb-redir.c ++++ b/usb-redir.c +@@ -441,7 +441,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, + /* Check iso_error for stream errors, otherwise its an underrun */ + status = dev->endpoint[EP2I(ep)].iso_error; + dev->endpoint[EP2I(ep)].iso_error = 0; +- return status ? USB_RET_NAK : 0; ++ return status ? USB_RET_IOERROR : 0; + } + DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep, + isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size); +@@ -449,7 +449,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, + status = isop->status; + if (status != usb_redir_success) { + bufp_free(dev, isop, ep); +- return USB_RET_NAK; ++ return USB_RET_IOERROR; + } + + len = isop->len; +@@ -1045,11 +1045,14 @@ static int usbredir_handle_status(USBRedirDevice *dev, + return USB_RET_STALL; + case usb_redir_cancelled: + WARNING("returning cancelled packet to HC?\n"); ++ return USB_RET_NAK; + case usb_redir_inval: ++ WARNING("got invalid param error from usb-host?\n"); ++ return USB_RET_NAK; + case usb_redir_ioerror: + case usb_redir_timeout: + default: +- return USB_RET_NAK; ++ return USB_RET_IOERROR; + } + } + +-- +1.7.9.3 + diff --git a/0139-usb-ehci-fix-reset.patch b/0139-usb-ehci-fix-reset.patch new file mode 100644 index 0000000..d9e6ac1 --- /dev/null +++ b/0139-usb-ehci-fix-reset.patch @@ -0,0 +1,40 @@ +From 21946e621f14553b72cde7fae221ae390a427eac Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Thu, 23 Feb 2012 13:24:00 +0000 +Subject: [PATCH 139/140] usb-ehci: fix reset + +Two reset fixes: + * pick up s->usbcmd value after ehci_reset call to make sure it + keeps the reset value and doesn't get rubbish filled in when + val is written back to the mmio register array later on. + * make sure the frame timer is zapped on reset. + +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ehci.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index ba1b9da..ad0f6e1 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -900,6 +900,7 @@ static void ehci_reset(void *opaque) + } + ehci_queues_rip_all(s, 0); + ehci_queues_rip_all(s, 1); ++ qemu_del_timer(s->frame_timer); + } + + static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) +@@ -1059,7 +1060,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) + + if (val & USBCMD_HCRESET) { + ehci_reset(s); +- val &= ~USBCMD_HCRESET; ++ val = s->usbcmd; + } + + /* not supporting dynamic frame list size at the moment */ +-- +1.7.9.3 + diff --git a/0140-usb-ehci-sanity-check-iso-xfers.patch b/0140-usb-ehci-sanity-check-iso-xfers.patch new file mode 100644 index 0000000..1305f51 --- /dev/null +++ b/0140-usb-ehci-sanity-check-iso-xfers.patch @@ -0,0 +1,42 @@ +From 985b7cfbd45960bb74a13ad8044765a8e35f2251 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Sun, 4 Mar 2012 12:41:11 +0100 +Subject: [PATCH 140/140] usb-ehci: sanity-check iso xfers + +This patch adds a sanity check to itd processing to make sure the +endpoint addressed by the guest is actually an iso endpoint. Also +verify that usb drivers don't return USB_RET_ASYNC which is illegal for +iso xfers. + +Signed-off-by: Gerd Hoffmann +(Cherry picked from: aa0568ff2559d7717f4684af6a83d0bd1a125f56) + +[qemu-kvm-1.0: we don't track ep types on RHEL-6 like we do upstream, so we +cannot check if an itd is pointing to a non iso ep in advance, but we do still +need to make sure that we never handle an iso xfer async. So check if the +device does want to handle it async, and if so cancel the xfer and treat it as +a NAK, like upstream does when the ep type check fails.] + +Signed-off-by: Hans de Goede +--- + hw/usb-ehci.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c +index ad0f6e1..b5d7037 100644 +--- a/hw/usb-ehci.c ++++ b/hw/usb-ehci.c +@@ -1485,6 +1485,10 @@ static int ehci_process_itd(EHCIState *ehci, + itd->transact[i] |= ITD_XACT_BABBLE; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + break; ++ case USB_RET_ASYNC: ++ /* ISO endpoints are never ASYNC, not an iso endpoint? */ ++ usb_cancel_packet(&ehci->ipacket); ++ /* Treat this as a NAK (fall through) */ + case USB_RET_NAK: + /* no data for us, so do a zero-length transfer */ + ret = 0; +-- +1.7.9.3 + diff --git a/qemu.spec b/qemu.spec index 6ffcbbc..ad235d1 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1,7 +1,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 1.0 -Release: 6%{?dist} +Release: 7%{?dist} # Epoch because we pushed a qemu-1.0 package Epoch: 2 License: GPLv2+ and LGPLv2+ and BSD @@ -86,6 +86,28 @@ Patch115: 0115-spice-qemu-char.c-remove-intermediate-buffer.patch Patch116: 0116-usb-redir-Add-flow-control-support.patch Patch117: 0117-virtio-serial-bus-replay-guest_open-on-migration.patch Patch118: 0118-char-Disable-write-callback-if-throttled-chardev-is-.patch +Patch119: 0119-usb-ehci-Clear-the-portstatus-powner-bit-on-device-d.patch +Patch120: 0120-usb-redir-Add-the-posibility-to-filter-out-certain-d.patch +Patch121: 0121-usb-redir-Fix-printing-of-device-version.patch +Patch122: 0122-usb-redir-Always-clear-device-state-on-filter-reject.patch +Patch123: 0123-usb-redir-Let-the-usb-host-know-about-our-device-fil.patch +Patch124: 0124-usb-redir-Limit-return-values-returned-by-iso-packet.patch +Patch125: 0125-usb-redir-Return-USB_RET_NAK-when-we-ve-no-data-for-.patch +Patch126: 0126-usb-ehci-Handle-ISO-packets-failing-with-an-error-ot.patch +Patch127: 0127-usb-ehci-Never-follow-table-entries-with-the-T-bit-s.patch +Patch128: 0128-usb-ehci-split-our-qh-queue-into-async-and-periodic-.patch +Patch129: 0129-usb-ehci-always-call-ehci_queues_rip_unused-for-peri.patch +Patch130: 0130-usb-ehci-Drop-cached-qhs-when-the-doorbell-gets-rung.patch +Patch131: 0131-usb-ehci-Rip-the-queues-when-the-async-or-period-sch.patch +Patch132: 0132-usb-ehci-Any-packet-completion-except-for-NAK-should.patch +Patch133: 0133-usb-ehci-Fix-cerr-tracking.patch +Patch134: 0134-usb-ehci-Remove-dead-nakcnt-code.patch +Patch135: 0135-usb-ehci-Fix-and-simplify-nakcnt-handling.patch +Patch136: 0136-usb-ehci-Remove-dead-isoch_pause-code.patch +Patch137: 0137-usb-return-BABBLE-rather-then-NAK-when-we-receive-to.patch +Patch138: 0138-usb-add-USB_RET_IOERROR.patch +Patch139: 0139-usb-ehci-fix-reset.patch +Patch140: 0140-usb-ehci-sanity-check-iso-xfers.patch # General bug fixes Patch201: Fix_save-restore_of_in-kernel_i8259.patch @@ -382,6 +404,28 @@ such as kvm_stat. %patch116 -p1 %patch117 -p1 %patch118 -p1 +%patch119 -p1 +%patch120 -p1 +%patch121 -p1 +%patch122 -p1 +%patch123 -p1 +%patch124 -p1 +%patch125 -p1 +%patch126 -p1 +%patch127 -p1 +%patch128 -p1 +%patch129 -p1 +%patch130 -p1 +%patch131 -p1 +%patch132 -p1 +%patch133 -p1 +%patch134 -p1 +%patch135 -p1 +%patch136 -p1 +%patch137 -p1 +%patch138 -p1 +%patch139 -p1 +%patch140 -p1 %patch201 -p1 @@ -768,6 +812,9 @@ fi %{_mandir}/man1/qemu-img.1* %changelog +* Fri Mar 9 2012 Hans de Goede - 2:1.0-7 +- Add a whole bunch of USB bugfixes from upstream + * Mon Feb 13 2012 Daniel P. Berrange - 2:1.0-6 - Add many more missing BRs for misc QEMU features - Enable running of test suite during build