Skip to main content

embassy_usb_driver/
lib.rs

1#![no_std]
2#![allow(async_fn_in_trait)]
3#![allow(unsafe_op_in_unsafe_fn)]
4#![doc = include_str!("../README.md")]
5#![warn(missing_docs)]
6
7pub mod host;
8
9/// Speed of a device or port
10#[derive(Copy, Clone, Eq, PartialEq, Debug)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12pub enum Speed {
13    /// 1.5 Mbit/s
14    Low,
15    /// 12 Mbit/s
16    Full,
17    /// 480 Mbit/s
18    High,
19}
20
21impl Speed {
22    /// Provides the default max_packet_size for a given port speed
23    pub const fn max_packet_size(self) -> u16 {
24        match self {
25            Speed::Low => 8,
26            Speed::Full => 64,
27            Speed::High => 64, // USB 2.0 spec requires 64 for high-speed control EP0
28        }
29    }
30}
31
32/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from
33/// the perspective of the host, which is backward for devices, but the standard directions are used
34/// for consistency.
35///
36/// The values of the enum also match the direction bit used in endpoint addresses and control
37/// request types.
38#[derive(Copy, Clone, Eq, PartialEq, Debug)]
39#[cfg_attr(feature = "defmt", derive(defmt::Format))]
40pub enum Direction {
41    /// Host to device (OUT)
42    Out,
43    /// Device to host (IN)
44    In,
45}
46
47/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the
48/// transfer bmAttributes transfer type bits.
49#[repr(u8)]
50#[derive(Copy, Clone, Eq, PartialEq, Debug)]
51#[cfg_attr(feature = "defmt", derive(defmt::Format))]
52pub enum EndpointType {
53    /// Control endpoint. Used for device management. Only the host can initiate requests. Usually
54    /// used only endpoint 0.
55    Control = 0b00,
56    /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet.
57    Isochronous = 0b01,
58    /// Bulk endpoint. Used for large amounts of best-effort reliable data.
59    Bulk = 0b10,
60    /// Interrupt endpoint. Used for small amounts of time-critical reliable data.
61    Interrupt = 0b11,
62}
63
64/// Type-safe endpoint address.
65#[derive(Debug, Clone, Copy, Eq, PartialEq)]
66#[cfg_attr(feature = "defmt", derive(defmt::Format))]
67pub struct EndpointAddress(u8);
68
69impl From<u8> for EndpointAddress {
70    #[inline]
71    fn from(addr: u8) -> EndpointAddress {
72        EndpointAddress(addr)
73    }
74}
75
76impl From<EndpointAddress> for u8 {
77    #[inline]
78    fn from(addr: EndpointAddress) -> u8 {
79        addr.0
80    }
81}
82
83impl EndpointAddress {
84    const INBITS: u8 = 0x80;
85
86    /// Constructs a new EndpointAddress with the given index and direction.
87    #[inline]
88    pub fn from_parts(index: usize, dir: Direction) -> Self {
89        let dir_u8 = match dir {
90            Direction::Out => 0x00,
91            Direction::In => Self::INBITS,
92        };
93        EndpointAddress(index as u8 | dir_u8)
94    }
95
96    /// Gets the direction part of the address.
97    #[inline]
98    pub fn direction(&self) -> Direction {
99        if (self.0 & Self::INBITS) != 0 {
100            Direction::In
101        } else {
102            Direction::Out
103        }
104    }
105
106    /// Returns true if the direction is IN, otherwise false.
107    #[inline]
108    pub fn is_in(&self) -> bool {
109        (self.0 & Self::INBITS) != 0
110    }
111
112    /// Returns true if the direction is OUT, otherwise false.
113    #[inline]
114    pub fn is_out(&self) -> bool {
115        (self.0 & Self::INBITS) == 0
116    }
117
118    /// Gets the index part of the endpoint address.
119    #[inline]
120    pub fn index(&self) -> usize {
121        (self.0 & !Self::INBITS) as usize
122    }
123}
124
125/// Information for an endpoint.
126#[derive(Copy, Clone, Eq, PartialEq, Debug)]
127#[cfg_attr(feature = "defmt", derive(defmt::Format))]
128pub struct EndpointInfo {
129    /// Endpoint's address.
130    pub addr: EndpointAddress,
131    /// Endpoint's type.
132    pub ep_type: EndpointType,
133    /// Max packet size, in bytes.
134    pub max_packet_size: u16,
135    /// Polling interval, in milliseconds.
136    pub interval_ms: u8,
137}
138
139/// Main USB device driver trait.
140///
141/// Implement this to add support for a new hardware platform.
142pub trait Driver<'a> {
143    /// Type of the OUT endpoints for this driver.
144    type EndpointOut: EndpointOut + 'a;
145    /// Type of the IN endpoints for this driver.
146    type EndpointIn: EndpointIn + 'a;
147    /// Type of the control pipe for this driver.
148    type ControlPipe: ControlPipe + 'a;
149    /// Type for bus control for this driver.
150    type Bus: Bus + 'a;
151
152    /// Allocates an OUT endpoint.
153    ///
154    /// This method is called by the USB stack to allocate endpoints.
155    /// It can only be called before [`start`](Self::start) is called.
156    ///
157    /// # Arguments
158    ///
159    /// * `ep_type` - the endpoint's type.
160    /// * `max_packet_size` - Maximum packet size in bytes.
161    /// * `interval_ms` - Polling interval parameter for interrupt endpoints.
162    fn alloc_endpoint_out(
163        &mut self,
164        ep_type: EndpointType,
165        ep_addr: Option<EndpointAddress>,
166        max_packet_size: u16,
167        interval_ms: u8,
168    ) -> Result<Self::EndpointOut, EndpointAllocError>;
169
170    /// Allocates an IN endpoint.
171    ///
172    /// This method is called by the USB stack to allocate endpoints.
173    /// It can only be called before [`start`](Self::start) is called.
174    ///
175    /// # Arguments
176    ///
177    /// * `ep_type` - the endpoint's type.
178    /// * `max_packet_size` - Maximum packet size in bytes.
179    /// * `interval_ms` - Polling interval parameter for interrupt endpoints.
180    fn alloc_endpoint_in(
181        &mut self,
182        ep_type: EndpointType,
183        ep_addr: Option<EndpointAddress>,
184        max_packet_size: u16,
185        interval_ms: u8,
186    ) -> Result<Self::EndpointIn, EndpointAllocError>;
187
188    /// Start operation of the USB device.
189    ///
190    /// This returns the `Bus` and `ControlPipe` instances that are used to operate
191    /// the USB device. Additionally, this makes all the previously allocated endpoints
192    /// start operating.
193    ///
194    /// This consumes the `Driver` instance, so it's no longer possible to allocate more
195    /// endpoints.
196    fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe);
197}
198
199/// USB bus trait.
200///
201/// This trait provides methods that act on the whole bus. It is kept owned by
202/// the main USB task, and used to manage the bus.
203pub trait Bus {
204    /// Enable the USB peripheral.
205    async fn enable(&mut self);
206
207    /// Disable and powers down the USB peripheral.
208    async fn disable(&mut self);
209
210    /// Wait for a bus-related event.
211    ///
212    /// This method should asynchronously wait for an event to happen, then
213    /// return it. See [`Event`] for the list of events this method should return.
214    async fn poll(&mut self) -> Event;
215
216    /// Enable or disable an endpoint.
217    fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool);
218
219    /// Set or clear the STALL condition for an endpoint.
220    ///
221    /// If the endpoint is an OUT endpoint, it should be prepared to receive data again.
222    fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool);
223
224    /// Get whether the STALL condition is set for an endpoint.
225    fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool;
226
227    /// Simulate a disconnect from the USB bus, causing the host to reset and re-enumerate the
228    /// device.
229    ///
230    /// The default implementation just returns `Unsupported`.
231    ///
232    /// # Errors
233    ///
234    /// * [`Unsupported`] - This UsbBus implementation doesn't support
235    ///   simulating a disconnect or it has not been enabled at creation time.
236    fn force_reset(&mut self) -> Result<(), Unsupported> {
237        Err(Unsupported)
238    }
239
240    /// Initiate a remote wakeup of the host by the device.
241    ///
242    /// # Errors
243    ///
244    /// * [`Unsupported`] - This UsbBus implementation doesn't support
245    ///   remote wakeup or it has not been enabled at creation time.
246    async fn remote_wakeup(&mut self) -> Result<(), Unsupported>;
247}
248
249/// Endpoint trait, common for OUT and IN.
250/// Endpoint is a buffer on a device that stores rx/tx data.
251/// Endpoint can be thought of as one end of a pipe/channel.
252pub trait Endpoint {
253    /// Get the endpoint address
254    fn info(&self) -> &EndpointInfo;
255
256    /// Wait for the endpoint to be enabled.
257    async fn wait_enabled(&mut self);
258}
259
260/// OUT Endpoint trait.
261pub trait EndpointOut: Endpoint {
262    /// Read a single packet of data from the endpoint, and return the actual length of
263    /// the packet.
264    ///
265    /// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
266    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>;
267
268    /// Read until the buffer is full or we receive a short packet from the USB host returning the
269    /// actual length of the entire data block.
270    ///
271    /// This should also clear any NAK flags and prepare the endpoint to receive the next packet or
272    /// data block.
273    async fn read_transfer(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
274        let mut n = 0;
275        loop {
276            let i = self.read(&mut buf[n..]).await?;
277            n += i;
278            if i < self.info().max_packet_size as usize {
279                return Ok(n);
280            }
281        }
282    }
283}
284
285/// USB control pipe trait.
286///
287/// The USB control pipe owns both OUT endpoint 0 and IN endpoint 0 in a single
288/// unit, and manages them together to implement the control pipe state machine.
289///
290/// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that
291/// many USB peripherals treat the control pipe endpoints differently (different registers,
292/// different procedures), usually to accelerate processing in hardware somehow. A separate
293/// trait allows the driver to handle it specially.
294///
295/// The call sequences made by the USB stack to the ControlPipe are the following:
296///
297/// - control in/out with len=0:
298///
299/// ```not_rust
300/// setup()
301/// (...processing...)
302/// accept() or reject()
303/// ```
304///
305/// - control out for setting the device address:
306///
307/// ```not_rust
308/// setup()
309/// (...processing...)
310/// accept_set_address(addr) or reject()
311/// ```
312///
313/// - control out with len != 0:
314///
315/// ```not_rust
316/// setup()
317/// data_out(first=true, last=false)
318/// data_out(first=false, last=false)
319/// ...
320/// data_out(first=false, last=false)
321/// data_out(first=false, last=true)
322/// (...processing...)
323/// accept() or reject()
324/// ```
325///
326/// - control in with len != 0, accepted:
327///
328/// ```not_rust
329/// setup()
330/// (...processing...)
331/// data_in(first=true, last=false)
332/// data_in(first=false, last=false)
333/// ...
334/// data_in(first=false, last=false)
335/// data_in(first=false, last=true)
336/// (NO `accept()`!!! This is because calling `data_in` already implies acceptance.)
337/// ```
338///
339/// - control in with len != 0, rejected:
340///
341/// ```not_rust
342/// setup()
343/// (...processing...)
344/// reject()
345/// ```
346///
347/// The driver is responsible for handling the status stage. The stack DOES NOT do zero-length
348/// calls to `data_in` or `data_out` for the status zero-length packet. The status stage should
349/// be triggered by either `accept()`, or `data_in` with `last = true`.
350///
351/// Note that the host can abandon a control request and send a new SETUP packet any time. If
352/// a SETUP packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`,
353/// the driver must immediately return (with `EndpointError::Disabled` from `data_in`, `data_out`)
354/// to let the stack call `setup()` again to start handling the new control request. Not doing
355/// so will cause things to get stuck, because the host will never read/send the packet we're
356/// waiting for.
357pub trait ControlPipe {
358    /// Maximum packet size for the control pipe
359    fn max_packet_size(&self) -> usize;
360
361    /// Read a single setup packet from the endpoint.
362    async fn setup(&mut self) -> [u8; 8];
363
364    /// Read a DATA OUT packet into `buf` in response to a control write request.
365    ///
366    /// Must be called after `setup()` for requests with `direction` of `Out`
367    /// and `length` greater than zero.
368    async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError>;
369
370    /// Send a DATA IN packet with `data` in response to a control read request.
371    ///
372    /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`.
373    async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>;
374
375    /// Accept a control request.
376    ///
377    /// Causes the STATUS packet for the current request to be ACKed.
378    async fn accept(&mut self);
379
380    /// Reject a control request.
381    ///
382    /// Sets a STALL condition on the pipe to indicate an error.
383    async fn reject(&mut self);
384
385    /// Accept SET_ADDRESS control and change bus address.
386    ///
387    /// For most drivers this function should firstly call `accept()` and then change the bus address.
388    /// However, there are peripherals (Synopsys USB OTG) that have reverse order.
389    async fn accept_set_address(&mut self, addr: u8);
390}
391
392/// IN Endpoint trait.
393pub trait EndpointIn: Endpoint {
394    /// Write a single packet of data to the endpoint.
395    async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>;
396
397    /// Write all the data from buf to the endpoint one wMaxPacketSize chunk at a time.
398    ///
399    /// If the buffer size is evenly divisible by wMaxPacketSize, this will also ensure the
400    /// terminating zero-length-packet is transmitted.
401    async fn write_transfer(&mut self, buf: &[u8], needs_zlp: bool) -> Result<(), EndpointError> {
402        for chunk in buf.chunks(self.info().max_packet_size as usize) {
403            self.write(chunk).await?;
404        }
405        if needs_zlp && buf.len() % self.info().max_packet_size as usize == 0 {
406            self.write(&[]).await?;
407        }
408        Ok(())
409    }
410}
411
412#[derive(Copy, Clone, Eq, PartialEq, Debug)]
413#[cfg_attr(feature = "defmt", derive(defmt::Format))]
414/// Event returned by [`Bus::poll`].
415pub enum Event {
416    /// The USB reset condition has been detected.
417    Reset,
418
419    /// A USB suspend request has been detected or, in the case of self-powered devices, the device
420    /// has been disconnected from the USB bus.
421    Suspend,
422
423    /// A USB resume request has been detected after being suspended or, in the case of self-powered
424    /// devices, the device has been connected to the USB bus.
425    Resume,
426
427    /// The USB power has been detected.
428    PowerDetected,
429
430    /// The USB power has been removed. Not supported by all devices.
431    PowerRemoved,
432}
433
434/// Allocating an endpoint failed.
435///
436/// This can be due to running out of endpoints, or out of endpoint memory,
437/// or because the hardware doesn't support the requested combination of features.
438#[derive(Copy, Clone, Eq, PartialEq, Debug)]
439#[cfg_attr(feature = "defmt", derive(defmt::Format))]
440pub struct EndpointAllocError;
441
442/// Operation is unsupported by the driver.
443#[derive(Copy, Clone, Eq, PartialEq, Debug)]
444#[cfg_attr(feature = "defmt", derive(defmt::Format))]
445pub struct Unsupported;
446
447/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`]
448#[derive(Copy, Clone, Eq, PartialEq, Debug)]
449#[cfg_attr(feature = "defmt", derive(defmt::Format))]
450pub enum EndpointError {
451    /// Either the packet to be written is too long to fit in the transmission
452    /// buffer or the received packet is too long to fit in `buf`.
453    BufferOverflow,
454
455    /// The endpoint is disabled.
456    Disabled,
457}
458
459// TODO: remove before releasing embassy-usb-driver v0.3
460impl embedded_io_async::Error for EndpointError {
461    fn kind(&self) -> embedded_io_async::ErrorKind {
462        match self {
463            Self::BufferOverflow => embedded_io_async::ErrorKind::OutOfMemory,
464            Self::Disabled => embedded_io_async::ErrorKind::NotConnected,
465        }
466    }
467}
468
469impl core::fmt::Display for EndpointError {
470    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
471        match self {
472            Self::BufferOverflow => write!(f, "Buffer overflow"),
473            Self::Disabled => write!(f, "Endpoint disabled"),
474        }
475    }
476}
477impl core::error::Error for EndpointError {}