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