From 0ae101fdd02861568611cec077fc48cc72d18a39 Mon Sep 17 00:00:00 2001 From: vasilito Date: Tue, 7 Jul 2026 00:51:54 +0300 Subject: [PATCH] usb-core: add class_driver_for_usb_class and spawn_class_driver_for_port P0-B1 spawn infrastructure from USB-IMPLEMENTATION-PLAN.md v2. Add two new public functions to usb-core::spawn: class_driver_for_usb_class(class, subclass, protocol) -> Option<&str> Maps USB class codes to driver binary paths: 0x03 -> /usr/bin/usbhidd (HID) 0x08 -> /usr/bin/usbscsid (Mass Storage) 0x09 -> /usr/bin/usbhubd (Hub) spawn_class_driver_for_port(...args...) Spawns the correct class daemon with the right argv layout: - HID/Hub: - Storage: The existing spawn_usb_driver is preserved for backward compatibility. Both new functions have no_std stubs so the crate still compiles without the std feature. Next: wire the spawn call into ehcid after device enumeration (P0-B1 call site) + add xhcid-compatible scheme paths (descriptors/request/ endpoints/attach) to ehcid's scheme handler so the spawned daemons can open XhciClientHandle successfully. --- .../drivers/usb-core/source/src/lib.rs | 2 + .../drivers/usb-core/source/src/spawn.rs | 80 ++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/local/recipes/drivers/usb-core/source/src/lib.rs b/local/recipes/drivers/usb-core/source/src/lib.rs index 1779c10b82..1c1a6e3278 100644 --- a/local/recipes/drivers/usb-core/source/src/lib.rs +++ b/local/recipes/drivers/usb-core/source/src/lib.rs @@ -12,6 +12,8 @@ pub mod types; pub use dma::{DmaBuffer, DmaError}; pub use scheme::{UsbError, UsbHostController}; pub use spawn::spawn_usb_driver; +pub use spawn::class_driver_for_usb_class; +pub use spawn::spawn_class_driver_for_port; pub use transfer::{ control_transfer, parse_config_descriptor, parse_device_descriptor, parse_endpoint_descriptor, }; diff --git a/local/recipes/drivers/usb-core/source/src/spawn.rs b/local/recipes/drivers/usb-core/source/src/spawn.rs index 06a6700532..cb4e3eff01 100644 --- a/local/recipes/drivers/usb-core/source/src/spawn.rs +++ b/local/recipes/drivers/usb-core/source/src/spawn.rs @@ -1,5 +1,68 @@ -/// Spawn a child USB class driver (hub, HID, storage). -/// On Redox, this forks and execs the driver binary with the USB device path. +/// Map a USB class/subclass/protocol triplet to the corresponding class driver +/// binary path. Returns `None` if no driver is registered for this class. +/// +/// USB class codes from : +/// 0x03 — HID (Human Interface Device) +/// 0x08 — Mass Storage +/// 0x09 — Hub +pub fn class_driver_for_usb_class(class: u8, _subclass: u8, _protocol: u8) -> Option<&'static str> { + match class { + 0x03 => Some("/usr/bin/usbhidd"), + 0x08 => Some("/usr/bin/usbscsid"), + 0x09 => Some("/usr/bin/usbhubd"), + _ => None, + } +} + +/// Spawn a class driver for a USB device port. +/// +/// `scheme_name` — the host-controller scheme name (e.g. `"usb"` for ehcid, +/// `"usb.0000:00:14.0_xhci"` for xhcid). +/// `port` — the port number (string form, as passed to the class daemon). +/// `iface_num` — the interface number within the configuration. +/// `protocol` — the interface protocol byte (mass storage uses this). +/// +/// The driver binary is selected from `class`, spawned with +/// ` ` as arguments, and disconnected +/// from stdin. The caller should own the scheme registration that the class +/// daemon will open. +#[cfg(feature = "std")] +pub fn spawn_class_driver_for_port( + class: u8, + subclass: u8, + protocol: u8, + scheme_name: &str, + port: &str, + iface_num: u8, +) { + let Some(driver_binary) = class_driver_for_usb_class(class, subclass, protocol) else { + return; + }; + + // Class daemons expect different argv layout. + // usbhidd: + // usbscsid: + // usbhubd: + // + // For HID and Hub, we pass the interface number. For Storage, pass the + // bInterfaceProtocol byte (CBI, BOT, UAS, etc.). + let extra_arg = match class { + 0x08 => protocol.to_string(), + _ => iface_num.to_string(), + }; + + let mut cmd = std::process::Command::new(driver_binary); + cmd.env_clear(); + cmd.stdin(std::process::Stdio::null()); + cmd.arg(scheme_name); + cmd.arg(port); + cmd.arg(&extra_arg); + let _ = cmd.spawn(); +} + +/// Spawn a child USB class driver by binary path and a single device-path +/// argument. Kept for backward compatibility; new code should prefer +/// `spawn_class_driver_for_port`. #[cfg(feature = "std")] pub fn spawn_usb_driver(driver_binary: &str, device_path: &str) { if driver_binary.is_empty() @@ -31,6 +94,19 @@ fn is_trusted_usb_driver(driver_binary: &str) -> bool { #[cfg(not(feature = "std"))] pub fn spawn_usb_driver(_driver_binary: &str, _device_path: &str) {} +/// On no_std builds, class-driver spawning is not available. +#[cfg(not(feature = "std"))] +pub fn class_driver_for_usb_class(_class: u8, _subclass: u8, _protocol: u8) -> Option<&'static str> { + None +} + +/// On no_std builds, class-driver spawning is not available. +#[cfg(not(feature = "std"))] +pub fn spawn_class_driver_for_port( + _class: u8, _subclass: u8, _protocol: u8, + _scheme_name: &str, _port: &str, _iface_num: u8, +) {} + #[cfg(all(test, feature = "std"))] mod tests { use super::is_trusted_usb_driver;