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 {}