USB: P1-A xhcid UsbHostController trait adapter

Adds impl UsbHostController for XhciAdapter<N>, closing the architectural gap
where UHCI/OHCI/EHCI all implement the trait but xhcid used an ad-hoc scheme.

Design:
- XhciAdapter holds Arc<Xhci<N>> (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.
This commit is contained in:
Red Bear OS
2026-07-07 13:58:02 +03:00
parent 71971d12e0
commit 69a8e406c6
4 changed files with 121 additions and 2 deletions
+1
View File
@@ -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
+8 -2
View File
@@ -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<File>, InterruptMethod) {
let pci_config = pcid_handle.config();
@@ -153,7 +155,7 @@ fn daemon_with_context_size<const N: usize>(
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<const N: usize>(
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);
+2
View File
@@ -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
+110
View File
@@ -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<Xhci<N>>` 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<const N: usize> {
hci: Arc<Xhci<N>>,
name: String,
}
impl<const N: usize> XhciAdapter<N> {
pub fn new(hci: Arc<Xhci<N>>) -> Self {
let name = hci.scheme_name.clone();
Self { hci, name }
}
}
impl<const N: usize> UsbHostController for XhciAdapter<N> {
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<PortStatus> {
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<usize, UsbError> {
// 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<usize, UsbError> {
// 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<usize, UsbError> {
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
}
}