187 Commits

Author SHA1 Message Date
Jiajie Chen
d5774284bf host: fix nusb transfers and pass through SuperSpeed descriptors
The nusb host path panicked on every non-empty bulk/interrupt OUT transfer:
endpoint.allocate() returns a Buffer whose len is 0, so copy_from_slice()
wrote into an empty slice. Use extend_from_slice() instead.

nusb also rejects IN transfers whose length is not a multiple of the endpoint
max packet size; a 36-byte SCSI INQUIRY on a 512-byte bulk endpoint made
usb-storage repeat resets. Round the request up and truncate the completion
back to the requested length.

While here, fix descriptor passthrough for USB 3 devices:
- encode bcdUSB/bcdDevice and the device qualifier as real BCD, keeping the
  sub-minor digit
- pass through endpoint extra descriptors (SuperSpeed endpoint companion, UAS
  pipe usage) so the client sees the real endpoint capabilities
- pass through the BOS descriptor so the SuperSpeed capability and its LPM
  latencies are not lost
- claim interfaces in the rusb backend; usbfs logged "did not claim interface"

Extend the QEMU passthrough test with a SuperSpeed bulk-only device
(usb-bot) for the data path, in addition to the SuperSpeed UAS device (which
can only be enumerated because vhci-hcd lacks streams) and the high-speed
usb-storage device. The test now writes and reads back a block through both
bulk-only devices with the nusb and rusb backends, and fails if the client
reports missing endpoint companions or LPM information.

Fixes #66
2026-11-09 15:47:13 +08:00
Jiajie Chen
f78a13ded3 qemu: also load xhci-pci-renesas for the passthrough test
On Ubuntu's 6.8 generic kernel xhci-pci.ko depends on xhci-pci-renesas.ko
(it calls renesas_xhci_check_request_fw), so insmod'ing xhci-pci alone failed
with 'unknown symbol in module' and the SuperSpeed device never appeared.
Verified against the exact CI kernel (linux-image/-modules/-modules-extra
6.8.0-149-generic) locally.

