From 3200d1085df5f368885428e33d9439f55c7f1a47 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 26 Jan 2012 13:57:40 +0100 Subject: [PATCH 01/28] usb-uhci: implement bandwidth management The OS is allowed to make the UHCI Controller run in circles. That is usually done to serve multiple connected USB devices in a robin-round fashion, so the available USB bandwidth is evenly distributed between devices. The uhci emulation handles this in a very poor way though. When it figures it runs in circles it stops processing unconditionally, so it usually processes at most a single transfer desriptor per queue, even if there are multiple transfer descriptors are queued up. This patch makes uhci act in a more sophisticated way. It keeps track of successful processed transfer descriptors and transfered bytes. Then it will stop processing when there is nothing to do (no transfer descriptor was completed the last round) or when the transfered data reaches the usb bandwidth limit. Result is that the usb-storage devices connected to uhci are ten times faster, mkfs.vfat time for a 64M stick goes down from five seconds to a half second. Reason for this is that we are now processing up to 20 transfer descriptors (with 64 bytes each) per frame instead of a single one. Signed-off-by: Gerd Hoffmann --- hw/usb-uhci.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index cddcc8927a..e0c7dbb71a 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -73,7 +73,7 @@ #define FRAME_TIMER_FREQ 1000 -#define FRAME_MAX_LOOPS 100 +#define FRAME_MAX_LOOPS 256 #define NB_PORTS 2 @@ -942,7 +942,7 @@ static int qhdb_insert(QhDb *db, uint32_t addr) static void uhci_process_frame(UHCIState *s) { uint32_t frame_addr, link, old_td_ctrl, val, int_mask; - uint32_t curr_qh; + uint32_t curr_qh, td_count = 0, bytes_count = 0; int cnt, ret; UHCI_TD td; UHCI_QH qh; @@ -967,13 +967,26 @@ static void uhci_process_frame(UHCIState *s) if (qhdb_insert(&qhdb, link)) { /* * We're going in circles. Which is not a bug because - * HCD is allowed to do that as part of the BW management. - * In our case though it makes no sense to spin here. Sync transations - * are already done, and async completion handler will re-process - * the frame when something is ready. + * HCD is allowed to do that as part of the BW management. + * + * Stop processing here if + * (a) no transaction has been done since we've been + * here last time, or + * (b) we've reached the usb 1.1 bandwidth, which is + * 1280 bytes/frame. */ DPRINTF("uhci: detected loop. qh 0x%x\n", link); - break; + if (td_count == 0) { + DPRINTF("uhci: no transaction last round, stop\n"); + break; + } else if (bytes_count >= 1280) { + DPRINTF("uhci: bandwidth limit reached, stop\n"); + break; + } else { + td_count = 0; + qhdb_reset(&qhdb); + qhdb_insert(&qhdb, link); + } } pci_dma_read(&s->dev, link & ~0xf, &qh, sizeof(qh)); @@ -1033,6 +1046,8 @@ static void uhci_process_frame(UHCIState *s) link, td.link, td.ctrl, td.token, curr_qh); link = td.link; + td_count++; + bytes_count += (td.ctrl & 0x7ff) + 1; if (curr_qh) { /* update QH element link */ From f76e1d814241794467366d70d3d77849a6fd5ab0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 13 Jan 2012 14:28:56 +0100 Subject: [PATCH 02/28] usb-ehci: Clear the portstatus powner bit on device disconnect According to the EHCI spec port ownership 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 Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 75ef71e69e..cdd74154a0 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -765,6 +765,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; } From 6af165892cf900291046f1d25f95416f379504c2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 25 Jan 2012 09:50:39 +0100 Subject: [PATCH 03/28] 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 Signed-off-by: Gerd Hoffmann --- configure | 2 +- usb-redir.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 106 insertions(+), 11 deletions(-) diff --git a/configure b/configure index 763db24cd0..68f9ee6acb 100755 --- a/configure +++ b/configure @@ -2584,7 +2584,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 303292acd6..0a9295142c 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 { @@ -780,6 +787,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); @@ -810,7 +818,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); } } @@ -880,6 +890,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); @@ -929,6 +950,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; } /* @@ -957,6 +1016,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"); @@ -965,26 +1025,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); } @@ -1011,15 +1093,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, @@ -1318,6 +1412,7 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id, static Property usbredir_properties[] = { 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(), }; From d1f8b53618b3daa93a113ba02520cd1bd34c254b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 6 Jan 2012 15:13:34 +0100 Subject: [PATCH 04/28] usb: kill USB_MSG_{ATTACH,DETACH} The USB subsystem pipes internal attach/detach notifications through usb_handle_packet() with a special magic PID. This indirection is a pretty pointless excercise as it ends up being handled by usb_generic_handle_packet anyway. Remove it. Signed-off-by: Gerd Hoffmann --- hw/usb.c | 14 +++----------- hw/usb.h | 2 -- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/hw/usb.c b/hw/usb.c index c3ff5b7093..800dd835de 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -35,7 +35,8 @@ void usb_attach(USBPort *port) assert(dev->attached); assert(dev->state == USB_STATE_NOTATTACHED); port->ops->attach(port); - usb_send_msg(dev, USB_MSG_ATTACH); + dev->state = USB_STATE_ATTACHED; + usb_device_handle_attach(dev); } void usb_detach(USBPort *port) @@ -45,7 +46,7 @@ void usb_detach(USBPort *port) assert(dev != NULL); assert(dev->state != USB_STATE_NOTATTACHED); port->ops->detach(port); - usb_send_msg(dev, USB_MSG_DETACH); + dev->state = USB_STATE_NOTATTACHED; } void usb_reset(USBPort *port) @@ -218,15 +219,6 @@ static int do_token_out(USBDevice *s, USBPacket *p) int usb_generic_handle_packet(USBDevice *s, USBPacket *p) { switch(p->pid) { - case USB_MSG_ATTACH: - s->state = USB_STATE_ATTACHED; - usb_device_handle_attach(s); - return 0; - - case USB_MSG_DETACH: - s->state = USB_STATE_NOTATTACHED; - return 0; - case USB_MSG_RESET: s->remote_wakeup = 0; s->addr = 0; diff --git a/hw/usb.h b/hw/usb.h index 13e7c8ea7f..0bd909818a 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -40,8 +40,6 @@ #define USB_TOKEN_OUT 0xe1 /* host -> device */ /* specific usb messages, also sent in the 'pid' parameter */ -#define USB_MSG_ATTACH 0x100 -#define USB_MSG_DETACH 0x101 #define USB_MSG_RESET 0x102 #define USB_RET_NODEV (-1) From d28f4e2d86317f7ef7398651e8a1d493e4bf8c88 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 6 Jan 2012 15:23:10 +0100 Subject: [PATCH 05/28] usb: kill USB_MSG_RESET The USB subsystem pipes internal reset notifications through usb_handle_packet() with a special magic PID. This indirection is a pretty pointless excercise as it ends up being handled by usb_generic_handle_packet anyway. Replace the USB_MSG_RESET with a usb_device_reset() function which can be called directly. Also rename the existing usb_reset() function to usb_port_reset() to avoid confusion. Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 4 ++-- hw/usb-hub.c | 2 +- hw/usb-musb.c | 2 +- hw/usb-ohci.c | 4 ++-- hw/usb-uhci.c | 10 +++------- hw/usb-xhci.c | 4 +--- hw/usb.c | 24 +++++++++++++----------- hw/usb.h | 6 ++---- 8 files changed, 25 insertions(+), 31 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index cdd74154a0..487214d751 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -888,7 +888,7 @@ static void ehci_reset(void *opaque) } if (devs[i] && devs[i]->attached) { usb_attach(&s->ports[i]); - usb_send_msg(devs[i], USB_MSG_RESET); + usb_device_reset(devs[i]); } } ehci_queues_rip_all(s); @@ -987,7 +987,7 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { trace_usb_ehci_port_reset(port, 0); if (dev && dev->attached) { - usb_reset(&s->ports[port]); + usb_port_reset(&s->ports[port]); *portsc &= ~PORTSC_CSC; } diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 956b020eed..0f3b2dda0e 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -305,7 +305,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p, break; case PORT_RESET: if (dev && dev->attached) { - usb_send_msg(dev, USB_MSG_RESET); + usb_device_reset(dev); port->wPortChange |= PORT_STAT_C_RESET; /* set enable bit */ port->wPortStatus |= PORT_STAT_ENABLE; diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 4f528d25e5..c2753c984c 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -1310,7 +1310,7 @@ static void musb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) s->power = (value & 0xef) | (s->power & 0x10); /* MGC_M_POWER_RESET is also read-only in Peripheral Mode */ if ((value & MGC_M_POWER_RESET) && s->port.dev) { - usb_send_msg(s->port.dev, USB_MSG_RESET); + usb_device_reset(s->port.dev); /* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set. */ if ((value & MGC_M_POWER_HSENAB) && s->port.dev->speed == USB_SPEED_HIGH) diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 425030f141..70f0f08a43 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -449,7 +449,7 @@ static void ohci_reset(void *opaque) port = &ohci->rhport[i]; port->ctrl = 0; if (port->port.dev && port->port.dev->attached) { - usb_reset(&port->port); + usb_port_reset(&port->port); } } if (ohci->async_td) { @@ -1435,7 +1435,7 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) { DPRINTF("usb-ohci: port %d: RESET\n", portnum); - usb_send_msg(port->port.dev, USB_MSG_RESET); + usb_device_reset(port->port.dev); port->ctrl &= ~OHCI_PORT_PRS; /* ??? Should this also set OHCI_PORT_PESC. */ port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC; diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index e0c7dbb71a..407e0f3a5a 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -342,7 +342,7 @@ static void uhci_reset(void *opaque) port = &s->ports[i]; port->ctrl = 0x0080; if (port->port.dev && port->port.dev->attached) { - usb_reset(&port->port); + usb_port_reset(&port->port); } } @@ -440,16 +440,12 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) } if (val & UHCI_CMD_GRESET) { UHCIPort *port; - USBDevice *dev; int i; /* send reset on the USB bus */ for(i = 0; i < NB_PORTS; i++) { port = &s->ports[i]; - dev = port->port.dev; - if (dev && dev->attached) { - usb_send_msg(dev, USB_MSG_RESET); - } + usb_device_reset(port->port.dev); } uhci_reset(s); return; @@ -491,7 +487,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) /* port reset */ if ( (val & UHCI_PORT_RESET) && !(port->ctrl & UHCI_PORT_RESET) ) { - usb_send_msg(dev, USB_MSG_RESET); + usb_device_reset(dev); } } port->ctrl &= UHCI_PORT_READ_ONLY; diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index 37e887c431..7028338a28 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -2346,9 +2346,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) /* write-1-to-start bits */ if (val & PORTSC_PR) { DPRINTF("xhci: port %d reset\n", port); - if (xhci->ports[port].port.dev) { - usb_send_msg(xhci->ports[port].port.dev, USB_MSG_RESET); - } + usb_device_reset(xhci->ports[port].port.dev); portsc |= PORTSC_PRC | PORTSC_PED; } xhci->ports[port].portsc = portsc; diff --git a/hw/usb.c b/hw/usb.c index 800dd835de..c8e6be4bc5 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -49,14 +49,25 @@ void usb_detach(USBPort *port) dev->state = USB_STATE_NOTATTACHED; } -void usb_reset(USBPort *port) +void usb_port_reset(USBPort *port) { USBDevice *dev = port->dev; assert(dev != NULL); usb_detach(port); usb_attach(port); - usb_send_msg(dev, USB_MSG_RESET); + usb_device_reset(dev); +} + +void usb_device_reset(USBDevice *dev) +{ + if (dev == NULL || !dev->attached) { + return; + } + dev->remote_wakeup = 0; + dev->addr = 0; + dev->state = USB_STATE_DEFAULT; + usb_device_handle_reset(dev); } void usb_wakeup(USBDevice *dev) @@ -218,15 +229,6 @@ static int do_token_out(USBDevice *s, USBPacket *p) */ int usb_generic_handle_packet(USBDevice *s, USBPacket *p) { - switch(p->pid) { - case USB_MSG_RESET: - s->remote_wakeup = 0; - s->addr = 0; - s->state = USB_STATE_DEFAULT; - usb_device_handle_reset(s); - return 0; - } - /* Rest of the PIDs must match our address */ if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) return USB_RET_NODEV; diff --git a/hw/usb.h b/hw/usb.h index 0bd909818a..b56e812d2c 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -39,9 +39,6 @@ #define USB_TOKEN_IN 0x69 /* device -> host */ #define USB_TOKEN_OUT 0xe1 /* host -> device */ -/* specific usb messages, also sent in the 'pid' parameter */ -#define USB_MSG_RESET 0x102 - #define USB_RET_NODEV (-1) #define USB_RET_NAK (-2) #define USB_RET_STALL (-3) @@ -352,7 +349,8 @@ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep); void usb_attach(USBPort *port); void usb_detach(USBPort *port); -void usb_reset(USBPort *port); +void usb_port_reset(USBPort *port); +void usb_device_reset(USBDevice *dev); void usb_wakeup(USBDevice *dev); int usb_generic_handle_packet(USBDevice *s, USBPacket *p); void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); From 70fc20d4dc7c8990a508411db1ee441ae9ee5034 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 6 Jan 2012 15:31:38 +0100 Subject: [PATCH 06/28] usb: kill usb_send_msg No users left. Zap it. Signed-off-by: Gerd Hoffmann --- hw/usb.c | 13 ------------- hw/usb.h | 1 - 2 files changed, 14 deletions(-) diff --git a/hw/usb.c b/hw/usb.c index c8e6be4bc5..0c261644ec 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -295,19 +295,6 @@ int set_usb_string(uint8_t *buf, const char *str) return q - buf; } -/* Send an internal message to a USB device. */ -void usb_send_msg(USBDevice *dev, int msg) -{ - USBPacket p; - int ret; - - memset(&p, 0, sizeof(p)); - p.pid = msg; - ret = usb_handle_packet(dev, &p); - /* This _must_ be synchronous */ - assert(ret != USB_RET_ASYNC); -} - /* Hand over a packet to a device for processing. Return value USB_RET_ASYNC indicates the processing isn't finished yet, the driver will call usb_packet_complete() when done processing it. */ diff --git a/hw/usb.h b/hw/usb.h index b56e812d2c..61271768bc 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -355,7 +355,6 @@ void usb_wakeup(USBDevice *dev); int usb_generic_handle_packet(USBDevice *s, USBPacket *p); void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); -void usb_send_msg(USBDevice *dev, int msg); /* usb-linux.c */ USBDevice *usb_host_device_open(const char *devname); From 73796fe6228ae1e294b4946c6c90337141cacc42 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 10 Jan 2012 16:59:28 +0100 Subject: [PATCH 07/28] usb: add usb_find_device() Add usb_find_device(). This function will check whenever a device with a specific address is connected to the specified port. Usually this will just check state and address of the device hooked up to the port, but in case of a hub it will ask the hub to check all hub ports for a matching device. This patch doesn't put the code into use yet, see the following patches for details. The master plan is to separate device lookup and packet processing. Right now the usb code simply walks all devices, calls usb_handle_packet() on each until one accepts the packet (by returning something different that USB_RET_NODEV). I want to have a device lookup first, then call usb_handle_packet() once, for the device which actually processes the packet. Signed-off-by: Gerd Hoffmann --- hw/usb-bus.c | 9 +++++++++ hw/usb.c | 13 +++++++++++++ hw/usb.h | 10 ++++++++++ 3 files changed, 32 insertions(+) diff --git a/hw/usb-bus.c b/hw/usb-bus.c index b753834584..5c05ed5806 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -74,6 +74,15 @@ static int usb_device_init(USBDevice *dev) return 0; } +USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr) +{ + USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); + if (klass->find_device) { + return klass->find_device(dev, addr); + } + return NULL; +} + static void usb_device_handle_destroy(USBDevice *dev) { USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); diff --git a/hw/usb.c b/hw/usb.c index 0c261644ec..bacdc81bfc 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -295,6 +295,19 @@ int set_usb_string(uint8_t *buf, const char *str) return q - buf; } +USBDevice *usb_find_device(USBPort *port, uint8_t addr) +{ + USBDevice *dev = port->dev; + + if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) { + return NULL; + } + if (dev->addr == addr) { + return dev; + } + return usb_device_find_device(dev, addr); +} + /* Hand over a packet to a device for processing. Return value USB_RET_ASYNC indicates the processing isn't finished yet, the driver will call usb_packet_complete() when done processing it. */ diff --git a/hw/usb.h b/hw/usb.h index 61271768bc..1beb4b32a9 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -228,6 +228,12 @@ typedef struct USBDeviceClass { int (*init)(USBDevice *dev); + /* + * Walk (enabled) downstream ports, check for a matching device. + * Only hubs implement this. + */ + USBDevice *(*find_device)(USBDevice *dev, uint8_t addr); + /* * Process USB packet. * Called by the HC (Host Controller). @@ -332,6 +338,8 @@ void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes); void usb_packet_skip(USBPacket *p, size_t bytes); void usb_packet_cleanup(USBPacket *p); +USBDevice *usb_find_device(USBPort *port, uint8_t addr); + int usb_handle_packet(USBDevice *dev, USBPacket *p); void usb_packet_complete(USBDevice *dev, USBPacket *p); void usb_cancel_packet(USBPacket * p); @@ -446,6 +454,8 @@ extern const VMStateDescription vmstate_usb_device; .offset = vmstate_offset_value(_state, _field, USBDevice), \ } +USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr); + int usb_device_handle_packet(USBDevice *dev, USBPacket *p); void usb_device_cancel_packet(USBDevice *dev, USBPacket *p); From 06c750888c57bdc374feaa097f6cbc720210b834 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 10 Jan 2012 17:08:13 +0100 Subject: [PATCH 08/28] usb-hub: implement find_device Implement the find_device callback for the usb hub. It'll loop over all ports, calling usb_find_device for all enabled ports until it finds a matching device. Signed-off-by: Gerd Hoffmann --- hw/usb-hub.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 0f3b2dda0e..bd7641c4bf 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -220,6 +220,26 @@ static void usb_hub_complete(USBPort *port, USBPacket *packet) s->dev.port->ops->complete(s->dev.port, packet); } +static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr) +{ + USBHubState *s = DO_UPCAST(USBHubState, dev, dev); + USBHubPort *port; + USBDevice *downstream; + int i; + + for (i = 0; i < NUM_PORTS; i++) { + port = &s->ports[i]; + if (!(port->wPortStatus & PORT_STAT_ENABLE)) { + continue; + } + downstream = usb_find_device(&port->port, addr); + if (downstream != NULL) { + return downstream; + } + } + return NULL; +} + static void usb_hub_handle_reset(USBDevice *dev) { USBHubState *s = DO_UPCAST(USBHubState, dev, dev); @@ -541,6 +561,7 @@ static void usb_hub_class_initfn(ObjectClass *klass, void *data) uc->init = usb_hub_initfn; uc->product_desc = "QEMU USB Hub"; uc->usb_desc = &desc_hub; + uc->find_device = usb_hub_find_device; uc->handle_packet = usb_hub_handle_packet; uc->handle_reset = usb_hub_handle_reset; uc->handle_control = usb_hub_handle_control; From 98861f512bbd8789bd7935a75316d3278be23a0a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 10 Jan 2012 17:33:02 +0100 Subject: [PATCH 09/28] usb: handle dev == NULL in usb_handle_packet() Allow passing in a NULL pointer, return USB_RET_NODEV in that case. Removes the burden to to a NULL pointer check from the callers. Signed-off-by: Gerd Hoffmann --- hw/usb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/usb.c b/hw/usb.c index bacdc81bfc..9976f81bd7 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -315,6 +315,10 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) { int ret; + if (dev == NULL) { + return USB_RET_NODEV; + } + assert(p->owner == NULL); ret = usb_device_handle_packet(dev, p); if (ret == USB_RET_ASYNC) { From 461700c1dc006600d7a955ee67184ced6084fd19 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 10 Jan 2012 17:34:24 +0100 Subject: [PATCH 10/28] usb-uhci: switch to usb_find_device() Switch over UHCI to use the new usb_find_device() function for device lookup. Signed-off-by: Gerd Hoffmann --- hw/usb-uhci.c | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 407e0f3a5a..a1f597ab83 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -94,15 +94,6 @@ static const char *pid2str(int pid) #define DPRINTF(...) #endif -#ifdef DEBUG_DUMP_DATA -static void dump_data(USBPacket *p, int ret) -{ - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret); -} -#else -static void dump_data(USBPacket *p, int ret) {} -#endif - typedef struct UHCIState UHCIState; /* @@ -643,30 +634,22 @@ static void uhci_wakeup(USBPort *port1) } } -static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) +static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr) { - int i, ret; + USBDevice *dev; + int i; - DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n", - pid2str(p->pid), p->devaddr, p->devep, p->iov.size); - if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP) - dump_data(p, 0); - - ret = USB_RET_NODEV; - for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) { + for (i = 0; i < NB_PORTS; i++) { UHCIPort *port = &s->ports[i]; - USBDevice *dev = port->port.dev; - - if (dev && dev->attached && (port->ctrl & UHCI_PORT_EN)) { - ret = usb_handle_packet(dev, p); + if (!(port->ctrl & UHCI_PORT_EN)) { + continue; + } + dev = usb_find_device(&port->port, addr); + if (dev != NULL) { + return dev; } } - - DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size); - if (p->pid == USB_TOKEN_IN && ret > 0) - dump_data(p, ret); - - return ret; + return NULL; } static void uhci_async_complete(USBPort *port, USBPacket *packet); @@ -830,13 +813,15 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in switch(pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: - len = uhci_broadcast_packet(s, &async->packet); + len = usb_handle_packet(uhci_find_device(s, async->packet.devaddr), + &async->packet); if (len >= 0) len = max_len; break; case USB_TOKEN_IN: - len = uhci_broadcast_packet(s, &async->packet); + len = usb_handle_packet(uhci_find_device(s, async->packet.devaddr), + &async->packet); break; default: From 828143c621d8fd1030b5fd07d7ed340b82a5b763 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 10 Jan 2012 17:46:15 +0100 Subject: [PATCH 11/28] usb-ehci: switch to usb_find_device() Switch over EHCI to use the new usb_find_device() function for device lookup. Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 69 ++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 487214d751..8f7b3c4e79 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -850,6 +850,26 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[], return 0; } +static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr) +{ + USBDevice *dev; + USBPort *port; + int i; + + for (i = 0; i < NB_PORTS; i++) { + port = &ehci->ports[i]; + if (!(ehci->portsc[i] & PORTSC_PED)) { + DPRINTF("Port %d not enabled\n", i); + continue; + } + dev = usb_find_device(port, addr); + if (dev != NULL) { + return dev; + } + } + return NULL; +} + /* 4.1 host controller initialization */ static void ehci_reset(void *opaque) { @@ -1336,10 +1356,8 @@ err: static int ehci_execute(EHCIQueue *q) { - USBPort *port; USBDevice *dev; int ret; - int i; int endp; int devadr; @@ -1375,27 +1393,12 @@ static int ehci_execute(EHCIQueue *q) usb_packet_map(&q->packet, &q->sgl); // TO-DO: associating device with ehci port - for(i = 0; i < NB_PORTS; i++) { - port = &q->ehci->ports[i]; - dev = port->dev; - - if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) { - DPRINTF("Port %d, no exec, not connected(%08X)\n", - i, q->ehci->portsc[i]); - continue; - } - - ret = usb_handle_packet(dev, &q->packet); - - DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " - "(total %d) endp %x ret %d\n", - q->qhaddr, q->qh.next, q->qtdaddr, q->pid, - q->packet.iov.size, q->tbytes, endp, ret); - - if (ret != USB_RET_NODEV) { - break; - } - } + dev = ehci_find_device(q->ehci, q->packet.devaddr); + ret = usb_handle_packet(dev, &q->packet); + DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " + "(total %d) endp %x ret %d\n", + q->qhaddr, q->qh.next, q->qtdaddr, q->pid, + q->packet.iov.size, q->tbytes, endp, ret); if (ret > BUFF_SIZE) { fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n"); @@ -1411,10 +1414,9 @@ static int ehci_execute(EHCIQueue *q) static int ehci_process_itd(EHCIState *ehci, EHCIitd *itd) { - USBPort *port; USBDevice *dev; int ret; - uint32_t i, j, len, pid, dir, devaddr, endp; + uint32_t i, len, pid, dir, devaddr, endp; uint32_t pg, off, ptr1, ptr2, max, mult; dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); @@ -1455,21 +1457,8 @@ static int ehci_process_itd(EHCIState *ehci, usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); usb_packet_map(&ehci->ipacket, &ehci->isgl); - ret = USB_RET_NODEV; - for (j = 0; j < NB_PORTS; j++) { - port = &ehci->ports[j]; - dev = port->dev; - - if (!(ehci->portsc[j] &(PORTSC_CONNECT))) { - continue; - } - - ret = usb_handle_packet(dev, &ehci->ipacket); - - if (ret != USB_RET_NODEV) { - break; - } - } + dev = ehci_find_device(ehci, ehci->ipacket.devaddr); + ret = usb_handle_packet(dev, &ehci->ipacket); usb_packet_unmap(&ehci->ipacket); qemu_sglist_destroy(&ehci->isgl); From 993048bb6bf6b74bbf6b7dae8e7c2db523cfcecf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 10 Jan 2012 17:56:17 +0100 Subject: [PATCH 12/28] usb-ohci: switch to usb_find_device() Switch over OHCI to use the new usb_find_device() function for device lookup. Signed-off-by: Gerd Hoffmann --- hw/usb-ohci.c | 73 ++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 70f0f08a43..ba854c73de 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -408,6 +408,23 @@ static void ohci_child_detach(USBPort *port1, USBDevice *child) ohci_async_cancel_device(s, child); } +static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr) +{ + USBDevice *dev; + int i; + + for (i = 0; i < ohci->num_ports; i++) { + if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) { + continue; + } + dev = usb_find_device(&ohci->rhport[i].port, addr); + if (dev != NULL) { + return dev; + } + } + return NULL; +} + /* Reset the controller */ static void ohci_reset(void *opaque) { @@ -779,20 +796,12 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, if (completion) { ret = ohci->usb_packet.result; } else { - ret = USB_RET_NODEV; - for (i = 0; i < ohci->num_ports; i++) { - dev = ohci->rhport[i].port.dev; - if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) - continue; - usb_packet_setup(&ohci->usb_packet, pid, - OHCI_BM(ed->flags, ED_FA), - OHCI_BM(ed->flags, ED_EN)); - usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); - ret = usb_handle_packet(dev, &ohci->usb_packet); - if (ret != USB_RET_NODEV) - break; - } - + usb_packet_setup(&ohci->usb_packet, pid, + OHCI_BM(ed->flags, ED_FA), + OHCI_BM(ed->flags, ED_EN)); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); + dev = ohci_find_device(ohci, ohci->usb_packet.devaddr); + ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret == USB_RET_ASYNC) { return 1; } @@ -972,31 +981,23 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) ohci->async_td = 0; ohci->async_complete = 0; } else { - ret = USB_RET_NODEV; - for (i = 0; i < ohci->num_ports; i++) { - dev = ohci->rhport[i].port.dev; - if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) - continue; - - if (ohci->async_td) { - /* ??? The hardware should allow one active packet per - endpoint. We only allow one active packet per controller. - This should be sufficient as long as devices respond in a - timely manner. - */ + if (ohci->async_td) { + /* ??? The hardware should allow one active packet per + endpoint. We only allow one active packet per controller. + This should be sufficient as long as devices respond in a + timely manner. + */ #ifdef DEBUG_PACKET - DPRINTF("Too many pending packets\n"); + DPRINTF("Too many pending packets\n"); #endif - return 1; - } - usb_packet_setup(&ohci->usb_packet, pid, - OHCI_BM(ed->flags, ED_FA), - OHCI_BM(ed->flags, ED_EN)); - usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); - ret = usb_handle_packet(dev, &ohci->usb_packet); - if (ret != USB_RET_NODEV) - break; + return 1; } + usb_packet_setup(&ohci->usb_packet, pid, + OHCI_BM(ed->flags, ED_FA), + OHCI_BM(ed->flags, ED_EN)); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); + dev = ohci_find_device(ohci, ohci->usb_packet.devaddr); + ret = usb_handle_packet(dev, &ohci->usb_packet); #ifdef DEBUG_PACKET DPRINTF("ret=%d\n", ret); #endif From 87e043f17e9b3781b3c29c02ff32abc23ac15708 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 10 Jan 2012 17:59:33 +0100 Subject: [PATCH 13/28] usb-musb: switch to usb_find_device() Switch over musb to use the new usb_find_device() function for device lookup. Signed-off-by: Gerd Hoffmann --- hw/usb-musb.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/usb-musb.c b/hw/usb-musb.c index c2753c984c..ecac631228 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -605,6 +605,7 @@ static int musb_timeout(int ttype, int speed, int val) static void musb_packet(MUSBState *s, MUSBEndPoint *ep, int epnum, int pid, int len, USBCallback cb, int dir) { + USBDevice *dev; int ret; int idx = epnum && dir; int ttype; @@ -628,10 +629,8 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->packey[dir].ep = ep; ep->packey[dir].dir = dir; - if (s->port.dev) - ret = usb_handle_packet(s->port.dev, &ep->packey[dir].p); - else - ret = USB_RET_NODEV; + dev = usb_find_device(&s->port, ep->packey[dir].p.devaddr); + ret = usb_handle_packet(dev, &ep->packey[dir].p); if (ret == USB_RET_ASYNC) { ep->status[dir] = len; From e74495e3ade61ceabc5a00713900d8295e4042f4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 10 Jan 2012 18:05:22 +0100 Subject: [PATCH 14/28] usb-xhci: switch to usb_find_device() Switch over xHCI to use the new usb_find_device() function for device lookup. Signed-off-by: Gerd Hoffmann --- hw/usb-xhci.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index 7028338a28..aa236c9a3f 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -1385,6 +1385,14 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret) return 0; } +static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) +{ + if (!(port->portsc & PORTSC_PED)) { + return NULL; + } + return usb_find_device(&port->port, addr); +} + static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) { XHCITRB *trb_setup, *trb_status; @@ -1444,7 +1452,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xfer->data_length = wLength; port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; - dev = port->port.dev; + dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); if (!dev) { fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid, xhci->slots[xfer->slotid-1].port); @@ -1502,7 +1510,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx } port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; - dev = port->port.dev; + dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); if (!dev) { fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid, xhci->slots[xfer->slotid-1].port); From 7f74a56b1416a759c1da0a280e99242662f350c5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 11 Jan 2012 11:16:20 +0100 Subject: [PATCH 15/28] usb: kill handle_packet callback All drivers except usb-hub use usb_generic_handle_packet. The only reason the usb hub has its own function is that it used to be called with packets which are intended for downstream devices. With the new, separate device lookup step this doesn't happen any more, so the need for a different handle_packet callback is gone. So we can kill the handle_packet callback and just call usb_generic_handle_packet directly. The special hub handling in usb_handle_packet() can go away for the same reason. Signed-off-by: Gerd Hoffmann --- hw/usb-audio.c | 1 - hw/usb-bt.c | 1 - hw/usb-bus.c | 9 --------- hw/usb-ccid.c | 1 - hw/usb-hid.c | 1 - hw/usb-hub.c | 39 --------------------------------------- hw/usb-msd.c | 1 - hw/usb-net.c | 1 - hw/usb-serial.c | 2 -- hw/usb-wacom.c | 1 - hw/usb.c | 15 ++++----------- hw/usb.h | 12 ------------ usb-bsd.c | 1 - usb-linux.c | 1 - usb-redir.c | 1 - 15 files changed, 4 insertions(+), 83 deletions(-) diff --git a/hw/usb-audio.c b/hw/usb-audio.c index cd589b718a..a4ea0b0fe2 100644 --- a/hw/usb-audio.c +++ b/hw/usb-audio.c @@ -691,7 +691,6 @@ static void usb_audio_class_init(ObjectClass *klass, void *data) k->product_desc = "QEMU USB Audio Interface"; k->usb_desc = &desc_audio; k->init = usb_audio_initfn; - k->handle_packet = usb_generic_handle_packet; k->handle_reset = usb_audio_handle_reset; k->handle_control = usb_audio_handle_control; k->handle_data = usb_audio_handle_data; diff --git a/hw/usb-bt.c b/hw/usb-bt.c index 90c3b0e0eb..58b247ef41 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -535,7 +535,6 @@ static void usb_bt_class_initfn(ObjectClass *klass, void *data) uc->init = usb_bt_initfn; uc->product_desc = "QEMU BT dongle"; uc->usb_desc = &desc_bluetooth; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_bt_handle_reset; uc->handle_control = usb_bt_handle_control; uc->handle_data = usb_bt_handle_data; diff --git a/hw/usb-bus.c b/hw/usb-bus.c index 5c05ed5806..e907d0db79 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -91,15 +91,6 @@ static void usb_device_handle_destroy(USBDevice *dev) } } -int usb_device_handle_packet(USBDevice *dev, USBPacket *p) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_packet) { - return klass->handle_packet(dev, p); - } - return -ENOSYS; -} - void usb_device_cancel_packet(USBDevice *dev, USBPacket *p) { USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index 881da3002e..b3bcfeb4ac 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -1320,7 +1320,6 @@ static void ccid_class_initfn(ObjectClass *klass, void *data) uc->init = ccid_initfn; uc->product_desc = "QEMU USB CCID"; uc->usb_desc = &desc_ccid; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = ccid_handle_reset; uc->handle_control = ccid_handle_control; uc->handle_data = ccid_handle_data; diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 3c4e45da70..c6489800fd 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -557,7 +557,6 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_hid_handle_reset; uc->handle_control = usb_hid_handle_control; uc->handle_data = usb_hid_handle_data; diff --git a/hw/usb-hub.c b/hw/usb-hub.c index bd7641c4bf..22562565c7 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -455,44 +455,6 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) return ret; } -static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p) -{ - USBHubPort *port; - USBDevice *dev; - int i, ret; - - for(i = 0; i < NUM_PORTS; i++) { - port = &s->ports[i]; - dev = port->port.dev; - if (dev && dev->attached && (port->wPortStatus & PORT_STAT_ENABLE)) { - ret = usb_handle_packet(dev, p); - if (ret != USB_RET_NODEV) { - return ret; - } - } - } - return USB_RET_NODEV; -} - -static int usb_hub_handle_packet(USBDevice *dev, USBPacket *p) -{ - USBHubState *s = (USBHubState *)dev; - -#if defined(DEBUG) && 0 - printf("usb_hub: pid=0x%x\n", pid); -#endif - if (dev->state == USB_STATE_DEFAULT && - dev->addr != 0 && - p->devaddr != dev->addr && - (p->pid == USB_TOKEN_SETUP || - p->pid == USB_TOKEN_OUT || - p->pid == USB_TOKEN_IN)) { - /* broadcast the packet to the devices */ - return usb_hub_broadcast_packet(s, p); - } - return usb_generic_handle_packet(dev, p); -} - static void usb_hub_handle_destroy(USBDevice *dev) { USBHubState *s = (USBHubState *)dev; @@ -562,7 +524,6 @@ static void usb_hub_class_initfn(ObjectClass *klass, void *data) uc->product_desc = "QEMU USB Hub"; uc->usb_desc = &desc_hub; uc->find_device = usb_hub_find_device; - uc->handle_packet = usb_hub_handle_packet; uc->handle_reset = usb_hub_handle_reset; uc->handle_control = usb_hub_handle_control; uc->handle_data = usb_hub_handle_data; diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 6153376f3f..c34cad55fe 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -651,7 +651,6 @@ static void usb_msd_class_initfn(ObjectClass *klass, void *data) uc->init = usb_msd_initfn; uc->product_desc = "QEMU USB MSD"; uc->usb_desc = &desc; - uc->handle_packet = usb_generic_handle_packet; uc->cancel_packet = usb_msd_cancel_io; uc->handle_attach = usb_desc_attach; uc->handle_reset = usb_msd_handle_reset; diff --git a/hw/usb-net.c b/hw/usb-net.c index e2111413b4..f00e854756 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -1398,7 +1398,6 @@ static void usb_net_class_initfn(ObjectClass *klass, void *data) uc->init = usb_net_initfn; uc->product_desc = "QEMU USB Network Interface"; uc->usb_desc = &desc_net; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_net_handle_reset; uc->handle_control = usb_net_handle_control; uc->handle_data = usb_net_handle_data; diff --git a/hw/usb-serial.c b/hw/usb-serial.c index c2cb6d24e8..cf83cf21de 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -583,7 +583,6 @@ static void usb_serial_class_initfn(ObjectClass *klass, void *data) uc->init = usb_serial_initfn; uc->product_desc = "QEMU USB Serial"; uc->usb_desc = &desc_serial; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_serial_handle_reset; uc->handle_control = usb_serial_handle_control; uc->handle_data = usb_serial_handle_data; @@ -612,7 +611,6 @@ static void usb_braille_class_initfn(ObjectClass *klass, void *data) uc->init = usb_serial_initfn; uc->product_desc = "QEMU USB Braille"; uc->usb_desc = &desc_braille; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_serial_handle_reset; uc->handle_control = usb_serial_handle_control; uc->handle_data = usb_serial_handle_data; diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index 14de14d5f7..46b8010f63 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -357,7 +357,6 @@ static void usb_wacom_class_init(ObjectClass *klass, void *data) uc->product_desc = "QEMU PenPartner Tablet"; uc->usb_desc = &desc_wacom; uc->init = usb_wacom_initfn; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_wacom_handle_reset; uc->handle_control = usb_wacom_handle_control; uc->handle_data = usb_wacom_handle_data; diff --git a/hw/usb.c b/hw/usb.c index 9976f81bd7..638a339840 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -227,7 +227,7 @@ static int do_token_out(USBDevice *s, USBPacket *p) * * Returns length of the transaction or one of the USB_RET_XXX codes. */ -int usb_generic_handle_packet(USBDevice *s, USBPacket *p) +static int usb_generic_handle_packet(USBDevice *s, USBPacket *p) { /* Rest of the PIDs must match our address */ if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) @@ -318,18 +318,12 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) if (dev == NULL) { return USB_RET_NODEV; } + assert(dev->addr == p->devaddr); assert(p->owner == NULL); - ret = usb_device_handle_packet(dev, p); + ret = usb_generic_handle_packet(dev, p); if (ret == USB_RET_ASYNC) { - if (p->owner == NULL) { - p->owner = usb_ep_get(dev, p->pid, p->devep); - } else { - /* We'll end up here when usb_handle_packet is called - * recursively due to a hub being in the chain. Nothing - * to do. Leave p->owner pointing to the device, not the - * hub. */; - } + p->owner = usb_ep_get(dev, p->pid, p->devep); } return ret; } @@ -339,7 +333,6 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) handle_packet. */ void usb_packet_complete(USBDevice *dev, USBPacket *p) { - /* Note: p->owner != dev is possible in case dev is a hub */ assert(p->owner != NULL); p->owner = NULL; dev->port->ops->complete(dev->port, p); diff --git a/hw/usb.h b/hw/usb.h index 1beb4b32a9..294c33d85d 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -234,15 +234,6 @@ typedef struct USBDeviceClass { */ USBDevice *(*find_device)(USBDevice *dev, uint8_t addr); - /* - * Process USB packet. - * Called by the HC (Host Controller). - * - * Returns length of the transaction - * or one of the USB_RET_XXX codes. - */ - int (*handle_packet)(USBDevice *dev, USBPacket *p); - /* * Called when a packet is canceled. */ @@ -360,7 +351,6 @@ void usb_detach(USBPort *port); void usb_port_reset(USBPort *port); void usb_device_reset(USBDevice *dev); void usb_wakeup(USBDevice *dev); -int usb_generic_handle_packet(USBDevice *s, USBPacket *p); void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); @@ -456,8 +446,6 @@ extern const VMStateDescription vmstate_usb_device; USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr); -int usb_device_handle_packet(USBDevice *dev, USBPacket *p); - void usb_device_cancel_packet(USBDevice *dev, USBPacket *p); void usb_device_handle_attach(USBDevice *dev); diff --git a/usb-bsd.c b/usb-bsd.c index fc722b3cfa..ca9a1bdc61 100644 --- a/usb-bsd.c +++ b/usb-bsd.c @@ -403,7 +403,6 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data) uc->product_desc = "USB Host Device"; uc->init = usb_host_initfn; - uc->handle_packet = usb_generic_handle_packet; uc->handle_reset = usb_host_handle_reset; uc->handle_control = usb_host_handle_control; uc->handle_data = usb_host_handle_data; diff --git a/usb-linux.c b/usb-linux.c index e7fc9ecd5c..afb13c4d0f 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1419,7 +1419,6 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data) uc->init = usb_host_initfn; uc->product_desc = "USB Host Device"; - uc->handle_packet = usb_generic_handle_packet; uc->cancel_packet = usb_host_async_cancel; uc->handle_data = usb_host_handle_data; uc->handle_control = usb_host_handle_control; diff --git a/usb-redir.c b/usb-redir.c index 0a9295142c..d2769a8431 100644 --- a/usb-redir.c +++ b/usb-redir.c @@ -1424,7 +1424,6 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) uc->init = usbredir_initfn; uc->product_desc = "USB Redirection Device"; uc->handle_destroy = usbredir_handle_destroy; - uc->handle_packet = usb_generic_handle_packet; uc->cancel_packet = usbredir_cancel_packet; uc->handle_reset = usbredir_handle_reset; uc->handle_data = usbredir_handle_data; From 1977f93dacf60466cd23b562ae498446b77d3b48 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 11 Jan 2012 12:14:02 +0100 Subject: [PATCH 16/28] usb: fold usb_generic_handle_packet into usb_handle_packet There is no reason to have a separate usb_generic_handle_packet function any more, fold it into usb_handle_packet(). Also call the do_token_* functions which handle control transfer emulation for control pipe packets only. Signed-off-by: Gerd Hoffmann --- hw/usb.c | 58 ++++++++++++++++++++++++-------------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/hw/usb.c b/hw/usb.c index 638a339840..91107f938d 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -140,8 +140,7 @@ static int do_token_in(USBDevice *s, USBPacket *p) int request, value, index; int ret = 0; - if (p->devep != 0) - return usb_device_handle_data(s, p); + assert(p->devep == 0); request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; @@ -187,8 +186,7 @@ static int do_token_in(USBDevice *s, USBPacket *p) static int do_token_out(USBDevice *s, USBPacket *p) { - if (p->devep != 0) - return usb_device_handle_data(s, p); + assert(p->devep == 0); switch(s->setup_state) { case SETUP_STATE_ACK: @@ -221,33 +219,6 @@ static int do_token_out(USBDevice *s, USBPacket *p) } } -/* - * Generic packet handler. - * Called by the HC (host controller). - * - * Returns length of the transaction or one of the USB_RET_XXX codes. - */ -static int usb_generic_handle_packet(USBDevice *s, USBPacket *p) -{ - /* Rest of the PIDs must match our address */ - if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) - return USB_RET_NODEV; - - switch (p->pid) { - case USB_TOKEN_SETUP: - return do_token_setup(s, p); - - case USB_TOKEN_IN: - return do_token_in(s, p); - - case USB_TOKEN_OUT: - return do_token_out(s, p); - - default: - return USB_RET_STALL; - } -} - /* ctrl complete function for devices which use usb_generic_handle_packet and may return USB_RET_ASYNC from their handle_control callback. Device code which does this *must* call this function instead of the normal @@ -319,9 +290,30 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) return USB_RET_NODEV; } assert(dev->addr == p->devaddr); - + assert(dev->state == USB_STATE_DEFAULT); assert(p->owner == NULL); - ret = usb_generic_handle_packet(dev, p); + + if (p->devep == 0) { + /* control pipe */ + switch (p->pid) { + case USB_TOKEN_SETUP: + ret = do_token_setup(dev, p); + break; + case USB_TOKEN_IN: + ret = do_token_in(dev, p); + break; + case USB_TOKEN_OUT: + ret = do_token_out(dev, p); + break; + default: + ret = USB_RET_STALL; + break; + } + } else { + /* data pipe */ + ret = usb_device_handle_data(dev, p); + } + if (ret == USB_RET_ASYNC) { p->owner = usb_ep_get(dev, p->pid, p->devep); } From f53c398aa603cea135ee58fd15249aeff7b9c7ea Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 12 Jan 2012 12:51:48 +0100 Subject: [PATCH 17/28] usb: USBPacket: add status, rename owner -> ep Add enum to track the status of USBPackets, use that instead of the owner pointer to figure whenever a usb packet is currently in flight or not. Add some more packet status sanity checks. Also rename the USBEndpoint pointer from "owner" to "ep". Signed-off-by: Gerd Hoffmann --- hw/usb-ehci.c | 4 ++-- hw/usb-musb.c | 4 ++-- hw/usb-ohci.c | 4 ++-- hw/usb-uhci.c | 4 ++-- hw/usb.c | 18 +++++++++++------- hw/usb.h | 19 ++++++++++++++++--- 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 8f7b3c4e79..f0213adec2 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -715,8 +715,8 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev) EHCIQueue *q, *tmp; QTAILQ_FOREACH_SAFE(q, &ehci->queues, next, tmp) { - if (q->packet.owner == NULL || - q->packet.owner->dev != dev) { + if (!usb_packet_is_inflight(&q->packet) || + q->packet.ep->dev != dev) { continue; } ehci_free_queue(q); diff --git a/hw/usb-musb.c b/hw/usb-musb.c index ecac631228..f4e52f16cb 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -811,8 +811,8 @@ static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) for (ep = 0; ep < 16; ep++) { for (dir = 0; dir < 2; dir++) { - if (s->ep[ep].packey[dir].p.owner == NULL || - s->ep[ep].packey[dir].p.owner->dev != dev) { + if (!usb_packet_is_inflight(&s->ep[ep].packey[dir].p) || + s->ep[ep].packey[dir].p.ep->dev != dev) { continue; } usb_cancel_packet(&s->ep[ep].packey[dir].p); diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index ba854c73de..8a8f3bc852 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -1709,8 +1709,8 @@ static void ohci_mem_write(void *opaque, static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) { if (ohci->async_td && - ohci->usb_packet.owner != NULL && - ohci->usb_packet.owner->dev == dev) { + usb_packet_is_inflight(&ohci->usb_packet) && + ohci->usb_packet.ep->dev == dev) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index a1f597ab83..ef0814570a 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -236,8 +236,8 @@ static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev) UHCIAsync *curr, *n; QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - if (curr->packet.owner == NULL || - curr->packet.owner->dev != dev) { + if (!usb_packet_is_inflight(&curr->packet) || + curr->packet.ep->dev != dev) { continue; } uhci_async_unlink(s, curr); diff --git a/hw/usb.c b/hw/usb.c index 91107f938d..8584db0dec 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -291,7 +291,7 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) } assert(dev->addr == p->devaddr); assert(dev->state == USB_STATE_DEFAULT); - assert(p->owner == NULL); + assert(p->state == USB_PACKET_SETUP); if (p->devep == 0) { /* control pipe */ @@ -315,7 +315,8 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) } if (ret == USB_RET_ASYNC) { - p->owner = usb_ep_get(dev, p->pid, p->devep); + p->ep = usb_ep_get(dev, p->pid, p->devep); + p->state = USB_PACKET_ASYNC; } return ret; } @@ -325,8 +326,8 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) handle_packet. */ void usb_packet_complete(USBDevice *dev, USBPacket *p) { - assert(p->owner != NULL); - p->owner = NULL; + assert(p->state == USB_PACKET_ASYNC); + p->state = USB_PACKET_COMPLETE; dev->port->ops->complete(dev->port, p); } @@ -335,9 +336,9 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) completed. */ void usb_cancel_packet(USBPacket * p) { - assert(p->owner != NULL); - usb_device_cancel_packet(p->owner->dev, p); - p->owner = NULL; + assert(p->state == USB_PACKET_ASYNC); + p->state = USB_PACKET_CANCELED; + usb_device_cancel_packet(p->ep->dev, p); } @@ -348,6 +349,8 @@ void usb_packet_init(USBPacket *p) void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep) { + assert(!usb_packet_is_inflight(p)); + p->state = USB_PACKET_SETUP; p->pid = pid; p->devaddr = addr; p->devep = ep; @@ -391,6 +394,7 @@ void usb_packet_skip(USBPacket *p, size_t bytes) void usb_packet_cleanup(USBPacket *p) { + assert(!usb_packet_is_inflight(p)); qemu_iovec_destroy(&p->iov); } diff --git a/hw/usb.h b/hw/usb.h index 294c33d85d..4e878d350a 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -289,8 +289,7 @@ typedef struct USBPortOps { void (*wakeup)(USBPort *port); /* * Note that port->dev will be different then the device from which - * the packet originated when a hub is involved, if you want the orginating - * device use p->owner + * the packet originated when a hub is involved. */ void (*complete)(USBPort *port, USBPacket *p); } USBPortOps; @@ -309,15 +308,24 @@ struct USBPort { typedef void USBCallback(USBPacket * packet, void *opaque); /* Structure used to hold information about an active USB packet. */ +typedef enum USBPacketState { + USB_PACKET_UNDEFINED = 0, + USB_PACKET_SETUP, + USB_PACKET_ASYNC, + USB_PACKET_COMPLETE, + USB_PACKET_CANCELED, +} USBPacketState; + struct USBPacket { /* Data fields for use by the driver. */ int pid; uint8_t devaddr; uint8_t devep; + USBEndpoint *ep; QEMUIOVector iov; int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ - USBEndpoint *owner; + USBPacketState state; }; void usb_packet_init(USBPacket *p); @@ -329,6 +337,11 @@ void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes); void usb_packet_skip(USBPacket *p, size_t bytes); void usb_packet_cleanup(USBPacket *p); +static inline bool usb_packet_is_inflight(USBPacket *p) +{ + return p->state == USB_PACKET_ASYNC; +} + USBDevice *usb_find_device(USBPort *port, uint8_t addr); int usb_handle_packet(USBDevice *dev, USBPacket *p); From 63095ab54c1ce554b1fc825fc678394ccb129e5b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 12 Jan 2012 13:24:22 +0100 Subject: [PATCH 18/28] usb: add USBEndpoint->{nr,pid} Add a "nr" and "pid" fields to USBEndpoint so you can easily figure the endpoint number and direction of any given endpoint. Signed-off-by: Gerd Hoffmann --- hw/usb.c | 5 +++++ hw/usb.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/hw/usb.c b/hw/usb.c index 8584db0dec..8bd1222ccd 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -402,10 +402,15 @@ void usb_ep_init(USBDevice *dev) { int ep; + dev->ep_ctl.nr = 0; dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; dev->ep_ctl.ifnum = 0; dev->ep_ctl.dev = dev; for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { + dev->ep_in[ep].nr = ep + 1; + dev->ep_out[ep].nr = ep + 1; + dev->ep_in[ep].pid = USB_TOKEN_IN; + dev->ep_out[ep].pid = USB_TOKEN_OUT; dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID; dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID; dev->ep_in[ep].ifnum = 0; diff --git a/hw/usb.h b/hw/usb.h index 4e878d350a..1e629af682 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -171,6 +171,8 @@ struct USBDescString { #define USB_MAX_INTERFACES 16 struct USBEndpoint { + uint8_t nr; + uint8_t pid; uint8_t type; uint8_t ifnum; int max_packet_size; From 079d0b7f1eedcc634c371fe05b617fdc55c8b762 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 12 Jan 2012 13:23:01 +0100 Subject: [PATCH 19/28] usb: Set USBEndpoint in usb_packet_setup(). With the separation of the device lookup (via usb_find_device) and packet processing we can lookup device and endpoint before setting up the usb packet. So we can initialize USBPacket->ep early and keep it valid for the whole lifecycle of the USBPacket. Also the devaddr and devep fields are not needed any more. Signed-off-by: Gerd Hoffmann --- hw/usb-audio.c | 4 ++-- hw/usb-bt.c | 4 ++-- hw/usb-ccid.c | 2 +- hw/usb-ehci.c | 15 +++++++++------ hw/usb-hid.c | 2 +- hw/usb-hub.c | 2 +- hw/usb-msd.c | 2 +- hw/usb-musb.c | 7 ++++--- hw/usb-net.c | 6 +++--- hw/usb-ohci.c | 16 ++++++++-------- hw/usb-serial.c | 2 +- hw/usb-uhci.c | 13 +++++++------ hw/usb-wacom.c | 2 +- hw/usb-xhci.c | 27 +++++++++++++-------------- hw/usb.c | 21 ++++++++++++--------- hw/usb.h | 4 +--- usb-bsd.c | 2 +- usb-linux.c | 44 ++++++++++++++++++++++---------------------- usb-redir.c | 2 +- 19 files changed, 91 insertions(+), 86 deletions(-) diff --git a/hw/usb-audio.c b/hw/usb-audio.c index a4ea0b0fe2..c60aaff26d 100644 --- a/hw/usb-audio.c +++ b/hw/usb-audio.c @@ -607,7 +607,7 @@ static int usb_audio_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_OUT: - switch (p->devep) { + switch (p->ep->nr) { case 1: ret = usb_audio_handle_dataout(s, p); break; @@ -624,7 +624,7 @@ fail: if (ret == USB_RET_STALL && s->debug) { fprintf(stderr, "usb-audio: failed data transaction: " "pid 0x%x ep 0x%x len 0x%zx\n", - p->pid, p->devep, p->iov.size); + p->pid, p->ep->nr, p->iov.size); } return ret; } diff --git a/hw/usb-bt.c b/hw/usb-bt.c index 58b247ef41..ea6a5a0c01 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -423,7 +423,7 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_IN: - switch (p->devep & 0xf) { + switch (p->ep->nr) { case USB_EVT_EP: ret = usb_bt_fifo_dequeue(&s->evt, p); break; @@ -442,7 +442,7 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p) break; case USB_TOKEN_OUT: - switch (p->devep & 0xf) { + switch (p->ep->nr) { case USB_ACL_EP: usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, usb_bt_hci_acl_complete, p); diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index b3bcfeb4ac..8c0c7178b5 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -995,7 +995,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p) break; case USB_TOKEN_IN: - switch (p->devep & 0xf) { + switch (p->ep->nr) { case CCID_BULK_IN_EP: if (!p->iov.size) { ret = USB_RET_NAK; diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index f0213adec2..6c01ca93a9 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -1357,6 +1357,7 @@ err: static int ehci_execute(EHCIQueue *q) { USBDevice *dev; + USBEndpoint *ep; int ret; int endp; int devadr; @@ -1387,13 +1388,13 @@ static int ehci_execute(EHCIQueue *q) endp = get_field(q->qh.epchar, QH_EPCHAR_EP); devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); - ret = USB_RET_NODEV; + /* TODO: associating device with ehci port */ + dev = ehci_find_device(q->ehci, devadr); + ep = usb_ep_get(dev, q->pid, endp); - usb_packet_setup(&q->packet, q->pid, devadr, endp); + usb_packet_setup(&q->packet, q->pid, ep); usb_packet_map(&q->packet, &q->sgl); - // TO-DO: associating device with ehci port - dev = ehci_find_device(q->ehci, q->packet.devaddr); ret = usb_handle_packet(dev, &q->packet); DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " "(total %d) endp %x ret %d\n", @@ -1415,6 +1416,7 @@ static int ehci_process_itd(EHCIState *ehci, EHCIitd *itd) { USBDevice *dev; + USBEndpoint *ep; int ret; uint32_t i, len, pid, dir, devaddr, endp; uint32_t pg, off, ptr1, ptr2, max, mult; @@ -1454,10 +1456,11 @@ static int ehci_process_itd(EHCIState *ehci, pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT; - usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); + dev = ehci_find_device(ehci, devaddr); + ep = usb_ep_get(dev, pid, endp); + usb_packet_setup(&ehci->ipacket, pid, ep); usb_packet_map(&ehci->ipacket, &ehci->isgl); - dev = ehci_find_device(ehci, ehci->ipacket.devaddr); ret = usb_handle_packet(dev, &ehci->ipacket); usb_packet_unmap(&ehci->ipacket); diff --git a/hw/usb-hid.c b/hw/usb-hid.c index c6489800fd..4d00c2813d 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -463,7 +463,7 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_IN: - if (p->devep == 1) { + if (p->ep->nr == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); if (!hid_has_events(hs) && (!hs->idle || hs->next_idle_clock - curtime > 0)) { diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 22562565c7..940e211d13 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -416,7 +416,7 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) switch(p->pid) { case USB_TOKEN_IN: - if (p->devep == 1) { + if (p->ep->nr == 1) { USBHubPort *port; unsigned int status; uint8_t buf[4]; diff --git a/hw/usb-msd.c b/hw/usb-msd.c index c34cad55fe..aac1181fe8 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -341,7 +341,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) uint32_t tag; int ret = 0; struct usb_msd_cbw cbw; - uint8_t devep = p->devep; + uint8_t devep = p->ep->nr; switch (p->pid) { case USB_TOKEN_OUT: diff --git a/hw/usb-musb.c b/hw/usb-musb.c index f4e52f16cb..820907a9a9 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -606,6 +606,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, int epnum, int pid, int len, USBCallback cb, int dir) { USBDevice *dev; + USBEndpoint *uep; int ret; int idx = epnum && dir; int ttype; @@ -623,13 +624,13 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->delayed_cb[dir] = cb; /* A wild guess on the FADDR semantics... */ - usb_packet_setup(&ep->packey[dir].p, pid, ep->faddr[idx], - ep->type[idx] & 0xf); + dev = usb_find_device(&s->port, ep->faddr[idx]); + uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); + usb_packet_setup(&ep->packey[dir].p, pid, uep); usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; ep->packey[dir].dir = dir; - dev = usb_find_device(&s->port, ep->packey[dir].p.devaddr); ret = usb_handle_packet(dev, &ep->packey[dir].p); if (ret == USB_RET_ASYNC) { diff --git a/hw/usb-net.c b/hw/usb-net.c index f00e854756..72c9185ba1 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -1210,7 +1210,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) switch(p->pid) { case USB_TOKEN_IN: - switch (p->devep) { + switch (p->ep->nr) { case 1: ret = usb_net_handle_statusin(s, p); break; @@ -1225,7 +1225,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) break; case USB_TOKEN_OUT: - switch (p->devep) { + switch (p->ep->nr) { case 2: ret = usb_net_handle_dataout(s, p); break; @@ -1243,7 +1243,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) if (ret == USB_RET_STALL) fprintf(stderr, "usbnet: failed data transaction: " "pid 0x%x ep 0x%x len 0x%zx\n", - p->pid, p->devep, p->iov.size); + p->pid, p->ep->nr, p->iov.size); return ret; } diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 8a8f3bc852..ba7231f2df 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -657,6 +657,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, int ret; int i; USBDevice *dev; + USBEndpoint *ep; struct ohci_iso_td iso_td; uint32_t addr; uint16_t starting_frame; @@ -796,11 +797,10 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, if (completion) { ret = ohci->usb_packet.result; } else { - usb_packet_setup(&ohci->usb_packet, pid, - OHCI_BM(ed->flags, ED_FA), - OHCI_BM(ed->flags, ED_EN)); + dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); + usb_packet_setup(&ohci->usb_packet, pid, ep); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); - dev = ohci_find_device(ohci, ohci->usb_packet.devaddr); ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret == USB_RET_ASYNC) { return 1; @@ -889,6 +889,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) int ret; int i; USBDevice *dev; + USBEndpoint *ep; struct ohci_td td; uint32_t addr; int flag_r; @@ -992,11 +993,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) #endif return 1; } - usb_packet_setup(&ohci->usb_packet, pid, - OHCI_BM(ed->flags, ED_FA), - OHCI_BM(ed->flags, ED_EN)); + dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); + usb_packet_setup(&ohci->usb_packet, pid, ep); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); - dev = ohci_find_device(ohci, ohci->usb_packet.devaddr); ret = usb_handle_packet(dev, &ohci->usb_packet); #ifdef DEBUG_PACKET DPRINTF("ret=%d\n", ret); diff --git a/hw/usb-serial.c b/hw/usb-serial.c index cf83cf21de..1cfb551a05 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -353,7 +353,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) { USBSerialState *s = (USBSerialState *)dev; int i, ret = 0; - uint8_t devep = p->devep; + uint8_t devep = p->ep->nr; struct iovec *iov; uint8_t header[2]; int first_len, len; diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index ef0814570a..ab64be6047 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -761,6 +761,8 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in int len = 0, max_len; uint8_t pid, isoc; uint32_t token; + USBDevice *dev; + USBEndpoint *ep; /* Is active ? */ if (!(td->ctrl & TD_CTRL_ACTIVE)) @@ -805,23 +807,22 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f, - (td->token >> 15) & 0xf); + dev = uhci_find_device(s, (td->token >> 8) & 0x7f); + ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); + usb_packet_setup(&async->packet, pid, ep); qemu_sglist_add(&async->sgl, td->buffer, max_len); usb_packet_map(&async->packet, &async->sgl); switch(pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: - len = usb_handle_packet(uhci_find_device(s, async->packet.devaddr), - &async->packet); + len = usb_handle_packet(dev, &async->packet); if (len >= 0) len = max_len; break; case USB_TOKEN_IN: - len = usb_handle_packet(uhci_find_device(s, async->packet.devaddr), - &async->packet); + len = usb_handle_packet(dev, &async->packet); break; default: diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index 46b8010f63..73ff241d47 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -306,7 +306,7 @@ static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_IN: - if (p->devep == 1) { + if (p->ep->nr == 1) { if (!(s->changed || s->idle)) return USB_RET_NAK; s->changed = 0; diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index aa236c9a3f..b274b806df 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -1336,15 +1336,17 @@ static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer, } #endif -static int xhci_setup_packet(XHCITransfer *xfer, XHCIPort *port, int ep) +static int xhci_setup_packet(XHCITransfer *xfer, XHCIPort *port, USBDevice *dev) { - usb_packet_setup(&xfer->packet, - xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT, - xfer->xhci->slots[xfer->slotid-1].devaddr, - ep & 0x7f); + USBEndpoint *ep; + int dir; + + dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT; + ep = usb_ep_get(dev, dir, xfer->epid >> 1); + usb_packet_setup(&xfer->packet, dir, ep); usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", - xfer->packet.pid, xfer->packet.devaddr, xfer->packet.devep); + xfer->packet.pid, dev->addr, ep->nr); return 0; } @@ -1462,7 +1464,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xfer->in_xfer = bmRequestType & USB_DIR_IN; xfer->iso_xfer = false; - xhci_setup_packet(xfer, port, 0); + xhci_setup_packet(xfer, port, dev); if (!xfer->in_xfer) { xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0); } @@ -1484,12 +1486,8 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx int ret; DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); - uint8_t ep = xfer->epid>>1; xfer->in_xfer = epctx->type>>2; - if (xfer->in_xfer) { - ep |= 0x80; - } if (xfer->data && xfer->data_alloced < xfer->data_length) { xfer->data_alloced = 0; @@ -1517,7 +1515,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx return -1; } - xhci_setup_packet(xfer, port, ep); + xhci_setup_packet(xfer, port, dev); switch(epctx->type) { case ET_INTR_OUT: @@ -1530,8 +1528,9 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx FIXME(); break; default: - fprintf(stderr, "xhci: unknown or unhandled EP type %d (ep %02x)\n", - epctx->type, ep); + fprintf(stderr, "xhci: unknown or unhandled EP " + "(type %d, in %d, ep %02x)\n", + epctx->type, xfer->in_xfer, xfer->epid); return -1; } diff --git a/hw/usb.c b/hw/usb.c index 8bd1222ccd..240f24b25e 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -140,7 +140,7 @@ static int do_token_in(USBDevice *s, USBPacket *p) int request, value, index; int ret = 0; - assert(p->devep == 0); + assert(p->ep->nr == 0); request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; @@ -186,7 +186,7 @@ static int do_token_in(USBDevice *s, USBPacket *p) static int do_token_out(USBDevice *s, USBPacket *p) { - assert(p->devep == 0); + assert(p->ep->nr == 0); switch(s->setup_state) { case SETUP_STATE_ACK: @@ -289,11 +289,11 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) if (dev == NULL) { return USB_RET_NODEV; } - assert(dev->addr == p->devaddr); + assert(dev == p->ep->dev); assert(dev->state == USB_STATE_DEFAULT); assert(p->state == USB_PACKET_SETUP); - if (p->devep == 0) { + if (p->ep->nr == 0) { /* control pipe */ switch (p->pid) { case USB_TOKEN_SETUP: @@ -315,7 +315,6 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) } if (ret == USB_RET_ASYNC) { - p->ep = usb_ep_get(dev, p->pid, p->devep); p->state = USB_PACKET_ASYNC; } return ret; @@ -347,13 +346,12 @@ void usb_packet_init(USBPacket *p) qemu_iovec_init(&p->iov, 1); } -void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep) +void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep) { assert(!usb_packet_is_inflight(p)); p->state = USB_PACKET_SETUP; p->pid = pid; - p->devaddr = addr; - p->devep = ep; + p->ep = ep; p->result = 0; qemu_iovec_reset(&p->iov); } @@ -464,7 +462,12 @@ void usb_ep_dump(USBDevice *dev) struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep) { - struct USBEndpoint *eps = pid == USB_TOKEN_IN ? dev->ep_in : dev->ep_out; + struct USBEndpoint *eps; + + if (dev == NULL) { + return NULL; + } + eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out; if (ep == 0) { return &dev->ep_ctl; } diff --git a/hw/usb.h b/hw/usb.h index 1e629af682..a80fe8f7f7 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -321,8 +321,6 @@ typedef enum USBPacketState { struct USBPacket { /* Data fields for use by the driver. */ int pid; - uint8_t devaddr; - uint8_t devep; USBEndpoint *ep; QEMUIOVector iov; int result; /* transfer length or USB_RET_* status code */ @@ -331,7 +329,7 @@ struct USBPacket { }; void usb_packet_init(USBPacket *p); -void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep); +void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep); void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); int usb_packet_map(USBPacket *p, QEMUSGList *sgl); void usb_packet_unmap(USBPacket *p); diff --git a/usb-bsd.c b/usb-bsd.c index ca9a1bdc61..e4c01d3c02 100644 --- a/usb-bsd.c +++ b/usb-bsd.c @@ -214,7 +214,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) int ret, fd, mode; int one = 1, shortpacket = 0, timeout = 50; sigset_t new_mask, old_mask; - uint8_t devep = p->devep; + uint8_t devep = p->ep->nr; /* protect data transfers from SIGALRM signal */ sigemptyset(&new_mask); diff --git a/usb-linux.c b/usb-linux.c index afb13c4d0f..cf074ec212 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -137,7 +137,7 @@ static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p) [USB_ENDPOINT_XFER_BULK] = USBDEVFS_URB_TYPE_BULK, [USB_ENDPOINT_XFER_INT] = USBDEVFS_URB_TYPE_INTERRUPT, }; - uint8_t type = usb_ep_get_type(&s->dev, p->pid, p->devep); + uint8_t type = p->ep->type; assert(type < ARRAY_SIZE(usbfs)); return usbfs[type]; } @@ -360,7 +360,7 @@ static void async_complete(void *opaque) break; case -EPIPE: - set_halt(s, p->pid, p->devep); + set_halt(s, p->pid, p->ep->nr); p->result = USB_RET_STALL; break; @@ -733,16 +733,16 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) int i, j, ret, max_packet_size, offset, len = 0; uint8_t *buf; - max_packet_size = usb_ep_get_max_packet_size(&s->dev, p->pid, p->devep); + max_packet_size = p->ep->max_packet_size; if (max_packet_size == 0) return USB_RET_NAK; - aurb = get_iso_urb(s, p->pid, p->devep); + aurb = get_iso_urb(s, p->pid, p->ep->nr); if (!aurb) { - aurb = usb_host_alloc_iso(s, p->pid, p->devep); + aurb = usb_host_alloc_iso(s, p->pid, p->ep->nr); } - i = get_iso_urb_idx(s, p->pid, p->devep); + i = get_iso_urb_idx(s, p->pid, p->ep->nr); j = aurb[i].iso_frame_idx; if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) { if (in) { @@ -769,7 +769,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) } } else { len = p->iov.size; - offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->devep); + offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr); /* Check the frame fits */ if (len > max_packet_size) { @@ -781,27 +781,27 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) usb_packet_copy(p, aurb[i].urb.buffer + offset, len); aurb[i].urb.iso_frame_desc[j].length = len; offset += len; - set_iso_buffer_used(s, p->pid, p->devep, offset); + set_iso_buffer_used(s, p->pid, p->ep->nr, offset); /* Start the stream once we have buffered enough data */ - if (!is_iso_started(s, p->pid, p->devep) && i == 1 && j == 8) { - set_iso_started(s, p->pid, p->devep); + if (!is_iso_started(s, p->pid, p->ep->nr) && i == 1 && j == 8) { + set_iso_started(s, p->pid, p->ep->nr); } } aurb[i].iso_frame_idx++; if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { i = (i + 1) % s->iso_urb_count; - set_iso_urb_idx(s, p->pid, p->devep, i); + set_iso_urb_idx(s, p->pid, p->ep->nr, i); } } else { if (in) { - set_iso_started(s, p->pid, p->devep); + set_iso_started(s, p->pid, p->ep->nr); } else { DPRINTF("hubs: iso out error no free buffer, dropping packet\n"); } } - if (is_iso_started(s, p->pid, p->devep)) { + if (is_iso_started(s, p->pid, p->ep->nr)) { /* (Re)-submit all fully consumed / filled urbs */ for (i = 0; i < s->iso_urb_count; i++) { if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { @@ -821,7 +821,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) break; } aurb[i].iso_frame_idx = -1; - change_iso_inflight(s, p->pid, p->devep, 1); + change_iso_inflight(s, p->pid, p->ep->nr, 1); } } } @@ -840,20 +840,20 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) trace_usb_host_req_data(s->bus_num, s->addr, p->pid == USB_TOKEN_IN, - p->devep, p->iov.size); + p->ep->nr, p->iov.size); - if (!is_valid(s, p->pid, p->devep)) { + if (!is_valid(s, p->pid, p->ep->nr)) { trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); return USB_RET_NAK; } if (p->pid == USB_TOKEN_IN) { - ep = p->devep | 0x80; + ep = p->ep->nr | 0x80; } else { - ep = p->devep; + ep = p->ep->nr; } - if (is_halted(s, p->pid, p->devep)) { + if (is_halted(s, p->pid, p->ep->nr)) { unsigned int arg = ep; ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg); if (ret < 0) { @@ -861,10 +861,10 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p) trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK); return USB_RET_NAK; } - clear_halt(s, p->pid, p->devep); + clear_halt(s, p->pid, p->ep->nr); } - if (is_isoc(s, p->pid, p->devep)) { + if (is_isoc(s, p->pid, p->ep->nr)) { return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); } @@ -1057,7 +1057,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, urb = &aurb->urb; urb->type = USBDEVFS_URB_TYPE_CONTROL; - urb->endpoint = p->devep; + urb->endpoint = p->ep->nr; urb->buffer = &dev->setup_buf; urb->buffer_length = length + 8; diff --git a/usb-redir.c b/usb-redir.c index d2769a8431..15e2b12aac 100644 --- a/usb-redir.c +++ b/usb-redir.c @@ -610,7 +610,7 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p) USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); uint8_t ep; - ep = p->devep; + ep = p->ep->nr; if (p->pid == USB_TOKEN_IN) { ep |= USB_DIR_IN; } From db4be873d312576c6971da15a38e056017a406b8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 12 Jan 2012 14:26:13 +0100 Subject: [PATCH 20/28] usb: maintain async packet list per endpoint Maintain a list of async packets per endpoint. With the current code the list will never receive more than a single item. I think you can guess what the future plan is though ;) Signed-off-by: Gerd Hoffmann --- hw/usb.c | 127 +++++++++++++++++++++++++++++++++++++++++++------------ hw/usb.h | 9 +++- 2 files changed, 108 insertions(+), 28 deletions(-) diff --git a/hw/usb.c b/hw/usb.c index 240f24b25e..712bdd49de 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -279,6 +279,28 @@ USBDevice *usb_find_device(USBPort *port, uint8_t addr) return usb_device_find_device(dev, addr); } +static int usb_process_one(USBPacket *p) +{ + USBDevice *dev = p->ep->dev; + + if (p->ep->nr == 0) { + /* control pipe */ + switch (p->pid) { + case USB_TOKEN_SETUP: + return do_token_setup(dev, p); + case USB_TOKEN_IN: + return do_token_in(dev, p); + case USB_TOKEN_OUT: + return do_token_out(dev, p); + default: + return USB_RET_STALL; + } + } else { + /* data pipe */ + return usb_device_handle_data(dev, p); + } +} + /* Hand over a packet to a device for processing. Return value USB_RET_ASYNC indicates the processing isn't finished yet, the driver will call usb_packet_complete() when done processing it. */ @@ -292,30 +314,21 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) assert(dev == p->ep->dev); assert(dev->state == USB_STATE_DEFAULT); assert(p->state == USB_PACKET_SETUP); + assert(p->ep != NULL); - if (p->ep->nr == 0) { - /* control pipe */ - switch (p->pid) { - case USB_TOKEN_SETUP: - ret = do_token_setup(dev, p); - break; - case USB_TOKEN_IN: - ret = do_token_in(dev, p); - break; - case USB_TOKEN_OUT: - ret = do_token_out(dev, p); - break; - default: - ret = USB_RET_STALL; - break; + if (QTAILQ_EMPTY(&p->ep->queue)) { + ret = usb_process_one(p); + if (ret == USB_RET_ASYNC) { + usb_packet_set_state(p, USB_PACKET_ASYNC); + QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); + } else { + p->result = ret; + usb_packet_set_state(p, USB_PACKET_COMPLETE); } } else { - /* data pipe */ - ret = usb_device_handle_data(dev, p); - } - - if (ret == USB_RET_ASYNC) { - p->state = USB_PACKET_ASYNC; + ret = USB_RET_ASYNC; + usb_packet_set_state(p, USB_PACKET_QUEUED); + QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); } return ret; } @@ -325,9 +338,28 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) handle_packet. */ void usb_packet_complete(USBDevice *dev, USBPacket *p) { + USBEndpoint *ep = p->ep; + int ret; + assert(p->state == USB_PACKET_ASYNC); - p->state = USB_PACKET_COMPLETE; + assert(QTAILQ_FIRST(&ep->queue) == p); + usb_packet_set_state(p, USB_PACKET_COMPLETE); + QTAILQ_REMOVE(&ep->queue, p, queue); dev->port->ops->complete(dev->port, p); + + while (!QTAILQ_EMPTY(&ep->queue)) { + p = QTAILQ_FIRST(&ep->queue); + assert(p->state == USB_PACKET_QUEUED); + ret = usb_process_one(p); + if (ret == USB_RET_ASYNC) { + usb_packet_set_state(p, USB_PACKET_ASYNC); + break; + } + p->result = ret; + usb_packet_set_state(p, USB_PACKET_COMPLETE); + QTAILQ_REMOVE(&ep->queue, p, queue); + dev->port->ops->complete(dev->port, p); + } } /* Cancel an active packet. The packed must have been deferred by @@ -335,9 +367,13 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) completed. */ void usb_cancel_packet(USBPacket * p) { - assert(p->state == USB_PACKET_ASYNC); - p->state = USB_PACKET_CANCELED; - usb_device_cancel_packet(p->ep->dev, p); + bool callback = (p->state == USB_PACKET_ASYNC); + assert(usb_packet_is_inflight(p)); + usb_packet_set_state(p, USB_PACKET_CANCELED); + QTAILQ_REMOVE(&p->ep->queue, p, queue); + if (callback) { + usb_device_cancel_packet(p->ep->dev, p); + } } @@ -346,14 +382,50 @@ void usb_packet_init(USBPacket *p) qemu_iovec_init(&p->iov, 1); } +void usb_packet_set_state(USBPacket *p, USBPacketState state) +{ +#ifdef DEBUG + static const char *name[] = { + [USB_PACKET_UNDEFINED] = "undef", + [USB_PACKET_SETUP] = "setup", + [USB_PACKET_QUEUED] = "queued", + [USB_PACKET_ASYNC] = "async", + [USB_PACKET_COMPLETE] = "complete", + [USB_PACKET_CANCELED] = "canceled", + }; + static const char *rets[] = { + [-USB_RET_NODEV] = "NODEV", + [-USB_RET_NAK] = "NAK", + [-USB_RET_STALL] = "STALL", + [-USB_RET_BABBLE] = "BABBLE", + [-USB_RET_ASYNC] = "ASYNC", + }; + char add[16] = ""; + + if (state == USB_PACKET_COMPLETE) { + if (p->result < 0) { + snprintf(add, sizeof(add), " - %s", rets[-p->result]); + } else { + snprintf(add, sizeof(add), " - %d", p->result); + } + } + fprintf(stderr, "bus %s, port %s, dev %d, ep %d: packet %p: %s -> %s%s\n", + p->ep->dev->qdev.parent_bus->name, + p->ep->dev->port->path, + p->ep->dev->addr, p->ep->nr, + p, name[p->state], name[state], add); +#endif + p->state = state; +} + void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep) { assert(!usb_packet_is_inflight(p)); - p->state = USB_PACKET_SETUP; p->pid = pid; p->ep = ep; p->result = 0; qemu_iovec_reset(&p->iov); + usb_packet_set_state(p, USB_PACKET_SETUP); } void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) @@ -404,6 +476,7 @@ void usb_ep_init(USBDevice *dev) dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; dev->ep_ctl.ifnum = 0; dev->ep_ctl.dev = dev; + QTAILQ_INIT(&dev->ep_ctl.queue); for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { dev->ep_in[ep].nr = ep + 1; dev->ep_out[ep].nr = ep + 1; @@ -415,6 +488,8 @@ void usb_ep_init(USBDevice *dev) dev->ep_out[ep].ifnum = 0; dev->ep_in[ep].dev = dev; dev->ep_out[ep].dev = dev; + QTAILQ_INIT(&dev->ep_in[ep].queue); + QTAILQ_INIT(&dev->ep_out[ep].queue); } } diff --git a/hw/usb.h b/hw/usb.h index a80fe8f7f7..6545b69bb9 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -177,6 +177,7 @@ struct USBEndpoint { uint8_t ifnum; int max_packet_size; USBDevice *dev; + QTAILQ_HEAD(, USBPacket) queue; }; /* definition of a USB device */ @@ -309,15 +310,16 @@ struct USBPort { typedef void USBCallback(USBPacket * packet, void *opaque); -/* Structure used to hold information about an active USB packet. */ typedef enum USBPacketState { USB_PACKET_UNDEFINED = 0, USB_PACKET_SETUP, + USB_PACKET_QUEUED, USB_PACKET_ASYNC, USB_PACKET_COMPLETE, USB_PACKET_CANCELED, } USBPacketState; +/* Structure used to hold information about an active USB packet. */ struct USBPacket { /* Data fields for use by the driver. */ int pid; @@ -326,9 +328,11 @@ struct USBPacket { int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ USBPacketState state; + QTAILQ_ENTRY(USBPacket) queue; }; void usb_packet_init(USBPacket *p); +void usb_packet_set_state(USBPacket *p, USBPacketState state); void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep); void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); int usb_packet_map(USBPacket *p, QEMUSGList *sgl); @@ -339,7 +343,8 @@ void usb_packet_cleanup(USBPacket *p); static inline bool usb_packet_is_inflight(USBPacket *p) { - return p->state == USB_PACKET_ASYNC; + return (p->state == USB_PACKET_QUEUED || + p->state == USB_PACKET_ASYNC); } USBDevice *usb_find_device(USBPort *port, uint8_t addr); From 7567b51fbe92e1300a672eaddd413c4a7e807d90 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 17 Jan 2012 13:25:46 +0100 Subject: [PATCH 21/28] usb: pass USBEndpoint to usb_wakeup Devices must specify which endpoint has data to transfer now. The plan is to use the usb_wakeup() not only for remove wakeup support, but for "data ready" signaling in general, so we can move away from constant polling to event driven usb device emulation. Signed-off-by: Gerd Hoffmann --- hw/usb-ccid.c | 4 +++- hw/usb-hid.c | 4 +++- hw/usb-hub.c | 8 +++++--- hw/usb.c | 4 +++- hw/usb.h | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index 8c0c7178b5..fda2d92a06 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -267,6 +267,7 @@ typedef struct CCIDBus { */ typedef struct USBCCIDState { USBDevice dev; + USBEndpoint *intr; CCIDBus bus; CCIDCardState *card; BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */ @@ -839,7 +840,7 @@ static void ccid_on_slot_change(USBCCIDState *s, bool full) s->bmSlotICCState |= SLOT_0_CHANGED_MASK; } s->notify_slot_change = true; - usb_wakeup(&s->dev); + usb_wakeup(s->intr); } static void ccid_write_data_block_error( @@ -1190,6 +1191,7 @@ static int ccid_initfn(USBDevice *dev) usb_desc_init(dev); qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL); + s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP); s->bus.qbus.allow_hotplug = 1; s->card = NULL; s->migration_state = MIGRATION_NONE; diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 4d00c2813d..53353d3c20 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -44,6 +44,7 @@ typedef struct USBHIDState { USBDevice dev; + USBEndpoint *intr; HIDState hid; } USBHIDState; @@ -360,7 +361,7 @@ static void usb_hid_changed(HIDState *hs) { USBHIDState *us = container_of(hs, USBHIDState, hid); - usb_wakeup(&us->dev); + usb_wakeup(us->intr); } static void usb_hid_handle_reset(USBDevice *dev) @@ -501,6 +502,7 @@ static int usb_hid_initfn(USBDevice *dev, int kind) USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); usb_desc_init(dev); + us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); hid_init(&us->hid, kind, usb_hid_changed); return 0; } diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 940e211d13..6d1ce065d9 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -37,6 +37,7 @@ typedef struct USBHubPort { typedef struct USBHubState { USBDevice dev; + USBEndpoint *intr; USBHubPort ports[NUM_PORTS]; } USBHubState; @@ -163,7 +164,7 @@ static void usb_hub_attach(USBPort *port1) } else { port->wPortStatus &= ~PORT_STAT_LOW_SPEED; } - usb_wakeup(&s->dev); + usb_wakeup(s->intr); } static void usb_hub_detach(USBPort *port1) @@ -171,7 +172,7 @@ static void usb_hub_detach(USBPort *port1) USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; - usb_wakeup(&s->dev); + usb_wakeup(s->intr); /* Let upstream know the device on this port is gone */ s->dev.port->ops->child_detach(s->dev.port, port1->dev); @@ -199,7 +200,7 @@ static void usb_hub_wakeup(USBPort *port1) if (port->wPortStatus & PORT_STAT_SUSPEND) { port->wPortChange |= PORT_STAT_C_SUSPEND; - usb_wakeup(&s->dev); + usb_wakeup(s->intr); } } @@ -481,6 +482,7 @@ static int usb_hub_initfn(USBDevice *dev) int i; usb_desc_init(dev); + s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); for (i = 0; i < NUM_PORTS; i++) { port = &s->ports[i]; usb_register_port(usb_bus_from_device(dev), diff --git a/hw/usb.c b/hw/usb.c index 712bdd49de..05725474e3 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -70,8 +70,10 @@ void usb_device_reset(USBDevice *dev) usb_device_handle_reset(dev); } -void usb_wakeup(USBDevice *dev) +void usb_wakeup(USBEndpoint *ep) { + USBDevice *dev = ep->dev; + if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { dev->port->ops->wakeup(dev->port); } diff --git a/hw/usb.h b/hw/usb.h index 6545b69bb9..b2caa77345 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -368,7 +368,7 @@ void usb_attach(USBPort *port); void usb_detach(USBPort *port); void usb_port_reset(USBPort *port); void usb_device_reset(USBDevice *dev); -void usb_wakeup(USBDevice *dev); +void usb_wakeup(USBEndpoint *ep); void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); From 37f32f0fdd3ed589266d159abb69f3a1bae20ec5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 20 Jan 2012 13:29:53 +0100 Subject: [PATCH 22/28] usb: add USBBusOps->wakeup_endpoint Add usb bus op which is called whenever a usb endpoint becomes ready, so the host adapter emulation can react on that event. Signed-off-by: Gerd Hoffmann --- hw/usb.c | 4 ++++ hw/usb.h | 1 + 2 files changed, 5 insertions(+) diff --git a/hw/usb.c b/hw/usb.c index 05725474e3..e5b8f335dc 100644 --- a/hw/usb.c +++ b/hw/usb.c @@ -73,10 +73,14 @@ void usb_device_reset(USBDevice *dev) void usb_wakeup(USBEndpoint *ep) { USBDevice *dev = ep->dev; + USBBus *bus = usb_bus_from_device(dev); if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { dev->port->ops->wakeup(dev->port); } + if (bus->ops->wakeup_endpoint) { + bus->ops->wakeup_endpoint(bus, ep); + } } /**********************/ diff --git a/hw/usb.h b/hw/usb.h index b2caa77345..4470ea890a 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -425,6 +425,7 @@ struct USBBus { struct USBBusOps { int (*register_companion)(USBBus *bus, USBPort *ports[], uint32_t portcount, uint32_t firstport); + void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep); }; void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); From 606352b735eb98ce0b1f3786e0b4b313bd2e1a35 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 13 Jan 2012 17:06:18 +0100 Subject: [PATCH 23/28] xhci: signal low- and fullspeed support Signed-off-by: Gerd Hoffmann --- hw/usb-xhci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index b274b806df..27bdc2b5ee 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -2672,7 +2672,10 @@ static void usb_xhci_init(XHCIState *xhci, DeviceState *dev) for (i = 0; i < MAXPORTS; i++) { memset(&xhci->ports[i], 0, sizeof(xhci->ports[i])); usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i, - &xhci_port_ops, USB_SPEED_MASK_HIGH); + &xhci_port_ops, + USB_SPEED_MASK_LOW | + USB_SPEED_MASK_FULL | + USB_SPEED_MASK_HIGH); } for (i = 0; i < MAXSLOTS; i++) { xhci->slots[i].enabled = 0; From f10de44e7eb8304406d42a2a7242887c08282c4a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 17 Jan 2012 11:21:06 +0100 Subject: [PATCH 24/28] xhci: add trb type name lookup support. When logging TRBs add a the type name for more readable debug output. Signed-off-by: Gerd Hoffmann --- hw/usb-xhci.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index 27bdc2b5ee..5e618f0db2 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -420,6 +420,60 @@ typedef struct XHCIEvRingSeg { uint32_t rsvd; } XHCIEvRingSeg; +#ifdef DEBUG_XHCI +static const char *TRBType_names[] = { + [TRB_RESERVED] = "TRB_RESERVED", + [TR_NORMAL] = "TR_NORMAL", + [TR_SETUP] = "TR_SETUP", + [TR_DATA] = "TR_DATA", + [TR_STATUS] = "TR_STATUS", + [TR_ISOCH] = "TR_ISOCH", + [TR_LINK] = "TR_LINK", + [TR_EVDATA] = "TR_EVDATA", + [TR_NOOP] = "TR_NOOP", + [CR_ENABLE_SLOT] = "CR_ENABLE_SLOT", + [CR_DISABLE_SLOT] = "CR_DISABLE_SLOT", + [CR_ADDRESS_DEVICE] = "CR_ADDRESS_DEVICE", + [CR_CONFIGURE_ENDPOINT] = "CR_CONFIGURE_ENDPOINT", + [CR_EVALUATE_CONTEXT] = "CR_EVALUATE_CONTEXT", + [CR_RESET_ENDPOINT] = "CR_RESET_ENDPOINT", + [CR_STOP_ENDPOINT] = "CR_STOP_ENDPOINT", + [CR_SET_TR_DEQUEUE] = "CR_SET_TR_DEQUEUE", + [CR_RESET_DEVICE] = "CR_RESET_DEVICE", + [CR_FORCE_EVENT] = "CR_FORCE_EVENT", + [CR_NEGOTIATE_BW] = "CR_NEGOTIATE_BW", + [CR_SET_LATENCY_TOLERANCE] = "CR_SET_LATENCY_TOLERANCE", + [CR_GET_PORT_BANDWIDTH] = "CR_GET_PORT_BANDWIDTH", + [CR_FORCE_HEADER] = "CR_FORCE_HEADER", + [CR_NOOP] = "CR_NOOP", + [ER_TRANSFER] = "ER_TRANSFER", + [ER_COMMAND_COMPLETE] = "ER_COMMAND_COMPLETE", + [ER_PORT_STATUS_CHANGE] = "ER_PORT_STATUS_CHANGE", + [ER_BANDWIDTH_REQUEST] = "ER_BANDWIDTH_REQUEST", + [ER_DOORBELL] = "ER_DOORBELL", + [ER_HOST_CONTROLLER] = "ER_HOST_CONTROLLER", + [ER_DEVICE_NOTIFICATION] = "ER_DEVICE_NOTIFICATION", + [ER_MFINDEX_WRAP] = "ER_MFINDEX_WRAP", + [CR_VENDOR_VIA_CHALLENGE_RESPONSE] = "CR_VENDOR_VIA_CHALLENGE_RESPONSE", + [CR_VENDOR_NEC_FIRMWARE_REVISION] = "CR_VENDOR_NEC_FIRMWARE_REVISION", + [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE", +}; + +static const char *lookup_name(uint32_t index, const char **list, uint32_t llen) +{ + if (index >= llen || list[index] == NULL) { + return "???"; + } + return list[index]; +} + +static const char *trb_name(XHCITRB *trb) +{ + return lookup_name(TRB_TYPE(*trb), TRBType_names, + ARRAY_SIZE(TRBType_names)); +} +#endif + static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); @@ -487,8 +541,9 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event) } ev_trb.control = cpu_to_le32(ev_trb.control); - DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x\n", - xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control); + DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x %s\n", + xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control, + trb_name(&ev_trb)); addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx; cpu_physical_memory_write(addr, (uint8_t *) &ev_trb, TRB_SIZE); @@ -649,8 +704,9 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb, le32_to_cpus(&trb->control); DPRINTF("xhci: TRB fetched [" TARGET_FMT_plx "]: " - "%016" PRIx64 " %08x %08x\n", - ring->dequeue, trb->parameter, trb->status, trb->control); + "%016" PRIx64 " %08x %08x %s\n", + ring->dequeue, trb->parameter, trb->status, trb->control, + trb_name(trb)); if ((trb->control & TRB_C) != ring->ccs) { return 0; From 3c4866e07ed17c7d64c2fd30738f68276ca353d9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 17 Jan 2012 13:25:13 +0100 Subject: [PATCH 25/28] xhci: stop on errors When some error happened we'll have to stop processing the endpoint. Signed-off-by: Gerd Hoffmann --- hw/usb-xhci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index 5e618f0db2..7682126e93 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -1721,10 +1721,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid } } + if (epctx->state == EP_HALTED) { + DPRINTF("xhci: ep halted, stopping schedule\n"); + break; + } + /* * Qemu usb can't handle multiple in-flight xfers. - * Also xfers might be finished here already, - * possibly with an error. Stop here for now. + * Stop here for now. */ break; } From b819d7169a8f026c7b9174b7cbce8ab50fc7908a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 20 Jan 2012 15:25:16 +0100 Subject: [PATCH 26/28] xhci: kill port arg from xhci_setup_packet Unused argument, remove it. Signed-off-by: Gerd Hoffmann --- hw/usb-xhci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index 7682126e93..65214af9c4 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -1392,7 +1392,7 @@ static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer, } #endif -static int xhci_setup_packet(XHCITransfer *xfer, XHCIPort *port, USBDevice *dev) +static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) { USBEndpoint *ep; int dir; @@ -1520,7 +1520,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xfer->in_xfer = bmRequestType & USB_DIR_IN; xfer->iso_xfer = false; - xhci_setup_packet(xfer, port, dev); + xhci_setup_packet(xfer, dev); if (!xfer->in_xfer) { xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0); } @@ -1571,7 +1571,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx return -1; } - xhci_setup_packet(xfer, port, dev); + xhci_setup_packet(xfer, dev); switch(epctx->type) { case ET_INTR_OUT: From 8c735e431d384663137a709c8207f4ada3146205 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 20 Jan 2012 17:09:58 +0100 Subject: [PATCH 27/28] xhci: remote wakeup support Signed-off-by: Gerd Hoffmann --- hw/usb-xhci.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index 65214af9c4..3026edbcdd 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -2698,6 +2698,26 @@ static void xhci_detach(USBPort *usbport) xhci_update_port(xhci, port, 1); } +static void xhci_wakeup(USBPort *usbport) +{ + XHCIState *xhci = usbport->opaque; + XHCIPort *port = &xhci->ports[usbport->index]; + int nr = port->port.index + 1; + XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24}; + uint32_t pls; + + pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; + if (pls != 3) { + return; + } + port->portsc |= 0xf << PORTSC_PLS_SHIFT; + if (port->portsc & PORTSC_PLC) { + return; + } + port->portsc |= PORTSC_PLC; + xhci_event(xhci, &ev); +} + static void xhci_complete(USBPort *port, USBPacket *packet) { XHCITransfer *xfer = container_of(packet, XHCITransfer, packet); @@ -2714,6 +2734,7 @@ static void xhci_child_detach(USBPort *port, USBDevice *child) static USBPortOps xhci_port_ops = { .attach = xhci_attach, .detach = xhci_detach, + .wakeup = xhci_wakeup, .complete = xhci_complete, .child_detach = xhci_child_detach, }; From 7c605a23b2c7606f5f06b7d83d8927b1dc111478 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 20 Jan 2012 13:29:16 +0100 Subject: [PATCH 28/28] xhci: handle USB_RET_NAK Add a field to XHCITransfer to correctly keep track of NAK'ed usb packets. Retry transfers when the endpoint is kicked again. Implement wakeup_endpoint bus op so we can kick the endpoint when needed. With this patch applied the emulated hid devices are working correctly when hooked up to xhci. usb-tabled without polling, yay! Signed-off-by: Gerd Hoffmann --- hw/usb-xhci.c | 107 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 11 deletions(-) diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index 3026edbcdd..ac7b12bc68 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -307,7 +307,8 @@ typedef struct XHCIState XHCIState; typedef struct XHCITransfer { XHCIState *xhci; USBPacket packet; - bool running; + bool running_async; + bool running_retry; bool cancelled; bool complete; bool backgrounded; @@ -338,6 +339,7 @@ typedef struct XHCIEPContext { unsigned int next_xfer; unsigned int comp_xfer; XHCITransfer transfers[TD_QUEUE]; + XHCITransfer *retry; bool bg_running; bool bg_updating; unsigned int next_bg; @@ -915,12 +917,17 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, xferi = epctx->next_xfer; for (i = 0; i < TD_QUEUE; i++) { XHCITransfer *t = &epctx->transfers[xferi]; - if (t->running) { + if (t->running_async) { + usb_cancel_packet(&t->packet); + t->running_async = 0; t->cancelled = 1; - /* libusb_cancel_transfer(t->usbxfer) */ DPRINTF("xhci: cancelling transfer %d, waiting for it to complete...\n", i); killed++; } + if (t->running_retry) { + t->running_retry = 0; + epctx->retry = NULL; + } if (t->backgrounded) { t->backgrounded = 0; } @@ -941,9 +948,10 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, xferi = epctx->next_bg; for (i = 0; i < BG_XFERS; i++) { XHCITransfer *t = &epctx->bg_transfers[xferi]; - if (t->running) { + if (t->running_async) { + usb_cancel_packet(&t->packet); + t->running_async = 0; t->cancelled = 1; - /* libusb_cancel_transfer(t->usbxfer); */ DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i); killed++; } @@ -1409,12 +1417,20 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) static int xhci_complete_packet(XHCITransfer *xfer, int ret) { if (ret == USB_RET_ASYNC) { - xfer->running = 1; + xfer->running_async = 1; + xfer->running_retry = 0; + xfer->complete = 0; + xfer->cancelled = 0; + return 0; + } else if (ret == USB_RET_NAK) { + xfer->running_async = 0; + xfer->running_retry = 1; xfer->complete = 0; xfer->cancelled = 0; return 0; } else { - xfer->running = 0; + xfer->running_async = 0; + xfer->running_retry = 0; xfer->complete = 1; } @@ -1529,7 +1545,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) wValue, wIndex, wLength, xfer->data); xhci_complete_packet(xfer, ret); - if (!xfer->running) { + if (!xfer->running_async && !xfer->running_retry) { xhci_kick_ep(xhci, xfer->slotid, xfer->epid); } return 0; @@ -1596,7 +1612,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx ret = usb_handle_packet(dev, &xfer->packet); xhci_complete_packet(xfer, ret); - if (!xfer->running) { + if (!xfer->running_async && !xfer->running_retry) { xhci_kick_ep(xhci, xfer->slotid, xfer->epid); } return 0; @@ -1667,6 +1683,25 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid return; } + if (epctx->retry) { + /* retry nak'ed transfer */ + XHCITransfer *xfer = epctx->retry; + int result; + + DPRINTF("xhci: retry nack'ed transfer ...\n"); + assert(xfer->running_retry); + xhci_setup_packet(xfer, xfer->packet.ep->dev); + result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); + if (result == USB_RET_NAK) { + DPRINTF("xhci: ... xfer still nacked\n"); + return; + } + DPRINTF("xhci: ... result %d\n", result); + xhci_complete_packet(xfer, result); + assert(!xfer->running_retry); + epctx->retry = NULL; + } + if (epctx->state == EP_HALTED) { DPRINTF("xhci: ep halted, not running schedule\n"); return; @@ -1676,9 +1711,13 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid while (1) { XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer]; - if (xfer->running || xfer->backgrounded) { - DPRINTF("xhci: ep is busy\n"); + if (xfer->running_async || xfer->running_retry || xfer->backgrounded) { + DPRINTF("xhci: ep is busy (#%d,%d,%d,%d)\n", + epctx->next_xfer, xfer->running_async, + xfer->running_retry, xfer->backgrounded); break; + } else { + DPRINTF("xhci: ep: using #%d\n", epctx->next_xfer); } length = xhci_ring_chain_length(xhci, &epctx->ring); if (length < 0) { @@ -1725,6 +1764,11 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid DPRINTF("xhci: ep halted, stopping schedule\n"); break; } + if (xfer->running_retry) { + DPRINTF("xhci: xfer nacked, stopping schedule\n"); + epctx->retry = xfer; + break; + } /* * Qemu usb can't handle multiple in-flight xfers. @@ -2739,7 +2783,48 @@ static USBPortOps xhci_port_ops = { .child_detach = xhci_child_detach, }; +static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev) +{ + XHCISlot *slot; + int slotid; + + for (slotid = 1; slotid <= MAXSLOTS; slotid++) { + slot = &xhci->slots[slotid-1]; + if (slot->devaddr == dev->addr) { + return slotid; + } + } + return 0; +} + +static int xhci_find_epid(USBEndpoint *ep) +{ + if (ep->nr == 0) { + return 1; + } + if (ep->pid == USB_TOKEN_IN) { + return ep->nr * 2 + 1; + } else { + return ep->nr * 2; + } +} + +static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) +{ + XHCIState *xhci = container_of(bus, XHCIState, bus); + int slotid; + + DPRINTF("%s\n", __func__); + slotid = xhci_find_slotid(xhci, ep->dev); + if (slotid == 0 || !xhci->slots[slotid-1].enabled) { + DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr); + return; + } + xhci_kick_ep(xhci, slotid, xhci_find_epid(ep)); +} + static USBBusOps xhci_bus_ops = { + .wakeup_endpoint = xhci_wakeup_endpoint, }; static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)