|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v8 13/18] hw/usb/redirect-server: Announce the exported device
Send device_connect when a device is attached and the host has greeted
us, device_disconnect when it goes away, and handle the bus reset the
host sends before enumerating.
device_connect carries no identity. The peer asks the device itself with
GET_DESCRIPTOR as soon as it enumerates, so anything we put there would
be made up. The speed is real: it comes from the device on our port.
Signed-off-by: Jamin Lin <jamin_lin@xxxxxxxxxxxxxx>
---
hw/usb/redirect-server.c | 150 ++++++++++++++++++++++++++++++++++++++-
hw/usb/trace-events | 7 ++
2 files changed, 156 insertions(+), 1 deletion(-)
diff --git a/hw/usb/redirect-server.c b/hw/usb/redirect-server.c
index f78351bde9..e207f5a7eb 100644
--- a/hw/usb/redirect-server.c
+++ b/hw/usb/redirect-server.c
@@ -56,6 +56,7 @@
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "migration/vmstate.h"
+#include "qemu/cutils.h"
#include "hw/core/sysbus.h"
#include "hw/usb/usb.h"
#include "chardev/char-fe.h"
@@ -85,17 +86,105 @@ struct USBRedirServer {
/* usbredir over the chardev */
struct usbredirparser *parser;
QEMUBH *chardev_close_bh;
+ QEMUBH *announce_bh;
const uint8_t *read_buf;
int read_buf_size;
bool in_write;
guint watch;
bool host_connected;
+ bool device_announced;
};
+/*
+ * The device is whatever USBDevice the user plugged into our port with
+ * "-device <device>,bus=<id>.0". NULL until then.
+ */
+static USBDevice *usbredir_server_device(USBRedirServer *s)
+{
+ return s->port.dev;
+}
+
+/*
+ * Device announcement
+ */
+
+static uint8_t usbredir_server_speed(USBDevice *device)
+{
+ switch (device->speed) {
+ case USB_SPEED_LOW:
+ return usb_redir_speed_low;
+ case USB_SPEED_FULL:
+ return usb_redir_speed_full;
+ default:
+ return usb_redir_speed_high;
+ }
+}
+
+static void usbredir_server_announce_device(void *opaque)
+{
+ USBRedirServer *s = opaque;
+ USBDevice *device = usbredir_server_device(s);
+ struct usb_redir_interface_info_header iface_info = {
+ .interface_count = 0,
+ };
+ struct usb_redir_device_connect_header conn = {};
+
+ if (!s->host_connected || s->device_announced ||
+ !device || !device->attached) {
+ return;
+ }
+ s->device_announced = true;
+ conn.speed = usbredir_server_speed(device);
+
+ /*
+ * The device has to be in DEFAULT state before it can answer
+ * anything, so reset it here.
+ */
+ usb_device_reset(device);
+
+ /* Send this before device_connect. The peer needs it to accept us. */
+ usbredirparser_send_interface_info(s->parser, &iface_info);
+ usbredirparser_do_write(s->parser);
+
+ trace_usbredir_server_announce(conn.speed);
+ usbredirparser_send_device_connect(s->parser, &conn);
+ usbredirparser_do_write(s->parser);
+}
+
/*
* USB port ops
*/
+static void usbredir_server_port_attach(USBPort *port)
+{
+ USBRedirServer *s = port->opaque;
+
+ trace_usbredir_server_attach();
+
+ /*
+ * usb_attach() sets the state to ATTACHED after this returns.
+ * A device needs DEFAULT state before it can serve a request.
+ * The BH runs after usb_attach(), so the device stays in DEFAULT.
+ */
+ qemu_bh_schedule(s->announce_bh);
+}
+
+static void usbredir_server_port_detach(USBPort *port)
+{
+ USBRedirServer *s = port->opaque;
+
+ trace_usbredir_server_detach(s->device_announced);
+
+ qemu_bh_cancel(s->announce_bh);
+
+ if (s->host_connected && s->parser && s->device_announced) {
+ trace_usbredir_server_disconnect();
+ usbredirparser_send_device_disconnect(s->parser);
+ usbredirparser_do_write(s->parser);
+ }
+ s->device_announced = false;
+}
+
static void usbredir_server_port_child_detach(USBPort *port, USBDevice *child)
{
/* We only export the device on our own port. Nothing to do. */
@@ -107,6 +196,8 @@ static void usbredir_server_port_wakeup(USBPort *port)
}
static USBPortOps usbredir_server_port_ops = {
+ .attach = usbredir_server_port_attach,
+ .detach = usbredir_server_port_detach,
.child_detach = usbredir_server_port_child_detach,
.wakeup = usbredir_server_port_wakeup,
};
@@ -210,13 +301,53 @@ static int usbredir_server_write(void *priv, uint8_t
*data, int count)
return ret;
}
-/* The remote host greets us once the socket is up. */
+/*
+ * usbredirparser message callbacks
+ */
+
static void usbredir_server_hello(void *priv,
struct usb_redir_hello_header *hello)
{
USBRedirServer *s = priv;
+ char version[sizeof(hello->version) + 1];
+
+ pstrcpy(version, sizeof(version), hello->version);
+ trace_usbredir_server_hello(version);
s->host_connected = true;
+ qemu_bh_schedule(s->announce_bh);
+}
+
+static void usbredir_server_reset(void *priv)
+{
+ USBRedirServer *s = priv;
+ USBDevice *device = usbredir_server_device(s);
+
+ trace_usbredir_server_bus_reset(device && device->attached);
+ usb_device_reset(device);
+}
+
+static void usbredir_server_filter_reject(void *priv)
+{
+ trace_usbredir_server_filter_reject();
+}
+
+static void usbredir_server_filter_filter(void *priv,
+ struct usbredirfilter_rule *rules, int rules_count)
+{
+ /* We accept any host. The callback owns the rules, so free them. */
+ free(rules);
+}
+
+static void usbredir_server_device_disconnect_ack(void *priv)
+{
+ /* The host saw our device_disconnect. Nothing to do. */
+}
+
+static void usbredir_server_interface_info(void *priv,
+ struct usb_redir_interface_info_header *hdr)
+{
+ /* The host should not send this to a device. Nothing to do. */
}
/*
@@ -240,6 +371,14 @@ static void usbredir_server_create_parser(USBRedirServer
*s)
/* Callbacks for messages the remote host sends to us */
s->parser->hello_func = usbredir_server_hello;
+ s->parser->reset_func = usbredir_server_reset;
+
+ /* The parser calls these directly, so they must not be NULL. */
+ s->parser->filter_reject_func = usbredir_server_filter_reject;
+ s->parser->filter_filter_func = usbredir_server_filter_filter;
+ s->parser->device_disconnect_ack_func =
+ usbredir_server_device_disconnect_ack;
+ s->parser->interface_info_func = usbredir_server_interface_info;
/* Capabilities: 64-bit IDs, connect_device_version, ep_info sizes */
usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
@@ -267,7 +406,9 @@ static void usbredir_server_create_parser(USBRedirServer *s)
static void usbredir_server_destroy_parser(USBRedirServer *s)
{
s->host_connected = false;
+ s->device_announced = false;
+ qemu_bh_cancel(s->announce_bh);
g_clear_handle_id(&s->watch, g_source_remove);
if (s->parser) {
@@ -368,6 +509,8 @@ static void usbredir_server_realize(DeviceState *dev, Error
**errp)
s->chardev_close_bh = qemu_bh_new_guarded(usbredir_server_chardev_close_bh,
s, &dev->mem_reentrancy_guard);
+ s->announce_bh = qemu_bh_new_guarded(usbredir_server_announce_device, s,
+ &dev->mem_reentrancy_guard);
qemu_chr_fe_set_handlers(&s->cs,
usbredir_server_chardev_can_read,
@@ -387,6 +530,11 @@ static void usbredir_server_unrealize(DeviceState *dev)
qemu_bh_delete(s->chardev_close_bh);
s->chardev_close_bh = NULL;
}
+
+ if (s->announce_bh) {
+ qemu_bh_delete(s->announce_bh);
+ s->announce_bh = NULL;
+ }
}
static const Property usbredir_server_props[] = {
diff --git a/hw/usb/trace-events b/hw/usb/trace-events
index d141661673..2cc6a244b9 100644
--- a/hw/usb/trace-events
+++ b/hw/usb/trace-events
@@ -399,3 +399,10 @@ usbredir_server_chardev_open(void) "chardev opened"
usbredir_server_chardev_close(void) "chardev closed"
usbredir_server_write_recursion(void) "recursive write, leaving it queued"
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) "device_connect speed %u"
+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 |