The host backends number their speed enums differently from Linux'
enum usb_device_speed, but both passthrough paths cast them with `as u32`:
through nusb a high-speed device was announced as full-speed and a
SuperSpeedPlus one as wireless; through rusb every SuperSpeed device was
announced as wireless. A client cannot enumerate a SuperSpeed device that
is announced with the wrong speed.
Map nusb::Speed and rusb::Speed to UsbSpeed explicitly instead, falling
back to Unknown for the future variants of those non_exhaustive enums.
The nusb path also hardcoded ep0's max packet size to 16 for every device.
bMaxPacketSize0 is the exponent 9 (= 512 bytes) at SuperSpeed and must be
64 at high speed, so take it from the device's own descriptor, like the
rusb path already did. It also left usb_version at 0, and Linux rejects a
device that claims SuperSpeed with bcdUSB < 0x0300 ("got a wrong device
descriptor, warm reset device"), so propagate bcdUSB as well.
While here:
- report 64 instead of the exponent in the device qualifier descriptor,
which describes high-speed operation (USB 2.0 5.5.3)
- add UsbDevice::set_speed(), which keeps the speed and ep0 in sync, so
simulated SuperSpeed devices can be built correctly (#40)
Verified with a new QEMU test (scripts/qemu/run-qemu-passthrough-test.sh):
QEMU provides a SuperSpeed device (usb-uas on xHCI) and a high-speed one
(usb-storage on EHCI); the guest exports them with both backends and
attaches them back through vhci-hcd. Before these changes it fails with
"ATTACH_TEST: FAIL (46f4:0003 speed=480/5000 bMaxPacketSize0=16/9
bcdUSB=0.00/3.00)" and a rusb SuperSpeed device never enumerates; after
them it passes for both backends (speed, bMaxPacketSize0 and bcdUSB of the
virtual copy match the real device). The example is extended with a
--nusb/--rusb switch and VID:PID filters to drive that test.
linux-modules-extra-generic is not a package name; the packages are versioned
as linux-modules-extra-<ver>-generic. Install it after detecting the generic
kernel version.
The GitHub runner's Azure kernel has CONFIG_USB_HID disabled, so it cannot
enumerate the simulated HID keyboard over USB/IP. Install and boot the Ubuntu
generic kernel instead (which ships usbhid/hid-generic as modules and
usbip/cdc-acm in linux-modules-extra), bundle + load the full module chain
(hid, hid-generic, usbhid, usbip-core, vhci-hcd, cdc-acm) in the guest, and
restore the keyboard check as a gating assertion. Now both the CDC ACM serial
device and the HID keyboard are verified in CI.
The Azure kernel does not load zstd/xz/gzip-compressed modules via insmod, so
decompress them to plain .ko when bundling the initramfs. Also copy the usbip
client to both /usr/sbin and /usr/bin (and its real path), and log its presence
in the guest for diagnostics.
The GitHub runner uses an Azure kernel that does not ship the usbip /
cdc-acm modules by default; on Ubuntu those live in the separate
linux-modules-extra package. Install it and make the initramfs builder /
guest loader accept compressed kernel modules (.ko.zst, .ko.xz, .ko.gz).
Boot a real Linux inside QEMU and confirm that simulated USB/IP devices
work against a real kernel (vhci-hcd + cdc_acm):
- Add examples/demo.rs: a single server exporting a HID keyboard and a
CDC ACM serial device on port 3240.
- Add scripts/qemu/: build a minimal initramfs (host kernel + busybox +
usbip tool + demo server), boot it in QEMU, attach the simulated devices
over 127.0.0.1, and verify:
* the serial port emits 'a' (0x61) -> SERIAL_TEST
* the keyboard generates a KEY_1 input event -> KEYBOARD_TEST
- Add .github/workflows/qemu.yml to run this in CI (KVM when available,
TCG fallback otherwise), uploading the serial console log as an artefact.