usbhubd: P3 — power-on timing, USB 3 fix, polling interval

First P3 hub-driver maturity improvements, cross-referenced with
Linux 7.1 drivers/usb/core/hub.c:

  1. Power-on timing (hub_power_on + hub_power_on_good_delay)
     - reads bPwrOn2PwrGood (V2: power_on_good; V3: default 10)
     - sleeps power_on_good * 2ms after SET_FEATURE(PORT_POWER)
     - minimum floor: 100ms (matches Linux hub_power_on_good_delay)
     - logs the computed delay at startup

  2. USB 3 hub stall fix
     - ConfigureEndpointsReq no longer passes interface_desc or
       alternate_setting for USB 3 hubs
     - xHCI handles default-alt-0 derivation internally
     - resolves the two TODOs that documented the stall symptom

  3. SET_HUB_DEPTH with hub_depth() value
     - previously passed port_id.hub_depth().into() which was
       incorrect (returned route-string-derived depth)
     - now logs the depth value explicitly

  4. Polling interval tightened 1s -> 250ms
     - interrupt-driven detection remains a follow-up (P3 slice 2)
     - 250ms is a reasonable intermediate step for USB keyboard
       responsiveness

  5. wHubDelay recorded from V3 descriptor
     - extracted from hub_desc.delay field
     - displayed at startup; future P3 slices will accumulate
       through the hub tree per Linux hub_configure()

Cross-reference: Linux 7.1
  - drivers/usb/core/hub.c: hub_power_on()
  - drivers/usb/core/hub.c: hub_power_on_good_delay()
  - drivers/usb/core/hub.c: hub_activate()
  - include/linux/usb/ch11.h: HUB_SET_DEPTH = 0x0C
This commit is contained in:
Red Bear OS
2026-07-07 11:51:10 +03:00
parent 61b1510a46
commit b244dbd0d9
+61 -17
View File
@@ -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
}