use crate::bitset::BitSet;
use crate::debug;
use crate::topology::Topology;
use crate::wire::{
ConfigurationDescriptor, DescriptorVisitor, EndpointDescriptor,
HubDescriptor, SetupPacket, CLASS_REQUEST, CLEAR_FEATURE,
CONFIGURATION_DESCRIPTOR, DEVICE_DESCRIPTOR, DEVICE_TO_HOST,
GET_DESCRIPTOR, GET_STATUS, HOST_TO_DEVICE, HUB_CLASSCODE, HUB_DESCRIPTOR,
PORT_POWER, PORT_RESET, RECIPIENT_OTHER, SET_ADDRESS, SET_CONFIGURATION,
SET_FEATURE,
};
use core::cell::{Cell, RefCell};
use core::pin::Pin;
use core::task::{Context, Poll};
use futures::future::FutureExt;
use futures::{Future, Stream, StreamExt};
pub use crate::host_controller::{
DataPhase, DeviceStatus, HostController, InterruptPacket, TransferExtras,
TransferType, UsbError, UsbSpeed,
};
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct DeviceInfo {
pub vid: u16,
pub pid: u16,
pub class: u8,
pub subclass: u8,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Copy, Clone, PartialEq, Eq)]
struct UnaddressedDevice {
usb_speed: UsbSpeed,
packet_size_ep0: u8,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(PartialEq, Eq)]
pub struct UnconfiguredDevice {
usb_address: u8,
usb_speed: UsbSpeed,
packet_size_ep0: u8,
}
impl UnconfiguredDevice {
pub fn address(&self) -> u8 {
self.usb_address
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(PartialEq, Eq)]
pub struct BulkIn {
usb_address: u8,
usb_speed: UsbSpeed,
endpoint: u8,
data_toggle: Cell<bool>,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(PartialEq, Eq)]
pub struct BulkOut {
usb_address: u8,
usb_speed: UsbSpeed,
endpoint: u8,
data_toggle: Cell<bool>,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(PartialEq, Eq)]
pub struct UsbDevice {
usb_address: u8,
usb_speed: UsbSpeed,
packet_size_ep0: u8,
in_endpoints_bitmap: u16,
out_endpoints_bitmap: u16,
}
impl UsbDevice {
pub fn address(&self) -> u8 {
self.usb_address
}
pub fn in_endpoints(&self) -> BitSet {
BitSet(self.in_endpoints_bitmap as u32)
}
pub fn out_endpoints(&self) -> BitSet {
BitSet(self.out_endpoints_bitmap as u32)
}
pub fn open_in_endpoint(&mut self, ep: u8) -> Result<BulkIn, UsbError> {
if ep > 0 && ep < 16 && (self.in_endpoints_bitmap & (1 << ep)) != 0 {
self.in_endpoints_bitmap &= !(1 << ep);
Ok(BulkIn {
usb_address: self.usb_address,
usb_speed: self.usb_speed,
endpoint: ep,
data_toggle: Cell::new(false),
})
} else {
Err(UsbError::NoSuchEndpoint)
}
}
pub fn open_out_endpoint(&mut self, ep: u8) -> Result<BulkOut, UsbError> {
if ep > 0 && ep < 16 && (self.out_endpoints_bitmap & (1 << ep)) != 0 {
self.out_endpoints_bitmap &= !(1 << ep);
Ok(BulkOut {
usb_address: self.usb_address,
usb_speed: self.usb_speed,
endpoint: ep,
data_toggle: Cell::new(false),
})
} else {
Err(UsbError::NoSuchEndpoint)
}
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(PartialEq, Eq)]
pub enum DeviceEvent {
Connect(UnconfiguredDevice, DeviceInfo),
HubConnect(UsbDevice),
Disconnect(BitSet),
EnumerationError(u8, u8, UsbError),
None,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Default, PartialEq, Eq)]
pub struct BasicConfiguration {
pub num_configurations: u8,
pub configuration_value: u8,
pub in_endpoints: u16,
pub out_endpoints: u16,
}
impl DescriptorVisitor for BasicConfiguration {
fn on_configuration(&mut self, c: &ConfigurationDescriptor) {
self.num_configurations += 1;
self.configuration_value = c.bConfigurationValue;
}
fn on_endpoint(&mut self, i: &EndpointDescriptor) {
if (i.bEndpointAddress & 0x80) == 0x80 {
self.in_endpoints |= 1 << (i.bEndpointAddress & 15);
} else {
self.out_endpoints |= 1 << (i.bEndpointAddress & 15);
}
}
}
struct SpecificConfiguration {
configuration_value: u8,
ok: bool,
in_endpoints: u16,
out_endpoints: u16,
}
impl SpecificConfiguration {
const fn new(configuration_value: u8) -> Self {
Self {
configuration_value,
ok: false,
in_endpoints: 0,
out_endpoints: 0,
}
}
}
impl DescriptorVisitor for SpecificConfiguration {
fn on_configuration(&mut self, c: &ConfigurationDescriptor) {
self.ok = c.bConfigurationValue == self.configuration_value;
}
fn on_endpoint(&mut self, i: &EndpointDescriptor) {
if self.ok {
if (i.bEndpointAddress & 0x80) == 0x80 {
self.in_endpoints |= 1 << (i.bEndpointAddress & 15);
} else {
self.out_endpoints |= 1 << (i.bEndpointAddress & 15);
}
}
}
}
pub struct HubState<HC: HostController> {
topology: RefCell<Topology>,
pipes: RefCell<[Option<HC::InterruptPipe>; 15]>,
}
impl<HC: HostController> Default for HubState<HC> {
fn default() -> Self {
Self {
topology: Default::default(),
pipes: Default::default(),
}
}
}
impl<HC: HostController> HubState<HC> {
pub fn topology(&self) -> Topology {
self.topology.borrow().clone()
}
fn try_add(
&self,
hc: &HC,
address: u8,
endpoint: u8,
max_packet_size: u8,
interval_ms: u8,
) -> Result<(), UsbError> {
for p in self.pipes.borrow_mut().iter_mut() {
if p.is_none() {
*p = Some(hc.try_alloc_interrupt_pipe(
address,
TransferExtras::Normal,
endpoint,
max_packet_size as u16,
interval_ms,
)?);
return Ok(());
}
}
Err(UsbError::TooManyDevices)
}
}
struct HubStateStream<'a, HC: HostController> {
state: &'a HubState<HC>,
}
impl<HC: HostController> Stream for HubStateStream<'_, HC> {
type Item = InterruptPacket;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Option<Self::Item>> {
for slot in self.state.pipes.borrow_mut().iter_mut() {
if let Some(pipe) = slot {
match pipe.poll_next_unpin(cx) {
Poll::Ready(Some(packet)) => {
return Poll::Ready(Some(packet));
}
Poll::Ready(None) => {
*slot = None;
}
Poll::Pending => {}
}
}
}
Poll::Pending
}
}
pub struct UsbBus<HC: HostController> {
driver: HC,
root_device_speed: Cell<Option<UsbSpeed>>,
}
impl<HC: HostController> UsbBus<HC> {
pub fn new(driver: HC) -> Self {
Self {
driver,
root_device_speed: Cell::new(None),
}
}
pub fn device_events<
'a,
D: Future<Output = ()>,
F: Fn(usize) -> D + 'static + Clone,
>(
&'a self,
hub_state: &'a HubState<HC>,
delay_ms_in: F,
) -> impl Stream<Item = DeviceEvent> + 'a {
let root_device = self.driver.device_detect();
enum InternalEvent {
Root(DeviceStatus),
Packet(InterruptPacket),
}
futures::stream::select(
root_device.map(InternalEvent::Root),
HubStateStream { state: hub_state }.map(InternalEvent::Packet),
)
.then(move |ev| {
let delay_ms = delay_ms_in.clone();
async move {
match ev {
InternalEvent::Root(status) => {
if let DeviceStatus::Present(speed) = status {
self.root_device_speed.set(Some(speed));
self.driver.reset_root_port(true);
delay_ms(50).await;
self.driver.reset_root_port(false);
delay_ms(10).await;
let (device, info) =
match self.new_device(speed).await {
Ok((device, info)) => (device, info),
Err(e) => {
return DeviceEvent::EnumerationError(
0, 1, e,
)
}
};
let is_hub = info.class == HUB_CLASSCODE;
let address = hub_state
.topology
.borrow_mut()
.device_connect(0, 1, is_hub)
.expect("Root connect should always succeed");
let device = match self
.set_address(device, address)
.await
{
Ok(device) => device,
Err(e) => {
return DeviceEvent::EnumerationError(
0, 1, e,
);
}
};
if is_hub {
debug::println!("It's a hub");
match self.new_hub(hub_state, device).await {
Ok(device) => {
return DeviceEvent::HubConnect(device)
}
Err(e) => {
return DeviceEvent::EnumerationError(
0, 1, e,
)
}
};
}
DeviceEvent::Connect(device, info)
} else {
hub_state
.topology
.borrow_mut()
.device_disconnect(0, 1);
for slot in hub_state.pipes.borrow_mut().iter_mut()
{
*slot = None;
}
DeviceEvent::Disconnect(BitSet(0xFFFF_FFFF))
}
}
InternalEvent::Packet(packet) => self
.handle_hub_packet(hub_state, &packet, delay_ms)
.await
.unwrap_or_else(|e| {
DeviceEvent::EnumerationError(0, 1, e)
}),
}
}
})
}
pub fn device_events_no_hubs<
D: Future<Output = ()>,
F: Fn(usize) -> D + 'static + Clone,
>(
&self,
delay_ms_in: F,
) -> impl Stream<Item = DeviceEvent> + '_ {
let root_device = self.driver.device_detect();
root_device.then(move |status| {
let delay_ms = delay_ms_in.clone();
async move {
if let DeviceStatus::Present(speed) = status {
self.root_device_speed.set(Some(speed));
self.driver.reset_root_port(true);
delay_ms(50).await;
self.driver.reset_root_port(false);
delay_ms(10).await;
match self.new_device(speed).await {
Ok((device, info)) => match self
.set_address(device, 1)
.await
{
Ok(device) => DeviceEvent::Connect(device, info),
Err(e) => DeviceEvent::EnumerationError(0, 1, e),
},
Err(e) => DeviceEvent::EnumerationError(0, 1, e),
}
} else {
DeviceEvent::Disconnect(BitSet(0xFFFF_FFFF))
}
}
})
}
fn get_transfer_extras(&self, speed: UsbSpeed) -> TransferExtras {
if self.root_device_speed.get() == Some(UsbSpeed::Full12)
&& speed == UsbSpeed::Low1_5
{
TransferExtras::WithPreamble
} else {
TransferExtras::Normal
}
}
pub async fn configure(
&self,
device: UnconfiguredDevice,
configuration_value: u8,
) -> Result<UsbDevice, UsbError> {
let transfer_extras = self.get_transfer_extras(device.usb_speed);
self.driver
.control_transfer(
device.address(),
transfer_extras,
device.packet_size_ep0,
SetupPacket {
bmRequestType: HOST_TO_DEVICE,
bRequest: SET_CONFIGURATION,
wValue: configuration_value as u16,
wIndex: 0,
wLength: 0,
},
DataPhase::None,
)
.await?;
let mut endpoints = SpecificConfiguration::new(configuration_value);
self.get_configuration(&device, &mut endpoints).await?;
Ok(UsbDevice {
usb_address: device.usb_address,
usb_speed: device.usb_speed,
packet_size_ep0: device.packet_size_ep0,
in_endpoints_bitmap: endpoints.in_endpoints,
out_endpoints_bitmap: endpoints.out_endpoints,
})
}
async fn new_device(
&self,
speed: UsbSpeed,
) -> Result<(UnaddressedDevice, DeviceInfo), UsbError> {
let transfer_extras = self.get_transfer_extras(speed);
let mut descriptors = [0u8; 18];
let sz = self
.driver
.control_transfer(
0,
transfer_extras,
8,
SetupPacket {
bmRequestType: DEVICE_TO_HOST,
bRequest: GET_DESCRIPTOR,
wValue: ((DEVICE_DESCRIPTOR as u16) << 8),
wIndex: 0,
wLength: 8,
},
DataPhase::In(&mut descriptors),
)
.await?;
if sz < 8 {
debug::println!("control in {}/8", sz);
return Err(UsbError::ProtocolError);
}
let packet_size_ep0 = descriptors[7];
let sz = self
.driver
.control_transfer(
0,
transfer_extras,
packet_size_ep0,
SetupPacket {
bmRequestType: DEVICE_TO_HOST,
bRequest: GET_DESCRIPTOR,
wValue: ((DEVICE_DESCRIPTOR as u16) << 8),
wIndex: 0,
wLength: 18,
},
DataPhase::In(&mut descriptors),
)
.await?;
if sz < 18 {
debug::println!("control in {}/18", sz);
return Err(UsbError::ProtocolError);
}
let vid = u16::from_le_bytes([descriptors[8], descriptors[9]]);
let pid = u16::from_le_bytes([descriptors[10], descriptors[11]]);
Ok((
UnaddressedDevice {
usb_speed: speed,
packet_size_ep0,
},
DeviceInfo {
vid,
pid,
class: descriptors[4],
subclass: descriptors[5],
},
))
}
async fn set_address(
&self,
device: UnaddressedDevice,
address: u8,
) -> Result<UnconfiguredDevice, UsbError> {
let transfer_extras = self.get_transfer_extras(device.usb_speed);
self.driver
.control_transfer(
0,
transfer_extras,
device.packet_size_ep0,
SetupPacket {
bmRequestType: HOST_TO_DEVICE,
bRequest: SET_ADDRESS,
wValue: address as u16,
wIndex: 0,
wLength: 0,
},
DataPhase::None,
)
.await?;
Ok(UnconfiguredDevice {
usb_address: address,
usb_speed: device.usb_speed,
packet_size_ep0: device.packet_size_ep0,
})
}
pub async fn control_transfer(
&self,
device: &UsbDevice,
setup: SetupPacket,
data_phase: DataPhase<'_>,
) -> Result<usize, UsbError> {
let transfer_extras = self.get_transfer_extras(device.usb_speed);
self.driver
.control_transfer(
device.usb_address,
transfer_extras,
device.packet_size_ep0,
setup,
data_phase,
)
.await
}
pub async fn clear_halt(&self, ep: &BulkIn) -> Result<(), UsbError> {
self.driver
.control_transfer(
ep.usb_address,
TransferExtras::Normal,
8,
SetupPacket {
bmRequestType: 2,
bRequest: CLEAR_FEATURE,
wValue: 0, wIndex: (ep.endpoint | 0x80) as u16,
wLength: 0,
},
DataPhase::None,
)
.await?;
ep.data_toggle.set(false); Ok(())
}
pub fn bulk_in_transfer<'a>(
&'a self,
ep: &'a BulkIn,
data: &'a mut [u8],
transfer_type: TransferType,
) -> impl Future<Output = Result<usize, UsbError>> + 'a {
self.driver.bulk_in_transfer(
ep.usb_address,
ep.endpoint,
64, data,
transfer_type,
&ep.data_toggle,
)
}
pub fn bulk_out_transfer<'a>(
&'a self,
ep: &'a BulkOut,
data: &'a [u8],
transfer_type: TransferType,
) -> impl Future<Output = Result<usize, UsbError>> + 'a {
self.driver.bulk_out_transfer(
ep.usb_address,
ep.endpoint,
64, data,
transfer_type,
&ep.data_toggle,
)
}
pub fn interrupt_endpoint_in(
&self,
device: &UsbDevice,
endpoint: u8,
max_packet_size: u16,
interval_ms: u8,
) -> impl Stream<Item = InterruptPacket> + '_ {
let transfer_extras = self.get_transfer_extras(device.usb_speed);
self.driver
.alloc_interrupt_pipe(
device.usb_address,
transfer_extras,
endpoint,
max_packet_size,
interval_ms,
)
.flatten_stream()
}
pub async fn get_configuration(
&self,
device: &UnconfiguredDevice,
visitor: &mut impl DescriptorVisitor,
) -> Result<(), UsbError> {
let transfer_extras = self.get_transfer_extras(device.usb_speed);
let mut buf = [0u8; 256];
let mut sz = self
.driver
.control_transfer(
device.address(),
transfer_extras,
device.packet_size_ep0,
SetupPacket {
bmRequestType: DEVICE_TO_HOST,
bRequest: GET_DESCRIPTOR,
wValue: ((CONFIGURATION_DESCRIPTOR as u16) << 8),
wIndex: 0,
wLength: 9,
},
DataPhase::In(&mut buf),
)
.await?;
let total_length = buf[2] as usize | ((buf[3] as usize) << 8);
if total_length > 9 {
if total_length <= buf.len() {
sz = self
.driver
.control_transfer(
device.address(),
transfer_extras,
device.packet_size_ep0,
SetupPacket {
bmRequestType: DEVICE_TO_HOST,
bRequest: GET_DESCRIPTOR,
wValue: ((CONFIGURATION_DESCRIPTOR as u16) << 8),
wIndex: 0,
wLength: total_length as u16,
},
DataPhase::In(&mut buf),
)
.await?;
} else {
return Err(UsbError::ProtocolError);
}
}
crate::wire::parse_descriptors(&buf[0..sz], visitor);
Ok(())
}
pub async fn get_basic_configuration(
&self,
device: &UnconfiguredDevice,
) -> Result<BasicConfiguration, UsbError> {
let mut bd = BasicConfiguration::default();
self.get_configuration(device, &mut bd).await?;
if bd.num_configurations == 0 || bd.configuration_value == 0 {
Err(UsbError::ProtocolError)
} else {
Ok(bd)
}
}
async fn new_hub(
&self,
hub_state: &HubState<HC>,
device: UnconfiguredDevice,
) -> Result<UsbDevice, UsbError> {
debug::println!("gbc!");
let bc = self.get_basic_configuration(&device).await?;
debug::println!("cfg: {:?}", bc);
let device = self.configure(device, bc.configuration_value).await?;
hub_state.try_add(
&self.driver,
device.address(),
bc.in_endpoints.trailing_zeros() as u8,
device.packet_size_ep0,
9,
)?;
let mut descriptors = [0u8; 64];
let sz = self
.driver
.control_transfer(
device.address(),
TransferExtras::Normal,
device.packet_size_ep0,
SetupPacket {
bmRequestType: DEVICE_TO_HOST | CLASS_REQUEST,
bRequest: GET_DESCRIPTOR,
wValue: (HUB_DESCRIPTOR as u16) << 8,
wIndex: 0,
wLength: 64,
},
DataPhase::In(&mut descriptors),
)
.await?;
if sz < core::mem::size_of::<HubDescriptor>() {
return Err(UsbError::ProtocolError);
}
let ports = descriptors[2];
debug::println!("{}-port hub", ports);
for port in 1..=ports {
self.set_port_feature(device.address(), port, PORT_POWER)
.await?;
}
Ok(device)
}
async fn get_hub_port_status(
&self,
hub_address: u8,
port: u8,
) -> Result<(u16, u16), UsbError> {
let mut data = [0u8; 4];
self.driver
.control_transfer(
hub_address,
TransferExtras::Normal,
8,
SetupPacket {
bmRequestType: DEVICE_TO_HOST
| CLASS_REQUEST
| RECIPIENT_OTHER,
bRequest: GET_STATUS,
wValue: 0,
wIndex: port as u16,
wLength: 4,
},
DataPhase::In(&mut data),
)
.await?;
Ok((
u16::from_le_bytes([data[0], data[1]]),
u16::from_le_bytes([data[2], data[3]]),
))
}
async fn clear_port_feature(
&self,
hub_address: u8,
port: u8,
feature: u16,
) -> Result<(), UsbError> {
self.driver
.control_transfer(
hub_address,
TransferExtras::Normal,
8,
SetupPacket {
bmRequestType: HOST_TO_DEVICE
| CLASS_REQUEST
| RECIPIENT_OTHER,
bRequest: CLEAR_FEATURE,
wValue: feature,
wIndex: port as u16,
wLength: 0,
},
DataPhase::None,
)
.await?;
Ok(())
}
async fn set_port_feature(
&self,
hub_address: u8,
port: u8,
feature: u16,
) -> Result<(), UsbError> {
self.driver
.control_transfer(
hub_address,
TransferExtras::Normal,
8,
SetupPacket {
bmRequestType: HOST_TO_DEVICE
| CLASS_REQUEST
| RECIPIENT_OTHER,
bRequest: SET_FEATURE,
wValue: feature,
wIndex: port as u16,
wLength: 0,
},
DataPhase::None,
)
.await?;
Ok(())
}
async fn handle_hub_packet<
D: Future<Output = ()>,
F: Fn(usize) -> D + 'static + Clone,
>(
&self,
hub_state: &HubState<HC>,
packet: &InterruptPacket,
delay_ms: F,
) -> Result<DeviceEvent, UsbError> {
debug::println!(
"Hub int {} [{}; {}]",
packet.address,
packet.data[0],
packet.size
);
if packet.size == 0 {
return Err(UsbError::ProtocolError);
}
let mut port_bitmap = packet.data[0] as u32;
if packet.size > 1 {
port_bitmap |= (packet.data[1] as u32) << 8;
}
let port_bitmap = BitSet(port_bitmap);
for port in port_bitmap.iter() {
debug::println!("I'm told to investigate port {}", port);
let (state, changes) =
self.get_hub_port_status(packet.address, port).await?;
debug::println!(
" port {} status3 {:x} {:x}",
port,
state,
changes
);
if changes != 0 {
let bit = changes.trailing_zeros();
if bit < 5 {
self.clear_port_feature(
packet.address,
port,
(bit + 16) as u16,
)
.await?;
}
if bit == 0 {
if (state & 1) == 0 {
let mask = hub_state
.topology
.borrow_mut()
.device_disconnect(packet.address, port);
return Ok(DeviceEvent::Disconnect(mask));
}
self.set_port_feature(packet.address, port, PORT_RESET)
.await?;
delay_ms(50).await;
let (state, _changes) =
self.get_hub_port_status(packet.address, port).await?;
if (state & 2) != 0 {
let speed = match state & 0x600 {
0 => UsbSpeed::Full12,
0x400 => UsbSpeed::High480,
_ => UsbSpeed::Low1_5,
};
let (device, info) = self.new_device(speed).await?;
let is_hub = info.class == HUB_CLASSCODE;
let address = hub_state
.topology
.borrow_mut()
.device_connect(packet.address, port, is_hub)
.ok_or(UsbError::TooManyDevices)?;
let device = self.set_address(device, address).await?;
if is_hub {
debug::println!("It's a hub");
return Ok(DeviceEvent::HubConnect(
self.new_hub(hub_state, device).await?,
));
}
return Ok(DeviceEvent::Connect(device, info));
}
}
}
}
Ok(DeviceEvent::None)
}
}
pub unsafe fn create_test_device(
in_endpoints_bitmap: u16,
out_endpoints_bitmap: u16,
) -> UsbDevice {
UsbDevice {
usb_address: 255,
usb_speed: UsbSpeed::Full12,
packet_size_ep0: 64,
in_endpoints_bitmap,
out_endpoints_bitmap,
}
}
#[cfg(all(test, feature = "std"))]
#[path = "tests/usb_bus.rs"]
mod tests;