usb_device/lib.rs
1//! Experimental device-side USB stack for embedded devices.
2//!
3//! ## Implementing a USB device
4//!
5//! A USB device consists of a [`UsbDevice`](device::UsbDevice) instance, one or more
6//! [`UsbClass`](crate::class::UsbClass)es, and a platform-specific [`UsbBus`](bus::UsbBus)
7//! implementation which together form a USB composite device.
8//!
9//! In the future USB device implementors will be able to use pre-existing peripheral driver crates
10//! and USB class implementation crates. The necessary types for the basic USB composite device
11//! implementation are available with:
12//!
13//! `use usb_device::prelude::*`.
14//!
15//! See the [`device`] module for a more complete example.
16//!
17//! ## USB classes
18//!
19//! For information on how to implement new USB classes, see the [`class`] module and the
20//! [`TestClass`](test_class::TestClass) source code for an example of a custom USB device class
21//! implementation. The necessary types for creating new classes are available with:
22//!
23//! `use usb_device::class_prelude::*`.
24//!
25//! ## USB peripheral drivers
26//!
27//! New peripheral driver crates can be created by implementing the [`UsbBus`](bus::UsbBus) trait.
28//!
29//! # Note about terminology
30//!
31//! This crate uses standard host-centric USB terminology for transfer directions. Therefore an OUT
32//! transfer refers to a host-to-device transfer, and an IN transfer refers to a device-to-host
33//! transfer. This is mainly a concern for implementing new USB peripheral drivers and USB classes,
34//! and people doing that should be familiar with the USB standard.
35
36#![no_std]
37#![warn(missing_docs)]
38
39#[macro_use]
40mod macros;
41
42/// A USB stack error.
43#[derive(Copy, Clone, Eq, PartialEq, Debug)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45pub enum UsbError {
46 /// An operation would block because the device is currently busy or there is no data available.
47 WouldBlock,
48
49 /// Parsing failed due to invalid input.
50 ParseError,
51
52 /// A buffer too short for the data to read was passed, or provided data cannot fit within
53 /// length constraints.
54 BufferOverflow,
55
56 /// Classes attempted to allocate more endpoints than the peripheral supports.
57 EndpointOverflow,
58
59 /// Classes attempted to allocate more packet buffer memory than the peripheral supports. This
60 /// can be caused by either a single class trying to allocate a packet buffer larger than the
61 /// peripheral supports per endpoint, or multiple allocated endpoints together using more memory
62 /// than the peripheral has available for the buffers.
63 EndpointMemoryOverflow,
64
65 /// The endpoint address is invalid or already used.
66 InvalidEndpoint,
67
68 /// Operation is not supported by device or configuration.
69 Unsupported,
70
71 /// Operation is not valid in the current state of the object.
72 InvalidState,
73}
74
75/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from
76/// the perspective of the host, which is backward for devices, but the standard directions are used
77/// for consistency.
78///
79/// The values of the enum also match the direction bit used in endpoint addresses and control
80/// request types.
81#[repr(u8)]
82#[derive(Copy, Clone, Eq, PartialEq, Debug)]
83#[cfg_attr(feature = "defmt", derive(defmt::Format))]
84pub enum UsbDirection {
85 /// Host to device (OUT)
86 Out = 0x00,
87 /// Device to host (IN)
88 In = 0x80,
89}
90
91impl From<u8> for UsbDirection {
92 fn from(value: u8) -> Self {
93 unsafe { core::mem::transmute(value & 0x80) }
94 }
95}
96
97/// Result for USB operations.
98pub type Result<T> = core::result::Result<T, UsbError>;
99
100/// USB control transfers and the SETUP packet.
101pub mod control;
102
103/// For implementing peripheral drivers.
104pub mod bus;
105
106/// For implementing standard as well as vendor-specific USB classes.
107///
108/// To implement a new class, implement the [`UsbClass`](class::UsbClass) trait. The trait contains
109/// numerous callbacks that you can use to respond to USB events. None of the methods are required,
110/// and you only need to override the ones that your specific class needs to function. See the trait
111/// documentation for more information on the callback methods.
112///
113/// Your class should *not* hold a direct reference to the [`UsbBus`](bus::UsbBus) object. Rather it
114/// should take a temporary reference to the [`UsbBusAllocator`](bus::UsbBusAllocator) object
115/// exposed by the bus in its constructor, and use that to allocate endpoints, as well as interface
116/// and string handles. Using the [`Endpoint`](endpoint::Endpoint) handles which wrap a reference to
117/// the `UsbBus` instance ensures that classes cannot inadvertently access an endpoint owned by
118/// another class.
119///
120/// In addition to implementing the trait, add struct methods for the end-user to send and receive
121/// data via your class. For example, a serial port class might have class-specific methods `read`
122/// and `write` to read and write data.
123pub mod class;
124
125/// USB endpoints.
126pub mod endpoint;
127
128/// USB composite device.
129///
130/// The [UsbDevice](device::UsbDevice) type in this module is the core of this crate. It combines
131/// multiple USB class implementations and the USB bus driver and dispatches bus state changes and
132/// control messages between them.
133///
134/// To implement USB support for your own project, the required code is usually as follows:
135///
136/// ``` ignore
137/// use usb_device::prelude::*;
138/// use usb_serial; // example class crate (not included)
139///
140/// // Create the device-specific USB peripheral driver. The exact name and arguments are device
141/// // specific, so check the documentation for your device driver crate.
142/// let usb_bus = device_specific_usb::UsbBus::new(...);
143///
144/// // Create one or more USB class implementation. The name and arguments depend on the class,
145/// // however most classes require the UsbAllocator as the first argument in order to allocate
146/// // the required shared resources.
147/// let mut serial = usb_serial::SerialPort::new(&usb_bus.allocator());
148///
149/// // Build the final [UsbDevice](device::UsbDevice) instance. The required arguments are a
150/// // reference to the peripheral driver created earlier, as well as a USB vendor ID/product ID
151/// // pair. Additional builder arguments can specify parameters such as device class code or
152/// // product name. If using an existing class, remember to check the class crate documentation
153/// // for correct values.
154/// let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x5824, 0x27dd))
155/// .strings(&[StringDescriptors::new(LangID::EN)
156/// .product("Serial port")])
157/// .expect("Failed to set strings")
158/// .device_class(usb_serial::DEVICE_CLASS)
159/// .build();
160///
161/// // At this point the USB peripheral is enabled and a connected host will attempt to enumerate
162/// // it.
163/// loop {
164/// // Must be called more often than once every 10ms to handle events and stay USB compilant,
165/// // or from a device-specific interrupt handler.
166/// if (usb_dev.poll(&mut [&mut serial])) {
167/// // Call class-specific methods here
168/// serial.read(...);
169/// }
170/// }
171/// ```
172pub mod device;
173
174/// Creating USB descriptors
175pub mod descriptor;
176
177pub use descriptor::lang_id::LangID;
178
179/// Test USB class for testing USB driver implementations. Peripheral driver implementations should
180/// include an example called "test_class" that creates a device with this class to enable the
181/// driver to be tested with the test_class_host example in this crate.
182pub mod test_class;
183
184mod control_pipe;
185
186mod device_builder;
187
188/// Prelude for device implementors.
189pub mod prelude {
190 pub use crate::device::{
191 StringDescriptors, UsbDevice, UsbDeviceBuilder, UsbDeviceState, UsbVidPid,
192 };
193 pub use crate::device_builder::BuilderError;
194 pub use crate::LangID;
195 pub use crate::UsbError;
196}
197
198/// Prelude for class implementors.
199pub mod class_prelude {
200 pub use crate::bus::{InterfaceNumber, StringIndex, UsbBus, UsbBusAllocator};
201 pub use crate::class::{ControlIn, ControlOut, UsbClass};
202 pub use crate::control;
203 pub use crate::descriptor::{BosWriter, DescriptorWriter};
204 pub use crate::endpoint::{
205 EndpointAddress, EndpointIn, EndpointOut, EndpointType, IsochronousSynchronizationType,
206 IsochronousUsageType,
207 };
208 pub use crate::LangID;
209 pub use crate::UsbError;
210}
211
212fn _ensure_sync() {
213 use crate::bus::PollResult;
214 use crate::class_prelude::*;
215
216 struct DummyBus<'a> {
217 _a: &'a str,
218 }
219
220 impl UsbBus for DummyBus<'_> {
221 fn alloc_ep(
222 &mut self,
223 _ep_dir: UsbDirection,
224 _ep_addr: Option<EndpointAddress>,
225 _ep_type: EndpointType,
226 _max_packet_size: u16,
227 _interval: u8,
228 ) -> Result<EndpointAddress> {
229 Err(UsbError::EndpointOverflow)
230 }
231
232 fn enable(&mut self) {}
233
234 fn reset(&self) {}
235 fn set_device_address(&self, _addr: u8) {}
236
237 fn write(&self, _ep_addr: EndpointAddress, _buf: &[u8]) -> Result<usize> {
238 Err(UsbError::InvalidEndpoint)
239 }
240
241 fn read(&self, _ep_addr: EndpointAddress, _buf: &mut [u8]) -> Result<usize> {
242 Err(UsbError::InvalidEndpoint)
243 }
244
245 fn set_stalled(&self, _ep_addr: EndpointAddress, _stalled: bool) {}
246 fn is_stalled(&self, _ep_addr: EndpointAddress) -> bool {
247 false
248 }
249 fn suspend(&self) {}
250 fn resume(&self) {}
251 fn poll(&self) -> PollResult {
252 PollResult::None
253 }
254 }
255
256 struct DummyClass<'a, B: UsbBus> {
257 _ep: crate::endpoint::EndpointIn<'a, B>,
258 }
259
260 impl<B: UsbBus> DummyClass<'_, B> {
261 fn _new(alloc: &UsbBusAllocator<B>) -> DummyClass<'_, B> {
262 DummyClass {
263 _ep: alloc.bulk(64),
264 }
265 }
266 }
267
268 impl<B: UsbBus> UsbClass<B> for DummyClass<'_, B> {}
269
270 fn ensure_sync<T: Sync + Send>() {}
271
272 ensure_sync::<DummyBus>();
273 ensure_sync::<crate::endpoint::EndpointIn<DummyBus>>();
274 ensure_sync::<crate::endpoint::EndpointOut<DummyBus>>();
275 ensure_sync::<DummyClass<'_, DummyBus>>();
276}