use alloc::{boxed::Box, vec, vec::Vec};
use core::time::Duration;
use futures::{FutureExt, future::BoxFuture};
use usb_if::{
descriptor::{Class, ConfigurationDescriptor, DeviceDescriptor, EndpointType},
err::USBError,
host::{
ControlSetup,
hub::{HubDescriptor, PortFeature, PortStatus, PortStatusChange, Speed},
},
transfer::{Recipient, Request, RequestType},
};
use super::HubOp;
use crate::{
Device,
backend::kmod::hub::{HubInfo, PortChangeInfo},
osal::Kernel,
};
const HUB_DEBOUNCE_TIMEOUT: u64 = 2000;
const HUB_DEBOUNCE_STEP: u64 = 25;
const HUB_DEBOUNCE_STABLE: u64 = 100;
const HUB_PR_FS: u8 = 0;
const HUB_PR_HS_SINGLE_TT: u8 = 1;
const HUB_PR_HS_MULTI_TT: u8 = 2;
const HUB_PR_SS: u8 = 3;
pub struct HubDevice {
settings: HubSettings,
data: Box<Inner>,
kernel: Kernel,
}
struct Inner {
pub state: HubState,
pub num_ports: u8,
pub ports: Vec<Port>,
pub dev: Device,
pub descriptor: HubDescriptor,
pub parent_hub_slot_id: u8,
pub root_port_id: u8,
}
pub struct HubSettings {
pub config_value: u8,
pub interface_number: u8,
pub alt_setting: u8,
}
impl HubOp for HubDevice {
fn slot_id(&self) -> u8 {
self.data.dev.slot_id()
}
fn init(&mut self, info: HubInfo) -> BoxFuture<'_, Result<HubInfo, USBError>> {
self.configure(info).boxed()
}
fn changed_ports<'a>(&'a mut self) -> BoxFuture<'a, Result<Vec<PortChangeInfo>, USBError>> {
self.changed_ports().boxed()
}
}
impl HubDevice {
pub fn is_hub(
desc: &DeviceDescriptor,
configs: &[ConfigurationDescriptor],
) -> Option<HubSettings> {
if !matches!(desc.class(), Class::Hub(_)) {
return None;
}
let Some(config) = configs.first() else {
warn!("Hub device has no configurations");
return None;
};
for interface in &config.interfaces {
for alt in &interface.alt_settings {
if alt.subclass != 0x00 && alt.protocol != 0x00 {
continue;
}
if alt.num_endpoints != 1 {
continue;
}
if alt.endpoints[0].transfer_type != EndpointType::Interrupt
|| alt.endpoints[0].direction != usb_if::transfer::Direction::In
{
continue;
}
return Some(HubSettings {
config_value: config.configuration_value,
interface_number: interface.interface_number,
alt_setting: alt.alternate_setting,
});
}
}
None
}
pub async fn new(
dev: Device,
settings: HubSettings,
root_port_id: u8,
parent_hub_slot_id: u8,
kernel: &Kernel,
) -> Result<Self, USBError> {
Ok(Self {
settings,
data: Box::new(Inner {
state: HubState::Uninitialized,
num_ports: 0,
ports: vec![],
dev,
descriptor: unsafe { core::mem::zeroed() },
parent_hub_slot_id,
root_port_id,
}),
kernel: kernel.clone(),
})
}
pub async fn changed_ports(&mut self) -> Result<Vec<PortChangeInfo>, USBError> {
let mut changed_ports = vec![];
for port_idx in 0..self.data.num_ports {
let port_id = port_idx + 1;
let (status, change) = self.get_port_status(port_id).await?;
debug!("Port {} status: {:?}", port_id, status);
if change.connection_changed {
info!("Port {} connection changed: {}", port_id, status.connected);
self.clear_port_feature(port_id, PortFeature::CConnection)
.await?;
}
if status.connected && self.data.ports[port_idx as usize].state == PortState::Uninit {
info!(
"Port {} connection changed: connected={}, enabled={}",
port_id, status.connected, status.enabled
);
let validation_result = self.handle_port_connection(port_id, &status).await?;
self.data.ports[port_idx as usize].state = PortState::Probed;
changed_ports.push(validation_result);
}
if change.enabled_changed {
info!("Port {} enabled changed: {}", port_id, status.enabled);
self.clear_port_feature(port_id, PortFeature::CEnable)
.await?;
if let Some(port) = self.data.ports.iter_mut().find(|p| p.id == port_id) {
port.status = status;
}
}
if change.reset_complete {
debug!("Port {} reset complete", port_id);
self.clear_port_feature(port_id, PortFeature::CReset)
.await?;
if let Some(port) = self.data.ports.iter_mut().find(|p| p.id == port_id) {
port.status = status;
}
}
}
Ok(changed_ports)
}
pub fn is_superspeed(&self) -> bool {
self.data.dev.descriptor().protocol == 3
}
pub async fn configure(&mut self, info: HubInfo) -> Result<HubInfo, USBError> {
debug!("Configuring hub device, depth={}...", info.hub_depth);
let mut info = info;
trace!(
"settings: config_value={}, interface_number={}, alt_setting={}",
self.settings.config_value, self.settings.interface_number, self.settings.alt_setting
);
let descriptor = self.get_hub_descriptor().await?;
self.data.descriptor = descriptor;
if self.hub_descriptor().bNbrPorts == 0 {
return Err(USBError::from("Hub has zero ports"));
}
self.data.num_ports = self.hub_descriptor().bNbrPorts;
let characteristics = self.data.descriptor.hub_characteristics();
let ttt_bits = (characteristics >> 5) & 0x03;
info.tt.think_time_ns = match ttt_bits {
0 => 0, 1 => 666, 2 => 666 * 2, 3 => 666 * 3, _ => unreachable!(),
};
let device_protocol = self.data.dev.descriptor().protocol;
match device_protocol {
HUB_PR_FS => {
info.speed = Speed::Full;
}
HUB_PR_HS_SINGLE_TT => {
info.speed = Speed::High;
debug!("Hub is High Speed with Single TT");
}
HUB_PR_HS_MULTI_TT => {
info.speed = Speed::High;
debug!("Hub is High Speed with Multiple TTs");
match self.data.dev.claim_interface(0, 1).await {
Ok(_) => {
debug!("TT per port");
info.tt.multi = true;
}
Err(e) => {
debug!("Using single TT due to claim interface failure: {e}");
}
}
}
HUB_PR_SS => {
info.speed = Speed::SuperSpeed;
}
_ => {
warn!("Unknown hub protocol: {}", device_protocol);
}
}
debug!(
"Hub parameters: ports={}, protocol={}, multi_tt={}, tt_think_time={}ns",
self.data.num_ports, device_protocol, info.tt.multi, info.tt.think_time_ns
);
let status = self.get_hub_status().await?;
debug!(
"local power source: {}",
if status.local_power_source() {
"lost (inactive)"
} else {
"good"
}
);
debug!(
"over current condition exists: {}",
if status.over_current() { "" } else { "no " }
);
let params = crate::backend::ty::HubParams {
num_ports: self.data.num_ports,
multi_tt: info.tt.multi,
tt_think_time_ns: info.tt.think_time_ns as _,
parent_hub_slot_id: self.data.parent_hub_slot_id,
root_hub_port_number: self.data.root_port_id,
};
self.data.dev.update_hub(params).await?;
if info.hub_depth > -1 && self.is_superspeed() {
assert!(
info.hub_depth < 5,
"Hub depth too large: {}",
info.hub_depth
);
self.set_hub_depth(info.hub_depth as _).await?;
debug!("Set hub depth to {}", info.hub_depth);
}
self.data.ports = (1..=self.data.num_ports).map(Port::new).collect();
self.hub_power_on().await?;
self.data.state = HubState::Running;
debug!("Hub initialized with {} ports", self.data.num_ports);
Ok(info)
}
async fn set_hub_depth(&mut self, depth: u8) -> Result<(), USBError> {
self.data
.dev
.ctrl_ep_mut()
.control_out(
ControlSetup {
request_type: RequestType::Class,
recipient: Recipient::Device,
request: Request::Other(0x0c),
value: depth as _,
index: 0,
},
&[],
)
.await?;
Ok(())
}
fn hub_descriptor(&self) -> &HubDescriptor {
&self.data.descriptor
}
async fn get_hub_descriptor(&mut self) -> Result<HubDescriptor, USBError> {
let mut buff = vec![0u8; 4]; self.read_hub_descriptor_raw(&mut buff).await?;
let desc_len = buff[0] as usize;
trace!("Hub descriptor length from initial read: {}", desc_len);
let mut full_buff = vec![0u8; desc_len];
self.read_hub_descriptor_raw(&mut full_buff).await?;
let desc = unsafe { (full_buff.as_ptr() as *const HubDescriptor).read_unaligned() };
Ok(desc)
}
async fn read_hub_descriptor_raw(&mut self, buff: &mut [u8]) -> Result<(), USBError> {
const DT_SS_HUB: u16 = 0x0a;
const DT_HUB: u16 = 0x9;
const TYPE_CLASS: u16 = 1 << 5;
let dtype = if self.is_superspeed() {
DT_SS_HUB
} else {
DT_HUB
} | TYPE_CLASS;
let n = self
.data
.dev
.ctrl_ep_mut()
.control_in(
ControlSetup {
request_type: RequestType::Class,
recipient: Recipient::Device,
request: Request::GetDescriptor,
value: dtype << 8,
index: 0,
},
buff,
)
.await?;
trace!("Hub raw descriptor read {n} bytes");
Ok(())
}
async fn hub_power_on(&mut self) -> Result<(), USBError> {
for port_id in 1..=self.data.num_ports {
self.set_port_feature(port_id, PortFeature::Power).await?;
debug!("Powered on port {}", port_id);
}
self.kernel.delay(Duration::from_millis(100));
Ok(())
}
async fn get_hub_status(&mut self) -> Result<HubStatus, USBError> {
let mut buffer = vec![0u8; size_of::<HubStatus>()];
self.data
.dev
.ctrl_ep_mut()
.control_in(
ControlSetup {
request_type: RequestType::Class,
recipient: Recipient::Device,
request: Request::GetStatus,
value: 0,
index: 0,
},
&mut buffer,
)
.await?;
let status = u16::from_le_bytes([buffer[0], buffer[1]]);
let change = u16::from_le_bytes([buffer[2], buffer[3]]);
trace!("Hub raw status: 0x{:04x}, change: 0x{:04x}", status, change);
Ok(HubStatus { status, change })
}
async fn get_port_status(
&mut self,
port_id: u8,
) -> Result<(PortStatus, PortStatusChange), USBError> {
let mut buffer = vec![0u8; 4];
self.data
.dev
.ctrl_ep_mut()
.control_in(
ControlSetup {
request_type: RequestType::Class,
recipient: Recipient::Other, request: Request::GetStatus,
value: 0,
index: port_id as u16,
},
&mut buffer,
)
.await?;
let status_raw = u16::from_le_bytes([buffer[0], buffer[1]]);
let change_raw = u16::from_le_bytes([buffer[2], buffer[3]]);
trace!(
"Port {} raw status: 0x{:04x}, change: 0x{:04x}",
port_id, status_raw, change_raw
);
Ok((
self.parse_port_status(status_raw),
self.parse_port_change(change_raw),
))
}
fn parse_port_status(&self, raw: u16) -> PortStatus {
PortStatus {
connected: (raw & 0x0001) != 0,
enabled: (raw & 0x0002) != 0,
suspended: (raw & 0x0004) != 0,
over_current: (raw & 0x0008) != 0,
resetting: (raw & 0x0010) != 0,
powered: (raw & 0x0100) != 0,
low_speed: (raw & 0x0200) != 0,
high_speed: (raw & 0x0400) != 0,
speed: Speed::from_usb2_hub_status(raw),
change: PortStatusChange {
connection_changed: false,
enabled_changed: false,
reset_complete: false,
suspend_changed: false,
over_current_changed: false,
},
}
}
fn parse_port_change(&self, raw: u16) -> PortStatusChange {
PortStatusChange {
connection_changed: (raw & 0x0001) != 0,
enabled_changed: (raw & 0x0002) != 0,
suspend_changed: (raw & 0x0004) != 0,
over_current_changed: (raw & 0x0008) != 0,
reset_complete: (raw & 0x0010) != 0,
}
}
async fn set_port_feature(
&mut self,
port_index: u8,
feature: PortFeature,
) -> Result<(), USBError> {
self.data
.dev
.ctrl_ep_mut()
.control_out(
ControlSetup {
request_type: RequestType::Class,
recipient: Recipient::Other,
request: Request::SetFeature,
value: feature as u16,
index: port_index as u16,
},
&[],
)
.await
.map_err(USBError::from)?;
Ok(())
}
async fn clear_port_feature(
&mut self,
port_id: u8,
feature: PortFeature,
) -> Result<(), USBError> {
self.data
.dev
.ctrl_ep_mut()
.control_out(
ControlSetup {
request_type: RequestType::Class,
recipient: Recipient::Other,
request: Request::ClearFeature,
value: feature as u16,
index: port_id as u16,
},
&[],
)
.await
.map_err(USBError::from)?;
Ok(())
}
async fn debounce_port(
&mut self,
port_index: u8,
must_be_connected: bool,
) -> Result<PortStatus, USBError> {
let mut stable_count = 0u8;
let required_stable = (HUB_DEBOUNCE_STABLE / HUB_DEBOUNCE_STEP) as u8;
let max_attempts = (HUB_DEBOUNCE_TIMEOUT / HUB_DEBOUNCE_STEP) as u8;
info!(
"Starting debounce on port {} (expected_connected: {})",
port_index, must_be_connected
);
for attempt in 0..max_attempts {
self.kernel
.delay(core::time::Duration::from_millis(HUB_DEBOUNCE_STEP));
let (status, _change) = self.get_port_status(port_index).await?;
if status.connected == must_be_connected {
stable_count = stable_count.saturating_add(1);
debug!(
"Port {} debounce stable: {}/{} (attempt {})",
port_index, stable_count, required_stable, attempt
);
if stable_count >= required_stable {
info!(
"Port {} debounce stable (connected: {})",
port_index, status.connected
);
return Ok(status);
}
} else {
stable_count = 0;
debug!(
"Port {} debounce unstable, current_connected: {}, expected: {}",
port_index, status.connected, must_be_connected
);
}
}
warn!(
"Port {} debounce timeout after {} attempts ({}ms)",
port_index, max_attempts, HUB_DEBOUNCE_TIMEOUT
);
Err(USBError::Timeout)
}
async fn reset_port(&mut self, port_id: u8, status: &PortStatus) -> Result<(), USBError> {
info!("Resetting port {}", port_id);
self.set_port_feature(port_id, PortFeature::Reset).await?;
let reset_time = if status.low_speed {
Duration::from_millis(100)
} else {
Duration::from_millis(50)
};
self.kernel.delay(reset_time);
for _retry in 0..10 {
let (_status, change) = self.get_port_status(port_id).await?;
if change.reset_complete {
self.clear_port_feature(port_id, PortFeature::CReset)
.await?;
info!("Port {} reset complete", port_id);
return Ok(());
}
self.kernel.delay(Duration::from_millis(10));
}
warn!("Port {} reset timeout", port_id);
Err(USBError::Timeout)
}
async fn handle_port_connection(
&mut self,
port_id: u8,
initial_status: &PortStatus,
) -> Result<PortChangeInfo, USBError> {
info!(
"Handling connection on port {}, speed: {:?}",
port_id, initial_status.speed
);
let stable_status = self.debounce_port(port_id, true).await?;
if !stable_status.connected {
return Err(USBError::from("Connection unstable"));
}
self.reset_port(port_id, &stable_status).await?;
let enabled_status = self.wait_for_port_enabled(port_id).await?;
let port_speed = enabled_status.speed;
info!(
"Port {} device ready: speed={:?}, enabled={}",
port_id, port_speed, enabled_status.enabled
);
let hub_speed = match self.data.dev.descriptor().protocol {
1 | 2 => Speed::High, 3 => Speed::SuperSpeed, _ => Speed::Full, };
let port = &mut self.data.ports[port_id as usize - 1];
port.tt_required = port_speed.requires_tt(hub_speed);
debug!(
"TT required: port_speed={:?}, hub_speed={:?}, tt_required={}",
port_speed, hub_speed, port.tt_required
);
let tt_port_on_hub = if port.tt_required {
Some(port_id)
} else {
None
};
Ok(PortChangeInfo {
root_port_id: self.root_port_id(),
port_id,
port_speed,
tt_port_on_hub,
})
}
async fn wait_for_port_enabled(&mut self, port_id: u8) -> Result<PortStatus, USBError> {
const MAX_WAIT_MS: u64 = 500;
const CHECK_INTERVAL_MS: u64 = 10;
let max_attempts = MAX_WAIT_MS / CHECK_INTERVAL_MS;
for attempt in 0..max_attempts {
let (status, _change) = self.get_port_status(port_id).await?;
if status.enabled && status.connected {
info!("Port {} enabled after {} checks", port_id, attempt + 1);
return Ok(status);
}
if !status.connected {
return Err(USBError::from("Device disconnected during enable wait"));
}
self.kernel.delay(Duration::from_millis(CHECK_INTERVAL_MS));
}
warn!("Port {} enable timeout after {}ms", port_id, MAX_WAIT_MS);
Err(USBError::Timeout)
}
pub fn root_port_id(&self) -> u8 {
self.data.root_port_id
}
}
#[derive(Debug)]
pub enum HubState {
Uninitialized,
Running,
}
pub struct Port {
pub id: u8,
pub status: PortStatus,
pub state: PortState,
pub tt_required: bool,
}
impl Port {
pub fn new(index: u8) -> Self {
Self {
id: index,
status: PortStatus {
connected: false,
enabled: false,
suspended: false,
over_current: false,
resetting: false,
powered: false,
low_speed: false,
high_speed: false,
speed: usb_if::host::hub::Speed::Full,
change: usb_if::host::hub::PortStatusChange {
connection_changed: false,
enabled_changed: false,
reset_complete: false,
suspend_changed: false,
over_current_changed: false,
},
},
state: PortState::Uninit,
tt_required: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PortState {
#[default]
Uninit,
Reseted,
Probed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct HubStatus {
status: u16,
change: u16,
}
impl HubStatus {
#[allow(dead_code)]
fn local_power_source(&self) -> bool {
(self.status & 0x0001) != 0
}
#[allow(dead_code)]
fn over_current(&self) -> bool {
(self.status & 0x0002) != 0
}
}