init project

This commit is contained in:
2025-04-24 12:07:40 +02:00
commit 788d9bd6ea
305 changed files with 61443 additions and 0 deletions

36
embassy-usb/CHANGELOG.md Normal file
View File

@@ -0,0 +1,36 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## 0.4.0 - 2025-01-15
- Change config defaults to to composite with IADs. This ensures embassy-usb Just Works in more cases when using classes with multiple interfaces, or multiple classes. (breaking change)
- `composite_with_iads` = `true`
- `device_class` = `0xEF`
- `device_sub_class` = `0x02`
- `device_protocol` = `0x01`
- Add support for USB Audio Class 1.
- Add support for isochronous endpoints.
- Add support for setting the USB version number.
- Add support for device qualifier descriptors.
- Allow `bos_descriptor_buf` to be a zero length if BOS descriptors aren't used.
## 0.3.0 - 2024-08-05
- bump usbd-hid from 0.7.0 to 0.8.1
- Add collapse_debuginfo to fmt.rs macros.
- update embassy-sync dependency
## 0.2.0 - 2024-05-20
- [#2862](https://github.com/embassy-rs/embassy/pull/2862) WebUSB implementation by @chmanie
- Removed dynamically sized `device_descriptor` fields
## 0.1.0 - 2024-01-11
- Initial Release

60
embassy-usb/Cargo.toml Normal file
View File

@@ -0,0 +1,60 @@
[package]
name = "embassy-usb"
version = "0.4.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Async USB device stack for embedded devices in Rust."
keywords = ["embedded", "async", "usb", "hal", "embedded-hal"]
categories = ["embedded", "hardware-support", "no-std", "asynchronous"]
repository = "https://github.com/embassy-rs/embassy"
documentation = "https://docs.embassy.dev/embassy-usb"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/src/"
features = ["defmt", "usbd-hid"]
target = "thumbv7em-none-eabi"
[package.metadata.docs.rs]
features = ["defmt", "usbd-hid"]
[features]
defmt = ["dep:defmt", "embassy-usb-driver/defmt"]
usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"]
default = ["usbd-hid"]
# BEGIN AUTOGENERATED CONFIG FEATURES
# Generated by gen_config.py. DO NOT EDIT.
max-interface-count-1 = []
max-interface-count-2 = []
max-interface-count-3 = []
max-interface-count-4 = [] # Default
max-interface-count-5 = []
max-interface-count-6 = []
max-interface-count-7 = []
max-interface-count-8 = []
max-handler-count-1 = []
max-handler-count-2 = []
max-handler-count-3 = []
max-handler-count-4 = [] # Default
max-handler-count-5 = []
max-handler-count-6 = []
max-handler-count-7 = []
max-handler-count-8 = []
# END AUTOGENERATED CONFIG FEATURES
[dependencies]
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
heapless = "0.8"
# for HID
usbd-hid = { version = "0.8.1", optional = true }
ssmarshal = { version = "1.0", default-features = false, optional = true }

50
embassy-usb/README.md Normal file
View File

@@ -0,0 +1,50 @@
# embassy-usb
Async USB device stack for embedded devices in Rust.
## Features
- Native async.
- Fully lock-free: endpoints are separate objects that can be used independently without needing a central mutex. If the driver supports it, they can even be used from different priority levels.
- Suspend/resume, remote wakeup.
- USB composite devices.
- Ergonomic descriptor builder.
- Ready-to-use implementations for a few USB classes (note you can still implement any class yourself outside the crate).
- Serial ports (CDC ACM)
- Ethernet (CDC NCM)
- Human Interface Devices (HID)
- MIDI
## Adding support for new hardware
To add `embassy-usb` support for new hardware (i.e. a new MCU chip), you have to write a driver that implements
the [`embassy-usb-driver`](https://crates.io/crates/embassy-usb-driver) traits.
Driver crates should depend only on `embassy-usb-driver`, not on the main `embassy-usb` crate.
This allows existing drivers to continue working for newer `embassy-usb` major versions, without needing an update, if the driver
trait has not had breaking changes.
## Configuration
`embassy-usb` has some configuration settings that are set at compile time, affecting sizes
and counts of buffers.
They can be set in two ways:
- Via Cargo features: enable a feature like `<name>-<value>`. `name` must be in lowercase and
use dashes instead of underscores. For example. `max-interface-count-3`. Only a selection of values
is available, check `Cargo.toml` for the list.
- Via environment variables at build time: set the variable named `EMBASSY_USB_<value>`. For example
`EMBASSY_USB_MAX_INTERFACE_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.
Any value can be set, unlike with Cargo features.
Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting
with different values, compilation fails.
### `MAX_INTERFACE_COUNT`
Max amount of interfaces that can be created in one device. Default: 4.
## Interoperability
This crate can run on any executor.

96
embassy-usb/build.rs Normal file
View File

@@ -0,0 +1,96 @@
use std::collections::HashMap;
use std::fmt::Write;
use std::path::PathBuf;
use std::{env, fs};
static CONFIGS: &[(&str, usize)] = &[
// BEGIN AUTOGENERATED CONFIG FEATURES
// Generated by gen_config.py. DO NOT EDIT.
("MAX_INTERFACE_COUNT", 4),
("MAX_HANDLER_COUNT", 4),
// END AUTOGENERATED CONFIG FEATURES
];
struct ConfigState {
value: usize,
seen_feature: bool,
seen_env: bool,
}
fn main() {
let crate_name = env::var("CARGO_PKG_NAME")
.unwrap()
.to_ascii_uppercase()
.replace('-', "_");
// only rebuild if build.rs changed. Otherwise Cargo will rebuild if any
// other file changed.
println!("cargo:rerun-if-changed=build.rs");
// Rebuild if config envvar changed.
for (name, _) in CONFIGS {
println!("cargo:rerun-if-env-changed={crate_name}_{name}");
}
let mut configs = HashMap::new();
for (name, default) in CONFIGS {
configs.insert(
*name,
ConfigState {
value: *default,
seen_env: false,
seen_feature: false,
},
);
}
let prefix = format!("{crate_name}_");
for (var, value) in env::vars() {
if let Some(name) = var.strip_prefix(&prefix) {
let Some(cfg) = configs.get_mut(name) else {
panic!("Unknown env var {name}")
};
let Ok(value) = value.parse::<usize>() else {
panic!("Invalid value for env var {name}: {value}")
};
cfg.value = value;
cfg.seen_env = true;
}
if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") {
if let Some(i) = feature.rfind('_') {
let name = &feature[..i];
let value = &feature[i + 1..];
if let Some(cfg) = configs.get_mut(name) {
let Ok(value) = value.parse::<usize>() else {
panic!("Invalid value for feature {name}: {value}")
};
// envvars take priority.
if !cfg.seen_env {
assert!(
!cfg.seen_feature,
"multiple values set for feature {}: {} and {}",
name, cfg.value, value
);
cfg.value = value;
cfg.seen_feature = true;
}
}
}
}
}
let mut data = String::new();
for (name, cfg) in &configs {
writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap();
}
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let out_file = out_dir.join("config.rs").to_string_lossy().to_string();
fs::write(out_file, data).unwrap();
}

74
embassy-usb/gen_config.py Normal file
View File

@@ -0,0 +1,74 @@
import os
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
features = []
def feature(name, default, min, max, pow2=None):
vals = set()
val = min
while val <= max:
vals.add(val)
if pow2 == True or (isinstance(pow2, int) and val >= pow2):
val *= 2
else:
val += 1
vals.add(default)
features.append(
{
"name": name,
"default": default,
"vals": sorted(list(vals)),
}
)
feature("max_interface_count", default=4, min=1, max=8)
feature("max_handler_count", default=4, min=1, max=8)
# ========= Update Cargo.toml
things = ""
for f in features:
name = f["name"].replace("_", "-")
for val in f["vals"]:
things += f"{name}-{val} = []"
if val == f["default"]:
things += " # Default"
things += "\n"
things += "\n"
SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n"
SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n"
HELP = "# Generated by gen_config.py. DO NOT EDIT.\n"
with open("Cargo.toml", "r") as f:
data = f.read()
before, data = data.split(SEPARATOR_START, maxsplit=1)
_, after = data.split(SEPARATOR_END, maxsplit=1)
data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after
with open("Cargo.toml", "w") as f:
f.write(data)
# ========= Update build.rs
things = ""
for f in features:
name = f["name"].upper()
things += f' ("{name}", {f["default"]}),\n'
SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n"
SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n"
HELP = " // Generated by gen_config.py. DO NOT EDIT.\n"
with open("build.rs", "r") as f:
data = f.read()
before, data = data.split(SEPARATOR_START, maxsplit=1)
_, after = data.split(SEPARATOR_END, maxsplit=1)
data = before + SEPARATOR_START + HELP + \
things + " " + SEPARATOR_END + after
with open("build.rs", "w") as f:
f.write(data)

623
embassy-usb/src/builder.rs Normal file
View File

@@ -0,0 +1,623 @@
use heapless::Vec;
use crate::config::MAX_HANDLER_COUNT;
use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType};
use crate::driver::{Driver, Endpoint, EndpointInfo, EndpointType};
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
use crate::types::{InterfaceNumber, StringIndex};
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
/// Allows Configuring the Bcd USB version below 2.1
pub enum UsbVersion {
/// Usb version 2.0
Two = 0x0200,
/// Usb version 2.1
TwoOne = 0x0210,
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
/// Configuration used when creating [`UsbDevice`].
pub struct Config<'a> {
pub(crate) vendor_id: u16,
pub(crate) product_id: u16,
/// Device BCD USB version.
///
/// Default: `0x0210` ("2.1")
pub bcd_usb: UsbVersion,
/// Device class code assigned by USB.org. Set to `0xff` for vendor-specific
/// devices that do not conform to any class.
///
/// Default: `0xEF`
/// See also: `composite_with_iads`
pub device_class: u8,
/// Device sub-class code. Depends on class.
///
/// Default: `0x02`
/// See also: `composite_with_iads`
pub device_sub_class: u8,
/// Device protocol code. Depends on class and sub-class.
///
/// Default: `0x01`
/// See also: `composite_with_iads`
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 depend on the speed at which the bus is enumerated.
/// - low speed: 8
/// - full speed: 8, 16, 32, or 64
/// - high speed: 64
///
/// Default: 64 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` (default), the following fields should have the given values:
///
/// - `device_class` = `0xEF`
/// - `device_sub_class` = `0x02`
/// - `device_protocol` = `0x01`
///
/// If set to `false`, those fields must be set correctly for the classes that will be
/// installed on the USB device.
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> {
/// Create default configuration with the provided vid and pid values.
pub const fn new(vid: u16, pid: u16) -> Self {
Self {
device_class: 0xEF,
device_sub_class: 0x02,
device_protocol: 0x01,
max_packet_size_0: 64,
vendor_id: vid,
product_id: pid,
device_release: 0x0010,
bcd_usb: UsbVersion::TwoOne,
manufacturer: None,
product: None,
serial_number: None,
self_powered: false,
supports_remote_wakeup: false,
composite_with_iads: true,
max_power: 100,
}
}
}
/// [`UsbDevice`] builder.
pub struct Builder<'d, D: Driver<'d>> {
config: Config<'d>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
control_buf: &'d mut [u8],
driver: D,
next_string_index: u8,
config_descriptor: DescriptorWriter<'d>,
bos_descriptor: BosWriter<'d>,
msos_descriptor: MsOsDescriptorWriter<'d>,
}
impl<'d, D: Driver<'d>> Builder<'d, D> {
/// Creates a builder for constructing a new [`UsbDevice`].
///
/// `control_buf` is a buffer used for USB control request data. It should be sized
/// large enough for the length of the largest control request (in or out)
/// anticipated by any class added to the device.
pub fn new(
driver: D,
config: Config<'d>,
config_descriptor_buf: &'d mut [u8],
bos_descriptor_buf: &'d mut [u8],
msos_descriptor_buf: &'d mut [u8],
control_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");
}
assert!(
config.max_power <= 500,
"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 config_descriptor = DescriptorWriter::new(config_descriptor_buf);
let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf));
config_descriptor.configuration(&config);
bos_descriptor.bos();
Builder {
driver,
config,
interfaces: Vec::new(),
handlers: Vec::new(),
control_buf,
next_string_index: STRING_INDEX_CUSTOM_START,
config_descriptor,
bos_descriptor,
msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf),
}
}
/// Creates the [`UsbDevice`] instance with the configuration in this builder.
pub fn build(mut self) -> UsbDevice<'d, D> {
let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);
self.config_descriptor.end_configuration();
self.bos_descriptor.end_bos();
// Log the number of allocator bytes actually used in descriptor buffers
info!("USB: config_descriptor used: {}", self.config_descriptor.position());
info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position());
info!("USB: msos_descriptor used: {}", msos_descriptor.len());
info!("USB: control_buf size: {}", self.control_buf.len());
UsbDevice::build(
self.driver,
self.config,
self.handlers,
self.config_descriptor.into_buf(),
self.bos_descriptor.writer.into_buf(),
msos_descriptor,
self.interfaces,
self.control_buf,
)
}
/// Returns the size of the control request data buffer. Can be used by
/// classes to validate the buffer is large enough for their needs.
pub fn control_buf_len(&self) -> usize {
self.control_buf.len()
}
/// Add an USB function.
///
/// If [`Config::composite_with_iads`] is set, this will add an IAD descriptor
/// with the given class/subclass/protocol, associating all the child interfaces.
///
/// If it's not set, no IAD descriptor is added.
pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> {
let first_interface = InterfaceNumber::new(self.interfaces.len() as u8);
let iface_count_index = if self.config.composite_with_iads {
self.config_descriptor
.iad(first_interface, 0, class, subclass, protocol);
Some(self.config_descriptor.position() - 5)
} else {
None
};
FunctionBuilder {
builder: self,
iface_count_index,
first_interface,
}
}
/// Add a Handler.
///
/// The Handler is called on some USB bus events, and to handle all control requests not already
/// handled by the USB stack.
pub fn handler(&mut self, handler: &'d mut dyn Handler) {
assert!(
self.handlers.push(handler).is_ok(),
"embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}",
MAX_HANDLER_COUNT
);
}
/// Allocates a new string index.
pub fn string(&mut self) -> StringIndex {
let index = self.next_string_index;
self.next_string_index += 1;
StringIndex::new(index)
}
/// Add an MS OS 2.0 Descriptor Set.
///
/// Panics if called more than once.
pub fn msos_descriptor(&mut self, windows_version: u32, vendor_code: u8) {
self.msos_descriptor.header(windows_version, vendor_code);
}
/// Add an MS OS 2.0 Device Level Feature Descriptor.
pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
self.msos_descriptor.device_feature(desc);
}
/// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that
/// do not add their own.
pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> {
&mut self.msos_descriptor
}
}
/// Function builder.
///
/// A function is a logical grouping of interfaces that perform a given USB function.
/// If [`Config::composite_with_iads`] is set, each function will have an IAD descriptor.
/// If not, functions will not be visible as descriptors.
pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {
builder: &'a mut Builder<'d, D>,
iface_count_index: Option<usize>,
first_interface: InterfaceNumber,
}
impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {
fn drop(&mut self) {
self.builder.msos_descriptor.end_function();
}
}
impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
/// Add an interface to the function.
///
/// Interface numbers are guaranteed to be allocated consecutively, starting from 0.
pub fn interface(&mut self) -> InterfaceBuilder<'_, 'd, D> {
if let Some(i) = self.iface_count_index {
self.builder.config_descriptor.buf[i] += 1;
}
let number = self.builder.interfaces.len() as _;
let iface = Interface {
current_alt_setting: 0,
num_alt_settings: 0,
};
assert!(self.builder.interfaces.push(iface).is_ok(),
"embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
MAX_INTERFACE_COUNT
);
InterfaceBuilder {
builder: self.builder,
interface_number: InterfaceNumber::new(number),
next_alt_setting_number: 0,
}
}
/// Add an MS OS 2.0 Function Level Feature Descriptor.
pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
if !self.builder.msos_descriptor.is_in_config_subset() {
self.builder.msos_descriptor.configuration(0);
}
if !self.builder.msos_descriptor.is_in_function_subset() {
self.builder.msos_descriptor.function(self.first_interface);
}
self.builder.msos_descriptor.function_feature(desc);
}
}
/// Interface builder.
pub struct InterfaceBuilder<'a, 'd, D: Driver<'d>> {
builder: &'a mut Builder<'d, D>,
interface_number: InterfaceNumber,
next_alt_setting_number: u8,
}
impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {
/// Get the interface number.
pub const fn interface_number(&self) -> InterfaceNumber {
self.interface_number
}
/// Allocates a new string index.
pub fn string(&mut self) -> StringIndex {
self.builder.string()
}
/// Add an alternate setting to the interface and write its descriptor.
///
/// Alternate setting numbers are guaranteed to be allocated consecutively, starting from 0.
///
/// The first alternate setting, with number 0, is the default one.
pub fn alt_setting(
&mut self,
class: u8,
subclass: u8,
protocol: u8,
interface_string: Option<StringIndex>,
) -> InterfaceAltBuilder<'_, 'd, D> {
let number = self.next_alt_setting_number;
self.next_alt_setting_number += 1;
self.builder.interfaces[self.interface_number.0 as usize].num_alt_settings += 1;
self.builder.config_descriptor.interface_alt(
self.interface_number,
number,
class,
subclass,
protocol,
interface_string,
);
InterfaceAltBuilder {
builder: self.builder,
interface_number: self.interface_number,
alt_setting_number: number,
}
}
}
/// Interface alternate setting builder.
pub struct InterfaceAltBuilder<'a, 'd, D: Driver<'d>> {
builder: &'a mut Builder<'d, D>,
interface_number: InterfaceNumber,
alt_setting_number: u8,
}
impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
/// Get the interface number.
pub const fn interface_number(&self) -> InterfaceNumber {
self.interface_number
}
/// Get the alternate setting number.
pub const fn alt_setting_number(&self) -> u8 {
self.alt_setting_number
}
/// Add a custom descriptor to this alternate setting.
///
/// Descriptors are written in the order builder functions are called. Note that some
/// classes care about the order.
pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) {
self.builder.config_descriptor.write(descriptor_type, descriptor, &[]);
}
/// Add a custom Binary Object Store (BOS) descriptor to this alternate setting.
pub fn bos_capability(&mut self, capability_type: u8, capability: &[u8]) {
self.builder.bos_descriptor.capability(capability_type, capability);
}
/// Write a custom endpoint descriptor for a certain endpoint.
///
/// This can be necessary, if the endpoint descriptors can only be written
/// after the endpoint was created. As an example, an endpoint descriptor
/// may contain the address of an endpoint that was allocated earlier.
pub fn endpoint_descriptor(
&mut self,
endpoint: &EndpointInfo,
synchronization_type: SynchronizationType,
usage_type: UsageType,
extra_fields: &[u8],
) {
self.builder
.config_descriptor
.endpoint(endpoint, synchronization_type, usage_type, extra_fields);
}
/// Allocate an IN endpoint, without writing its descriptor.
///
/// Used for granular control over the order of endpoint and descriptor creation.
pub fn alloc_endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
let ep = self
.builder
.driver
.alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
.expect("alloc_endpoint_in failed");
ep
}
fn endpoint_in(
&mut self,
ep_type: EndpointType,
max_packet_size: u16,
interval_ms: u8,
synchronization_type: SynchronizationType,
usage_type: UsageType,
extra_fields: &[u8],
) -> D::EndpointIn {
let ep = self.alloc_endpoint_in(ep_type, max_packet_size, interval_ms);
self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
ep
}
/// Allocate an OUT endpoint, without writing its descriptor.
///
/// Use for granular control over the order of endpoint and descriptor creation.
pub fn alloc_endpoint_out(
&mut self,
ep_type: EndpointType,
max_packet_size: u16,
interval_ms: u8,
) -> D::EndpointOut {
let ep = self
.builder
.driver
.alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
.expect("alloc_endpoint_out failed");
ep
}
fn endpoint_out(
&mut self,
ep_type: EndpointType,
max_packet_size: u16,
interval_ms: u8,
synchronization_type: SynchronizationType,
usage_type: UsageType,
extra_fields: &[u8],
) -> D::EndpointOut {
let ep = self.alloc_endpoint_out(ep_type, max_packet_size, interval_ms);
self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
ep
}
/// Allocate a BULK IN endpoint and write its descriptor.
///
/// Descriptors are written in the order builder functions are called. Note that some
/// classes care about the order.
pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
self.endpoint_in(
EndpointType::Bulk,
max_packet_size,
0,
SynchronizationType::NoSynchronization,
UsageType::DataEndpoint,
&[],
)
}
/// Allocate a BULK OUT endpoint and write its descriptor.
///
/// Descriptors are written in the order builder functions are called. Note that some
/// classes care about the order.
pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
self.endpoint_out(
EndpointType::Bulk,
max_packet_size,
0,
SynchronizationType::NoSynchronization,
UsageType::DataEndpoint,
&[],
)
}
/// Allocate a INTERRUPT IN endpoint and write its descriptor.
///
/// Descriptors are written in the order builder functions are called. Note that some
/// classes care about the order.
pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
self.endpoint_in(
EndpointType::Interrupt,
max_packet_size,
interval_ms,
SynchronizationType::NoSynchronization,
UsageType::DataEndpoint,
&[],
)
}
/// Allocate a INTERRUPT OUT endpoint and write its descriptor.
pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
self.endpoint_out(
EndpointType::Interrupt,
max_packet_size,
interval_ms,
SynchronizationType::NoSynchronization,
UsageType::DataEndpoint,
&[],
)
}
/// Allocate a ISOCHRONOUS IN endpoint and write its descriptor.
///
/// Descriptors are written in the order builder functions are called. Note that some
/// classes care about the order.
pub fn endpoint_isochronous_in(
&mut self,
max_packet_size: u16,
interval_ms: u8,
synchronization_type: SynchronizationType,
usage_type: UsageType,
extra_fields: &[u8],
) -> D::EndpointIn {
self.endpoint_in(
EndpointType::Isochronous,
max_packet_size,
interval_ms,
synchronization_type,
usage_type,
extra_fields,
)
}
/// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
pub fn endpoint_isochronous_out(
&mut self,
max_packet_size: u16,
interval_ms: u8,
synchronization_type: SynchronizationType,
usage_type: UsageType,
extra_fields: &[u8],
) -> D::EndpointOut {
self.endpoint_out(
EndpointType::Isochronous,
max_packet_size,
interval_ms,
synchronization_type,
usage_type,
extra_fields,
)
}
}

