Merge branch 'master' into usb_msc

This commit is contained in:
chemicstry
2023-02-27 01:19:52 +02:00
142 changed files with 3965 additions and 1165 deletions

View File

@ -13,8 +13,31 @@ target = "thumbv7em-none-eabi"
[features]
defmt = ["dep:defmt", "embassy-usb-driver/defmt"]
usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"]
msos-descriptor = []
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" }

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

@ -0,0 +1,44 @@
# embassy-usb
TODO crate description
## 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.
## Minimum supported Rust version (MSRV)
This crate requires nightly Rust, due to using "async fn in trait" support.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.

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

@ -0,0 +1,94 @@
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 {
if cfg.seen_feature {
panic!("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)

View File

@ -1,10 +1,12 @@
use heapless::Vec;
use crate::control::ControlHandler;
use crate::config::*;
use crate::descriptor::{BosWriter, DescriptorWriter};
use crate::driver::{Driver, Endpoint, EndpointType};
#[cfg(feature = "msos-descriptor")]
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
use crate::types::*;
use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -120,8 +122,8 @@ impl<'a> Config<'a> {
/// [`UsbDevice`] builder.
pub struct Builder<'d, D: Driver<'d>> {
config: Config<'d>,
handler: Option<&'d dyn DeviceStateHandler>,
interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
control_buf: &'d mut [u8],
driver: D,
@ -130,6 +132,9 @@ pub struct Builder<'d, D: Driver<'d>> {
device_descriptor: DescriptorWriter<'d>,
config_descriptor: DescriptorWriter<'d>,
bos_descriptor: BosWriter<'d>,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: MsOsDescriptorWriter<'d>,
}
impl<'d, D: Driver<'d>> Builder<'d, D> {
@ -144,8 +149,8 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
device_descriptor_buf: &'d mut [u8],
config_descriptor_buf: &'d mut [u8],
bos_descriptor_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8],
control_buf: &'d mut [u8],
handler: Option<&'d dyn DeviceStateHandler>,
) -> Self {
// Magic values specified in USB-IF ECN on IADs.
if config.composite_with_iads
@ -173,32 +178,40 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
Builder {
driver,
handler,
config,
interfaces: Vec::new(),
handlers: Vec::new(),
control_buf,
next_string_index: STRING_INDEX_CUSTOM_START,
device_descriptor,
config_descriptor,
bos_descriptor,
#[cfg(feature = "msos-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> {
#[cfg(feature = "msos-descriptor")]
let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);
self.config_descriptor.end_configuration();
self.bos_descriptor.end_bos();
UsbDevice::build(
self.driver,
self.config,
self.handler,
self.handlers,
self.device_descriptor.into_buf(),
self.config_descriptor.into_buf(),
self.bos_descriptor.writer.into_buf(),
self.interfaces,
self.control_buf,
#[cfg(feature = "msos-descriptor")]
msos_descriptor,
)
}
@ -215,14 +228,10 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
///
/// 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(
InterfaceNumber::new(self.interfaces.len() as _),
0,
class,
subclass,
protocol,
);
self.config_descriptor
.iad(first_interface, 0, class, subclass, protocol);
Some(self.config_descriptor.position() - 5)
} else {
@ -232,8 +241,52 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
FunctionBuilder {
builder: self,
iface_count_index,
#[cfg(feature = "msos-descriptor")]
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) {
if self.handlers.push(handler).is_err() {
panic!(
"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)
}
#[cfg(feature = "msos-descriptor")]
/// 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);
}
#[cfg(feature = "msos-descriptor")]
/// 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);
}
#[cfg(feature = "msos-descriptor")]
/// 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.
@ -244,6 +297,16 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {
builder: &'a mut Builder<'d, D>,
iface_count_index: Option<usize>,
#[cfg(feature = "msos-descriptor")]
first_interface: InterfaceNumber,
}
impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {
fn drop(&mut self) {
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.end_function();
}
}
impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
@ -257,14 +320,15 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
let number = self.builder.interfaces.len() as _;
let iface = Interface {
handler: None,
current_alt_setting: 0,
num_alt_settings: 0,
num_strings: 0,
};
if self.builder.interfaces.push(iface).is_err() {
panic!("max interface count reached")
panic!(
"embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
MAX_INTERFACE_COUNT
)
}
InterfaceBuilder {
@ -273,6 +337,21 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
next_alt_setting_number: 0,
}
}
#[cfg(feature = "msos-descriptor")]
/// 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);
}
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.function_feature(desc);
}
}
/// Interface builder.
@ -288,17 +367,9 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {
self.interface_number
}
pub fn handler(&mut self, handler: &'d mut dyn ControlHandler) {
self.builder.interfaces[self.interface_number.0 as usize].handler = Some(handler);
}
/// Allocates a new string index.
pub fn string(&mut self) -> StringIndex {
let index = self.builder.next_string_index;
self.builder.next_string_index += 1;
self.builder.interfaces[self.interface_number.0 as usize].num_strings += 1;
StringIndex::new(index)
self.builder.string()
}
/// Add an alternate setting to the interface and write its descriptor.
@ -306,14 +377,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {
/// 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) -> InterfaceAltBuilder<'_, 'd, D> {
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, None);
self.builder.config_descriptor.interface_alt(
self.interface_number,
number,
class,
subclass,
protocol,
interface_string,
);
InterfaceAltBuilder {
builder: self.builder,
@ -349,11 +431,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
self.builder.config_descriptor.write(descriptor_type, descriptor)
}
fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointIn {
fn 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)
.alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
.expect("alloc_endpoint_in failed");
self.builder.config_descriptor.endpoint(ep.info());
@ -361,11 +443,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
ep
}
fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointOut {
fn 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)
.alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
.expect("alloc_endpoint_out failed");
self.builder.config_descriptor.endpoint(ep.info());
@ -393,12 +475,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
///
/// 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: u8) -> D::EndpointIn {
self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval)
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)
}
/// Allocate a INTERRUPT OUT endpoint and write its descriptor.
pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut {
self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval)
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)
}
/// 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) -> D::EndpointIn {
self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms)
}
/// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms)
}
}

