ehcid: P0-B1 — xhcid-compat scheme paths + auto-spawn class drivers

Add xhcid-compatible path aliases to the EHCI scheme handler so that
class daemons (usbhubd, usbhidd, usbscsid) can successfully open an
XhciClientHandle on scheme "usb".

  resolve_root_path / resolve_port_child:
    "descriptors" → aliases "descriptor"
    "request"     → aliases "control"
    "attach"      → new write-only stub (logs port attach)
    "endpoints"   → new stub (resolves to PortDir so openat succeeds;
                       child reads/writes return empty, enough for
                       class daemon polling to work)

  Directory listing updated with the new entries.

  After device enumeration (setup_port_device), call
  usb_core::spawn::spawn_class_driver_for_port to auto-spawn the
  matching class daemon.  HID (0x03), Mass Storage (0x08), and Hub
  (0x09) are mapped to their respective binaries.

This is P0-B1 from USB-IMPLEMENTATION-PLAN.md v2.  Real endpoint
transfer through the Endpoints stub is follow-up work (P0-B1-compat).
This commit is contained in:
2026-07-07 00:58:03 +03:00
parent 0ae101fdd0
commit f7f2890f4e
+47 -6
View File
@@ -113,6 +113,10 @@ enum HandleKind {
Descriptor { port: usize },
Control { port: usize },
Config { port: usize },
/// xhcid-compat: signals a class driver attached to this port (write-only stub)
Attach { port: usize },
/// xhcid-compat: endpoint transfer handle (stub — always returns Ok(0))
Endpoints { port: usize },
}
#[derive(Clone, Debug)]
@@ -562,6 +566,24 @@ impl EhciController {
self.ports[port].device = Some(device);
self.ports[port].last_error = None;
// P0-B1: auto-spawn class drivers (usbhubd, usbhidd, usbscsid)
// for devices enumerated on this port. The class daemons will open
// an XhciClientHandle on scheme "usb", which our compat aliases
// (descriptors, request, attach, endpoints) now serve.
if let Some(ref dev) = self.ports[port].device {
if !dev.device_descriptor.is_empty() {
usb_core::spawn::spawn_class_driver_for_port(
dev.device_class,
dev.device_subclass,
dev.device_protocol,
"usb",
&format!("{}", port + 1),
0,
);
}
}
Ok(())
}
@@ -1126,9 +1148,15 @@ impl EhciScheme {
match (parts.next(), parts.next()) {
(None, None) => Ok(HandleKind::PortDir { port }),
(Some("status"), None) => Ok(HandleKind::Status { port }),
(Some("descriptor"), None) => Ok(HandleKind::Descriptor { port }),
(Some("control"), None) => Ok(HandleKind::Control { port }),
(Some("descriptor"), None) | (Some("descriptors"), None) => {
Ok(HandleKind::Descriptor { port })
}
(Some("control"), None) | (Some("request"), None) => {
Ok(HandleKind::Control { port })
}
(Some("config"), None) => Ok(HandleKind::Config { port }),
(Some("attach"), None) => Ok(HandleKind::Attach { port }),
(Some("endpoints"), Some(_)) => Ok(HandleKind::Endpoints { port }),
_ => Err(SysError::new(ENOENT)),
}
}
@@ -1136,9 +1164,11 @@ impl EhciScheme {
fn resolve_port_child(&self, port: usize, path: &str) -> SysResult<HandleKind> {
match path {
"status" => Ok(HandleKind::Status { port }),
"descriptor" => Ok(HandleKind::Descriptor { port }),
"control" => Ok(HandleKind::Control { port }),
"descriptor" | "descriptors" => Ok(HandleKind::Descriptor { port }),
"control" | "request" => Ok(HandleKind::Control { port }),
"config" => Ok(HandleKind::Config { port }),
"attach" => Ok(HandleKind::Attach { port }),
"endpoints" => Ok(HandleKind::PortDir { port }), // open as O_DIRECTORY, then openat into child
_ => Err(SysError::new(ENOENT)),
}
}
@@ -1257,11 +1287,12 @@ impl EhciScheme {
let handle = self.handle(id)?;
match &handle.kind {
HandleKind::PortDir { .. } => Ok(b"status\ndescriptor\ncontrol\nconfig\n".to_vec()),
HandleKind::PortDir { .. } => Ok(b"status\ndescriptor\ndescriptors\ncontrol\nrequest\nconfig\nattach\nendpoints\n".to_vec()),
HandleKind::Status { port } => self.status_bytes(*port),
HandleKind::Descriptor { port } => self.descriptor_bytes(*port),
HandleKind::Control { .. } => Ok(handle.response.clone()),
HandleKind::Config { port } => self.config_bytes(*port),
HandleKind::Attach { .. } | HandleKind::Endpoints { .. } => Ok(Vec::new()),
}
}
@@ -1279,6 +1310,8 @@ impl EhciScheme {
}
HandleKind::Control { port } => format!("{SCHEME_NAME}:/port{}/control", port + 1),
HandleKind::Config { port } => format!("{SCHEME_NAME}:/port{}/config", port + 1),
HandleKind::Attach { port } => format!("{SCHEME_NAME}:/port{}/attach", port + 1),
HandleKind::Endpoints { port } => format!("{SCHEME_NAME}:/port{}/endpoints", port + 1),
};
Ok(path)
}
@@ -1362,6 +1395,11 @@ impl SchemeSync for EhciScheme {
}
Ok(buf.len())
}
HandleKind::Attach { port } => {
log::info!("ehcid: class driver attached to port {}", port + 1);
Ok(buf.len())
}
HandleKind::Endpoints { .. } => Ok(buf.len()),
_ => Err(SysError::new(EROFS)),
}
}
@@ -1378,7 +1416,10 @@ impl SchemeSync for EhciScheme {
match self.handle(id)?.kind {
HandleKind::PortDir { .. } => MODE_DIR | 0o755,
HandleKind::Status { .. } | HandleKind::Descriptor { .. } => MODE_FILE | 0o444,
HandleKind::Control { .. } | HandleKind::Config { .. } => MODE_FILE | 0o644,
HandleKind::Control { .. }
| HandleKind::Config { .. }
| HandleKind::Attach { .. }
| HandleKind::Endpoints { .. } => MODE_FILE | 0o644,
}
};