View File

@@ -0,0 +1,546 @@
//! CDC-ACM class implementation, aka Serial over USB.
use core::cell::{Cell, RefCell};
use core::future::{poll_fn, Future};
use core::mem::{self, MaybeUninit};
use core::sync::atomic::{AtomicBool, Ordering};
use core::task::Poll;
use embassy_sync::blocking_mutex::CriticalSectionMutex;
use embassy_sync::waitqueue::WakerRegistration;
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::InterfaceNumber;
use crate::{Builder, Handler};
/// This should be used as `device_class` when building the `UsbDevice`.
pub const USB_CLASS_CDC: u8 = 0x02;
const USB_CLASS_CDC_DATA: u8 = 0x0a;
const CDC_SUBCLASS_ACM: u8 = 0x02;
const CDC_PROTOCOL_NONE: u8 = 0x00;
const CS_INTERFACE: u8 = 0x24;
const CDC_TYPE_HEADER: u8 = 0x00;
const CDC_TYPE_ACM: u8 = 0x02;
const CDC_TYPE_UNION: u8 = 0x06;
const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
#[allow(unused)]
const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
const REQ_SET_LINE_CODING: u8 = 0x20;
const REQ_GET_LINE_CODING: u8 = 0x21;
const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
/// Internal state for CDC-ACM
pub struct State<'a> {
control: MaybeUninit<Control<'a>>,
shared: ControlShared,
}
impl<'a> Default for State<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> State<'a> {
/// Create a new `State`.
pub fn new() -> Self {
Self {
control: MaybeUninit::uninit(),
shared: ControlShared::default(),
}
}
}
/// Packet level implementation of a CDC-ACM serial port.
///
/// This class can be used directly and it has the least overhead due to directly reading and
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial
/// port. The following constraints must be followed if you use this class directly:
///
/// - `read_packet` must be called with a buffer large enough to hold `max_packet_size` bytes.
/// - `write_packet` must not be called with a buffer larger than `max_packet_size` bytes.
/// - If you write a packet that is exactly `max_packet_size` bytes long, it won't be processed by the
/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
/// can be sent if there is no other data to send. This is because USB bulk transactions must be
/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
pub struct CdcAcmClass<'d, D: Driver<'d>> {
_comm_ep: D::EndpointIn,
_data_if: InterfaceNumber,
read_ep: D::EndpointOut,
write_ep: D::EndpointIn,
control: &'d ControlShared,
}
struct Control<'a> {
comm_if: InterfaceNumber,
shared: &'a ControlShared,
}
/// Shared data between Control and CdcAcmClass
struct ControlShared {
line_coding: CriticalSectionMutex<Cell<LineCoding>>,
dtr: AtomicBool,
rts: AtomicBool,
waker: RefCell<WakerRegistration>,
changed: AtomicBool,
}
impl Default for ControlShared {
fn default() -> Self {
ControlShared {
dtr: AtomicBool::new(false),
rts: AtomicBool::new(false),
line_coding: CriticalSectionMutex::new(Cell::new(LineCoding {
stop_bits: StopBits::One,
data_bits: 8,
parity_type: ParityType::None,
data_rate: 8_000,
})),
waker: RefCell::new(WakerRegistration::new()),
changed: AtomicBool::new(false),
}
}
}
impl ControlShared {
fn changed(&self) -> impl Future<Output = ()> + '_ {
poll_fn(|cx| {
if self.changed.load(Ordering::Relaxed) {
self.changed.store(false, Ordering::Relaxed);
Poll::Ready(())
} else {
self.waker.borrow_mut().register(cx.waker());
Poll::Pending
}
})
}
}
impl<'a> Control<'a> {
fn shared(&mut self) -> &'a ControlShared {
self.shared
}
}
impl<'d> Handler for Control<'d> {
fn reset(&mut self) {
let shared = self.shared();
shared.line_coding.lock(|x| x.set(LineCoding::default()));
shared.dtr.store(false, Ordering::Relaxed);
shared.rts.store(false, Ordering::Relaxed);
shared.changed.store(true, Ordering::Relaxed);
shared.waker.borrow_mut().wake();
}
fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
if (req.request_type, req.recipient, req.index)
!= (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)
{
return None;
}
match req.request {
REQ_SEND_ENCAPSULATED_COMMAND => {
// We don't actually support encapsulated commands but pretend we do for standards
// compatibility.
Some(OutResponse::Accepted)
}
REQ_SET_LINE_CODING if data.len() >= 7 => {
let coding = LineCoding {
data_rate: u32::from_le_bytes(data[0..4].try_into().unwrap()),
stop_bits: data[4].into(),
parity_type: data[5].into(),
data_bits: data[6],
};
let shared = self.shared();
shared.line_coding.lock(|x| x.set(coding));
debug!("Set line coding to: {:?}", coding);
shared.changed.store(true, Ordering::Relaxed);
shared.waker.borrow_mut().wake();
Some(OutResponse::Accepted)
}
REQ_SET_CONTROL_LINE_STATE => {
let dtr = (req.value & 0x0001) != 0;
let rts = (req.value & 0x0002) != 0;
let shared = self.shared();
shared.dtr.store(dtr, Ordering::Relaxed);
shared.rts.store(rts, Ordering::Relaxed);
debug!("Set dtr {}, rts {}", dtr, rts);
shared.changed.store(true, Ordering::Relaxed);
shared.waker.borrow_mut().wake();
Some(OutResponse::Accepted)
}
_ => Some(OutResponse::Rejected),
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
if (req.request_type, req.recipient, req.index)
!= (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)
{
return None;
}
match req.request {
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
REQ_GET_LINE_CODING if req.length == 7 => {
debug!("Sending line coding");
let coding = self.shared().line_coding.lock(Cell::get);
assert!(buf.len() >= 7);
buf[0..4].copy_from_slice(&coding.data_rate.to_le_bytes());
buf[4] = coding.stop_bits as u8;
buf[5] = coding.parity_type as u8;
buf[6] = coding.data_bits;
Some(InResponse::Accepted(&buf[0..7]))
}
_ => Some(InResponse::Rejected),
}
}
}
impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
/// Creates a new CdcAcmClass with the provided UsbBus and `max_packet_size` in bytes. For
/// full-speed devices, `max_packet_size` has to be one of 8, 16, 32 or 64.
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self {
assert!(builder.control_buf_len() >= 7);
let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE);
// Control interface
let mut iface = func.interface();
let comm_if = iface.interface_number();
let data_if = u8::from(comm_if) + 1;
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_HEADER, // bDescriptorSubtype
0x10,
0x01, // bcdCDC (1.10)
],
);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_ACM, // bDescriptorSubtype
0x02, // bmCapabilities:
// D1: Device supports the request combination of
// Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding,
// and the Notification Serial_State.
],
);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_UNION, // bDescriptorSubtype
comm_if.into(), // bControlInterface
data_if, // bSubordinateInterface
],
);
let comm_ep = alt.endpoint_interrupt_in(8, 255);
// Data interface
let mut iface = func.interface();
let data_if = iface.interface_number();
let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE, None);
let read_ep = alt.endpoint_bulk_out(max_packet_size);
let write_ep = alt.endpoint_bulk_in(max_packet_size);
drop(func);
let control = state.control.write(Control {
shared: &state.shared,
comm_if,
});
builder.handler(control);
let control_shared = &state.shared;
CdcAcmClass {
_comm_ep: comm_ep,
_data_if: data_if,
read_ep,
write_ep,
control: control_shared,
}
}
/// Gets the maximum packet size in bytes.
pub fn max_packet_size(&self) -> u16 {
// The size is the same for both endpoints.
self.read_ep.info().max_packet_size
}
/// Gets the current line coding. The line coding contains information that's mainly relevant
/// for USB to UART serial port emulators, and can be ignored if not relevant.
pub fn line_coding(&self) -> LineCoding {
self.control.line_coding.lock(Cell::get)
}
/// Gets the DTR (data terminal ready) state
pub fn dtr(&self) -> bool {
self.control.dtr.load(Ordering::Relaxed)
}
/// Gets the RTS (request to send) state
pub fn rts(&self) -> bool {
self.control.rts.load(Ordering::Relaxed)
}
/// Writes a single packet into the IN endpoint.
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
self.write_ep.write(data).await
}
/// Reads a single packet from the OUT endpoint.
pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
self.read_ep.read(data).await
}
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.read_ep.wait_enabled().await;
}
/// Split the class into a sender and receiver.
///
/// This allows concurrently sending and receiving packets from separate tasks.
pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
(
Sender {
write_ep: self.write_ep,
control: self.control,
},
Receiver {
read_ep: self.read_ep,
control: self.control,
},
)
}
/// Split the class into sender, receiver and control
///
/// Allows concurrently sending and receiving packets whilst monitoring for
/// control changes (dtr, rts)
pub fn split_with_control(self) -> (Sender<'d, D>, Receiver<'d, D>, ControlChanged<'d>) {
(
Sender {
write_ep: self.write_ep,
control: self.control,
},
Receiver {
read_ep: self.read_ep,
control: self.control,
},
ControlChanged { control: self.control },
)
}
}
/// CDC ACM Control status change monitor
///
/// You can obtain a `ControlChanged` with [`CdcAcmClass::split_with_control`]
pub struct ControlChanged<'d> {
control: &'d ControlShared,
}
impl<'d> ControlChanged<'d> {
/// Return a future for when the control settings change
pub async fn control_changed(&self) {
self.control.changed().await;
}
}
/// CDC ACM class packet sender.
///
/// You can obtain a `Sender` with [`CdcAcmClass::split`]
pub struct Sender<'d, D: Driver<'d>> {
write_ep: D::EndpointIn,
control: &'d ControlShared,
}
impl<'d, D: Driver<'d>> Sender<'d, D> {
/// Gets the maximum packet size in bytes.
pub fn max_packet_size(&self) -> u16 {
// The size is the same for both endpoints.
self.write_ep.info().max_packet_size
}
/// Gets the current line coding. The line coding contains information that's mainly relevant
/// for USB to UART serial port emulators, and can be ignored if not relevant.
pub fn line_coding(&self) -> LineCoding {
self.control.line_coding.lock(Cell::get)
}
/// Gets the DTR (data terminal ready) state
pub fn dtr(&self) -> bool {
self.control.dtr.load(Ordering::Relaxed)
}
/// Gets the RTS (request to send) state
pub fn rts(&self) -> bool {
self.control.rts.load(Ordering::Relaxed)
}
/// Writes a single packet into the IN endpoint.
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
self.write_ep.write(data).await
}
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.write_ep.wait_enabled().await;
}
}
/// CDC ACM class packet receiver.
///
/// You can obtain a `Receiver` with [`CdcAcmClass::split`]
pub struct Receiver<'d, D: Driver<'d>> {
read_ep: D::EndpointOut,
control: &'d ControlShared,
}
impl<'d, D: Driver<'d>> Receiver<'d, D> {
/// Gets the maximum packet size in bytes.
pub fn max_packet_size(&self) -> u16 {
// The size is the same for both endpoints.
self.read_ep.info().max_packet_size
}
/// Gets the current line coding. The line coding contains information that's mainly relevant
/// for USB to UART serial port emulators, and can be ignored if not relevant.
pub fn line_coding(&self) -> LineCoding {
self.control.line_coding.lock(Cell::get)
}
/// Gets the DTR (data terminal ready) state
pub fn dtr(&self) -> bool {
self.control.dtr.load(Ordering::Relaxed)
}
/// Gets the RTS (request to send) state
pub fn rts(&self) -> bool {
self.control.rts.load(Ordering::Relaxed)
}
/// Reads a single packet from the OUT endpoint.
/// Must be called with a buffer large enough to hold max_packet_size bytes.
pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
self.read_ep.read(data).await
}
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.read_ep.wait_enabled().await;
}
}
/// Number of stop bits for LineCoding
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum StopBits {
/// 1 stop bit
One = 0,
/// 1.5 stop bits
OnePointFive = 1,
/// 2 stop bits
Two = 2,
}
impl From<u8> for StopBits {
fn from(value: u8) -> Self {
if value <= 2 {
unsafe { mem::transmute(value) }
} else {
StopBits::One
}
}
}
/// Parity for LineCoding
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ParityType {
/// No parity bit.
None = 0,
/// Parity bit is 1 if the amount of `1` bits in the data byte is odd.
Odd = 1,
/// Parity bit is 1 if the amount of `1` bits in the data byte is even.
Even = 2,
/// Parity bit is always 1
Mark = 3,
/// Parity bit is always 0
Space = 4,
}
impl From<u8> for ParityType {
fn from(value: u8) -> Self {
if value <= 4 {
unsafe { mem::transmute(value) }
} else {
ParityType::None
}
}
}
/// Line coding parameters
///
/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
/// be ignored if you don't plan to interface with a physical UART.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LineCoding {
stop_bits: StopBits,
data_bits: u8,
parity_type: ParityType,
data_rate: u32,
}
impl LineCoding {
/// Gets the number of stop bits for UART communication.
pub fn stop_bits(&self) -> StopBits {
self.stop_bits
}
/// Gets the number of data bits for UART communication.
pub const fn data_bits(&self) -> u8 {
self.data_bits
}
/// Gets the parity type for UART communication.
pub const fn parity_type(&self) -> ParityType {
self.parity_type
}
/// Gets the data rate in bits per second for UART communication.
pub const fn data_rate(&self) -> u32 {
self.data_rate
}
}
impl Default for LineCoding {
fn default() -> Self {
LineCoding {
stop_bits: StopBits::One,
data_bits: 8,
parity_type: ParityType::None,
data_rate: 8_000,
}
}
}

View File

@@ -0,0 +1,104 @@
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the CDC-NCM class.
use embassy_futures::select::{select, Either};
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::LinkState;
use embassy_usb_driver::Driver;
use super::{CdcNcmClass, Receiver, Sender};
/// Internal state for the embassy-net integration.
pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {
ch_state: ch::State<MTU, N_RX, N_TX>,
}
impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> {
/// Create a new `State`.
pub const fn new() -> Self {
Self {
ch_state: ch::State::new(),
}
}
}
/// Background runner for the CDC-NCM class.
///
/// You must call `.run()` in a background task for the class to operate.
pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
tx_usb: Sender<'d, D>,
rx_usb: Receiver<'d, D>,
ch: ch::Runner<'d, MTU>,
}
impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
/// Run the CDC-NCM class.
///
/// You must call this in a background task for the class to operate.
pub async fn run(mut self) -> ! {
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
let rx_fut = async move {
loop {
trace!("WAITING for connection");
state_chan.set_link_state(LinkState::Down);
self.rx_usb.wait_connection().await.unwrap();
trace!("Connected");
state_chan.set_link_state(LinkState::Up);
loop {
let p = rx_chan.rx_buf().await;
match self.rx_usb.read_packet(p).await {
Ok(n) => rx_chan.rx_done(n),
Err(e) => {
warn!("error reading packet: {:?}", e);
break;
}
};
}
}
};
let tx_fut = async move {
loop {
let p = tx_chan.tx_buf().await;
if let Err(e) = self.tx_usb.write_packet(p).await {
warn!("Failed to TX packet: {:?}", e);
}
tx_chan.tx_done();
}
};
match select(rx_fut, tx_fut).await {
Either::First(x) => x,
Either::Second(x) => x,
}
}
}
// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug?
//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd;
/// Type alias for the embassy-net driver for CDC-NCM.
pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
/// Obtain a driver for using the CDC-NCM class with [`embassy-net`](https://crates.io/crates/embassy-net).
pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
self,
state: &'d mut State<MTU, N_RX, N_TX>,
ethernet_address: [u8; 6],
) -> (Runner<'d, D, MTU>, Device<'d, MTU>) {
let (tx_usb, rx_usb) = self.split();
let (runner, device) = ch::new(
&mut state.ch_state,
ch::driver::HardwareAddress::Ethernet(ethernet_address),
);
(
Runner {
tx_usb,
rx_usb,
ch: runner,
},
device,
)
}
}

View File