Also dump dmesg when the QEMU devices do not show up, so a failure like this
is diagnosable from the serial log alone.
2026-11-07 19:20:42 +08:00
Jiajie Chen
69c91f50a0 qemu: load the host controller drivers in the passthrough test
On Debian/Ubuntu kernels ehci-hcd/ehci-pci and xhci-hcd/xhci-pci are modules,
so without loading them the emulated USB devices QEMU provides never show up
and the test complained about a single exported device (none at all on a
Debian kernel). Bundle and insmod them, and wait for both devices to appear
before starting the server.
2026-11-07 19:02:47 +08:00
Jiajie Chen
f1e5a83264 Fix host passthrough speed and ep0 max packet size (#66)
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.
2026-11-07 18:45:30 +08:00
Jiajie Chen
458e3d480a Fix usbspeed conversion between enums 2026-11-07 18:02:39 +08:00
Jiajie Chen
61938f8c48 Merge branch 'feat/qemu-ci-test' 2026-09-04 02:06:28 +08:00
Jiajie Chen
c9c25e6ffe Install versioned linux-modules-extra for the generic kernel
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.
2026-09-04 02:01:11 +08:00
Jiajie Chen
defc096808 Boot the Ubuntu generic kernel in CI and load HID modules to verify the keyboard
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.
2026-09-04 01:59:42 +08:00
Jiajie Chen
bb665f5da4 Harden QEMU test: kernel overrides, best-effort HID check, cleaner diagnostics
- Allow overriding KERNEL / VMLINUZ / MODTREE so the test can run against a
  downloaded kernel (e.g. to reproduce a CI kernel locally).
- Place decompressed kernel modules under /lib/modules/$KERNEL regardless of
  where they are found.
- Make the HID-keyboard check best-effort: it is reported but does not gate the
  result, because some kernels (Ubuntu azure/generic) do not complete HID
  enumeration over USB/IP. The CDC ACM serial device remains the always-verified
  device.
- Share the initramfs builder's MODTREE setting with run-qemu-test.sh.
2026-09-04 01:45:48 +08:00
Jiajie Chen
ed1deb31d9 Capture dmesg from the start for usb 1-1 diagnostics 2026-09-04 01:16:18 +08:00
Jiajie Chen
8bb0dc8f24 Dump full usb/dmesg diagnostics for the keyboard device 2026-09-04 01:11:59 +08:00
Jiajie Chen
bce97bdd6e Dump server.log panics and HID/cdc activity in the QEMU guest 2026-09-04 01:07:18 +08:00
Jiajie Chen
b5ce7dc31e HID: handle standard control requests instead of panicking
The simulated HID keyboard handler called unimplemented!() on HID control
requests it did not recognise (e.g. SET_PROTOCOL or a GET_DESCRIPTOR for the
HID descriptor), which panicked the connection task and stalled enumeration on
kernels whose usbhid core issues those requests (e.g. the Ubuntu Azure kernel).
Answer them gracefully (empty response / the HID descriptor) and add a
regression test.
2026-09-04 01:02:11 +08:00
Jiajie Chen
8fd6c2477f Prefer a real (ELF) usbip binary over the linux-tools shell wrapper
On Ubuntu, linux-tools-common installs /usr/bin/usbip as a tiny shell script
that execs a versioned binary, which is why the guest reported 'usbip: not
found'. Locate a real ELF usbip binary (e.g. /usr/sbin/usbip from the usbip
package, or /usr/lib/linux-tools/*/usbip) and bundle that instead.
2026-09-04 00:55:51 +08:00
Jiajie Chen
ff738948ec Decompress kernel modules and harden the usbip client copy
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.
2026-09-04 00:51:13 +08:00
Jiajie Chen
749e32e4a9 Copy the root-only kernel image via sudo when it is not readable
On GitHub runners /boot/vmlinuz-* is mode 0600 (root-only), so a plain cp by
the non-root runner user fails. Fall back to sudo cp + chmod so QEMU can read
the kernel.
2026-09-04 00:44:51 +08:00
Jiajie Chen
d442235aaa Run QEMU via sudo and use a readable copy of the kernel
On GitHub runners the kernel image (/boot/vmlinuz-*) is root-only and
/dev/kvm is not writable by the runner user. Copy the kernel to a readable
temp path, and when /dev/kvm needs root, run QEMU under passwordless sudo to
get KVM acceleration (falling back to TCG). Ensure the serial log stays
readable for the artifact upload.
2026-09-04 00:41:10 +08:00
Jiajie Chen
bbd74e5bd8 Make initramfs library bundling robust
ldd can return non-zero / empty for statically-linked or symlinked binaries
(e.g. the distro usbip tool), which under set -o pipefail aborted the build.
Copy the dynamic loader plus a comprehensive set of libc/usbip libraries
explicitly, and drive the ldd-based copy via process substitution so it
never aborts. Also follow the usbip symlink and preserve its real path.
2026-09-04 00:36:33 +08:00
Jiajie Chen
c374ad1017 Temporarily trace build-initramfs with set -x for debugging 2026-09-04 00:31:37 +08:00
Jiajie Chen
2dc5a8fab1 Fix initramfs build with large module trees
Under set -o pipefail, 'find ... | head -1' aborts with SIGPIPE once head
reads one line, which happened after installing linux-modules-extra (a large
tree). Use find -print -quit instead of piping to head, and add an ERR trap
for clearer diagnostics.
2026-09-04 00:27:27 +08:00
Jiajie Chen
8d33603873 Install linux-modules-extra and handle compressed kernel modules
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).
2026-09-04 00:22:32 +08:00
Jiajie Chen
411e63f8e8 Add QEMU end-to-end test verifying simulated USB devices
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.
2026-09-04 00:16:40 +08:00
Jiajie Chen
65c3d1864d Bump dependencies 2026-08-13 09:28:02 +08:00
Jiajie Chen
5561aa4abf Release v0.9.0 v0.9.0 2026-07-21 23:39:42 +08:00
Jiajie Chen
8e6bca49dd Address review comments: restore Direction re-export, clarify device_handler comments 2026-07-21 23:38:29 +08:00
Chen
1b5219e390 Merge pull request #64 from AryanMadhavVerma/fix/host-passthrough-crashes
Fix two host-passthrough crashes hit by real USB-serial devices
2026-07-21 23:35:59 +08:00
Jiajie Chen
66b7503f33 Fix formatting for string descriptor warnings 2026-07-21 23:34:01 +08:00
Jiajie Chen
1c3a03888c Include device identity in string descriptor read failure warnings 2026-07-21 23:31:40 +08:00
Jiajie Chen
aaff681bbe Remove unused re-export of rusb::Direction to fix clippy CI 2026-07-21 23:30:42 +08:00
AryanMadhavVerma
d6747146f6 Fix two host-passthrough crashes hit by real USB-serial devices
1. src/device.rs: control transfer dispatch only forwarded requests to
   device_handler when the recipient bit was Device (0), even though
   device_handler implementations (RusbUsbHostDeviceHandler,
   NusbUsbHostDeviceHandler) reconstruct the full recipient from
   setup_packet.request_type themselves and pass it straight through to
   the real transfer - they're correct for any recipient already.
   Endpoint/Other-recipient control transfers fell through to
   unimplemented!("control in"/"control out") instead, panicking the
   worker task handling that transfer. Some USB-serial chips (observed:
   WCH's CH34x/CH9102 family, widely used in hobbyist robotics/serial
   adapters) use vendor-specific, non-Device-recipient control transfers
   to configure line state, and hit this on every connection attempt.

2. src/lib.rs: with_rusb_device_handles() read each device's manufacturer/
   product/serial-number string descriptors with .unwrap(), which is not
   guaranteed to succeed (some devices NAK a string descriptor read, or
   don't support the requested language ID). A single device failing this
   panicked the whole synchronous enumeration pass in new_from_host(),
   taking down every other device on the bus along with it. Now logs a
   warning and leaves that one string unset instead.

Both were found while passing a physical CH9102-based USB-serial device
through to a Docker container via the `host` example. The recipient
restriction in (1) was confirmed as the cause by inspecting
NusbUsbHostDeviceHandler::handle_urb, which already reconstructs
Device/Interface/Endpoint/Other recipients generically from the raw
setup packet - nothing in the handler itself required narrowing the
caller's guard to Device only.
2026-07-21 20:31:13 +05:30
Chen
dbc8ffdb9c Merge pull request #62 from jiegec/fix-out-actual-length
Fix: correct actual_length for OUT transfers in USBIP_RET_SUBMIT
2026-05-13 09:25:43 +08:00
Jiajie Chen
ed51a26ba9 Address review comments:
- Add debug_assert that OUT transfers return empty resp buffer
- Pass resp directly (assertion ensures it's empty for OUT)
- Fix copy-paste doc comments: OP_REP_IMPORT -> USBIP_RET_SUBMIT/UNLINK
2026-05-03 23:29:56 +08:00
Jiajie Chen
97e819ce62 fix(usbip): set actual_length correctly for OUT transfers
For OUT transfers, actual_length should reflect bytes written to device,
not response buffer length (which is empty by design). This matches the
Linux kernel USB/IP implementation where actual_length = urb->actual_length.

Fixes #61
2026-05-03 23:08:16 +08:00
Jiajie Chen
0878920532 Release v0.8.0 2026-01-27 13:00:46 +08:00
Jiajie Chen
a19bfe8301 Simplify documentation (fixes #59)
Remove redundant sections from README and update LICENSE copyright year.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2025-12-25 20:58:48 +08:00
Jiajie Chen
c8cd214e1d Fix clippy warning: cloned_ref_to_slice_refs
Replace &[device.clone()] with std::slice::from_ref(&device) in
test to address clippy::cloned_ref_to_slice_refs warning.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2025-12-25 20:52:45 +08:00
Jiajie Chen
48d0d9ca34 Fix bus num on non-Linux platform 2025-12-24 23:23:25 +08:00
Jiajie Chen
b83f584391 Handle upper case in hid keyboard report 2025-12-24 23:18:11 +08:00
Jiajie Chen
fdbb9574aa Fix missing max packet size handling 2025-12-24 23:17:29 +08:00
Jiajie Chen
3ff5df5c2e Migrate to nusb 0.2.1 2025-12-24 23:17:02 +08:00
Jiajie Chen
156ec45a81 Apply more cargo clippy 2025-08-01 16:38:47 +08:00
Jiajie Chen
e14858f918 Apply cargo clippy 2025-08-01 16:35:06 +08:00
Jiajie Chen
ad1537a0f8 Add more comments in descriptor handling 2025-08-01 16:05:33 +08:00
Jiajie Chen
664d9ee143 Run cargo fmt 2025-06-13 18:26:58 +08:00
Jiajie Chen
5d49acaa2e Run clippy fix 2025-06-13 17:57:50 +08:00
Jiajie Chen
a584b3713d Add stale bot 2025-06-13 17:17:59 +08:00
Jiajie Chen
c4f7a376b5 Drop broken coveralls 2025-03-24 14:15:43 +08:00
Jiajie Chen
276acbbb23 Format code 2025-03-24 14:10:35 +08:00
Jiajie Chen
bd7c558ee1 Fix clippy 2025-03-24 14:03:53 +08:00
Jiajie Chen
c622dd9763 Format code 2025-03-24 13:50:04 +08:00