View File

@ -1,13 +1,15 @@
//! CDC-ACM class implementation, aka Serial over USB.
use core::cell::Cell;
use core::mem::{self, MaybeUninit};
use core::sync::atomic::{AtomicBool, Ordering};
use embassy_sync::blocking_mutex::CriticalSectionMutex;
use crate::control::{self, ControlHandler, InResponse, OutResponse, Request};
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::*;
use crate::Builder;
use crate::{Builder, Handler};
/// This should be used as `device_class` when building the `UsbDevice`.
pub const USB_CLASS_CDC: u8 = 0x02;
@ -28,12 +30,14 @@ 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> State<'a> {
/// Create a new `State`.
pub fn new() -> Self {
Self {
control: MaybeUninit::uninit(),
@ -63,6 +67,7 @@ pub struct CdcAcmClass<'d, D: Driver<'d>> {
}
struct Control<'a> {
comm_if: InterfaceNumber,
shared: &'a ControlShared,
}
@ -94,7 +99,7 @@ impl<'a> Control<'a> {
}
}
impl<'d> ControlHandler for Control<'d> {
impl<'d> Handler for Control<'d> {
fn reset(&mut self) {
let shared = self.shared();
shared.line_coding.lock(|x| x.set(LineCoding::default()));
@ -102,12 +107,18 @@ impl<'d> ControlHandler for Control<'d> {
shared.rts.store(false, Ordering::Relaxed);
}
fn control_out(&mut self, req: control::Request, data: &[u8]) -> OutResponse {
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.
OutResponse::Accepted
Some(OutResponse::Accepted)
}
REQ_SET_LINE_CODING if data.len() >= 7 => {
let coding = LineCoding {
@ -119,7 +130,7 @@ impl<'d> ControlHandler for Control<'d> {
self.shared().line_coding.lock(|x| x.set(coding));
debug!("Set line coding to: {:?}", coding);
OutResponse::Accepted
Some(OutResponse::Accepted)
}
REQ_SET_CONTROL_LINE_STATE => {
let dtr = (req.value & 0x0001) != 0;
@ -130,13 +141,19 @@ impl<'d> ControlHandler for Control<'d> {
shared.rts.store(rts, Ordering::Relaxed);
debug!("Set dtr {}, rts {}", dtr, rts);
OutResponse::Accepted
Some(OutResponse::Accepted)
}
_ => OutResponse::Rejected,
_ => Some(OutResponse::Rejected),
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
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 => {
@ -147,9 +164,9 @@ impl<'d> ControlHandler for Control<'d> {
buf[4] = coding.stop_bits as u8;
buf[5] = coding.parity_type as u8;
buf[6] = coding.data_bits;
InResponse::Accepted(&buf[0..7])
Some(InResponse::Accepted(&buf[0..7]))
}
_ => InResponse::Rejected,
_ => Some(InResponse::Rejected),
}
}
}
@ -158,20 +175,15 @@ 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 {
let control = state.control.write(Control { shared: &state.shared });
let control_shared = &state.shared;
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();
iface.handler(control);
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);
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None);
alt.descriptor(
CS_INTERFACE,
@ -205,10 +217,20 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
// 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);
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,
@ -284,10 +306,15 @@ impl From<u8> for StopBits {
#[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,
}

View File

@ -1,3 +1,4 @@
//! [`embassy-net`](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;
@ -5,11 +6,13 @@ 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(),
@ -17,6 +20,9 @@ impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_
}
}
/// 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>,
@ -24,6 +30,9 @@ pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
}
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 {
@ -66,9 +75,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
// 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`](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>,

View File

@ -1,25 +1,26 @@
/// CDC-NCM, aka Ethernet over USB.
///
/// # Compatibility
///
/// Windows: NOT supported in Windows 10. Supported 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
//! 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::intrinsics::copy_nonoverlapping;
use core::mem::{size_of, MaybeUninit};
use crate::control::{self, ControlHandler, InResponse, OutResponse, Request};
use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::*;
use crate::Builder;
use crate::{Builder, Handler};
pub mod embassy_net;
@ -114,17 +115,17 @@ fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
&buf[..len]
}
/// Internal state for the CDC-NCM class.
pub struct State<'a> {
comm_control: MaybeUninit<CommControl<'a>>,
data_control: MaybeUninit<DataControl>,
control: MaybeUninit<Control<'a>>,
shared: ControlShared,
}
impl<'a> State<'a> {
/// Create a new `State`.
pub fn new() -> Self {
Self {
comm_control: MaybeUninit::uninit(),
data_control: MaybeUninit::uninit(),
control: MaybeUninit::uninit(),
shared: Default::default(),
}
}
@ -141,29 +142,55 @@ impl Default for ControlShared {
}
}
struct CommControl<'a> {
struct Control<'a> {
mac_addr_string: StringIndex,
shared: &'a ControlShared,
mac_addr_str: [u8; 12],
comm_if: InterfaceNumber,
data_if: InterfaceNumber,
}
impl<'d> ControlHandler for CommControl<'d> {
fn control_out(&mut self, req: control::Request, _data: &[u8]) -> OutResponse {
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.
OutResponse::Accepted
Some(OutResponse::Accepted)
}
REQ_SET_NTB_INPUT_SIZE => {
// TODO
OutResponse::Accepted
Some(OutResponse::Accepted)
}
_ => OutResponse::Rejected,
_ => Some(OutResponse::Rejected),
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
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 {
@ -184,9 +211,9 @@ impl<'d> ControlHandler for CommControl<'d> {
max_datagram_count: 1, // We only decode 1 packet per NTB
},
};
InResponse::Accepted(byteify(buf, res))
Some(InResponse::Accepted(byteify(buf, res)))
}
_ => InResponse::Rejected,
_ => Some(InResponse::Rejected),
}
}
@ -211,18 +238,7 @@ impl<'d> ControlHandler for CommControl<'d> {
}
}
struct DataControl {}
impl ControlHandler for DataControl {
fn set_alternate_setting(&mut self, alternate_setting: u8) {
match alternate_setting {
ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"),
ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"),
_ => unreachable!(),
}
}
}
/// CDC-NCM class
pub struct CdcNcmClass<'d, D: Driver<'d>> {
_comm_if: InterfaceNumber,
comm_ep: D::EndpointIn,
@ -235,6 +251,7 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> {
}
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>,
@ -248,13 +265,8 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
// Control interface
let mut iface = func.interface();
let mac_addr_string = iface.string();
iface.handler(state.comm_control.write(CommControl {
mac_addr_string,
shared: &state.shared,
mac_addr_str: [0; 12],
}));
let comm_if = iface.interface_number();
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE);
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None);
alt.descriptor(
CS_INTERFACE,
@ -302,13 +314,23 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
// Data interface
let mut iface = func.interface();
iface.handler(state.data_control.write(DataControl {}));
let data_if = iface.interface_number();
let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB);
let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB);
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,
@ -319,6 +341,9 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
}
}
/// 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 {
@ -334,12 +359,18 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
}
}
/// 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,
}
impl<'d, D: Driver<'d>> Sender<'d, D> {
/// Write a packet.
///
/// This waits until the packet is succesfully stored in the CDC-NCM endpoint buffers.
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
let seq = self.seq;
self.seq = self.seq.wrapping_add(1);
@ -393,6 +424,9 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
}
}
/// 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,
@ -400,7 +434,9 @@ pub struct Receiver<'d, D: Driver<'d>> {
}
impl<'d, D: Driver<'d>> Receiver<'d, D> {
/// Reads a single packet from the OUT endpoint.
/// Write a network packet.
///
/// This waits until a packet is succesfully received from the endpoint buffers.
pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
// Retry loop
loop {

View File

@ -1,3 +1,5 @@
//! USB HID (Human Interface Device) class implementation.
use core::mem::MaybeUninit;
use core::ops::Range;
use core::sync::atomic::{AtomicUsize, Ordering};
@ -7,9 +9,10 @@ use ssmarshal::serialize;
#[cfg(feature = "usbd-hid")]
use usbd_hid::descriptor::AsInputReport;
use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType};
use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::Builder;
use crate::types::InterfaceNumber;
use crate::{Builder, Handler};
const USB_CLASS_HID: u8 = 0x03;
const USB_SUBCLASS_NONE: u8 = 0x00;
@ -28,6 +31,7 @@ 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],
@ -46,11 +50,15 @@ pub struct Config<'d> {
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),
}
@ -65,12 +73,14 @@ impl ReportId {
}
}
/// Internal state for USB HID.
pub struct State<'d> {
control: MaybeUninit<Control<'d>>,
out_report_offset: AtomicUsize,
}
impl<'d> State<'d> {
/// Create a new `State`.
pub fn new() -> Self {
State {
control: MaybeUninit::uninit(),
@ -79,6 +89,7 @@ impl<'d> State<'d> {
}
}
/// 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>,
@ -90,18 +101,12 @@ fn build<'d, D: Driver<'d>>(
config: Config<'d>,
with_out_endpoint: bool,
) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) {
let control = state.control.write(Control::new(
config.report_descriptor,
config.request_handler,
&state.out_report_offset,
));
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();
iface.handler(control);
let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE);
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(
@ -129,6 +134,16 @@ fn build<'d, D: Driver<'d>>(
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)
}
@ -180,20 +195,30 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit
}
}
/// 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>),
}
@ -344,6 +369,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
}
}
/// Handler for HID-related control requests.
pub trait RequestHandler {
/// Reads the value of report `id` into `buf` returning the size.
///
@ -379,6 +405,7 @@ pub trait RequestHandler {
}
struct Control<'d> {
if_num: InterfaceNumber,
report_descriptor: &'d [u8],
request_handler: Option<&'d dyn RequestHandler>,
out_report_offset: &'d AtomicUsize,
@ -387,11 +414,13 @@ struct Control<'d> {
impl<'d> Control<'d> {
fn new(
if_num: InterfaceNumber,
report_descriptor: &'d [u8],
request_handler: Option<&'d dyn RequestHandler>,
out_report_offset: &'d AtomicUsize,
) -> Self {
Control {
if_num,
report_descriptor,
request_handler,
out_report_offset,
@ -417,88 +446,100 @@ impl<'d> Control<'d> {
}
}
impl<'d> ControlHandler for Control<'d> {
impl<'d> Handler for Control<'d> {
fn reset(&mut self) {
self.out_report_offset.store(0, Ordering::Release);
}
fn get_descriptor<'a>(&'a mut self, req: Request, _buf: &'a mut [u8]) -> InResponse<'a> {
match (req.value >> 8) as u8 {
HID_DESC_DESCTYPE_HID_REPORT => InResponse::Accepted(self.report_descriptor),
HID_DESC_DESCTYPE_HID => InResponse::Accepted(&self.hid_descriptor),
_ => InResponse::Rejected,
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;
}
}
fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse {
trace!("HID control_out {:?} {=[u8]:x}", req, data);
if let RequestType::Class = req.request_type {
match req.request {
HID_REQ_SET_IDLE => {
if let Some(handler) = self.request_handler {
let id = req.value as u8;
let id = (id != 0).then(|| 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);
}
OutResponse::Accepted
}
HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) {
(Ok(id), Some(handler)) => handler.set_report(id, data),
_ => OutResponse::Rejected,
},
HID_REQ_SET_PROTOCOL => {
if req.value == 1 {
OutResponse::Accepted
} else {
warn!("HID Boot Protocol is unsupported.");
OutResponse::Rejected // UNSUPPORTED: Boot Protocol
}
}
_ => OutResponse::Rejected,
}
} else {
OutResponse::Rejected // UNSUPPORTED: SET_DESCRIPTOR
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
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.and_then(|x| x.get_report(id, buf)),
Err(_) => None,
};
if let Some(size) = size {
InResponse::Accepted(&buf[0..size])
} else {
InResponse::Rejected
}
}
HID_REQ_GET_IDLE => {
HID_REQ_SET_IDLE => {
if let Some(handler) = self.request_handler {
let id = req.value as u8;
let id = (id != 0).then(|| 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;
InResponse::Accepted(&buf[0..1])
} else {
InResponse::Rejected
}
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) {
(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 {
InResponse::Rejected
warn!("HID Boot Protocol is unsupported.");
Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol
}
}
HID_REQ_GET_PROTOCOL => {
// UNSUPPORTED: Boot Protocol
buf[0] = 1;
InResponse::Accepted(&buf[0..1])
_ => 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.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 {
let id = req.value as u8;
let id = (id != 0).then(|| 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),
}
}
_ => InResponse::Rejected,
_ => None,
}
}
}

View File

@ -1,3 +1,4 @@
//! Implementations of well-known USB classes.
pub mod cdc_acm;
pub mod cdc_ncm;
pub mod hid;

View File

@ -2,7 +2,6 @@
use core::mem;
use crate::driver::Direction;
use crate::types::StringIndex;
/// Control request type.
#[repr(u8)]
@ -126,72 +125,22 @@ impl Request {
}
}
/// 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,
}
/// Handler for control requests.
///
/// All methods are optional callbacks that will be called by
/// [`UsbDevice::run()`](crate::UsbDevice::run)
pub trait ControlHandler {
/// Called after a USB reset after the bus reset sequence is complete.
fn reset(&mut self) {}
fn set_alternate_setting(&mut self, alternate_setting: u8) {
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.
fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse {
let _ = (req, data);
OutResponse::Rejected
}
/// 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.
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
let _ = (req, buf);
InResponse::Rejected
}
/// Called when a GET DESCRIPTOR control request is received on the interface.
///
/// 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.
fn get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
let _ = (req, buf);
InResponse::Rejected
}
/// 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
}
}