@@ -0,0 +1,528 @@
//! CDC-NCM class implementation, aka Ethernet over USB.
//!
//! # Compatibility
//!
//! Windows: NOT supported in Windows 10 (though there's apparently a driver you can install?). Supported out of the box in Windows 11.
//!
//! Linux: Well-supported since forever.
//!
//! Android: Support for CDC-NCM is spotty and varies across manufacturers.
//!
//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
//! This is due to regex spaghetti: <https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417>
//! and this nonsense in the linux kernel: <https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757>
use core::mem::{size_of, MaybeUninit};
use core::ptr::{addr_of, copy_nonoverlapping};
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::{InterfaceNumber, StringIndex};
use crate::{Builder, Handler};
pub mod embassy_net;
/// This should be used as `device_class` when building the `UsbDevice`.
pub const USB_CLASS_CDC: u8 = 0x02;
const USB_CLASS_CDC_DATA: u8 = 0x0a;
const CDC_SUBCLASS_NCM: u8 = 0x0d;
const CDC_PROTOCOL_NONE: u8 = 0x00;
const CDC_PROTOCOL_NTB: u8 = 0x01;
const CS_INTERFACE: u8 = 0x24;
const CDC_TYPE_HEADER: u8 = 0x00;
const CDC_TYPE_UNION: u8 = 0x06;
const CDC_TYPE_ETHERNET: u8 = 0x0F;
const CDC_TYPE_NCM: u8 = 0x1A;
const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
//const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
//const REQ_SET_ETHERNET_MULTICAST_FILTERS: u8 = 0x40;
//const REQ_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: u8 = 0x41;
//const REQ_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: u8 = 0x42;
//const REQ_SET_ETHERNET_PACKET_FILTER: u8 = 0x43;
//const REQ_GET_ETHERNET_STATISTIC: u8 = 0x44;
const REQ_GET_NTB_PARAMETERS: u8 = 0x80;
//const REQ_GET_NET_ADDRESS: u8 = 0x81;
//const REQ_SET_NET_ADDRESS: u8 = 0x82;
//const REQ_GET_NTB_FORMAT: u8 = 0x83;
//const REQ_SET_NTB_FORMAT: u8 = 0x84;
//const REQ_GET_NTB_INPUT_SIZE: u8 = 0x85;
const REQ_SET_NTB_INPUT_SIZE: u8 = 0x86;
//const REQ_GET_MAX_DATAGRAM_SIZE: u8 = 0x87;
//const REQ_SET_MAX_DATAGRAM_SIZE: u8 = 0x88;
//const REQ_GET_CRC_MODE: u8 = 0x89;
//const REQ_SET_CRC_MODE: u8 = 0x8A;
//const NOTIF_MAX_PACKET_SIZE: u16 = 8;
//const NOTIF_POLL_INTERVAL: u8 = 20;
const NTB_MAX_SIZE: usize = 2048;
const SIG_NTH: u32 = 0x484d_434e;
const SIG_NDP_NO_FCS: u32 = 0x304d_434e;
const SIG_NDP_WITH_FCS: u32 = 0x314d_434e;
const ALTERNATE_SETTING_DISABLED: u8 = 0x00;
const ALTERNATE_SETTING_ENABLED: u8 = 0x01;
/// Simple NTB header (NTH+NDP all in one) for sending packets
#[repr(packed)]
#[allow(unused)]
struct NtbOutHeader {
// NTH
nth_sig: u32,
nth_len: u16,
nth_seq: u16,
nth_total_len: u16,
nth_first_index: u16,
// NDP
ndp_sig: u32,
ndp_len: u16,
ndp_next_index: u16,
ndp_datagram_index: u16,
ndp_datagram_len: u16,
ndp_term1: u16,
ndp_term2: u16,
}
#[repr(packed)]
#[allow(unused)]
struct NtbParameters {
length: u16,
formats_supported: u16,
in_params: NtbParametersDir,
out_params: NtbParametersDir,
}
#[repr(packed)]
#[allow(unused)]
struct NtbParametersDir {
max_size: u32,
divisor: u16,
payload_remainder: u16,
out_alignment: u16,
max_datagram_count: u16,
}
fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
let len = size_of::<T>();
unsafe { copy_nonoverlapping(addr_of!(data).cast(), buf.as_mut_ptr(), len) }
&buf[..len]
}
/// Internal state for the CDC-NCM class.
pub struct State<'a> {
control: MaybeUninit<Control<'a>>,
shared: ControlShared,
}
impl<'a> Default for State<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> State<'a> {
/// Create a new `State`.
pub fn new() -> Self {
Self {
control: MaybeUninit::uninit(),
shared: ControlShared::default(),
}
}
}
/// Shared data between Control and `CdcAcmClass`
#[derive(Default)]
struct ControlShared {
mac_addr: [u8; 6],
}
struct Control<'a> {
mac_addr_string: StringIndex,
shared: &'a ControlShared,
mac_addr_str: [u8; 12],
comm_if: InterfaceNumber,
data_if: InterfaceNumber,
}
impl<'d> Handler for Control<'d> {
fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) {
if iface != self.data_if {
return;
}
match alternate_setting {
ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"),
ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"),
_ => unreachable!(),
}
}
fn control_out(&mut self, req: control::Request, _data: &[u8]) -> Option<OutResponse> {
if (req.request_type, req.recipient, req.index)
!= (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)
{
return None;
}
match req.request {
REQ_SEND_ENCAPSULATED_COMMAND => {
// We don't actually support encapsulated commands but pretend we do for standards
// compatibility.
Some(OutResponse::Accepted)
}
REQ_SET_NTB_INPUT_SIZE => {
// TODO
Some(OutResponse::Accepted)
}
_ => Some(OutResponse::Rejected),
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
if (req.request_type, req.recipient, req.index)
!= (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)
{
return None;
}
match req.request {
REQ_GET_NTB_PARAMETERS => {
let res = NtbParameters {
length: size_of::<NtbParameters>() as _,
formats_supported: 1, // only 16bit,
in_params: NtbParametersDir {
max_size: NTB_MAX_SIZE as _,
divisor: 4,
payload_remainder: 0,
out_alignment: 4,
max_datagram_count: 0, // not used
},
out_params: NtbParametersDir {
max_size: NTB_MAX_SIZE as _,
divisor: 4,
payload_remainder: 0,
out_alignment: 4,
max_datagram_count: 1, // We only decode 1 packet per NTB
},
};
Some(InResponse::Accepted(byteify(buf, res)))
}
_ => Some(InResponse::Rejected),
}
}
fn get_string(&mut self, index: StringIndex, _lang_id: u16) -> Option<&str> {
if index == self.mac_addr_string {
let mac_addr = self.shared.mac_addr;
let s = &mut self.mac_addr_str;
for i in 0..12 {
let n = (mac_addr[i / 2] >> ((1 - i % 2) * 4)) & 0xF;
s[i] = match n {
0x0..=0x9 => b'0' + n,
0xA..=0xF => b'A' + n - 0xA,
_ => unreachable!(),
}
}
Some(unsafe { core::str::from_utf8_unchecked(s) })
} else {
warn!("unknown string index requested");
None
}
}
}
/// CDC-NCM class
pub struct CdcNcmClass<'d, D: Driver<'d>> {
_comm_if: InterfaceNumber,
comm_ep: D::EndpointIn,
data_if: InterfaceNumber,
read_ep: D::EndpointOut,
write_ep: D::EndpointIn,
_control: &'d ControlShared,
max_packet_size: usize,
}
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
/// Create a new CDC NCM class.
pub fn new(
builder: &mut Builder<'d, D>,
state: &'d mut State<'d>,
mac_address: [u8; 6],
max_packet_size: u16,
) -> Self {
state.shared.mac_addr = mac_address;
let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE);
// Control interface
let mut iface = func.interface();
let mac_addr_string = iface.string();
let comm_if = iface.interface_number();
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_HEADER, // bDescriptorSubtype
0x10,
0x01, // bcdCDC (1.10)
],
);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_UNION, // bDescriptorSubtype
comm_if.into(), // bControlInterface
u8::from(comm_if) + 1, // bSubordinateInterface
],
);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_ETHERNET, // bDescriptorSubtype
mac_addr_string.into(), // iMACAddress
0, // bmEthernetStatistics
0, // |
0, // |
0, // |
0xea, // wMaxSegmentSize = 1514
0x05, // |
0, // wNumberMCFilters
0, // |
0, // bNumberPowerFilters
],
);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_NCM, // bDescriptorSubtype
0x00, // bcdNCMVersion
0x01, // |
0, // bmNetworkCapabilities
],
);
let comm_ep = alt.endpoint_interrupt_in(8, 255);
// Data interface
let mut iface = func.interface();
let data_if = iface.interface_number();
let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None);
let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None);
let read_ep = alt.endpoint_bulk_out(max_packet_size);
let write_ep = alt.endpoint_bulk_in(max_packet_size);
drop(func);
let control = state.control.write(Control {
mac_addr_string,
shared: &state.shared,
mac_addr_str: [0; 12],
comm_if,
data_if,
});
builder.handler(control);
CdcNcmClass {
_comm_if: comm_if,
comm_ep,
data_if,
read_ep,
write_ep,
_control: &state.shared,
max_packet_size: max_packet_size as usize,
}
}
/// Split the class into a sender and receiver.
///
/// This allows concurrently sending and receiving packets from separate tasks.
pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
(
Sender {
write_ep: self.write_ep,
seq: 0,
max_packet_size: self.max_packet_size,
},
Receiver {
data_if: self.data_if,
comm_ep: self.comm_ep,
read_ep: self.read_ep,
},
)
}
}
/// CDC NCM class packet sender.
///
/// You can obtain a `Sender` with [`CdcNcmClass::split`]
pub struct Sender<'d, D: Driver<'d>> {
write_ep: D::EndpointIn,
seq: u16,
max_packet_size: usize,
}
impl<'d, D: Driver<'d>> Sender<'d, D> {
/// Write a packet.
///
/// This waits until the packet is successfully stored in the CDC-NCM endpoint buffers.
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
const OUT_HEADER_LEN: usize = 28;
const ABS_MAX_PACKET_SIZE: usize = 512;
let seq = self.seq;
self.seq = self.seq.wrapping_add(1);
let header = NtbOutHeader {
nth_sig: SIG_NTH,
nth_len: 0x0c,
nth_seq: seq,
nth_total_len: (data.len() + OUT_HEADER_LEN) as u16,
nth_first_index: 0x0c,
ndp_sig: SIG_NDP_NO_FCS,
ndp_len: 0x10,
ndp_next_index: 0x00,
ndp_datagram_index: OUT_HEADER_LEN as u16,
ndp_datagram_len: data.len() as u16,
ndp_term1: 0x00,
ndp_term2: 0x00,
};
// Build first packet on a buffer, send next packets straight from `data`.
let mut buf = [0; ABS_MAX_PACKET_SIZE];
let n = byteify(&mut buf, header);
assert_eq!(n.len(), OUT_HEADER_LEN);
if OUT_HEADER_LEN + data.len() < self.max_packet_size {
// First packet is not full, just send it.
// No need to send ZLP because it's short for sure.
buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data);
self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?;
} else {
let (d1, d2) = data.split_at(self.max_packet_size - OUT_HEADER_LEN);
buf[OUT_HEADER_LEN..self.max_packet_size].copy_from_slice(d1);
self.write_ep.write(&buf[..self.max_packet_size]).await?;
for chunk in d2.chunks(self.max_packet_size) {
self.write_ep.write(chunk).await?;
}
// Send ZLP if needed.
if d2.len() % self.max_packet_size == 0 {
self.write_ep.write(&[]).await?;
}
}
Ok(())
}
}
/// CDC NCM class packet receiver.
///
/// You can obtain a `Receiver` with [`CdcNcmClass::split`]
pub struct Receiver<'d, D: Driver<'d>> {
data_if: InterfaceNumber,
comm_ep: D::EndpointIn,
read_ep: D::EndpointOut,
}
impl<'d, D: Driver<'d>> Receiver<'d, D> {
/// Write a network packet.
///
/// This waits until a packet is successfully received from the endpoint buffers.
pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
// Retry loop
loop {
// read NTB
let mut ntb = [0u8; NTB_MAX_SIZE];
let mut pos = 0;
loop {
let n = self.read_ep.read(&mut ntb[pos..]).await?;
pos += n;
if n < self.read_ep.info().max_packet_size as usize || pos == NTB_MAX_SIZE {
break;
}
}
let ntb = &ntb[..pos];
// Process NTB header (NTH)
let Some(nth) = ntb.get(..12) else {
warn!("Received too short NTB");
continue;
};
let sig = u32::from_le_bytes(nth[0..4].try_into().unwrap());
if sig != SIG_NTH {
warn!("Received bad NTH sig.");
continue;
}
let ndp_idx = u16::from_le_bytes(nth[10..12].try_into().unwrap()) as usize;
// Process NTB Datagram Pointer (NDP)
let Some(ndp) = ntb.get(ndp_idx..ndp_idx + 12) else {
warn!("NTH has an NDP pointer out of range.");
continue;
};
let sig = u32::from_le_bytes(ndp[0..4].try_into().unwrap());
if sig != SIG_NDP_NO_FCS && sig != SIG_NDP_WITH_FCS {
warn!("Received bad NDP sig.");
continue;
}
let datagram_index = u16::from_le_bytes(ndp[8..10].try_into().unwrap()) as usize;
let datagram_len = u16::from_le_bytes(ndp[10..12].try_into().unwrap()) as usize;
if datagram_index == 0 || datagram_len == 0 {
// empty, ignore. This is allowed by the spec, so don't warn.
continue;
}
// Process actual datagram, finally.
let Some(datagram) = ntb.get(datagram_index..datagram_index + datagram_len) else {
warn!("NDP has a datagram pointer out of range.");
continue;
};
buf[..datagram_len].copy_from_slice(datagram);
return Ok(datagram_len);
}
}
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) -> Result<(), EndpointError> {
loop {
self.read_ep.wait_enabled().await;
self.comm_ep.wait_enabled().await;
let buf = [
0xA1, //bmRequestType
0x00, //bNotificationType = NETWORK_CONNECTION
0x01, // wValue = connected
0x00,
self.data_if.into(), // wIndex = interface
0x00,
0x00, // wLength
0x00,
];
match self.comm_ep.write(&buf).await {
Ok(()) => break, // Done!
Err(EndpointError::Disabled) => {} // Got disabled again, wait again.
Err(e) => return Err(e),
}
}
Ok(())
}
}

View File

@@ -0,0 +1,552 @@
//! USB HID (Human Interface Device) class implementation.
use core::mem::MaybeUninit;
use core::ops::Range;
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "usbd-hid")]
use ssmarshal::serialize;
#[cfg(feature = "usbd-hid")]
use usbd_hid::descriptor::AsInputReport;
use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::InterfaceNumber;
use crate::{Builder, Handler};
const USB_CLASS_HID: u8 = 0x03;
const USB_SUBCLASS_NONE: u8 = 0x00;
const USB_PROTOCOL_NONE: u8 = 0x00;
// HID
const HID_DESC_DESCTYPE_HID: u8 = 0x21;
const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22;
const HID_DESC_SPEC_1_10: [u8; 2] = [0x10, 0x01];
const HID_DESC_COUNTRY_UNSPEC: u8 = 0x00;
const HID_REQ_SET_IDLE: u8 = 0x0a;
const HID_REQ_GET_IDLE: u8 = 0x02;
const HID_REQ_GET_REPORT: u8 = 0x01;
const HID_REQ_SET_REPORT: u8 = 0x09;
const HID_REQ_GET_PROTOCOL: u8 = 0x03;
const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
/// Configuration for the HID class.
pub struct Config<'d> {
/// HID report descriptor.
pub report_descriptor: &'d [u8],
/// Handler for control requests.
pub request_handler: Option<&'d mut dyn RequestHandler>,
/// Configures how frequently the host should poll for reading/writing HID reports.
///
/// A lower value means better throughput & latency, at the expense
/// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for
/// high performance uses, and a value of 255 is good for best-effort usecases.
pub poll_ms: u8,
/// Max packet size for both the IN and OUT endpoints.
pub max_packet_size: u16,
}
/// Report ID
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ReportId {
/// IN report
In(u8),
/// OUT report
Out(u8),
/// Feature report
Feature(u8),
}
impl ReportId {
const fn try_from(value: u16) -> Result<Self, ()> {
match value >> 8 {
1 => Ok(ReportId::In(value as u8)),
2 => Ok(ReportId::Out(value as u8)),
3 => Ok(ReportId::Feature(value as u8)),
_ => Err(()),
}
}
}
/// Internal state for USB HID.
pub struct State<'d> {
control: MaybeUninit<Control<'d>>,
out_report_offset: AtomicUsize,
}
impl<'d> Default for State<'d> {
fn default() -> Self {
Self::new()
}
}
impl<'d> State<'d> {
/// Create a new `State`.
pub const fn new() -> Self {
State {
control: MaybeUninit::uninit(),
out_report_offset: AtomicUsize::new(0),
}
}
}
/// USB HID reader/writer.
pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> {
reader: HidReader<'d, D, READ_N>,
writer: HidWriter<'d, D, WRITE_N>,
}
fn build<'d, D: Driver<'d>>(
builder: &mut Builder<'d, D>,
state: &'d mut State<'d>,
config: Config<'d>,
with_out_endpoint: bool,
) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) {
let len = config.report_descriptor.len();
let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE);
let mut iface = func.interface();
let if_num = iface.interface_number();
let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None);
// HID descriptor
alt.descriptor(
HID_DESC_DESCTYPE_HID,
&[
// HID Class spec version
HID_DESC_SPEC_1_10[0],
HID_DESC_SPEC_1_10[1],
// Country code not supported
HID_DESC_COUNTRY_UNSPEC,
// Number of following descriptors
1,
// We have a HID report descriptor the host should read
HID_DESC_DESCTYPE_HID_REPORT,
// HID report descriptor size,
(len & 0xFF) as u8,
(len >> 8 & 0xFF) as u8,
],
);
let ep_in = alt.endpoint_interrupt_in(config.max_packet_size, config.poll_ms);
let ep_out = if with_out_endpoint {
Some(alt.endpoint_interrupt_out(config.max_packet_size, config.poll_ms))
} else {
None
};
drop(func);
let control = state.control.write(Control::new(
if_num,
config.report_descriptor,
config.request_handler,
&state.out_report_offset,
));
builder.handler(control);
(ep_out, ep_in, &state.out_report_offset)
}
impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWriter<'d, D, READ_N, WRITE_N> {
/// Creates a new `HidReaderWriter`.
///
/// This will allocate one IN and one OUT endpoints. If you only need writing (sending)
/// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only.
///
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self {
let (ep_out, ep_in, offset) = build(builder, state, config, true);
Self {
reader: HidReader {
ep_out: ep_out.unwrap(),
offset,
},
writer: HidWriter { ep_in },
}
}
/// Splits into separate readers/writers for input and output reports.
pub fn split(self) -> (HidReader<'d, D, READ_N>, HidWriter<'d, D, WRITE_N>) {
(self.reader, self.writer)
}
/// Waits for both IN and OUT endpoints to be enabled.
pub async fn ready(&mut self) {
self.reader.ready().await;
self.writer.ready().await;
}
/// Writes an input report by serializing the given report structure.
#[cfg(feature = "usbd-hid")]
pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> {
self.writer.write_serialize(r).await
}
/// Writes `report` to its interrupt endpoint.
pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {
self.writer.write(report).await
}
/// Reads an output report from the Interrupt Out pipe.
///
/// See [`HidReader::read`].
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
self.reader.read(buf).await
}
}
/// USB HID writer.
///
/// You can obtain a `HidWriter` using [`HidReaderWriter::split`].
pub struct HidWriter<'d, D: Driver<'d>, const N: usize> {
ep_in: D::EndpointIn,
}
/// USB HID reader.
///
/// You can obtain a `HidReader` using [`HidReaderWriter::split`].
pub struct HidReader<'d, D: Driver<'d>, const N: usize> {
ep_out: D::EndpointOut,
offset: &'d AtomicUsize,
}
/// Error when reading a HID report.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ReadError {
/// The given buffer was too small to read the received report.
BufferOverflow,
/// The endpoint is disabled.
Disabled,
/// The report was only partially read. See [`HidReader::read`] for details.
Sync(Range<usize>),
}
impl From<EndpointError> for ReadError {
fn from(val: EndpointError) -> Self {
use EndpointError::{BufferOverflow, Disabled};
match val {
BufferOverflow => ReadError::BufferOverflow,
Disabled => ReadError::Disabled,
}
}
}
impl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> {
/// Creates a new HidWriter.
///
/// This will allocate one IN endpoint only, so the host won't be able to send
/// reports to us. If you need that, consider using [`HidReaderWriter::new`] instead.
///
/// poll_ms configures how frequently the host should poll for reading/writing
/// HID reports. A lower value means better throughput & latency, at the expense
/// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for
/// high performance uses, and a value of 255 is good for best-effort usecases.
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self {
let (ep_out, ep_in, _offset) = build(builder, state, config, false);
assert!(ep_out.is_none());
Self { ep_in }
}
/// Waits for the interrupt in endpoint to be enabled.
pub async fn ready(&mut self) {
self.ep_in.wait_enabled().await;
}
/// Writes an input report by serializing the given report structure.
#[cfg(feature = "usbd-hid")]
pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> {
let mut buf: [u8; N] = [0; N];
let Ok(size) = serialize(&mut buf, r) else {
return Err(EndpointError::BufferOverflow);
};
self.write(&buf[0..size]).await
}
/// Writes `report` to its interrupt endpoint.
pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {
assert!(report.len() <= N);
let max_packet_size = usize::from(self.ep_in.info().max_packet_size);
let zlp_needed = report.len() < N && (report.len() % max_packet_size == 0);
for chunk in report.chunks(max_packet_size) {
self.ep_in.write(chunk).await?;
}
if zlp_needed {
self.ep_in.write(&[]).await?;
}
Ok(())
}
}
impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
/// Waits for the interrupt out endpoint to be enabled.
pub async fn ready(&mut self) {
self.ep_out.wait_enabled().await;
}
/// Delivers output reports from the Interrupt Out pipe to `handler`.
///
/// If `use_report_ids` is true, the first byte of the report will be used as
/// the `ReportId` value. Otherwise the `ReportId` value will be 0.
pub async fn run<T: RequestHandler>(mut self, use_report_ids: bool, handler: &mut T) -> ! {
let offset = self.offset.load(Ordering::Acquire);
assert!(offset == 0);
let mut buf = [0; N];
loop {
match self.read(&mut buf).await {
Ok(len) => {
let id = if use_report_ids { buf[0] } else { 0 };
handler.set_report(ReportId::Out(id), &buf[..len]);
}
Err(ReadError::BufferOverflow) => warn!(
"Host sent output report larger than the configured maximum output report length ({})",
N
),
Err(ReadError::Disabled) => self.ep_out.wait_enabled().await,
Err(ReadError::Sync(_)) => unreachable!(),
}
}
}
/// Reads an output report from the Interrupt Out pipe.
///
/// **Note:** Any reports sent from the host over the control pipe will be
/// passed to [`RequestHandler::set_report()`] for handling. The application
/// is responsible for ensuring output reports from both pipes are handled
/// correctly.
///
/// **Note:** If `N` > the maximum packet size of the endpoint (i.e. output
/// reports may be split across multiple packets) and this method's future
/// is dropped after some packets have been read, the next call to `read()`
/// will return a [`ReadError::Sync`]. The range in the sync error
/// indicates the portion `buf` that was filled by the current call to
/// `read()`. If the dropped future used the same `buf`, then `buf` will
/// contain the full report.
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
assert!(N != 0);
assert!(buf.len() >= N);
// Read packets from the endpoint
let max_packet_size = usize::from(self.ep_out.info().max_packet_size);
let starting_offset = self.offset.load(Ordering::Acquire);
let mut total = starting_offset;
loop {
for chunk in buf[starting_offset..N].chunks_mut(max_packet_size) {
match self.ep_out.read(chunk).await {
Ok(size) => {
total += size;
if size < max_packet_size || total == N {
self.offset.store(0, Ordering::Release);
break;
}
self.offset.store(total, Ordering::Release);
}
Err(err) => {
self.offset.store(0, Ordering::Release);
return Err(err.into());
}
}
}
// Some hosts may send ZLPs even when not required by the HID spec, so we'll loop as long as total == 0.
if total > 0 {
break;
}
}
if starting_offset > 0 {
Err(ReadError::Sync(starting_offset..total))
} else {
Ok(total)
}
}
}
/// Handler for HID-related control requests.
pub trait RequestHandler {
/// Reads the value of report `id` into `buf` returning the size.
///
/// Returns `None` if `id` is invalid or no data is available.
fn get_report(&mut self, id: ReportId, buf: &mut [u8]) -> Option<usize> {
let _ = (id, buf);
None
}
/// Sets the value of report `id` to `data`.
fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {
let _ = (id, data);
OutResponse::Rejected
}
/// Get the idle rate for `id`.
///
/// If `id` is `None`, get the idle rate for all reports. Returning `None`
/// will reject the control request. Any duration at or above 1.024 seconds
/// or below 4ms will be returned as an indefinite idle rate.
fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {
let _ = id;
None
}
/// Set the idle rate for `id` to `dur`.
///
/// If `id` is `None`, set the idle rate of all input reports to `dur`. If
/// an indefinite duration is requested, `dur` will be set to `u32::MAX`.
fn set_idle_ms(&mut self, id: Option<ReportId>, duration_ms: u32) {
let _ = (id, duration_ms);
}
}
struct Control<'d> {
if_num: InterfaceNumber,
report_descriptor: &'d [u8],
request_handler: Option<&'d mut dyn RequestHandler>,
out_report_offset: &'d AtomicUsize,
hid_descriptor: [u8; 9],
}
impl<'d> Control<'d> {
fn new(
if_num: InterfaceNumber,
report_descriptor: &'d [u8],
request_handler: Option<&'d mut dyn RequestHandler>,
out_report_offset: &'d AtomicUsize,
) -> Self {
Control {
if_num,
report_descriptor,
request_handler,
out_report_offset,
hid_descriptor: [
// Length of buf inclusive of size prefix
9,
// Descriptor type
HID_DESC_DESCTYPE_HID,
// HID Class spec version
HID_DESC_SPEC_1_10[0],
HID_DESC_SPEC_1_10[1],
// Country code not supported
HID_DESC_COUNTRY_UNSPEC,
// Number of following descriptors
1,
// We have a HID report descriptor the host should read
HID_DESC_DESCTYPE_HID_REPORT,
// HID report descriptor size,
(report_descriptor.len() & 0xFF) as u8,
(report_descriptor.len() >> 8 & 0xFF) as u8,
],
}
}
}
impl<'d> Handler for Control<'d> {
fn reset(&mut self) {
self.out_report_offset.store(0, Ordering::Release);
}
fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> {
if (req.request_type, req.recipient, req.index)
!= (RequestType::Class, Recipient::Interface, self.if_num.0 as u16)
{
return None;
}
// This uses a defmt-specific formatter that causes use of the `log`
// feature to fail to build, so leave it defmt-specific for now.
#[cfg(feature = "defmt")]
trace!("HID control_out {:?} {=[u8]:x}", req, data);
match req.request {
HID_REQ_SET_IDLE => {
if let Some(handler) = self.request_handler.as_mut() {
let id = req.value as u8;
let id = (id != 0).then_some(ReportId::In(id));
let dur = u32::from(req.value >> 8);
let dur = if dur == 0 { u32::MAX } else { 4 * dur };
handler.set_idle_ms(id, dur);
}
Some(OutResponse::Accepted)
}
HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler.as_mut()) {
(Ok(id), Some(handler)) => Some(handler.set_report(id, data)),
_ => Some(OutResponse::Rejected),
},
HID_REQ_SET_PROTOCOL => {
if req.value == 1 {
Some(OutResponse::Accepted)
} else {
warn!("HID Boot Protocol is unsupported.");
Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol
}
}
_ => Some(OutResponse::Rejected),
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
if req.index != self.if_num.0 as u16 {
return None;
}
match (req.request_type, req.recipient) {
(RequestType::Standard, Recipient::Interface) => match req.request {
Request::GET_DESCRIPTOR => match (req.value >> 8) as u8 {
HID_DESC_DESCTYPE_HID_REPORT => Some(InResponse::Accepted(self.report_descriptor)),
HID_DESC_DESCTYPE_HID => Some(InResponse::Accepted(&self.hid_descriptor)),
_ => Some(InResponse::Rejected),
},
_ => Some(InResponse::Rejected),
},
(RequestType::Class, Recipient::Interface) => {
trace!("HID control_in {:?}", req);
match req.request {
HID_REQ_GET_REPORT => {
let size = match ReportId::try_from(req.value) {
Ok(id) => self.request_handler.as_mut().and_then(|x| x.get_report(id, buf)),
Err(_) => None,
};
if let Some(size) = size {
Some(InResponse::Accepted(&buf[0..size]))
} else {
Some(InResponse::Rejected)
}
}
HID_REQ_GET_IDLE => {
if let Some(handler) = self.request_handler.as_mut() {
let id = req.value as u8;
let id = (id != 0).then_some(ReportId::In(id));
if let Some(dur) = handler.get_idle_ms(id) {
let dur = u8::try_from(dur / 4).unwrap_or(0);
buf[0] = dur;
Some(InResponse::Accepted(&buf[0..1]))
} else {
Some(InResponse::Rejected)
}
} else {
Some(InResponse::Rejected)
}
}
HID_REQ_GET_PROTOCOL => {
// UNSUPPORTED: Boot Protocol
buf[0] = 1;
Some(InResponse::Accepted(&buf[0..1]))
}
_ => Some(InResponse::Rejected),
}
}
_ => None,
}
}
}

