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