From: Jamin Lin <jamin_lin@aspeedtech.com>
To: "Cédric Le Goater" <clg@kaod.org>,
"Peter Maydell" <peter.maydell@linaro.org>,
"Steven Lee" <steven_lee@aspeedtech.com>,
"Troy Lee" <leetroy@gmail.com>,
"Kane Chen" <kane_chen@aspeedtech.com>,
"Andrew Jeffery" <andrew@codeconstruct.com.au>,
"Joel Stanley" <joel@jms.id.au>,
"open list:ASPEED BMCs" <qemu-arm@nongnu.org>,
"open list:All patches CC here" <qemu-devel@nongnu.org>
Cc: Jamin Lin <jamin_lin@aspeedtech.com>, Troy Lee <troy_lee@aspeedtech.com>
Subject: [PATCH v3 4/8] hw/usb/redirect-server: Announce the exported device
Date: Mon, 12 Oct 2026 02:16:40 +0000 [thread overview]
Message-ID: <20261012021634.2006424-5-jamin_lin@aspeedtech.com> (raw)
In-Reply-To: <20261012021634.2006424-1-jamin_lin@aspeedtech.com>
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@aspeedtech.com>
---
hw/usb/redirect-server.c | 130 ++++++++++++++++++++++++++++++++++++++-
hw/usb/trace-events | 7 +++
2 files changed, 136 insertions(+), 1 deletion(-)
diff --git a/hw/usb/redirect-server.c b/hw/usb/redirect-server.c
index a48b821642..05ee0f3873 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"
@@ -90,12 +91,88 @@ struct USBRedirServer {
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(USBRedirServer *s)
+{
+ 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);
+
+ /* Put the device in DEFAULT state. The host may not reset the bus. */
+ device->addr = 0;
+ device->state = USB_STATE_DEFAULT;
+
+ /* 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();
+ usbredir_server_announce_device(s);
+}
+
+static void usbredir_server_port_detach(USBPort *port)
+{
+ USBRedirServer *s = port->opaque;
+
+ trace_usbredir_server_detach(s->device_announced);
+
+ 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 +184,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 +289,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;
+ usbredir_server_announce_device(s);
+}
+
+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 +359,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,6 +394,7 @@ static void usbredir_server_create_parser(USBRedirServer *s)
static void usbredir_server_destroy_parser(USBRedirServer *s)
{
s->host_connected = false;
+ s->device_announced = false;
g_clear_handle_id(&s->watch, g_source_remove);
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.53.0
next prev parent reply other threads:[~2026-10-12 2:17 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-12 2:16 [PATCH v3 0/8] hw/usb: Add a usbredir server transport Jamin Lin
2026-10-12 2:16 ` [PATCH v3 1/8] hw/usb/bus: Let a bus opt out of automatic hub insertion Jamin Lin
2026-10-12 2:16 ` [PATCH v3 2/8] hw/usb/redirect-server: Add a usbredir server device Jamin Lin
2026-10-12 10:01 ` marcandre.lureau
2026-10-12 2:16 ` [PATCH v3 3/8] hw/usb/redirect-server: Connect usbredirparser to a chardev Jamin Lin
2026-10-12 2:16 ` Jamin Lin [this message]
2026-10-12 10:01 ` [PATCH v3 4/8] hw/usb/redirect-server: Announce the exported device marcandre.lureau
2026-10-12 2:16 ` [PATCH v3 5/8] hw/usb/redirect-server: Implement control transfers Jamin Lin
2026-10-12 10:01 ` marcandre.lureau
2026-10-13 1:30 ` Jamin Lin
2026-10-12 2:16 ` [PATCH v3 6/8] hw/usb/redirect-server: Implement bulk and interrupt transfers Jamin Lin
2026-10-12 10:01 ` marcandre.lureau
2026-10-12 2:16 ` [PATCH v3 7/8] hw/usb/redirect-server: Stream interrupt IN endpoints Jamin Lin
2026-10-12 10:01 ` marcandre.lureau
2026-10-12 2:16 ` [PATCH v3 8/8] hw/arm/aspeed: Enable the usbredir server transport Jamin Lin
2026-10-12 10:01 ` marcandre.lureau
2026-10-12 2:19 ` [PATCH v3 0/8] hw/usb: Add a " Jamin Lin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261012021634.2006424-5-jamin_lin@aspeedtech.com \
--to=jamin_lin@aspeedtech.com \
--cc=andrew@codeconstruct.com.au \
--cc=clg@kaod.org \
--cc=joel@jms.id.au \
--cc=kane_chen@aspeedtech.com \
--cc=leetroy@gmail.com \
--cc=peter.maydell@linaro.org \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=steven_lee@aspeedtech.com \
--cc=troy_lee@aspeedtech.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.