View File

@@ -0,0 +1,227 @@
//! MIDI class implementation.
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::Builder;
/// This should be used as `device_class` when building the `UsbDevice`.
pub const USB_AUDIO_CLASS: u8 = 0x01;
const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;
const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03;
const MIDI_IN_JACK_SUBTYPE: u8 = 0x02;
const MIDI_OUT_JACK_SUBTYPE: u8 = 0x03;
const EMBEDDED: u8 = 0x01;
const EXTERNAL: u8 = 0x02;
const CS_INTERFACE: u8 = 0x24;
const CS_ENDPOINT: u8 = 0x25;
const HEADER_SUBTYPE: u8 = 0x01;
const MS_HEADER_SUBTYPE: u8 = 0x01;
const MS_GENERAL: u8 = 0x01;
const PROTOCOL_NONE: u8 = 0x00;
const MIDI_IN_SIZE: u8 = 0x06;
const MIDI_OUT_SIZE: u8 = 0x09;
/// Packet level implementation of a USB MIDI device.
///
/// This class can be used directly and it has the least overhead due to directly reading and
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like port.
/// The following constraints must be followed if you use this class directly:
///
/// - `read_packet` must be called with a buffer large enough to hold `max_packet_size` bytes.
/// - `write_packet` must not be called with a buffer larger than `max_packet_size` bytes.
/// - If you write a packet that is exactly `max_packet_size` bytes long, it won't be processed by the
/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
/// can be sent if there is no other data to send. This is because USB bulk transactions must be
/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
pub struct MidiClass<'d, D: Driver<'d>> {
read_ep: D::EndpointOut,
write_ep: D::EndpointIn,
}
impl<'d, D: Driver<'d>> MidiClass<'d, D> {
/// Creates a new `MidiClass` with the provided UsbBus, number of input and output jacks and `max_packet_size` in bytes.
/// For full-speed devices, `max_packet_size` has to be one of 8, 16, 32 or 64.
pub fn new(builder: &mut Builder<'d, D>, n_in_jacks: u8, n_out_jacks: u8, max_packet_size: u16) -> Self {
let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE);
// Audio control interface
let mut iface = func.interface();
let audio_if = iface.interface_number();
let midi_if = u8::from(audio_if) + 1;
let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None);
alt.descriptor(CS_INTERFACE, &[HEADER_SUBTYPE, 0x00, 0x01, 0x09, 0x00, 0x01, midi_if]);
// MIDIStreaming interface
let mut iface = func.interface();
let _midi_if = iface.interface_number();
let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_MIDISTREAMING_SUBCLASS, PROTOCOL_NONE, None);
let midi_streaming_total_length = 7
+ (n_in_jacks + n_out_jacks) as usize * (MIDI_IN_SIZE + MIDI_OUT_SIZE) as usize
+ 7
+ (4 + n_out_jacks as usize)
+ 7
+ (4 + n_in_jacks as usize);
alt.descriptor(
CS_INTERFACE,
&[
MS_HEADER_SUBTYPE,
0x00,
0x01,
(midi_streaming_total_length & 0xFF) as u8,
((midi_streaming_total_length >> 8) & 0xFF) as u8,
],
);
// Calculates the index'th external midi in jack id
let in_jack_id_ext = |index| 2 * index + 1;
// Calculates the index'th embedded midi out jack id
let out_jack_id_emb = |index| 2 * index + 2;
// Calculates the index'th external midi out jack id
let out_jack_id_ext = |index| 2 * n_in_jacks + 2 * index + 1;
// Calculates the index'th embedded midi in jack id
let in_jack_id_emb = |index| 2 * n_in_jacks + 2 * index + 2;
for i in 0..n_in_jacks {
alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EXTERNAL, in_jack_id_ext(i), 0x00]);
}
for i in 0..n_out_jacks {
alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EMBEDDED, in_jack_id_emb(i), 0x00]);
}
for i in 0..n_out_jacks {
alt.descriptor(
CS_INTERFACE,
&[
MIDI_OUT_JACK_SUBTYPE,
EXTERNAL,
out_jack_id_ext(i),
0x01,
in_jack_id_emb(i),
0x01,
0x00,
],
);
}
for i in 0..n_in_jacks {
alt.descriptor(
CS_INTERFACE,
&[
MIDI_OUT_JACK_SUBTYPE,
EMBEDDED,
out_jack_id_emb(i),
0x01,
in_jack_id_ext(i),
0x01,
0x00,
],
);
}
let mut endpoint_data = [
MS_GENERAL, 0, // Number of jacks
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Jack mappings
];
endpoint_data[1] = n_out_jacks;
for i in 0..n_out_jacks {
endpoint_data[2 + i as usize] = in_jack_id_emb(i);
}
let read_ep = alt.endpoint_bulk_out(max_packet_size);
alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]);
endpoint_data[1] = n_in_jacks;
for i in 0..n_in_jacks {
endpoint_data[2 + i as usize] = out_jack_id_emb(i);
}
let write_ep = alt.endpoint_bulk_in(max_packet_size);
alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]);
MidiClass { read_ep, write_ep }
}
/// Gets the maximum packet size in bytes.
pub fn max_packet_size(&self) -> u16 {
// The size is the same for both endpoints.
self.read_ep.info().max_packet_size
}
/// Writes a single packet into the IN endpoint.
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
self.write_ep.write(data).await
}
/// Reads a single packet from the OUT endpoint.
pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
self.read_ep.read(data).await
}
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.read_ep.wait_enabled().await;
}
/// Split the class into a sender and receiver.
///
/// This allows concurrently sending and receiving packets from separate tasks.
pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
(
Sender {
write_ep: self.write_ep,
},
Receiver { read_ep: self.read_ep },
)
}
}
/// Midi class packet sender.
///
/// You can obtain a `Sender` with [`MidiClass::split`]
pub struct Sender<'d, D: Driver<'d>> {
write_ep: D::EndpointIn,
}
impl<'d, D: Driver<'d>> Sender<'d, D> {
/// Gets the maximum packet size in bytes.
pub fn max_packet_size(&self) -> u16 {
// The size is the same for both endpoints.
self.write_ep.info().max_packet_size
}
/// Writes a single packet.
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
self.write_ep.write(data).await
}
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.write_ep.wait_enabled().await;
}
}
/// Midi class packet receiver.
///
/// You can obtain a `Receiver` with [`MidiClass::split`]
pub struct Receiver<'d, D: Driver<'d>> {
read_ep: D::EndpointOut,
}
impl<'d, D: Driver<'d>> Receiver<'d, D> {
/// Gets the maximum packet size in bytes.
pub fn max_packet_size(&self) -> u16 {
// The size is the same for both endpoints.
self.read_ep.info().max_packet_size
}
/// Reads a single packet.
pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
self.read_ep.read(data).await
}
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.read_ep.wait_enabled().await;
}
}

View File

@@ -0,0 +1,7 @@
//! Implementations of well-known USB classes.
pub mod cdc_acm;
pub mod cdc_ncm;
pub mod hid;
pub mod midi;
pub mod uac1;
pub mod web_usb;

View File

@@ -0,0 +1,151 @@
//! Audio Device Class Codes as defined in Universal Serial Bus Device Class
//! Definition for Audio Devices, Release 1.0, Appendix A and Universal Serial
//! Bus Device Class Definition for Audio Data Formats, Release 1.0, Appendix
//! A.1.1 (Audio Data Format Type I Codes)
#![allow(dead_code)]
/// The current version of the ADC specification (1.0)
pub const ADC_VERSION: u16 = 0x0100;
/// The current version of the USB device (1.0)
pub const DEVICE_VERSION: u16 = 0x0100;
/// Audio Interface Class Code
pub const USB_AUDIO_CLASS: u8 = 0x01;
// Audio Interface Subclass Codes
pub const USB_UNDEFINED_SUBCLASS: u8 = 0x00;
pub const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;
pub const USB_AUDIOSTREAMING_SUBCLASS: u8 = 0x02;
pub const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03;
// Audio Protocol Code
pub const PROTOCOL_NONE: u8 = 0x00;
// Audio Class-Specific Descriptor Types
pub const CS_UNDEFINED: u8 = 0x20;
pub const CS_DEVICE: u8 = 0x21;
pub const CS_CONFIGURATION: u8 = 0x22;
pub const CS_STRING: u8 = 0x23;
pub const CS_INTERFACE: u8 = 0x24;
pub const CS_ENDPOINT: u8 = 0x25;
// Descriptor Subtype
pub const AC_DESCRIPTOR_UNDEFINED: u8 = 0x00;
pub const HEADER_SUBTYPE: u8 = 0x01;
pub const INPUT_TERMINAL: u8 = 0x02;
pub const OUTPUT_TERMINAL: u8 = 0x03;
pub const MIXER_UNIT: u8 = 0x04;
pub const SELECTOR_UNIT: u8 = 0x05;
pub const FEATURE_UNIT: u8 = 0x06;
pub const PROCESSING_UNIT: u8 = 0x07;
pub const EXTENSION_UNIT: u8 = 0x08;
// Audio Class-Specific AS Interface Descriptor Subtypes
pub const AS_DESCRIPTOR_UNDEFINED: u8 = 0x00;
pub const AS_GENERAL: u8 = 0x01;
pub const FORMAT_TYPE: u8 = 0x02;
pub const FORMAT_SPECIFIC: u8 = 0x03;
// Processing Unit Process Types
pub const PROCESS_UNDEFINED: u16 = 0x00;
pub const UP_DOWNMIX_PROCESS: u16 = 0x01;
pub const DOLBY_PROLOGIC_PROCESS: u16 = 0x02;
pub const DDD_STEREO_EXTENDER_PROCESS: u16 = 0x03;
pub const REVERBERATION_PROCESS: u16 = 0x04;
pub const CHORUS_PROCESS: u16 = 0x05;
pub const DYN_RANGE_COMP_PROCESS: u16 = 0x06;
// Audio Class-Specific Endpoint Descriptor Subtypes
pub const EP_DESCRIPTOR_UNDEFINED: u8 = 0x00;
pub const EP_GENERAL: u8 = 0x01;
// Audio Class-Specific Request Codes
pub const REQUEST_CODE_UNDEFINED: u8 = 0x00;
pub const SET_CUR: u8 = 0x01;
pub const GET_CUR: u8 = 0x81;
pub const SET_MIN: u8 = 0x02;
pub const GET_MIN: u8 = 0x82;
pub const SET_MAX: u8 = 0x03;
pub const GET_MAX: u8 = 0x83;
pub const SET_RES: u8 = 0x04;
pub const GET_RES: u8 = 0x84;
pub const SET_MEM: u8 = 0x05;
pub const GET_MEM: u8 = 0x85;
pub const GET_STAT: u8 = 0xFF;
// Terminal Control Selectors
pub const TE_CONTROL_UNDEFINED: u8 = 0x00;
pub const COPY_PROTECT_CONTROL: u8 = 0x01;
// Feature Unit Control Selectors
pub const FU_CONTROL_UNDEFINED: u8 = 0x00;
pub const MUTE_CONTROL: u8 = 0x01;
pub const VOLUME_CONTROL: u8 = 0x02;
pub const BASS_CONTROL: u8 = 0x03;
pub const MID_CONTROL: u8 = 0x04;
pub const TREBLE_CONTROL: u8 = 0x05;
pub const GRAPHIC_EQUALIZER_CONTROL: u8 = 0x06;
pub const AUTOMATIC_GAIN_CONTROL: u8 = 0x07;
pub const DELAY_CONTROL: u8 = 0x08;
pub const BASS_BOOST_CONTROL: u8 = 0x09;
pub const LOUDNESS_CONTROL: u8 = 0x0A;
// Up/Down-mix Processing Unit Control Selectors
pub const UD_CONTROL_UNDEFINED: u8 = 0x00;
pub const UD_ENABLE_CONTROL: u8 = 0x01;
pub const UD_MODE_SELECT_CONTROL: u8 = 0x02;
// Dolby Prologic Processing Unit Control Selectors
pub const DP_CONTROL_UNDEFINED: u8 = 0x00;
pub const DP_ENABLE_CONTROL: u8 = 0x01;
pub const DP_MODE_SELECT_CONTROL: u8 = 0x2;
// 3D Stereo Extender Processing Unit Control Selectors
pub const DDD_CONTROL_UNDEFINED: u8 = 0x00;
pub const DDD_ENABLE_CONTROL: u8 = 0x01;
pub const DDD_SPACIOUSNESS_CONTROL: u8 = 0x03;
// Reverberation Processing Unit Control Selectors
pub const RV_CONTROL_UNDEFINED: u8 = 0x00;
pub const RV_ENABLE_CONTROL: u8 = 0x01;
pub const REVERB_LEVEL_CONTROL: u8 = 0x02;
pub const REVERB_TIME_CONTROL: u8 = 0x03;
pub const REVERB_FEEDBACK_CONTROL: u8 = 0x04;
// Chorus Processing Unit Control Selectors
pub const CH_CONTROL_UNDEFINED: u8 = 0x00;
pub const CH_ENABLE_CONTROL: u8 = 0x01;
pub const CHORUS_LEVEL_CONTROL: u8 = 0x02;
pub const CHORUS_RATE_CONTROL: u8 = 0x03;
pub const CHORUS_DEPTH_CONTROL: u8 = 0x04;
// Dynamic Range Compressor Processing Unit Control Selectors
pub const DR_CONTROL_UNDEFINED: u8 = 0x00;
pub const DR_ENABLE_CONTROL: u8 = 0x01;
pub const COMPRESSION_RATE_CONTROL: u8 = 0x02;
pub const MAXAMPL_CONTROL: u8 = 0x03;
pub const THRESHOLD_CONTROL: u8 = 0x04;
pub const ATTACK_TIME: u8 = 0x05;
pub const RELEASE_TIME: u8 = 0x06;
// Extension Unit Control Selectors
pub const XU_CONTROL_UNDEFINED: u16 = 0x00;
pub const XU_ENABLE_CONTROL: u16 = 0x01;
// Endpoint Control Selectors
pub const EP_CONTROL_UNDEFINED: u8 = 0x00;
pub const SAMPLING_FREQ_CONTROL: u8 = 0x01;
pub const PITCH_CONTROL: u8 = 0x02;
// Format Type Codes
pub const FORMAT_TYPE_UNDEFINED: u8 = 0x00;
pub const FORMAT_TYPE_I: u8 = 0x01;
// Audio Data Format Type I Codes
pub const TYPE_I_UNDEFINED: u16 = 0x0000;
pub const PCM: u16 = 0x0001;
pub const PCM8: u16 = 0x0002;
pub const IEEE_FLOAT: u16 = 0x0003;
pub const ALAW: u16 = 0x0004;
pub const MULAW: u16 = 0x0005;

View File

@@ -0,0 +1,134 @@
//! USB Audio Class 1.0 implementations for different applications.
//!
//! Contains:
//! - The `speaker` class with a single audio streaming interface (host to device)
pub mod speaker;
mod class_codes;
mod terminal_type;
/// The maximum supported audio channel index (corresponds to `Top`).
/// FIXME: Use `core::mem::variant_count(...)` when stabilized.
const MAX_AUDIO_CHANNEL_INDEX: usize = 12;
/// The maximum number of supported audio channels.
///
/// Includes all twelve channels from `Channel`, plus the Master channel.
const MAX_AUDIO_CHANNEL_COUNT: usize = MAX_AUDIO_CHANNEL_INDEX + 1;
/// USB Audio Channel
#[derive(Debug, Clone, Copy, PartialEq)]
#[allow(missing_docs)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Channel {
LeftFront,
RightFront,
CenterFront,
Lfe,
LeftSurround,
RightSurround,
LeftOfCenter,
RightOfCenter,
Surround,
SideLeft,
SideRight,
Top,
}
impl Channel {
/// Map a `Channel` to its corresponding USB Audio `ChannelConfig`.
fn get_channel_config(&self) -> ChannelConfig {
match self {
Channel::LeftFront => ChannelConfig::LeftFront,
Channel::RightFront => ChannelConfig::RightFront,
Channel::CenterFront => ChannelConfig::CenterFront,
Channel::Lfe => ChannelConfig::Lfe,
Channel::LeftSurround => ChannelConfig::LeftSurround,
Channel::RightSurround => ChannelConfig::RightSurround,
Channel::LeftOfCenter => ChannelConfig::LeftOfCenter,
Channel::RightOfCenter => ChannelConfig::RightOfCenter,
Channel::Surround => ChannelConfig::Surround,
Channel::SideLeft => ChannelConfig::SideLeft,
Channel::SideRight => ChannelConfig::SideRight,
Channel::Top => ChannelConfig::Top,
}
}
}
/// USB Audio Channel configuration
#[repr(u16)]
#[non_exhaustive]
// #[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum ChannelConfig {
None = 0x0000,
LeftFront = 0x0001,
RightFront = 0x0002,
CenterFront = 0x0004,
Lfe = 0x0008,
LeftSurround = 0x0010,
RightSurround = 0x0020,
LeftOfCenter = 0x0040,
RightOfCenter = 0x0080,
Surround = 0x0100,
SideLeft = 0x0200,
SideRight = 0x0400,
Top = 0x0800,
}
impl From<ChannelConfig> for u16 {
fn from(t: ChannelConfig) -> u16 {
t as u16
}
}
/// Feedback period adjustment `bRefresh` [UAC 3.7.2.2]
///
/// From the specification: "A new Ff value is available every 2^(10 P) frames with P ranging from 1 to 9. The
/// bRefresh field of the synch standard endpoint descriptor is used to report the exponent (10-P) to the Host."
///
/// This means:
/// - 512 ms (2^9 frames) to 2 ms (2^1 frames) for USB full-speed
/// - 64 ms (2^9 microframes) to 0.25 ms (2^1 microframes) for USB high-speed
#[repr(u8)]
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum FeedbackRefresh {
Period2Frames = 1,
Period4Frames = 2,
Period8Frames = 3,
Period16Frames = 4,
Period32Frames = 5,
Period64Frames = 6,
Period128Frames = 7,
Period256Frames = 8,
Period512Frames = 9,
}
impl FeedbackRefresh {
/// Gets the number of frames, after which a new feedback frame is returned.
pub const fn frame_count(&self) -> usize {
1 << (*self as usize)
}
}
/// Audio sample width.
///
/// Stored in number of bytes per sample.
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum SampleWidth {
/// 16 bit audio
Width2Byte = 2,
/// 24 bit audio
Width3Byte = 3,
/// 32 bit audio
Width4Byte = 4,
}
impl SampleWidth {
/// Get the audio sample resolution in number of bit.
pub const fn in_bit(self) -> usize {
8 * self as usize
}
}

View File