View File

@ -1,3 +1,5 @@
//! Utilities for writing USB descriptors.
use crate::builder::Config;
use crate::driver::EndpointInfo;
use crate::types::*;
@ -236,7 +238,7 @@ impl<'a> DescriptorWriter<'a> {
endpoint.ep_type as u8, // bmAttributes
endpoint.max_packet_size as u8,
(endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
endpoint.interval, // bInterval
endpoint.interval_ms, // bInterval
],
);
}

View File

@ -1,5 +1,6 @@
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))]
@ -75,7 +76,7 @@ impl<'a, 'b> Iterator for DescriptorIter<'a, 'b> {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointInfo {
pub configuration: u8,
pub interface: u8,
pub interface: InterfaceNumber,
pub interface_alt: u8,
pub ep_address: EndpointAddress,
}
@ -83,7 +84,7 @@ pub struct EndpointInfo {
pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<(), ReadError> {
let mut ep = EndpointInfo {
configuration: 0,
interface: 0,
interface: InterfaceNumber(0),
interface_alt: 0,
ep_address: EndpointAddress::from(0),
};
@ -96,7 +97,7 @@ pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<
ep.configuration = r.read_u8()?;
}
descriptor_type::INTERFACE => {
ep.interface = r.read_u8()?;
ep.interface = InterfaceNumber(r.read_u8()?);
ep.interface_alt = r.read_u8()?;
}
descriptor_type::ENDPOINT => {

View File

@ -1,6 +1,8 @@
#![no_std]
#![feature(type_alias_impl_trait)]
#![feature(async_fn_in_trait)]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
@ -12,13 +14,20 @@ pub mod class;
pub mod control;
pub mod descriptor;
mod descriptor_reader;
pub mod msos;
mod packed;
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};
use crate::config::*;
use crate::control::*;
use crate::descriptor::*;
use crate::descriptor_reader::foreach_endpoint;
@ -48,10 +57,13 @@ pub enum UsbDeviceState {
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,
}
@ -67,41 +79,95 @@ pub const CONFIGURATION_NONE: u8 = 0;
/// The bConfiguration value for the single configuration supported by this device.
pub const CONFIGURATION_VALUE: u8 = 1;
pub const MAX_INTERFACE_COUNT: usize = 4;
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;
/// A handler trait for changes in the device state of the [UsbDevice].
pub trait DeviceStateHandler {
/// 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(&self, _enabled: bool) {}
fn enabled(&mut self, _enabled: bool) {}
/// Called when the host resets the device.
fn reset(&self) {}
/// 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(&self, _addr: u8) {}
fn addressed(&mut self, _addr: u8) {}
/// Called when the host has enabled or disabled the configuration of the device.
fn configured(&self, _configured: bool) {}
fn configured(&mut self, _configured: bool) {}
/// Called when the bus has entered or exited the suspend state.
fn suspended(&self, _suspended: bool) {}
fn suspended(&mut self, _suspended: bool) {}
/// Called when remote wakeup feature is enabled or disabled.
fn remote_wakeup_enabled(&self, _enabled: bool) {}
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 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 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<'d> {
handler: Option<&'d mut dyn ControlHandler>,
struct Interface {
current_alt_setting: u8,
num_alt_settings: u8,
num_strings: u8,
}
/// Main struct for the USB device stack.
pub struct UsbDevice<'d, D: Driver<'d>> {
control_buf: &'d mut [u8],
control: D::ControlPipe,
@ -110,7 +176,6 @@ pub struct UsbDevice<'d, D: Driver<'d>> {
struct Inner<'d, D: Driver<'d>> {
bus: D::Bus,
handler: Option<&'d dyn DeviceStateHandler>,
config: Config<'d>,
device_descriptor: &'d [u8],
@ -129,19 +194,24 @@ struct Inner<'d, D: Driver<'d>> {
/// instead of regular `accept()`.
set_address_pending: bool,
interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
}
impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
pub(crate) fn build(
driver: D,
config: Config<'d>,
handler: Option<&'d dyn DeviceStateHandler>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
device_descriptor: &'d [u8],
config_descriptor: &'d [u8],
bos_descriptor: &'d [u8],
interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
control_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
) -> UsbDevice<'d, D> {
// Start the USB bus.
// This prevent further allocation by consuming the driver.
@ -153,7 +223,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
inner: Inner {
bus,
config,
handler,
device_descriptor,
config_descriptor,
bos_descriptor,
@ -165,6 +234,9 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
address: 0,
set_address_pending: false,
interfaces,
handlers,
#[cfg(feature = "msos-descriptor")]
msos_descriptor,
},
}
}
@ -207,7 +279,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
self.inner.suspended = false;
self.inner.remote_wakeup_enabled = false;
if let Some(h) = &self.inner.handler {
for h in &mut self.inner.handlers {
h.enabled(false);
}
}
@ -236,7 +308,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
self.inner.bus.remote_wakeup().await?;
self.inner.suspended = false;
if let Some(h) = &self.inner.handler {
for h in &mut self.inner.handlers {
h.suspended(false);
}
@ -347,29 +419,29 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
self.remote_wakeup_enabled = false;
self.address = 0;
for iface in self.interfaces.iter_mut() {
iface.current_alt_setting = 0;
if let Some(h) = &mut iface.handler {
h.reset();
h.set_alternate_setting(0);
}
for h in &mut self.handlers {
h.reset();
}
if let Some(h) = &self.handler {
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;
if let Some(h) = &self.handler {
for h in &mut self.handlers {
h.suspended(false);
}
}
Event::Suspend => {
trace!("usb: suspend");
self.suspended = true;
if let Some(h) = &self.handler {
for h in &mut self.handlers {
h.suspended(true);
}
}
@ -378,7 +450,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
self.bus.enable().await;
self.device_state = UsbDeviceState::Default;
if let Some(h) = &self.handler {
for h in &mut self.handlers {
h.enabled(true);
}
}
@ -387,7 +459,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
self.bus.disable().await;
self.device_state = UsbDeviceState::Unpowered;
if let Some(h) = &self.handler {
for h in &mut self.handlers {
h.enabled(false);
}
}
@ -402,14 +474,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
(RequestType::Standard, Recipient::Device) => match (req.request, req.value) {
(Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => {
self.remote_wakeup_enabled = false;
if let Some(h) = &self.handler {
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;
if let Some(h) = &self.handler {
for h in &mut self.handlers {
h.remote_wakeup_enabled(true);
}
OutResponse::Accepted
@ -418,7 +490,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
self.address = addr as u8;
self.set_address_pending = true;
self.device_state = UsbDeviceState::Addressed;
if let Some(h) = &self.handler {
for h in &mut self.handlers {
h.addressed(self.address);
}
OutResponse::Accepted
@ -429,14 +501,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
// Enable all endpoints of selected alt settings.
foreach_endpoint(self.config_descriptor, |ep| {
let iface = &self.interfaces[ep.interface as usize];
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 handler.
if let Some(h) = &self.handler {
// Notify handlers.
for h in &mut self.handlers {
h.configured(true);
}
@ -454,8 +526,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
})
.unwrap();
// Notify handler.
if let Some(h) = &self.handler {
// Notify handlers.
for h in &mut self.handlers {
h.configured(false);
}
@ -465,7 +537,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
_ => OutResponse::Rejected,
},
(RequestType::Standard, Recipient::Interface) => {
let iface = match self.interfaces.get_mut(req.index as usize) {
let iface_num = InterfaceNumber::new(req.index as _);
let iface = match self.interfaces.get_mut(iface_num.0 as usize) {
Some(iface) => iface,
None => return OutResponse::Rejected,
};
@ -483,7 +556,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
// Enable/disable EPs of this interface as needed.
foreach_endpoint(self.config_descriptor, |ep| {
if ep.interface == req.index as u8 {
if ep.interface == iface_num {
self.bus
.endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt);
}
@ -491,10 +564,9 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
.unwrap();
// TODO check it is valid (not out of range)
// TODO actually enable/disable endpoints.
if let Some(handler) = &mut iface.handler {
handler.set_alternate_setting(new_altsetting);
for h in &mut self.handlers {
h.set_alternate_setting(iface_num, new_altsetting);
}
OutResponse::Accepted
}
@ -514,17 +586,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
}
_ => OutResponse::Rejected,
},
(RequestType::Class, Recipient::Interface) => {
let iface = match self.interfaces.get_mut(req.index as usize) {
Some(iface) => iface,
None => return OutResponse::Rejected,
};
match &mut iface.handler {
Some(handler) => handler.control_out(req, data),
None => OutResponse::Rejected,
}
}
_ => OutResponse::Rejected,
_ => self.handle_control_out_delegated(req, data),
}
}
@ -569,11 +631,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
buf[0] = iface.current_alt_setting;
InResponse::Accepted(&buf[..1])
}
Request::GET_DESCRIPTOR => match &mut iface.handler {
Some(handler) => handler.get_descriptor(req, buf),
None => InResponse::Rejected,
},
_ => InResponse::Rejected,
_ => self.handle_control_in_delegated(req, buf),
}
}
(RequestType::Standard, Recipient::Endpoint) => match req.request {
@ -588,21 +646,48 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
}
_ => InResponse::Rejected,
},
(RequestType::Class, Recipient::Interface) => {
let iface = match self.interfaces.get_mut(req.index as usize) {
Some(iface) => iface,
None => return InResponse::Rejected,
};
match &mut iface.handler {
Some(handler) => handler.control_in(req, buf),
None => InResponse::Rejected,
#[cfg(feature = "msos-descriptor")]
(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)
}
}
_ => InResponse::Rejected,
_ => 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<'x, 'y>(r: InResponse<'x>) -> 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();
@ -623,30 +708,16 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
STRING_INDEX_PRODUCT => self.config.product,
STRING_INDEX_SERIAL_NUMBER => self.config.serial_number,
_ => {
// Find out which iface owns this string index.
let mut index_left = index - STRING_INDEX_CUSTOM_START;
let mut the_iface = None;
for iface in &mut self.interfaces {
if index_left < iface.num_strings {
the_iface = Some(iface);
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;
}
index_left -= iface.num_strings;
}
if let Some(iface) = the_iface {
if let Some(handler) = &mut iface.handler {
let index = StringIndex::new(index);
let lang_id = req.index;
handler.get_string(index, lang_id)
} else {
warn!("String requested to an interface with no handler.");
None
}
} else {
warn!("String requested but didn't match to an interface.");
None
}
s
}
};

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

@ -0,0 +1,727 @@
#![cfg(feature = "msos-descriptor")]
//! 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 super::{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()
}
}
/// 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 compabitility
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;
}
mod sealed {
use core::mem::size_of;
/// A trait for descriptors
pub trait Descriptor: Sized {
const TYPE: super::DescriptorType;
/// The size of the descriptor's header.
fn size(&self) -> usize {
size_of::<Self>()
}
fn write_to(&self, buf: &mut [u8]);
}
pub trait DescriptorSet: Descriptor {
const LENGTH_OFFSET: usize;
}
}
use sealed::*;
/// 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");
(&mut 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)]
#[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)]
#[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.
pub trait DeviceLevelDescriptor: Descriptor {}
/// A marker trait for feature descriptors that are valid at the function level.
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];
(&mut cid[..compatible_id.len()]).copy_from_slice(compatible_id.as_bytes());
let mut scid = [0u8; 8];
(&mut 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(),
}
}
}

View File

@ -1,7 +1,10 @@
//! USB types.
/// A handle for a USB interface that contains its number.
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InterfaceNumber(pub(crate) u8);
#[repr(transparent)]
pub struct InterfaceNumber(pub u8);
impl InterfaceNumber {
pub(crate) fn new(index: u8) -> InterfaceNumber {
@ -18,7 +21,8 @@ impl From<InterfaceNumber> for u8 {
/// A handle for a USB string descriptor that contains its index.
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StringIndex(u8);
#[repr(transparent)]
pub struct StringIndex(pub u8);
impl StringIndex {
pub(crate) fn new(index: u8) -> StringIndex {