From 69a8e406c6d59088791d83047be4f54d4ae9dc1d Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Tue, 7 Jul 2026 13:58:02 +0300 Subject: [PATCH] USB: P1-A xhcid UsbHostController trait adapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds impl UsbHostController for XhciAdapter, closing the architectural gap where UHCI/OHCI/EHCI all implement the trait but xhcid used an ad-hoc scheme. Design: - XhciAdapter holds Arc> (xhci already uses interior mutability: Mutex/CHashMap/Atomic for all state, so &mut self trait methods are satisfied by delegating to &self Arc methods) - port_status: maps xHCI PortFlags (CCS/PED/OCA/PR/PP) + speed + link state into usb_core::PortStatus - port_reset: delegates to existing reset_port(PortId) with usize-to-PortId conversion (root ports only, route_string=0) - Transfer methods (control/bulk/interrupt) are stubbed with Unsupported — xhci handles enumeration internally via attach_device(), and class drivers communicate through the scheme IPC, not trait methods - set_address returns true (SET_ADDRESS is sent via control_transfer, handled internally by attach_device, like UHCI's approach) main.rs updated to use usb_core::scheme_path() for consistent scheme naming (replaces hardcoded format!("usb.{}", name)). usb-core added as path dependency to xhcid (no workspace member needed — Cargo allows path deps outside the workspace root). N=0 for P1-A; control/bulk/interrupt transfer trait bridges deferred to the usb-core unified enumeration loop follow-up. --- drivers/usb/xhcid/Cargo.toml | 1 + drivers/usb/xhcid/src/main.rs | 10 +- drivers/usb/xhcid/src/xhci/mod.rs | 2 + drivers/usb/xhcid/src/xhci/trait_adapter.rs | 110 ++++++++++++++++++++ 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/xhcid/src/xhci/trait_adapter.rs diff --git a/drivers/usb/xhcid/Cargo.toml b/drivers/usb/xhcid/Cargo.toml index ca767fd257..f989eec1f3 100644 --- a/drivers/usb/xhcid/Cargo.toml +++ b/drivers/usb/xhcid/Cargo.toml @@ -35,6 +35,7 @@ daemon = { path = "../../../daemon" } pcid = { path = "../../pcid" } libredox.workspace = true regex = "1.10.6" +usb-core = { path = "../../../../../recipes/drivers/usb-core/source" } [lints] workspace = true diff --git a/drivers/usb/xhcid/src/main.rs b/drivers/usb/xhcid/src/main.rs index 7f2b643d65..340a8bb723 100644 --- a/drivers/usb/xhcid/src/main.rs +++ b/drivers/usb/xhcid/src/main.rs @@ -40,7 +40,7 @@ use pcid_interface::{PciFeature, PciFeatureInfo, PciFunctionHandle}; use redox_scheme::{scheme::register_sync_scheme, Socket}; use scheme_utils::Blocking; -use crate::xhci::{InterruptMethod, Xhci}; +use crate::xhci::{InterruptMethod, Xhci, XhciAdapter}; // Declare as pub so that no warnings appear due to parts of the interface code not being used by // the driver. Since there's also a dedicated crate for the driver interface, those warnings don't @@ -50,6 +50,8 @@ pub mod driver_interface; mod usb; mod xhci; +use usb_core::scheme_path; + #[cfg(target_arch = "x86_64")] fn get_int_method(pcid_handle: &mut PciFunctionHandle) -> (Option, InterruptMethod) { let pci_config = pcid_handle.config(); @@ -153,7 +155,7 @@ fn daemon_with_context_size( vendor, device, hci_version, quirks.bits() ); - let scheme_name = format!("usb.{}", name); + let scheme_name = scheme_path(&name); let socket = Socket::create().expect("xhcid: failed to create usb scheme"); let handler = Blocking::new(&socket, 16); @@ -164,6 +166,10 @@ fn daemon_with_context_size( register_sync_scheme(&socket, &scheme_name, &mut &*hci) .expect("xhcid: failed to regsiter scheme to namespace"); + // Create the UsbHostController trait adapter for controller-agnostic + // port enumeration and future usb-core unified polling. + let _adapter = XhciAdapter::new(Arc::clone(&hci)); + daemon.ready(); xhci::start_irq_reactor(&hci, irq_file); diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs index 1f662885e2..6a9ac6e543 100644 --- a/drivers/usb/xhcid/src/xhci/mod.rs +++ b/drivers/usb/xhcid/src/xhci/mod.rs @@ -42,6 +42,7 @@ mod port; mod ring; mod runtime; pub mod scheme; +mod trait_adapter; mod trb; pub use self::capability::CapabilityRegs; @@ -63,6 +64,7 @@ use self::trb::{TransferKind, Trb, TrbCompletionCode}; use self::scheme::EndpIfState; pub use crate::driver_interface::PortId; +pub use self::trait_adapter::XhciAdapter; use crate::driver_interface::*; /// Specifies the configurable interrupt mechanism used by the xhci subsystem for registering diff --git a/drivers/usb/xhcid/src/xhci/trait_adapter.rs b/drivers/usb/xhcid/src/xhci/trait_adapter.rs new file mode 100644 index 0000000000..ec8be8d90d --- /dev/null +++ b/drivers/usb/xhcid/src/xhci/trait_adapter.rs @@ -0,0 +1,110 @@ +use std::sync::Arc; + +use super::port::PortFlags; +use super::{PortId, Xhci}; + +use usb_core::{PortStatus, SetupPacket, TransferDirection, UsbError, UsbHostController}; + +/// Adapter wrapping an `Arc>` to implement the `UsbHostController` trait. +/// +/// xhcid's internal methods are all `&self` (they use Arc/Mutex/CHashMap for interior +/// mutability). This adapter satisfies the `&mut self` requirement of the trait by +/// holding a cheap stack-allocated struct that delegates every method to the Arc'd +/// Xhci instance. +/// +/// The transfer methods (`control_transfer`, `bulk_transfer`, `interrupt_transfer`) +/// are stubbed — xhci handles device enumeration internally via `attach_device()`, +/// and class drivers communicate with devices through the scheme IPC, not through +/// these trait methods. +pub struct XhciAdapter { + hci: Arc>, + name: String, +} + +impl XhciAdapter { + pub fn new(hci: Arc>) -> Self { + let name = hci.scheme_name.clone(); + Self { hci, name } + } +} + +impl UsbHostController for XhciAdapter { + fn name(&self) -> &str { + &self.name + } + + fn port_count(&self) -> usize { + self.hci.ports.lock().unwrap_or_else(|e| e.into_inner()).len() + } + + fn port_status(&self, port: usize) -> Option { + let ports = self.hci.ports.lock().unwrap_or_else(|e| e.into_inner()); + let p = ports.get(port)?; + let flags = p.flags(); + let link_state = p.state(); + let speed = p.speed(); + + Some(PortStatus { + connected: flags.contains(PortFlags::CCS), + enabled: flags.contains(PortFlags::PED), + suspended: link_state == 3, // U3 = suspend + over_current: flags.contains(PortFlags::OCA), + reset: flags.contains(PortFlags::PR), + power: flags.contains(PortFlags::PP), + low_speed: speed == 2, // USB 1.0 low-speed (1.5 Mbps) + high_speed: speed == 3, // USB 2.0 high-speed (480 Mbps) + test_mode: false, + indicator: flags.intersects(PortFlags::PIC_AMB | PortFlags::PIC_GRN), + }) + } + + fn port_reset(&mut self, port: usize) -> bool { + let port_id = PortId { + root_hub_port_num: (port + 1) as u8, + route_string: 0, + }; + self.hci.reset_port(port_id).is_ok() + } + + fn control_transfer( + &mut self, + _device_address: u8, + _setup: &SetupPacket, + _data: &mut [u8], + ) -> Result { + // xhci handles device enumeration internally via attach_device(). + // Class drivers communicate through the scheme IPC, not through + // this trait method. Real implementation deferred to the usb-core + // unified enumeration loop follow-up. + Err(UsbError::Unsupported) + } + + fn bulk_transfer( + &mut self, + _device_address: u8, + _endpoint: u8, + _data: &mut [u8], + _direction: TransferDirection, + ) -> Result { + // Class drivers (usbscsid, usbhidd) talk to devices through the + // xhci scheme IPC. Bulk transfers through the trait are not used + // in the current architecture. + Err(UsbError::Unsupported) + } + + fn interrupt_transfer( + &mut self, + _device_address: u8, + _endpoint: u8, + _data: &mut [u8], + ) -> Result { + Err(UsbError::Unsupported) + } + + fn set_address(&mut self, _device_address: u8) -> bool { + // Like UHCI, SET_ADDRESS is a standard USB control transfer sent + // through control_transfer(), not a separate controller command. + // The actual SET_ADDRESS is handled during attach_device(). + true + } +}