@@ -0,0 +1,777 @@
//! USB Audio Class 1.0 - Speaker device
//!
//! Provides a class with a single audio streaming interface (host to device),
//! that advertises itself as a speaker. Includes explicit sample rate feedback.
//!
//! Various aspects of the audio stream can be configured, for example:
//! - sample rate
//! - sample resolution
//! - audio channel count and assignment
//!
//! The class provides volume and mute controls for each channel.
use core::cell::{Cell, RefCell};
use core::future::{poll_fn, Future};
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use core::task::Poll;
use embassy_sync::blocking_mutex::CriticalSectionMutex;
use embassy_sync::waitqueue::WakerRegistration;
use heapless::Vec;
use super::class_codes::*;
use super::terminal_type::TerminalType;
use super::{Channel, ChannelConfig, FeedbackRefresh, SampleWidth, MAX_AUDIO_CHANNEL_COUNT, MAX_AUDIO_CHANNEL_INDEX};
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
use crate::descriptor::{SynchronizationType, UsageType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut, EndpointType};
use crate::types::InterfaceNumber;
use crate::{Builder, Handler};
/// Maximum allowed sampling rate (3 bytes) in Hz.
const MAX_SAMPLE_RATE_HZ: u32 = 0x7FFFFF;
/// Arbitrary unique identifier for the input unit.
const INPUT_UNIT_ID: u8 = 0x01;
/// Arbitrary unique identifier for the feature unit.
const FEATURE_UNIT_ID: u8 = 0x02;
/// Arbitrary unique identifier for the output unit.
const OUTPUT_UNIT_ID: u8 = 0x03;
// Volume settings go from -25600 to 0, in steps of 256.
// Therefore, the volume settings are 8q8 values in units of dB.
const VOLUME_STEPS_PER_DB: i16 = 256;
const MIN_VOLUME_DB: i16 = -100;
const MAX_VOLUME_DB: i16 = 0;
// Maximum number of supported discrete sample rates.
const MAX_SAMPLE_RATE_COUNT: usize = 10;
/// The volume of an audio channel.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Volume {
/// The channel is muted.
Muted,
/// The channel volume in dB. Ranges from `MIN_VOLUME_DB` (quietest) to `MAX_VOLUME_DB` (loudest).
DeciBel(f32),
}
/// Internal state for the USB Audio Class.
pub struct State<'d> {
control: Option<Control<'d>>,
shared: SharedControl<'d>,
}
impl<'d> Default for State<'d> {
fn default() -> Self {
Self::new()
}
}
impl<'d> State<'d> {
/// Create a new `State`.
pub fn new() -> Self {
Self {
control: None,
shared: SharedControl::default(),
}
}
}
/// Implementation of the USB audio class 1.0.
pub struct Speaker<'d, D: Driver<'d>> {
phantom: PhantomData<&'d D>,
}
impl<'d, D: Driver<'d>> Speaker<'d, D> {
/// Creates a new [`Speaker`] device, split into a stream, feedback, and a control change notifier.
///
/// The packet size should be chosen, based on the expected transfer size of samples per (micro)frame.
/// For example, a stereo stream at 32 bit resolution and 48 kHz sample rate yields packets of 384 byte for
/// full-speed USB (1 ms frame interval) or 48 byte for high-speed USB (125 us microframe interval).
/// When using feedback, the packet size varies and thus, the `max_packet_size` should be increased (e.g. to double).
///
/// # Arguments
///
/// * `builder` - The builder for the class.
/// * `state` - The internal state of the class.
/// * `max_packet_size` - The maximum packet size per (micro)frame.
/// * `resolution` - The audio sample resolution.
/// * `sample_rates_hz` - The supported sample rates in Hz.
/// * `channels` - The advertised audio channels (up to 12). Entries must be unique, or this function panics.
/// * `feedback_refresh_period` - The refresh period for the feedback value.
pub fn new(
builder: &mut Builder<'d, D>,
state: &'d mut State<'d>,
max_packet_size: u16,
resolution: SampleWidth,
sample_rates_hz: &[u32],
channels: &'d [Channel],
feedback_refresh_period: FeedbackRefresh,
) -> (Stream<'d, D>, Feedback<'d, D>, ControlMonitor<'d>) {
// The class and subclass fields of the IAD aren't required to match the class and subclass fields of
// the interfaces in the interface collection that the IAD describes. Microsoft recommends that
// the first interface of the collection has class and subclass fields that match the class and
// subclass fields of the IAD.
let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE);
// Audio control interface (mandatory) [UAC 4.3.1]
let mut interface = func.interface();
let control_interface = interface.interface_number().into();
let streaming_interface = u8::from(control_interface) + 1;
let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None);
// Terminal topology:
// Input terminal (receives audio stream) -> Feature Unit (mute and volume) -> Output terminal (e.g. towards speaker)
// =======================================
// Input Terminal Descriptor [UAC 3.3.2.1]
// Audio input
let terminal_type: u16 = TerminalType::UsbStreaming.into();
// Assemble channel configuration field
let mut channel_config: u16 = ChannelConfig::None.into();
for channel in channels {
let channel: u16 = channel.get_channel_config().into();
if channel_config & channel != 0 {
panic!("Invalid channel config, duplicate channel {}.", channel);
}
channel_config |= channel;
}
let input_terminal_descriptor = [
INPUT_TERMINAL, // bDescriptorSubtype
INPUT_UNIT_ID, // bTerminalID
terminal_type as u8,
(terminal_type >> 8) as u8, // wTerminalType
0x00, // bAssocTerminal (none)
channels.len() as u8, // bNrChannels
channel_config as u8,
(channel_config >> 8) as u8, // wChannelConfig
0x00, // iChannelNames (none)
0x00, // iTerminal (none)
];
// ========================================
// Output Terminal Descriptor [UAC 4.3.2.2]
// Speaker output
let terminal_type: u16 = TerminalType::OutSpeaker.into();
let output_terminal_descriptor = [
OUTPUT_TERMINAL, // bDescriptorSubtype
OUTPUT_UNIT_ID, // bTerminalID
terminal_type as u8,
(terminal_type >> 8) as u8, // wTerminalType
0x00, // bAssocTerminal (none)
FEATURE_UNIT_ID, // bSourceID (the feature unit)
0x00, // iTerminal (none)
];
// =====================================
// Feature Unit Descriptor [UAC 4.3.2.5]
// Mute and volume control
let controls = MUTE_CONTROL | VOLUME_CONTROL;
const FEATURE_UNIT_DESCRIPTOR_SIZE: usize = 5;
let mut feature_unit_descriptor: Vec<u8, { FEATURE_UNIT_DESCRIPTOR_SIZE + MAX_AUDIO_CHANNEL_COUNT + 1 }> =
Vec::from_slice(&[
FEATURE_UNIT, // bDescriptorSubtype (Feature Unit)
FEATURE_UNIT_ID, // bUnitID
INPUT_UNIT_ID, // bSourceID
1, // bControlSize (one byte per control)
FU_CONTROL_UNDEFINED, // Master controls (disabled, use only per-channel control)
])
.unwrap();
// Add per-channel controls
for _channel in channels {
feature_unit_descriptor.push(controls).unwrap();
}
feature_unit_descriptor.push(0x00).unwrap(); // iFeature (none)
// ===============================================
// Format desciptor [UAC 4.5.3]
// Used later, for operational streaming interface
let mut format_descriptor: Vec<u8, { 6 + 3 * MAX_SAMPLE_RATE_COUNT }> = Vec::from_slice(&[
FORMAT_TYPE, // bDescriptorSubtype
FORMAT_TYPE_I, // bFormatType
channels.len() as u8, // bNrChannels
resolution as u8, // bSubframeSize
resolution.in_bit() as u8, // bBitResolution
])
.unwrap();
format_descriptor.push(sample_rates_hz.len() as u8).unwrap();
for sample_rate_hz in sample_rates_hz {
assert!(*sample_rate_hz <= MAX_SAMPLE_RATE_HZ);
format_descriptor.push((sample_rate_hz & 0xFF) as u8).unwrap();
format_descriptor.push(((sample_rate_hz >> 8) & 0xFF) as u8).unwrap();
format_descriptor.push(((sample_rate_hz >> 16) & 0xFF) as u8).unwrap();
}
// ==================================================
// Class-specific AC Interface Descriptor [UAC 4.3.2]
const DESCRIPTOR_HEADER_SIZE: usize = 2;
const INTERFACE_DESCRIPTOR_SIZE: usize = 7;
let mut total_descriptor_length = 0;
for size in [
INTERFACE_DESCRIPTOR_SIZE,
input_terminal_descriptor.len(),
feature_unit_descriptor.len(),
output_terminal_descriptor.len(),
] {
total_descriptor_length += size + DESCRIPTOR_HEADER_SIZE;
}
let interface_descriptor: [u8; INTERFACE_DESCRIPTOR_SIZE] = [
HEADER_SUBTYPE, // bDescriptorSubtype (Header)
ADC_VERSION as u8,
(ADC_VERSION >> 8) as u8, // bcdADC
total_descriptor_length as u8,
(total_descriptor_length >> 8) as u8, // wTotalLength
0x01, // bInCollection (1 streaming interface)
streaming_interface, // baInterfaceNr
];
alt.descriptor(CS_INTERFACE, &interface_descriptor);
alt.descriptor(CS_INTERFACE, &input_terminal_descriptor);
alt.descriptor(CS_INTERFACE, &feature_unit_descriptor);
alt.descriptor(CS_INTERFACE, &output_terminal_descriptor);
// =====================================================
// Audio streaming interface, zero-bandwidth [UAC 4.5.1]
let mut interface = func.interface();
let alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None);
drop(alt);
// ==================================================
// Audio streaming interface, operational [UAC 4.5.1]
let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None);
alt.descriptor(
CS_INTERFACE,
&[
AS_GENERAL, // bDescriptorSubtype
INPUT_UNIT_ID, // bTerminalLink
0x00, // bDelay (none)
PCM as u8,
(PCM >> 8) as u8, // wFormatTag (PCM format)
],
);
alt.descriptor(CS_INTERFACE, &format_descriptor);
let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, max_packet_size, 1);
let feedback_endpoint = alt.alloc_endpoint_in(
EndpointType::Isochronous,
4, // Feedback packets are 24 bit (10.14 format).
1,
);
// Write the descriptor for the streaming endpoint, after knowing the address of the feedback endpoint.
alt.endpoint_descriptor(
streaming_endpoint.info(),
SynchronizationType::Asynchronous,
UsageType::DataEndpoint,
&[
0x00, // bRefresh (0)
feedback_endpoint.info().addr.into(), // bSynchAddress (the feedback endpoint)
],
);
alt.descriptor(
CS_ENDPOINT,
&[
AS_GENERAL, // bDescriptorSubtype (General)
SAMPLING_FREQ_CONTROL, // bmAttributes (support sampling frequency control)
0x02, // bLockDelayUnits (PCM)
0x0000 as u8,
(0x0000 >> 8) as u8, // wLockDelay (0)
],
);
// Write the feedback endpoint descriptor after the streaming endpoint descriptor
// This is demanded by the USB audio class specification.
alt.endpoint_descriptor(
feedback_endpoint.info(),
SynchronizationType::NoSynchronization,
UsageType::FeedbackEndpoint,
&[
feedback_refresh_period as u8, // bRefresh
0x00, // bSynchAddress (none)
],
);
// Free up the builder.
drop(func);
// Store channel information
state.shared.channels = channels;
state.control = Some(Control {
shared: &state.shared,
streaming_endpoint_address: streaming_endpoint.info().addr.into(),
control_interface_number: control_interface,
});
builder.handler(state.control.as_mut().unwrap());
let control = &state.shared;
(
Stream { streaming_endpoint },
Feedback { feedback_endpoint },
ControlMonitor { shared: control },
)
}
}
/// Audio settings for the feature unit.
///
/// Contains volume and mute control.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AudioSettings {
/// Channel mute states.
muted: [bool; MAX_AUDIO_CHANNEL_COUNT],
/// Channel volume levels in 8.8 format (in dB).
volume_8q8_db: [i16; MAX_AUDIO_CHANNEL_COUNT],
}
impl Default for AudioSettings {
fn default() -> Self {
AudioSettings {
muted: [true; MAX_AUDIO_CHANNEL_COUNT],
volume_8q8_db: [MAX_VOLUME_DB * VOLUME_STEPS_PER_DB; MAX_AUDIO_CHANNEL_COUNT],
}
}
}
struct Control<'d> {
control_interface_number: InterfaceNumber,
streaming_endpoint_address: u8,
shared: &'d SharedControl<'d>,
}
/// Shared data between [`Control`] and the [`Speaker`] class.
struct SharedControl<'d> {
/// The collection of audio settings (volumes, mute states).
audio_settings: CriticalSectionMutex<Cell<AudioSettings>>,
/// Channel assignments.
channels: &'d [Channel],
/// The audio sample rate in Hz.
sample_rate_hz: AtomicU32,
// Notification mechanism.
waker: RefCell<WakerRegistration>,
changed: AtomicBool,
}
impl<'d> Default for SharedControl<'d> {
fn default() -> Self {
SharedControl {
audio_settings: CriticalSectionMutex::new(Cell::new(AudioSettings::default())),
channels: &[],
sample_rate_hz: AtomicU32::new(0),
waker: RefCell::new(WakerRegistration::new()),
changed: AtomicBool::new(false),
}
}
}
impl<'d> SharedControl<'d> {
fn changed(&self) -> impl Future<Output = ()> + '_ {
poll_fn(|context| {
if self.changed.load(Ordering::Relaxed) {
self.changed.store(false, Ordering::Relaxed);
Poll::Ready(())
} else {
self.waker.borrow_mut().register(context.waker());
Poll::Pending
}
})
}
}
/// Used for reading audio frames.
pub struct Stream<'d, D: Driver<'d>> {
streaming_endpoint: D::EndpointOut,
}
impl<'d, D: Driver<'d>> Stream<'d, D> {
/// Reads a single packet from the OUT endpoint
pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
self.streaming_endpoint.read(data).await
}
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) {
self.streaming_endpoint.wait_enabled().await;
}
}
/// Used for writing sample rate information over the feedback endpoint.
pub struct Feedback<'d, D: Driver<'d>> {
feedback_endpoint: D::EndpointIn,
}
impl<'d, D: Driver<'d>> Feedback<'d, D> {
/// Writes a single packet into the IN endpoint.
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
self.feedback_endpoint.write(data).await
}
/// Waits for the USB host to enable this interface.
pub async fn wait_connection(&mut self) {
self.feedback_endpoint.wait_enabled().await;
}
}
/// Control status change monitor
///
/// Await [`ControlMonitor::changed`] for being notified of configuration changes. Afterwards, the updated
/// configuration settings can be read with [`ControlMonitor::volume`] and [`ControlMonitor::sample_rate_hz`].
pub struct ControlMonitor<'d> {
shared: &'d SharedControl<'d>,
}
impl<'d> ControlMonitor<'d> {
fn audio_settings(&self) -> AudioSettings {
let audio_settings = self.shared.audio_settings.lock(|x| x.get());
audio_settings
}
fn get_logical_channel(&self, search_channel: Channel) -> Option<usize> {
let index = self.shared.channels.iter().position(|&c| c == search_channel)?;
// The logical channels start at one (zero is the master channel).
Some(index + 1)
}
/// Get the volume of a selected channel.
pub fn volume(&self, channel: Channel) -> Option<Volume> {
let channel_index = self.get_logical_channel(channel)?;
if self.audio_settings().muted[channel_index] {
return Some(Volume::Muted);
}
Some(Volume::DeciBel(
(self.audio_settings().volume_8q8_db[channel_index] as f32) / 256.0f32,
))
}
/// Get the streaming endpoint's sample rate in Hz.
pub fn sample_rate_hz(&self) -> u32 {
self.shared.sample_rate_hz.load(Ordering::Relaxed)
}
/// Return a future for when the control settings change.
pub async fn changed(&self) {
self.shared.changed().await;
}
}
impl<'d> Control<'d> {
fn changed(&mut self) {
self.shared.changed.store(true, Ordering::Relaxed);
self.shared.waker.borrow_mut().wake();
}
fn interface_set_mute_state(
&mut self,
audio_settings: &mut AudioSettings,
channel_index: u8,
data: &[u8],
) -> OutResponse {
let mute_state = data[0] != 0;
match channel_index as usize {
..=MAX_AUDIO_CHANNEL_INDEX => {
audio_settings.muted[channel_index as usize] = mute_state;
}
_ => {
debug!("Failed to set channel {} mute state: {}", channel_index, mute_state);
return OutResponse::Rejected;
}
}
debug!("Set channel {} mute state: {}", channel_index, mute_state);
OutResponse::Accepted
}
fn interface_set_volume(
&mut self,
audio_settings: &mut AudioSettings,
channel_index: u8,
data: &[u8],
) -> OutResponse {
let volume = i16::from_ne_bytes(data[..2].try_into().expect("Failed to read volume."));
match channel_index as usize {
..=MAX_AUDIO_CHANNEL_INDEX => {
audio_settings.volume_8q8_db[channel_index as usize] = volume;
}
_ => {
debug!("Failed to set channel {} volume: {}", channel_index, volume);
return OutResponse::Rejected;
}
}
debug!("Set channel {} volume: {}", channel_index, volume);
OutResponse::Accepted
}
fn interface_set_request(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
let interface_number = req.index as u8;
let entity_index = (req.index >> 8) as u8;
let channel_index = req.value as u8;
let control_unit = (req.value >> 8) as u8;
if interface_number != self.control_interface_number.into() {
debug!("Unhandled interface set request for interface {}", interface_number);
return None;
}
if entity_index != FEATURE_UNIT_ID {
debug!("Unsupported interface set request for entity {}", entity_index);
return Some(OutResponse::Rejected);
}
if req.request != SET_CUR {
debug!("Unsupported interface set request type {}", req.request);
return Some(OutResponse::Rejected);
}
let mut audio_settings = self.shared.audio_settings.lock(|x| x.get());
let response = match control_unit {
MUTE_CONTROL => self.interface_set_mute_state(&mut audio_settings, channel_index, data),
VOLUME_CONTROL => self.interface_set_volume(&mut audio_settings, channel_index, data),
_ => OutResponse::Rejected,
};
if response == OutResponse::Rejected {
return Some(response);
}
// Store updated settings
self.shared.audio_settings.lock(|x| x.set(audio_settings));
self.changed();
Some(OutResponse::Accepted)
}
fn endpoint_set_request(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
let control_selector = (req.value >> 8) as u8;
let endpoint_address = req.index as u8;
if endpoint_address != self.streaming_endpoint_address {
debug!(
"Unhandled endpoint set request for endpoint {} and control {} with data {:?}",
endpoint_address, control_selector, data
);
return None;
}
if control_selector != SAMPLING_FREQ_CONTROL {
debug!(
"Unsupported endpoint set request for control selector {}",
control_selector
);
return Some(OutResponse::Rejected);
}
let sample_rate_hz: u32 = (data[0] as u32) | (data[1] as u32) << 8 | (data[2] as u32) << 16;
self.shared.sample_rate_hz.store(sample_rate_hz, Ordering::Relaxed);
debug!("Set endpoint {} sample rate to {} Hz", endpoint_address, sample_rate_hz);
self.changed();
Some(OutResponse::Accepted)
}
fn interface_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option<InResponse<'r>> {
let interface_number = req.index as u8;
let entity_index = (req.index >> 8) as u8;
let channel_index = req.value as u8;
let control_unit = (req.value >> 8) as u8;
if interface_number != self.control_interface_number.into() {
debug!("Unhandled interface get request for interface {}.", interface_number);
return None;
}
if entity_index != FEATURE_UNIT_ID {
// Only this function unit can be handled at the moment.
debug!("Unsupported interface get request for entity {}.", entity_index);
return Some(InResponse::Rejected);
}
let audio_settings = self.shared.audio_settings.lock(|x| x.get());
match req.request {
GET_CUR => match control_unit {
VOLUME_CONTROL => {
let volume: i16;
match channel_index as usize {
..=MAX_AUDIO_CHANNEL_INDEX => volume = audio_settings.volume_8q8_db[channel_index as usize],
_ => return Some(InResponse::Rejected),
}
buf[0] = volume as u8;
buf[1] = (volume >> 8) as u8;
debug!("Got channel {} volume: {}.", channel_index, volume);
return Some(InResponse::Accepted(&buf[..2]));
}
MUTE_CONTROL => {
let mute_state: bool;
match channel_index as usize {
..=MAX_AUDIO_CHANNEL_INDEX => mute_state = audio_settings.muted[channel_index as usize],
_ => return Some(InResponse::Rejected),
}
buf[0] = mute_state.into();
debug!("Got channel {} mute state: {}.", channel_index, mute_state);
return Some(InResponse::Accepted(&buf[..1]));
}
_ => return Some(InResponse::Rejected),
},
GET_MIN => match control_unit {
VOLUME_CONTROL => {
let min_volume = MIN_VOLUME_DB * VOLUME_STEPS_PER_DB;
buf[0] = min_volume as u8;
buf[1] = (min_volume >> 8) as u8;
return Some(InResponse::Accepted(&buf[..2]));
}
_ => return Some(InResponse::Rejected),
},
GET_MAX => match control_unit {
VOLUME_CONTROL => {
let max_volume = MAX_VOLUME_DB * VOLUME_STEPS_PER_DB;
buf[0] = max_volume as u8;
buf[1] = (max_volume >> 8) as u8;
return Some(InResponse::Accepted(&buf[..2]));
}
_ => return Some(InResponse::Rejected),
},
GET_RES => match control_unit {
VOLUME_CONTROL => {
buf[0] = VOLUME_STEPS_PER_DB as u8;
buf[1] = (VOLUME_STEPS_PER_DB >> 8) as u8;
return Some(InResponse::Accepted(&buf[..2]));
}
_ => return Some(InResponse::Rejected),
},
_ => return Some(InResponse::Rejected),
}
}
fn endpoint_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option<InResponse<'r>> {
let control_selector = (req.value >> 8) as u8;
let endpoint_address = req.index as u8;
if endpoint_address != self.streaming_endpoint_address {
debug!("Unhandled endpoint get request for endpoint {}.", endpoint_address);
return None;
}
if control_selector != SAMPLING_FREQ_CONTROL as u8 {
debug!(
"Unsupported endpoint get request for control selector {}.",
control_selector
);
return Some(InResponse::Rejected);
}
let sample_rate_hz = self.shared.sample_rate_hz.load(Ordering::Relaxed);
buf[0] = (sample_rate_hz & 0xFF) as u8;
buf[1] = ((sample_rate_hz >> 8) & 0xFF) as u8;
buf[2] = ((sample_rate_hz >> 16) & 0xFF) as u8;
Some(InResponse::Accepted(&buf[..3]))
}
}
impl<'d> Handler for Control<'d> {
/// Called when the USB device has been enabled or disabled.
fn enabled(&mut self, enabled: bool) {
debug!("USB device enabled: {}", enabled);
}
/// Called when the host has set the address of the device to `addr`.
fn addressed(&mut self, addr: u8) {
debug!("Host set address to: {}", addr);
}
/// Called when the host has enabled or disabled the configuration of the device.
fn configured(&mut self, configured: bool) {
debug!("USB device configured: {}", configured);
}
/// Called when remote wakeup feature is enabled or disabled.
fn remote_wakeup_enabled(&mut self, enabled: bool) {
debug!("USB remote wakeup enabled: {}", enabled);
}
/// Called when a "set alternate setting" control request is done on the interface.
fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) {
debug!(
"USB set interface number {} to alt setting {}.",
iface, alternate_setting
);
}
/// Called after a USB reset after the bus reset sequence is complete.
fn reset(&mut self) {
let shared = self.shared;
shared.audio_settings.lock(|x| x.set(AudioSettings::default()));
shared.changed.store(true, Ordering::Relaxed);
shared.waker.borrow_mut().wake();
}
/// Called when the bus has entered or exited the suspend state.
fn suspended(&mut self, suspended: bool) {
debug!("USB device suspended: {}", suspended);
}
// Handle control set requests.
fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {
match req.request_type {
RequestType::Class => match req.recipient {
Recipient::Interface => self.interface_set_request(req, data),
Recipient::Endpoint => self.endpoint_set_request(req, data),
_ => Some(OutResponse::Rejected),
},
_ => None,
}
}
// Handle control get requests.
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
match req.request_type {
RequestType::Class => match req.recipient {
Recipient::Interface => self.interface_get_request(req, buf),
Recipient::Endpoint => self.endpoint_get_request(req, buf),
_ => None,
},
_ => None,
}
}
}

