This series redirects a USB device emulated in one QEMU instance to a USB host controller emulated in another one, over the usbredir protocol, and uses it to bring up the ASPEED AST1030 USB Device Controller (UDC).
It is the second step of a larger plan to model USB device-side support on ASPEED BMC/BIC SoCs, which has three goals: 1. Model the ASPEED UDC (AST2600 / AST1030). The AST2600 also has USB host (EHCI) controllers, so that work targets the AST2600 UDC: its gadget can be attached to the SoC's own EHCI bus, letting the guest enumerate its own gadget and exercise the UDC end-to-end. [Done] https://lore.kernel.org/qemu-devel/[email protected]/ 2. AST1030 UDC. The AST1030 has no USB host controller, so testing its UDC needs a second QEMU instance. The UDC gadget is redirected out of the guest with libusbredir and attached to another QEMU that runs a USB host (a VMM, or an AST2600 / AST2700 guest). [this series] 3. ASPEED vHub, as a longer-term goal towards BMC KVM / Virtual Media support in QEMU. [future] This series implements goal 2. The transport ============= usb-redir-server 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. 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 series) | ^ 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 usb-redir-server 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 usb-redir-server 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. AST1030 gadget to an AST2700 host ================================= QEMU #1 -M ast1030-evb QEMU #2 -M ast2700a2-evb +-----------------------------+ +-----------------------------+ | Zephyr usb_dc_aspeed | | Linux ehci-platform | +--------------+--------------+ +--------------+--------------+ | MMIO | MMIO +--------------v--------------+ +--------------v--------------+ | aspeed.udc | | EHCI3 (usb-bus.3) | +--------------+--------------+ +--------------+--------------+ | USBPacket | USBPacket +--------------v--------------+ +--------------v--------------+ | aspeed.udc-gadget | | usb-redir | +--------------+--------------+ +--------------+--------------+ | USBPacket | +--------------v--------------+ | | usb-redir-server | | +--------------+--------------+ | | | +=========== unix socket ============+ usbredir protocol Testing ======= 1. AST1030 CDC-ACM gadget, enumerated by an AST2700 host -------------------------------------------------------- The device side owns the socket and has to be started first. $ qemu-system-arm -M ast1030-evb -nographic \ -kernel zephyr.bin \ -chardev socket,id=u,path=/tmp/usbredir.sock,server=on,wait=off \ -device usb-redir-server,id=udcredir,chardev=u \ -device aspeed.udc-gadget,bus=udcredir.0,udc=/machine/soc/udc uart:~$ usb enable uart:~$ usb print 1 $ qemu-system-aarch64 -M ast2700a2-evb -nographic \ -drive file=image-bmc,if=mtd,format=raw -snapshot \ -net nic -net user \ -chardev socket,id=r,path=/tmp/usbredir.sock \ -device usb-redir,chardev=r,bus=usb-bus.3 root@ast2700-default:~# lsusb Bus 001 Device 001: ID 1d6b:0002 Linux 6.18.36 ehci_hcd EHCI Host Controller Bus 001 Device 002: ID 2245:1030 ZEPHYR Zephyr DFU sample Data was checked: 2700 -> 1030 echo '1234567890' > /dev/ttyACM0 1030 console: Print Data: 0x31 0x32 ... 0x30 2. A plain usb-storage exported the same way -------------------------------------------- Any USBDevice can be exported, not just the UDC gadget. The machine on the exporting side is only a container for usb-redir-server, so it does not have to run anything. $ qemu-system-arm -M ast2600-evb -nographic -serial null \ -chardev socket,id=u,path=/tmp/storage.sock,server=on,wait=off \ -device usb-redir-server,id=keyredir,chardev=u \ -drive id=usbdisk,if=none,file=disk.img,format=raw -snapshot \ -device usb-storage,bus=keyredir.0,id=mystorage,drive=usbdisk $ qemu-system-aarch64 -M ast2700a2-evb -nographic \ -drive file=image-bmc,if=mtd,format=raw -snapshot \ -net nic -net user \ -chardev socket,id=r,path=/tmp/storage.sock \ -device usb-redir,chardev=r,bus=usb-bus.3 root@ast2700-default:~# lsusb Bus 001 Device 001: ID 1d6b:0002 Linux 6.18.36 ehci_hcd EHCI Host Controller Bus 001 Device 002: ID 46f4:0001 QEMU QEMU USB HARDDRIVE root@ast2700-default:~# mount /dev/sda /mnt/usb MOUNTED A 4 MiB file was written, unmounted and remounted, and the sha256 matched. v1 1. Add a usbredir server transport 1. Add a usbredir server device 2. Announce the exported device 3. Implement control transfers 4. Implement bulk and interrupt transfers 5. Stream interrupt IN endpoints 2. ASPEED UDC 1. Make the SETUP buffer byte addressable 2. Support single-stage IN transfers 3. Clear EP_POOL_ACK when no endpoint ack is left 4. Mask the reserved bits on register writes 5. Use the endpoint max packet size for OUT transfers 6. Test the usbredir server transport 7. Add the USB device controller for AST1030 Jamin Lin (19): hw/usb/bus: Let a bus opt out of automatic hub insertion hw/usb/redirect-server: Add a usbredir server device hw/usb/redirect-server: Connect usbredirparser to a chardev hw/usb/redirect-server: Announce the exported device hw/usb/redirect-server: Implement control transfers hw/usb/redirect-server: Implement bulk and interrupt transfers hw/usb/redirect-server: Stream interrupt IN endpoints hw/arm/aspeed: Enable the usbredir server transport hw/usb/aspeed-udc: Make the SETUP buffer byte addressable hw/usb/aspeed-udc: Mask the reserved bits on register writes hw/usb/aspeed-udc: Clear EP_POOL_ACK when no endpoint ack is left hw/usb/aspeed-udc: Rename the transfer helpers after their DMA mode hw/usb/aspeed-udc: Store the EP_DMA_STS write first hw/usb/aspeed-udc: Pick the EP_DMA_STS path by direction hw/usb/aspeed-udc: Support single-stage IN transfers hw/usb/aspeed-udc: Use the endpoint max packet size for OUT transfers hw/usb/aspeed-udc: Add trace events for the transfer paths hw/arm/aspeed_ast10x0: Add the USB device controller tests/functional/arm/test_aspeed_ast1030: Test the usbredir server transport include/hw/arm/aspeed_soc.h | 1 + include/hw/usb/aspeed-udc.h | 13 +- include/hw/usb/redirect-server.h | 116 ++ include/hw/usb/usb.h | 1 + hw/arm/aspeed.c | 6 + hw/arm/aspeed_ast10x0.c | 17 +- hw/usb/aspeed-udc.c | 290 +++- hw/usb/bus.c | 3 +- hw/usb/redirect-server.c | 1604 +++++++++++++++++++ hw/usb/meson.build | 3 +- hw/usb/trace-events | 36 + tests/functional/arm/meson.build | 1 + tests/functional/arm/test_aspeed_ast1030.py | 52 + 13 files changed, 2088 insertions(+), 55 deletions(-) create mode 100644 include/hw/usb/redirect-server.h create mode 100644 hw/usb/redirect-server.c -- 2.53.0
