wip: experimental async usb stack
This commit is contained in:
347
embassy-usb/src/builder.rs
Normal file
347
embassy-usb/src/builder.rs
Normal file
@ -0,0 +1,347 @@
|
||||
use super::descriptor::{BosWriter, DescriptorWriter};
|
||||
use super::driver::{Driver, EndpointAllocError};
|
||||
use super::types::*;
|
||||
use super::UsbDevice;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub struct Config<'a> {
|
||||
pub(crate) vendor_id: u16,
|
||||
pub(crate) product_id: u16,
|
||||
|
||||
/// 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)
|
||||
pub device_class: u8,
|
||||
|
||||
/// Device sub-class code. Depends on class.
|
||||
///
|
||||
/// Default: `0x00`
|
||||
pub device_sub_class: u8,
|
||||
|
||||
/// Device protocol code. Depends on class and sub-class.
|
||||
///
|
||||
/// Default: `0x00`
|
||||
pub device_protocol: u8,
|
||||
|
||||
/// Device release version in BCD.
|
||||
///
|
||||
/// Default: `0x0010` ("0.1")
|
||||
pub device_release: u16,
|
||||
|
||||
/// 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 max_packet_size_0: u8,
|
||||
|
||||
/// Manufacturer name string descriptor.
|
||||
///
|
||||
/// Default: (none)
|
||||
pub manufacturer: Option<&'a str>,
|
||||
|
||||
/// Product name string descriptor.
|
||||
///
|
||||
/// Default: (none)
|
||||
pub product: Option<&'a str>,
|
||||
|
||||
/// Serial number string descriptor.
|
||||
///
|
||||
/// Default: (none)
|
||||
pub serial_number: Option<&'a str>,
|
||||
|
||||
/// Whether the device supports remotely waking up the host is requested.
|
||||
///
|
||||
/// Default: `false`
|
||||
pub supports_remote_wakeup: bool,
|
||||
|
||||
/// Configures the device as a composite device with interface association descriptors.
|
||||
///
|
||||
/// If set to `true`, the following fields should have the given values:
|
||||
///
|
||||
/// - `device_class` = `0xEF`
|
||||
/// - `device_sub_class` = `0x02`
|
||||
/// - `device_protocol` = `0x01`
|
||||
pub composite_with_iads: bool,
|
||||
|
||||
/// Whether the device has its own 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`
|
||||
pub self_powered: bool,
|
||||
|
||||
/// 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
|
||||
/// Max: 500mA
|
||||
pub max_power: u16,
|
||||
}
|
||||
|
||||
impl<'a> Config<'a> {
|
||||
pub fn new(vid: u16, pid: u16) -> Self {
|
||||
Self {
|
||||
device_class: 0x00,
|
||||
device_sub_class: 0x00,
|
||||
device_protocol: 0x00,
|
||||
max_packet_size_0: 8,
|
||||
vendor_id: vid,
|
||||
product_id: pid,
|
||||
device_release: 0x0010,
|
||||
manufacturer: None,
|
||||
product: None,
|
||||
serial_number: None,
|
||||
self_powered: false,
|
||||
supports_remote_wakeup: false,
|
||||
composite_with_iads: false,
|
||||
max_power: 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to build new [`UsbDevice`]s.
|
||||
pub struct UsbDeviceBuilder<'d, D: Driver<'d>> {
|
||||
config: Config<'d>,
|
||||
|
||||
bus: D,
|
||||
next_interface_number: u8,
|
||||
next_string_index: u8,
|
||||
|
||||
// TODO make not pub?
|
||||
pub device_descriptor: DescriptorWriter<'d>,
|
||||
pub config_descriptor: DescriptorWriter<'d>,
|
||||
pub bos_descriptor: BosWriter<'d>,
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
|
||||
/// Creates a builder for constructing a new [`UsbDevice`].
|
||||
pub fn new(
|
||||
bus: D,
|
||||
config: Config<'d>,
|
||||
device_descriptor_buf: &'d mut [u8],
|
||||
config_descriptor_buf: &'d mut [u8],
|
||||
bos_descriptor_buf: &'d mut [u8],
|
||||
) -> Self {
|
||||
// Magic values specified in USB-IF ECN on IADs.
|
||||
if config.composite_with_iads
|
||||
&& (config.device_class != 0xEF
|
||||
|| config.device_sub_class != 0x02
|
||||
|| config.device_protocol != 0x01)
|
||||
{
|
||||
panic!("if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01");
|
||||
}
|
||||
|
||||
if config.max_power > 500 {
|
||||
panic!("The maximum allowed value for `max_power` is 500mA");
|
||||
}
|
||||
|
||||
match config.max_packet_size_0 {
|
||||
8 | 16 | 32 | 64 => {}
|
||||
_ => panic!("invalid max_packet_size_0, the allowed values are 8, 16, 32 or 64"),
|
||||
}
|
||||
|
||||
let mut device_descriptor = DescriptorWriter::new(device_descriptor_buf);
|
||||
let mut config_descriptor = DescriptorWriter::new(config_descriptor_buf);
|
||||
let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf));
|
||||
|
||||
device_descriptor.device(&config).unwrap();
|
||||
config_descriptor.configuration(&config).unwrap();
|
||||
bos_descriptor.bos().unwrap();
|
||||
|
||||
UsbDeviceBuilder {
|
||||
bus,
|
||||
config,
|
||||
next_interface_number: 0,
|
||||
next_string_index: 4,
|
||||
|
||||
device_descriptor,
|
||||
config_descriptor,
|
||||
bos_descriptor,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the [`UsbDevice`] instance with the configuration in this builder.
|
||||
pub fn build(mut self) -> UsbDevice<'d, D> {
|
||||
self.config_descriptor.end_configuration();
|
||||
self.bos_descriptor.end_bos();
|
||||
|
||||
UsbDevice::build(
|
||||
self.bus,
|
||||
self.config,
|
||||
self.device_descriptor.into_buf(),
|
||||
self.config_descriptor.into_buf(),
|
||||
self.bos_descriptor.writer.into_buf(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Allocates a new interface number.
|
||||
pub fn alloc_interface(&mut self) -> InterfaceNumber {
|
||||
let number = self.next_interface_number;
|
||||
self.next_interface_number += 1;
|
||||
|
||||
InterfaceNumber::new(number)
|
||||
}
|
||||
|
||||
/// Allocates a new string index.
|
||||
pub fn alloc_string(&mut self) -> StringIndex {
|
||||
let index = self.next_string_index;
|
||||
self.next_string_index += 1;
|
||||
|
||||
StringIndex::new(index)
|
||||
}
|
||||
|
||||
/// Allocates an in endpoint.
|
||||
///
|
||||
/// This directly delegates to [`Driver::alloc_endpoint_in`], so see that method for details. In most
|
||||
/// cases classes should call the endpoint type specific methods instead.
|
||||
pub fn alloc_endpoint_in(
|
||||
&mut self,
|
||||
ep_addr: Option<EndpointAddress>,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
) -> Result<D::EndpointIn, EndpointAllocError> {
|
||||
self.bus
|
||||
.alloc_endpoint_in(ep_addr, ep_type, max_packet_size, interval)
|
||||
}
|
||||
|
||||
/// Allocates an out endpoint.
|
||||
///
|
||||
/// This directly delegates to [`Driver::alloc_endpoint_out`], so see that method for details. In most
|
||||
/// cases classes should call the endpoint type specific methods instead.
|
||||
pub fn alloc_endpoint_out(
|
||||
&mut self,
|
||||
ep_addr: Option<EndpointAddress>,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
) -> Result<D::EndpointOut, EndpointAllocError> {
|
||||
self.bus
|
||||
.alloc_endpoint_out(ep_addr, ep_type, max_packet_size, interval)
|
||||
}
|
||||
|
||||
/// Allocates a control in 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 alloc_control_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
|
||||
self.alloc_endpoint_in(None, EndpointType::Control, max_packet_size, 0)
|
||||
.expect("alloc_ep failed")
|
||||
}
|
||||
|
||||
/// Allocates a control out 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 alloc_control_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
|
||||
self.alloc_endpoint_out(None, EndpointType::Control, max_packet_size, 0)
|
||||
.expect("alloc_ep failed")
|
||||
}
|
||||
|
||||
/// Allocates a bulk in 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 alloc_bulk_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
|
||||
self.alloc_endpoint_in(None, EndpointType::Bulk, max_packet_size, 0)
|
||||
.expect("alloc_ep failed")
|
||||
}
|
||||
|
||||
/// Allocates a bulk out 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 alloc_bulk_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
|
||||
self.alloc_endpoint_out(None, EndpointType::Bulk, max_packet_size, 0)
|
||||
.expect("alloc_ep failed")
|
||||
}
|
||||
|
||||
/// Allocates a bulk in endpoint.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
|
||||
/// feasibly recoverable.
|
||||
#[inline]
|
||||
pub fn alloc_interrupt_endpoint_in(
|
||||
&mut self,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
) -> D::EndpointIn {
|
||||
self.alloc_endpoint_in(None, EndpointType::Interrupt, max_packet_size, interval)
|
||||
.expect("alloc_ep failed")
|
||||
}
|
||||
|
||||
/// Allocates a bulk in endpoint.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if endpoint allocation fails, because running out of endpoints or memory is not
|
||||
/// feasibly recoverable.
|
||||
#[inline]
|
||||
pub fn alloc_interrupt_endpoint_out(
|
||||
&mut self,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
) -> D::EndpointOut {
|
||||
self.alloc_endpoint_out(None, EndpointType::Interrupt, max_packet_size, interval)
|
||||
.expect("alloc_ep failed")
|
||||
}
|
||||
}
|
134
embassy-usb/src/control.rs
Normal file
134
embassy-usb/src/control.rs
Normal file
@ -0,0 +1,134 @@
|
||||
use core::mem;
|
||||
|
||||
use super::types::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ParseError {
|
||||
InvalidLength,
|
||||
}
|
||||
|
||||
/// 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, ParseError> {
|
||||
if buf.len() != 8 {
|
||||
return Err(ParseError::InvalidLength);
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
387
embassy-usb/src/descriptor.rs
Normal file
387
embassy-usb/src/descriptor.rs
Normal file
@ -0,0 +1,387 @@
|
||||
use super::builder::Config;
|
||||
use super::{types::*, CONFIGURATION_VALUE, DEFAULT_ALTERNATE_SETTING};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
BufferFull,
|
||||
InvalidState,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// English (US)
|
||||
///
|
||||
/// Recommended for use as the first language ID for compatibility.
|
||||
pub const ENGLISH_US: u16 = 0x0409;
|
||||
}
|
||||
|
||||
/// 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<'a> DescriptorWriter<'a> {
|
||||
pub(crate) fn new(buf: &'a mut [u8]) -> Self {
|
||||
DescriptorWriter {
|
||||
buf,
|
||||
position: 0,
|
||||
num_interfaces_mark: None,
|
||||
num_endpoints_mark: None,
|
||||
write_iads: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_buf(self) -> &'a mut [u8] {
|
||||
&mut self.buf[..self.position]
|
||||
}
|
||||
|
||||
/// 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<(), Error> {
|
||||
let length = descriptor.len();
|
||||
|
||||
if (self.position + 2 + length) > self.buf.len() || (length + 2) > 255 {
|
||||
return Err(Error::BufferFull);
|
||||
}
|
||||
|
||||
self.buf[self.position] = (length + 2) as u8;
|
||||
self.buf[self.position + 1] = descriptor_type;
|
||||
|
||||
let start = self.position + 2;
|
||||
|
||||
self.buf[start..start + length].copy_from_slice(descriptor);
|
||||
|
||||
self.position = start + length;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn device(&mut self, config: &Config) -> Result<(), Error> {
|
||||
self.write(
|
||||
descriptor_type::DEVICE,
|
||||
&[
|
||||
0x10,
|
||||
0x02, // bcdUSB 2.1
|
||||
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.manufacturer.map_or(0, |_| 1), // iManufacturer
|
||||
config.product.map_or(0, |_| 2), // iProduct
|
||||
config.serial_number.map_or(0, |_| 3), // iSerialNumber
|
||||
1, // bNumConfigurations
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn configuration(&mut self, config: &Config) -> Result<(), Error> {
|
||||
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
|
||||
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 / 2) as u8, // 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
|
||||
/// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::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.
|
||||
pub fn iad(
|
||||
&mut self,
|
||||
first_interface: InterfaceNumber,
|
||||
interface_count: u8,
|
||||
function_class: u8,
|
||||
function_sub_class: u8,
|
||||
function_protocol: u8,
|
||||
) -> Result<(), Error> {
|
||||
if !self.write_iads {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.write(
|
||||
descriptor_type::IAD,
|
||||
&[
|
||||
first_interface.into(), // bFirstInterface
|
||||
interface_count, // bInterfaceCount
|
||||
function_class,
|
||||
function_sub_class,
|
||||
function_protocol,
|
||||
0,
|
||||
],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a interface descriptor.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `number` - Interface number previously allocated with
|
||||
/// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::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<(), Error> {
|
||||
self.interface_alt(
|
||||
number,
|
||||
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
|
||||
/// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::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<(), Error> {
|
||||
if alternate_setting == DEFAULT_ALTERNATE_SETTING {
|
||||
match self.num_interfaces_mark {
|
||||
Some(mark) => self.buf[mark] += 1,
|
||||
None => return Err(Error::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
|
||||
/// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder).
|
||||
pub fn endpoint(&mut self, endpoint: &EndpointInfo) -> Result<(), Error> {
|
||||
match self.num_endpoints_mark {
|
||||
Some(mark) => self.buf[mark] += 1,
|
||||
None => return Err(Error::InvalidState),
|
||||
};
|
||||
|
||||
self.write(
|
||||
descriptor_type::ENDPOINT,
|
||||
&[
|
||||
endpoint.addr.into(), // bEndpointAddress
|
||||
endpoint.ep_type as u8, // bmAttributes
|
||||
endpoint.max_packet_size as u8,
|
||||
(endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
|
||||
endpoint.interval, // bInterval
|
||||
],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a string descriptor.
|
||||
pub(crate) fn string(&mut self, string: &str) -> Result<(), Error> {
|
||||
let mut pos = self.position;
|
||||
|
||||
if pos + 2 > self.buf.len() {
|
||||
return Err(Error::BufferFull);
|
||||
}
|
||||
|
||||
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(Error::BufferFull);
|
||||
}
|
||||
|
||||
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<'a> {
|
||||
pub(crate) writer: DescriptorWriter<'a>,
|
||||
num_caps_mark: Option<usize>,
|
||||
}
|
||||
|
||||
impl<'a> BosWriter<'a> {
|
||||
pub(crate) fn new(writer: DescriptorWriter<'a>) -> Self {
|
||||
Self {
|
||||
writer: writer,
|
||||
num_caps_mark: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn bos(&mut self) -> Result<(), Error> {
|
||||
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<(), Error> {
|
||||
match self.num_caps_mark {
|
||||
Some(mark) => self.writer.buf[mark] += 1,
|
||||
None => return Err(Error::InvalidState),
|
||||
}
|
||||
|
||||
let mut start = self.writer.position;
|
||||
let blen = data.len();
|
||||
|
||||
if (start + blen + 3) > self.writer.buf.len() || (blen + 3) > 255 {
|
||||
return Err(Error::BufferFull);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
160
embassy-usb/src/driver.rs
Normal file
160
embassy-usb/src/driver.rs
Normal file
@ -0,0 +1,160 @@
|
||||
use core::future::Future;
|
||||
|
||||
use super::types::*;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointAllocError;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
||||
/// Operation is unsupported by the driver.
|
||||
pub struct Unsupported;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
||||
/// Errors returned by [`EndpointIn::write`]
|
||||
pub enum WriteError {
|
||||
/// 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.
|
||||
BufferOverflow,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
||||
/// Errors returned by [`EndpointOut::read`]
|
||||
pub enum ReadError {
|
||||
/// 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.
|
||||
BufferOverflow,
|
||||
}
|
||||
|
||||
/// Driver for a specific USB peripheral. Implement this to add support for a new hardware
|
||||
/// platform.
|
||||
pub trait Driver<'a> {
|
||||
type EndpointOut: EndpointOut + 'a;
|
||||
type EndpointIn: EndpointIn + 'a;
|
||||
type Bus: Bus + 'a;
|
||||
|
||||
/// 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_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.
|
||||
fn alloc_endpoint_out(
|
||||
&mut self,
|
||||
ep_addr: Option<EndpointAddress>,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
) -> Result<Self::EndpointOut, EndpointAllocError>;
|
||||
|
||||
fn alloc_endpoint_in(
|
||||
&mut self,
|
||||
ep_addr: Option<EndpointAddress>,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
) -> Result<Self::EndpointIn, EndpointAllocError>;
|
||||
|
||||
/// 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(self) -> Self::Bus;
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
pub trait Bus {
|
||||
/// 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(&mut self);
|
||||
|
||||
/// Sets the device USB address to `addr`.
|
||||
fn set_device_address(&mut self, addr: u8);
|
||||
|
||||
/// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
|
||||
/// should be prepared to receive data again. Only used during control transfers.
|
||||
fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool);
|
||||
|
||||
/// Gets whether the STALL condition is set for an endpoint. Only used during control transfers.
|
||||
fn is_stalled(&mut 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(&mut self);
|
||||
|
||||
/// Resumes from suspend mode. This may only be called after the peripheral has been previously
|
||||
/// suspended.
|
||||
fn resume(&mut self);
|
||||
|
||||
/// 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(&mut self) -> Result<(), Unsupported> {
|
||||
Err(Unsupported)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Endpoint {
|
||||
/// Get the endpoint address
|
||||
fn info(&self) -> &EndpointInfo;
|
||||
|
||||
/// 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, stalled: bool);
|
||||
|
||||
/// Gets whether the STALL condition is set for an endpoint.
|
||||
fn is_stalled(&self) -> bool;
|
||||
|
||||
// TODO enable/disable?
|
||||
}
|
||||
|
||||
pub trait EndpointOut: Endpoint {
|
||||
type ReadFuture<'a>: Future<Output = Result<usize, ReadError>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Reads a single packet of data from the 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.
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>;
|
||||
}
|
||||
|
||||
pub trait EndpointIn: Endpoint {
|
||||
type WriteFuture<'a>: Future<Output = Result<(), WriteError>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Writes a single packet of data to the endpoint.
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>;
|
||||
}
|
225
embassy-usb/src/fmt.rs
Normal file
225
embassy-usb/src/fmt.rs
Normal file
@ -0,0 +1,225 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||
|
||||
macro_rules! assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! todo {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::todo!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::todo!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! panic {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::panic!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::panic!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! trace {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::trace!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::debug!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! info {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::info!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! warn {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::warn!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::error!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unwrap {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unwrap!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unwrap {
|
||||
($arg:expr) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
342
embassy-usb/src/lib.rs
Normal file
342
embassy-usb/src/lib.rs
Normal file
@ -0,0 +1,342 @@
|
||||
#![no_std]
|
||||
#![feature(generic_associated_types)]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
mod builder;
|
||||
mod control;
|
||||
pub mod descriptor;
|
||||
pub mod driver;
|
||||
pub mod types;
|
||||
|
||||
use self::control::*;
|
||||
use self::descriptor::*;
|
||||
use self::driver::*;
|
||||
use self::types::*;
|
||||
|
||||
pub use self::builder::Config;
|
||||
pub use self::builder::UsbDeviceBuilder;
|
||||
|
||||
/// 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)]
|
||||
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,
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
pub struct UsbDevice<'d, D: Driver<'d>> {
|
||||
driver: D::Bus,
|
||||
control_in: D::EndpointIn,
|
||||
control_out: D::EndpointOut,
|
||||
|
||||
config: Config<'d>,
|
||||
device_descriptor: &'d [u8],
|
||||
config_descriptor: &'d [u8],
|
||||
bos_descriptor: &'d [u8],
|
||||
|
||||
device_state: UsbDeviceState,
|
||||
remote_wakeup_enabled: bool,
|
||||
self_powered: bool,
|
||||
pending_address: u8,
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
||||
pub(crate) fn build(
|
||||
mut driver: D,
|
||||
config: Config<'d>,
|
||||
device_descriptor: &'d [u8],
|
||||
config_descriptor: &'d [u8],
|
||||
bos_descriptor: &'d [u8],
|
||||
) -> Self {
|
||||
let control_out = driver
|
||||
.alloc_endpoint_out(
|
||||
Some(0x00.into()),
|
||||
EndpointType::Control,
|
||||
config.max_packet_size_0 as u16,
|
||||
0,
|
||||
)
|
||||
.expect("failed to alloc control endpoint");
|
||||
|
||||
let control_in = driver
|
||||
.alloc_endpoint_in(
|
||||
Some(0x80.into()),
|
||||
EndpointType::Control,
|
||||
config.max_packet_size_0 as u16,
|
||||
0,
|
||||
)
|
||||
.expect("failed to alloc control endpoint");
|
||||
|
||||
// Enable the USB bus.
|
||||
// This prevent further allocation by consuming the driver.
|
||||
let driver = driver.enable();
|
||||
|
||||
Self {
|
||||
driver,
|
||||
config,
|
||||
control_in,
|
||||
control_out,
|
||||
device_descriptor,
|
||||
config_descriptor,
|
||||
bos_descriptor,
|
||||
device_state: UsbDeviceState::Default,
|
||||
remote_wakeup_enabled: false,
|
||||
self_powered: false,
|
||||
pending_address: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
loop {
|
||||
let mut buf = [0; 8];
|
||||
let n = self.control_out.read(&mut buf).await.unwrap();
|
||||
assert_eq!(n, 8);
|
||||
let req = Request::parse(&buf).unwrap();
|
||||
info!("setup request: {:x}", req);
|
||||
|
||||
// Now that we have properly parsed the setup packet, ensure the end-point is no longer in
|
||||
// a stalled state.
|
||||
self.control_out.set_stalled(false);
|
||||
|
||||
match req.direction {
|
||||
UsbDirection::In => self.handle_control_in(req).await,
|
||||
UsbDirection::Out => self.handle_control_out(req).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn write_chunked(&mut self, data: &[u8]) -> Result<(), driver::WriteError> {
|
||||
for c in data.chunks(8) {
|
||||
self.control_in.write(c).await?;
|
||||
}
|
||||
if data.len() % 8 == 0 {
|
||||
self.control_in.write(&[]).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn control_out_accept(&mut self, req: Request) {
|
||||
info!("control out accept");
|
||||
// status phase
|
||||
// todo: cleanup
|
||||
self.control_out.read(&mut []).await.unwrap();
|
||||
}
|
||||
|
||||
async fn control_in_accept(&mut self, req: Request, data: &[u8]) {
|
||||
info!("control accept {:x}", data);
|
||||
|
||||
let len = data.len().min(req.length as _);
|
||||
if let Err(e) = self.write_chunked(&data[..len]).await {
|
||||
info!("write_chunked failed: {:?}", e);
|
||||
}
|
||||
|
||||
// status phase
|
||||
// todo: cleanup
|
||||
self.control_out.read(&mut []).await.unwrap();
|
||||
}
|
||||
|
||||
async fn control_in_accept_writer(
|
||||
&mut self,
|
||||
req: Request,
|
||||
f: impl FnOnce(&mut DescriptorWriter),
|
||||
) {
|
||||
let mut buf = [0; 256];
|
||||
let mut w = DescriptorWriter::new(&mut buf);
|
||||
f(&mut w);
|
||||
let pos = w.position();
|
||||
self.control_in_accept(req, &buf[..pos]).await;
|
||||
}
|
||||
|
||||
fn control_reject(&mut self, req: Request) {
|
||||
self.control_out.set_stalled(true);
|
||||
}
|
||||
|
||||
async fn handle_control_out(&mut self, req: Request) {
|
||||
// TODO actually read the data if there's an OUT data phase.
|
||||
|
||||
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.request_type {
|
||||
RequestType::Standard => match (req.recipient, req.request, req.value) {
|
||||
(
|
||||
Recipient::Device,
|
||||
Request::CLEAR_FEATURE,
|
||||
Request::FEATURE_DEVICE_REMOTE_WAKEUP,
|
||||
) => {
|
||||
self.remote_wakeup_enabled = false;
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
|
||||
(Recipient::Endpoint, Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
|
||||
//self.bus.set_stalled(((req.index as u8) & 0x8f).into(), false);
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
|
||||
(
|
||||
Recipient::Device,
|
||||
Request::SET_FEATURE,
|
||||
Request::FEATURE_DEVICE_REMOTE_WAKEUP,
|
||||
) => {
|
||||
self.remote_wakeup_enabled = true;
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
|
||||
(Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
|
||||
//self.bus.set_stalled(((req.index as u8) & 0x8f).into(), true);
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
|
||||
(Recipient::Device, Request::SET_ADDRESS, 1..=127) => {
|
||||
self.pending_address = req.value as u8;
|
||||
|
||||
// on NRF the hardware auto-handles SET_ADDRESS.
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
|
||||
(Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => {
|
||||
self.device_state = UsbDeviceState::Configured;
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
|
||||
(Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => {
|
||||
match self.device_state {
|
||||
UsbDeviceState::Default => {
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
_ => {
|
||||
self.device_state = UsbDeviceState::Addressed;
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(Recipient::Interface, Request::SET_INTERFACE, DEFAULT_ALTERNATE_SETTING_U16) => {
|
||||
// TODO: do something when alternate settings are implemented
|
||||
self.control_out_accept(req).await;
|
||||
}
|
||||
|
||||
_ => self.control_reject(req),
|
||||
},
|
||||
_ => self.control_reject(req),
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_control_in(&mut self, req: Request) {
|
||||
match req.request_type {
|
||||
RequestType::Standard => match (req.recipient, req.request) {
|
||||
(Recipient::Device, Request::GET_STATUS) => {
|
||||
let mut status: u16 = 0x0000;
|
||||
if self.self_powered {
|
||||
status |= 0x0001;
|
||||
}
|
||||
if self.remote_wakeup_enabled {
|
||||
status |= 0x0002;
|
||||
}
|
||||
self.control_in_accept(req, &status.to_le_bytes()).await;
|
||||
}
|
||||
|
||||
(Recipient::Interface, Request::GET_STATUS) => {
|
||||
let status: u16 = 0x0000;
|
||||
self.control_in_accept(req, &status.to_le_bytes()).await;
|
||||
}
|
||||
|
||||
(Recipient::Endpoint, Request::GET_STATUS) => {
|
||||
let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into();
|
||||
let mut status: u16 = 0x0000;
|
||||
if self.driver.is_stalled(ep_addr) {
|
||||
status |= 0x0001;
|
||||
}
|
||||
self.control_in_accept(req, &status.to_le_bytes()).await;
|
||||
}
|
||||
|
||||
(Recipient::Device, Request::GET_DESCRIPTOR) => {
|
||||
self.handle_get_descriptor(req).await;
|
||||
}
|
||||
|
||||
(Recipient::Device, Request::GET_CONFIGURATION) => {
|
||||
let status = match self.device_state {
|
||||
UsbDeviceState::Configured => CONFIGURATION_VALUE,
|
||||
_ => CONFIGURATION_NONE,
|
||||
};
|
||||
self.control_in_accept(req, &status.to_le_bytes()).await;
|
||||
}
|
||||
|
||||
(Recipient::Interface, Request::GET_INTERFACE) => {
|
||||
// TODO: change when alternate settings are implemented
|
||||
let status = DEFAULT_ALTERNATE_SETTING;
|
||||
self.control_in_accept(req, &status.to_le_bytes()).await;
|
||||
}
|
||||
_ => self.control_reject(req),
|
||||
},
|
||||
_ => self.control_reject(req),
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_get_descriptor(&mut self, req: Request) {
|
||||
let (dtype, index) = req.descriptor_type_index();
|
||||
let config = self.config.clone();
|
||||
|
||||
match dtype {
|
||||
descriptor_type::BOS => self.control_in_accept(req, self.bos_descriptor).await,
|
||||
descriptor_type::DEVICE => self.control_in_accept(req, self.device_descriptor).await,
|
||||
descriptor_type::CONFIGURATION => {
|
||||
self.control_in_accept(req, self.config_descriptor).await
|
||||
}
|
||||
descriptor_type::STRING => {
|
||||
if index == 0 {
|
||||
self.control_in_accept_writer(req, |w| {
|
||||
w.write(descriptor_type::STRING, &lang_id::ENGLISH_US.to_le_bytes())
|
||||
.unwrap();
|
||||
})
|
||||
.await
|
||||
} else {
|
||||
let s = match index {
|
||||
1 => self.config.manufacturer,
|
||||
2 => self.config.product,
|
||||
3 => self.config.serial_number,
|
||||
_ => {
|
||||
let index = StringIndex::new(index);
|
||||
let lang_id = req.index;
|
||||
None
|
||||
//classes
|
||||
// .iter()
|
||||
// .filter_map(|cls| cls.get_string(index, lang_id))
|
||||
// .nth(0)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(s) = s {
|
||||
self.control_in_accept_writer(req, |w| w.string(s).unwrap())
|
||||
.await;
|
||||
} else {
|
||||
self.control_reject(req)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => self.control_reject(req),
|
||||
}
|
||||
}
|
||||
}
|
138
embassy-usb/src/types.rs
Normal file
138
embassy-usb/src/types.rs
Normal file
@ -0,0 +1,138 @@
|
||||
/// 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) }
|
||||
}
|
||||
}
|
||||
|
||||
/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the
|
||||
/// transfer bmAttributes transfer type bits.
|
||||
#[repr(u8)]
|
||||
#[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 = 0b00,
|
||||
/// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet.
|
||||
Isochronous = 0b01,
|
||||
/// Bulk endpoint. Used for large amounts of best-effort reliable data.
|
||||
Bulk = 0b10,
|
||||
/// Interrupt endpoint. Used for small amounts of time-critical reliable data.
|
||||
Interrupt = 0b11,
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct EndpointInfo {
|
||||
pub addr: EndpointAddress,
|
||||
pub ep_type: EndpointType,
|
||||
pub max_packet_size: u16,
|
||||
pub interval: u8,
|
||||
}
|
||||
|
||||
/// 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(u8);
|
||||
|
||||
impl InterfaceNumber {
|
||||
pub(crate) fn new(index: u8) -> InterfaceNumber {
|
||||
InterfaceNumber(index)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user