> Add "usb-redir-server". It exports a USB device from this QEMU to a USB
> host in another QEMU, using the usbredir protocol. The other side is the
> existing "usb-redir" device.
> 
> The exported device is plugged in with "-device <dev>,bus=<id>.0". So we
> have to provide a USB bus. To find that bus, qdev starts at the main
> system bus and walks down the tree. A device that is not on a bus is not
> in the tree, so the bus it provides cannot be found. We have to sit on
> the main system bus. That makes this a sysbus device, even though it has
> no MMIO and no IRQ.
> 
> usbredir carries one device, not a bus. A hub cannot be exported because
> the protocol has no device address field, so only the device on the
> first port is redirected. To export several devices, run one
> usb-redir-server per device, each with its own chardev.
> 
> The link to the remote host is a chardev, so this device cannot be
> migrated. Its VMStateDescription says so with unmigratable.
> 
> Signed-off-by: Jamin Lin <[email protected]>
> Message-ID: <[email protected]>
>
> diff --git a/hw/usb/meson.build b/hw/usb/meson.build
> index 429dcd6a24f7..0061b935e6bb 100644
> --- a/hw/usb/meson.build
> +++ b/hw/usb/meson.build
> @@ -78,7 +78,8 @@ endif
>  if usbredir.found()
>    usbredir_ss = ss.source_set()
>    usbredir_ss.add(when: 'CONFIG_USB',
> -                  if_true: [usbredir, files('redirect.c', 'quirks.c')])
> +                  if_true: [usbredir, files('redirect.c', 'quirks.c',
> +                                            'redirect-server.c')])
>    hw_usb_modules += {'redirect': usbredir_ss}
>  endif
>  
> diff --git a/hw/usb/redirect-server.c b/hw/usb/redirect-server.c
> new file mode 100644
> index 000000000000..5f365f0a35fc
> --- /dev/null
> +++ b/hw/usb/redirect-server.c
> @@ -0,0 +1,130 @@
> +/*
> + * USB redirector, server side
> + *
> + * Copyright (c) 2026 ASPEED Technology Inc.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Exports a locally emulated USB device to a remote USB host over the
> + * usbredir protocol, so a device emulated in one QEMU instance can be
> + * enumerated by a host controller emulated in another one.
> + *
> + * Architecture. The left column is a request going to the device. The
> + * right column is the answer coming back. The middle hop carries
> + * usbredir messages over a socket. The top and bottom hops carry
> + * USBPackets inside QEMU.
> + *
> + *     remote QEMU: guest driver -> EHCI/XHCI
> + *              |                     ^
> + *    USBPacket |                     | USBPacket
> + *              v                     |
> + *     "usb-redir" (the client)
> + *              |                     ^
> + *     usbredir |   chardev socket    | usbredir
> + *              v                     |
> + *     usb-redir-server (the server, this file)
> + *              |                     ^
> + *    USBPacket |                     | USBPacket
> + *              v                     |
> + *     any USBDevice, "-device <dev>,bus=<id>.0"
> + *
> + * "usb-redir" (hw/usb/redirect.c) is the client:
> + *   - it takes a USBPacket from the remote guest and writes it to the
> + *     socket as a usbredir message
> + *   - it reads the answer from the socket and completes the USBPacket
> + *
> + * This file is the server. It does the same thing, but backwards:
> + *   - it reads a usbredir message from the socket and runs it as a
> + *     USBPacket on the bus below
> + *   - it takes the result of that USBPacket and writes it back to the
> + *     same socket as a usbredir message, for the client to read
> + *
> + * A USB device has to sit on a USB bus, and in QEMU a USB bus is always
> + * made by a host controller. So this file makes one and acts as the
> + * host controller on this side. It models no real chip: its cable is
> + * the chardev socket. The real host is in the other QEMU.
> + *
> + * usbredir carries one device, not a bus. A hub cannot be exported:
> + * the protocol has no device address field. To export several devices,
> + * run one usb-redir-server per device, each with its own chardev.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/module.h"
> +#include "migration/vmstate.h"
> +#include "hw/usb/redirect-server.h"
> +
> +/*
> + * USB port ops
> + */
> +
> +static void usbredir_server_port_child_detach(USBPort *port, USBDevice 
> *child)
> +{
> +    /* We only export the device on our own port. Nothing to do. */
> +}
> +
> +static void usbredir_server_port_wakeup(USBPort *port)
> +{
> +    /* We do not pass remote wakeup to the host. Nothing to do. */
> +}
> +
> +static USBPortOps usbredir_server_port_ops = {
> +    .child_detach = usbredir_server_port_child_detach,
> +    .wakeup = usbredir_server_port_wakeup,
> +};
> +
> +/*
> + * USB bus ops
> + */
> +
> +static USBBusOps usbredir_server_bus_ops = {
> +};
> +
> +/*
> + * Device registration
> + */
> +
> +static void usbredir_server_realize(DeviceState *dev, Error **errp)
> +{
> +    USBRedirServer *s = USB_REDIR_SERVER(dev);
> +
> +    /* One port: usbredir carries a single device. */
> +    usb_bus_new(&s->bus, sizeof(s->bus), &usbredir_server_bus_ops, dev);
> +    s->bus.no_auto_hub = true;
> +    usb_register_port(&s->bus, &s->port, s, 0, &usbredir_server_port_ops,
> +                      USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL |
> +                      USB_SPEED_MASK_HIGH);
> +}
> +
> +/*
> + * The link to the remote host is a chardev, and we cannot migrate
> + * that. So this device cannot be migrated either.
> + */
> +static const VMStateDescription vmstate_usbredir_server = {
> +    .name = TYPE_USB_REDIR_SERVER,
> +    .unmigratable = 1,
> +};
> +
> +static void usbredir_server_class_init(ObjectClass *klass, const void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->desc = "USB Redirection Server";
> +    dc->realize = usbredir_server_realize;
> +    dc->vmsd = &vmstate_usbredir_server;
> +    set_bit(DEVICE_CATEGORY_USB, dc->categories);
> +}
> +
> +static const TypeInfo usbredir_server_types[] = {
> +    {
> +        .name = TYPE_USB_REDIR_SERVER,
> +        .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE,
> +        .instance_size = sizeof(USBRedirServer),
> +        .class_init = usbredir_server_class_init,
> +    },
> +};
> +module_obj(TYPE_USB_REDIR_SERVER);
> +module_kconfig(USB);
> +
> +DEFINE_TYPES(usbredir_server_types)
> diff --git a/include/hw/usb/redirect-server.h 
> b/include/hw/usb/redirect-server.h

What do you need a header for? If there is nothing to share with other units, 
better
keep it all in .c.

-- 
Marc-AndrĂ© Lureau <[email protected]>


Reply via email to