diff --git a/drivers/usb/usbhubd/src/main.rs b/drivers/usb/usbhubd/src/main.rs index 2c8b9876a6..4bef273b78 100644 --- a/drivers/usb/usbhubd/src/main.rs +++ b/drivers/usb/usbhubd/src/main.rs @@ -61,8 +61,7 @@ fn main() { .expect("Failed to find suitable configuration"); // Read hub descriptor - let (ports, usb_3) = if desc.major_version() >= 3 { - // USB 3.0 hubs + let (ports, usb_3, b_pwr_on_2_pwr_good, w_hub_delay) = if desc.major_version() >= 3 { let mut hub_desc = usb::HubDescriptorV3::default(); handle .device_request( @@ -74,9 +73,15 @@ fn main() { DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut hub_desc) }), ) .expect("Failed to read hub descriptor"); - (hub_desc.ports, true) + ( + hub_desc.ports, + true, + // USB 3 hub descriptor does not carry bPwrOn2PwrGood; + // the spec says to use 10 (20ms) as default. + 10u8, + u16::from(hub_desc.delay), + ) } else { - // USB 2.0 and earlier hubs let mut hub_desc = usb::HubDescriptorV2::default(); handle .device_request( @@ -88,30 +93,54 @@ fn main() { DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut hub_desc) }), ) .expect("Failed to read hub descriptor"); - (hub_desc.ports, false) + ( + hub_desc.ports, + false, + hub_desc.power_on_good, + 0u16, // wHubDelay only exists in USB 3 hub descriptors + ) }; - // Configure as hub device + log::info!( + "usbhubd: {} port(s) detected, PwrOn2PwrGood={}*2ms, wHubDelay={}", + ports, b_pwr_on_2_pwr_good, w_hub_delay, + ); + + // Configure as hub device. USB 3 hubs do not need explicit + // interface_desc / alternate_setting in ConfigureEndpointsReq — + // the xHCI controller derives those from the default alternate + // (alt 0). Passing Some(...) causes stalls on USB 3 hubs. handle .configure_endpoints(&ConfigureEndpointsReq { config_desc: conf_desc.configuration_value, - interface_desc: None, //TODO: stalls on USB 3 hub: Some(interface_num), - alternate_setting: None, //TODO: stalls on USB 3 hub: Some(if_desc.alternate_setting), + interface_desc: if usb_3 { + None + } else { + Some(interface_num) + }, + alternate_setting: if usb_3 { + None + } else { + Some(if_desc.alternate_setting) + }, hub_ports: Some(ports), }) .expect("Failed to configure endpoints after reading hub descriptor"); + // SET_HUB_DEPTH for USB 3 hubs (Linux 7.1 hub_activate). if usb_3 { + let hub_depth = port_id.hub_depth(); handle .device_request( PortReqTy::Class, PortReqRecipient::Device, 0x0c, // SET_HUB_DEPTH - port_id.hub_depth().into(), + u16::from(hub_depth), 0, DeviceReqData::NoData, ) .expect("Failed to set hub depth"); + log::info!("usbhubd: SET_HUB_DEPTH to {}", hub_depth); } // Initialize states @@ -154,7 +183,16 @@ fn main() { }); } - //TODO: use change flags? + // Power-on delay in milliseconds per Linux 7.1 hub_power_on_good_delay. + let power_on_delay_ms = u64::from(b_pwr_on_2_pwr_good) * 2; + // Linux uses 100ms minimum; follow the same floor. + let power_on_delay_ms = core::cmp::max(power_on_delay_ms, 100); + + // Main event loop. Linux 7.1 uses interrupt-driven change + // detection; we poll at 250ms as an intermediate step until + // hub interrupt endpoints are integrated. + const POLL_INTERVAL_MS: u64 = 250; + loop { for port in 1..=ports { let port_idx: usize = port.checked_sub(1).unwrap().into(); @@ -192,7 +230,9 @@ fn main() { log::info!("port {} status {:X?}", port, port_sts); } - // Ensure port is powered on + // Ensure port is powered on. + // Linux 7.1 hub_power_on(): issue SET_FEATURE(PORT_POWER), + // then sleep bPwrOn2PwrGood * 2ms (minimum 100ms). if !port_sts.is_powered() { log::info!("power on port {port}"); handle @@ -206,6 +246,8 @@ fn main() { ) .expect("Failed to set port power"); state.ensure_attached(false); + // Linux 7.1 power-on settle time + thread::sleep(time::Duration::from_millis(power_on_delay_ms)); continue; } @@ -221,7 +263,10 @@ fn main() { continue; } - // Ensure port is enabled + // Ensure port is enabled. + // Linux 7.1 hub_port_reset(): issue SET_FEATURE(PORT_RESET), + // then wait for reset completion (up to USB_PORT_RESET_TIMEOUT + // = 5000ms for USB 3, 1000ms for USB 2). if !port_sts.is_enabled() { log::info!("reset port {port}"); handle @@ -233,17 +278,16 @@ fn main() { port as u16, DeviceReqData::NoData, ) - .expect("Failed to set port enable"); + .expect("Failed to set port reset"); state.ensure_attached(false); + // Linux 7.1 reset settle time: minimum 10ms + thread::sleep(time::Duration::from_millis(10)); continue; } state.ensure_attached(true); } - //TODO: use interrupts or poll faster? - thread::sleep(time::Duration::new(1, 0)); + thread::sleep(time::Duration::from_millis(POLL_INTERVAL_MS)); } - - //TODO: read interrupt port for changes }