Skip to main content

embassy_usb_host/
lib.rs

1#![no_std]
2#![allow(async_fn_in_trait)]
3#![doc = include_str!("../README.md")]
4#![warn(missing_docs)]
5
6// This mod MUST go first, so that the others see its macros.
7pub(crate) mod fmt;
8
9pub mod class;
10pub mod control;
11pub mod descriptor;
12pub mod handler;
13
14use core::cell::RefCell;
15use core::marker::PhantomData;
16
17use embassy_sync::blocking_mutex::Mutex as BlockingMutex;
18use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
19use embassy_sync::mutex::Mutex as AsyncMutex;
20use embassy_usb_driver::host::{DeviceEvent, HostError, PipeError, UsbHostAllocator, UsbHostController, UsbPipe, pipe};
21pub use embassy_usb_driver::host::{SplitInfo, SplitSpeed};
22use embassy_usb_driver::{Direction as UsbDirection, EndpointAddress, EndpointInfo, EndpointType, Speed};
23
24use crate::control::{ControlPipeExt, SetupPacket};
25use crate::descriptor::{ConfigurationDescriptor, DeviceDescriptor, USBDescriptor};
26pub use crate::handler::BusRoute;
27use crate::handler::EnumerationInfo;
28
29/// USB host enumeration error.
30#[derive(Debug)]
31#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32pub enum EnumerationError {
33    /// Transfer failed during enumeration.
34    Transfer(PipeError),
35    /// Invalid or unexpected descriptor received.
36    InvalidDescriptor,
37    /// Configuration buffer too small
38    ConfigBufferTooSmall(usize),
39    /// No free pipe for EP0 or no free device address.
40    NoPipe,
41    /// The device did not respond to a control request after retries.
42    RequestFailed,
43}
44
45impl From<PipeError> for EnumerationError {
46    fn from(e: PipeError) -> Self {
47        Self::Transfer(e)
48    }
49}
50
51impl From<HostError> for EnumerationError {
52    fn from(e: HostError) -> Self {
53        match e {
54            HostError::PipeError(e) => Self::Transfer(e),
55            HostError::InvalidDescriptor => Self::InvalidDescriptor,
56            HostError::RequestFailed => Self::RequestFailed,
57            _ => Self::NoPipe,
58        }
59    }
60}
61
62impl core::fmt::Display for EnumerationError {
63    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64        match self {
65            Self::Transfer(_e) => write!(f, "Transfer error during enumeration"),
66            Self::InvalidDescriptor => write!(f, "Invalid descriptor"),
67            Self::ConfigBufferTooSmall(size) => {
68                write!(f, "Configuration buffer too small: device requires {} bytes", size)
69            }
70            Self::NoPipe => write!(f, "No free pipe or no free device address"),
71            Self::RequestFailed => write!(f, "Device did not respond"),
72        }
73    }
74}
75
76impl core::error::Error for EnumerationError {}
77
78/// Shared bus-wide state used by a [`BusHandle`].
79///
80/// Holds the in-use USB device addresses (1–127) and an async mutex used to
81/// serialise enumerations on a single bus.
82///
83/// Addresses greater than 127 are not issued because their behavior is not specified (USB 2.0 §9.4.6).
84pub struct BusState {
85    addr_bitmap: BlockingMutex<CriticalSectionRawMutex, RefCell<[usize; 127usize.div_ceil(usize::BITS as usize)]>>,
86    enum_lock: AsyncMutex<CriticalSectionRawMutex, ()>,
87}
88
89impl BusState {
90    /// Create new, empty bus state.
91    pub const fn new() -> Self {
92        Self {
93            addr_bitmap: BlockingMutex::new(RefCell::new([0usize; _])),
94            enum_lock: AsyncMutex::new(()),
95        }
96    }
97
98    /// Allocate the next free device address (1–127),
99    /// marking it as in use.
100    ///
101    /// Returns `None` when every address in the 1–127 range is already
102    /// taken.
103    fn alloc_address(&self) -> Option<u8> {
104        self.addr_bitmap.lock(|b| {
105            let mut b = b.borrow_mut();
106            for addr in 1u8..=127 {
107                let word = (addr / usize::BITS as u8) as usize;
108                let bit = addr % usize::BITS as u8;
109                if b[word] & (1usize << bit) == 0 {
110                    b[word] |= 1usize << bit;
111                    return Some(addr);
112                }
113            }
114            None
115        })
116    }
117
118    /// Release a previously allocated device address.
119    ///
120    /// No-op if the address is out of range or was not marked as in use.
121    pub fn free_address(&self, addr: u8) {
122        if addr >= 1 && addr <= 127 {
123            self.addr_bitmap.lock(|b| {
124                let mut b = b.borrow_mut();
125                let word = (addr / usize::BITS as u8) as usize;
126                let bit = addr % usize::BITS as u8;
127                b[word] &= !(1usize << bit);
128            });
129        }
130    }
131}
132
133impl Default for BusState {
134    fn default() -> Self {
135        Self::new()
136    }
137}
138
139/// Bus-level controller for a single root USB controller.
140///
141/// Owns the [`UsbHostController`] implementation and exposes the
142/// bus-wide operations that must be serialised against each other
143/// (root-port event waiting, bus reset).
144///
145/// Pipe allocation and device enumeration live on the companion
146/// [`BusHandle`] returned alongside a `BusController` by the [`bus`]
147/// constructor.
148pub struct BusController<'d, C: UsbHostController<'d>> {
149    driver: C,
150    _phantom: PhantomData<&'d ()>,
151}
152
153impl<'d, C: UsbHostController<'d>> BusController<'d, C> {
154    /// Get a reference to the underlying controller.
155    pub fn controller(&self) -> &C {
156        &self.driver
157    }
158
159    /// Get a mutable reference to the underlying controller.
160    pub fn controller_mut(&mut self) -> &mut C {
161        &mut self.driver
162    }
163
164    /// Wait for a root-port attach/detach.
165    ///
166    /// On attach, the implementation drives a bus reset to completion
167    /// before returning and reports the speed the device settled on.
168    pub async fn wait_for_device_event(&mut self) -> DeviceEvent {
169        self.driver.wait_for_device_event().await
170    }
171
172    /// Wait for a device to connect on the root port.
173    ///
174    /// Issues a bus reset internally and returns the detected speed.
175    /// Spurious disconnects, overcurrent events, and other non-attach
176    /// events are silently absorbed.
177    pub async fn wait_for_connection(&mut self) -> Speed {
178        loop {
179            match self.driver.wait_for_device_event().await {
180                DeviceEvent::Connected(speed) => {
181                    info!("USB device connected, speed: {:?}", speed);
182                    return speed;
183                }
184                DeviceEvent::Disconnected => continue,
185                _ => continue,
186            }
187        }
188    }
189}
190
191/// Shareable handle for pipe allocation and device enumeration.
192///
193/// A `BusHandle` bundles a [`UsbHostAllocator`] produced by a
194/// [`UsbHostController`] with a reference to the bus-wide
195/// [`BusState`].
196///
197/// `BusHandle` itself implements [`UsbHostAllocator`] by forwarding to
198/// its inner allocator, so it can be passed directly to class driver
199/// constructors.
200#[derive(Clone)]
201pub struct BusHandle<'d, A: UsbHostAllocator<'d>> {
202    alloc: A,
203    state: &'d BusState,
204}
205
206impl<'d, A: UsbHostAllocator<'d>> BusHandle<'d, A> {
207    /// Borrow the shared bus state.
208    pub fn state(&self) -> &'d BusState {
209        self.state
210    }
211
212    /// Release a previously allocated device address.
213    ///
214    /// Equivalent to `self.state().free_address(addr)`. Must be called
215    /// by application code when a device is removed.
216    pub fn free_address(&self, addr: u8) {
217        self.state.free_address(addr);
218    }
219
220    /// Enumerate a connected device.
221    ///
222    /// Performs the standard enumeration sequence:
223    /// 1. Get device descriptor (first 8 bytes) to learn EP0 max packet size
224    /// 2. SET_ADDRESS to assign a unique address
225    /// 3. Get full device descriptor
226    /// 4. Get configuration descriptor
227    /// 5. SET_CONFIGURATION
228    ///
229    /// `route` describes how the device is reached on the bus (directly
230    /// at its native speed, or via split transactions / legacy `PRE`
231    /// through a hub's transaction translator).
232    ///
233    /// Enumerations are serialised bus-wide through the
234    /// [`BusState`]'s enumeration mutex.
235    ///
236    /// # Preconditions
237    ///
238    /// The caller must have placed the device into the default
239    /// (address 0) state *before* calling this method. For a root-port
240    /// device that means an upstream bus reset has completed; for a
241    /// hub-attached device, the parent hub's port reset must have
242    /// completed and [`BusRoute::Translated`] must carry the
243    /// appropriate [`SplitInfo`].
244    ///
245    /// Returns the [`EnumerationInfo`] for the device and the number of
246    /// bytes written to `config_buf`.
247    ///
248    /// [`SplitInfo`]: embassy_usb_driver::host::SplitInfo
249    pub async fn enumerate(
250        &self,
251        route: BusRoute,
252        config_buf: &mut [u8],
253    ) -> Result<(EnumerationInfo, usize), EnumerationError> {
254        use embassy_time::Timer;
255
256        use crate::descriptor::DeviceDescriptorPartial;
257
258        // Serialise enumerations against other concurrent callers on
259        // the same bus: the default (address 0) state is bus-global.
260        let _enum_guard = self.state.enum_lock.lock().await;
261
262        let addr = self.state.alloc_address().ok_or(EnumerationError::NoPipe)?;
263
264        let ep0_info = EndpointInfo {
265            addr: EndpointAddress::from_parts(0, UsbDirection::In),
266            ep_type: EndpointType::Control,
267            max_packet_size: route.device_speed().max_packet_size(),
268            interval_ms: 0,
269        };
270
271        let mut ch = self
272            .alloc
273            .alloc_pipe::<pipe::Control, pipe::InOut>(0, &ep0_info, route.split())
274            .map_err(|_| {
275                self.state.free_address(addr);
276                EnumerationError::NoPipe
277            })?;
278
279        trace!("[enum] Getting max_packet_size for new device");
280        let max_packet_size0 = {
281            let mut max_retries = 10;
282            loop {
283                match ch
284                    .request_descriptor::<DeviceDescriptorPartial, { DeviceDescriptorPartial::SIZE }>(0, false)
285                    .await
286                {
287                    Ok(desc) => break desc.max_packet_size0,
288                    Err(e) => {
289                        warn!("Request descriptor error: {:?}, retries: {}", e, max_retries);
290                        if max_retries > 0 {
291                            max_retries -= 1;
292                            Timer::after_millis(1).await;
293                            continue;
294                        } else {
295                            self.state.free_address(addr);
296                            return Err(e.into());
297                        }
298                    }
299                }
300            }
301        };
302        // USB 2.0 §9.6.1: legal EP0 max packet sizes are 8, 16, 32, 64.
303        if !matches!(max_packet_size0, 8 | 16 | 32 | 64) {
304            self.state.free_address(addr);
305            return Err(EnumerationError::InvalidDescriptor);
306        }
307
308        ch.device_set_address(addr).await?;
309        // USB 2.0 §9.2.6.3: allow the device a 2ms recovery interval after SET_ADDRESS.
310        Timer::after_millis(2).await;
311
312        // Drop pipe to re-allocate with new address and correct max_packet_size.
313        drop(ch);
314
315        let ep0_info = EndpointInfo {
316            addr: EndpointAddress::from_parts(0, UsbDirection::In),
317            ep_type: EndpointType::Control,
318            max_packet_size: max_packet_size0 as u16,
319            interval_ms: 0,
320        };
321
322        let mut ch = self
323            .alloc
324            .alloc_pipe::<pipe::Control, pipe::InOut>(addr, &ep0_info, route.split())
325            .map_err(|_| {
326                self.state.free_address(addr);
327                EnumerationError::NoPipe
328            })?;
329
330        let retries = 5;
331        let dev_desc = async {
332            for _ in 0..retries {
333                match ch
334                    .request_descriptor::<DeviceDescriptor, { DeviceDescriptor::SIZE }>(0, false)
335                    .await
336                {
337                    Err(HostError::PipeError(PipeError::Timeout)) => {
338                        Timer::after_millis(1).await;
339                        continue;
340                    }
341                    v => return v,
342                }
343            }
344            Err(HostError::PipeError(PipeError::Timeout))
345        }
346        .await?;
347
348        info!(
349            "Device: VID={:04x} PID={:04x} class={:02x}",
350            dev_desc.vendor_id, dev_desc.product_id, dev_desc.device_class
351        );
352
353        // Step 4: Get configuration descriptor header (9 bytes).
354        let setup = SetupPacket::get_config_descriptor(0, 9);
355        let n = ch
356            .control_in(&setup.to_bytes(), &mut config_buf[..9])
357            .await
358            .inspect_err(|_| self.state.free_address(addr))?;
359
360        if n < 9 {
361            self.state.free_address(addr);
362            return Err(EnumerationError::InvalidDescriptor);
363        }
364
365        let config_header = ConfigurationDescriptor::try_from_bytes(&config_buf[..9])
366            .map_err(|_| EnumerationError::InvalidDescriptor)?;
367        let total_len = config_header.total_len as usize;
368
369        if total_len > config_buf.len() {
370            self.state.free_address(addr);
371            return Err(EnumerationError::ConfigBufferTooSmall(total_len));
372        }
373
374        // Get full configuration descriptor.
375        let setup = SetupPacket::get_config_descriptor(0, total_len as u16);
376        let n = ch.control_in(&setup.to_bytes(), &mut config_buf[..total_len]).await?;
377
378        // USB 2.0 §9.4.3: the device must return exactly total_len bytes for a full config descriptor.
379        if n != total_len {
380            self.state.free_address(addr);
381            return Err(EnumerationError::InvalidDescriptor);
382        }
383
384        trace!("Config descriptor: {} bytes", n);
385
386        // Step 5: SET_CONFIGURATION.
387        let setup = SetupPacket::set_configuration(config_header.configuration_value);
388        ch.control_out(&setup.to_bytes(), &[])
389            .await
390            .inspect_err(|_| self.state.free_address(addr))?;
391
392        info!("Device configured (config={})", config_header.configuration_value);
393
394        // Pipe is released on drop.
395        drop(ch);
396
397        Ok((
398            EnumerationInfo {
399                device_address: addr,
400                route,
401                device_desc: dev_desc,
402            },
403            n,
404        ))
405    }
406}
407
408impl<'d, A: UsbHostAllocator<'d>> UsbHostAllocator<'d> for BusHandle<'d, A> {
409    type Pipe<T: pipe::Type, D: pipe::Direction> = A::Pipe<T, D>;
410
411    fn alloc_pipe<T: pipe::Type, D: pipe::Direction>(
412        &self,
413        addr: u8,
414        endpoint: &EndpointInfo,
415        split: Option<SplitInfo>,
416    ) -> Result<Self::Pipe<T, D>, HostError> {
417        self.alloc.alloc_pipe::<T, D>(addr, endpoint, split)
418    }
419}
420
421/// Split a [`UsbHostController`] into a bus controller / bus handle pair.
422///
423/// The returned [`BusController`] drives root-port events and bus
424/// resets. The [`BusHandle`] owns pipe allocation and device enumeration
425/// and can be freely shared, handed to class drivers, hub handlers, or
426/// other concurrent tasks while the controller task is blocked inside
427/// [`wait_for_device_event`](BusController::wait_for_device_event).
428pub fn bus<'d, C: UsbHostController<'d>>(
429    driver: C,
430    state: &'d BusState,
431) -> (BusController<'d, C>, BusHandle<'d, C::Allocator>) {
432    let alloc = driver.allocator();
433    (
434        BusController {
435            driver,
436            _phantom: PhantomData,
437        },
438        BusHandle { alloc, state },
439    )
440}