diff options
Diffstat (limited to 'hw/usb')
-rw-r--r-- | hw/usb/hcd-xhci.c | 16 | ||||
-rw-r--r-- | hw/usb/host-libusb.c | 20 | ||||
-rw-r--r-- | hw/usb/redirect.c | 29 | ||||
-rw-r--r-- | hw/usb/trace-events | 1 |
4 files changed, 63 insertions, 3 deletions
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 80988bb305..fb05de4b05 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1861,6 +1861,13 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, xhci_kick_epctx(epctx, streamid); } +static bool xhci_slot_ok(XHCIState *xhci, int slotid) +{ + return (xhci->slots[slotid - 1].uport && + xhci->slots[slotid - 1].uport->dev && + xhci->slots[slotid - 1].uport->dev->attached); +} + static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) { XHCIState *xhci = epctx->xhci; @@ -1878,9 +1885,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) /* If the device has been detached, but the guest has not noticed this yet the 2 above checks will succeed, but we must NOT continue */ - if (!xhci->slots[epctx->slotid - 1].uport || - !xhci->slots[epctx->slotid - 1].uport->dev || - !xhci->slots[epctx->slotid - 1].uport->dev->attached) { + if (!xhci_slot_ok(xhci, epctx->slotid)) { return; } @@ -1987,6 +1992,10 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) } else { xhci_fire_transfer(xhci, xfer, epctx); } + if (!xhci_slot_ok(xhci, epctx->slotid)) { + /* surprise removal -> stop processing */ + break; + } if (xfer->complete) { /* update ring dequeue ptr */ xhci_set_ep_state(xhci, epctx, stctx, epctx->state); @@ -2000,6 +2009,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) if (xfer != NULL && xfer->running_retry) { DPRINTF("xhci: xfer nacked, stopping schedule\n"); epctx->retry = xfer; + xhci_xfer_unmap(xfer); break; } if (count++ > TRANSFER_LIMIT) { diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index fcf48c0193..00e0e36369 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -88,6 +88,7 @@ struct USBHostDevice { bool needs_autoscan; bool allow_one_guest_reset; bool allow_all_guest_resets; + bool suppress_remote_wake; /* state */ QTAILQ_ENTRY(USBHostDevice) next; @@ -386,6 +387,8 @@ static void LIBUSB_CALL usb_host_req_complete_ctrl(struct libusb_transfer *xfer) r->p->status = status_map[xfer->status]; r->p->actual_length = xfer->actual_length; if (r->in && xfer->actual_length) { + USBDevice *udev = USB_DEVICE(s); + struct libusb_config_descriptor *conf = (void *)r->cbuf; memcpy(r->cbuf, r->buffer + 8, xfer->actual_length); /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices @@ -394,6 +397,21 @@ static void LIBUSB_CALL usb_host_req_complete_ctrl(struct libusb_transfer *xfer) r->cbuf[7] == 9) { r->cbuf[7] = 64; } + /* + *If this is GET_DESCRIPTOR request for configuration descriptor, + * remove 'remote wakeup' flag from it to prevent idle power down + * in Windows guest + */ + if (s->suppress_remote_wake && + udev->setup_buf[0] == USB_DIR_IN && + udev->setup_buf[1] == USB_REQ_GET_DESCRIPTOR && + udev->setup_buf[3] == USB_DT_CONFIG && udev->setup_buf[2] == 0 && + xfer->actual_length > + offsetof(struct libusb_config_descriptor, bmAttributes) && + (conf->bmAttributes & USB_CFG_ATT_WAKEUP)) { + trace_usb_host_remote_wakeup_removed(s->bus_num, s->addr); + conf->bmAttributes &= ~USB_CFG_ATT_WAKEUP; + } } trace_usb_host_req_complete(s->bus_num, s->addr, r->p, r->p->status, r->p->actual_length); @@ -1596,6 +1614,8 @@ static Property usb_host_dev_properties[] = { LIBUSB_LOG_LEVEL_WARNING), DEFINE_PROP_BIT("pipeline", USBHostDevice, options, USB_HOST_OPT_PIPELINE, true), + DEFINE_PROP_BOOL("suppress-remote-wake", USBHostDevice, + suppress_remote_wake, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 0068aa8a19..efdbcc074c 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -113,6 +113,8 @@ struct USBRedirDevice { /* Properties */ CharBackend cs; bool enable_streams; + bool suppress_remote_wake; + bool in_write; uint8_t debug; int32_t bootindex; char *filter_str; @@ -290,6 +292,13 @@ static int usbredir_write(void *priv, uint8_t *data, int count) return 0; } + /* Recursion check */ + if (dev->in_write) { + DPRINTF("usbredir_write recursion\n"); + return 0; + } + dev->in_write = true; + r = qemu_chr_fe_write(&dev->cs, data, count); if (r < count) { if (!dev->watch) { @@ -300,6 +309,7 @@ static int usbredir_write(void *priv, uint8_t *data, int count) r = 0; } } + dev->in_write = false; return r; } @@ -1994,6 +2004,23 @@ static void usbredir_control_packet(void *priv, uint64_t id, memcpy(dev->dev.data_buf, data, data_len); } p->actual_length = len; + /* + * If this is GET_DESCRIPTOR request for configuration descriptor, + * remove 'remote wakeup' flag from it to prevent idle power down + * in Windows guest + */ + if (dev->suppress_remote_wake && + control_packet->requesttype == USB_DIR_IN && + control_packet->request == USB_REQ_GET_DESCRIPTOR && + control_packet->value == (USB_DT_CONFIG << 8) && + control_packet->index == 0 && + /* bmAttributes field of config descriptor */ + len > 7 && (dev->dev.data_buf[7] & USB_CFG_ATT_WAKEUP)) { + DPRINTF("Removed remote wake %04X:%04X\n", + dev->device_info.vendor_id, + dev->device_info.product_id); + dev->dev.data_buf[7] &= ~USB_CFG_ATT_WAKEUP; + } usb_generic_async_ctrl_complete(&dev->dev, p); } free(data); @@ -2535,6 +2562,8 @@ static Property usbredir_properties[] = { DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning), DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), DEFINE_PROP_BOOL("streams", USBRedirDevice, enable_streams, true), + DEFINE_PROP_BOOL("suppress-remote-wake", USBRedirDevice, + suppress_remote_wake, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 2d3713351c..1c24d82c09 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -266,3 +266,4 @@ usb_host_parse_config(int bus, int addr, int value, int active) "dev %d:%d, valu usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev %d:%d, num %d, alt %d, active %d" usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d" usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s" +usb_host_remote_wakeup_removed(int bus, int addr) "dev %d:%d" |