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: <scheme> <port> <interface_num>
- Storage: <scheme> <port> <protocol_byte>
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.
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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 <https://www.usb.org/defined-class-codes>:
|
||||
/// 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
|
||||
/// `<scheme_name> <port> <iface_num_or_protocol>` 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: <scheme> <port> <interface>
|
||||
// usbscsid: <scheme> <port> <protocol>
|
||||
// usbhubd: <scheme> <port> <interface>
|
||||
//
|
||||
// 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;
|
||||
|
||||
Reference in New Issue
Block a user