Skip to main content

embassy_usb_driver/
host.rs

1//! USB host driver traits and data types.
2
3use core::time::Duration;
4
5use crate::{EndpointInfo, EndpointType, Speed};
6
7/// Speed of a low- or full-speed device reached through split transactions
8/// (USB 2.0 §11.14) or a `PRE` prefix (USB 1.1 §11.8.6).
9///
10/// High-speed devices are not valid split targets; split metadata only applies
11/// to devices operating at low or full speed behind a hub.
12#[derive(Copy, Clone, Eq, PartialEq, Debug)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub enum SplitSpeed {
15    /// 1.5 Mbit/s
16    Low,
17    /// 12 Mbit/s
18    Full,
19}
20
21/// Per-pipe information necessary to encode a split-transaction token
22/// (USB 2.0 §11.14) or a legacy full-speed `PRE` packet (USB 1.1 §11.8.6).
23#[derive(Copy, Clone, Eq, PartialEq, Debug)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25pub struct SplitInfo {
26    hub_addr: u8,
27    port: u8,
28    device_speed: SplitSpeed,
29}
30
31impl SplitInfo {
32    /// Create a new [`SplitInfo`].
33    ///
34    /// `hub_addr` is the USB address of the hub that owns the Transaction
35    /// Translator; `port` is the 1-based port number on that hub where the
36    /// target device is attached; `device_speed` is the speed of that target
37    /// device ([`SplitSpeed::Low`] or [`SplitSpeed::Full`] only).
38    pub const fn new(hub_addr: u8, port: u8, device_speed: SplitSpeed) -> Self {
39        Self {
40            hub_addr,
41            port,
42            device_speed,
43        }
44    }
45
46    /// USB address of the hub that owns the Transaction Translator.
47    pub const fn hub_addr(self) -> u8 {
48        self.hub_addr
49    }
50
51    /// 1-based port number on the hub where the target device is attached.
52    pub const fn port(self) -> u8 {
53        self.port
54    }
55
56    /// Speed of the split target device (low or full only).
57    pub const fn device_speed(self) -> SplitSpeed {
58        self.device_speed
59    }
60}
61
62/// Errors returned by [`UsbPipe`] operations.
63#[derive(Copy, Clone, Eq, PartialEq, Debug)]
64#[cfg_attr(feature = "defmt", derive(defmt::Format))]
65#[non_exhaustive]
66pub enum PipeError {
67    /// The packet is too long to fit in the buffer.
68    BufferOverflow,
69
70    /// CRC or other hardware-level framing error.
71    BadResponse,
72
73    /// The device sent more data than expected (babble).
74    Babble,
75
76    /// Data toggle sequence mismatch detected.
77    DataToggleError,
78
79    /// Transaction was canceled
80    Canceled,
81
82    /// The device endpoint is stalled.
83    Stall,
84
85    /// Device did not respond in time
86    Timeout,
87
88    /// Device disconnected
89    Disconnected,
90}
91
92/// Device has been attached/detached
93#[derive(Copy, Clone, Eq, PartialEq, Debug)]
94#[cfg_attr(feature = "defmt", derive(defmt::Format))]
95#[non_exhaustive]
96pub enum DeviceEvent {
97    /// Indicates a root-device has become attached
98    Connected(Speed),
99
100    /// Indicates that a device has been detached
101    Disconnected,
102
103    /// Root port overcurrent protection tripped.
104    Overcurrent,
105}
106
107/// Indicates type of error of Host interface
108#[derive(Copy, Clone, Eq, PartialEq, Debug)]
109#[cfg_attr(feature = "defmt", derive(defmt::Format))]
110#[non_exhaustive]
111pub enum HostError {
112    /// A pipe-level transfer error occurred.
113    PipeError(PipeError),
114    /// The control request was not acknowledged by the device.
115    RequestFailed,
116    /// A descriptor returned by the device could not be parsed.
117    InvalidDescriptor,
118    /// No free device slots available.
119    OutOfSlots,
120    /// No free host pipes available.
121    OutOfPipes,
122    /// The addressed device does not exist.
123    NoSuchDevice,
124    /// Insufficient memory for the requested operation.
125    InsufficientMemory,
126    /// An unspecified error with a static description.
127    Other(&'static str),
128}
129
130impl From<PipeError> for HostError {
131    fn from(value: PipeError) -> Self {
132        HostError::PipeError(value)
133    }
134}
135
136/// Pipe allocator trait for USB host drivers.
137///
138/// Implementations are expected to back allocator state with `'d`-lifetime
139/// storage (typically statics or user-provided `&'d` buffers), not with
140/// fields on the controller struct.
141pub trait UsbHostAllocator<'d>: Sized + Clone {
142    /// Pipe implementation produced by this allocator.
143    type Pipe<T: pipe::Type, D: pipe::Direction>: UsbPipe<T, D> + 'd;
144
145    /// Allocate a pipe for communication with a device endpoint.
146    ///
147    /// This can be a scarce resource; for one-off requests please scope the
148    /// pipe so that it is dropped after completion.
149    ///
150    /// `split` — when `Some`, every transfer on this pipe is routed as a
151    /// split transaction through the specified hub's TT (USB 2.0 §11.14), or
152    /// as a legacy `PRE` packet on full-speed controllers (USB 1.1 §11.8.6).
153    /// Pass `None` when the device is reached directly (host at the same
154    /// speed as the device, or the device is high-speed).
155    fn alloc_pipe<T: pipe::Type, D: pipe::Direction>(
156        &self,
157        addr: u8,
158        endpoint: &EndpointInfo,
159        split: Option<SplitInfo>,
160    ) -> Result<Self::Pipe<T, D>, HostError>;
161}
162
163/// Main USB host controller trait.
164///
165/// Covers the bus-level operations that must be serialised on a single
166/// controller instance (root-port event waiting, bus reset). Pipe allocation
167/// lives on the companion [`UsbHostAllocator`] trait.
168///
169/// Implemented by the HAL.
170pub trait UsbHostController<'d>: Sized {
171    /// Pipe allocator associated with this controller.
172    type Allocator: UsbHostAllocator<'d>;
173
174    /// Return an allocator handle.
175    ///
176    /// Callers may keep the handle and use it to allocate pipes concurrently
177    /// with [`wait_for_device_event`](Self::wait_for_device_event)
178    /// or [`bus_reset`](Self::bus_reset).
179    fn allocator(&self) -> Self::Allocator;
180
181    /// Wait for a root-port attach/detach.
182    ///
183    /// On attach, the implementation must drive a bus reset to completion
184    /// before returning and must report the speed that the device settled
185    /// on after reset.
186    async fn wait_for_device_event(&mut self) -> DeviceEvent;
187
188    /// Force a bus reset on the root port.
189    ///
190    /// Invalidates every pipe currently allocated against addresses other
191    /// than 0. Used to recover from a misbehaving device or to force
192    /// re-enumeration without unplug.
193    async fn bus_reset(&mut self);
194}
195
196/// Type-level pipe markers for endpoint type and direction.
197///
198/// These structs and traits are used as generic parameters on [`UsbPipe`]
199/// to statically enforce correct endpoint type and direction at compile time.
200///
201/// All marker traits are sealed — they cannot be implemented outside this crate.
202pub mod pipe {
203    use super::EndpointType;
204
205    mod sealed {
206        pub trait Sealed {}
207    }
208
209    /// Marker trait for the endpoint transfer type of a pipe.
210    pub trait Type: sealed::Sealed + 'static {
211        /// Returns the [`EndpointType`] this marker represents.
212        fn ep_type() -> EndpointType;
213    }
214
215    /// Marker for a control endpoint pipe.
216    pub struct Control {}
217    /// Marker for an interrupt endpoint pipe.
218    pub struct Interrupt {}
219    /// Marker for a bulk endpoint pipe.
220    pub struct Bulk {}
221    /// Marker for an isochronous endpoint pipe.
222    pub struct Isochronous {}
223
224    impl sealed::Sealed for Control {}
225    impl sealed::Sealed for Interrupt {}
226    impl sealed::Sealed for Bulk {}
227    impl sealed::Sealed for Isochronous {}
228
229    impl Type for Control {
230        fn ep_type() -> EndpointType {
231            EndpointType::Control
232        }
233    }
234    impl Type for Interrupt {
235        fn ep_type() -> EndpointType {
236            EndpointType::Interrupt
237        }
238    }
239    impl Type for Bulk {
240        fn ep_type() -> EndpointType {
241            EndpointType::Bulk
242        }
243    }
244    impl Type for Isochronous {
245        fn ep_type() -> EndpointType {
246            EndpointType::Isochronous
247        }
248    }
249
250    /// Trait bound satisfied only by [`Control`] pipes.
251    #[diagnostic::on_unimplemented(message = "This is not a CONTROL pipe")]
252    pub trait IsControl: Type {}
253    impl IsControl for Control {}
254
255    /// Trait bound satisfied only by [`Interrupt`] pipes.
256    #[diagnostic::on_unimplemented(message = "This is not an INTERRUPT pipe")]
257    pub trait IsInterrupt: Type {}
258    impl IsInterrupt for Interrupt {}
259
260    /// Trait bound satisfied only by [`Bulk`] or [`Interrupt`] pipes.
261    #[diagnostic::on_unimplemented(message = "This is not a BULK or INTERRUPT pipe")]
262    pub trait IsBulkOrInterrupt: Type {}
263    impl IsBulkOrInterrupt for Bulk {}
264    impl IsBulkOrInterrupt for Interrupt {}
265
266    /// Marker trait for the transfer direction of a pipe.
267    pub trait Direction: sealed::Sealed + 'static {
268        /// Returns `true` if this direction supports IN (device-to-host) transfers.
269        fn is_in() -> bool;
270        /// Returns `true` if this direction supports OUT (host-to-device) transfers.
271        fn is_out() -> bool;
272    }
273
274    /// Marker for an IN-only (device-to-host) pipe.
275    pub struct In {}
276    /// Marker for an OUT-only (host-to-device) pipe.
277    pub struct Out {}
278    /// Marker for a bidirectional pipe (used for control endpoints).
279    pub struct InOut {}
280
281    impl sealed::Sealed for In {}
282    impl sealed::Sealed for Out {}
283    impl sealed::Sealed for InOut {}
284
285    impl Direction for In {
286        fn is_in() -> bool {
287            true
288        }
289        fn is_out() -> bool {
290            false
291        }
292    }
293    impl Direction for Out {
294        fn is_in() -> bool {
295            false
296        }
297        fn is_out() -> bool {
298            true
299        }
300    }
301    impl Direction for InOut {
302        fn is_in() -> bool {
303            true
304        }
305        fn is_out() -> bool {
306            true
307        }
308    }
309
310    /// Trait bound satisfied by directions that support IN transfers.
311    #[diagnostic::on_unimplemented(message = "This is not an IN pipe")]
312    pub trait IsIn: Direction {}
313    impl IsIn for In {}
314    impl IsIn for InOut {}
315
316    /// Trait bound satisfied by directions that support OUT transfers.
317    #[diagnostic::on_unimplemented(message = "This is not an OUT pipe")]
318    pub trait IsOut: Direction {}
319    impl IsOut for Out {}
320    impl IsOut for InOut {}
321}
322
323/// Timeouts applied to a control pipe's NAK-retry behaviour.
324#[derive(Copy, Clone, Debug, Eq, PartialEq)]
325#[cfg_attr(feature = "defmt", derive(defmt::Format))]
326#[non_exhaustive]
327pub struct TimeoutConfig {
328    /// Maximum response timeout for transactions with a Data Stage.
329    pub data_timeout: Duration,
330
331    /// Maximum response timeout for transactions without a Data Stage.
332    pub no_data_timeout: Duration,
333}
334
335impl Default for TimeoutConfig {
336    fn default() -> Self {
337        TimeoutConfig {
338            data_timeout: Duration::from_millis(500),
339            no_data_timeout: Duration::from_millis(50),
340        }
341    }
342}
343
344/// ## USB Pipes
345/// These contain the required information to send a packet correctly to a device endpoint.
346/// The information is carried with the pipe on creation (see [`UsbHostAllocator::alloc_pipe`]).
347///
348/// It is up to the HAL's driver how to implement concurrent requests, some hardware IP may allow for multiple hardware channels
349///  while others may only have a single channel which needs to be multiplexed in software, while others still use DMA request linked-lists.
350/// Any of these are compatible with the UsbPipe with varying degrees of sync primitives required.
351///
352/// ### NAK handling
353/// Implementations must retry on NAK if appropriate for the transfer type.
354/// - For **control** transfers, the implementation should retry until the configurable timeout expires (see [`UsbPipe::set_timeout`]).
355/// - For **bulk** transfers, the implementation must retry indefinitely. Use `embassy_time::with_timeout` around the future to impose a deadline; dropping the future must abort the transfer.
356/// - For **interrupt** transfers, a NAK indicates no data is available; the implementation should poll again at the next interval.
357///
358/// ### Data toggle
359/// Implementations are responsible for maintaining the data toggle sequence for bulk and interrupt endpoints.
360/// The toggle is initialized to DATA0 when the pipe is allocated and should advance after each successful transfer.
361///
362/// ### Cancellation
363/// All transfer methods (`control_in`, `control_out`, `request_in`, `request_out`) are asynchronous.
364/// If the returned future is dropped before completion, the implementation must abort the in-progress
365/// transfer and leave the pipe in a consistent state for future requests.
366pub trait UsbPipe<T: pipe::Type, D: pipe::Direction> {
367    /// Send IN control request.
368    ///
369    /// Returns the number of bytes received into `buf`.
370    async fn control_in(&mut self, setup: &[u8; 8], buf: &mut [u8]) -> Result<usize, PipeError>
371    where
372        T: pipe::IsControl,
373        D: pipe::IsIn;
374
375    /// Send OUT control request
376    async fn control_out(&mut self, setup: &[u8; 8], buf: &[u8]) -> Result<(), PipeError>
377    where
378        T: pipe::IsControl,
379        D: pipe::IsOut;
380
381    /// Send IN request of type other from control
382    /// For interrupt pipes this will return the result of the next successful interrupt poll
383    async fn request_in(&mut self, buf: &mut [u8]) -> Result<usize, PipeError>
384    where
385        D: pipe::IsIn;
386
387    /// Send OUT request of type other from control
388    /// ensure_transaction_end: Send a zero length packet at the end of transaction if last packet is of max size.
389    async fn request_out(&mut self, buf: &[u8], ensure_transaction_end: bool) -> Result<(), PipeError>
390    where
391        D: pipe::IsOut;
392
393    /// Configure the timeouts of this pipe.
394    fn set_timeout(&mut self, timeout: TimeoutConfig)
395    where
396        T: pipe::IsControl;
397
398    /// Reset the host-side data toggle on this pipe to DATA0.
399    ///
400    /// The caller must invoke this method after:
401    ///
402    /// - `CLEAR_FEATURE(ENDPOINT_HALT)` successfully clears a functional
403    ///   stall on this endpoint.
404    /// - `SET_CONFIGURATION` succeeds (all non-control endpoints on the
405    ///   affected interfaces must be reset).
406    /// - `SET_INTERFACE` succeeds (all non-control endpoints on the
407    ///   affected interface must be reset).
408    fn reset_data_toggle(&mut self)
409    where
410        T: pipe::IsBulkOrInterrupt;
411}