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}