Skip to main content

embassy_usb_host/
handler.rs

1//! USB host device enumeration helpers.
2#![allow(missing_docs)]
3
4use embassy_usb_driver::Speed;
5use embassy_usb_driver::host::pipe::{self, IsIn, IsOut};
6use embassy_usb_driver::host::{HostError, SplitInfo, SplitSpeed, UsbPipe};
7
8use crate::control::ControlPipeExt;
9use crate::descriptor::{ConfigurationDescriptor, DeviceDescriptor, USBDescriptor};
10
11/// How a device's traffic reaches it on the bus.
12///
13/// Unifies the device speed with the optional split-transaction (or legacy
14/// `PRE` prefix) routing. Illegal combinations (a high-speed device reached
15/// through a TT or PRE) are unrepresentable: [`BusRoute::Translated`] wraps
16/// a [`SplitInfo`] whose [`SplitSpeed`] only admits low or full speed.
17#[derive(Copy, Clone, Eq, PartialEq, Debug)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub enum BusRoute {
20    /// No bus-level translation. The device runs at its native speed and is
21    /// addressed directly. Covers root-port devices and any device whose
22    /// traffic is not re-clocked by an intermediate hub (high-speed device
23    /// behind a high-speed hub, full-speed device behind a full-speed hub).
24    Direct(Speed),
25
26    /// The device is reached through a transaction translator on a
27    /// high-speed controller (USB 2.0 §11.14), or via a legacy `PRE` prefix
28    /// on a full-speed controller (USB 1.1 §11.8.6). The device's speed is
29    /// recorded inside the [`SplitInfo`].
30    Translated(SplitInfo),
31}
32
33impl BusRoute {
34    /// Speed at which the target device operates.
35    pub const fn device_speed(self) -> Speed {
36        match self {
37            BusRoute::Direct(s) => s,
38            BusRoute::Translated(info) => match info.device_speed() {
39                SplitSpeed::Low => Speed::Low,
40                SplitSpeed::Full => Speed::Full,
41            },
42        }
43    }
44
45    /// Returns the [`SplitInfo`] describing TT/PRE routing, or `None` when
46    /// the device is reached directly.
47    pub const fn split(self) -> Option<SplitInfo> {
48        match self {
49            BusRoute::Direct(_) => None,
50            BusRoute::Translated(info) => Some(info),
51        }
52    }
53}
54
55/// Information obtained through preliminary enumeration.
56#[derive(Clone, Copy, Debug)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58pub struct EnumerationInfo {
59    /// Assigned device address.
60    pub device_address: u8,
61    /// How the device's traffic reaches it on the bus (speed + optional
62    /// split-transaction / PRE-prefix routing).
63    pub route: BusRoute,
64    /// Parsed device descriptor.
65    pub device_desc: DeviceDescriptor,
66}
67
68impl EnumerationInfo {
69    /// Negotiated device speed.
70    pub const fn speed(&self) -> Speed {
71        self.route.device_speed()
72    }
73
74    /// Split-transaction routing, when this device is behind a hub that
75    /// requires splits (HS host reaching LS/FS device through a HS hub, or
76    /// FS host reaching LS device through a FS hub). `None` for a device
77    /// reached directly.
78    pub const fn split(&self) -> Option<SplitInfo> {
79        self.route.split()
80    }
81}
82
83impl EnumerationInfo {
84    /// Retrieves the active device configuration, or sets the default if none is active.
85    pub async fn active_config_or_set_default<'a, D: IsIn + IsOut, C: UsbPipe<pipe::Control, D>>(
86        &self,
87        channel: &mut C,
88        cfg_desc_buf: &'a mut [u8],
89    ) -> Result<ConfigurationDescriptor<'a>, HostError> {
90        Ok(match channel.active_configuration_value().await? {
91            Some(_) => self.get_active_configuration(channel, cfg_desc_buf).await?.unwrap(),
92            None => {
93                let default_cfg = self.get_configuration(0, channel, cfg_desc_buf).await?;
94                channel.set_configuration(default_cfg.configuration_value).await?;
95                default_cfg
96            }
97        })
98    }
99
100    /// Retrieves the active device configuration, or `None` if none is active.
101    pub async fn get_active_configuration<'a, D: IsIn, C: UsbPipe<pipe::Control, D>>(
102        &self,
103        channel: &mut C,
104        cfg_desc_buf: &'a mut [u8],
105    ) -> Result<Option<ConfigurationDescriptor<'a>>, HostError> {
106        let cfg_id = match channel.active_configuration_value().await? {
107            Some(v) => v.into(),
108            None => return Ok(None),
109        };
110
111        let mut index = None;
112        let mut cfg_len = 0;
113        for i in 0..self.device_desc.num_configurations {
114            let cfg_desc_short = channel
115                .request_descriptor::<ConfigurationDescriptor, { ConfigurationDescriptor::SIZE }>(i, false)
116                .await?;
117
118            if cfg_desc_short.configuration_value == cfg_id {
119                if cfg_desc_short.total_len as usize > cfg_desc_buf.len() {
120                    return Err(HostError::InsufficientMemory);
121                }
122                cfg_len = cfg_desc_short.total_len as usize;
123                index.replace(i);
124                break;
125            }
126        }
127
128        let index = index.ok_or(HostError::Other("Active configuration not found on device"))?;
129        let dest = &mut cfg_desc_buf[0..cfg_len];
130        channel
131            .request_descriptor_bytes(ConfigurationDescriptor::DESC_TYPE, index, dest)
132            .await?;
133
134        let cfg = ConfigurationDescriptor::try_from_slice(cfg_desc_buf).map_err(|_| HostError::InvalidDescriptor)?;
135        Ok(Some(cfg))
136    }
137
138    /// Retrieve a device configuration by index.
139    pub async fn get_configuration<'a, D: pipe::IsIn, C: UsbPipe<pipe::Control, D>>(
140        &self,
141        index: u8,
142        channel: &mut C,
143        cfg_desc_buf: &'a mut [u8],
144    ) -> Result<ConfigurationDescriptor<'a>, HostError> {
145        if index >= self.device_desc.num_configurations {
146            return Err(HostError::InvalidDescriptor);
147        }
148
149        let cfg_desc_short = channel
150            .request_descriptor::<ConfigurationDescriptor, { ConfigurationDescriptor::SIZE }>(index, false)
151            .await?;
152
153        let total_len = cfg_desc_short.total_len as usize;
154        if total_len > cfg_desc_buf.len() {
155            return Err(HostError::InsufficientMemory);
156        }
157        let dest = &mut cfg_desc_buf[0..total_len];
158        channel
159            .request_descriptor_bytes(ConfigurationDescriptor::DESC_TYPE, index, dest)
160            .await?;
161
162        trace!(
163            "Full Configuration Descriptor [{}]: {:?}",
164            cfg_desc_short.total_len, dest
165        );
166
167        ConfigurationDescriptor::try_from_slice(dest).map_err(|_| HostError::InvalidDescriptor)
168    }
169}
170
171#[derive(Copy, Clone, Eq, PartialEq, Debug)]
172#[cfg_attr(feature = "defmt", derive(defmt::Format))]
173pub enum HandlerEvent<T> {
174    NoChange,
175    HandlerDisconnected,
176    HandlerEvent(T),
177}
178
179#[derive(Copy, Clone, Eq, PartialEq, Debug)]
180#[cfg_attr(feature = "defmt", derive(defmt::Format))]
181pub enum RegisterError {
182    NoSupportedInterface,
183    InvalidDescriptor,
184    HostError(HostError),
185}
186
187impl From<HostError> for RegisterError {
188    fn from(value: HostError) -> Self {
189        RegisterError::HostError(value)
190    }
191}