View File

@@ -0,0 +1,50 @@
//! USB Audio Terminal Types from Universal Serial Bus Device Class Definition
//! for Terminal Types, Release 1.0
/// USB Audio Terminal Types from "Universal Serial Bus Device Class Definition
/// for Terminal Types, Release 1.0"
#[repr(u16)]
#[non_exhaustive]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[allow(missing_docs)]
pub enum TerminalType {
// USB Terminal Types
UsbUndefined = 0x0100,
UsbStreaming = 0x0101,
UsbVendor = 0x01ff,
// Input Terminal Types
InUndefined = 0x0200,
InMicrophone = 0x0201,
InDesktopMicrophone = 0x0202,
InPersonalMicrophone = 0x0203,
InOmniDirectionalMicrophone = 0x0204,
InMicrophoneArray = 0x0205,
InProcessingMicrophoneArray = 0x0206,
// Output Terminal Types
OutUndefined = 0x0300,
OutSpeaker = 0x0301,
OutHeadphones = 0x0302,
OutHeadMountedDisplayAudio = 0x0303,
OutDesktopSpeaker = 0x0304,
OutRoomSpeaker = 0x0305,
OutCommunicationSpeaker = 0x0306,
OutLowFrequencyEffectsSpeaker = 0x0307,
// External Terminal Types
ExtUndefined = 0x0600,
ExtAnalogConnector = 0x0601,
ExtDigitalAudioInterface = 0x0602,
ExtLineConnector = 0x0603,
ExtLegacyAudioConnector = 0x0604,
ExtSpdifConnector = 0x0605,
Ext1394DaStream = 0x0606,
Ext1394DvStreamSoundtrack = 0x0607,
}
impl From<TerminalType> for u16 {
fn from(t: TerminalType) -> u16 {
t as u16
}
}

View File

@@ -0,0 +1,186 @@
//! WebUSB API capability implementation.
//!
//! See https://wicg.github.io/webusb
use core::mem::MaybeUninit;
use crate::control::{InResponse, Recipient, Request, RequestType};
use crate::descriptor::capability_type;
use crate::driver::Driver;
use crate::{Builder, Handler};
const USB_CLASS_VENDOR: u8 = 0xff;
const USB_SUBCLASS_NONE: u8 = 0x00;
const USB_PROTOCOL_NONE: u8 = 0x00;
const WEB_USB_REQUEST_GET_URL: u16 = 0x02;
const WEB_USB_DESCRIPTOR_TYPE_URL: u8 = 0x03;
/// URL descriptor for WebUSB landing page.
///
/// An ecoded URL descriptor to point to a website that is suggested to the user when the device is connected.
pub struct Url<'d>(&'d str, u8);
impl<'d> Url<'d> {
/// Create a new WebUSB URL descriptor.
pub fn new(url: &'d str) -> Self {
let (prefix, stripped_url) = if let Some(stripped) = url.strip_prefix("https://") {
(1, stripped)
} else if let Some(stripped) = url.strip_prefix("http://") {
(0, stripped)
} else {
(255, url)
};
assert!(
stripped_url.len() <= 252,
"URL too long. ({} bytes). Maximum length is 252 bytes.",
stripped_url.len()
);
Self(stripped_url, prefix)
}
fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
fn scheme(&self) -> u8 {
self.1
}
}
/// Configuration for WebUSB.
pub struct Config<'d> {
/// Maximum packet size in bytes for the data endpoints.
///
/// Valid values depend on the speed at which the bus is enumerated.
/// - low speed: 8
/// - full speed: 8, 16, 32, or 64
/// - high speed: 64
pub max_packet_size: u16,
/// URL to navigate to when the device is connected.
///
/// If defined, shows a landing page which the device manufacturer would like the user to visit in order to control their device.
pub landing_url: Option<Url<'d>>,
/// Vendor code for the WebUSB request.
///
/// This value defines the request id (bRequest) the device expects the host to use when issuing control transfers these requests. This can be an arbitrary u8 and is not to be confused with the USB Vendor ID.
pub vendor_code: u8,
}
struct Control<'d> {
ep_buf: [u8; 128],
vendor_code: u8,
landing_url: Option<&'d Url<'d>>,
}
impl<'d> Control<'d> {
fn new(config: &'d Config<'d>) -> Self {
Control {
ep_buf: [0u8; 128],
vendor_code: config.vendor_code,
landing_url: config.landing_url.as_ref(),
}
}
}
impl<'d> Handler for Control<'d> {
fn control_in(&mut self, req: Request, _data: &mut [u8]) -> Option<InResponse> {
let landing_value = if self.landing_url.is_some() { 1 } else { 0 };
if req.request_type == RequestType::Vendor
&& req.recipient == Recipient::Device
&& req.request == self.vendor_code
&& req.value == landing_value
&& req.index == WEB_USB_REQUEST_GET_URL
{
if let Some(url) = self.landing_url {
let url_bytes = url.as_bytes();
let len = url_bytes.len();
self.ep_buf[0] = len as u8 + 3;
self.ep_buf[1] = WEB_USB_DESCRIPTOR_TYPE_URL;
self.ep_buf[2] = url.scheme();
self.ep_buf[3..3 + len].copy_from_slice(url_bytes);
return Some(InResponse::Accepted(&self.ep_buf[..3 + len]));
}
}
None
}
}
/// Internal state for WebUSB
pub struct State<'d> {
control: MaybeUninit<Control<'d>>,
}
impl<'d> Default for State<'d> {
fn default() -> Self {
Self::new()
}
}
impl<'d> State<'d> {
/// Create a new `State`.
pub const fn new() -> Self {
State {
control: MaybeUninit::uninit(),
}
}
}
/// WebUSB capability implementation.
///
/// WebUSB is a W3C standard that allows a web page to communicate with USB devices.
/// See See https://wicg.github.io/webusb for more information and the browser API.
/// This implementation provides one read and one write endpoint.
pub struct WebUsb<'d, D: Driver<'d>> {
_driver: core::marker::PhantomData<&'d D>,
}
impl<'d, D: Driver<'d>> WebUsb<'d, D> {
/// Builder for the WebUSB capability implementation.
///
/// Pass in a USB `Builder`, a `State`, which holds the control endpoint state, and a `Config` for the WebUSB configuration.
pub fn configure(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: &'d Config<'d>) {
let mut func = builder.function(USB_CLASS_VENDOR, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE);
let mut iface = func.interface();
let mut alt = iface.alt_setting(USB_CLASS_VENDOR, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None);
alt.bos_capability(
capability_type::PLATFORM,
&[
// PlatformCapabilityUUID (3408b638-09a9-47a0-8bfd-a0768815b665)
0x0,
0x38,
0xb6,
0x08,
0x34,
0xa9,
0x09,
0xa0,
0x47,
0x8b,
0xfd,
0xa0,
0x76,
0x88,
0x15,
0xb6,
0x65,
// bcdVersion of WebUSB (1.0)
0x00,
0x01,
// bVendorCode
config.vendor_code,
// iLandingPage
if config.landing_url.is_some() { 1 } else { 0 },
],
);
let control = state.control.write(Control::new(config));
drop(func);
builder.handler(control);
}
}

146
embassy-usb/src/control.rs Normal file
View File

@@ -0,0 +1,146 @@
//! USB control data types.
use core::mem;
use crate::driver::Direction;
/// 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::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: Direction,
/// 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;
/// Parses a USB control request from a byte array.
pub fn parse(buf: &[u8; 8]) -> Request {
let rt = buf[0];
let recipient = rt & 0b11111;
Request {
direction: if rt & 0x80 == 0 { Direction::Out } else { Direction::In },
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 const fn descriptor_type_index(&self) -> (u8, u8) {
((self.value >> 8) as u8, self.value as u8)
}
}
/// Response for a CONTROL OUT request.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum OutResponse {
/// The request was accepted.
Accepted,
/// The request was rejected.
Rejected,
}
/// Response for a CONTROL IN request.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InResponse<'a> {
/// The request was accepted. The buffer contains the response data.
Accepted(&'a [u8]),
/// The request was rejected.
Rejected,
}

View File

@@ -0,0 +1,434 @@
//! Utilities for writing USB descriptors.
use embassy_usb_driver::EndpointType;
use crate::builder::Config;
use crate::driver::EndpointInfo;
use crate::types::{InterfaceNumber, StringIndex};
use crate::CONFIGURATION_VALUE;
/// 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 DEVICE_QUALIFIER: u8 = 6;
pub const OTHER_SPEED_CONFIGURATION: u8 = 7;
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;
}
/// USB endpoint synchronization type. The values of this enum can be directly
/// cast into `u8` to get the bmAttributes synchronization type bits.
/// Values other than `NoSynchronization` are only allowed on isochronous endpoints.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SynchronizationType {
/// No synchronization is used.
NoSynchronization = 0b00,
/// Unsynchronized, although sinks provide data rate feedback.
Asynchronous = 0b01,
/// Synchronized using feedback or feedforward data rate information.
Adaptive = 0b10,
/// Synchronized to the USBs SOF.
Synchronous = 0b11,
}
/// USB endpoint usage type. The values of this enum can be directly cast into
/// `u8` to get the bmAttributes usage type bits.
/// Values other than `DataEndpoint` are only allowed on isochronous endpoints.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UsageType {
/// Use the endpoint for regular data transfer.
DataEndpoint = 0b00,
/// Endpoint conveys explicit feedback information for one or more data endpoints.
FeedbackEndpoint = 0b01,
/// A data endpoint that also serves as an implicit feedback endpoint for one or more data endpoints.
ImplicitFeedbackDataEndpoint = 0b10,
/// Reserved usage type.
Reserved = 0b11,
}
/// A writer for USB descriptors.
pub(crate) struct DescriptorWriter<'a> {
pub buf: &'a mut [u8],
position: usize,
num_interfaces_mark: Option<usize>,
num_endpoints_mark: Option<usize>,
}
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,
}
}
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 const fn position(&self) -> usize {
self.position
}
/// Writes an arbitrary (usually class-specific) descriptor with optional extra fields.
pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8], extra_fields: &[u8]) {
let descriptor_length = descriptor.len();
let extra_fields_length = extra_fields.len();
let total_length = descriptor_length + extra_fields_length;
assert!(
(self.position + 2 + total_length) <= self.buf.len() && (total_length + 2) <= 255,
"Descriptor buffer full"
);
self.buf[self.position] = (total_length + 2) as u8;
self.buf[self.position + 1] = descriptor_type;
let start = self.position + 2;
self.buf[start..start + descriptor_length].copy_from_slice(descriptor);
self.buf[start + descriptor_length..start + total_length].copy_from_slice(extra_fields);
self.position = start + total_length;
}
pub(crate) fn configuration(&mut self, config: &Config) {
self.num_interfaces_mark = Some(self.position + 4);
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
],
&[],
);
}
#[allow(unused)]
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,
) {
self.write(
descriptor_type::IAD,
&[
first_interface.into(), // bFirstInterface
interface_count, // bInterfaceCount
function_class,
function_sub_class,
function_protocol,
0,
],
&[],
);
}
/// 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>,
) {
if alternate_setting == 0 {
match self.num_interfaces_mark {
Some(mark) => self.buf[mark] += 1,
None => {
panic!("you can only call `interface/interface_alt` after `configuration`.")
}
};
}
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
],
&[],
);
}
/// Writes an endpoint descriptor.
///
/// # Arguments
///
/// * `endpoint` - Endpoint previously allocated with
/// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder).
/// * `synchronization_type` - The synchronization type of the endpoint.
/// * `usage_type` - The usage type of the endpoint.
/// * `extra_fields` - Additional, class-specific entries at the end of the endpoint descriptor.
pub fn endpoint(
&mut self,
endpoint: &EndpointInfo,
synchronization_type: SynchronizationType,
usage_type: UsageType,
extra_fields: &[u8],
) {
match self.num_endpoints_mark {
Some(mark) => self.buf[mark] += 1,
None => panic!("you can only call `endpoint` after `interface/interface_alt`."),
};
let mut bm_attributes = endpoint.ep_type as u8;
// Synchronization types other than `NoSynchronization`,
// and usage types other than `DataEndpoint`
// are only allowed for isochronous endpoints.
if endpoint.ep_type != EndpointType::Isochronous {
assert_eq!(synchronization_type, SynchronizationType::NoSynchronization);
assert_eq!(usage_type, UsageType::DataEndpoint);
} else {
if usage_type == UsageType::FeedbackEndpoint {
assert_eq!(synchronization_type, SynchronizationType::NoSynchronization)
}
let synchronization_bm_attibutes: u8 = (synchronization_type as u8) << 2;
let usage_bm_attibutes: u8 = (usage_type as u8) << 4;
bm_attributes |= usage_bm_attibutes | synchronization_bm_attibutes;
}
self.write(
descriptor_type::ENDPOINT,
&[
endpoint.addr.into(), // bEndpointAddress
bm_attributes, // bmAttributes
endpoint.max_packet_size as u8,
(endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
endpoint.interval_ms, // bInterval
],
extra_fields,
);
}
/// Writes a string descriptor.
#[allow(unused)]
pub(crate) fn string(&mut self, string: &str) {
let mut pos = self.position;
assert!(pos + 2 <= self.buf.len(), "Descriptor buffer full");
self.buf[pos] = 0; // length placeholder
self.buf[pos + 1] = descriptor_type::STRING;
pos += 2;
for c in string.encode_utf16() {
assert!(pos < self.buf.len(), "Descriptor buffer full");
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;
}
}
/// Create a new Device Descriptor array.
///
/// All device descriptors are always 18 bytes, so there's no need for
/// a variable-length buffer or DescriptorWriter.
pub(crate) fn device_descriptor(config: &Config) -> [u8; 18] {
[
18, // bLength
0x01, // bDescriptorType
config.bcd_usb as u8,
(config.bcd_usb 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.manufacturer.map_or(0, |_| 1), // iManufacturer
config.product.map_or(0, |_| 2), // iProduct
config.serial_number.map_or(0, |_| 3), // iSerialNumber
1, // bNumConfigurations
]
}
/// Create a new Device Qualifier Descriptor array.
///
/// All device qualifier descriptors are always 10 bytes, so there's no need for
/// a variable-length buffer or DescriptorWriter.
pub(crate) fn device_qualifier_descriptor(config: &Config) -> [u8; 10] {
[
10, // bLength
0x06, // bDescriptorType
config.bcd_usb as u8,
(config.bcd_usb 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
1, // bNumConfigurations
0, // Reserved
]
}
/// 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) const fn new(writer: DescriptorWriter<'a>) -> Self {
Self {
writer,
num_caps_mark: None,
}
}
pub(crate) fn bos(&mut self) {
if (self.writer.buf.len() - self.writer.position) < 5 {
return;
}
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]);
}
/// 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]) {
match self.num_caps_mark {
Some(mark) => self.writer.buf[mark] += 1,
None => panic!("called `capability` not between `bos` and `end_bos`."),
}
let mut start = self.writer.position;
let blen = data.len();
assert!(
(start + blen + 3) <= self.writer.buf.len() && (blen + 3) <= 255,
"Descriptor buffer full"
);
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;
}
pub(crate) fn end_bos(&mut self) {
if self.writer.position == 0 {
return;
}
self.num_caps_mark = None;
let position = self.writer.position as u16;
self.writer.buf[2..4].copy_from_slice(&position.to_le_bytes());
}
}

View File

@@ -0,0 +1,111 @@
use crate::descriptor::descriptor_type;
use crate::driver::EndpointAddress;
use crate::types::InterfaceNumber;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ReadError;
pub struct Reader<'a> {
data: &'a [u8],
}
impl<'a> Reader<'a> {
pub const fn new(data: &'a [u8]) -> Self {
Self { data }
}
pub const fn eof(&self) -> bool {
self.data.is_empty()
}
pub fn read<const N: usize>(&mut self) -> Result<[u8; N], ReadError> {
let n = self.data.get(0..N).ok_or(ReadError)?;
self.data = &self.data[N..];
Ok(n.try_into().unwrap())
}
pub fn read_u8(&mut self) -> Result<u8, ReadError> {
Ok(u8::from_le_bytes(self.read()?))
}
pub fn read_u16(&mut self) -> Result<u16, ReadError> {
Ok(u16::from_le_bytes(self.read()?))
}
pub fn read_slice(&mut self, len: usize) -> Result<&'a [u8], ReadError> {
let res = self.data.get(0..len).ok_or(ReadError)?;
self.data = &self.data[len..];
Ok(res)
}
pub fn read_descriptors(&mut self) -> DescriptorIter<'_, 'a> {
DescriptorIter { r: self }
}
}
pub struct DescriptorIter<'a, 'b> {
r: &'a mut Reader<'b>,
}
impl<'a, 'b> Iterator for DescriptorIter<'a, 'b> {
type Item = Result<(u8, Reader<'a>), ReadError>;
fn next(&mut self) -> Option<Self::Item> {
if self.r.eof() {
return None;
}
let len = match self.r.read_u8() {
Ok(x) => x,
Err(e) => return Some(Err(e)),
};
let type_ = match self.r.read_u8() {
Ok(x) => x,
Err(e) => return Some(Err(e)),
};
let data = match self.r.read_slice(len as usize - 2) {
Ok(x) => x,
Err(e) => return Some(Err(e)),
};
Some(Ok((type_, Reader::new(data))))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointInfo {
pub configuration: u8,
pub interface: InterfaceNumber,
pub interface_alt: u8,
pub ep_address: EndpointAddress,
}
pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<(), ReadError> {
let mut ep = EndpointInfo {
configuration: 0,
interface: InterfaceNumber(0),
interface_alt: 0,
ep_address: EndpointAddress::from(0),
};
for res in Reader::new(data).read_descriptors() {
let (kind, mut r) = res?;
match kind {
descriptor_type::CONFIGURATION => {
let _total_length = r.read_u16()?;
let _total_length = r.read_u8()?;
ep.configuration = r.read_u8()?;
}
descriptor_type::INTERFACE => {
ep.interface = InterfaceNumber(r.read_u8()?);
ep.interface_alt = r.read_u8()?;
}
descriptor_type::ENDPOINT => {
ep.ep_address = EndpointAddress::from(r.read_u8()?);
f(ep);
}
_ => {}
}
}
Ok(())
}

270
embassy-usb/src/fmt.rs Normal file
View File

@@ -0,0 +1,270 @@
#![macro_use]
#![allow(unused)]
use core::fmt::{Debug, Display, LowerHex};
#[cfg(all(feature = "defmt", feature = "log"))]
compile_error!("You may not enable both `defmt` and `log` features.");
#[collapse_debuginfo(yes)]
macro_rules! assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_eq!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_ne!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! debug_assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! debug_assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_eq!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! debug_assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_ne!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! todo {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::todo!($($x)*);
#[cfg(feature = "defmt")]
::defmt::todo!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! panic {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::panic!($($x)*);
#[cfg(feature = "defmt")]
::defmt::panic!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
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 ),*);
}
};
}
#[collapse_debuginfo(yes)]
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 ),*);
}
};
}
#[collapse_debuginfo(yes)]
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 ),*);
}
};
}
#[collapse_debuginfo(yes)]
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 ),*);
}
};
}
#[collapse_debuginfo(yes)]
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")]
#[collapse_debuginfo(yes)]
macro_rules! unwrap {
($($x:tt)*) => {
::defmt::unwrap!($($x)*)
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
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
}
}
pub(crate) struct Bytes<'a>(pub &'a [u8]);
impl<'a> Debug for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> Display for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> LowerHex for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
#[cfg(feature = "defmt")]
impl<'a> defmt::Format for Bytes<'a> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "{:02x}", self.0)
}
}

