From 7ed4657396add28382081a15557c78cd480c1cf1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 22 May 2019 11:47:00 +0200 Subject: [PATCH 1/9] usb: call reset handler before updating state That way the device reset handler can see what the before-reset state of the device is. Signed-off-by: Gerd Hoffmann Message-id: 20190522094702.17619-2-kraxel@redhat.com --- hw/usb/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/core.c b/hw/usb/core.c index 8fbd9c7d57..3ab48a1607 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -87,10 +87,10 @@ void usb_device_reset(USBDevice *dev) if (dev == NULL || !dev->attached) { return; } + usb_device_handle_reset(dev); dev->remote_wakeup = 0; dev->addr = 0; dev->state = USB_STATE_DEFAULT; - usb_device_handle_reset(dev); } void usb_wakeup(USBEndpoint *ep, unsigned int stream) From 65f14ab98da1da920f98ee8734dc1588b01d6b2b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 22 May 2019 11:47:01 +0200 Subject: [PATCH 2/9] usb-host: skip reset for untouched devices If the guest didn't talk to the device yet, skip the reset. Without this usb-host devices get resetted a number of times at boot time for no good reason. Signed-off-by: Gerd Hoffmann Message-id: 20190522094702.17619-3-kraxel@redhat.com --- hw/usb/host-libusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 67b7465915..4e9a45a5d2 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1459,6 +1459,9 @@ static void usb_host_handle_reset(USBDevice *udev) if (!s->allow_guest_reset) { return; } + if (udev->addr == 0) { + return; + } trace_usb_host_reset(s->bus_num, s->addr); From bfe44898848614cfcb3a269bc965afbe1f0f331c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 22 May 2019 11:47:02 +0200 Subject: [PATCH 3/9] usb-host: avoid libusb_set_configuration calls Seems some devices become confused when we call libusb_set_configuration(). So before calling the function check whenever the device has multiple configurations in the first place, and in case it hasn't (which is the case for the majority of devices) simply skip the call as it will have no effect anyway. Signed-off-by: Gerd Hoffmann Message-id: 20190522094702.17619-4-kraxel@redhat.com --- hw/usb/host-libusb.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 4e9a45a5d2..4f765d7f9a 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1225,19 +1225,21 @@ static void usb_host_set_address(USBHostDevice *s, int addr) static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p) { - int rc; + int rc = 0; trace_usb_host_set_config(s->bus_num, s->addr, config); usb_host_release_interfaces(s); - rc = libusb_set_configuration(s->dh, config); - if (rc != 0) { - usb_host_libusb_error("libusb_set_configuration", rc); - p->status = USB_RET_STALL; - if (rc == LIBUSB_ERROR_NO_DEVICE) { - usb_host_nodev(s); + if (s->ddesc.bNumConfigurations != 1) { + rc = libusb_set_configuration(s->dh, config); + if (rc != 0) { + usb_host_libusb_error("libusb_set_configuration", rc); + p->status = USB_RET_STALL; + if (rc == LIBUSB_ERROR_NO_DEVICE) { + usb_host_nodev(s); + } + return; } - return; } p->status = usb_host_claim_interfaces(s, config); if (p->status != USB_RET_SUCCESS) { From bdb88a8e120e6b5eca5d3a19f5f8f1b76fa64453 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 24 May 2019 09:03:06 +0200 Subject: [PATCH 4/9] usb-hub: tweak feature names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dashes, so they don't look like two separate things when printed. Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20190524070310.4952-2-kraxel@redhat.com --- hw/usb/dev-hub.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 7e9339b8a8..9b04b6a6ec 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -287,11 +287,11 @@ static const char *feature_name(int feature) [PORT_POWER] = "power", [PORT_LOWSPEED] = "lowspeed", [PORT_HIGHSPEED] = "highspeed", - [PORT_C_CONNECTION] = "change connection", - [PORT_C_ENABLE] = "change enable", - [PORT_C_SUSPEND] = "change suspend", - [PORT_C_OVERCURRENT] = "change overcurrent", - [PORT_C_RESET] = "change reset", + [PORT_C_CONNECTION] = "change-connection", + [PORT_C_ENABLE] = "change-enable", + [PORT_C_SUSPEND] = "change-suspend", + [PORT_C_OVERCURRENT] = "change-overcurrent", + [PORT_C_RESET] = "change-reset", [PORT_TEST] = "test", [PORT_INDICATOR] = "indicator", }; From 9d84bb001c2c99aa1df3f32a529c1fa3e3afb39d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 24 May 2019 09:03:07 +0200 Subject: [PATCH 5/9] usb-hub: make number of ports runtime-configurable Add num_ports property which allows configure the number of downstream ports. Valid range is 1-8, default is 8. Signed-off-by: Gerd Hoffmann Message-id: 20190524070310.4952-3-kraxel@redhat.com --- hw/usb/dev-hub.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 9b04b6a6ec..4f700c8f92 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -29,7 +29,7 @@ #include "desc.h" #include "qemu/error-report.h" -#define NUM_PORTS 8 +#define MAX_PORTS 8 typedef struct USBHubPort { USBPort port; @@ -40,7 +40,8 @@ typedef struct USBHubPort { typedef struct USBHubState { USBDevice dev; USBEndpoint *intr; - USBHubPort ports[NUM_PORTS]; + uint32_t num_ports; + USBHubPort ports[MAX_PORTS]; } USBHubState; #define TYPE_USB_HUB "usb-hub" @@ -109,7 +110,7 @@ static const USBDescIface desc_iface_hub = { { .bEndpointAddress = USB_DIR_IN | 0x01, .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 1 + DIV_ROUND_UP(NUM_PORTS, 8), + .wMaxPacketSize = 1 + DIV_ROUND_UP(MAX_PORTS, 8), .bInterval = 0xff, }, } @@ -242,7 +243,7 @@ static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr) USBDevice *downstream; int i; - for (i = 0; i < NUM_PORTS; i++) { + for (i = 0; i < s->num_ports; i++) { port = &s->ports[i]; if (!(port->wPortStatus & PORT_STAT_ENABLE)) { continue; @@ -262,7 +263,7 @@ static void usb_hub_handle_reset(USBDevice *dev) int i; trace_usb_hub_reset(s->dev.addr); - for (i = 0; i < NUM_PORTS; i++) { + for (i = 0; i < s->num_ports; i++) { port = s->ports + i; port->wPortStatus = PORT_STAT_POWER; port->wPortChange = 0; @@ -332,7 +333,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, { unsigned int n = index - 1; USBHubPort *port; - if (n >= NUM_PORTS) { + if (n >= s->num_ports) { goto fail; } port = &s->ports[n]; @@ -361,7 +362,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, trace_usb_hub_set_port_feature(s->dev.addr, index, feature_name(value)); - if (n >= NUM_PORTS) { + if (n >= s->num_ports) { goto fail; } port = &s->ports[n]; @@ -394,7 +395,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, trace_usb_hub_clear_port_feature(s->dev.addr, index, feature_name(value)); - if (n >= NUM_PORTS) { + if (n >= s->num_ports) { goto fail; } port = &s->ports[n]; @@ -443,17 +444,17 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, unsigned int n, limit, var_hub_size = 0; memcpy(data, qemu_hub_hub_descriptor, sizeof(qemu_hub_hub_descriptor)); - data[2] = NUM_PORTS; + data[2] = s->num_ports; /* fill DeviceRemovable bits */ - limit = DIV_ROUND_UP(NUM_PORTS + 1, 8) + 7; + limit = DIV_ROUND_UP(s->num_ports + 1, 8) + 7; for (n = 7; n < limit; n++) { data[n] = 0x00; var_hub_size++; } /* fill PortPwrCtrlMask bits */ - limit = limit + DIV_ROUND_UP(NUM_PORTS, 8); + limit = limit + DIV_ROUND_UP(s->num_ports, 8); for (;n < limit; n++) { data[n] = 0xff; var_hub_size++; @@ -481,7 +482,7 @@ static void usb_hub_handle_data(USBDevice *dev, USBPacket *p) unsigned int status; uint8_t buf[4]; int i, n; - n = DIV_ROUND_UP(NUM_PORTS + 1, 8); + n = DIV_ROUND_UP(s->num_ports + 1, 8); if (p->iov.size == 1) { /* FreeBSD workaround */ n = 1; } else if (n > p->iov.size) { @@ -489,7 +490,7 @@ static void usb_hub_handle_data(USBDevice *dev, USBPacket *p) return; } status = 0; - for(i = 0; i < NUM_PORTS; i++) { + for (i = 0; i < s->num_ports; i++) { port = &s->ports[i]; if (port->wPortChange) status |= (1 << (i + 1)); @@ -520,7 +521,7 @@ static void usb_hub_unrealize(USBDevice *dev, Error **errp) USBHubState *s = (USBHubState *)dev; int i; - for (i = 0; i < NUM_PORTS; i++) { + for (i = 0; i < s->num_ports; i++) { usb_unregister_port(usb_bus_from_device(dev), &s->ports[i].port); } @@ -540,6 +541,12 @@ static void usb_hub_realize(USBDevice *dev, Error **errp) USBHubPort *port; int i; + if (s->num_ports < 1 || s->num_ports > MAX_PORTS) { + error_setg(errp, "num_ports (%d) out of range (1..%d)", + s->num_ports, MAX_PORTS); + return; + } + if (dev->port->hubcount == 5) { error_setg(errp, "usb hub chain too deep"); return; @@ -548,7 +555,7 @@ static void usb_hub_realize(USBDevice *dev, Error **errp) usb_desc_create_serial(dev); usb_desc_init(dev); s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); - for (i = 0; i < NUM_PORTS; i++) { + for (i = 0; i < s->num_ports; i++) { port = &s->ports[i]; usb_register_port(usb_bus_from_device(dev), &port->port, s, i, &usb_hub_port_ops, @@ -575,12 +582,17 @@ static const VMStateDescription vmstate_usb_hub = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_USB_DEVICE(dev, USBHubState), - VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0, + VMSTATE_STRUCT_ARRAY(ports, USBHubState, MAX_PORTS, 0, vmstate_usb_hub_port, USBHubPort), VMSTATE_END_OF_LIST() } }; +static Property usb_hub_properties[] = { + DEFINE_PROP_UINT32("ports", USBHubState, num_ports, 8), + DEFINE_PROP_END_OF_LIST(), +}; + static void usb_hub_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -597,6 +609,7 @@ static void usb_hub_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "hub"; dc->vmsd = &vmstate_usb_hub; + dc->props = usb_hub_properties; } static const TypeInfo hub_info = { From 868a420393e1231be75d12a3d0df67f3c466a56a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 24 May 2019 09:03:08 +0200 Subject: [PATCH 6/9] usb-hub: add helpers to update port state Add usb_hub_port_set() and usb_hub_port_clear() helpers which care about updating the change bits (port->wPortChange) properly, so we don't need to have that logic sprinkled all over the place ;) Signed-off-by: Gerd Hoffmann Message-id: 20190524070310.4952-4-kraxel@redhat.com --- hw/usb/dev-hub.c | 84 ++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 4f700c8f92..1cc92a5f9a 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -159,18 +159,46 @@ static const uint8_t qemu_hub_hub_descriptor[] = /* DeviceRemovable and PortPwrCtrlMask patched in later */ }; +static bool usb_hub_port_change(USBHubPort *port, uint16_t status) +{ + bool notify = false; + + if (status & 0x1f) { + port->wPortChange |= status; + notify = true; + } + return notify; +} + +static bool usb_hub_port_set(USBHubPort *port, uint16_t status) +{ + if (port->wPortStatus & status) { + return false; + } + port->wPortStatus |= status; + return usb_hub_port_change(port, status); +} + +static bool usb_hub_port_clear(USBHubPort *port, uint16_t status) +{ + if (!(port->wPortStatus & status)) { + return false; + } + port->wPortStatus &= ~status; + return usb_hub_port_change(port, status); +} + static void usb_hub_attach(USBPort *port1) { USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; trace_usb_hub_attach(s->dev.addr, port1->index + 1); - port->wPortStatus |= PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; + usb_hub_port_set(port, PORT_STAT_CONNECTION); if (port->port.dev->speed == USB_SPEED_LOW) { - port->wPortStatus |= PORT_STAT_LOW_SPEED; + usb_hub_port_set(port, PORT_STAT_LOW_SPEED); } else { - port->wPortStatus &= ~PORT_STAT_LOW_SPEED; + usb_hub_port_clear(port, PORT_STAT_LOW_SPEED); } usb_wakeup(s->intr, 0); } @@ -186,16 +214,9 @@ static void usb_hub_detach(USBPort *port1) /* Let upstream know the device on this port is gone */ s->dev.port->ops->child_detach(s->dev.port, port1->dev); - port->wPortStatus &= ~PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->wPortStatus & PORT_STAT_ENABLE) { - port->wPortStatus &= ~PORT_STAT_ENABLE; - port->wPortChange |= PORT_STAT_C_ENABLE; - } - if (port->wPortStatus & PORT_STAT_SUSPEND) { - port->wPortStatus &= ~PORT_STAT_SUSPEND; - port->wPortChange |= PORT_STAT_C_SUSPEND; - } + usb_hub_port_clear(port, PORT_STAT_CONNECTION); + usb_hub_port_clear(port, PORT_STAT_ENABLE); + usb_hub_port_clear(port, PORT_STAT_SUSPEND); usb_wakeup(s->intr, 0); } @@ -212,9 +233,7 @@ static void usb_hub_wakeup(USBPort *port1) USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; - if (port->wPortStatus & PORT_STAT_SUSPEND) { - port->wPortStatus &= ~PORT_STAT_SUSPEND; - port->wPortChange |= PORT_STAT_C_SUSPEND; + if (usb_hub_port_clear(port, PORT_STAT_SUSPEND)) { usb_wakeup(s->intr, 0); } } @@ -265,13 +284,13 @@ static void usb_hub_handle_reset(USBDevice *dev) trace_usb_hub_reset(s->dev.addr); for (i = 0; i < s->num_ports; i++) { port = s->ports + i; - port->wPortStatus = PORT_STAT_POWER; + port->wPortStatus = 0; port->wPortChange = 0; + usb_hub_port_set(port, PORT_STAT_POWER); if (port->port.dev && port->port.dev->attached) { - port->wPortStatus |= PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; + usb_hub_port_set(port, PORT_STAT_CONNECTION); if (port->port.dev->speed == USB_SPEED_LOW) { - port->wPortStatus |= PORT_STAT_LOW_SPEED; + usb_hub_port_set(port, PORT_STAT_LOW_SPEED); } } } @@ -372,13 +391,13 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, port->wPortStatus |= PORT_STAT_SUSPEND; break; case PORT_RESET: + usb_hub_port_set(port, PORT_STAT_RESET); + usb_hub_port_clear(port, PORT_STAT_RESET); if (dev && dev->attached) { usb_device_reset(dev); - port->wPortChange |= PORT_STAT_C_RESET; - /* set enable bit */ - port->wPortStatus |= PORT_STAT_ENABLE; - usb_wakeup(s->intr, 0); + usb_hub_port_set(port, PORT_STAT_ENABLE); } + usb_wakeup(s->intr, 0); break; case PORT_POWER: break; @@ -407,20 +426,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, port->wPortChange &= ~PORT_STAT_C_ENABLE; break; case PORT_SUSPEND: - if (port->wPortStatus & PORT_STAT_SUSPEND) { - port->wPortStatus &= ~PORT_STAT_SUSPEND; - - /* - * USB Spec rev2.0 11.24.2.7.2.3 C_PORT_SUSPEND - * "This bit is set on the following transitions: - * - On transition from the Resuming state to the - * SendEOP [sic] state" - * - * Note that this includes both remote wake-up and - * explicit ClearPortFeature(PORT_SUSPEND). - */ - port->wPortChange |= PORT_STAT_C_SUSPEND; - } + usb_hub_port_clear(port, PORT_STAT_SUSPEND); break; case PORT_C_SUSPEND: port->wPortChange &= ~PORT_STAT_C_SUSPEND; From 638ac2d8437b7600262bc584e3634f1aaca732cf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 24 May 2019 09:03:09 +0200 Subject: [PATCH 7/9] usb-hub: add usb_hub_port_update() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Helper function to update port status bits which depends on the connected device. We need the same logic for device attach and port reset, so factor it out. Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20190524070310.4952-5-kraxel@redhat.com --- hw/usb/dev-hub.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 1cc92a5f9a..29f4d6723e 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -188,18 +188,28 @@ static bool usb_hub_port_clear(USBHubPort *port, uint16_t status) return usb_hub_port_change(port, status); } +static bool usb_hub_port_update(USBHubPort *port) +{ + bool notify = false; + + if (port->port.dev && port->port.dev->attached) { + notify = usb_hub_port_set(port, PORT_STAT_CONNECTION); + if (port->port.dev->speed == USB_SPEED_LOW) { + usb_hub_port_set(port, PORT_STAT_LOW_SPEED); + } else { + usb_hub_port_clear(port, PORT_STAT_LOW_SPEED); + } + } + return notify; +} + static void usb_hub_attach(USBPort *port1) { USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; trace_usb_hub_attach(s->dev.addr, port1->index + 1); - usb_hub_port_set(port, PORT_STAT_CONNECTION); - if (port->port.dev->speed == USB_SPEED_LOW) { - usb_hub_port_set(port, PORT_STAT_LOW_SPEED); - } else { - usb_hub_port_clear(port, PORT_STAT_LOW_SPEED); - } + usb_hub_port_update(port); usb_wakeup(s->intr, 0); } @@ -287,12 +297,7 @@ static void usb_hub_handle_reset(USBDevice *dev) port->wPortStatus = 0; port->wPortChange = 0; usb_hub_port_set(port, PORT_STAT_POWER); - if (port->port.dev && port->port.dev->attached) { - usb_hub_port_set(port, PORT_STAT_CONNECTION); - if (port->port.dev->speed == USB_SPEED_LOW) { - usb_hub_port_set(port, PORT_STAT_LOW_SPEED); - } - } + usb_hub_port_update(port); } } From 1cc403eb2164146b0769507746ff6565d40fee98 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 24 May 2019 09:03:10 +0200 Subject: [PATCH 8/9] usb-hub: emulate per port power switching Add support for per port power switching. Virtual power of course ;) Use port-power=on property to enable this. Signed-off-by: Gerd Hoffmann Message-id: 20190524070310.4952-6-kraxel@redhat.com --- hw/usb/dev-hub.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 29f4d6723e..2b64d6ef03 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" +#include "qemu/timer.h" #include "trace.h" #include "hw/usb.h" #include "desc.h" @@ -41,6 +42,8 @@ typedef struct USBHubState { USBDevice dev; USBEndpoint *intr; uint32_t num_ports; + bool port_power; + QEMUTimer *port_timer; USBHubPort ports[MAX_PORTS]; } USBHubState; @@ -203,6 +206,20 @@ static bool usb_hub_port_update(USBHubPort *port) return notify; } +static void usb_hub_port_update_timer(void *opaque) +{ + USBHubState *s = opaque; + bool notify = false; + int i; + + for (i = 0; i < s->num_ports; i++) { + notify |= usb_hub_port_update(&s->ports[i]); + } + if (notify) { + usb_wakeup(s->intr, 0); + } +} + static void usb_hub_attach(USBPort *port1) { USBHubState *s = port1->opaque; @@ -405,6 +422,11 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, usb_wakeup(s->intr, 0); break; case PORT_POWER: + if (s->port_power) { + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + usb_hub_port_set(port, PORT_STAT_POWER); + timer_mod(s->port_timer, now + 5000000); /* 5 ms */ + } break; default: goto fail; @@ -445,6 +467,14 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, case PORT_C_RESET: port->wPortChange &= ~PORT_STAT_C_RESET; break; + case PORT_POWER: + if (s->port_power) { + usb_hub_port_clear(port, PORT_STAT_POWER); + usb_hub_port_clear(port, PORT_STAT_CONNECTION); + usb_hub_port_clear(port, PORT_STAT_ENABLE); + usb_hub_port_clear(port, PORT_STAT_SUSPEND); + port->wPortChange = 0; + } default: goto fail; } @@ -457,6 +487,11 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, sizeof(qemu_hub_hub_descriptor)); data[2] = s->num_ports; + if (s->port_power) { + data[3] &= ~0x03; + data[3] |= 0x01; + } + /* fill DeviceRemovable bits */ limit = DIV_ROUND_UP(s->num_ports + 1, 8) + 7; for (n = 7; n < limit; n++) { @@ -536,6 +571,9 @@ static void usb_hub_unrealize(USBDevice *dev, Error **errp) usb_unregister_port(usb_bus_from_device(dev), &s->ports[i].port); } + + timer_del(s->port_timer); + timer_free(s->port_timer); } static USBPortOps usb_hub_port_ops = { @@ -565,6 +603,8 @@ static void usb_hub_realize(USBDevice *dev, Error **errp) usb_desc_create_serial(dev); usb_desc_init(dev); + s->port_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + usb_hub_port_update_timer, s); s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); for (i = 0; i < s->num_ports; i++) { port = &s->ports[i]; @@ -587,6 +627,24 @@ static const VMStateDescription vmstate_usb_hub_port = { } }; +static bool usb_hub_port_timer_needed(void *opaque) +{ + USBHubState *s = opaque; + + return s->port_power; +} + +static const VMStateDescription vmstate_usb_hub_port_timer = { + .name = "usb-hub/port-timer", + .version_id = 1, + .minimum_version_id = 1, + .needed = usb_hub_port_timer_needed, + .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(port_timer, USBHubState), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_usb_hub = { .name = "usb-hub", .version_id = 1, @@ -596,11 +654,16 @@ static const VMStateDescription vmstate_usb_hub = { VMSTATE_STRUCT_ARRAY(ports, USBHubState, MAX_PORTS, 0, vmstate_usb_hub_port, USBHubPort), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_usb_hub_port_timer, + NULL } }; static Property usb_hub_properties[] = { DEFINE_PROP_UINT32("ports", USBHubState, num_ports, 8), + DEFINE_PROP_BOOL("port-power", USBHubState, port_power, false), DEFINE_PROP_END_OF_LIST(), }; From 442bac16a6cd708a9f87adb0a263f9d833f03ed5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 20 May 2019 10:18:05 +0200 Subject: [PATCH 9/9] usb-tablet: fix serial compat property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit s/kbd/tablet/, fixes cut+paste bug. Cc: qemu-stable@nongnu.org Reported-by: Dr. David Alan Gilbert Signed-off-by: Gerd Hoffmann Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20190520081805.15019-1-kraxel@redhat.com --- hw/core/machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 934c1bcceb..16ba667434 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -36,7 +36,7 @@ GlobalProperty hw_compat_3_1[] = { { "tpm-tis", "ppi", "false" }, { "usb-kbd", "serial", "42" }, { "usb-mouse", "serial", "42" }, - { "usb-kbd", "serial", "42" }, + { "usb-tablet", "serial", "42" }, { "virtio-blk-device", "discard", "false" }, { "virtio-blk-device", "write-zeroes", "false" }, };