#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegWidth {
U8,
U16,
U32,
U64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryBarrierType {
Read,
Write,
Full,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HubRequest {
GetHubDescriptor,
GetHubStatus,
SetHubFeature,
ClearHubFeature,
GetPortStatus,
SetPortFeature,
ClearPortFeature,
GetHubDescriptor16, }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PortFeature {
Connection = 0,
Enable = 1,
Suspend = 2,
OverCurrent = 3,
Reset = 4,
Power = 8,
LowSpeed = 9,
CConnection = 16, CEnable = 17, CSuspend = 18, COverCurrent = 19, CReset = 20, }
const USB_MAXCHILDREN: usize = 8;
const DEVICE_BITMAP_BYTES: usize = (USB_MAXCHILDREN + 1 + 7).div_ceil(8);
#[derive(Clone, Copy)]
#[allow(non_snake_case)]
#[repr(C, packed)]
pub struct HubDescriptor {
pub bDescLength: u8,
pub bDescriptorType: u8,
pub bNbrPorts: u8,
wHubCharacteristics: u16,
pub bPwrOn2PwrGood: u8,
pub bHubContrCurrent: u8,
pub u: HubDescriptorVariant,
}
impl HubDescriptor {
pub fn hub_characteristics(&self) -> u16 {
u16::from_le(self.wHubCharacteristics)
}
pub fn from_bytes(data: &[u8]) -> Option<&Self> {
if data.is_empty() {
return None;
}
let length = data[0] as usize;
if data.len() < length {
return None;
}
if data.len() < 2 || data[1] != 0x29 {
return None;
}
Some(unsafe { &*(data.as_ptr() as *const HubDescriptor) })
}
}
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub union HubDescriptorVariant {
pub hs: HighSpeedHubDescriptorTail,
pub ss: SuperSpeedHubDescriptorTail,
}
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct HighSpeedHubDescriptorTail {
pub device_removable: [u8; DEVICE_BITMAP_BYTES],
pub port_pwr_ctrl_mask: [u8; DEVICE_BITMAP_BYTES],
}
#[allow(non_snake_case)]
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct SuperSpeedHubDescriptorTail {
pub bHubHdrDecLat: u8,
wHubDelay: u16,
device_removable: u16,
}
impl SuperSpeedHubDescriptorTail {
pub fn hub_delay(&self) -> u16 {
u16::from_le(self.wHubDelay)
}
pub fn device_removable(&self) -> u16 {
u16::from_le(self.device_removable)
}
}
#[derive(Debug, Clone, Copy)]
pub struct TtInfo {
pub think_time: u8,
pub multi_tt: bool,
pub num_ports: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HubCharacteristics {
pub power_switching: PowerSwitchingMode,
pub compound_device: bool,
pub over_current_mode: OverCurrentMode,
pub port_indicators: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PowerSwitchingMode {
Ganged,
Individual,
AlwaysPower,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OverCurrentMode {
Global,
Individual,
}
#[derive(Debug, Clone, Copy)]
pub struct PortStatus {
pub connected: bool,
pub enabled: bool,
pub suspended: bool,
pub over_current: bool,
pub resetting: bool,
pub powered: bool,
pub low_speed: bool,
pub high_speed: bool,
pub speed: Speed,
pub change: PortStatusChange,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PortStatusChange {
pub connection_changed: bool,
pub enabled_changed: bool,
pub reset_complete: bool,
pub suspend_changed: bool,
pub over_current_changed: bool,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Speed {
Low = 0,
#[default]
Full = 1,
High = 2,
Wireless = 3,
SuperSpeed = 4,
SuperSpeedPlus = 5,
}
impl From<u8> for Speed {
fn from(value: u8) -> Self {
match value {
0 => Speed::Low,
1 => Speed::Full,
2 => Speed::High,
3 => Speed::Wireless,
4 => Speed::SuperSpeed,
5 => Speed::SuperSpeedPlus,
_ => Speed::Full,
}
}
}
impl Speed {
pub fn from_usb2_hub_status(raw: u16) -> Self {
if (raw & 0x0200) != 0 {
Speed::Low
} else if (raw & 0x0400) != 0 {
Speed::High
} else if (raw & 0x0600) != 0 {
Speed::SuperSpeed
} else {
Speed::Full
}
}
pub fn from_xhci_portsc(speed_value: u8) -> Self {
match speed_value {
1 => Speed::Full,
2 => Speed::Low,
3 => Speed::High,
4 => Speed::SuperSpeed,
5 => Speed::SuperSpeedPlus,
_ => Speed::Full, }
}
pub fn to_xhci_slot_value(&self) -> u8 {
match self {
Speed::Full => 1,
Speed::Low => 2,
Speed::High => 3,
Speed::SuperSpeed => 4,
Speed::SuperSpeedPlus => 5,
Speed::Wireless => 3,
}
}
pub fn to_xhci_portsc_value(&self) -> u8 {
match self {
Speed::Full => 1,
Speed::Low => 2,
Speed::High => 3,
Speed::SuperSpeed => 4,
Speed::SuperSpeedPlus => 5,
Speed::Wireless => 3,
}
}
pub fn requires_tt(&self, hub_speed: Self) -> bool {
matches!(self, Self::Low | Self::Full) && matches!(hub_speed, Self::High)
}
pub fn is_low_or_full_speed(&self) -> bool {
matches!(self, Self::Low | Self::Full)
}
}
impl HubCharacteristics {
pub fn from_descriptor(value: u16) -> Self {
let power_switching = match value & 0x03 {
0x01 => PowerSwitchingMode::Ganged,
0x02 => PowerSwitchingMode::Individual,
_ => PowerSwitchingMode::AlwaysPower,
};
let compound_device = (value & 0x04) != 0;
let over_current_mode = if (value & 0x08) != 0 {
OverCurrentMode::Individual
} else {
OverCurrentMode::Global
};
let port_indicators = (value & 0x10) != 0;
Self {
power_switching,
compound_device,
over_current_mode,
port_indicators,
}
}
pub fn to_descriptor(&self) -> u16 {
let mut value = 0u16;
value |= match self.power_switching {
PowerSwitchingMode::Ganged => 0x01,
PowerSwitchingMode::Individual => 0x02,
PowerSwitchingMode::AlwaysPower => 0x00,
};
if self.compound_device {
value |= 0x04;
}
if matches!(self.over_current_mode, OverCurrentMode::Individual) {
value |= 0x08;
}
if self.port_indicators {
value |= 0x10;
}
value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hub_characteristics_roundtrip() {
let original = HubCharacteristics {
power_switching: PowerSwitchingMode::Individual,
compound_device: true,
over_current_mode: OverCurrentMode::Global,
port_indicators: true,
};
let descriptor = original.to_descriptor();
let decoded = HubCharacteristics::from_descriptor(descriptor);
assert_eq!(original.power_switching, decoded.power_switching);
assert_eq!(original.compound_device, decoded.compound_device);
assert_eq!(original.over_current_mode, decoded.over_current_mode);
assert_eq!(original.port_indicators, decoded.port_indicators);
}
#[test]
fn test_hub_descriptor_from_bytes() {
let data = [
0x09, 0x29, 0x04, 0x12, 0x00, 0x32, 0x64, 0x00, 0x00, ];
let desc = HubDescriptor::from_bytes(&data).expect("Failed to parse");
assert_eq!(desc.bNbrPorts, 4);
assert_eq!(desc.bPwrOn2PwrGood, 50);
assert_eq!(desc.bHubContrCurrent, 100);
assert_eq!(desc.hub_characteristics(), 0x0012);
}
#[test]
fn test_hub_descriptor_invalid_length() {
let data = [0x09, 0x29]; assert!(HubDescriptor::from_bytes(&data).is_none());
}
#[test]
fn test_hub_descriptor_invalid_type() {
let mut data = [0x09u8; 7];
data[1] = 0x01; assert!(HubDescriptor::from_bytes(&data).is_none());
}
}