|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v9 15/19] hw/usb/redirect-server: Send device and interface information
The device_connect and interface_info messages currently contain zeroed
fields, so clients cannot apply device filters or quirks correctly.
Read the device and configuration descriptors before announcing the
device, and use them to fill these messages. Update interface and
endpoint information when the configuration or alternate setting changes.
Keep internal descriptor requests separate from host transfers. Retry
on NAK and stop on timeout or invalid descriptors. Do not announce the
device if reading its descriptors fails.
Wait like a USB host. Wait 100 ms between the attach and the reset,
and 10 ms between the reset and the first request. Some firmware turns
on the pull-up before it sets up endpoint 0.
Signed-off-by: Jamin Lin <jamin_lin@xxxxxxxxxxxxxx>
---
hw/usb/redirect-server.c | 496 +++++++++++++++++++++++++++++++++++++++
hw/usb/trace-events | 7 +
2 files changed, 503 insertions(+)
diff --git a/hw/usb/redirect-server.c b/hw/usb/redirect-server.c
index 89d88d6457..914697fdea 100644
--- a/hw/usb/redirect-server.c
+++ b/hw/usb/redirect-server.c
@@ -87,6 +87,28 @@ OBJECT_DECLARE_SIMPLE_TYPE(USBRedirServer, USB_REDIR_SERVER)
#define USBREDIR_SERVER_DESC_CONFIG_SIZE 9
#define USBREDIR_SERVER_DESC_INTERFACE_SIZE 9
#define USBREDIR_SERVER_DESC_ENDPOINT_SIZE 7
+#define USBREDIR_SERVER_DESC_DEVICE_SIZE 18
+
+/*
+ * How long we keep asking the device what it is. This covers every
+ * retry and every wait, so a device that never answers stops here.
+ */
+#define USBREDIR_SERVER_DESC_TIMEOUT_MS 10000
+
+/* How long to wait before asking again after the device said NAK. */
+#define USBREDIR_SERVER_DESC_RETRY_MS 10
+
+/*
+ * The USB spec gives the device an attach debounce interval of at least
+ * 100 ms. We wait this long after the attach. Then we reset the device.
+ */
+#define USBREDIR_SERVER_ATTACH_DEBOUNCE_MS 100
+
+/*
+ * The USB spec gives the device a reset recovery time of 10 ms. We wait
+ * this long after the reset. Then we ask the device for its descriptor.
+ */
+#define USBREDIR_SERVER_RESET_RECOVERY_MS 10
/*
* Ask again this often after an endpoint answered NAK. Most devices
@@ -95,8 +117,34 @@ OBJECT_DECLARE_SIMPLE_TYPE(USBRedirServer, USB_REDIR_SERVER)
*/
#define USBREDIR_SERVER_RETRY_MS 1000
+/*
+ * The steps to read the descriptors, in order:
+ *
+ * IDLE: not started yet.
+ * ATTACH_DEBOUNCE: the device is attached.
+ * Wait USBREDIR_SERVER_ATTACH_DEBOUNCE_MS, then reset it.
+ * RESET_RECOVERY: the device is reset. Wait USBREDIR_SERVER_RESET_RECOVERY_MS.
+ * GET_DEVICE: ask for the device descriptor.
+ * GET_CFG_HEAD: ask for the first part of the config descriptor.
+ * It gives the full length.
+ * GET_CFG_FULL: ask for the full config descriptor.
+ * DONE: all read. Now we can announce the device.
+ * FAILED: the read failed. Close the connection.
+ */
+typedef enum {
+ USBREDIR_SERVER_DESC_IDLE,
+ USBREDIR_SERVER_DESC_ATTACH_DEBOUNCE,
+ USBREDIR_SERVER_DESC_RESET_RECOVERY,
+ USBREDIR_SERVER_DESC_GET_DEVICE,
+ USBREDIR_SERVER_DESC_GET_CFG_HEAD,
+ USBREDIR_SERVER_DESC_GET_CFG_FULL,
+ USBREDIR_SERVER_DESC_DONE,
+ USBREDIR_SERVER_DESC_FAILED,
+} USBRedirServerDescStep;
+
typedef enum {
USBREDIR_SERVER_CTRL,
+ USBREDIR_SERVER_DESCRIPTOR,
} USBRedirServerPktType;
/* Which message answers the host when a control transfer ends. */
@@ -181,6 +229,22 @@ struct USBRedirServer {
bool host_connected;
bool device_announced;
+ uint8_t device_desc[USBREDIR_SERVER_DESC_DEVICE_SIZE];
+ uint8_t config_desc[USBREDIR_SERVER_DESC_CONFIG_SIZE];
+ uint8_t internal_config;
+
+ /*
+ * Our own request on endpoint 0. It never goes on inflight or
+ * waiting, so a cancel from the host cannot reach it.
+ */
+ USBRedirServerDescStep desc_step;
+ USBRedirServerPkt *desc_pkt;
+ QEMUTimer *desc_timeout;
+ QEMUTimer *desc_wait;
+ bool desc_retrying;
+ bool desc_answered;
+ QEMUBH *desc_bh;
+
/* Every request the host is still waiting for an answer to. */
QTAILQ_HEAD(, USBRedirServerPkt) inflight;
uint64_t next_id;
@@ -533,14 +597,333 @@ static void
usbredir_server_send_ep_iface_info(USBRedirServer *s,
usbredirparser_do_write(s->parser);
}
+/*
+ * Reading the descriptor
+ *
+ * The host cannot tell us what the device is, so we ask the device
+ * ourselves before we announce it.
+ */
+
+static const char *usbredir_server_desc_step_name(USBRedirServerDescStep step)
+{
+ static const char *const name[] = {
+ [USBREDIR_SERVER_DESC_IDLE] = "idle",
+ [USBREDIR_SERVER_DESC_ATTACH_DEBOUNCE] = "attach-debounce",
+ [USBREDIR_SERVER_DESC_RESET_RECOVERY] = "reset-recovery",
+ [USBREDIR_SERVER_DESC_GET_DEVICE] = "get-device",
+ [USBREDIR_SERVER_DESC_GET_CFG_HEAD] = "get-cfg-head",
+ [USBREDIR_SERVER_DESC_GET_CFG_FULL] = "get-cfg-full",
+ [USBREDIR_SERVER_DESC_DONE] = "done",
+ [USBREDIR_SERVER_DESC_FAILED] = "failed",
+ };
+
+ assert(step < ARRAY_SIZE(name) && name[step]);
+ return name[step];
+}
+
+static void usbredir_server_cancel_descriptor(USBRedirServer *s)
+{
+ USBRedirServerPkt *rp = s->desc_pkt;
+
+ qemu_bh_cancel(s->desc_bh);
+ timer_del(s->desc_wait);
+ timer_del(s->desc_timeout);
+ s->desc_pkt = NULL;
+ s->desc_retrying = false;
+ s->desc_answered = false;
+ if (rp) {
+ usbredir_server_pkt_free(s, rp);
+ }
+}
+
static void usbredir_server_clear_descriptor(USBRedirServer *s)
{
+ usbredir_server_cancel_descriptor(s);
+ s->desc_step = USBREDIR_SERVER_DESC_IDLE;
+ s->internal_config = 0;
s->host_config = 0;
+ memset(s->device_desc, 0, sizeof(s->device_desc));
+ memset(s->config_desc, 0, sizeof(s->config_desc));
memset(s->alt_setting, 0, sizeof(s->alt_setting));
usbredir_server_clear_ep_table(s);
g_hash_table_remove_all(s->config_table);
}
+static void usbredir_server_descriptor_failed(USBRedirServer *s,
+ const char *reason)
+{
+ error_report(TYPE_USB_REDIR_SERVER ": could not read the descriptor "
+ "at step %s: %s",
+ usbredir_server_desc_step_name(s->desc_step), reason);
+ usbredir_server_clear_descriptor(s);
+ s->desc_step = USBREDIR_SERVER_DESC_FAILED;
+ qemu_bh_schedule(s->announce_bh);
+}
+
+static void usbredir_server_descriptor_timeout(void *opaque)
+{
+ USBRedirServer *s = opaque;
+
+ usbredir_server_descriptor_failed(s, "the device took too long");
+}
+
+static void usbredir_server_descriptor_timer(void *opaque)
+{
+ USBRedirServer *s = opaque;
+
+ qemu_bh_schedule(s->desc_bh);
+}
+
+static void usbredir_server_submit_descriptor(USBRedirServer *s)
+{
+ const char *step = usbredir_server_desc_step_name(s->desc_step);
+ USBRedirServerPkt *rp = s->desc_pkt;
+
+ s->desc_retrying = false;
+ timer_del(s->desc_wait);
+
+ /* The same packet goes out again after a NAK, so empty it first. */
+ rp->pkt.actual_length = 0;
+ memset(rp->data, 0, rp->pkt.iov.size);
+
+ trace_usbredir_server_submit_descriptor(step);
+ usb_handle_packet(usbredir_server_device(s), &rp->pkt);
+
+ switch (rp->pkt.status) {
+ case USB_RET_ASYNC:
+ /*
+ * The device keeps the packet and its buffer. It answers
+ * later, and the core calls usbredir_server_packet_complete().
+ */
+ break;
+ case USB_RET_NAK:
+ s->desc_retrying = true;
+ timer_mod(s->desc_wait, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (int64_t)USBREDIR_SERVER_DESC_RETRY_MS * SCALE_MS);
+ break;
+ default:
+ /*
+ * The transfer ended. SUCCESS has the data, STALL and the
+ * other errors do not. The bottom half looks at the status
+ * and decides which.
+ */
+ s->desc_answered = true;
+ qemu_bh_schedule(s->desc_bh);
+ break;
+ }
+}
+
+static void usbredir_server_ask_descriptor(USBRedirServer *s, uint8_t type,
+ uint16_t length)
+{
+ struct usb_redir_control_packet_header hdr = {
+ .endpoint = 0,
+ .request = USB_REQ_GET_DESCRIPTOR,
+ /* Device to host. The zeros mean standard, to the device. */
+ .requesttype = USB_DIR_IN,
+ .status = 0,
+ .value = type << 8,
+ .index = 0,
+ .length = length,
+ };
+ USBRedirServerPkt *rp;
+
+ assert(!s->desc_pkt);
+ trace_usbredir_server_ask_descriptor(type, length);
+
+ rp = usbredir_server_pkt_alloc(length, 0);
+ rp->type = USBREDIR_SERVER_DESCRIPTOR;
+ usbredir_server_prepare_control(s, rp, &hdr);
+ s->desc_pkt = rp;
+ usbredir_server_submit_descriptor(s);
+}
+
+static bool usbredir_server_accept_descriptor(USBRedirServer *s,
+ const uint8_t *data,
+ int actual_length)
+{
+ const char *step = usbredir_server_desc_step_name(s->desc_step);
+ const USBDescriptor *desc = (const USBDescriptor *)data;
+ const USBDescriptor *head;
+ uint16_t total;
+
+ switch (s->desc_step) {
+ case USBREDIR_SERVER_DESC_GET_DEVICE:
+ if (actual_length != USBREDIR_SERVER_DESC_DEVICE_SIZE ||
+ desc->bLength != USBREDIR_SERVER_DESC_DEVICE_SIZE ||
+ desc->bDescriptorType != USB_DT_DEVICE) {
+ trace_usbredir_server_accept_descriptor(step,
+ "not a device descriptor");
+ return false;
+ }
+ if (desc->u.device.bNumConfigurations == 0) {
+ trace_usbredir_server_accept_descriptor(step,
+ "no configuration");
+ return false;
+ }
+ memcpy(s->device_desc, data, actual_length);
+ s->desc_step = USBREDIR_SERVER_DESC_GET_CFG_HEAD;
+ break;
+ case USBREDIR_SERVER_DESC_GET_CFG_HEAD:
+ if (actual_length != USBREDIR_SERVER_DESC_CONFIG_SIZE ||
+ desc->bLength != USBREDIR_SERVER_DESC_CONFIG_SIZE ||
+ desc->bDescriptorType != USB_DT_CONFIG) {
+ trace_usbredir_server_accept_descriptor(step,
+ "not a configuration descriptor");
+ return false;
+ }
+ if (desc->u.config.bConfigurationValue == 0) {
+ trace_usbredir_server_accept_descriptor(step,
+ "configuration value 0");
+ return false;
+ }
+ total = desc->u.config.wTotalLength_hi << 8 |
+ desc->u.config.wTotalLength_lo;
+ if (total < USBREDIR_SERVER_DESC_CONFIG_SIZE ||
+ total > USB_DESC_MAX_LEN) {
+ trace_usbredir_server_desc_too_long(total);
+ return false;
+ }
+ memcpy(s->config_desc, data, sizeof(s->config_desc));
+ s->desc_step = USBREDIR_SERVER_DESC_GET_CFG_FULL;
+ break;
+ case USBREDIR_SERVER_DESC_GET_CFG_FULL:
+ head = (const USBDescriptor *)s->config_desc;
+ total = head->u.config.wTotalLength_hi << 8 |
+ head->u.config.wTotalLength_lo;
+ if (actual_length != total) {
+ trace_usbredir_server_accept_descriptor(step,
+ "wrong length");
+ return false;
+ }
+ if (memcmp(data, s->config_desc, sizeof(s->config_desc))) {
+ trace_usbredir_server_accept_descriptor(step,
+ "the head changed");
+ return false;
+ }
+ if (!usbredir_server_save_config(s, data, actual_length)) {
+ trace_usbredir_server_accept_descriptor(step,
+ "the configuration does not hold together");
+ return false;
+ }
+ s->internal_config = desc->u.config.bConfigurationValue;
+ s->desc_step = USBREDIR_SERVER_DESC_DONE;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return true;
+}
+
+static void usbredir_server_descriptor_answer(USBRedirServer *s,
+ USBRedirServerPkt *rp)
+{
+ const char *step = usbredir_server_desc_step_name(s->desc_step);
+
+ trace_usbredir_server_descriptor_answer(step, rp->pkt.status,
+ rp->pkt.actual_length);
+
+ if (rp->pkt.status != USB_RET_SUCCESS) {
+ usbredir_server_descriptor_failed(s, "the transfer failed");
+ return;
+ }
+
+ if (!usbredir_server_accept_descriptor(s, rp->data,
+ rp->pkt.actual_length)) {
+ usbredir_server_descriptor_failed(s,
+ "the answer is not what we asked for");
+ return;
+ }
+
+ /*
+ * usbredir_server_accept_descriptor() set the next step. DONE
+ * means the read is finished.
+ */
+ if (s->desc_step == USBREDIR_SERVER_DESC_DONE) {
+ timer_del(s->desc_timeout);
+ qemu_bh_schedule(s->announce_bh);
+ return;
+ }
+
+ qemu_bh_schedule(s->desc_bh);
+}
+
+static void usbredir_server_descriptor_bh(void *opaque)
+{
+ USBRedirServer *s = opaque;
+ USBDevice *device = usbredir_server_device(s);
+ USBRedirServerPkt *rp = s->desc_pkt;
+
+ if (!s->host_connected || !s->parser || !device || !device->attached) {
+ return;
+ }
+
+ switch (s->desc_step) {
+ case USBREDIR_SERVER_DESC_ATTACH_DEBOUNCE:
+ /*
+ * After attach the device is in ATTACHED state, and
+ * usb_handle_packet() only takes a packet in DEFAULT state.
+ * A reset moves it there.
+ */
+ usb_device_reset(device);
+ s->desc_step = USBREDIR_SERVER_DESC_RESET_RECOVERY;
+ timer_mod(s->desc_wait, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (int64_t)USBREDIR_SERVER_RESET_RECOVERY_MS * SCALE_MS);
+ return;
+ case USBREDIR_SERVER_DESC_RESET_RECOVERY:
+ s->desc_step = USBREDIR_SERVER_DESC_GET_DEVICE;
+ qemu_bh_schedule(s->desc_bh);
+ return;
+ case USBREDIR_SERVER_DESC_GET_DEVICE:
+ if (!rp) {
+ usbredir_server_ask_descriptor(s, USB_DT_DEVICE,
+ USBREDIR_SERVER_DESC_DEVICE_SIZE);
+ return;
+ }
+ break;
+ case USBREDIR_SERVER_DESC_GET_CFG_HEAD:
+ if (!rp) {
+ usbredir_server_ask_descriptor(s, USB_DT_CONFIG,
+ USBREDIR_SERVER_DESC_CONFIG_SIZE);
+ return;
+ }
+ break;
+ case USBREDIR_SERVER_DESC_GET_CFG_FULL:
+ if (!rp) {
+ usbredir_server_ask_descriptor(s, USB_DT_CONFIG,
+ lduw_le_p(s->config_desc + 2));
+ return;
+ }
+ break;
+ default:
+ /* IDLE, DONE and FAILED have nothing to send. */
+ return;
+ }
+
+ /* rp is the request we gave the device. */
+ if (s->desc_answered) {
+ /*
+ * Set s->desc_pkt to NULL first. On failure,
+ * usbredir_server_descriptor_answer() ends up in
+ * usbredir_server_cancel_descriptor(), which frees s->desc_pkt.
+ * With NULL there, only the line below frees the packet.
+ */
+ s->desc_pkt = NULL;
+ s->desc_answered = false;
+ usbredir_server_descriptor_answer(s, rp);
+ usbredir_server_pkt_free(s, rp);
+ return;
+ }
+
+ if (s->desc_retrying) {
+ usbredir_server_submit_descriptor(s);
+ return;
+ }
+
+ /* The device is still holding the request. */
+}
+
/*
* Announcing the device
*/
@@ -557,6 +940,22 @@ static uint8_t usbredir_server_speed(USBDevice *device)
}
}
+static void usbredir_server_describe_device(USBRedirServer *s,
+ struct usb_redir_device_connect_header *conn)
+{
+ const USBDescriptor *desc = (const USBDescriptor *)s->device_desc;
+
+ conn->device_class = desc->u.device.bDeviceClass;
+ conn->device_subclass = desc->u.device.bDeviceSubClass;
+ conn->device_protocol = desc->u.device.bDeviceProtocol;
+ conn->vendor_id = desc->u.device.idVendor_hi << 8 |
+ desc->u.device.idVendor_lo;
+ conn->product_id = desc->u.device.idProduct_hi << 8 |
+ desc->u.device.idProduct_lo;
+ conn->device_version_bcd = desc->u.device.bcdDevice_hi << 8 |
+ desc->u.device.bcdDevice_lo;
+}
+
static void usbredir_server_announce_bh(void *opaque)
{
USBRedirServer *s = opaque;
@@ -567,9 +966,42 @@ static void usbredir_server_announce_bh(void *opaque)
!device || !device->attached) {
return;
}
+
+ if (s->desc_step == USBREDIR_SERVER_DESC_IDLE) {
+ s->desc_step = USBREDIR_SERVER_DESC_ATTACH_DEBOUNCE;
+ timer_mod(s->desc_timeout, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (int64_t)USBREDIR_SERVER_DESC_TIMEOUT_MS * SCALE_MS);
+ timer_mod(s->desc_wait, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (int64_t)USBREDIR_SERVER_ATTACH_DEBOUNCE_MS * SCALE_MS);
+ return;
+ }
+
+ /*
+ * We could not read the descriptor. The host waits for
+ * device_connect, so close the connection to tell it.
+ */
+ if (s->desc_step == USBREDIR_SERVER_DESC_FAILED) {
+ trace_usbredir_server_announce_failed();
+ qemu_chr_fe_disconnect(&s->cs);
+ return;
+ }
+
+ if (s->desc_step != USBREDIR_SERVER_DESC_DONE) {
+ return;
+ }
+
s->device_announced = true;
conn.speed = usbredir_server_speed(device);
+ usbredir_server_describe_device(s, &conn);
+ /*
+ * Send this first. The host looks the device over when
+ * device_connect arrives, and this is what it looks at.
+ */
+ usbredir_server_send_ep_iface_info(s, s->internal_config);
+
+ trace_usbredir_server_announce(conn.speed, conn.vendor_id,
+ conn.product_id, conn.device_class);
usbredirparser_send_device_connect(s->parser, &conn);
usbredirparser_do_write(s->parser);
}
@@ -730,6 +1162,28 @@ static void usbredir_server_packet_complete(USBPort
*port, USBPacket *p)
USBRedirServerPkt *rp = container_of(p, USBRedirServerPkt, pkt);
USBDevice *device = usbredir_server_device(s);
+ /*
+ * Our own request. We read the descriptor before we announce
+ * the device, so the host knows what it is getting.
+ */
+ if (rp->type == USBREDIR_SERVER_DESCRIPTOR) {
+ assert(s->desc_pkt == rp);
+
+ /*
+ * If the endpoint halts with this packet still queued, the core
+ * leaves it there and loops in usb_packet_complete() until the
+ * queue is empty. A bottom half would never run, so take it off
+ * now.
+ */
+ if (p->status == USB_RET_REMOVE_FROM_QUEUE &&
+ usb_packet_is_inflight(p)) {
+ usb_cancel_packet(p);
+ }
+ s->desc_answered = true;
+ qemu_bh_schedule(s->desc_bh);
+ return;
+ }
+
/* A cancel can arrive while we answer, so it must not find this one. */
QTAILQ_REMOVE(&s->inflight, rp, inflight_link);
@@ -830,6 +1284,16 @@ static void usbredir_server_wakeup_ep(USBBus *bus,
USBEndpoint *ep,
USBRedirServer *s = container_of(bus, USBRedirServer, bus);
uint8_t addr = ep->nr | (ep->pid == USB_TOKEN_IN ? USB_DIR_IN : 0);
+ /*
+ * Our descriptor request is not on inflight or waiting, so
+ * usbredir_server_kick_ep() below cannot see it. The device is
+ * still running when it calls us, so sending now would call back
+ * into it.
+ */
+ if (ep->nr == 0 && s->desc_retrying) {
+ qemu_bh_schedule(s->desc_bh);
+ }
+
usbredir_server_kick_ep(s, usbredir_server_ep_index(addr));
}
@@ -972,6 +1436,16 @@ static void usbredir_server_reset(void *priv)
usbredir_server_stop_transfers(s);
usb_device_reset(device);
+
+ /*
+ * Before the announcement, usbredir_server_stop_transfers() above
+ * also stopped our descriptor read. Start it again from the
+ * beginning.
+ */
+ if (!s->device_announced) {
+ usbredir_server_clear_descriptor(s);
+ qemu_bh_schedule(s->announce_bh);
+ }
}
static void usbredir_server_prepare_control(USBRedirServer *s,
@@ -1211,6 +1685,8 @@ static void usbredir_server_stop_transfers(USBRedirServer
*s)
USBRedirServerPkt *rp;
int i;
+ usbredir_server_cancel_descriptor(s);
+
for (i = 0; i < USBREDIR_SERVER_MAX_EP; i++) {
timer_del(s->ep[i].retry);
}
@@ -1291,6 +1767,7 @@ static void usbredir_server_destroy_parser(USBRedirServer
*s)
s->host_connected = false;
s->device_announced = false;
+ usbredir_server_clear_descriptor(s);
qemu_bh_cancel(s->announce_bh);
g_clear_handle_id(&s->watch, g_source_remove);
@@ -1364,6 +1841,9 @@ static void usbredir_server_chardev_event(void *opaque,
break;
case CHR_EVENT_CLOSED:
trace_usbredir_server_chardev_close();
+ s->host_connected = false;
+ qemu_bh_cancel(s->announce_bh);
+ usbredir_server_cancel_descriptor(s);
qemu_bh_schedule(s->chardev_close_bh);
break;
case CHR_EVENT_BREAK:
@@ -1412,6 +1892,12 @@ static void usbredir_server_realize(DeviceState *dev,
Error **errp)
s, &dev->mem_reentrancy_guard);
s->announce_bh = qemu_bh_new_guarded(usbredir_server_announce_bh, s,
&dev->mem_reentrancy_guard);
+ s->desc_bh = qemu_bh_new_guarded(usbredir_server_descriptor_bh, s,
+ &dev->mem_reentrancy_guard);
+ s->desc_wait = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ usbredir_server_descriptor_timer, s);
+ s->desc_timeout = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ usbredir_server_descriptor_timeout, s);
qemu_chr_fe_set_handlers(&s->cs,
usbredir_server_chardev_can_read,
@@ -1440,6 +1926,16 @@ static void usbredir_server_unrealize(DeviceState *dev)
s->announce_bh = NULL;
}
+ if (s->desc_bh) {
+ qemu_bh_delete(s->desc_bh);
+ s->desc_bh = NULL;
+ }
+
+ timer_free(s->desc_wait);
+ s->desc_wait = NULL;
+ timer_free(s->desc_timeout);
+ s->desc_timeout = NULL;
+
for (i = 0; i < USBREDIR_SERVER_MAX_EP; i++) {
timer_free(s->ep[i].retry);
s->ep[i].retry = NULL;
diff --git a/hw/usb/trace-events b/hw/usb/trace-events
index 917881ab1e..549f5f6e23 100644
--- a/hw/usb/trace-events
+++ b/hw/usb/trace-events
@@ -402,7 +402,14 @@ usbredir_server_log(const char *msg) "%s"
usbredir_server_hello(const char *version) "peer is %s"
usbredir_server_attach(void) "device attached"
usbredir_server_detach(bool announced) "device detached, was announced %d"
+usbredir_server_announce(uint8_t speed, uint16_t vendor, uint16_t product,
uint8_t class) "device_connect speed %u, %04x:%04x, class %u"
+usbredir_server_announce_failed(void) "closing the connection"
+usbredir_server_ask_descriptor(uint8_t type, uint16_t length) "asking the
device for descriptor type %u, %u bytes"
+usbredir_server_submit_descriptor(const char *step) "step %s"
+usbredir_server_descriptor_answer(const char *step, int status, int length)
"step %s status %d length %d"
+usbredir_server_accept_descriptor(const char *step, const char *reason) "step
%s rejected: %s"
usbredir_server_interface_info(uint32_t count) "interface_info for %u
interface(s)"
+usbredir_server_desc_too_long(uint16_t total) "configuration descriptor of %u
bytes is more than we take"
usbredir_server_disconnect(void) "device_disconnect sent"
usbredir_server_bus_reset(bool attached) "bus reset, attached %d"
usbredir_server_filter_reject(void) "host rejected our device"
--
2.43.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |