use crate::timer::Timer;
use crate::usb::control::{
clear_port_feature, get_configuration_descriptor, get_hub_descriptor, get_port_status,
set_configuration, set_port_power, set_port_reset, PORT_FEATURE_C_CONNECTION,
PORT_FEATURE_C_RESET,
};
use crate::usb::descriptor::{ConfigurationDescriptor, Descriptors, EndpointDescriptor};
use crate::usb::dwc2::{Channel, ControlEndpoint, SplitTarget};
use crate::usb::EnumerationError;
const PORT_RESET_POLLS: u32 = 20;
const PORT_RESET_POLL_MS: u32 = 10;
const CONNECT_DEBOUNCE_MS: u32 = 100;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PortStatus(pub u16);
impl PortStatus {
pub fn connected(&self) -> bool {
self.0 & (1 << 0) != 0
}
pub fn enabled(&self) -> bool {
self.0 & (1 << 1) != 0
}
pub fn low_speed(&self) -> bool {
self.0 & (1 << 9) != 0
}
pub fn high_speed(&self) -> bool {
self.0 & (1 << 10) != 0
}
}
#[derive(Clone, Copy)]
pub struct Hub {
endpoint: ControlEndpoint,
pub num_ports: u8,
pub power_on_good_ms: u32,
pub high_speed: bool,
status_endpoint: Option<(u8, u16)>,
}
impl Hub {
pub fn configure(
channel: &mut Channel,
timer: &Timer,
endpoint: ControlEndpoint,
high_speed: bool,
) -> Result<Hub, EnumerationError> {
let mut config = [0u8; 64];
let config_len = get_configuration_descriptor(channel, timer, endpoint, 0, &mut config)?;
let config_value = ConfigurationDescriptor::parse(&config)
.ok_or(EnumerationError::MalformedDescriptor)?
.value();
set_configuration(channel, timer, endpoint, config_value)?;
let status_endpoint = find_status_endpoint(&config[..config_len]);
let mut hub_descriptor = [0u8; 16];
let len = get_hub_descriptor(channel, timer, endpoint, &mut hub_descriptor)?;
if len < 6 {
return Err(EnumerationError::MalformedDescriptor);
}
let num_ports = hub_descriptor[2];
let power_on_good_ms = hub_descriptor[5] as u32 * 2;
for port in 1..=num_ports {
set_port_power(channel, timer, endpoint, port)?;
}
timer.delay_ms(power_on_good_ms + CONNECT_DEBOUNCE_MS);
Ok(Hub {
endpoint,
num_ports,
power_on_good_ms,
high_speed,
status_endpoint,
})
}
pub fn status_endpoint(&self) -> Option<(u8, u16)> {
self.status_endpoint
}
pub fn endpoint(&self) -> ControlEndpoint {
self.endpoint
}
pub fn port_status(
&self,
channel: &mut Channel,
timer: &Timer,
port: u8,
) -> Result<PortStatus, EnumerationError> {
let (status, _change) = get_port_status(channel, timer, self.endpoint, port)?;
Ok(PortStatus(status))
}
pub fn reset_port(
&self,
channel: &mut Channel,
timer: &Timer,
port: u8,
) -> Result<PortStatus, EnumerationError> {
let endpoint = self.endpoint;
let _ = clear_port_feature(channel, timer, endpoint, port, PORT_FEATURE_C_CONNECTION);
set_port_reset(channel, timer, endpoint, port)?;
let mut status = PortStatus(0);
for _ in 0..PORT_RESET_POLLS {
timer.delay_ms(PORT_RESET_POLL_MS);
if let Ok((s, _)) = get_port_status(channel, timer, endpoint, port) {
status = PortStatus(s);
if status.enabled() {
break;
}
}
}
let _ = clear_port_feature(channel, timer, endpoint, port, PORT_FEATURE_C_RESET);
Ok(status)
}
pub fn split_target(&self, port: u8, status: &PortStatus) -> Option<SplitTarget> {
if !self.high_speed {
return self.endpoint.split;
}
if status.high_speed() {
None
} else {
Some(SplitTarget {
hub_address: self.endpoint.address,
port,
})
}
}
}
fn find_status_endpoint(config: &[u8]) -> Option<(u8, u16)> {
Descriptors::new(config)
.filter_map(EndpointDescriptor::parse)
.find(|endpoint| endpoint.is_interrupt() && endpoint.is_in())
.map(|endpoint| (endpoint.number(), endpoint.max_packet_size()))
}