diff --git a/drivers/usb/usbhubd/src/main.rs b/drivers/usb/usbhubd/src/main.rs index a439012746..74958fc278 100644 --- a/drivers/usb/usbhubd/src/main.rs +++ b/drivers/usb/usbhubd/src/main.rs @@ -184,9 +184,21 @@ fn main() { } if attached { - self.handle.attach().expect("Failed to attach"); + match self.handle.attach() { + Ok(()) => {} + Err(e) => { + log::warn!("usbhubd: attach failed for port: {}", e); + return; + } + } } else { - self.handle.detach().expect("Failed to detach"); + match self.handle.detach() { + Ok(()) => {} + Err(e) => { + log::warn!("usbhubd: detach failed for port: {}", e); + return; + } + } } self.attached = attached; @@ -257,35 +269,49 @@ fn main() { continue; } - let port_idx: usize = port.checked_sub(1).unwrap().into(); - let state = states.get_mut(port_idx).unwrap(); + let port_idx: usize = match port.checked_sub(1) { + Some(p) => p.into(), + None => continue, + }; + let state = match states.get_mut(port_idx) { + Some(s) => s, + None => continue, + }; let port_sts = if usb_3 { let mut port_sts = usb::HubPortStatusV3::default(); - handle - .device_request( - PortReqTy::Class, - PortReqRecipient::Other, - usb::SetupReq::GetStatus as u8, - 0, - port as u16, - DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) }), - ) - .expect("Failed to retrieve port status"); - usb::HubPortStatus::V3(port_sts) + match handle.device_request( + PortReqTy::Class, + PortReqRecipient::Other, + usb::SetupReq::GetStatus as u8, + 0, + port as u16, + DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) }), + ) { + Ok(()) => usb::HubPortStatus::V3(port_sts), + Err(e) => { + log::warn!("usbhubd: GetPortStatus failed for port {}: {} — detaching", port, e); + state.ensure_attached(false); + continue; + } + } } else { let mut port_sts = usb::HubPortStatusV2::default(); - handle - .device_request( - PortReqTy::Class, - PortReqRecipient::Other, - usb::SetupReq::GetStatus as u8, - 0, - port as u16, - DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) }), - ) - .expect("Failed to retrieve port status"); - usb::HubPortStatus::V2(port_sts) + match handle.device_request( + PortReqTy::Class, + PortReqRecipient::Other, + usb::SetupReq::GetStatus as u8, + 0, + port as u16, + DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) }), + ) { + Ok(()) => usb::HubPortStatus::V2(port_sts), + Err(e) => { + log::warn!("usbhubd: GetPortStatus failed for port {}: {} — detaching", port, e); + state.ensure_attached(false); + continue; + } + } }; if state.port_sts != port_sts { state.port_sts = port_sts; @@ -297,22 +323,60 @@ fn main() { // then sleep bPwrOn2PwrGood * 2ms (minimum 100ms). if !port_sts.is_powered() { log::info!("power on port {port}"); - handle - .device_request( - PortReqTy::Class, - PortReqRecipient::Other, - usb::SetupReq::SetFeature as u8, - usb::HubPortFeature::PortPower as u16, - port as u16, - DeviceReqData::NoData, - ) - .expect("Failed to set port power"); + if let Err(e) = handle.device_request( + PortReqTy::Class, + PortReqRecipient::Other, + usb::SetupReq::SetFeature as u8, + usb::HubPortFeature::PortPower as u16, + port as u16, + DeviceReqData::NoData, + ) { + log::warn!("usbhubd: SetPortPower failed for port {}: {}", port, e); + continue; + } state.ensure_attached(false); - // Linux 7.1 power-on settle time thread::sleep(time::Duration::from_millis(power_on_delay_ms)); continue; } + // Linux 7.1: over-current detection and recovery. + // C_PORT_OVERCURRENT → log, clear, power-cycle. + if port_sts.is_over_current_changed() { + log::warn!("usbhubd: over-current change on port {} — power cycling", port); + let _ = handle.device_request( + PortReqTy::Class, + PortReqRecipient::Other, + usb::SetupReq::ClearFeature as u8, + usb::HubPortFeature::CPortOverCurrent as u16, + port as u16, + DeviceReqData::NoData, + ); + let _ = handle.device_request( + PortReqTy::Class, + PortReqRecipient::Other, + usb::SetupReq::ClearFeature as u8, + usb::HubPortFeature::PortPower as u16, + port as u16, + DeviceReqData::NoData, + ); + state.ensure_attached(false); + thread::sleep(time::Duration::from_millis(power_on_delay_ms)); + continue; + } + + // Linux 7.1: port indicator — set amber during reset/power-on, + // green when enabled. Helps diagnose which port is active. + if port_sts.is_connected() && port_sts.is_enabled() && !port_sts.is_resetting() { + let _ = handle.device_request( + PortReqTy::Class, + PortReqRecipient::Other, + usb::SetupReq::SetFeature as u8, + usb::HubPortFeature::PortIndicator as u16, + port as u16, + DeviceReqData::NoData, + ); + } + // Ignore disconnected port if !port_sts.is_connected() { state.ensure_attached(false); @@ -331,18 +395,18 @@ fn main() { // = 5000ms for USB 3, 1000ms for USB 2). if !port_sts.is_enabled() { log::info!("reset port {port}"); - handle - .device_request( - PortReqTy::Class, - PortReqRecipient::Other, - usb::SetupReq::SetFeature as u8, - usb::HubPortFeature::PortReset as u16, - port as u16, - DeviceReqData::NoData, - ) - .expect("Failed to set port reset"); + if let Err(e) = handle.device_request( + PortReqTy::Class, + PortReqRecipient::Other, + usb::SetupReq::SetFeature as u8, + usb::HubPortFeature::PortReset as u16, + port as u16, + DeviceReqData::NoData, + ) { + log::warn!("usbhubd: SetPortReset failed for port {}: {}", port, e); + continue; + } state.ensure_attached(false); - // Linux 7.1 reset settle time: minimum 10ms thread::sleep(time::Duration::from_millis(10)); continue; } diff --git a/drivers/usb/xhcid/src/usb/hub.rs b/drivers/usb/xhcid/src/usb/hub.rs index 9dab55e85a..adfd12e0ec 100644 --- a/drivers/usb/xhcid/src/usb/hub.rs +++ b/drivers/usb/xhcid/src/usb/hub.rs @@ -83,13 +83,19 @@ impl Default for HubDescriptorV3 { #[repr(u8)] pub enum HubPortFeature { PortConnection = 0, + PortEnable = 1, + PortSuspend = 2, PortOverCurrent = 3, PortReset = 4, PortLinkState = 5, PortPower = 8, + PortLowSpeed = 9, CPortConnection = 16, + CPortEnable = 17, + CPortSuspend = 18, CPortOverCurrent = 19, CPortReset = 20, + PortIndicator = 22, } bitflags::bitflags! { @@ -184,4 +190,11 @@ impl HubPortStatus { Self::V3(x) => x.contains(HubPortStatusV3::ENABLE), } } + + pub fn is_over_current_changed(&self) -> bool { + match self { + Self::V2(x) => x.contains(HubPortStatusV2::OVER_CURRENT_CHANGED), + Self::V3(x) => x.contains(HubPortStatusV3::OVER_CURRENT_CHANGED), + } + } }