786
embassy-usb/src/lib.rs Normal file
View File

@@ -0,0 +1,786 @@
#![no_std]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
pub use embassy_usb_driver as driver;
mod builder;
pub mod class;
pub mod control;
pub mod descriptor;
mod descriptor_reader;
pub mod msos;
pub mod types;
mod config {
#![allow(unused)]
include!(concat!(env!("OUT_DIR"), "/config.rs"));
}
use embassy_futures::select::{select, Either};
use heapless::Vec;
pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder, UsbVersion};
use crate::config::{MAX_HANDLER_COUNT, MAX_INTERFACE_COUNT};
use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use crate::descriptor::{descriptor_type, lang_id};
use crate::descriptor_reader::foreach_endpoint;
use crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event};
use crate::types::{InterfaceNumber, StringIndex};
/// 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 no power.
Unpowered,
/// The USB device is disabled.
Disabled,
/// The USB device has just been enabled 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,
}
/// Error returned by [`UsbDevice::remote_wakeup`].
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RemoteWakeupError {
/// The USB device is not suspended, or remote wakeup was not enabled.
InvalidState,
/// The underlying driver doesn't support remote wakeup.
Unsupported,
}
impl From<driver::Unsupported> for RemoteWakeupError {
fn from(_: driver::Unsupported) -> Self {
RemoteWakeupError::Unsupported
}
}
/// 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;
const STRING_INDEX_MANUFACTURER: u8 = 1;
const STRING_INDEX_PRODUCT: u8 = 2;
const STRING_INDEX_SERIAL_NUMBER: u8 = 3;
const STRING_INDEX_CUSTOM_START: u8 = 4;
/// Handler for device events and control requests.
///
/// All methods are optional callbacks that will be called by
/// [`UsbDevice::run()`](crate::UsbDevice::run)
pub trait Handler {
/// Called when the USB device has been enabled or disabled.
fn enabled(&mut self, _enabled: bool) {}
/// Called after a USB reset after the bus reset sequence is complete.
fn reset(&mut self) {}
/// Called when the host has set the address of the device to `addr`.
fn addressed(&mut self, _addr: u8) {}
/// Called when the host has enabled or disabled the configuration of the device.
fn configured(&mut self, _configured: bool) {}
/// Called when the bus has entered or exited the suspend state.
fn suspended(&mut self, _suspended: bool) {}
/// Called when remote wakeup feature is enabled or disabled.
fn remote_wakeup_enabled(&mut self, _enabled: bool) {}
/// Called when a "set alternate setting" control request is done on the interface.
fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) {
let _ = iface;
let _ = alternate_setting;
}
/// Called when a control request is received with direction HostToDevice.
///
/// # Arguments
///
/// * `req` - The request from the SETUP packet.
/// * `data` - The data from the request.
///
/// # Returns
///
/// If you didn't handle this request (for example if it's for the wrong interface), return
/// `None`. In this case, the USB stack will continue calling the other handlers, to see
/// if another handles it.
///
/// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack
/// respond to the control request, and stop calling other handlers.
fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> {
let _ = (req, data);
None
}
/// Called when a control request is received with direction DeviceToHost.
///
/// You should write the response somewhere (usually to `buf`, but you may use another buffer
/// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`.
///
/// # Arguments
///
/// * `req` - The request from the SETUP packet.
///
/// # Returns
///
/// If you didn't handle this request (for example if it's for the wrong interface), return
/// `None`. In this case, the USB stack will continue calling the other handlers, to see
/// if another handles it.
///
/// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack
/// respond to the control request, and stop calling other handlers.
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
let _ = (req, buf);
None
}
/// Called when a GET_DESCRIPTOR STRING control request is received.
fn get_string(&mut self, index: StringIndex, lang_id: u16) -> Option<&str> {
let _ = (index, lang_id);
None
}
}
struct Interface {
current_alt_setting: u8,
num_alt_settings: u8,
}
/// A report of the used size of the runtime allocated buffers
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UsbBufferReport {
/// Number of config descriptor bytes used
pub config_descriptor_used: usize,
/// Number of bos descriptor bytes used
pub bos_descriptor_used: usize,
/// Number of msos descriptor bytes used
pub msos_descriptor_used: usize,
/// Size of the control buffer
pub control_buffer_size: usize,
}
/// Main struct for the USB device stack.
pub struct UsbDevice<'d, D: Driver<'d>> {
control_buf: &'d mut [u8],
control: D::ControlPipe,
inner: Inner<'d, D>,
}
struct Inner<'d, D: Driver<'d>> {
bus: D::Bus,
config: Config<'d>,
device_descriptor: [u8; 18],
device_qualifier_descriptor: [u8; 10],
config_descriptor: &'d [u8],
bos_descriptor: &'d [u8],
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
device_state: UsbDeviceState,
suspended: bool,
remote_wakeup_enabled: bool,
self_powered: bool,
/// Our device address, or 0 if none.
address: u8,
/// SET_ADDRESS requests have special handling depending on the driver.
/// This flag indicates that requests must be handled by `ControlPipe::accept_set_address()`
/// instead of regular `accept()`.
set_address_pending: bool,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
}
impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
pub(crate) fn build(
driver: D,
config: Config<'d>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
config_descriptor: &'d [u8],
bos_descriptor: &'d [u8],
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
control_buf: &'d mut [u8],
) -> UsbDevice<'d, D> {
// Start the USB bus.
// This prevent further allocation by consuming the driver.
let (bus, control) = driver.start(config.max_packet_size_0 as u16);
let device_descriptor = descriptor::device_descriptor(&config);
let device_qualifier_descriptor = descriptor::device_qualifier_descriptor(&config);
Self {
control_buf,
control,
inner: Inner {
bus,
config,
device_descriptor,
device_qualifier_descriptor,
config_descriptor,
bos_descriptor,
msos_descriptor,
device_state: UsbDeviceState::Unpowered,
suspended: false,
remote_wakeup_enabled: false,
self_powered: false,
address: 0,
set_address_pending: false,
interfaces,
handlers,
},
}
}
/// Returns a report of the consumed buffers
///
/// Useful for tuning buffer sizes for actual usage
pub fn buffer_usage(&self) -> UsbBufferReport {
UsbBufferReport {
config_descriptor_used: self.inner.config_descriptor.len(),
bos_descriptor_used: self.inner.bos_descriptor.len(),
msos_descriptor_used: self.inner.msos_descriptor.len(),
control_buffer_size: self.control_buf.len(),
}
}
/// Runs the `UsbDevice` forever.
///
/// This future may leave the bus in an invalid state if it is dropped.
/// After dropping the future, [`UsbDevice::disable()`] should be called
/// before calling any other `UsbDevice` methods to fully reset the
/// peripheral.
pub async fn run(&mut self) -> ! {
loop {
self.run_until_suspend().await;
self.wait_resume().await;
}
}
/// Runs the `UsbDevice` until the bus is suspended.
///
/// This future may leave the bus in an invalid state if it is dropped.
/// After dropping the future, [`UsbDevice::disable()`] should be called
/// before calling any other `UsbDevice` methods to fully reset the
/// peripheral.
pub async fn run_until_suspend(&mut self) {
while !self.inner.suspended {
let control_fut = self.control.setup();
let bus_fut = self.inner.bus.poll();
match select(bus_fut, control_fut).await {
Either::First(evt) => self.inner.handle_bus_event(evt).await,
Either::Second(req) => self.handle_control(req).await,
}
}
}
/// Disables the USB peripheral.
pub async fn disable(&mut self) {
if self.inner.device_state != UsbDeviceState::Disabled {
self.inner.bus.disable().await;
self.inner.device_state = UsbDeviceState::Disabled;
self.inner.suspended = false;
self.inner.remote_wakeup_enabled = false;
for h in &mut self.inner.handlers {
h.enabled(false);
}
}
}
/// Waits for a resume condition on the USB bus.
///
/// This future is cancel-safe.
pub async fn wait_resume(&mut self) {
while self.inner.suspended {
let evt = self.inner.bus.poll().await;
self.inner.handle_bus_event(evt).await;
}
}
/// Initiates a device remote wakeup on the USB bus.
///
/// If the bus is not suspended or remote wakeup is not enabled, an error
/// will be returned.
///
/// This future may leave the bus in an inconsistent state if dropped.
/// After dropping the future, [`UsbDevice::disable()`] should be called
/// before calling any other `UsbDevice` methods to fully reset the peripheral.
pub async fn remote_wakeup(&mut self) -> Result<(), RemoteWakeupError> {
if self.inner.suspended && self.inner.remote_wakeup_enabled {
self.inner.bus.remote_wakeup().await?;
self.inner.suspended = false;
for h in &mut self.inner.handlers {
h.suspended(false);
}
Ok(())
} else {
Err(RemoteWakeupError::InvalidState)
}
}
async fn handle_control(&mut self, req: [u8; 8]) {
let req = Request::parse(&req);
trace!("control request: {:?}", req);
match req.direction {
Direction::In => self.handle_control_in(req).await,
Direction::Out => self.handle_control_out(req).await,
}
}
async fn handle_control_in(&mut self, req: Request) {
const DEVICE_DESCRIPTOR_LEN: usize = 18;
let mut resp_length = req.length as usize;
let max_packet_size = self.control.max_packet_size();
// If we don't have an address yet, respond with max 1 packet.
// The host doesn't know our EP0 max packet size yet, and might assume
// a full-length packet is a short packet, thinking we're done sending data.
// See https://github.com/hathach/tinyusb/issues/184
if self.inner.address == 0 && max_packet_size < DEVICE_DESCRIPTOR_LEN && max_packet_size < resp_length {
trace!("received control req while not addressed: capping response to 1 packet.");
resp_length = max_packet_size;
}
match self.inner.handle_control_in(req, self.control_buf) {
InResponse::Accepted(data) => {
let len = data.len().min(resp_length);
let need_zlp = len != resp_length && (len % max_packet_size) == 0;
let chunks = data[0..len]
.chunks(max_packet_size)
.chain(need_zlp.then(|| -> &[u8] { &[] }));
for (first, last, chunk) in first_last(chunks) {
match self.control.data_in(chunk, first, last).await {
Ok(()) => {}
Err(e) => {
warn!("control accept_in failed: {:?}", e);
return;
}
}
}
}
InResponse::Rejected => self.control.reject().await,
}
}
async fn handle_control_out(&mut self, req: Request) {
let req_length = req.length as usize;
let max_packet_size = self.control.max_packet_size();
let mut total = 0;
if req_length > self.control_buf.len() {
warn!(
"got CONTROL OUT with length {} higher than the control_buf len {}, rejecting.",
req_length,
self.control_buf.len()
);
self.control.reject().await;
return;
}
let chunks = self.control_buf[..req_length].chunks_mut(max_packet_size);
for (first, last, chunk) in first_last(chunks) {
let size = match self.control.data_out(chunk, first, last).await {
Ok(x) => x,
Err(e) => {
warn!("usb: failed to read CONTROL OUT data stage: {:?}", e);
return;
}
};
total += size;
if size < max_packet_size || total == req_length {
break;
}
}
let data = &self.control_buf[0..total];
#[cfg(feature = "defmt")]
trace!(" control out data: {:02x}", data);
#[cfg(not(feature = "defmt"))]
trace!(" control out data: {:02x?}", data);
match self.inner.handle_control_out(req, data) {
OutResponse::Accepted => {
if self.inner.set_address_pending {
self.control.accept_set_address(self.inner.address).await;
self.inner.set_address_pending = false;
} else {
self.control.accept().await;
}
}
OutResponse::Rejected => self.control.reject().await,
}
}
}
impl<'d, D: Driver<'d>> Inner<'d, D> {
async fn handle_bus_event(&mut self, evt: Event) {
match evt {
Event::Reset => {
trace!("usb: reset");
self.device_state = UsbDeviceState::Default;
self.suspended = false;
self.remote_wakeup_enabled = false;
self.address = 0;
for h in &mut self.handlers {
h.reset();
}
for (i, iface) in self.interfaces.iter_mut().enumerate() {
iface.current_alt_setting = 0;
for h in &mut self.handlers {
h.set_alternate_setting(InterfaceNumber::new(i as _), 0);
}
}
}
Event::Resume => {
trace!("usb: resume");
self.suspended = false;
for h in &mut self.handlers {
h.suspended(false);
}
}
Event::Suspend => {
trace!("usb: suspend");
self.suspended = true;
for h in &mut self.handlers {
h.suspended(true);
}
}
Event::PowerDetected => {
trace!("usb: power detected");
self.bus.enable().await;
self.device_state = UsbDeviceState::Default;
for h in &mut self.handlers {
h.enabled(true);
}
}
Event::PowerRemoved => {
trace!("usb: power removed");
self.bus.disable().await;
self.device_state = UsbDeviceState::Unpowered;
for h in &mut self.handlers {
h.enabled(false);
}
}
}
}
fn handle_control_out(&mut self, req: Request, data: &[u8]) -> OutResponse {
const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16;
const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16;
match (req.request_type, req.recipient) {
(RequestType::Standard, Recipient::Device) => match (req.request, req.value) {
(Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => {
self.remote_wakeup_enabled = false;
for h in &mut self.handlers {
h.remote_wakeup_enabled(false);
}
OutResponse::Accepted
}
(Request::SET_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => {
self.remote_wakeup_enabled = true;
for h in &mut self.handlers {
h.remote_wakeup_enabled(true);
}
OutResponse::Accepted
}
(Request::SET_ADDRESS, addr @ 1..=127) => {
self.address = addr as u8;
self.set_address_pending = true;
self.device_state = UsbDeviceState::Addressed;
for h in &mut self.handlers {
h.addressed(self.address);
}
OutResponse::Accepted
}
(Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => {
debug!("SET_CONFIGURATION: configured");
self.device_state = UsbDeviceState::Configured;
// Enable all endpoints of selected alt settings.
foreach_endpoint(self.config_descriptor, |ep| {
let iface = &self.interfaces[ep.interface.0 as usize];
self.bus
.endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt);
})
.unwrap();
// Notify handlers.
for h in &mut self.handlers {
h.configured(true);
}
OutResponse::Accepted
}
(Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => {
if self.device_state != UsbDeviceState::Default {
debug!("SET_CONFIGURATION: unconfigured");
self.device_state = UsbDeviceState::Addressed;
// Disable all endpoints.
foreach_endpoint(self.config_descriptor, |ep| {
self.bus.endpoint_set_enabled(ep.ep_address, false);
})
.unwrap();
// Notify handlers.
for h in &mut self.handlers {
h.configured(false);
}
}
OutResponse::Accepted
}
_ => OutResponse::Rejected,
},
(RequestType::Standard, Recipient::Interface) => {
let iface_num = InterfaceNumber::new(req.index as _);
let Some(iface) = self.interfaces.get_mut(iface_num.0 as usize) else {
return OutResponse::Rejected;
};
match req.request {
Request::SET_INTERFACE => {
let new_altsetting = req.value as u8;
if new_altsetting >= iface.num_alt_settings {
warn!("SET_INTERFACE: trying to select alt setting out of range.");
return OutResponse::Rejected;
}
iface.current_alt_setting = new_altsetting;
// Enable/disable EPs of this interface as needed.
foreach_endpoint(self.config_descriptor, |ep| {
if ep.interface == iface_num {
self.bus
.endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt);
}
})
.unwrap();
// TODO check it is valid (not out of range)
for h in &mut self.handlers {
h.set_alternate_setting(iface_num, new_altsetting);
}
OutResponse::Accepted
}
_ => OutResponse::Rejected,
}
}
(RequestType::Standard, Recipient::Endpoint) => match (req.request, req.value) {
(Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
let ep_addr = ((req.index as u8) & 0x8f).into();
self.bus.endpoint_set_stalled(ep_addr, true);
OutResponse::Accepted
}
(Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
let ep_addr = ((req.index as u8) & 0x8f).into();
self.bus.endpoint_set_stalled(ep_addr, false);
OutResponse::Accepted
}
_ => OutResponse::Rejected,
},
_ => self.handle_control_out_delegated(req, data),
}
}
fn handle_control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
match (req.request_type, req.recipient) {
(RequestType::Standard, Recipient::Device) => match req.request {
Request::GET_STATUS => {
let mut status: u16 = 0x0000;
if self.self_powered {
status |= 0x0001;
}
if self.remote_wakeup_enabled {
status |= 0x0002;
}
buf[..2].copy_from_slice(&status.to_le_bytes());
InResponse::Accepted(&buf[..2])
}
Request::GET_DESCRIPTOR => self.handle_get_descriptor(req, buf),
Request::GET_CONFIGURATION => {
let status = match self.device_state {
UsbDeviceState::Configured => CONFIGURATION_VALUE,
_ => CONFIGURATION_NONE,
};
buf[0] = status;
InResponse::Accepted(&buf[..1])
}
_ => InResponse::Rejected,
},
(RequestType::Standard, Recipient::Interface) => {
let Some(iface) = self.interfaces.get_mut(req.index as usize) else {
return InResponse::Rejected;
};
match req.request {
Request::GET_STATUS => {
let status: u16 = 0;
buf[..2].copy_from_slice(&status.to_le_bytes());
InResponse::Accepted(&buf[..2])
}
Request::GET_INTERFACE => {
buf[0] = iface.current_alt_setting;
InResponse::Accepted(&buf[..1])
}
_ => self.handle_control_in_delegated(req, buf),
}
}
(RequestType::Standard, Recipient::Endpoint) => match req.request {
Request::GET_STATUS => {
let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into();
let mut status: u16 = 0x0000;
if self.bus.endpoint_is_stalled(ep_addr) {
status |= 0x0001;
}
buf[..2].copy_from_slice(&status.to_le_bytes());
InResponse::Accepted(&buf[..2])
}
_ => InResponse::Rejected,
},
(RequestType::Vendor, Recipient::Device) => {
if !self.msos_descriptor.is_empty()
&& req.request == self.msos_descriptor.vendor_code()
&& req.index == 7
{
// Index 7 retrieves the MS OS Descriptor Set
InResponse::Accepted(self.msos_descriptor.descriptor())
} else {
self.handle_control_in_delegated(req, buf)
}
}
_ => self.handle_control_in_delegated(req, buf),
}
}
fn handle_control_out_delegated(&mut self, req: Request, data: &[u8]) -> OutResponse {
for h in &mut self.handlers {
if let Some(res) = h.control_out(req, data) {
return res;
}
}
OutResponse::Rejected
}
fn handle_control_in_delegated<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
unsafe fn extend_lifetime<'y>(r: InResponse<'_>) -> InResponse<'y> {
core::mem::transmute(r)
}
for h in &mut self.handlers {
if let Some(res) = h.control_in(req, buf) {
// safety: the borrow checker isn't smart enough to know this pattern (returning a
// borrowed value from inside the loop) is sound. Workaround by unsafely extending lifetime.
// Also, Polonius (the WIP new borrow checker) does accept it.
return unsafe { extend_lifetime(res) };
}
}
InResponse::Rejected
}
fn handle_get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
let (dtype, index) = req.descriptor_type_index();
match dtype {
descriptor_type::BOS => InResponse::Accepted(self.bos_descriptor),
descriptor_type::DEVICE => InResponse::Accepted(&self.device_descriptor),
descriptor_type::CONFIGURATION => InResponse::Accepted(self.config_descriptor),
descriptor_type::STRING => {
if index == 0 {
buf[0] = 4; // len
buf[1] = descriptor_type::STRING;
buf[2] = lang_id::ENGLISH_US as u8;
buf[3] = (lang_id::ENGLISH_US >> 8) as u8;
InResponse::Accepted(&buf[..4])
} else {
let s = match index {
STRING_INDEX_MANUFACTURER => self.config.manufacturer,
STRING_INDEX_PRODUCT => self.config.product,
STRING_INDEX_SERIAL_NUMBER => self.config.serial_number,
_ => {
let mut s = None;
for handler in &mut self.handlers {
let index = StringIndex::new(index);
let lang_id = req.index;
if let Some(res) = handler.get_string(index, lang_id) {
s = Some(res);
break;
}
}
s
}
};
if let Some(s) = s {
assert!(buf.len() >= 2, "control buffer too small");
buf[1] = descriptor_type::STRING;
let mut pos = 2;
for c in s.encode_utf16() {
assert!(pos + 2 < buf.len(), "control buffer too small");
buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
pos += 2;
}
buf[0] = pos as u8;
InResponse::Accepted(&buf[..pos])
} else {
InResponse::Rejected
}
}
}
descriptor_type::DEVICE_QUALIFIER => InResponse::Accepted(&self.device_qualifier_descriptor),
_ => InResponse::Rejected,
}
}
}
fn first_last<T: Iterator>(iter: T) -> impl Iterator<Item = (bool, bool, T::Item)> {
let mut iter = iter.peekable();
let mut first = true;
core::iter::from_fn(move || {
let val = iter.next()?;
let is_first = first;
first = false;
let is_last = iter.peek().is_none();
Some((is_first, is_last, val))
})
}

728
embassy-usb/src/msos.rs Normal file
View File

