feat: add display UI with splash screen, dashboard, and RSSI sparkline

Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+381
View File
@@ -0,0 +1,381 @@
use crate::endpoint::{
Endpoint, EndpointAddress, EndpointDirection, EndpointType, IsochronousSynchronizationType,
IsochronousUsageType,
};
use crate::{Result, UsbDirection, UsbError};
use core::cell::RefCell;
use core::mem;
use core::ptr;
use portable_atomic::{AtomicPtr, Ordering};
/// A trait for device-specific USB peripherals. Implement this to add support for a new hardware
/// platform.
///
/// The UsbBus is shared by reference between the global [`UsbDevice`](crate::device::UsbDevice) as
/// well as [`UsbClass`](crate::class::UsbClass)es, and therefore any required mutability must be
/// implemented using interior mutability. Most operations that may mutate the bus object itself
/// take place before [`enable`](UsbBus::enable) is called. After the bus is enabled, in practice
/// most access won't mutate the object itself but only endpoint-specific registers and buffers, the
/// access to which is mostly arbitrated by endpoint handles.
pub trait UsbBus: Sync + Sized {
/// Allocates an endpoint and specified endpoint parameters. This method is called by the device
/// and class implementations to allocate endpoints, and can only be called before
/// [`enable`](UsbBus::enable) is called.
///
/// # Arguments
///
/// * `ep_dir` - The endpoint direction.
/// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should
/// attempt to return an endpoint with the specified address. If None, the implementation
/// should return the next available one.
/// * `max_packet_size` - Maximum packet size in bytes.
/// * `interval` - Polling interval parameter for interrupt endpoints.
///
/// # Errors
///
/// * [`EndpointOverflow`](crate::UsbError::EndpointOverflow) - Available total number of
/// endpoints, endpoints of the specified type, or endpoind packet memory has been exhausted.
/// This is generally caused when a user tries to add too many classes to a composite device.
/// * [`InvalidEndpoint`](crate::UsbError::InvalidEndpoint) - A specific `ep_addr` was specified
/// but the endpoint in question has already been allocated.
fn alloc_ep(
&mut self,
ep_dir: UsbDirection,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<EndpointAddress>;
/// Enables and initializes the USB peripheral. Soon after enabling the device will be reset, so
/// there is no need to perform a USB reset in this method.
fn enable(&mut self);
/// Called when the host resets the device. This will be soon called after
/// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Reset`]. This method should
/// reset the state of all endpoints and peripheral flags back to a state suitable for
/// enumeration, as well as ensure that all endpoints previously allocated with alloc_ep are
/// initialized as specified.
fn reset(&self);
/// Sets the device USB address to `addr`.
fn set_device_address(&self, addr: u8);
/// Writes a single packet of data to the specified endpoint and returns number of bytes
/// actually written.
///
/// The only reason for a short write is if the caller passes a slice larger than the amount of
/// memory allocated earlier, and this is generally an error in the class implementation.
///
/// # Errors
///
/// * [`InvalidEndpoint`](crate::UsbError::InvalidEndpoint) - The `ep_addr` does not point to a
/// valid endpoint that was previously allocated with [`UsbBus::alloc_ep`].
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - A previously written packet is still pending
/// to be sent.
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The packet is too long to fit in the
/// transmission buffer. This is generally an error in the class implementation, because the
/// class shouldn't provide more data than the `max_packet_size` it specified when allocating
/// the endpoint.
///
/// Implementations may also return other errors if applicable.
fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> Result<usize>;
/// Reads a single packet of data from the specified endpoint and returns the actual length of
/// the packet.
///
/// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
///
/// # Errors
///
/// * [`InvalidEndpoint`](crate::UsbError::InvalidEndpoint) - The `ep_addr` does not point to a
/// valid endpoint that was previously allocated with [`UsbBus::alloc_ep`].
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - There is no packet to be read. Note that
/// this is different from a received zero-length packet, which is valid in USB. A zero-length
/// packet will return `Ok(0)`.
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The received packet is too long to
/// fit in `buf`. This is generally an error in the class implementation, because the class
/// should use a buffer that is large enough for the `max_packet_size` it specified when
/// allocating the endpoint.
///
/// Implementations may also return other errors if applicable.
fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> Result<usize>;
/// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
/// should be prepared to receive data again.
fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool);
/// Gets whether the STALL condition is set for an endpoint.
fn is_stalled(&self, ep_addr: EndpointAddress) -> bool;
/// Causes the USB peripheral to enter USB suspend mode, lowering power consumption and
/// preparing to detect a USB wakeup event. This will be called after
/// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Suspend`]. The device will
/// continue be polled, and it shall return a value other than `Suspend` from `poll` when it no
/// longer detects the suspend condition.
fn suspend(&self);
/// Resumes from suspend mode. This may only be called after the peripheral has been previously
/// suspended.
fn resume(&self);
/// Gets information about events and incoming data. Usually called in a loop or from an
/// interrupt handler. See the [`PollResult`] struct for more information.
fn poll(&self) -> PollResult;
/// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the
/// device.
///
/// The default implementation just returns `Unsupported`.
///
/// # Errors
///
/// * [`Unsupported`](crate::UsbError::Unsupported) - This UsbBus implementation doesn't support
/// simulating a disconnect or it has not been enabled at creation time.
fn force_reset(&self) -> Result<()> {
Err(UsbError::Unsupported)
}
/// Indicates that `set_device_address` must be called before accepting the corresponding
/// control transfer, not after.
///
/// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6
const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false;
}
struct AllocatorState {
next_interface_number: u8,
next_string_index: u8,
}
/// Helper type used for UsbBus resource allocation and initialization.
pub struct UsbBusAllocator<B: UsbBus> {
bus: RefCell<B>,
bus_ptr: AtomicPtr<B>,
state: RefCell<AllocatorState>,
}
impl<B: UsbBus> UsbBusAllocator<B> {
/// Creates a new [`UsbBusAllocator`] that wraps the provided [`UsbBus`]. Usually only called by
/// USB driver implementations.
pub fn new(bus: B) -> UsbBusAllocator<B> {
UsbBusAllocator {
bus: RefCell::new(bus),
bus_ptr: AtomicPtr::new(ptr::null_mut()),
state: RefCell::new(AllocatorState {
next_interface_number: 0,
next_string_index: 4,
}),
}
}
pub(crate) fn freeze(&self) -> &B {
// Prevent further allocation by borrowing the allocation state permanently.
mem::forget(self.state.borrow_mut());
// Enable the USB bus
self.bus.borrow_mut().enable();
// An AtomicPtr is used for the reference from Endpoints to UsbBus, in order to ensure that
// Endpoints stay Sync (if the Endpoints had a reference to a RefCell, they would not be
// Sync) Set the pointer used by the Endpoints to access the UsbBus to point to the UsbBus
// in the RefCell.
let mut bus_ref = self.bus.borrow_mut();
let bus_ptr_v = &mut *bus_ref as *mut B;
self.bus_ptr.store(bus_ptr_v, Ordering::SeqCst);
// And then leave the RefCell borrowed permanently so that it cannot be borrowed mutably
// anymore.
mem::forget(bus_ref);
// Return the reference to the UsbBus, for use by UsbDevice.
unsafe { &*bus_ptr_v }
}
/// Allocates a new interface number.
pub fn interface(&self) -> InterfaceNumber {
let mut state = self.state.borrow_mut();
let number = state.next_interface_number;
state.next_interface_number += 1;
InterfaceNumber(number)
}
/// Allocates a new string index.
pub fn string(&self) -> StringIndex {
let mut state = self.state.borrow_mut();
let index = state.next_string_index;
state.next_string_index += 1;
StringIndex(index)
}
/// Allocates an endpoint with the specified direction and address.
///
/// This directly delegates to [`UsbBus::alloc_ep`], so see that method for details. In most
/// cases classes should call the endpoint type specific methods instead.
pub fn alloc<D: EndpointDirection>(
&self,
ep_addr: Option<EndpointAddress>,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<Endpoint<'_, B, D>> {
self.bus
.borrow_mut()
.alloc_ep(D::DIRECTION, ep_addr, ep_type, max_packet_size, interval)
.map(|a| Endpoint::new(&self.bus_ptr, a, ep_type, max_packet_size, interval))
}
/// Allocates a control endpoint.
///
/// This crate implements the control state machine only for endpoint 0. If classes want to
/// support control requests in other endpoints, the state machine must be implemented manually.
/// This should rarely be needed by classes.
///
/// # Arguments
///
/// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn control<D: EndpointDirection>(&self, max_packet_size: u16) -> Endpoint<'_, B, D> {
self.alloc(None, EndpointType::Control, max_packet_size, 0)
.expect("alloc_ep failed")
}
/// Allocates an isochronous endpoint.
///
/// # Arguments
///
/// * `synchronization` - Type of synchronization used by the endpoint
/// * `usage` - Whether the endpoint is data, explicit feedback, or data+implicit feedback
/// * `payload_size` - Payload size in bytes.
/// * `interval` - Interval for polling, expressed in frames/microframes.
///
/// See USB 2.0 section 9.6.6.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn isochronous<D: EndpointDirection>(
&self,
synchronization: IsochronousSynchronizationType,
usage: IsochronousUsageType,
payload_size: u16,
interval: u8,
) -> Endpoint<'_, B, D> {
self.alloc(
None,
EndpointType::Isochronous {
synchronization,
usage,
},
payload_size,
interval,
)
.expect("alloc_ep failed")
}
/// Allocates a bulk endpoint.
///
/// # Arguments
///
/// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn bulk<D: EndpointDirection>(&self, max_packet_size: u16) -> Endpoint<'_, B, D> {
self.alloc(None, EndpointType::Bulk, max_packet_size, 0)
.expect("alloc_ep failed")
}
/// Allocates an interrupt endpoint.
///
/// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes.
/// * `interval` - Polling interval.
///
/// # Panics
///
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
/// feasibly recoverable.
#[inline]
pub fn interrupt<D: EndpointDirection>(
&self,
max_packet_size: u16,
interval: u8,
) -> Endpoint<'_, B, D> {
self.alloc(None, EndpointType::Interrupt, max_packet_size, interval)
.expect("alloc_ep failed")
}
}
/// A handle for a USB interface that contains its number.
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InterfaceNumber(pub(crate) u8);
impl From<InterfaceNumber> for u8 {
fn from(n: InterfaceNumber) -> u8 {
n.0
}
}
/// A handle for a USB string descriptor that contains its index.
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StringIndex(u8);
impl StringIndex {
pub(crate) fn new(index: u8) -> StringIndex {
StringIndex(index)
}
}
impl From<StringIndex> for u8 {
fn from(i: StringIndex) -> u8 {
i.0
}
}
/// Event and incoming packet information returned by [`UsbBus::poll`].
pub enum PollResult {
/// No events or packets to report.
None,
/// The USB reset condition has been detected.
Reset,
/// USB packets have been received or sent. Each data field is a bit-field where the least
/// significant bit represents endpoint 0 etc., and a set bit signifies the event has occurred
/// for the corresponding endpoint.
Data {
/// An OUT packet has been received. This event should continue to be reported until the
/// packet is read.
ep_out: u16,
/// An IN packet has finished transmitting. This event should only be reported once for each
/// completed transfer.
ep_in_complete: u16,
/// A SETUP packet has been received. This event should continue to be reported until the
/// packet is read. The corresponding bit in `ep_out` may also be set but is ignored.
ep_setup: u16,
},
/// A USB suspend request has been detected or, in the case of self-powered devices, the device
/// has been disconnected from the USB bus.
Suspend,
/// A USB resume request has been detected after being suspended or, in the case of self-powered
/// devices, the device has been connected to the USB bus.
Resume,
}
+222
View File
@@ -0,0 +1,222 @@
use crate::bus::{InterfaceNumber, StringIndex, UsbBus};
use crate::control;
use crate::control_pipe::ControlPipe;
use crate::descriptor::lang_id::LangID;
use crate::descriptor::{BosWriter, DescriptorWriter};
use crate::endpoint::EndpointAddress;
use crate::{Result, UsbError};
/// A trait for implementing USB classes.
///
/// All methods are optional callbacks that will be called by
/// [UsbBus::poll](crate::bus::UsbBus::poll)
pub trait UsbClass<B: UsbBus> {
/// Called when a GET_DESCRIPTOR request is received for a configuration descriptor. When
/// called, the implementation should write its interface, endpoint and any extra class
/// descriptors into `writer`. The configuration descriptor itself will be written by
/// [UsbDevice](crate::device::UsbDevice) and shouldn't be written by classes.
///
/// # Errors
///
/// Generally errors returned by `DescriptorWriter`. Implementors should propagate any errors
/// using `?`.
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
let _ = writer;
Ok(())
}
/// Called when a GET_DESCRIPTOR request is received for a BOS descriptor.
/// When called, the implementation should write its blobs such as capability
/// descriptors into `writer`. The BOS descriptor itself will be written by
/// [UsbDevice](crate::device::UsbDevice) and shouldn't be written by classes.
fn get_bos_descriptors(&self, writer: &mut BosWriter) -> Result<()> {
let _ = writer;
Ok(())
}
/// Gets a class-specific string descriptor.
///
/// Note: All string descriptor requests are passed to all classes in turn, so implementations
/// should return [`None`] if an unknown index is requested.
///
/// # Arguments
///
/// * `index` - A string index allocated earlier with
/// [`UsbAllocator`](crate::bus::UsbBusAllocator).
/// * `lang_id` - The language ID for the string to retrieve. If the requested lang_id is not
/// valid it will default to EN_US.
fn get_string(&self, index: StringIndex, lang_id: LangID) -> Option<&str> {
let _ = (index, lang_id);
None
}
/// Called after a USB reset after the bus reset sequence is complete.
fn reset(&mut self) {}
/// Called whenever the `UsbDevice` is polled.
fn poll(&mut self) {}
/// Called when a control request is received with direction HostToDevice.
///
/// All requests are passed to classes in turn, which can choose to accept, ignore or report an
/// error. Classes can even choose to override standard requests, but doing that is rarely
/// necessary.
///
/// See [`ControlOut`] for how to respond to the transfer.
///
/// When implementing your own class, you should ignore any requests that are not meant for your
/// class so that any other classes in the composite device can process them.
///
/// # Arguments
///
/// * `req` - The request from the SETUP packet.
/// * `xfer` - A handle to the transfer.
fn control_out(&mut self, xfer: ControlOut<B>) {
let _ = xfer;
}
/// Called when a control request is received with direction DeviceToHost.
///
/// All requests are passed to classes in turn, which can choose to accept, ignore or report an
/// error. Classes can even choose to override standard requests, but doing that is rarely
/// necessary.
///
/// See [`ControlIn`] for how to respond to the transfer.
///
/// When implementing your own class, you should ignore any requests that are not meant for your
/// class so that any other classes in the composite device can process them.
///
/// # Arguments
///
/// * `req` - The request from the SETUP packet.
/// * `data` - Data to send in the DATA stage of the control transfer.
fn control_in(&mut self, xfer: ControlIn<B>) {
let _ = xfer;
}
/// Called when endpoint with address `addr` has received a SETUP packet. Implementing this
/// shouldn't be necessary in most cases, but is provided for completeness' sake.
///
/// Note: This method may be called for an endpoint address you didn't allocate, and in that
/// case you should ignore the event.
fn endpoint_setup(&mut self, addr: EndpointAddress) {
let _ = addr;
}
/// Called when endpoint with address `addr` has received data (OUT packet).
///
/// Note: This method may be called for an endpoint address you didn't allocate, and in that
/// case you should ignore the event.
fn endpoint_out(&mut self, addr: EndpointAddress) {
let _ = addr;
}
/// Called when endpoint with address `addr` has completed transmitting data (IN packet).
///
/// Note: This method may be called for an endpoint address you didn't allocate, and in that
/// case you should ignore the event.
fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
let _ = addr;
}
/// Called when the interfaces alternate setting state is requested.
///
/// Note: This method may be called on interfaces, that are not relevant to this class.
/// You should return `None, if `interface` belongs to an interface you don't know.
fn get_alt_setting(&mut self, interface: InterfaceNumber) -> Option<u8> {
let _ = interface;
None
}
/// Called when the interfaces alternate setting state is altered.
///
/// Note: This method may be called on interfaces, that are not relevant to this class.
/// You should return `false`, if `interface` belongs to an interface you don't know.
fn set_alt_setting(&mut self, interface: InterfaceNumber, alternative: u8) -> bool {
let _ = (interface, alternative);
false
}
}
/// Handle for a control IN transfer. When implementing a class, use the methods of this object to
/// response to the transfer with either data or an error (STALL condition). To ignore the request
/// and pass it on to the next class, simply don't call any method.
pub struct ControlIn<'a, 'p, 'r, B: UsbBus> {
pipe: &'p mut ControlPipe<'a, B>,
req: &'r control::Request,
}
impl<'a, 'p, 'r, B: UsbBus> ControlIn<'a, 'p, 'r, B> {
pub(crate) fn new(pipe: &'p mut ControlPipe<'a, B>, req: &'r control::Request) -> Self {
ControlIn { pipe, req }
}
/// Gets the request from the SETUP packet.
pub fn request(&self) -> &control::Request {
self.req
}
/// Accepts the transfer with the supplied buffer.
pub fn accept_with(self, data: &[u8]) -> Result<()> {
self.pipe.accept_in(|buf| {
if data.len() > buf.len() {
return Err(UsbError::BufferOverflow);
}
buf[..data.len()].copy_from_slice(data);
Ok(data.len())
})
}
/// Accepts the transfer with the supplied static buffer.
/// This method is useful when you have a large static descriptor to send as one packet.
pub fn accept_with_static(self, data: &'static [u8]) -> Result<()> {
self.pipe.accept_in_static(data)
}
/// Accepts the transfer with a callback that can write to the internal buffer of the control
/// pipe. Can be used to avoid an extra copy.
pub fn accept(self, f: impl FnOnce(&mut [u8]) -> Result<usize>) -> Result<()> {
self.pipe.accept_in(f)
}
/// Rejects the transfer by stalling the pipe.
pub fn reject(self) -> Result<()> {
self.pipe.reject()
}
}
/// Handle for a control OUT transfer. When implementing a class, use the methods of this object to
/// response to the transfer with an ACT or an error (STALL condition). To ignore the request and
/// pass it on to the next class, simply don't call any method.
pub struct ControlOut<'a, 'p, 'r, B: UsbBus> {
pipe: &'p mut ControlPipe<'a, B>,
req: &'r control::Request,
}
impl<'a, 'p, 'r, B: UsbBus> ControlOut<'a, 'p, 'r, B> {
pub(crate) fn new(pipe: &'p mut ControlPipe<'a, B>, req: &'r control::Request) -> Self {
ControlOut { pipe, req }
}
/// Gets the request from the SETUP packet.
pub fn request(&self) -> &control::Request {
self.req
}
/// Gets the data from the data stage of the request. May be empty if there was no data stage.
pub fn data(&self) -> &[u8] {
self.pipe.data()
}
/// Accepts the transfer by succesfully responding to the status stage.
pub fn accept(self) -> Result<()> {
self.pipe.accept_out()
}
/// Rejects the transfer by stalling the pipe.
pub fn reject(self) -> Result<()> {
self.pipe.reject()
}
}
+127
View File
@@ -0,0 +1,127 @@
use crate::{Result, UsbDirection, UsbError};
use core::mem;
/// Control request type.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RequestType {
/// Request is a USB standard request. Usually handled by
/// [`UsbDevice`](crate::device::UsbDevice).
Standard = 0,
/// Request is intended for a USB class.
Class = 1,
/// Request is vendor-specific.
Vendor = 2,
/// Reserved.
Reserved = 3,
}
/// Control request recipient.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Recipient {
/// Request is intended for the entire device.
Device = 0,
/// Request is intended for an interface. Generally, the `index` field of the request specifies
/// the interface number.
Interface = 1,
/// Request is intended for an endpoint. Generally, the `index` field of the request specifies
/// the endpoint address.
Endpoint = 2,
/// None of the above.
Other = 3,
/// Reserved.
Reserved = 4,
}
/// A control request read from a SETUP packet.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Request {
/// Direction of the request.
pub direction: UsbDirection,
/// Type of the request.
pub request_type: RequestType,
/// Recipient of the request.
pub recipient: Recipient,
/// Request code. The meaning of the value depends on the previous fields.
pub request: u8,
/// Request value. The meaning of the value depends on the previous fields.
pub value: u16,
/// Request index. The meaning of the value depends on the previous fields.
pub index: u16,
/// Length of the DATA stage. For control OUT transfers this is the exact length of the data the
/// host sent. For control IN transfers this is the maximum length of data the device should
/// return.
pub length: u16,
}
impl Request {
/// Standard USB control request Get Status
pub const GET_STATUS: u8 = 0;
/// Standard USB control request Clear Feature
pub const CLEAR_FEATURE: u8 = 1;
/// Standard USB control request Set Feature
pub const SET_FEATURE: u8 = 3;
/// Standard USB control request Set Address
pub const SET_ADDRESS: u8 = 5;
/// Standard USB control request Get Descriptor
pub const GET_DESCRIPTOR: u8 = 6;
/// Standard USB control request Set Descriptor
pub const SET_DESCRIPTOR: u8 = 7;
/// Standard USB control request Get Configuration
pub const GET_CONFIGURATION: u8 = 8;
/// Standard USB control request Set Configuration
pub const SET_CONFIGURATION: u8 = 9;
/// Standard USB control request Get Interface
pub const GET_INTERFACE: u8 = 10;
/// Standard USB control request Set Interface
pub const SET_INTERFACE: u8 = 11;
/// Standard USB control request Synch Frame
pub const SYNCH_FRAME: u8 = 12;
/// Standard USB feature Endpoint Halt for Set/Clear Feature
pub const FEATURE_ENDPOINT_HALT: u16 = 0;
/// Standard USB feature Device Remote Wakeup for Set/Clear Feature
pub const FEATURE_DEVICE_REMOTE_WAKEUP: u16 = 1;
pub(crate) fn parse(buf: &[u8]) -> Result<Request> {
if buf.len() != 8 {
return Err(UsbError::ParseError);
}
let rt = buf[0];
let recipient = rt & 0b11111;
Ok(Request {
direction: rt.into(),
request_type: unsafe { mem::transmute((rt >> 5) & 0b11) },
recipient: if recipient <= 3 {
unsafe { mem::transmute(recipient) }
} else {
Recipient::Reserved
},
request: buf[1],
value: (buf[2] as u16) | ((buf[3] as u16) << 8),
index: (buf[4] as u16) | ((buf[5] as u16) << 8),
length: (buf[6] as u16) | ((buf[7] as u16) << 8),
})
}
/// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR request.
pub fn descriptor_type_index(&self) -> (u8, u8) {
((self.value >> 8) as u8, self.value as u8)
}
}
+317
View File
@@ -0,0 +1,317 @@
use crate::bus::UsbBus;
use crate::control::Request;
use crate::endpoint::{EndpointIn, EndpointOut};
use crate::{Result, UsbDirection, UsbError};
use core::cmp::min;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(unused)]
enum ControlState {
Idle,
DataIn,
DataInZlp,
DataInLast,
CompleteIn(Request),
StatusOut,
CompleteOut,
DataOut(Request),
StatusIn,
Error,
}
// Maximum length of control transfer data stage in bytes. 128 bytes by default. You can define the
// feature "control-buffer-256" to make it 256 bytes if you have larger control transfers.
#[cfg(not(feature = "control-buffer-256"))]
const CONTROL_BUF_LEN: usize = 128;
#[cfg(feature = "control-buffer-256")]
const CONTROL_BUF_LEN: usize = 256;
/// Buffers and parses USB control transfers.
pub struct ControlPipe<'a, B: UsbBus> {
ep_out: EndpointOut<'a, B>,
ep_in: EndpointIn<'a, B>,
state: ControlState,
buf: [u8; CONTROL_BUF_LEN],
static_in_buf: Option<&'static [u8]>,
i: usize,
len: usize,
}
impl<B: UsbBus> ControlPipe<'_, B> {
pub fn new<'a>(ep_out: EndpointOut<'a, B>, ep_in: EndpointIn<'a, B>) -> ControlPipe<'a, B> {
ControlPipe {
ep_out,
ep_in,
state: ControlState::Idle,
buf: [0; CONTROL_BUF_LEN],
static_in_buf: None,
i: 0,
len: 0,
}
}
pub fn waiting_for_response(&self) -> bool {
matches!(
self.state,
ControlState::CompleteOut | ControlState::CompleteIn(_)
)
}
pub fn data(&self) -> &[u8] {
&self.buf[0..self.len]
}
pub fn reset(&mut self) {
usb_trace!("Control pipe reset");
self.state = ControlState::Idle;
}
pub fn handle_setup(&mut self) -> Option<Request> {
let count = match self.ep_out.read(&mut self.buf[..]) {
Ok(count) => {
usb_trace!("Read {} bytes on EP0-OUT: {:?}", count, &self.buf[..count]);
count
}
Err(UsbError::WouldBlock) => return None,
Err(_) => {
return None;
}
};
let req = match Request::parse(&self.buf[0..count]) {
Ok(req) => req,
Err(_) => {
// Failed to parse SETUP packet. We are supposed to silently ignore this.
return None;
}
};
// Now that we have properly parsed the setup packet, ensure the end-point is no longer in
// a stalled state.
self.ep_out.unstall();
usb_debug!("EP0 request received: {:?}", req);
/*sprintln!("SETUP {:?} {:?} {:?} req:{} val:{} idx:{} len:{} {:?}",
req.direction, req.request_type, req.recipient,
req.request, req.value, req.index, req.length,
self.state);*/
if req.direction == UsbDirection::Out {
// OUT transfer
if req.length > 0 {
// Has data stage
if req.length as usize > self.buf.len() {
// Data stage won't fit in buffer
return None;
}
self.i = 0;
self.len = req.length as usize;
self.state = ControlState::DataOut(req);
} else {
// No data stage
self.len = 0;
self.state = ControlState::CompleteOut;
return Some(req);
}
} else {
// IN transfer
self.state = ControlState::CompleteIn(req);
return Some(req);
}
None
}
pub fn handle_out(&mut self) -> Result<Option<Request>> {
match self.state {
ControlState::DataOut(req) => {
let i = self.i;
let count = match self.ep_out.read(&mut self.buf[i..]) {
Ok(count) => count,
Err(UsbError::WouldBlock) => return Ok(None),
Err(_err) => {
// Failed to read or buffer overflow (overflow is only possible if the host
// sends more data than it indicated in the SETUP request)
usb_debug!("Failed EP0 read: {:?}", _err);
self.set_error();
return Ok(None);
}
};
usb_trace!(
"Read {} bytes on EP0-OUT: {:?}",
count,
&self.buf[i..(i + count)]
);
self.i += count;
if self.i >= self.len {
usb_debug!("Request OUT complete: {:?}", req);
self.state = ControlState::CompleteOut;
return Ok(Some(req));
}
}
// The host may terminate a DATA stage early by sending a zero-length status packet
// acknowledging the data we sent it.
ControlState::DataIn
| ControlState::DataInLast
| ControlState::DataInZlp
| ControlState::StatusOut => {
usb_debug!(
"Control transfer completed. Current state: {:?}",
self.state
);
self.ep_out.read(&mut [])?;
self.state = ControlState::Idle;
}
_ => {
// Discard the packet
usb_debug!(
"Discarding EP0 data due to unexpected state. Current state: {:?}",
self.state
);
self.ep_out.read(&mut [])?;
// Unexpected OUT packet
self.set_error()
}
}
Ok(None)
}
pub fn handle_in_complete(&mut self) -> Result<bool> {
match self.state {
ControlState::DataIn => {
self.write_in_chunk()?;
}
ControlState::DataInZlp => {
self.ep_in.write(&[])?;
usb_trace!("wrote EP0: ZLP");
self.state = ControlState::DataInLast;
}
ControlState::DataInLast => {
self.ep_out.unstall();
self.state = ControlState::StatusOut;
}
ControlState::StatusIn => {
self.state = ControlState::Idle;
return Ok(true);
}
ControlState::Idle => {
// If we received a message on EP0 while sending the last portion of an IN
// transfer, we may have already transitioned to IDLE without getting the last
// IN-complete status. Just ignore this indication.
}
_ => {
// If we get IN-COMPLETE indications in unexpected states, it's generally because
// of control flow in previous phases updating after our packet was successfully
// sent. Ignore these indications if they don't drive any further behavior.
}
};
Ok(false)
}
fn write_in_chunk(&mut self) -> Result<()> {
let count = min(self.len - self.i, self.ep_in.max_packet_size() as usize);
let buffer = self.static_in_buf.unwrap_or(&self.buf);
let count = self.ep_in.write(&buffer[self.i..(self.i + count)])?;
usb_trace!("wrote EP0: {:?}", &buffer[self.i..(self.i + count)]);
self.i += count;
if self.i >= self.len {
self.static_in_buf = None;
self.state = if count == self.ep_in.max_packet_size() as usize {
ControlState::DataInZlp
} else {
ControlState::DataInLast
};
}
Ok(())
}
pub fn accept_out(&mut self) -> Result<()> {
match self.state {
ControlState::CompleteOut => {}
_ => {
usb_debug!("Cannot ACK, invalid state: {:?}", self.state);
return Err(UsbError::InvalidState);
}
};
self.ep_in.write(&[])?;
self.state = ControlState::StatusIn;
Ok(())
}
pub fn accept_in(&mut self, f: impl FnOnce(&mut [u8]) -> Result<usize>) -> Result<()> {
let req = match self.state {
ControlState::CompleteIn(req) => req,
_ => {
usb_debug!("EP0-IN cannot ACK, invalid state: {:?}", self.state);
return Err(UsbError::InvalidState);
}
};
let len = f(&mut self.buf[..])?;
if len > self.buf.len() {
self.set_error();
return Err(UsbError::BufferOverflow);
}
self.start_in_transfer(req, len)
}
pub fn accept_in_static(&mut self, data: &'static [u8]) -> Result<()> {
let req = match self.state {
ControlState::CompleteIn(req) => req,
_ => {
usb_debug!("EP0-IN cannot ACK, invalid state: {:?}", self.state);
return Err(UsbError::InvalidState);
}
};
self.static_in_buf = Some(data);
self.start_in_transfer(req, data.len())
}
fn start_in_transfer(&mut self, req: Request, data_len: usize) -> Result<()> {
self.len = min(data_len, req.length as usize);
self.i = 0;
self.state = ControlState::DataIn;
self.write_in_chunk()?;
Ok(())
}
pub fn reject(&mut self) -> Result<()> {
usb_debug!("EP0 transfer rejected");
if !self.waiting_for_response() {
return Err(UsbError::InvalidState);
}
self.set_error();
Ok(())
}
fn set_error(&mut self) {
usb_debug!("EP0 stalled - error");
self.state = ControlState::Error;
self.ep_out.stall();
self.ep_in.stall();
}
}
+447
View File
@@ -0,0 +1,447 @@
use core::cmp::min;
use crate::bus::{InterfaceNumber, StringIndex, UsbBus};
use crate::device;
use crate::endpoint::{Endpoint, EndpointDirection};
use crate::{Result, UsbError};
/// Standard descriptor types
#[allow(missing_docs)]
pub mod descriptor_type {
pub const DEVICE: u8 = 1;
pub const CONFIGURATION: u8 = 2;
pub const STRING: u8 = 3;
pub const INTERFACE: u8 = 4;
pub const ENDPOINT: u8 = 5;
pub const IAD: u8 = 11;
pub const BOS: u8 = 15;
pub const CAPABILITY: u8 = 16;
}
/// String descriptor language IDs.
pub mod lang_id;
/// Standard capability descriptor types
#[allow(missing_docs)]
pub mod capability_type {
pub const WIRELESS_USB: u8 = 1;
pub const USB_2_0_EXTENSION: u8 = 2;
pub const SS_USB_DEVICE: u8 = 3;
pub const CONTAINER_ID: u8 = 4;
pub const PLATFORM: u8 = 5;
}
/// A writer for USB descriptors.
pub struct DescriptorWriter<'a> {
buf: &'a mut [u8],
position: usize,
num_interfaces_mark: Option<usize>,
num_endpoints_mark: Option<usize>,
write_iads: bool,
}
impl DescriptorWriter<'_> {
pub(crate) fn new(buf: &mut [u8]) -> DescriptorWriter<'_> {
DescriptorWriter {
buf,
position: 0,
num_interfaces_mark: None,
num_endpoints_mark: None,
write_iads: false,
}
}
/// Gets the current position in the buffer, i.e. the number of bytes written so far.
pub fn position(&self) -> usize {
self.position
}
/// Writes an arbitrary (usually class-specific) descriptor.
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) -> Result<()> {
self.write_with(descriptor_type, |buf| {
if descriptor.len() > buf.len() {
return Err(UsbError::BufferOverflow);
}
buf[..descriptor.len()].copy_from_slice(descriptor);
Ok(descriptor.len())
})
}
/// Writes an arbitrary (usually class-specific) descriptor by using a callback function.
///
/// The callback function gets a reference to the remaining buffer space, and it should write
/// the descriptor into it and return the number of bytes written. If the descriptor doesn't
/// fit, the function should return `Err(UsbError::BufferOverflow)`. That and any error returned
/// by it will be propagated up.
pub fn write_with(
&mut self,
descriptor_type: u8,
f: impl FnOnce(&mut [u8]) -> Result<usize>,
) -> Result<()> {
if self.position + 2 > self.buf.len() {
return Err(UsbError::BufferOverflow);
}
let data_end = min(self.buf.len(), self.position + 256);
let data_buf = &mut self.buf[self.position + 2..data_end];
let total_len = f(data_buf)? + 2;
if self.position + total_len > self.buf.len() {
return Err(UsbError::BufferOverflow);
}
self.buf[self.position] = total_len as u8;
self.buf[self.position + 1] = descriptor_type;
self.position += total_len;
Ok(())
}
pub(crate) fn device(&mut self, config: &device::Config) -> Result<()> {
self.write(
descriptor_type::DEVICE,
&[
(config.usb_rev as u16) as u8,
(config.usb_rev as u16 >> 8) as u8, // bcdUSB
config.device_class, // bDeviceClass
config.device_sub_class, // bDeviceSubClass
config.device_protocol, // bDeviceProtocol
config.max_packet_size_0, // bMaxPacketSize0
config.vendor_id as u8,
(config.vendor_id >> 8) as u8, // idVendor
config.product_id as u8,
(config.product_id >> 8) as u8, // idProduct
config.device_release as u8,
(config.device_release >> 8) as u8, // bcdDevice
config.string_descriptors.first().map_or(0, |lang| {
if lang.manufacturer.is_some() {
1
} else {
0
}
}),
config.string_descriptors.first().map_or(0, |lang| {
if lang.product.is_some() {
2
} else {
0
}
}),
config.string_descriptors.first().map_or(0, |lang| {
if lang.serial.is_some() {
3
} else {
0
}
}),
1, // bNumConfigurations
],
)
}
pub(crate) fn configuration(&mut self, config: &device::Config) -> Result<()> {
self.num_interfaces_mark = Some(self.position + 4);
self.write_iads = config.composite_with_iads;
self.write(
descriptor_type::CONFIGURATION,
&[
0,
0, // wTotalLength
0, // bNumInterfaces
device::CONFIGURATION_VALUE, // bConfigurationValue
0, // iConfiguration
0x80 | if config.self_powered { 0x40 } else { 0x00 }
| if config.supports_remote_wakeup {
0x20
} else {
0x00
}, // bmAttributes
config.max_power, // bMaxPower
],
)
}
pub(crate) fn end_class(&mut self) {
self.num_endpoints_mark = None;
}
pub(crate) fn end_configuration(&mut self) {
let position = self.position as u16;
self.buf[2..4].copy_from_slice(&position.to_le_bytes());
}
/// Writes a interface association descriptor. Call from `UsbClass::get_configuration_descriptors`
/// before writing the USB class or function's interface descriptors if your class has more than
/// one interface and wants to play nicely with composite devices on Windows. If the USB device
/// hosting the class was not configured as composite with IADs enabled, calling this function
/// does nothing, so it is safe to call from libraries.
///
/// # Arguments
///
/// * `first_interface` - Number of the function's first interface, previously allocated with
/// [`UsbBusAllocator::interface`](crate::bus::UsbBusAllocator::interface).
/// * `interface_count` - Number of interfaces in the function.
/// * `function_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
/// that do not conform to any class.
/// * `function_sub_class` - Sub-class code. Depends on class.
/// * `function_protocol` - Protocol code. Depends on class and sub-class.
/// * `function_string` - Index of string descriptor describing this function
pub fn iad(
&mut self,
first_interface: InterfaceNumber,
interface_count: u8,
function_class: u8,
function_sub_class: u8,
function_protocol: u8,
function_string: Option<StringIndex>,
) -> Result<()> {
if !self.write_iads {
return Ok(());
}
let str_index = function_string.map_or(0, Into::into);
self.write(
descriptor_type::IAD,
&[
first_interface.into(), // bFirstInterface
interface_count, // bInterfaceCount
function_class,
function_sub_class,
function_protocol,
str_index,
],
)?;
Ok(())
}
/// Writes a interface descriptor.
///
/// # Arguments
///
/// * `number` - Interface number previously allocated with
/// [`UsbBusAllocator::interface`](crate::bus::UsbBusAllocator::interface).
/// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
/// that do not conform to any class.
/// * `interface_sub_class` - Sub-class code. Depends on class.
/// * `interface_protocol` - Protocol code. Depends on class and sub-class.
pub fn interface(
&mut self,
number: InterfaceNumber,
interface_class: u8,
interface_sub_class: u8,
interface_protocol: u8,
) -> Result<()> {
self.interface_alt(
number,
device::DEFAULT_ALTERNATE_SETTING,
interface_class,
interface_sub_class,
interface_protocol,
None,
)
}
/// Writes a interface descriptor with a specific alternate setting and
/// interface string identifier.
///
/// # Arguments
///
/// * `number` - Interface number previously allocated with
/// [`UsbBusAllocator::interface`](crate::bus::UsbBusAllocator::interface).
/// * `alternate_setting` - Number of the alternate setting
/// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
/// that do not conform to any class.
/// * `interface_sub_class` - Sub-class code. Depends on class.
/// * `interface_protocol` - Protocol code. Depends on class and sub-class.
/// * `interface_string` - Index of string descriptor describing this interface
pub fn interface_alt(
&mut self,
number: InterfaceNumber,
alternate_setting: u8,
interface_class: u8,
interface_sub_class: u8,
interface_protocol: u8,
interface_string: Option<StringIndex>,
) -> Result<()> {
if alternate_setting == device::DEFAULT_ALTERNATE_SETTING {
match self.num_interfaces_mark {
Some(mark) => self.buf[mark] += 1,
None => return Err(UsbError::InvalidState),
};
}
let str_index = interface_string.map_or(0, Into::into);
self.num_endpoints_mark = Some(self.position + 4);
self.write(
descriptor_type::INTERFACE,
&[
number.into(), // bInterfaceNumber
alternate_setting, // bAlternateSetting
0, // bNumEndpoints
interface_class, // bInterfaceClass
interface_sub_class, // bInterfaceSubClass
interface_protocol, // bInterfaceProtocol
str_index, // iInterface
],
)?;
Ok(())
}
/// Writes an endpoint descriptor.
///
/// # Arguments
///
/// * `endpoint` - Endpoint previously allocated with
/// [`UsbBusAllocator`](crate::bus::UsbBusAllocator).
pub fn endpoint<B: UsbBus, D: EndpointDirection>(
&mut self,
endpoint: &Endpoint<'_, B, D>,
) -> Result<()> {
self.endpoint_ex(endpoint, |_| Ok(0))
}
/// Writes an endpoint descriptor with extra trailing data.
///
/// This is rarely needed and shouldn't be used except for compatibility with standard USB
/// classes that require it. Extra data is normally written in a separate class specific
/// descriptor.
///
/// # Arguments
///
/// * `endpoint` - Endpoint previously allocated with
/// [`UsbBusAllocator`](crate::bus::UsbBusAllocator).
/// * `f` - Callback for the extra data. See `write_with` for more information.
pub fn endpoint_ex<B: UsbBus, D: EndpointDirection>(
&mut self,
endpoint: &Endpoint<'_, B, D>,
f: impl FnOnce(&mut [u8]) -> Result<usize>,
) -> Result<()> {
match self.num_endpoints_mark {
Some(mark) => self.buf[mark] += 1,
None => return Err(UsbError::InvalidState),
};
self.write_with(descriptor_type::ENDPOINT, |buf| {
if buf.len() < 5 {
return Err(UsbError::BufferOverflow);
}
let mps = endpoint.max_packet_size();
buf[0] = endpoint.address().into();
buf[1] = endpoint.ep_type().to_bm_attributes();
buf[2] = mps as u8;
buf[3] = (mps >> 8) as u8;
buf[4] = endpoint.interval();
Ok(f(&mut buf[5..])? + 5)
})
}
/// Writes a string descriptor.
pub(crate) fn string(&mut self, string: &str) -> Result<()> {
let mut pos = self.position;
if pos + 2 > self.buf.len() {
return Err(UsbError::BufferOverflow);
}
self.buf[pos] = 0; // length placeholder
self.buf[pos + 1] = descriptor_type::STRING;
pos += 2;
for c in string.encode_utf16() {
if pos >= self.buf.len() {
return Err(UsbError::BufferOverflow);
}
self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
pos += 2;
}
self.buf[self.position] = (pos - self.position) as u8;
self.position = pos;
Ok(())
}
}
/// A writer for Binary Object Store descriptor.
pub struct BosWriter<'w, 'a: 'w> {
writer: &'w mut DescriptorWriter<'a>,
num_caps_mark: Option<usize>,
}
impl<'w, 'a: 'w> BosWriter<'w, 'a> {
pub(crate) fn new(writer: &'w mut DescriptorWriter<'a>) -> Self {
Self {
writer,
num_caps_mark: None,
}
}
pub(crate) fn bos(&mut self) -> Result<()> {
self.num_caps_mark = Some(self.writer.position + 4);
self.writer.write(
descriptor_type::BOS,
&[
0x00, 0x00, // wTotalLength
0x00, // bNumDeviceCaps
],
)?;
self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4])?;
Ok(())
}
/// Writes capability descriptor to a BOS
///
/// # Arguments
///
/// * `capability_type` - Type of a capability
/// * `data` - Binary data of the descriptor
pub fn capability(&mut self, capability_type: u8, data: &[u8]) -> Result<()> {
match self.num_caps_mark {
Some(mark) => self.writer.buf[mark] += 1,
None => return Err(UsbError::InvalidState),
}
let mut start = self.writer.position;
let blen = data.len();
if (start + blen + 3) > self.writer.buf.len() || (blen + 3) > 255 {
return Err(UsbError::BufferOverflow);
}
self.writer.buf[start] = (blen + 3) as u8;
self.writer.buf[start + 1] = descriptor_type::CAPABILITY;
self.writer.buf[start + 2] = capability_type;
start += 3;
self.writer.buf[start..start + blen].copy_from_slice(data);
self.writer.position = start + blen;
Ok(())
}
pub(crate) fn end_bos(&mut self) {
self.num_caps_mark = None;
let position = self.writer.position as u16;
self.writer.buf[2..4].copy_from_slice(&position.to_le_bytes());
}
}
+479
View File
@@ -0,0 +1,479 @@
#![allow(non_upper_case_globals, non_camel_case_types)]
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LangID(u16);
impl From<LangID> for u16 {
fn from(lang_id: LangID) -> Self {
let LangID(id) = lang_id;
id
}
}
impl From<u16> for LangID {
fn from(id: u16) -> Self {
LangID(id)
}
}
#[allow(missing_docs)]
impl LangID {
pub const AR: LangID = LangID(0x0001);
pub const BG: LangID = LangID(0x0002);
pub const CA: LangID = LangID(0x0003);
pub const ZH_HANS: LangID = LangID(0x0004);
pub const CS: LangID = LangID(0x0005);
pub const DA: LangID = LangID(0x0006);
pub const DE: LangID = LangID(0x0007);
pub const EL: LangID = LangID(0x0008);
pub const EN: LangID = LangID(0x0009);
pub const ES: LangID = LangID(0x000A);
pub const FI: LangID = LangID(0x000B);
pub const FR: LangID = LangID(0x000C);
pub const HE: LangID = LangID(0x000D);
pub const HU: LangID = LangID(0x000E);
pub const IS: LangID = LangID(0x000F);
pub const IT: LangID = LangID(0x0010);
pub const JA: LangID = LangID(0x0011);
pub const KO: LangID = LangID(0x0012);
pub const NL: LangID = LangID(0x0013);
pub const NO: LangID = LangID(0x0014);
pub const PL: LangID = LangID(0x0015);
pub const PT: LangID = LangID(0x0016);
pub const RM: LangID = LangID(0x0017);
pub const RO: LangID = LangID(0x0018);
pub const RU: LangID = LangID(0x0019);
pub const HR: LangID = LangID(0x001A);
pub const SK: LangID = LangID(0x001B);
pub const SQ: LangID = LangID(0x001C);
pub const SV: LangID = LangID(0x001D);
pub const TH: LangID = LangID(0x001E);
pub const TR: LangID = LangID(0x001F);
pub const UR: LangID = LangID(0x0020);
pub const ID: LangID = LangID(0x0021);
pub const UK: LangID = LangID(0x0022);
pub const BE: LangID = LangID(0x0023);
pub const SL: LangID = LangID(0x0024);
pub const ET: LangID = LangID(0x0025);
pub const LV: LangID = LangID(0x0026);
pub const LT: LangID = LangID(0x0027);
pub const TG: LangID = LangID(0x0028);
pub const FA: LangID = LangID(0x0029);
pub const VI: LangID = LangID(0x002A);
pub const HY: LangID = LangID(0x002B);
pub const AZ: LangID = LangID(0x002C);
pub const EU: LangID = LangID(0x002D);
pub const HSB: LangID = LangID(0x002E);
pub const MK: LangID = LangID(0x002F);
pub const ST: LangID = LangID(0x0030);
pub const TS: LangID = LangID(0x0031);
pub const TN: LangID = LangID(0x0032);
pub const VE: LangID = LangID(0x0033);
pub const XH: LangID = LangID(0x0034);
pub const ZU: LangID = LangID(0x0035);
pub const AF: LangID = LangID(0x0036);
pub const KA: LangID = LangID(0x0037);
pub const FO: LangID = LangID(0x0038);
pub const HI: LangID = LangID(0x0039);
pub const MT: LangID = LangID(0x003A);
pub const SE: LangID = LangID(0x003B);
pub const GA: LangID = LangID(0x003C);
pub const YI: LangID = LangID(0x003D);
pub const MS: LangID = LangID(0x003E);
pub const KK: LangID = LangID(0x003F);
pub const KY: LangID = LangID(0x0040);
pub const SW: LangID = LangID(0x0041);
pub const TK: LangID = LangID(0x0042);
pub const UZ: LangID = LangID(0x0043);
pub const TT: LangID = LangID(0x0044);
pub const BN: LangID = LangID(0x0045);
pub const PA: LangID = LangID(0x0046);
pub const GU: LangID = LangID(0x0047);
pub const OR: LangID = LangID(0x0048);
pub const TA: LangID = LangID(0x0049);
pub const TE: LangID = LangID(0x004A);
pub const KN: LangID = LangID(0x004B);
pub const ML: LangID = LangID(0x004C);
pub const AS: LangID = LangID(0x004D);
pub const MR: LangID = LangID(0x004E);
pub const SA: LangID = LangID(0x004F);
pub const MN: LangID = LangID(0x0050);
pub const BO: LangID = LangID(0x0051);
pub const CY: LangID = LangID(0x0052);
pub const KM: LangID = LangID(0x0053);
pub const LO: LangID = LangID(0x0054);
pub const MY: LangID = LangID(0x0055);
pub const GL: LangID = LangID(0x0056);
pub const KOK: LangID = LangID(0x0057);
pub const MNI: LangID = LangID(0x0058);
pub const SD: LangID = LangID(0x0059);
pub const SYR: LangID = LangID(0x005A);
pub const SI: LangID = LangID(0x005B);
pub const CHR: LangID = LangID(0x005C);
pub const IU: LangID = LangID(0x005D);
pub const AM: LangID = LangID(0x005E);
pub const TZM: LangID = LangID(0x005F);
pub const KS: LangID = LangID(0x0060);
pub const NE: LangID = LangID(0x0061);
pub const FY: LangID = LangID(0x0062);
pub const PS: LangID = LangID(0x0063);
pub const FIL: LangID = LangID(0x0064);
pub const DV: LangID = LangID(0x0065);
pub const BIN: LangID = LangID(0x0066);
pub const FF: LangID = LangID(0x0067);
pub const HA: LangID = LangID(0x0068);
pub const IBB: LangID = LangID(0x0069);
pub const YO: LangID = LangID(0x006A);
pub const QUZ: LangID = LangID(0x006B);
pub const NSO: LangID = LangID(0x006C);
pub const BA: LangID = LangID(0x006D);
pub const LB: LangID = LangID(0x006E);
pub const KL: LangID = LangID(0x006F);
pub const IG: LangID = LangID(0x0070);
pub const KR: LangID = LangID(0x0071);
pub const OM: LangID = LangID(0x0072);
pub const TI: LangID = LangID(0x0073);
pub const GN: LangID = LangID(0x0074);
pub const HAW: LangID = LangID(0x0075);
pub const LA: LangID = LangID(0x0076);
pub const SO: LangID = LangID(0x0077);
pub const II: LangID = LangID(0x0078);
pub const PAP: LangID = LangID(0x0079);
pub const ARN: LangID = LangID(0x007A);
pub const MOH: LangID = LangID(0x007C);
pub const BR: LangID = LangID(0x007E);
pub const UG: LangID = LangID(0x0080);
pub const MI: LangID = LangID(0x0081);
pub const OC: LangID = LangID(0x0082);
pub const CO: LangID = LangID(0x0083);
pub const GSW: LangID = LangID(0x0084);
pub const SAH: LangID = LangID(0x0085);
pub const QUT: LangID = LangID(0x0086);
pub const RW: LangID = LangID(0x0087);
pub const WO: LangID = LangID(0x0088);
pub const PRS: LangID = LangID(0x008C);
pub const GD: LangID = LangID(0x0091);
pub const KU: LangID = LangID(0x0092);
pub const QUC: LangID = LangID(0x0093);
pub const AR_SA: LangID = LangID(0x0401);
pub const BG_BG: LangID = LangID(0x0402);
pub const CA_ES: LangID = LangID(0x0403);
pub const ZH_TW: LangID = LangID(0x0404);
pub const CS_CZ: LangID = LangID(0x0405);
pub const DA_DK: LangID = LangID(0x0406);
pub const DE_DE: LangID = LangID(0x0407);
pub const EL_GR: LangID = LangID(0x0408);
pub const EN_US: LangID = LangID(0x0409);
pub const ES_ES_TRADNL: LangID = LangID(0x040A);
pub const FI_FI: LangID = LangID(0x040B);
pub const FR_FR: LangID = LangID(0x040C);
pub const HE_IL: LangID = LangID(0x040D);
pub const HU_HU: LangID = LangID(0x040E);
pub const IS_IS: LangID = LangID(0x040F);
pub const IT_IT: LangID = LangID(0x0410);
pub const JA_JP: LangID = LangID(0x0411);
pub const KO_KR: LangID = LangID(0x0412);
pub const NL_NL: LangID = LangID(0x0413);
pub const NB_NO: LangID = LangID(0x0414);
pub const PL_PL: LangID = LangID(0x0415);
pub const PT_BR: LangID = LangID(0x0416);
pub const RM_CH: LangID = LangID(0x0417);
pub const RO_RO: LangID = LangID(0x0418);
pub const RU_RU: LangID = LangID(0x0419);
pub const HR_HR: LangID = LangID(0x041A);
pub const SK_SK: LangID = LangID(0x041B);
pub const SQ_AL: LangID = LangID(0x041C);
pub const SV_SE: LangID = LangID(0x041D);
pub const TH_TH: LangID = LangID(0x041E);
pub const TR_TR: LangID = LangID(0x041F);
pub const UR_PK: LangID = LangID(0x0420);
pub const ID_ID: LangID = LangID(0x0421);
pub const UK_UA: LangID = LangID(0x0422);
pub const BE_BY: LangID = LangID(0x0423);
pub const SL_SI: LangID = LangID(0x0424);
pub const ET_EE: LangID = LangID(0x0425);
pub const LV_LV: LangID = LangID(0x0426);
pub const LT_LT: LangID = LangID(0x0427);
pub const TG_CYRL_TJ: LangID = LangID(0x0428);
pub const FA_IR: LangID = LangID(0x0429);
pub const VI_VN: LangID = LangID(0x042A);
pub const HY_AM: LangID = LangID(0x042B);
pub const AZ_LATN_AZ: LangID = LangID(0x042C);
pub const EU_ES: LangID = LangID(0x042D);
pub const HSB_DE: LangID = LangID(0x042E);
pub const MK_MK: LangID = LangID(0x042F);
pub const ST_ZA: LangID = LangID(0x0430);
pub const TS_ZA: LangID = LangID(0x0431);
pub const TN_ZA: LangID = LangID(0x0432);
pub const VE_ZA: LangID = LangID(0x0433);
pub const XH_ZA: LangID = LangID(0x0434);
pub const ZU_ZA: LangID = LangID(0x0435);
pub const AF_ZA: LangID = LangID(0x0436);
pub const KA_GE: LangID = LangID(0x0437);
pub const FO_FO: LangID = LangID(0x0438);
pub const HI_IN: LangID = LangID(0x0439);
pub const MT_MT: LangID = LangID(0x043A);
pub const SE_NO: LangID = LangID(0x043B);
pub const YI_001: LangID = LangID(0x043D);
pub const MS_MY: LangID = LangID(0x043E);
pub const KK_KZ: LangID = LangID(0x043F);
pub const KY_KG: LangID = LangID(0x0440);
pub const SW_KE: LangID = LangID(0x0441);
pub const TK_TM: LangID = LangID(0x0442);
pub const UZ_LATN_UZ: LangID = LangID(0x0443);
pub const TT_RU: LangID = LangID(0x0444);
pub const BN_IN: LangID = LangID(0x0445);
pub const PA_IN: LangID = LangID(0x0446);
pub const GU_IN: LangID = LangID(0x0447);
pub const OR_IN: LangID = LangID(0x0448);
pub const TA_IN: LangID = LangID(0x0449);
pub const TE_IN: LangID = LangID(0x044A);
pub const KN_IN: LangID = LangID(0x044B);
pub const ML_IN: LangID = LangID(0x044C);
pub const AS_IN: LangID = LangID(0x044D);
pub const MR_IN: LangID = LangID(0x044E);
pub const SA_IN: LangID = LangID(0x044F);
pub const MN_MN: LangID = LangID(0x0450);
pub const BO_CN: LangID = LangID(0x0451);
pub const CY_GB: LangID = LangID(0x0452);
pub const KM_KH: LangID = LangID(0x0453);
pub const LO_LA: LangID = LangID(0x0454);
pub const MY_MM: LangID = LangID(0x0455);
pub const GL_ES: LangID = LangID(0x0456);
pub const KOK_IN: LangID = LangID(0x0457);
pub const MNI_IN: LangID = LangID(0x0458);
pub const SD_DEVA_IN: LangID = LangID(0x0459);
pub const SYR_SY: LangID = LangID(0x045A);
pub const SI_LK: LangID = LangID(0x045B);
pub const CHR_CHER_US: LangID = LangID(0x045C);
pub const IU_CANS_CA: LangID = LangID(0x045D);
pub const AM_ET: LangID = LangID(0x045E);
pub const TZM_ARAB_MA: LangID = LangID(0x045F);
pub const KS_ARAB: LangID = LangID(0x0460);
pub const NE_NP: LangID = LangID(0x0461);
pub const FY_NL: LangID = LangID(0x0462);
pub const PS_AF: LangID = LangID(0x0463);
pub const FIL_PH: LangID = LangID(0x0464);
pub const DV_MV: LangID = LangID(0x0465);
pub const BIN_NG: LangID = LangID(0x0466);
pub const FF_NG__FF_LATN_NG: LangID = LangID(0x0467);
pub const HA_LATN_NG: LangID = LangID(0x0468);
pub const IBB_NG: LangID = LangID(0x0469);
pub const YO_NG: LangID = LangID(0x046A);
pub const QUZ_BO: LangID = LangID(0x046B);
pub const NSO_ZA: LangID = LangID(0x046C);
pub const BA_RU: LangID = LangID(0x046D);
pub const LB_LU: LangID = LangID(0x046E);
pub const KL_GL: LangID = LangID(0x046F);
pub const IG_NG: LangID = LangID(0x0470);
pub const KR_LATN_NG: LangID = LangID(0x0471);
pub const OM_ET: LangID = LangID(0x0472);
pub const TI_ET: LangID = LangID(0x0473);
pub const GN_PY: LangID = LangID(0x0474);
pub const HAW_US: LangID = LangID(0x0475);
pub const LA_VA: LangID = LangID(0x0476);
pub const SO_SO: LangID = LangID(0x0477);
pub const II_CN: LangID = LangID(0x0478);
pub const PAP_029: LangID = LangID(0x0479);
pub const ARN_CL: LangID = LangID(0x047A);
pub const MOH_CA: LangID = LangID(0x047C);
pub const BR_FR: LangID = LangID(0x047E);
pub const UG_CN: LangID = LangID(0x0480);
pub const MI_NZ: LangID = LangID(0x0481);
pub const OC_FR: LangID = LangID(0x0482);
pub const CO_FR: LangID = LangID(0x0483);
pub const GSW_FR: LangID = LangID(0x0484);
pub const SAH_RU: LangID = LangID(0x0485);
pub const QUT_GT: LangID = LangID(0x0486);
pub const RW_RW: LangID = LangID(0x0487);
pub const WO_SN: LangID = LangID(0x0488);
pub const PRS_AF: LangID = LangID(0x048C);
pub const PLT_MG: LangID = LangID(0x048D);
pub const ZH_YUE_HK: LangID = LangID(0x048E);
pub const TDD_TALE_CN: LangID = LangID(0x048F);
pub const KHB_TALU_CN: LangID = LangID(0x0490);
pub const GD_GB: LangID = LangID(0x0491);
pub const KU_ARAB_IQ: LangID = LangID(0x0492);
pub const QUC_CO: LangID = LangID(0x0493);
pub const QPS_PLOC: LangID = LangID(0x0501);
pub const QPS_PLOCA: LangID = LangID(0x05FE);
pub const AR_IQ: LangID = LangID(0x0801);
pub const CA_ES_VALENCIA: LangID = LangID(0x0803);
pub const ZH_CN: LangID = LangID(0x0804);
pub const DE_CH: LangID = LangID(0x0807);
pub const EN_GB: LangID = LangID(0x0809);
pub const ES_MX: LangID = LangID(0x080A);
pub const FR_BE: LangID = LangID(0x080C);
pub const IT_CH: LangID = LangID(0x0810);
pub const JA_PLOC_JP: LangID = LangID(0x0811);
pub const NL_BE: LangID = LangID(0x0813);
pub const NN_NO: LangID = LangID(0x0814);
pub const PT_PT: LangID = LangID(0x0816);
pub const RO_MD: LangID = LangID(0x0818);
pub const RU_MD: LangID = LangID(0x0819);
pub const SR_LATN_CS: LangID = LangID(0x081A);
pub const SV_FI: LangID = LangID(0x081D);
pub const UR_IN: LangID = LangID(0x0820);
pub const AZ_CYRL_AZ: LangID = LangID(0x082C);
pub const DSB_DE: LangID = LangID(0x082E);
pub const TN_BW: LangID = LangID(0x0832);
pub const SE_SE: LangID = LangID(0x083B);
pub const GA_IE: LangID = LangID(0x083C);
pub const MS_BN: LangID = LangID(0x083E);
pub const KK_LATN_KZ: LangID = LangID(0x083F);
pub const UZ_CYRL_UZ: LangID = LangID(0x0843);
pub const BN_BD: LangID = LangID(0x0845);
pub const PA_ARAB_PK: LangID = LangID(0x0846);
pub const TA_LK: LangID = LangID(0x0849);
pub const MN_MONG_CN: LangID = LangID(0x0850);
pub const BO_BT: LangID = LangID(0x0851);
pub const SD_ARAB_PK: LangID = LangID(0x0859);
pub const IU_LATN_CA: LangID = LangID(0x085D);
pub const TZM_LATN_DZ: LangID = LangID(0x085F);
pub const KS_DEVA_IN: LangID = LangID(0x0860);
pub const NE_IN: LangID = LangID(0x0861);
pub const FF_LATN_SN: LangID = LangID(0x0867);
pub const QUZ_EC: LangID = LangID(0x086B);
pub const TI_ER: LangID = LangID(0x0873);
pub const QPS_PLOCM: LangID = LangID(0x09FF);
pub const LOCALE_CUSTOM_USER_DEFAULT: LangID = LangID(0x0C00);
pub const AR_EG: LangID = LangID(0x0C01);
pub const ZH_HK: LangID = LangID(0x0C04);
pub const DE_AT: LangID = LangID(0x0C07);
pub const EN_AU: LangID = LangID(0x0C09);
pub const ES_ES: LangID = LangID(0x0C0A);
pub const FR_CA: LangID = LangID(0x0C0C);
pub const SR_CYRL_CS: LangID = LangID(0x0C1A);
pub const SE_FI: LangID = LangID(0x0C3B);
pub const MN_MONG_MN: LangID = LangID(0x0C50);
pub const DZ_BT: LangID = LangID(0x0C51);
pub const TMZ_MA: LangID = LangID(0x0C5F);
pub const QUZ_PE: LangID = LangID(0x0C6B);
pub const LOCALE_CUSTOM_UNSPECIFIED: LangID = LangID(0x1000);
pub const AR_LY: LangID = LangID(0x1001);
pub const ZH_SG: LangID = LangID(0x1004);
pub const DE_LU: LangID = LangID(0x1007);
pub const EN_CA: LangID = LangID(0x1009);
pub const ES_GT: LangID = LangID(0x100A);
pub const FR_CH: LangID = LangID(0x100C);
pub const HR_BA: LangID = LangID(0x101A);
pub const SMJ_NO: LangID = LangID(0x103B);
pub const TZM_TFNG_MA: LangID = LangID(0x105F);
pub const AR_DZ: LangID = LangID(0x1401);
pub const ZH_MO: LangID = LangID(0x1404);
pub const DE_LI: LangID = LangID(0x1407);
pub const EN_NZ: LangID = LangID(0x1409);
pub const ES_CR: LangID = LangID(0x140A);
pub const FR_LU: LangID = LangID(0x140C);
pub const BS_LATN_BA: LangID = LangID(0x141A);
pub const SMJ_SE: LangID = LangID(0x143B);
pub const AR_MA: LangID = LangID(0x1801);
pub const EN_IE: LangID = LangID(0x1809);
pub const ES_PA: LangID = LangID(0x180A);
pub const FR_MC: LangID = LangID(0x180C);
pub const SR_LATN_BA: LangID = LangID(0x181A);
pub const SMA_NO: LangID = LangID(0x183B);
pub const AR_TN: LangID = LangID(0x1C01);
pub const EN_ZA: LangID = LangID(0x1C09);
pub const ES_DO: LangID = LangID(0x1C0A);
pub const FR_029: LangID = LangID(0x1C0C);
pub const SR_CYRL_BA: LangID = LangID(0x1C1A);
pub const SMA_SE: LangID = LangID(0x1C3B);
pub const AR_OM: LangID = LangID(0x2001);
pub const EN_JM: LangID = LangID(0x2009);
pub const ES_VE: LangID = LangID(0x200A);
pub const FR_RE: LangID = LangID(0x200C);
pub const BS_CYRL_BA: LangID = LangID(0x201A);
pub const SMS_FI: LangID = LangID(0x203B);
pub const AR_YE: LangID = LangID(0x2401);
pub const EN_029: LangID = LangID(0x2409);
pub const ES_CO: LangID = LangID(0x240A);
pub const FR_CD: LangID = LangID(0x240C);
pub const SR_LATN_RS: LangID = LangID(0x241A);
pub const SMN_FI: LangID = LangID(0x243B);
pub const AR_SY: LangID = LangID(0x2801);
pub const EN_BZ: LangID = LangID(0x2809);
pub const ES_PE: LangID = LangID(0x280A);
pub const FR_SN: LangID = LangID(0x280C);
pub const SR_CYRL_RS: LangID = LangID(0x281A);
pub const AR_JO: LangID = LangID(0x2C01);
pub const EN_TT: LangID = LangID(0x2C09);
pub const ES_AR: LangID = LangID(0x2C0A);
pub const FR_CM: LangID = LangID(0x2C0C);
pub const SR_LATN_ME: LangID = LangID(0x2C1A);
pub const AR_LB: LangID = LangID(0x3001);
pub const EN_ZW: LangID = LangID(0x3009);
pub const ES_EC: LangID = LangID(0x300A);
pub const FR_CI: LangID = LangID(0x300C);
pub const SR_CYRL_ME: LangID = LangID(0x301A);
pub const AR_KW: LangID = LangID(0x3401);
pub const EN_PH: LangID = LangID(0x3409);
pub const ES_CL: LangID = LangID(0x340A);
pub const FR_ML: LangID = LangID(0x340C);
pub const AR_AE: LangID = LangID(0x3801);
pub const EN_ID: LangID = LangID(0x3809);
pub const ES_UY: LangID = LangID(0x380A);
pub const FR_MA: LangID = LangID(0x380C);
pub const AR_BH: LangID = LangID(0x3C01);
pub const EN_HK: LangID = LangID(0x3C09);
pub const ES_PY: LangID = LangID(0x3C0A);
pub const FR_HT: LangID = LangID(0x3C0C);
pub const AR_QA: LangID = LangID(0x4001);
pub const EN_IN: LangID = LangID(0x4009);
pub const ES_BO: LangID = LangID(0x400A);
pub const AR_PLOC_SA: LangID = LangID(0x4401);
pub const EN_MY: LangID = LangID(0x4409);
pub const ES_SV: LangID = LangID(0x440A);
pub const AR_145: LangID = LangID(0x4801);
pub const EN_SG: LangID = LangID(0x4809);
pub const ES_HN: LangID = LangID(0x480A);
pub const EN_AE: LangID = LangID(0x4C09);
pub const ES_NI: LangID = LangID(0x4C0A);
pub const EN_BH: LangID = LangID(0x5009);
pub const ES_PR: LangID = LangID(0x500A);
pub const EN_EG: LangID = LangID(0x5409);
pub const ES_US: LangID = LangID(0x540A);
pub const EN_JO: LangID = LangID(0x5809);
pub const ES_419: LangID = LangID(0x580A);
pub const EN_KW: LangID = LangID(0x5C09);
pub const ES_CU: LangID = LangID(0x5C0A);
pub const EN_TR: LangID = LangID(0x6009);
pub const EN_YE: LangID = LangID(0x6409);
pub const BS_CYRL: LangID = LangID(0x641A);
pub const BS_LATN: LangID = LangID(0x681A);
pub const SR_CYRL: LangID = LangID(0x6C1A);
pub const SR_LATN: LangID = LangID(0x701A);
pub const SMN: LangID = LangID(0x703B);
pub const AZ_CYRL: LangID = LangID(0x742C);
pub const SMS: LangID = LangID(0x743B);
pub const ZH: LangID = LangID(0x7804);
pub const NN: LangID = LangID(0x7814);
pub const BS: LangID = LangID(0x781A);
pub const AZ_LATN: LangID = LangID(0x782C);
pub const SMA: LangID = LangID(0x783B);
pub const KK_CYRL: LangID = LangID(0x783F);
pub const UZ_CYRL: LangID = LangID(0x7843);
pub const MN_CYRL: LangID = LangID(0x7850);
pub const IU_CANS: LangID = LangID(0x785D);
pub const TZM_TFNG: LangID = LangID(0x785F);
pub const ZH_HANT: LangID = LangID(0x7C04);
pub const NB: LangID = LangID(0x7C14);
pub const SR: LangID = LangID(0x7C1A);
pub const TG_CYRL: LangID = LangID(0x7C28);
pub const DSB: LangID = LangID(0x7C2E);
pub const SMJ: LangID = LangID(0x7C3B);
pub const KK_LATN: LangID = LangID(0x7C3F);
pub const UZ_LATN: LangID = LangID(0x7C43);
pub const PA_ARAB: LangID = LangID(0x7C46);
pub const MN_MONG: LangID = LangID(0x7C50);
pub const SD_ARAB: LangID = LangID(0x7C59);
pub const CHR_CHER: LangID = LangID(0x7C5C);
pub const IU_LATN: LangID = LangID(0x7C5D);
pub const TZM_LATN: LangID = LangID(0x7C5F);
pub const FF_LATN: LangID = LangID(0x7C67);
pub const HA_LATN: LangID = LangID(0x7C68);
pub const KU_ARAB: LangID = LangID(0x7C92);
pub const FR_015: LangID = LangID(0xE40C);
}
+686
View File
@@ -0,0 +1,686 @@
use crate::bus::{InterfaceNumber, PollResult, StringIndex, UsbBus, UsbBusAllocator};
use crate::class::{ControlIn, ControlOut, UsbClass};
use crate::control;
use crate::control_pipe::ControlPipe;
use crate::descriptor::{descriptor_type, lang_id::LangID, BosWriter, DescriptorWriter};
pub use crate::device_builder::{StringDescriptors, UsbDeviceBuilder, UsbVidPid};
use crate::endpoint::{EndpointAddress, EndpointType};
use crate::{Result, UsbDirection};
/// The global state of the USB device.
///
/// In general class traffic is only possible in the `Configured` state.
#[repr(u8)]
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UsbDeviceState {
/// The USB device has just been created or reset.
Default,
/// The USB device has received an address from the host.
Addressed,
/// The USB device has been configured and is fully functional.
Configured,
/// The USB device has been suspended by the host or it has been unplugged from the USB bus.
Suspend,
}
// Maximum number of endpoints in one direction. Specified by the USB specification.
const MAX_ENDPOINTS: usize = 16;
/// Usb spec revision.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u16)]
pub enum UsbRev {
/// USB 2.0 compliance
Usb200 = 0x200,
/// USB 2.1 compliance.
///
/// Typically adds support for BOS requests.
Usb210 = 0x210,
}
/// A USB device consisting of one or more device classes.
pub struct UsbDevice<'a, B: UsbBus> {
bus: &'a B,
config: Config<'a>,
control: ControlPipe<'a, B>,
device_state: UsbDeviceState,
remote_wakeup_enabled: bool,
self_powered: bool,
suspended_device_state: Option<UsbDeviceState>,
pending_address: u8,
}
pub(crate) struct Config<'a> {
pub device_class: u8,
pub device_sub_class: u8,
pub device_protocol: u8,
pub max_packet_size_0: u8,
pub vendor_id: u16,
pub product_id: u16,
pub usb_rev: UsbRev,
pub device_release: u16,
pub string_descriptors: heapless::Vec<StringDescriptors<'a>, 16>,
pub self_powered: bool,
pub supports_remote_wakeup: bool,
pub composite_with_iads: bool,
pub max_power: u8,
}
/// The bConfiguration value for the not configured state.
pub const CONFIGURATION_NONE: u8 = 0;
/// The bConfiguration value for the single configuration supported by this device.
pub const CONFIGURATION_VALUE: u8 = 1;
/// The default value for bAlternateSetting for all interfaces.
pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
type ClassList<'a, B> = [&'a mut dyn UsbClass<B>];
impl<B: UsbBus> UsbDevice<'_, B> {
pub(crate) fn build<'a>(alloc: &'a UsbBusAllocator<B>, config: Config<'a>) -> UsbDevice<'a, B> {
let control_out = alloc
.alloc(
Some(0x00.into()),
EndpointType::Control,
config.max_packet_size_0 as u16,
0,
)
.expect("failed to alloc control endpoint");
let control_in = alloc
.alloc(
Some(0x80.into()),
EndpointType::Control,
config.max_packet_size_0 as u16,
0,
)
.expect("failed to alloc control endpoint");
let bus = alloc.freeze();
UsbDevice {
bus,
config,
control: ControlPipe::new(control_out, control_in),
device_state: UsbDeviceState::Default,
remote_wakeup_enabled: false,
self_powered: false,
suspended_device_state: None,
pending_address: 0,
}
}
/// Gets a reference to the [`UsbBus`] implementation used by this `UsbDevice`. You can use this
/// to call platform-specific methods on the `UsbBus`.
///
/// While it is also possible to call the standard `UsbBus` trait methods through this
/// reference, this is not recommended as it can cause the device to misbehave.
pub fn bus(&self) -> &B {
self.bus
}
/// Gets the current state of the device.
///
/// In general class traffic is only possible in the `Configured` state.
pub fn state(&self) -> UsbDeviceState {
self.device_state
}
/// Gets whether host remote wakeup has been enabled by the host.
pub fn remote_wakeup_enabled(&self) -> bool {
self.remote_wakeup_enabled
}
/// Gets whether the device is currently self powered.
pub fn self_powered(&self) -> bool {
self.self_powered
}
/// Sets whether the device is currently self powered.
pub fn set_self_powered(&mut self, is_self_powered: bool) {
self.self_powered = is_self_powered;
}
/// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the
/// device.
///
/// Mostly useful for development. Calling this at the start of your program ensures that the
/// host re-enumerates your device after a new program has been flashed.
pub fn force_reset(&mut self) -> Result<()> {
self.bus.force_reset()
}
/// Polls the [`UsbBus`] for new events and dispatches them to the provided classes. Returns
/// true if one of the classes may have data available for reading or be ready for writing,
/// false otherwise. This should be called periodically as often as possible for the best data
/// rate, or preferably from an interrupt handler. Must be called at least once every 10
/// milliseconds while connected to the USB host to be USB compliant.
///
/// Note: The list of classes passed in must be the same classes in the same order for every
/// call while the device is configured, or the device may enumerate incorrectly or otherwise
/// misbehave. The easiest way to do this is to call the `poll` method in only one place in your
/// code, as follows:
///
/// ``` ignore
/// usb_dev.poll(&mut [&mut class1, &mut class2]);
/// ```
///
/// Strictly speaking the list of classes is allowed to change between polls if the device has
/// been reset, which is indicated by `state` being equal to [`UsbDeviceState::Default`].
pub fn poll(&mut self, classes: &mut ClassList<'_, B>) -> bool {
let pr = self.bus.poll();
if self.device_state == UsbDeviceState::Suspend {
match pr {
PollResult::Suspend | PollResult::None => {
return false;
}
_ => {
self.bus.resume();
self.device_state = self
.suspended_device_state
.expect("Unknown state before suspend");
self.suspended_device_state = None;
}
}
}
match pr {
PollResult::None => {}
PollResult::Reset => self.reset(classes),
PollResult::Data {
ep_out,
ep_in_complete,
ep_setup,
} => {
// Combine bit fields for quick tests
let mut eps = ep_out | ep_in_complete | ep_setup;
// Pending events for endpoint 0?
if (eps & 1) != 0 {
usb_debug!(
"EP0: setup={}, in_complete={}, out={}",
ep_setup & 1,
ep_in_complete & 1,
ep_out & 1
);
let req = if (ep_setup & 1) != 0 {
self.control.handle_setup()
} else if (ep_out & 1) != 0 {
match self.control.handle_out() {
Ok(req) => req,
Err(_err) => {
// TODO: Propagate error out of `poll()`
usb_debug!("Failed to handle EP0: {:?}", _err);
None
}
}
} else {
None
};
match req {
Some(req) if req.direction == UsbDirection::In => {
if let Err(_err) = self.control_in(classes, req) {
// TODO: Propagate error out of `poll()`
usb_debug!("Failed to handle input control request: {:?}", _err);
}
}
Some(req) if req.direction == UsbDirection::Out => {
if let Err(_err) = self.control_out(classes, req) {
// TODO: Propagate error out of `poll()`
usb_debug!("Failed to handle output control request: {:?}", _err);
}
}
None if ((ep_in_complete & 1) != 0) => {
// We only handle EP0-IN completion if there's no other request being
// processed. EP0-IN tokens may be issued due to completed STATUS
// phases of the control transfer. If we just got a SETUP packet or
// an OUT token, we can safely ignore the IN-COMPLETE indication and
// continue with the next transfer.
let completed = match self.control.handle_in_complete() {
Ok(completed) => completed,
Err(_err) => {
// TODO: Propagate this out of `poll()`
usb_debug!(
"Failed to process control-input complete: {:?}",
_err
);
false
}
};
if !B::QUIRK_SET_ADDRESS_BEFORE_STATUS
&& completed
&& self.pending_address != 0
{
self.bus.set_device_address(self.pending_address);
self.pending_address = 0;
self.device_state = UsbDeviceState::Addressed;
}
}
_ => (),
};
eps &= !1;
}
// Pending events for other endpoints?
if eps != 0 {
let mut bit = 2u16;
for i in 1..MAX_ENDPOINTS {
if (ep_setup & bit) != 0 {
for cls in classes.iter_mut() {
usb_trace!("Handling EP{}-SETUP", i);
cls.endpoint_setup(EndpointAddress::from_parts(
i,
UsbDirection::Out,
));
}
} else if (ep_out & bit) != 0 {
usb_trace!("Handling EP{}-OUT", i);
for cls in classes.iter_mut() {
cls.endpoint_out(EndpointAddress::from_parts(i, UsbDirection::Out));
}
}
if (ep_in_complete & bit) != 0 {
usb_trace!("Handling EP{}-IN", i);
for cls in classes.iter_mut() {
cls.endpoint_in_complete(EndpointAddress::from_parts(
i,
UsbDirection::In,
));
}
}
eps &= !bit;
if eps == 0 {
// No more pending events for higher endpoints
break;
}
bit <<= 1;
}
}
for cls in classes.iter_mut() {
cls.poll();
}
return true;
}
PollResult::Resume => {}
PollResult::Suspend => {
usb_debug!("Suspending bus");
self.bus.suspend();
self.suspended_device_state = Some(self.device_state);
self.device_state = UsbDeviceState::Suspend;
}
}
false
}
fn control_in(&mut self, classes: &mut ClassList<'_, B>, req: control::Request) -> Result<()> {
use crate::control::{Recipient, Request};
for cls in classes.iter_mut() {
cls.control_in(ControlIn::new(&mut self.control, &req));
if !self.control.waiting_for_response() {
return Ok(());
}
}
if req.request_type == control::RequestType::Standard {
let xfer = ControlIn::new(&mut self.control, &req);
match (req.recipient, req.request) {
(Recipient::Device, Request::GET_STATUS) => {
usb_trace!("Processing Device::GetStatus");
let status: u16 = if self.self_powered { 0x0001 } else { 0x0000 }
| if self.remote_wakeup_enabled {
0x0002
} else {
0x0000
};
xfer.accept_with(&status.to_le_bytes())?;
}
(Recipient::Interface, Request::GET_STATUS) => {
usb_trace!("Processing Interface::GetStatus");
let status: u16 = 0x0000;
xfer.accept_with(&status.to_le_bytes())?;
}
(Recipient::Endpoint, Request::GET_STATUS) => {
usb_trace!("Processing EP::GetStatus");
let ep_addr = ((req.index as u8) & 0x8f).into();
let status: u16 = if self.bus.is_stalled(ep_addr) {
0x0001
} else {
0x0000
};
xfer.accept_with(&status.to_le_bytes())?;
}
(Recipient::Device, Request::GET_DESCRIPTOR) => {
usb_trace!("Processing Device::GetDescriptor");
UsbDevice::get_descriptor(&self.config, classes, xfer)?;
}
(Recipient::Device, Request::GET_CONFIGURATION) => {
usb_trace!("Processing Device::GetConfiguration");
let config = match self.device_state {
UsbDeviceState::Configured => CONFIGURATION_VALUE,
_ => CONFIGURATION_NONE,
};
xfer.accept_with(&config.to_le_bytes())?;
}
(Recipient::Interface, Request::GET_INTERFACE) => {
usb_trace!("Processing Interface::GetInterface");
// Reject interface numbers bigger than 255
if req.index > core::u8::MAX.into() {
return xfer.reject();
}
// Ask class implementations, whether they know the alternate setting
// of the interface in question
for cls in classes {
if let Some(setting) = cls.get_alt_setting(InterfaceNumber(req.index as u8))
{
return xfer.accept_with(&setting.to_le_bytes());
}
}
// If no class returned an alternate setting, return the default value
xfer.accept_with(&DEFAULT_ALTERNATE_SETTING.to_le_bytes())?;
}
_ => {}
};
}
if self.control.waiting_for_response() {
usb_debug!("Rejecting control transfer because we were waiting for a response");
self.control.reject()?;
}
Ok(())
}
fn control_out(&mut self, classes: &mut ClassList<'_, B>, req: control::Request) -> Result<()> {
use crate::control::{Recipient, Request};
for cls in classes.iter_mut() {
cls.control_out(ControlOut::new(&mut self.control, &req));
if !self.control.waiting_for_response() {
return Ok(());
}
}
if req.request_type == control::RequestType::Standard {
let xfer = ControlOut::new(&mut self.control, &req);
const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16;
const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16;
const DEFAULT_ALTERNATE_SETTING_U16: u16 = DEFAULT_ALTERNATE_SETTING as u16;
match (req.recipient, req.request, req.value) {
(
Recipient::Device,
Request::CLEAR_FEATURE,
Request::FEATURE_DEVICE_REMOTE_WAKEUP,
) => {
usb_debug!("Remote wakeup disabled");
self.remote_wakeup_enabled = false;
xfer.accept()?;
}
(Recipient::Endpoint, Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
usb_debug!("EP{} halt removed", req.index & 0x8f);
self.bus
.set_stalled(((req.index as u8) & 0x8f).into(), false);
xfer.accept()?;
}
(
Recipient::Device,
Request::SET_FEATURE,
Request::FEATURE_DEVICE_REMOTE_WAKEUP,
) => {
usb_debug!("Remote wakeup enabled");
self.remote_wakeup_enabled = true;
xfer.accept()?;
}
(Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
usb_debug!("EP{} halted", req.index & 0x8f);
self.bus
.set_stalled(((req.index as u8) & 0x8f).into(), true);
xfer.accept()?;
}
(Recipient::Device, Request::SET_ADDRESS, 1..=127) => {
usb_debug!("Setting device address to {}", req.value);
if B::QUIRK_SET_ADDRESS_BEFORE_STATUS {
self.bus.set_device_address(req.value as u8);
self.device_state = UsbDeviceState::Addressed;
} else {
self.pending_address = req.value as u8;
}
xfer.accept()?;
}
(Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => {
usb_debug!("Device configured");
self.device_state = UsbDeviceState::Configured;
xfer.accept()?;
}
(Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => {
usb_debug!("Device deconfigured");
match self.device_state {
UsbDeviceState::Default => {
xfer.accept()?;
}
_ => {
self.device_state = UsbDeviceState::Addressed;
xfer.accept()?;
}
}
}
(Recipient::Interface, Request::SET_INTERFACE, alt_setting) => {
// Reject interface numbers and alt settings bigger than 255
if req.index > core::u8::MAX.into() || alt_setting > core::u8::MAX.into() {
xfer.reject()?;
return Ok(());
}
// Ask class implementations, whether they accept the alternate interface setting.
for cls in classes {
if cls.set_alt_setting(InterfaceNumber(req.index as u8), alt_setting as u8)
{
xfer.accept()?;
return Ok(());
}
}
// Default behaviour, if no class implementation accepted the alternate setting.
if alt_setting == DEFAULT_ALTERNATE_SETTING_U16 {
usb_debug!("Accepting unused alternate settings");
xfer.accept()?;
} else {
usb_debug!("Rejecting unused alternate settings");
xfer.reject()?;
}
}
_ => {
xfer.reject()?;
return Ok(());
}
}
}
if self.control.waiting_for_response() {
usb_debug!("Rejecting control transfer due to waiting response");
self.control.reject()?;
}
Ok(())
}
fn get_descriptor(
config: &Config,
classes: &mut ClassList<'_, B>,
xfer: ControlIn<B>,
) -> Result<()> {
let req = *xfer.request();
let (dtype, index) = req.descriptor_type_index();
fn accept_writer<B: UsbBus>(
xfer: ControlIn<B>,
f: impl FnOnce(&mut DescriptorWriter) -> Result<()>,
) -> Result<()> {
xfer.accept(|buf| {
let mut writer = DescriptorWriter::new(buf);
f(&mut writer)?;
Ok(writer.position())
})?;
Ok(())
}
match dtype {
descriptor_type::BOS if config.usb_rev > UsbRev::Usb200 => accept_writer(xfer, |w| {
let mut bw = BosWriter::new(w);
bw.bos()?;
for cls in classes {
cls.get_bos_descriptors(&mut bw)?;
}
bw.end_bos();
Ok(())
})?,
descriptor_type::DEVICE => accept_writer(xfer, |w| w.device(config))?,
descriptor_type::CONFIGURATION => accept_writer(xfer, |w| {
w.configuration(config)?;
for cls in classes {
cls.get_configuration_descriptors(w)?;
w.end_class();
}
w.end_configuration();
Ok(())
})?,
descriptor_type::STRING => match index {
// first STRING Request
0 => {
let mut lang_id_bytes = [0u8; 32];
for (lang, buf) in config
.string_descriptors
.iter()
.zip(lang_id_bytes.chunks_exact_mut(2))
{
buf.copy_from_slice(&u16::from(lang.id).to_le_bytes());
}
accept_writer(xfer, |w| {
w.write(
descriptor_type::STRING,
&lang_id_bytes[..config.string_descriptors.len() * 2],
)
})?;
}
// rest STRING Requests
_ => {
let lang_id = LangID::from(req.index);
let string = match index {
// Manufacturer, product, and serial are handled directly here.
1..=3 => {
let Some(lang) = config
.string_descriptors
.iter()
.find(|lang| lang.id == lang_id)
else {
xfer.reject()?;
return Ok(());
};
match index {
1 => lang.manufacturer,
2 => lang.product,
3 => lang.serial,
_ => unreachable!(),
}
}
_ => {
let index = StringIndex::new(index);
classes
.iter()
.find_map(|cls| cls.get_string(index, lang_id))
}
};
if let Some(string_descriptor) = string {
accept_writer(xfer, |w| w.string(string_descriptor))?;
} else {
xfer.reject()?;
}
}
},
_ => {
xfer.reject()?;
}
};
Ok(())
}
fn reset(&mut self, classes: &mut ClassList<'_, B>) {
self.bus.reset();
self.device_state = UsbDeviceState::Default;
self.suspended_device_state = None; // We may reset during Suspend
self.remote_wakeup_enabled = false;
self.pending_address = 0;
self.control.reset();
for cls in classes {
cls.reset();
}
}
}
+211
View File
@@ -0,0 +1,211 @@
use crate::bus::{UsbBus, UsbBusAllocator};
use crate::descriptor::lang_id::LangID;
use crate::device::{Config, UsbDevice, UsbRev};
/// A USB vendor ID and product ID pair.
pub struct UsbVidPid(pub u16, pub u16);
/// Used to build new [`UsbDevice`]s.
pub struct UsbDeviceBuilder<'a, B: UsbBus> {
alloc: &'a UsbBusAllocator<B>,
config: Config<'a>,
}
macro_rules! builder_fields {
( $( $(#[$meta:meta])* $name:ident: $type:ty, )* ) => {
$(
$(#[$meta])*
pub fn $name(mut self, $name: $type) -> Self {
self.config.$name = $name;
self
}
)*
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
/// Error type for the USB device builder
pub enum BuilderError {
/// String descriptors were provided in more languages than are supported
TooManyLanguages,
/// Control endpoint can only be 8, 16, 32, or 64 byte max packet size
InvalidPacketSize,
/// Configuration specifies higher USB power draw than allowed
PowerTooHigh,
}
/// Provides basic string descriptors about the device, including the manufacturer, product name,
/// and serial number of the device in a specified language.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct StringDescriptors<'a> {
pub(crate) id: LangID,
pub(crate) serial: Option<&'a str>,
pub(crate) product: Option<&'a str>,
pub(crate) manufacturer: Option<&'a str>,
}
impl<'a> Default for StringDescriptors<'a> {
fn default() -> Self {
Self::new(LangID::EN_US)
}
}
impl<'a> StringDescriptors<'a> {
/// Create a new descriptor list with the provided language.
pub fn new(lang_id: LangID) -> Self {
Self {
id: lang_id,
serial: None,
product: None,
manufacturer: None,
}
}
/// Specify the serial number for this language.
pub fn serial_number(mut self, serial: &'a str) -> Self {
self.serial.replace(serial);
self
}
/// Specify the manufacturer name for this language.
pub fn manufacturer(mut self, manufacturer: &'a str) -> Self {
self.manufacturer.replace(manufacturer);
self
}
/// Specify the product name for this language.
pub fn product(mut self, product: &'a str) -> Self {
self.product.replace(product);
self
}
}
impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> {
/// Creates a builder for constructing a new [`UsbDevice`].
pub fn new(alloc: &'a UsbBusAllocator<B>, vid_pid: UsbVidPid) -> UsbDeviceBuilder<'a, B> {
UsbDeviceBuilder {
alloc,
config: Config {
device_class: 0x00,
device_sub_class: 0x00,
device_protocol: 0x00,
max_packet_size_0: 8,
vendor_id: vid_pid.0,
product_id: vid_pid.1,
usb_rev: UsbRev::Usb210,
device_release: 0x0010,
string_descriptors: heapless::Vec::new(),
self_powered: false,
supports_remote_wakeup: false,
composite_with_iads: false,
max_power: 50,
},
}
}
/// Creates the [`UsbDevice`] instance with the configuration in this builder.
pub fn build(self) -> UsbDevice<'a, B> {
UsbDevice::build(self.alloc, self.config)
}
builder_fields! {
/// Sets the device class code assigned by USB.org. Set to `0xff` for vendor-specific
/// devices that do not conform to any class.
///
/// Default: `0x00` (class code specified by interfaces)
device_class: u8,
/// Sets the device sub-class code. Depends on class.
///
/// Default: `0x00`
device_sub_class: u8,
/// Sets the device protocol code. Depends on class and sub-class.
///
/// Default: `0x00`
device_protocol: u8,
/// Sets the device release version in BCD.
///
/// Default: `0x0010` ("0.1")
device_release: u16,
/// Sets whether the device may have an external power source.
///
/// This should be set to `true` even if the device is sometimes self-powered and may not
/// always draw power from the USB bus.
///
/// Default: `false`
///
/// See also: `max_power`
self_powered: bool,
/// Sets whether the device supports remotely waking up the host is requested.
///
/// Default: `false`
supports_remote_wakeup: bool,
/// Sets which Usb 2 revision to comply to.
///
/// Default: `UsbRev::Usb210`
usb_rev: UsbRev,
}
/// Configures the device as a composite device with interface association descriptors.
pub fn composite_with_iads(mut self) -> Self {
// Magic values specified in USB-IF ECN on IADs.
self.config.device_class = 0xEF;
self.config.device_sub_class = 0x02;
self.config.device_protocol = 0x01;
self.config.composite_with_iads = true;
self
}
/// Specify the strings for the device.
///
/// # Note
/// Up to 16 languages may be provided.
pub fn strings(mut self, descriptors: &[StringDescriptors<'a>]) -> Result<Self, BuilderError> {
// The 16 language limit comes from the size of the buffer used to provide the list of
// language descriptors to the host.
self.config.string_descriptors =
heapless::Vec::from_slice(descriptors).map_err(|_| BuilderError::TooManyLanguages)?;
Ok(self)
}
/// Sets the maximum packet size in bytes for the control endpoint 0.
///
/// Valid values are 8, 16, 32 and 64. There's generally no need to change this from the default
/// value of 8 bytes unless a class uses control transfers for sending large amounts of data, in
/// which case using a larger packet size may be more efficient.
///
/// Default: 8 bytes
pub fn max_packet_size_0(mut self, max_packet_size_0: u8) -> Result<Self, BuilderError> {
match max_packet_size_0 {
8 | 16 | 32 | 64 => {}
_ => return Err(BuilderError::InvalidPacketSize),
}
self.config.max_packet_size_0 = max_packet_size_0;
Ok(self)
}
/// Sets the maximum current drawn from the USB bus by the device in milliamps.
///
/// The default is 100 mA. If your device always uses an external power source and never draws
/// power from the USB bus, this can be set to 0.
///
/// See also: `self_powered`
///
/// Default: 100mA
pub fn max_power(mut self, max_power_ma: usize) -> Result<Self, BuilderError> {
if max_power_ma > 500 {
return Err(BuilderError::PowerTooHigh);
}
self.config.max_power = (max_power_ma / 2) as u8;
Ok(self)
}
}
+274
View File
@@ -0,0 +1,274 @@
use crate::bus::UsbBus;
use crate::{Result, UsbDirection};
use core::marker::PhantomData;
use portable_atomic::{AtomicPtr, Ordering};
/// Trait for endpoint type markers.
pub trait EndpointDirection {
/// Direction value of the marker type.
const DIRECTION: UsbDirection;
}
/// Marker type for OUT endpoints.
pub struct Out;
impl EndpointDirection for Out {
const DIRECTION: UsbDirection = UsbDirection::Out;
}
/// Marker type for IN endpoints.
pub struct In;
impl EndpointDirection for In {
const DIRECTION: UsbDirection = UsbDirection::In;
}
/// A host-to-device (OUT) endpoint.
pub type EndpointOut<'a, B> = Endpoint<'a, B, Out>;
/// A device-to-host (IN) endpoint.
pub type EndpointIn<'a, B> = Endpoint<'a, B, In>;
/// Isochronous transfers employ one of three synchronization schemes. See USB 2.0 spec 5.12.4.1.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum IsochronousSynchronizationType {
/// Synchronization is not implemented for this endpoint.
NoSynchronization,
/// Source and Sink sample clocks are free running.
Asynchronous,
/// Source sample clock is locked to Sink, Sink sample clock is locked to data flow.
Adaptive,
/// Source and Sink sample clocks are locked to USB SOF.
Synchronous,
}
/// Intended use of an isochronous endpoint, see USB 2.0 spec sections 5.12 and 9.6.6.
/// Associations between data and feedback endpoints are described in section 9.6.6.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum IsochronousUsageType {
/// Endpoint is used for isochronous data.
Data,
/// Feedback for synchronization.
Feedback,
/// Endpoint is data and provides implicit feedback for synchronization.
ImplicitFeedbackData,
}
/// USB endpoint transfer type.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum EndpointType {
/// Control endpoint. Used for device management. Only the host can initiate requests. Usually
/// used only endpoint 0.
Control,
/// Isochronous endpoint. Used for time-critical unreliable data.
///
/// See USB 2.0 spec section 5.12 "Special Considerations for Isochronous Transfers"
Isochronous {
/// Synchronization model used for the data stream that this endpoint relates to.
synchronization: IsochronousSynchronizationType,
/// Endpoint's role in the synchronization model selected by [Self::Isochronous::synchronization].
usage: IsochronousUsageType,
},
/// Bulk endpoint. Used for large amounts of best-effort reliable data.
Bulk,
/// Interrupt endpoint. Used for small amounts of time-critical reliable data.
Interrupt,
}
impl EndpointType {
/// Format EndpointType for use in bmAttributes transfer type field USB 2.0 spec section 9.6.6
pub fn to_bm_attributes(&self) -> u8 {
match self {
EndpointType::Control => 0b00,
EndpointType::Isochronous {
synchronization,
usage,
} => {
let sync_bits = match synchronization {
IsochronousSynchronizationType::NoSynchronization => 0b00,
IsochronousSynchronizationType::Asynchronous => 0b01,
IsochronousSynchronizationType::Adaptive => 0b10,
IsochronousSynchronizationType::Synchronous => 0b11,
};
let usage_bits = match usage {
IsochronousUsageType::Data => 0b00,
IsochronousUsageType::Feedback => 0b01,
IsochronousUsageType::ImplicitFeedbackData => 0b10,
};
(usage_bits << 4) | (sync_bits << 2) | 0b01
}
EndpointType::Bulk => 0b10,
EndpointType::Interrupt => 0b11,
}
}
}
/// Handle for a USB endpoint. The endpoint direction is constrained by the `D` type argument, which
/// must be either `In` or `Out`.
pub struct Endpoint<'a, B: UsbBus, D: EndpointDirection> {
bus_ptr: &'a AtomicPtr<B>,
address: EndpointAddress,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
_marker: PhantomData<D>,
}
impl<B: UsbBus, D: EndpointDirection> Endpoint<'_, B, D> {
pub(crate) fn new(
bus_ptr: &AtomicPtr<B>,
address: EndpointAddress,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Endpoint<'_, B, D> {
Endpoint {
bus_ptr,
address,
ep_type,
max_packet_size,
interval,
_marker: PhantomData,
}
}
fn bus(&self) -> &B {
let bus_ptr = self.bus_ptr.load(Ordering::SeqCst);
if bus_ptr.is_null() {
panic!("UsbBus initialization not complete");
}
unsafe { &*bus_ptr }
}
/// Gets the endpoint address including direction bit.
pub fn address(&self) -> EndpointAddress {
self.address
}
/// Gets the endpoint transfer type.
pub fn ep_type(&self) -> EndpointType {
self.ep_type
}
/// Gets the maximum packet size for the endpoint.
pub fn max_packet_size(&self) -> u16 {
self.max_packet_size
}
/// Gets the poll interval for interrupt endpoints.
pub fn interval(&self) -> u8 {
self.interval
}
/// Sets the STALL condition for the endpoint.
pub fn stall(&self) {
self.bus().set_stalled(self.address, true);
}
/// Clears the STALL condition of the endpoint.
pub fn unstall(&self) {
self.bus().set_stalled(self.address, false);
}
}
impl<B: UsbBus> Endpoint<'_, B, In> {
/// Writes a single packet of data to the specified endpoint and returns number of bytes
/// actually written. The buffer must not be longer than the `max_packet_size` specified when
/// allocating the endpoint.
///
/// # Errors
///
/// Note: USB bus implementation errors are directly passed through, so be prepared to handle
/// other errors as well.
///
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - The transmission buffer of the USB
/// peripheral is full and the packet cannot be sent now. A peripheral may or may not support
/// concurrent transmission of packets.
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The data is longer than the
/// `max_packet_size` specified when allocating the endpoint. This is generally an error in
/// the class implementation.
pub fn write(&self, data: &[u8]) -> Result<usize> {
self.bus().write(self.address, data)
}
}
impl<B: UsbBus> Endpoint<'_, B, Out> {
/// Reads a single packet of data from the specified endpoint and returns the actual length of
/// the packet. The buffer should be large enough to fit at least as many bytes as the
/// `max_packet_size` specified when allocating the endpoint.
///
/// # Errors
///
/// Note: USB bus implementation errors are directly passed through, so be prepared to handle
/// other errors as well.
///
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - There is no packet to be read. Note that
/// this is different from a received zero-length packet, which is valid and significant in
/// USB. A zero-length packet will return `Ok(0)`.
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The received packet is too long to
/// fit in `data`. This is generally an error in the class implementation.
pub fn read(&self, data: &mut [u8]) -> Result<usize> {
self.bus().read(self.address, data)
}
}
/// Type-safe endpoint address.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointAddress(u8);
impl From<u8> for EndpointAddress {
#[inline]
fn from(addr: u8) -> EndpointAddress {
EndpointAddress(addr)
}
}
impl From<EndpointAddress> for u8 {
#[inline]
fn from(addr: EndpointAddress) -> u8 {
addr.0
}
}
impl EndpointAddress {
const INBITS: u8 = UsbDirection::In as u8;
/// Constructs a new EndpointAddress with the given index and direction.
#[inline]
pub fn from_parts(index: usize, dir: UsbDirection) -> Self {
EndpointAddress(index as u8 | dir as u8)
}
/// Gets the direction part of the address.
#[inline]
pub fn direction(&self) -> UsbDirection {
if (self.0 & Self::INBITS) != 0 {
UsbDirection::In
} else {
UsbDirection::Out
}
}
/// Returns true if the direction is IN, otherwise false.
#[inline]
pub fn is_in(&self) -> bool {
(self.0 & Self::INBITS) != 0
}
/// Returns true if the direction is OUT, otherwise false.
#[inline]
pub fn is_out(&self) -> bool {
(self.0 & Self::INBITS) == 0
}
/// Gets the index part of the endpoint address.
#[inline]
pub fn index(&self) -> usize {
(self.0 & !Self::INBITS) as usize
}
}
+276
View File
@@ -0,0 +1,276 @@
//! Experimental device-side USB stack for embedded devices.
//!
//! ## Implementing a USB device
//!
//! A USB device consists of a [`UsbDevice`](device::UsbDevice) instance, one or more
//! [`UsbClass`](crate::class::UsbClass)es, and a platform-specific [`UsbBus`](bus::UsbBus)
//! implementation which together form a USB composite device.
//!
//! In the future USB device implementors will be able to use pre-existing peripheral driver crates
//! and USB class implementation crates. The necessary types for the basic USB composite device
//! implementation are available with:
//!
//! `use usb_device::prelude::*`.
//!
//! See the [`device`] module for a more complete example.
//!
//! ## USB classes
//!
//! For information on how to implement new USB classes, see the [`class`] module and the
//! [`TestClass`](test_class::TestClass) source code for an example of a custom USB device class
//! implementation. The necessary types for creating new classes are available with:
//!
//! `use usb_device::class_prelude::*`.
//!
//! ## USB peripheral drivers
//!
//! New peripheral driver crates can be created by implementing the [`UsbBus`](bus::UsbBus) trait.
//!
//! # Note about terminology
//!
//! This crate uses standard host-centric USB terminology for transfer directions. Therefore an OUT
//! transfer refers to a host-to-device transfer, and an IN transfer refers to a device-to-host
//! transfer. This is mainly a concern for implementing new USB peripheral drivers and USB classes,
//! and people doing that should be familiar with the USB standard.
#![no_std]
#![warn(missing_docs)]
#[macro_use]
mod macros;
/// A USB stack error.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UsbError {
/// An operation would block because the device is currently busy or there is no data available.
WouldBlock,
/// Parsing failed due to invalid input.
ParseError,
/// A buffer too short for the data to read was passed, or provided data cannot fit within
/// length constraints.
BufferOverflow,
/// Classes attempted to allocate more endpoints than the peripheral supports.
EndpointOverflow,
/// Classes attempted to allocate more packet buffer memory than the peripheral supports. This
/// can be caused by either a single class trying to allocate a packet buffer larger than the
/// peripheral supports per endpoint, or multiple allocated endpoints together using more memory
/// than the peripheral has available for the buffers.
EndpointMemoryOverflow,
/// The endpoint address is invalid or already used.
InvalidEndpoint,
/// Operation is not supported by device or configuration.
Unsupported,
/// Operation is not valid in the current state of the object.
InvalidState,
}
/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from
/// the perspective of the host, which is backward for devices, but the standard directions are used
/// for consistency.
///
/// The values of the enum also match the direction bit used in endpoint addresses and control
/// request types.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UsbDirection {
/// Host to device (OUT)
Out = 0x00,
/// Device to host (IN)
In = 0x80,
}
impl From<u8> for UsbDirection {
fn from(value: u8) -> Self {
unsafe { core::mem::transmute(value & 0x80) }
}
}
/// Result for USB operations.
pub type Result<T> = core::result::Result<T, UsbError>;
/// USB control transfers and the SETUP packet.
pub mod control;
/// For implementing peripheral drivers.
pub mod bus;
/// For implementing standard as well as vendor-specific USB classes.
///
/// To implement a new class, implement the [`UsbClass`](class::UsbClass) trait. The trait contains
/// numerous callbacks that you can use to respond to USB events. None of the methods are required,
/// and you only need to override the ones that your specific class needs to function. See the trait
/// documentation for more information on the callback methods.
///
/// Your class should *not* hold a direct reference to the [`UsbBus`](bus::UsbBus) object. Rather it
/// should take a temporary reference to the [`UsbBusAllocator`](bus::UsbBusAllocator) object
/// exposed by the bus in its constructor, and use that to allocate endpoints, as well as interface
/// and string handles. Using the [`Endpoint`](endpoint::Endpoint) handles which wrap a reference to
/// the `UsbBus` instance ensures that classes cannot inadvertently access an endpoint owned by
/// another class.
///
/// In addition to implementing the trait, add struct methods for the end-user to send and receive
/// data via your class. For example, a serial port class might have class-specific methods `read`
/// and `write` to read and write data.
pub mod class;
/// USB endpoints.
pub mod endpoint;
/// USB composite device.
///
/// The [UsbDevice](device::UsbDevice) type in this module is the core of this crate. It combines
/// multiple USB class implementations and the USB bus driver and dispatches bus state changes and
/// control messages between them.
///
/// To implement USB support for your own project, the required code is usually as follows:
///
/// ``` ignore
/// use usb_device::prelude::*;
/// use usb_serial; // example class crate (not included)
///
/// // Create the device-specific USB peripheral driver. The exact name and arguments are device
/// // specific, so check the documentation for your device driver crate.
/// let usb_bus = device_specific_usb::UsbBus::new(...);
///
/// // Create one or more USB class implementation. The name and arguments depend on the class,
/// // however most classes require the UsbAllocator as the first argument in order to allocate
/// // the required shared resources.
/// let mut serial = usb_serial::SerialPort::new(&usb_bus.allocator());
///
/// // Build the final [UsbDevice](device::UsbDevice) instance. The required arguments are a
/// // reference to the peripheral driver created earlier, as well as a USB vendor ID/product ID
/// // pair. Additional builder arguments can specify parameters such as device class code or
/// // product name. If using an existing class, remember to check the class crate documentation
/// // for correct values.
/// let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x5824, 0x27dd))
/// .strings(&[StringDescriptors::new(LangID::EN)
/// .product("Serial port")])
/// .expect("Failed to set strings")
/// .device_class(usb_serial::DEVICE_CLASS)
/// .build();
///
/// // At this point the USB peripheral is enabled and a connected host will attempt to enumerate
/// // it.
/// loop {
/// // Must be called more often than once every 10ms to handle events and stay USB compilant,
/// // or from a device-specific interrupt handler.
/// if (usb_dev.poll(&mut [&mut serial])) {
/// // Call class-specific methods here
/// serial.read(...);
/// }
/// }
/// ```
pub mod device;
/// Creating USB descriptors
pub mod descriptor;
pub use descriptor::lang_id::LangID;
/// Test USB class for testing USB driver implementations. Peripheral driver implementations should
/// include an example called "test_class" that creates a device with this class to enable the
/// driver to be tested with the test_class_host example in this crate.
pub mod test_class;
mod control_pipe;
mod device_builder;
/// Prelude for device implementors.
pub mod prelude {
pub use crate::device::{
StringDescriptors, UsbDevice, UsbDeviceBuilder, UsbDeviceState, UsbVidPid,
};
pub use crate::device_builder::BuilderError;
pub use crate::LangID;
pub use crate::UsbError;
}
/// Prelude for class implementors.
pub mod class_prelude {
pub use crate::bus::{InterfaceNumber, StringIndex, UsbBus, UsbBusAllocator};
pub use crate::class::{ControlIn, ControlOut, UsbClass};
pub use crate::control;
pub use crate::descriptor::{BosWriter, DescriptorWriter};
pub use crate::endpoint::{
EndpointAddress, EndpointIn, EndpointOut, EndpointType, IsochronousSynchronizationType,
IsochronousUsageType,
};
pub use crate::LangID;
pub use crate::UsbError;
}
fn _ensure_sync() {
use crate::bus::PollResult;
use crate::class_prelude::*;
struct DummyBus<'a> {
_a: &'a str,
}
impl UsbBus for DummyBus<'_> {
fn alloc_ep(
&mut self,
_ep_dir: UsbDirection,
_ep_addr: Option<EndpointAddress>,
_ep_type: EndpointType,
_max_packet_size: u16,
_interval: u8,
) -> Result<EndpointAddress> {
Err(UsbError::EndpointOverflow)
}
fn enable(&mut self) {}
fn reset(&self) {}
fn set_device_address(&self, _addr: u8) {}
fn write(&self, _ep_addr: EndpointAddress, _buf: &[u8]) -> Result<usize> {
Err(UsbError::InvalidEndpoint)
}
fn read(&self, _ep_addr: EndpointAddress, _buf: &mut [u8]) -> Result<usize> {
Err(UsbError::InvalidEndpoint)
}
fn set_stalled(&self, _ep_addr: EndpointAddress, _stalled: bool) {}
fn is_stalled(&self, _ep_addr: EndpointAddress) -> bool {
false
}
fn suspend(&self) {}
fn resume(&self) {}
fn poll(&self) -> PollResult {
PollResult::None
}
}
struct DummyClass<'a, B: UsbBus> {
_ep: crate::endpoint::EndpointIn<'a, B>,
}
impl<B: UsbBus> DummyClass<'_, B> {
fn _new(alloc: &UsbBusAllocator<B>) -> DummyClass<'_, B> {
DummyClass {
_ep: alloc.bulk(64),
}
}
}
impl<B: UsbBus> UsbClass<B> for DummyClass<'_, B> {}
fn ensure_sync<T: Sync + Send>() {}
ensure_sync::<DummyBus>();
ensure_sync::<crate::endpoint::EndpointIn<DummyBus>>();
ensure_sync::<crate::endpoint::EndpointOut<DummyBus>>();
ensure_sync::<DummyClass<'_, DummyBus>>();
}
+24
View File
@@ -0,0 +1,24 @@
#[cfg(all(feature = "log", not(feature = "defmt")))]
macro_rules! usb_log {
(trace, $($arg:expr),*) => { log::trace!($($arg),*) };
(debug, $($arg:expr),*) => { log::debug!($($arg),*) };
}
#[cfg(feature = "defmt")]
macro_rules! usb_log {
(trace, $($arg:expr),*) => { defmt::trace!($($arg),*) };
(debug, $($arg:expr),*) => { defmt::debug!($($arg),*) };
}
#[cfg(not(any(feature = "log", feature = "defmt")))]
macro_rules! usb_log {
($level:ident, $($arg:expr),*) => {{ $( let _ = $arg; )* }}
}
macro_rules! usb_trace {
($($arg:expr),*) => (usb_log!(trace, $($arg),*));
}
macro_rules! usb_debug {
($($arg:expr),*) => (usb_log!(debug, $($arg),*));
}
+337
View File
@@ -0,0 +1,337 @@
#![allow(missing_docs)]
use crate::class_prelude::*;
use crate::descriptor::lang_id::LangID;
use crate::device::{StringDescriptors, UsbDevice, UsbDeviceBuilder, UsbVidPid};
use crate::Result;
use core::cmp;
#[cfg(feature = "test-class-high-speed")]
mod sizes {
pub const BUFFER: usize = 2048;
pub const CONTROL_ENDPOINT: u8 = 64;
pub const BULK_ENDPOINT: u16 = 512;
pub const INTERRUPT_ENDPOINT: u16 = 1024;
}
#[cfg(not(feature = "test-class-high-speed"))]
mod sizes {
pub const BUFFER: usize = 256;
pub const CONTROL_ENDPOINT: u8 = 8;
pub const BULK_ENDPOINT: u16 = 64;
pub const INTERRUPT_ENDPOINT: u16 = 31;
}
/// Test USB class for testing USB driver implementations. Supports various endpoint types and
/// requests for testing USB peripheral drivers on actual hardware.
pub struct TestClass<'a, B: UsbBus> {
custom_string: StringIndex,
interface_string: StringIndex,
iface: InterfaceNumber,
ep_bulk_in: EndpointIn<'a, B>,
ep_bulk_out: EndpointOut<'a, B>,
ep_interrupt_in: EndpointIn<'a, B>,
ep_interrupt_out: EndpointOut<'a, B>,
ep_iso_in: EndpointIn<'a, B>,
control_buf: [u8; sizes::BUFFER],
bulk_buf: [u8; sizes::BUFFER],
interrupt_buf: [u8; sizes::BUFFER],
len: usize,
i: usize,
bench: bool,
expect_bulk_in_complete: bool,
expect_bulk_out: bool,
expect_interrupt_in_complete: bool,
expect_interrupt_out: bool,
}
pub const VID: u16 = 0x16c0;
pub const PID: u16 = 0x05dc;
pub const MANUFACTURER: &str = "TestClass Manufacturer";
pub const PRODUCT: &str = "virkkunen.net usb-device TestClass";
pub const SERIAL_NUMBER: &str = "TestClass Serial";
pub const CUSTOM_STRING: &str = "TestClass Custom String";
pub const INTERFACE_STRING: &str = "TestClass Interface";
pub const REQ_STORE_REQUEST: u8 = 1;
pub const REQ_READ_BUFFER: u8 = 2;
pub const REQ_WRITE_BUFFER: u8 = 3;
pub const REQ_SET_BENCH_ENABLED: u8 = 4;
pub const REQ_READ_LONG_DATA: u8 = 5;
pub const REQ_UNKNOWN: u8 = 42;
pub const LONG_DATA: &[u8] = &[0x17; 257];
impl<B: UsbBus> TestClass<'_, B> {
/// Creates a new TestClass.
pub fn new(alloc: &UsbBusAllocator<B>) -> TestClass<'_, B> {
TestClass {
custom_string: alloc.string(),
interface_string: alloc.string(),
iface: alloc.interface(),
ep_bulk_in: alloc.bulk(sizes::BULK_ENDPOINT),
ep_bulk_out: alloc.bulk(sizes::BULK_ENDPOINT),
ep_interrupt_in: alloc.interrupt(sizes::INTERRUPT_ENDPOINT, 1),
ep_interrupt_out: alloc.interrupt(sizes::INTERRUPT_ENDPOINT, 1),
ep_iso_in: alloc.isochronous(
IsochronousSynchronizationType::Asynchronous,
IsochronousUsageType::ImplicitFeedbackData,
500, // These last two args are arbitrary in this usage, they
1, // let the host know how much bandwidth to reserve.
),
control_buf: [0; sizes::BUFFER],
bulk_buf: [0; sizes::BUFFER],
interrupt_buf: [0; sizes::BUFFER],
len: 0,
i: 0,
bench: false,
expect_bulk_in_complete: false,
expect_bulk_out: false,
expect_interrupt_in_complete: false,
expect_interrupt_out: false,
}
}
/// Convenience method to create a UsbDevice that is configured correctly for TestClass.
pub fn make_device<'a>(&self, usb_bus: &'a UsbBusAllocator<B>) -> UsbDevice<'a, B> {
self.make_device_builder(usb_bus).build()
}
/// Convenience method to create a UsbDeviceBuilder that is configured correctly for TestClass.
///
/// The methods sets
///
/// - manufacturer
/// - product
/// - serial number
/// - max_packet_size_0
///
/// on the returned builder. If you change the manufacturer, product, or serial number fields,
/// the test host may misbehave.
pub fn make_device_builder<'a>(
&self,
usb_bus: &'a UsbBusAllocator<B>,
) -> UsbDeviceBuilder<'a, B> {
UsbDeviceBuilder::new(usb_bus, UsbVidPid(VID, PID))
.strings(&[StringDescriptors::default()
.manufacturer(MANUFACTURER)
.product(PRODUCT)
.serial_number(SERIAL_NUMBER)])
.unwrap()
.max_packet_size_0(sizes::CONTROL_ENDPOINT)
.unwrap()
}
/// Must be called after polling the UsbDevice.
pub fn poll(&mut self) {
if self.bench {
match self.ep_bulk_out.read(&mut self.bulk_buf) {
Ok(_) | Err(UsbError::WouldBlock) => {}
Err(err) => panic!("bulk bench read {:?}", err),
};
match self
.ep_bulk_in
.write(&self.bulk_buf[0..self.ep_bulk_in.max_packet_size() as usize])
{
Ok(_) | Err(UsbError::WouldBlock) => {}
Err(err) => panic!("bulk bench write {:?}", err),
};
return;
}
let temp_i = self.i;
match self.ep_bulk_out.read(&mut self.bulk_buf[temp_i..]) {
Ok(count) => {
if self.expect_bulk_out {
self.expect_bulk_out = false;
} else {
panic!("unexpectedly read data from bulk out endpoint");
}
self.i += count;
if count < self.ep_bulk_out.max_packet_size() as usize {
self.len = self.i;
self.i = 0;
self.write_bulk_in(count == 0);
}
}
Err(UsbError::WouldBlock) => {}
Err(err) => panic!("bulk read {:?}", err),
};
match self.ep_interrupt_out.read(&mut self.interrupt_buf) {
Ok(count) => {
if self.expect_interrupt_out {
self.expect_interrupt_out = false;
} else {
panic!("unexpectedly read data from interrupt out endpoint");
}
self.ep_interrupt_in
.write(&self.interrupt_buf[0..count])
.expect("interrupt write");
self.expect_interrupt_in_complete = true;
}
Err(UsbError::WouldBlock) => {}
Err(err) => panic!("interrupt read {:?}", err),
};
}
fn write_bulk_in(&mut self, write_empty: bool) {
let to_write = cmp::min(
self.len - self.i,
self.ep_bulk_in.max_packet_size() as usize,
);
if to_write == 0 && !write_empty {
self.len = 0;
self.i = 0;
return;
}
match self
.ep_bulk_in
.write(&self.bulk_buf[self.i..self.i + to_write])
{
Ok(count) => {
assert_eq!(count, to_write);
self.expect_bulk_in_complete = true;
self.i += count;
}
Err(UsbError::WouldBlock) => {}
Err(err) => panic!("bulk write {:?}", err),
};
}
}
impl<B: UsbBus> UsbClass<B> for TestClass<'_, B> {
fn reset(&mut self) {
self.len = 0;
self.i = 0;
self.bench = false;
self.expect_bulk_in_complete = false;
self.expect_bulk_out = false;
self.expect_interrupt_in_complete = false;
self.expect_interrupt_out = false;
}
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
writer.interface(self.iface, 0xff, 0x00, 0x00)?;
writer.endpoint(&self.ep_bulk_in)?;
writer.endpoint(&self.ep_bulk_out)?;
writer.endpoint(&self.ep_interrupt_in)?;
writer.endpoint(&self.ep_interrupt_out)?;
writer.interface_alt(self.iface, 1, 0xff, 0x01, 0x00, Some(self.interface_string))?;
writer.endpoint(&self.ep_iso_in)?;
Ok(())
}
fn get_string(&self, index: StringIndex, lang_id: LangID) -> Option<&str> {
if lang_id == LangID::EN_US {
if index == self.custom_string {
return Some(CUSTOM_STRING);
} else if index == self.interface_string {
return Some(INTERFACE_STRING);
}
}
None
}
fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
if self.bench {
return;
}
if addr == self.ep_bulk_in.address() {
if self.expect_bulk_in_complete {
self.expect_bulk_in_complete = false;
self.write_bulk_in(false);
} else {
panic!("unexpected endpoint_in_complete");
}
} else if addr == self.ep_interrupt_in.address() {
if self.expect_interrupt_in_complete {
self.expect_interrupt_in_complete = false;
} else {
panic!("unexpected endpoint_in_complete");
}
}
}
fn endpoint_out(&mut self, addr: EndpointAddress) {
if addr == self.ep_bulk_out.address() {
self.expect_bulk_out = true;
} else if addr == self.ep_interrupt_out.address() {
self.expect_interrupt_out = true;
}
}
fn control_in(&mut self, xfer: ControlIn<B>) {
let req = *xfer.request();
if !(req.request_type == control::RequestType::Vendor
&& req.recipient == control::Recipient::Device)
{
return;
}
match req.request {
REQ_READ_BUFFER if req.length as usize <= self.control_buf.len() => xfer
.accept_with(&self.control_buf[0..req.length as usize])
.expect("control_in REQ_READ_BUFFER failed"),
REQ_READ_LONG_DATA => xfer
.accept_with_static(LONG_DATA)
.expect("control_in REQ_READ_LONG_DATA failed"),
_ => xfer.reject().expect("control_in reject failed"),
}
}
fn control_out(&mut self, xfer: ControlOut<B>) {
let req = *xfer.request();
if !(req.request_type == control::RequestType::Vendor
&& req.recipient == control::Recipient::Device)
{
return;
}
match req.request {
REQ_STORE_REQUEST => {
self.control_buf[0] =
(req.direction as u8) | (req.request_type as u8) << 5 | (req.recipient as u8);
self.control_buf[1] = req.request;
self.control_buf[2..4].copy_from_slice(&req.value.to_le_bytes());
self.control_buf[4..6].copy_from_slice(&req.index.to_le_bytes());
self.control_buf[6..8].copy_from_slice(&req.length.to_le_bytes());
xfer.accept().expect("control_out REQ_STORE_REQUEST failed");
}
REQ_WRITE_BUFFER if xfer.data().len() <= self.control_buf.len() => {
assert_eq!(
xfer.data().len(),
req.length as usize,
"xfer data len == req.length"
);
self.control_buf[0..xfer.data().len()].copy_from_slice(xfer.data());
xfer.accept().expect("control_out REQ_WRITE_BUFFER failed");
}
REQ_SET_BENCH_ENABLED => {
self.bench = req.value != 0;
xfer.accept()
.expect("control_out REQ_SET_BENCH_ENABLED failed");
}
_ => xfer.reject().expect("control_out reject failed"),
}
}
}