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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user