@@ -0,0 +1,728 @@
//! Microsoft OS Descriptors
//!
//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification>
use core::mem::size_of;
use crate::descriptor::{capability_type, BosWriter};
use crate::types::InterfaceNumber;
/// A serialized Microsoft OS 2.0 Descriptor set.
///
/// Create with [`DeviceDescriptorSetBuilder`].
pub struct MsOsDescriptorSet<'d> {
descriptor: &'d [u8],
vendor_code: u8,
}
impl<'d> MsOsDescriptorSet<'d> {
/// Gets the raw bytes of the MS OS descriptor
pub fn descriptor(&self) -> &[u8] {
self.descriptor
}
/// Gets the vendor code used by the host to retrieve the MS OS descriptor
pub fn vendor_code(&self) -> u8 {
self.vendor_code
}
/// Returns `true` if no MS OS descriptor data is available
pub fn is_empty(&self) -> bool {
self.descriptor.is_empty()
}
/// Returns the length of the descriptor field
pub fn len(&self) -> usize {
self.descriptor.len()
}
}
/// Writes a Microsoft OS 2.0 Descriptor set into a buffer.
pub struct MsOsDescriptorWriter<'d> {
buf: &'d mut [u8],
position: usize,
config_mark: Option<usize>,
function_mark: Option<usize>,
vendor_code: u8,
}
impl<'d> MsOsDescriptorWriter<'d> {
pub(crate) fn new(buf: &'d mut [u8]) -> Self {
MsOsDescriptorWriter {
buf,
position: 0,
config_mark: None,
function_mark: None,
vendor_code: 0,
}
}
pub(crate) fn build(mut self, bos: &mut BosWriter) -> MsOsDescriptorSet<'d> {
self.end();
if self.is_empty() {
MsOsDescriptorSet {
descriptor: &[],
vendor_code: 0,
}
} else {
self.write_bos(bos);
MsOsDescriptorSet {
descriptor: &self.buf[..self.position],
vendor_code: self.vendor_code,
}
}
}
/// Returns `true` if the MS OS descriptor header has not yet been written
pub fn is_empty(&self) -> bool {
self.position == 0
}
/// Returns `true` if a configuration subset header has been started
pub fn is_in_config_subset(&self) -> bool {
self.config_mark.is_some()
}
/// Returns `true` if a function subset header has been started and not yet ended
pub fn is_in_function_subset(&self) -> bool {
self.function_mark.is_some()
}
/// Write the MS OS descriptor set header.
///
/// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`]
/// module.
/// - `vendor_code` is the vendor request code used to read the MS OS descriptor set.
pub fn header(&mut self, windows_version: u32, vendor_code: u8) {
assert!(self.is_empty(), "You can only call MsOsDescriptorWriter::header once");
self.write(DescriptorSetHeader::new(windows_version));
self.vendor_code = vendor_code;
}
/// Add a device level feature descriptor.
///
/// Note that some feature descriptors may only be used at the device level in non-composite devices.
/// Those features must be written before the first call to [`Self::configuration`].
pub fn device_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
assert!(
!self.is_empty(),
"device features may only be added after the header is written"
);
assert!(
self.config_mark.is_none(),
"device features must be added before the first configuration subset"
);
self.write(desc);
}
/// Add a configuration subset.
pub fn configuration(&mut self, config: u8) {
assert!(
!self.is_empty(),
"MsOsDescriptorWriter: configuration must be called after header"
);
Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);
self.config_mark = Some(self.position);
self.write(ConfigurationSubsetHeader::new(config));
}
/// Add a function subset.
pub fn function(&mut self, first_interface: InterfaceNumber) {
assert!(
self.config_mark.is_some(),
"MsOsDescriptorWriter: function subset requires a configuration subset"
);
self.end_function();
self.function_mark = Some(self.position);
self.write(FunctionSubsetHeader::new(first_interface));
}
/// Add a function level feature descriptor.
///
/// Note that some features may only be used at the function level. Those features must be written after a call
/// to [`Self::function`].
pub fn function_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
assert!(
self.function_mark.is_some(),
"function features may only be added to a function subset"
);
self.write(desc);
}
/// Ends the current function subset (if any)
pub fn end_function(&mut self) {
Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);
}
fn write<T: Descriptor>(&mut self, desc: T) {
desc.write_to(&mut self.buf[self.position..]);
self.position += desc.size();
}
fn end_subset<T: DescriptorSet>(buf: &mut [u8], position: usize, mark: &mut Option<usize>) {
if let Some(mark) = mark.take() {
let len = position - mark;
let p = mark + T::LENGTH_OFFSET;
buf[p..(p + 2)].copy_from_slice(&(len as u16).to_le_bytes());
}
}
fn end(&mut self) {
if self.position > 0 {
Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);
Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);
Self::end_subset::<DescriptorSetHeader>(self.buf, self.position, &mut Some(0));
}
}
fn write_bos(&mut self, bos: &mut BosWriter) {
let windows_version = &self.buf[4..8];
let len = (self.position as u16).to_le_bytes();
bos.capability(
capability_type::PLATFORM,
&[
0, // reserved
// platform capability UUID, Microsoft OS 2.0 platform compatibility
0xdf,
0x60,
0xdd,
0xd8,
0x89,
0x45,
0xc7,
0x4c,
0x9c,
0xd2,
0x65,
0x9d,
0x9e,
0x64,
0x8a,
0x9f,
// Minimum compatible Windows version
windows_version[0],
windows_version[1],
windows_version[2],
windows_version[3],
// Descriptor set length
len[0],
len[1],
self.vendor_code,
0x0, // Device does not support alternate enumeration
],
);
}
}
/// Microsoft Windows version codes
///
/// Windows 8.1 is the minimum version allowed for MS OS 2.0 descriptors.
pub mod windows_version {
/// Windows 8.1 (aka `NTDDI_WINBLUE`)
pub const WIN8_1: u32 = 0x06030000;
/// Windows 10
pub const WIN10: u32 = 0x0A000000;
}
/// A trait for descriptors
trait Descriptor: Sized {
const TYPE: DescriptorType;
/// The size of the descriptor's header.
fn size(&self) -> usize {
size_of::<Self>()
}
fn write_to(&self, buf: &mut [u8]);
}
trait DescriptorSet: Descriptor {
const LENGTH_OFFSET: usize;
}
/// Copies the data of `t` into `buf`.
///
/// # Safety
/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct)
unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) {
let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>());
assert!(buf.len() >= bytes.len(), "MS OS descriptor buffer full");
buf[..bytes.len()].copy_from_slice(bytes);
}
/// Table 9. Microsoft OS 2.0 descriptor wDescriptorType values.
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum DescriptorType {
/// MS OS descriptor set header
SetHeaderDescriptor = 0,
/// Configuration subset header
SubsetHeaderConfiguration = 1,
/// Function subset header
SubsetHeaderFunction = 2,
/// Compatible device ID feature descriptor
FeatureCompatibleId = 3,
/// Registry property feature descriptor
FeatureRegProperty = 4,
/// Minimum USB resume time feature descriptor
FeatureMinResumeTime = 5,
/// Vendor revision feature descriptor
FeatureModelId = 6,
/// CCGP device descriptor feature descriptor
FeatureCcgpDevice = 7,
/// Vendor revision feature descriptor
FeatureVendorRevision = 8,
}
/// Table 5. Descriptor set information structure.
#[allow(non_snake_case)]
#[allow(unused)]
#[repr(C, packed(1))]
pub struct DescriptorSetInformation {
dwWindowsVersion: u32,
wMSOSDescriptorSetTotalLength: u16,
bMS_VendorCode: u8,
bAltEnumCode: u8,
}
/// Table 4. Microsoft OS 2.0 platform capability descriptor header.
#[allow(non_snake_case)]
#[allow(unused)]
#[repr(C, packed(1))]
pub struct PlatformDescriptor {
bLength: u8,
bDescriptorType: u8,
bDevCapabilityType: u8,
bReserved: u8,
platformCapabilityUUID: [u8; 16],
descriptor_set_information: DescriptorSetInformation,
}
/// Table 10. Microsoft OS 2.0 descriptor set header.
#[allow(non_snake_case)]
#[repr(C, packed(1))]
pub struct DescriptorSetHeader {
wLength: u16,
wDescriptorType: u16,
dwWindowsVersion: u32,
wTotalLength: u16,
}
impl DescriptorSetHeader {
/// Creates a MS OS descriptor set header.
///
/// `windows_version` is the minimum Windows version the descriptor set can apply to.
pub fn new(windows_version: u32) -> Self {
DescriptorSetHeader {
wLength: (size_of::<Self>() as u16).to_le(),
wDescriptorType: (Self::TYPE as u16).to_le(),
dwWindowsVersion: windows_version.to_le(),
wTotalLength: 0,
}
}
}
impl Descriptor for DescriptorSetHeader {
const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor;
fn write_to(&self, buf: &mut [u8]) {
unsafe { transmute_write_to(self, buf) }
}
}
impl DescriptorSet for DescriptorSetHeader {
const LENGTH_OFFSET: usize = 8;
}
/// Table 11. Configuration subset header.
#[allow(non_snake_case)]
#[repr(C, packed(1))]
pub struct ConfigurationSubsetHeader {
wLength: u16,
wDescriptorType: u16,
bConfigurationValue: u8,
bReserved: u8,
wTotalLength: u16,
}
impl ConfigurationSubsetHeader {
/// Creates a configuration subset header
pub fn new(config: u8) -> Self {
ConfigurationSubsetHeader {
wLength: (size_of::<Self>() as u16).to_le(),
wDescriptorType: (Self::TYPE as u16).to_le(),
bConfigurationValue: config,
bReserved: 0,
wTotalLength: 0,
}
}
}
impl Descriptor for ConfigurationSubsetHeader {
const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration;
fn write_to(&self, buf: &mut [u8]) {
unsafe { transmute_write_to(self, buf) }
}
}
impl DescriptorSet for ConfigurationSubsetHeader {
const LENGTH_OFFSET: usize = 6;
}
/// Table 12. Function subset header.
#[allow(non_snake_case)]
#[repr(C, packed(1))]
pub struct FunctionSubsetHeader {
wLength: u16,
wDescriptorType: u16,
bFirstInterface: InterfaceNumber,
bReserved: u8,
wSubsetLength: u16,
}
impl FunctionSubsetHeader {
/// Creates a function subset header
pub fn new(first_interface: InterfaceNumber) -> Self {
FunctionSubsetHeader {
wLength: (size_of::<Self>() as u16).to_le(),
wDescriptorType: (Self::TYPE as u16).to_le(),
bFirstInterface: first_interface,
bReserved: 0,
wSubsetLength: 0,
}
}
}
impl Descriptor for FunctionSubsetHeader {
const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction;
fn write_to(&self, buf: &mut [u8]) {
unsafe { transmute_write_to(self, buf) }
}
}
impl DescriptorSet for FunctionSubsetHeader {
const LENGTH_OFFSET: usize = 6;
}
// Feature Descriptors
/// A marker trait for feature descriptors that are valid at the device level.
#[allow(private_bounds)]
pub trait DeviceLevelDescriptor: Descriptor {}
/// A marker trait for feature descriptors that are valid at the function level.
#[allow(private_bounds)]
pub trait FunctionLevelDescriptor: Descriptor {}
/// Table 13. Microsoft OS 2.0 compatible ID descriptor.
#[allow(non_snake_case)]
#[repr(C, packed(1))]
pub struct CompatibleIdFeatureDescriptor {
wLength: u16,
wDescriptorType: u16,
compatibleId: [u8; 8],
subCompatibleId: [u8; 8],
}
impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {}
impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor {}
impl Descriptor for CompatibleIdFeatureDescriptor {
const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId;
fn write_to(&self, buf: &mut [u8]) {
unsafe { transmute_write_to(self, buf) }
}
}
impl CompatibleIdFeatureDescriptor {
/// Creates a compatible ID feature descriptor
///
/// The ids must be 8 ASCII bytes or fewer.
pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self {
assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8);
let mut cid = [0u8; 8];
cid[..compatible_id.len()].copy_from_slice(compatible_id.as_bytes());
let mut scid = [0u8; 8];
scid[..sub_compatible_id.len()].copy_from_slice(sub_compatible_id.as_bytes());
Self::new_raw(cid, scid)
}
fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self {
Self {
wLength: (size_of::<Self>() as u16).to_le(),
wDescriptorType: (Self::TYPE as u16).to_le(),
compatibleId: compatible_id,
subCompatibleId: sub_compatible_id,
}
}
}
/// Table 14. Microsoft OS 2.0 registry property descriptor
#[allow(non_snake_case)]
pub struct RegistryPropertyFeatureDescriptor<'a> {
name: &'a str,
data: PropertyData<'a>,
}
/// Data values that can be encoded into a registry property descriptor
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PropertyData<'a> {
/// A registry property containing a string.
Sz(&'a str),
/// A registry property containing a string that expands environment variables.
ExpandSz(&'a str),
/// A registry property containing binary data.
Binary(&'a [u8]),
/// A registry property containing a little-endian 32-bit integer.
DwordLittleEndian(u32),
/// A registry property containing a big-endian 32-bit integer.
DwordBigEndian(u32),
/// A registry property containing a string that contains a symbolic link.
Link(&'a str),
/// A registry property containing multiple strings.
RegMultiSz(&'a [&'a str]),
}
fn write_bytes(val: &[u8], buf: &mut [u8]) -> usize {
assert!(buf.len() >= val.len());
buf[..val.len()].copy_from_slice(val);
val.len()
}
fn write_utf16(val: &str, buf: &mut [u8]) -> usize {
let mut pos = 0;
for c in val.encode_utf16() {
pos += write_bytes(&c.to_le_bytes(), &mut buf[pos..]);
}
pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])
}
impl<'a> PropertyData<'a> {
/// Gets the `PropertyDataType` for this property value
pub fn kind(&self) -> PropertyDataType {
match self {
PropertyData::Sz(_) => PropertyDataType::Sz,
PropertyData::ExpandSz(_) => PropertyDataType::ExpandSz,
PropertyData::Binary(_) => PropertyDataType::Binary,
PropertyData::DwordLittleEndian(_) => PropertyDataType::DwordLittleEndian,
PropertyData::DwordBigEndian(_) => PropertyDataType::DwordBigEndian,
PropertyData::Link(_) => PropertyDataType::Link,
PropertyData::RegMultiSz(_) => PropertyDataType::RegMultiSz,
}
}
/// Gets the size (in bytes) of this property value when encoded.
pub fn size(&self) -> usize {
match self {
PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => {
core::mem::size_of::<u16>() * (val.encode_utf16().count() + 1)
}
PropertyData::Binary(val) => val.len(),
PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val),
PropertyData::RegMultiSz(val) => {
core::mem::size_of::<u16>() * (val.iter().map(|x| x.encode_utf16().count() + 1).sum::<usize>() + 1)
}
}
}
/// Encodes the data for this property value and writes it to `buf`.
pub fn write(&self, buf: &mut [u8]) -> usize {
match self {
PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => write_utf16(val, buf),
PropertyData::Binary(val) => write_bytes(val, buf),
PropertyData::DwordLittleEndian(val) => write_bytes(&val.to_le_bytes(), buf),
PropertyData::DwordBigEndian(val) => write_bytes(&val.to_be_bytes(), buf),
PropertyData::RegMultiSz(val) => {
let mut pos = 0;
for s in *val {
pos += write_utf16(s, &mut buf[pos..]);
}
pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])
}
}
}
}
/// Table 15. wPropertyDataType values for the Microsoft OS 2.0 registry property descriptor.
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum PropertyDataType {
/// A registry property containing a string.
Sz = 1,
/// A registry property containing a string that expands environment variables.
ExpandSz = 2,
/// A registry property containing binary data.
Binary = 3,
/// A registry property containing a little-endian 32-bit integer.
DwordLittleEndian = 4,
/// A registry property containing a big-endian 32-bit integer.
DwordBigEndian = 5,
/// A registry property containing a string that contains a symbolic link.
Link = 6,
/// A registry property containing multiple strings.
RegMultiSz = 7,
}
impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> {
const TYPE: DescriptorType = DescriptorType::FeatureRegProperty;
fn size(&self) -> usize {
10 + self.name_size() + self.data.size()
}
fn write_to(&self, buf: &mut [u8]) {
assert!(buf.len() >= self.size(), "MS OS descriptor buffer full");
let mut pos = 0;
pos += write_bytes(&(self.size() as u16).to_le_bytes(), &mut buf[pos..]);
pos += write_bytes(&(Self::TYPE as u16).to_le_bytes(), &mut buf[pos..]);
pos += write_bytes(&(self.data.kind() as u16).to_le_bytes(), &mut buf[pos..]);
pos += write_bytes(&(self.name_size() as u16).to_le_bytes(), &mut buf[pos..]);
pos += write_utf16(self.name, &mut buf[pos..]);
pos += write_bytes(&(self.data.size() as u16).to_le_bytes(), &mut buf[pos..]);
self.data.write(&mut buf[pos..]);
}
}
impl<'a> RegistryPropertyFeatureDescriptor<'a> {
/// A registry property.
pub fn new(name: &'a str, data: PropertyData<'a>) -> Self {
Self { name, data }
}
fn name_size(&self) -> usize {
core::mem::size_of::<u16>() * (self.name.encode_utf16().count() + 1)
}
}
/// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor.
#[allow(non_snake_case)]
#[repr(C, packed(1))]
pub struct MinimumRecoveryTimeDescriptor {
wLength: u16,
wDescriptorType: u16,
bResumeRecoveryTime: u8,
bResumeSignalingTime: u8,
}
impl DeviceLevelDescriptor for MinimumRecoveryTimeDescriptor {}
impl Descriptor for MinimumRecoveryTimeDescriptor {
const TYPE: DescriptorType = DescriptorType::FeatureMinResumeTime;
fn write_to(&self, buf: &mut [u8]) {
unsafe { transmute_write_to(self, buf) }
}
}
impl MinimumRecoveryTimeDescriptor {
/// Times are in milliseconds.
///
/// `resume_recovery_time` must be >= 0 and <= 10.
/// `resume_signaling_time` must be >= 1 and <= 20.
pub fn new(resume_recovery_time: u8, resume_signaling_time: u8) -> Self {
assert!(resume_recovery_time <= 10);
assert!(resume_signaling_time >= 1 && resume_signaling_time <= 20);
Self {
wLength: (size_of::<Self>() as u16).to_le(),
wDescriptorType: (Self::TYPE as u16).to_le(),
bResumeRecoveryTime: resume_recovery_time,
bResumeSignalingTime: resume_signaling_time,
}
}
}
/// Table 17. Microsoft OS 2.0 model ID descriptor.
#[allow(non_snake_case)]
#[repr(C, packed(1))]
pub struct ModelIdDescriptor {
wLength: u16,
wDescriptorType: u16,
modelId: [u8; 16],
}
impl DeviceLevelDescriptor for ModelIdDescriptor {}
impl Descriptor for ModelIdDescriptor {
const TYPE: DescriptorType = DescriptorType::FeatureModelId;
fn write_to(&self, buf: &mut [u8]) {
unsafe { transmute_write_to(self, buf) }
}
}
impl ModelIdDescriptor {
/// Creates a new model ID descriptor
///
/// `model_id` should be a uuid that uniquely identifies a physical device.
pub fn new(model_id: u128) -> Self {
Self {
wLength: (size_of::<Self>() as u16).to_le(),
wDescriptorType: (Self::TYPE as u16).to_le(),
modelId: model_id.to_le_bytes(),
}
}
}
/// Table 18. Microsoft OS 2.0 CCGP device descriptor.
#[allow(non_snake_case)]
#[repr(C, packed(1))]
pub struct CcgpDeviceDescriptor {
wLength: u16,
wDescriptorType: u16,
}
impl DeviceLevelDescriptor for CcgpDeviceDescriptor {}
impl Descriptor for CcgpDeviceDescriptor {
const TYPE: DescriptorType = DescriptorType::FeatureCcgpDevice;
fn write_to(&self, buf: &mut [u8]) {
unsafe { transmute_write_to(self, buf) }
}
}
impl CcgpDeviceDescriptor {
/// Creates a new CCGP device descriptor
pub fn new() -> Self {
Self {
wLength: (size_of::<Self>() as u16).to_le(),
wDescriptorType: (Self::TYPE as u16).to_le(),
}
}
}
/// Table 19. Microsoft OS 2.0 vendor revision descriptor.
#[allow(non_snake_case)]
#[repr(C, packed(1))]
pub struct VendorRevisionDescriptor {
wLength: u16,
wDescriptorType: u16,
/// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or
/// other MS OS descriptor. Shell set to greater than or equal to 1.
VendorRevision: u16,
}
impl DeviceLevelDescriptor for VendorRevisionDescriptor {}
impl FunctionLevelDescriptor for VendorRevisionDescriptor {}
impl Descriptor for VendorRevisionDescriptor {
const TYPE: DescriptorType = DescriptorType::FeatureVendorRevision;
fn write_to(&self, buf: &mut [u8]) {
unsafe { transmute_write_to(self, buf) }
}
}
impl VendorRevisionDescriptor {
/// Creates a new vendor revision descriptor
pub fn new(revision: u16) -> Self {
assert!(revision >= 1);
Self {
wLength: (size_of::<Self>() as u16).to_le(),
wDescriptorType: (Self::TYPE as u16).to_le(),
VendorRevision: revision.to_le(),
}
}
}

43
embassy-usb/src/types.rs Normal file
View File

@@ -0,0 +1,43 @@
//! USB types.
/// A handle for a USB interface that contains its number.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(transparent)]
pub struct InterfaceNumber(pub u8);
impl InterfaceNumber {
pub(crate) const fn new(index: u8) -> InterfaceNumber {
InterfaceNumber(index)
}
}
impl From<InterfaceNumber> for u8 {
fn from(n: InterfaceNumber) -> u8 {
n.0
}
}
impl core::fmt::Display for InterfaceNumber {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}
/// A handle for a USB string descriptor that contains its index.
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(transparent)]
pub struct StringIndex(pub u8);
impl StringIndex {
pub(crate) const fn new(index: u8) -> StringIndex {
StringIndex(index)
}
}
impl From<StringIndex> for u8 {
fn from(i: StringIndex) -> u8 {
i.0
}
}