1203: usb: unify ControlHandler+DeviceStateHandler, route all control requests to all handlers. r=Dirbaio a=Dirbaio

depends on #1202 

- Allows classes to handle vendor requests. (fixes #1078)
- Allows classes to use a single handler for multiple interfaces.
- Allows classes to access the other events (previously only `reset` was available).

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
bors[bot] 2023-02-07 23:31:24 +00:00 committed by GitHub
commit 9d637070a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 602 additions and 348 deletions

View File

@ -74,7 +74,6 @@ impl<const N: usize> UsbLogger<N> {
&mut state.config_descriptor, &mut state.config_descriptor,
&mut state.bos_descriptor, &mut state.bos_descriptor,
&mut state.control_buf, &mut state.control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -16,6 +16,28 @@ usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"]
msos-descriptor = [] msos-descriptor = []
default = ["usbd-hid"] 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] [dependencies]
embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }

View File

@ -1,6 +1,28 @@
# embassy-usb # embassy-usb
TODO crate description/ 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 ## Interoperability

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,12 +1,12 @@
use heapless::Vec; use heapless::Vec;
use crate::control::ControlHandler; use crate::config::*;
use crate::descriptor::{BosWriter, DescriptorWriter}; use crate::descriptor::{BosWriter, DescriptorWriter};
use crate::driver::{Driver, Endpoint, EndpointType}; use crate::driver::{Driver, Endpoint, EndpointType};
#[cfg(feature = "msos-descriptor")] #[cfg(feature = "msos-descriptor")]
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
use crate::types::*; 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)] #[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -122,8 +122,8 @@ impl<'a> Config<'a> {
/// [`UsbDevice`] builder. /// [`UsbDevice`] builder.
pub struct Builder<'d, D: Driver<'d>> { pub struct Builder<'d, D: Driver<'d>> {
config: Config<'d>, config: Config<'d>,
handler: Option<&'d dyn DeviceStateHandler>, handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
control_buf: &'d mut [u8], control_buf: &'d mut [u8],
driver: D, driver: D,
@ -151,7 +151,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
bos_descriptor_buf: &'d mut [u8], bos_descriptor_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], #[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8],
control_buf: &'d mut [u8], control_buf: &'d mut [u8],
handler: Option<&'d dyn DeviceStateHandler>,
) -> Self { ) -> Self {
// Magic values specified in USB-IF ECN on IADs. // Magic values specified in USB-IF ECN on IADs.
if config.composite_with_iads if config.composite_with_iads
@ -179,9 +178,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
Builder { Builder {
driver, driver,
handler,
config, config,
interfaces: Vec::new(), interfaces: Vec::new(),
handlers: Vec::new(),
control_buf, control_buf,
next_string_index: STRING_INDEX_CUSTOM_START, next_string_index: STRING_INDEX_CUSTOM_START,
@ -205,7 +204,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
UsbDevice::build( UsbDevice::build(
self.driver, self.driver,
self.config, self.config,
self.handler, self.handlers,
self.device_descriptor.into_buf(), self.device_descriptor.into_buf(),
self.config_descriptor.into_buf(), self.config_descriptor.into_buf(),
self.bos_descriptor.writer.into_buf(), self.bos_descriptor.writer.into_buf(),
@ -248,6 +247,26 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
} }
} }
/// 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")] #[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Descriptor Set. /// Add an MS OS 2.0 Descriptor Set.
/// ///
@ -301,14 +320,15 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
let number = self.builder.interfaces.len() as _; let number = self.builder.interfaces.len() as _;
let iface = Interface { let iface = Interface {
handler: None,
current_alt_setting: 0, current_alt_setting: 0,
num_alt_settings: 0, num_alt_settings: 0,
num_strings: 0,
}; };
if self.builder.interfaces.push(iface).is_err() { 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 { InterfaceBuilder {
@ -326,7 +346,7 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
} }
if !self.builder.msos_descriptor.is_in_function_subset() { if !self.builder.msos_descriptor.is_in_function_subset() {
self.builder.msos_descriptor.function(self.first_interface.0); self.builder.msos_descriptor.function(self.first_interface);
} }
#[cfg(feature = "msos-descriptor")] #[cfg(feature = "msos-descriptor")]
@ -347,17 +367,9 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {
self.interface_number 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. /// Allocates a new string index.
pub fn string(&mut self) -> StringIndex { pub fn string(&mut self) -> StringIndex {
let index = self.builder.next_string_index; self.builder.string()
self.builder.next_string_index += 1;
self.builder.interfaces[self.interface_number.0 as usize].num_strings += 1;
StringIndex::new(index)
} }
/// Add an alternate setting to the interface and write its descriptor. /// Add an alternate setting to the interface and write its descriptor.

View File

@ -6,10 +6,10 @@ use core::sync::atomic::{AtomicBool, Ordering};
use embassy_sync::blocking_mutex::CriticalSectionMutex; 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::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::*; use crate::types::*;
use crate::Builder; use crate::{Builder, Handler};
/// This should be used as `device_class` when building the `UsbDevice`. /// This should be used as `device_class` when building the `UsbDevice`.
pub const USB_CLASS_CDC: u8 = 0x02; pub const USB_CLASS_CDC: u8 = 0x02;
@ -67,6 +67,7 @@ pub struct CdcAcmClass<'d, D: Driver<'d>> {
} }
struct Control<'a> { struct Control<'a> {
comm_if: InterfaceNumber,
shared: &'a ControlShared, shared: &'a ControlShared,
} }
@ -98,7 +99,7 @@ impl<'a> Control<'a> {
} }
} }
impl<'d> ControlHandler for Control<'d> { impl<'d> Handler for Control<'d> {
fn reset(&mut self) { fn reset(&mut self) {
let shared = self.shared(); let shared = self.shared();
shared.line_coding.lock(|x| x.set(LineCoding::default())); shared.line_coding.lock(|x| x.set(LineCoding::default()));
@ -106,12 +107,18 @@ impl<'d> ControlHandler for Control<'d> {
shared.rts.store(false, Ordering::Relaxed); 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 { match req.request {
REQ_SEND_ENCAPSULATED_COMMAND => { REQ_SEND_ENCAPSULATED_COMMAND => {
// We don't actually support encapsulated commands but pretend we do for standards // We don't actually support encapsulated commands but pretend we do for standards
// compatibility. // compatibility.
OutResponse::Accepted Some(OutResponse::Accepted)
} }
REQ_SET_LINE_CODING if data.len() >= 7 => { REQ_SET_LINE_CODING if data.len() >= 7 => {
let coding = LineCoding { let coding = LineCoding {
@ -123,7 +130,7 @@ impl<'d> ControlHandler for Control<'d> {
self.shared().line_coding.lock(|x| x.set(coding)); self.shared().line_coding.lock(|x| x.set(coding));
debug!("Set line coding to: {:?}", coding); debug!("Set line coding to: {:?}", coding);
OutResponse::Accepted Some(OutResponse::Accepted)
} }
REQ_SET_CONTROL_LINE_STATE => { REQ_SET_CONTROL_LINE_STATE => {
let dtr = (req.value & 0x0001) != 0; let dtr = (req.value & 0x0001) != 0;
@ -134,13 +141,19 @@ impl<'d> ControlHandler for Control<'d> {
shared.rts.store(rts, Ordering::Relaxed); shared.rts.store(rts, Ordering::Relaxed);
debug!("Set dtr {}, rts {}", dtr, rts); 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 { match req.request {
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
REQ_GET_LINE_CODING if req.length == 7 => { REQ_GET_LINE_CODING if req.length == 7 => {
@ -151,9 +164,9 @@ impl<'d> ControlHandler for Control<'d> {
buf[4] = coding.stop_bits as u8; buf[4] = coding.stop_bits as u8;
buf[5] = coding.parity_type as u8; buf[5] = coding.parity_type as u8;
buf[6] = coding.data_bits; buf[6] = coding.data_bits;
InResponse::Accepted(&buf[0..7]) Some(InResponse::Accepted(&buf[0..7]))
} }
_ => InResponse::Rejected, _ => Some(InResponse::Rejected),
} }
} }
} }
@ -162,17 +175,12 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For /// 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. /// 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 { 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); assert!(builder.control_buf_len() >= 7);
let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE); let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE);
// Control interface // Control interface
let mut iface = func.interface(); let mut iface = func.interface();
iface.handler(control);
let comm_if = iface.interface_number(); let comm_if = iface.interface_number();
let data_if = u8::from(comm_if) + 1; let data_if = u8::from(comm_if) + 1;
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None); let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None);
@ -213,6 +221,16 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
let read_ep = alt.endpoint_bulk_out(max_packet_size); let read_ep = alt.endpoint_bulk_out(max_packet_size);
let write_ep = alt.endpoint_bulk_in(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 { CdcAcmClass {
_comm_ep: comm_ep, _comm_ep: comm_ep,
_data_if: data_if, _data_if: data_if,

View File

@ -17,10 +17,10 @@
use core::intrinsics::copy_nonoverlapping; use core::intrinsics::copy_nonoverlapping;
use core::mem::{size_of, MaybeUninit}; 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::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::*; use crate::types::*;
use crate::Builder; use crate::{Builder, Handler};
pub mod embassy_net; pub mod embassy_net;
@ -117,8 +117,7 @@ fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
/// Internal state for the CDC-NCM class. /// Internal state for the CDC-NCM class.
pub struct State<'a> { pub struct State<'a> {
comm_control: MaybeUninit<CommControl<'a>>, control: MaybeUninit<Control<'a>>,
data_control: MaybeUninit<DataControl>,
shared: ControlShared, shared: ControlShared,
} }
@ -126,8 +125,7 @@ impl<'a> State<'a> {
/// Create a new `State`. /// Create a new `State`.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
comm_control: MaybeUninit::uninit(), control: MaybeUninit::uninit(),
data_control: MaybeUninit::uninit(),
shared: Default::default(), shared: Default::default(),
} }
} }
@ -144,29 +142,55 @@ impl Default for ControlShared {
} }
} }
struct CommControl<'a> { struct Control<'a> {
mac_addr_string: StringIndex, mac_addr_string: StringIndex,
shared: &'a ControlShared, shared: &'a ControlShared,
mac_addr_str: [u8; 12], mac_addr_str: [u8; 12],
comm_if: InterfaceNumber,
data_if: InterfaceNumber,
} }
impl<'d> ControlHandler for CommControl<'d> { impl<'d> Handler for Control<'d> {
fn control_out(&mut self, req: control::Request, _data: &[u8]) -> OutResponse { 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 { match req.request {
REQ_SEND_ENCAPSULATED_COMMAND => { REQ_SEND_ENCAPSULATED_COMMAND => {
// We don't actually support encapsulated commands but pretend we do for standards // We don't actually support encapsulated commands but pretend we do for standards
// compatibility. // compatibility.
OutResponse::Accepted Some(OutResponse::Accepted)
} }
REQ_SET_NTB_INPUT_SIZE => { REQ_SET_NTB_INPUT_SIZE => {
// TODO // 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 { match req.request {
REQ_GET_NTB_PARAMETERS => { REQ_GET_NTB_PARAMETERS => {
let res = NtbParameters { let res = NtbParameters {
@ -187,9 +211,9 @@ impl<'d> ControlHandler for CommControl<'d> {
max_datagram_count: 1, // We only decode 1 packet per NTB 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),
} }
} }
@ -214,18 +238,6 @@ 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 /// CDC-NCM class
pub struct CdcNcmClass<'d, D: Driver<'d>> { pub struct CdcNcmClass<'d, D: Driver<'d>> {
_comm_if: InterfaceNumber, _comm_if: InterfaceNumber,
@ -253,11 +265,6 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
// Control interface // Control interface
let mut iface = func.interface(); let mut iface = func.interface();
let mac_addr_string = iface.string(); 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 comm_if = iface.interface_number();
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None); let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None);
@ -307,13 +314,23 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
// Data interface // Data interface
let mut iface = func.interface(); let mut iface = func.interface();
iface.handler(state.data_control.write(DataControl {}));
let data_if = iface.interface_number(); let data_if = iface.interface_number();
let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); 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 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 read_ep = alt.endpoint_bulk_out(max_packet_size);
let write_ep = alt.endpoint_bulk_in(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 { CdcNcmClass {
_comm_if: comm_if, _comm_if: comm_if,
comm_ep, comm_ep,

View File

@ -9,9 +9,10 @@ use ssmarshal::serialize;
#[cfg(feature = "usbd-hid")] #[cfg(feature = "usbd-hid")]
use usbd_hid::descriptor::AsInputReport; 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::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_CLASS_HID: u8 = 0x03;
const USB_SUBCLASS_NONE: u8 = 0x00; const USB_SUBCLASS_NONE: u8 = 0x00;
@ -100,17 +101,11 @@ fn build<'d, D: Driver<'d>>(
config: Config<'d>, config: Config<'d>,
with_out_endpoint: bool, with_out_endpoint: bool,
) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { ) -> (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 len = config.report_descriptor.len();
let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE);
let mut iface = func.interface(); let mut iface = func.interface();
iface.handler(control); let if_num = iface.interface_number();
let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None);
// HID descriptor // HID descriptor
@ -139,6 +134,16 @@ fn build<'d, D: Driver<'d>>(
None 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) (ep_out, ep_in, &state.out_report_offset)
} }
@ -400,6 +405,7 @@ pub trait RequestHandler {
} }
struct Control<'d> { struct Control<'d> {
if_num: InterfaceNumber,
report_descriptor: &'d [u8], report_descriptor: &'d [u8],
request_handler: Option<&'d dyn RequestHandler>, request_handler: Option<&'d dyn RequestHandler>,
out_report_offset: &'d AtomicUsize, out_report_offset: &'d AtomicUsize,
@ -408,11 +414,13 @@ struct Control<'d> {
impl<'d> Control<'d> { impl<'d> Control<'d> {
fn new( fn new(
if_num: InterfaceNumber,
report_descriptor: &'d [u8], report_descriptor: &'d [u8],
request_handler: Option<&'d dyn RequestHandler>, request_handler: Option<&'d dyn RequestHandler>,
out_report_offset: &'d AtomicUsize, out_report_offset: &'d AtomicUsize,
) -> Self { ) -> Self {
Control { Control {
if_num,
report_descriptor, report_descriptor,
request_handler, request_handler,
out_report_offset, out_report_offset,
@ -438,22 +446,19 @@ impl<'d> Control<'d> {
} }
} }
impl<'d> ControlHandler for Control<'d> { impl<'d> Handler for Control<'d> {
fn reset(&mut self) { fn reset(&mut self) {
self.out_report_offset.store(0, Ordering::Release); self.out_report_offset.store(0, Ordering::Release);
} }
fn get_descriptor<'a>(&'a mut self, req: Request, _buf: &'a mut [u8]) -> InResponse<'a> { fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> {
match (req.value >> 8) as u8 { if (req.request_type, req.recipient, req.index)
HID_DESC_DESCTYPE_HID_REPORT => InResponse::Accepted(self.report_descriptor), != (RequestType::Class, Recipient::Interface, self.if_num.0 as u16)
HID_DESC_DESCTYPE_HID => InResponse::Accepted(&self.hid_descriptor), {
_ => InResponse::Rejected, return None;
}
} }
fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse {
trace!("HID control_out {:?} {=[u8]:x}", req, data); trace!("HID control_out {:?} {=[u8]:x}", req, data);
if let RequestType::Class = req.request_type {
match req.request { match req.request {
HID_REQ_SET_IDLE => { HID_REQ_SET_IDLE => {
if let Some(handler) = self.request_handler { if let Some(handler) = self.request_handler {
@ -463,28 +468,40 @@ impl<'d> ControlHandler for Control<'d> {
let dur = if dur == 0 { u32::MAX } else { 4 * dur }; let dur = if dur == 0 { u32::MAX } else { 4 * dur };
handler.set_idle_ms(id, dur); handler.set_idle_ms(id, dur);
} }
OutResponse::Accepted Some(OutResponse::Accepted)
} }
HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) {
(Ok(id), Some(handler)) => handler.set_report(id, data), (Ok(id), Some(handler)) => Some(handler.set_report(id, data)),
_ => OutResponse::Rejected, _ => Some(OutResponse::Rejected),
}, },
HID_REQ_SET_PROTOCOL => { HID_REQ_SET_PROTOCOL => {
if req.value == 1 { if req.value == 1 {
OutResponse::Accepted Some(OutResponse::Accepted)
} else { } else {
warn!("HID Boot Protocol is unsupported."); warn!("HID Boot Protocol is unsupported.");
OutResponse::Rejected // UNSUPPORTED: Boot Protocol Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol
} }
} }
_ => OutResponse::Rejected, _ => Some(OutResponse::Rejected),
}
} else {
OutResponse::Rejected // UNSUPPORTED: SET_DESCRIPTOR
} }
} }
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.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); trace!("HID control_in {:?}", req);
match req.request { match req.request {
HID_REQ_GET_REPORT => { HID_REQ_GET_REPORT => {
@ -494,9 +511,9 @@ impl<'d> ControlHandler for Control<'d> {
}; };
if let Some(size) = size { if let Some(size) = size {
InResponse::Accepted(&buf[0..size]) Some(InResponse::Accepted(&buf[0..size]))
} else { } else {
InResponse::Rejected Some(InResponse::Rejected)
} }
} }
HID_REQ_GET_IDLE => { HID_REQ_GET_IDLE => {
@ -506,20 +523,23 @@ impl<'d> ControlHandler for Control<'d> {
if let Some(dur) = handler.get_idle_ms(id) { if let Some(dur) = handler.get_idle_ms(id) {
let dur = u8::try_from(dur / 4).unwrap_or(0); let dur = u8::try_from(dur / 4).unwrap_or(0);
buf[0] = dur; buf[0] = dur;
InResponse::Accepted(&buf[0..1]) Some(InResponse::Accepted(&buf[0..1]))
} else { } else {
InResponse::Rejected Some(InResponse::Rejected)
} }
} else { } else {
InResponse::Rejected Some(InResponse::Rejected)
} }
} }
HID_REQ_GET_PROTOCOL => { HID_REQ_GET_PROTOCOL => {
// UNSUPPORTED: Boot Protocol // UNSUPPORTED: Boot Protocol
buf[0] = 1; buf[0] = 1;
InResponse::Accepted(&buf[0..1]) Some(InResponse::Accepted(&buf[0..1]))
} }
_ => InResponse::Rejected, _ => Some(InResponse::Rejected),
}
}
_ => None,
} }
} }
} }

View File

@ -2,7 +2,6 @@
use core::mem; use core::mem;
use crate::driver::Direction; use crate::driver::Direction;
use crate::types::StringIndex;
/// Control request type. /// Control request type.
#[repr(u8)] #[repr(u8)]
@ -145,60 +144,3 @@ pub enum InResponse<'a> {
/// The request was rejected. /// The request was rejected.
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) {}
/// Called when a "set alternate setting" control request is done on the interface.
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,5 +1,6 @@
use crate::descriptor::descriptor_type; use crate::descriptor::descriptor_type;
use crate::driver::EndpointAddress; use crate::driver::EndpointAddress;
use crate::types::InterfaceNumber;
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[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))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointInfo { pub struct EndpointInfo {
pub configuration: u8, pub configuration: u8,
pub interface: u8, pub interface: InterfaceNumber,
pub interface_alt: u8, pub interface_alt: u8,
pub ep_address: EndpointAddress, pub ep_address: EndpointAddress,
} }
@ -83,7 +84,7 @@ pub struct EndpointInfo {
pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<(), ReadError> { pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<(), ReadError> {
let mut ep = EndpointInfo { let mut ep = EndpointInfo {
configuration: 0, configuration: 0,
interface: 0, interface: InterfaceNumber(0),
interface_alt: 0, interface_alt: 0,
ep_address: EndpointAddress::from(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()?; ep.configuration = r.read_u8()?;
} }
descriptor_type::INTERFACE => { descriptor_type::INTERFACE => {
ep.interface = r.read_u8()?; ep.interface = InterfaceNumber(r.read_u8()?);
ep.interface_alt = r.read_u8()?; ep.interface_alt = r.read_u8()?;
} }
descriptor_type::ENDPOINT => { descriptor_type::ENDPOINT => {

View File

@ -16,10 +16,16 @@ mod descriptor_reader;
pub mod msos; pub mod msos;
pub mod types; pub mod types;
mod config {
#![allow(unused)]
include!(concat!(env!("OUT_DIR"), "/config.rs"));
}
use embassy_futures::select::{select, Either}; use embassy_futures::select::{select, Either};
use heapless::Vec; use heapless::Vec;
pub use crate::builder::{Builder, Config}; pub use crate::builder::{Builder, Config};
use crate::config::*;
use crate::control::*; use crate::control::*;
use crate::descriptor::*; use crate::descriptor::*;
use crate::descriptor_reader::foreach_endpoint; use crate::descriptor_reader::foreach_endpoint;
@ -71,40 +77,92 @@ pub const CONFIGURATION_NONE: u8 = 0;
/// The bConfiguration value for the single configuration supported by this device. /// The bConfiguration value for the single configuration supported by this device.
pub const CONFIGURATION_VALUE: u8 = 1; pub const CONFIGURATION_VALUE: u8 = 1;
/// Maximum interface count, configured at compile time.
pub const MAX_INTERFACE_COUNT: usize = 4;
const STRING_INDEX_MANUFACTURER: u8 = 1; const STRING_INDEX_MANUFACTURER: u8 = 1;
const STRING_INDEX_PRODUCT: u8 = 2; const STRING_INDEX_PRODUCT: u8 = 2;
const STRING_INDEX_SERIAL_NUMBER: u8 = 3; const STRING_INDEX_SERIAL_NUMBER: u8 = 3;
const STRING_INDEX_CUSTOM_START: u8 = 4; const STRING_INDEX_CUSTOM_START: u8 = 4;
/// A handler trait for changes in the device state of the [UsbDevice]. /// Handler for device events and control requests.
pub trait DeviceStateHandler { ///
/// 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. /// 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. /// Called after a USB reset after the bus reset sequence is complete.
fn reset(&self) {} fn reset(&mut self) {}
/// Called when the host has set the address of the device to `addr`. /// 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. /// 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. /// 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. /// 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> { struct Interface {
handler: Option<&'d mut dyn ControlHandler>,
current_alt_setting: u8, current_alt_setting: u8,
num_alt_settings: u8, num_alt_settings: u8,
num_strings: u8,
} }
/// Main struct for the USB device stack. /// Main struct for the USB device stack.
@ -116,7 +174,6 @@ pub struct UsbDevice<'d, D: Driver<'d>> {
struct Inner<'d, D: Driver<'d>> { struct Inner<'d, D: Driver<'d>> {
bus: D::Bus, bus: D::Bus,
handler: Option<&'d dyn DeviceStateHandler>,
config: Config<'d>, config: Config<'d>,
device_descriptor: &'d [u8], device_descriptor: &'d [u8],
@ -135,7 +192,9 @@ struct Inner<'d, D: Driver<'d>> {
/// instead of regular `accept()`. /// instead of regular `accept()`.
set_address_pending: bool, 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")] #[cfg(feature = "msos-descriptor")]
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
} }
@ -144,11 +203,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
pub(crate) fn build( pub(crate) fn build(
driver: D, driver: D,
config: Config<'d>, config: Config<'d>,
handler: Option<&'d dyn DeviceStateHandler>, handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
device_descriptor: &'d [u8], device_descriptor: &'d [u8],
config_descriptor: &'d [u8], config_descriptor: &'d [u8],
bos_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], control_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
) -> UsbDevice<'d, D> { ) -> UsbDevice<'d, D> {
@ -162,7 +221,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
inner: Inner { inner: Inner {
bus, bus,
config, config,
handler,
device_descriptor, device_descriptor,
config_descriptor, config_descriptor,
bos_descriptor, bos_descriptor,
@ -174,6 +232,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
address: 0, address: 0,
set_address_pending: false, set_address_pending: false,
interfaces, interfaces,
handlers,
#[cfg(feature = "msos-descriptor")] #[cfg(feature = "msos-descriptor")]
msos_descriptor, msos_descriptor,
}, },
@ -218,7 +277,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
self.inner.suspended = false; self.inner.suspended = false;
self.inner.remote_wakeup_enabled = false; self.inner.remote_wakeup_enabled = false;
if let Some(h) = &self.inner.handler { for h in &mut self.inner.handlers {
h.enabled(false); h.enabled(false);
} }
} }
@ -247,7 +306,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
self.inner.bus.remote_wakeup().await?; self.inner.bus.remote_wakeup().await?;
self.inner.suspended = false; self.inner.suspended = false;
if let Some(h) = &self.inner.handler { for h in &mut self.inner.handlers {
h.suspended(false); h.suspended(false);
} }
@ -358,29 +417,29 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
self.remote_wakeup_enabled = false; self.remote_wakeup_enabled = false;
self.address = 0; self.address = 0;
for iface in self.interfaces.iter_mut() { for h in &mut self.handlers {
iface.current_alt_setting = 0;
if let Some(h) = &mut iface.handler {
h.reset(); h.reset();
h.set_alternate_setting(0);
}
} }
if let Some(h) = &self.handler { for (i, iface) in self.interfaces.iter_mut().enumerate() {
h.reset(); iface.current_alt_setting = 0;
for h in &mut self.handlers {
h.set_alternate_setting(InterfaceNumber::new(i as _), 0);
}
} }
} }
Event::Resume => { Event::Resume => {
trace!("usb: resume"); trace!("usb: resume");
self.suspended = false; self.suspended = false;
if let Some(h) = &self.handler { for h in &mut self.handlers {
h.suspended(false); h.suspended(false);
} }
} }
Event::Suspend => { Event::Suspend => {
trace!("usb: suspend"); trace!("usb: suspend");
self.suspended = true; self.suspended = true;
if let Some(h) = &self.handler { for h in &mut self.handlers {
h.suspended(true); h.suspended(true);
} }
} }
@ -389,7 +448,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
self.bus.enable().await; self.bus.enable().await;
self.device_state = UsbDeviceState::Default; self.device_state = UsbDeviceState::Default;
if let Some(h) = &self.handler { for h in &mut self.handlers {
h.enabled(true); h.enabled(true);
} }
} }
@ -398,7 +457,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
self.bus.disable().await; self.bus.disable().await;
self.device_state = UsbDeviceState::Unpowered; self.device_state = UsbDeviceState::Unpowered;
if let Some(h) = &self.handler { for h in &mut self.handlers {
h.enabled(false); h.enabled(false);
} }
} }
@ -413,14 +472,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
(RequestType::Standard, Recipient::Device) => match (req.request, req.value) { (RequestType::Standard, Recipient::Device) => match (req.request, req.value) {
(Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { (Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => {
self.remote_wakeup_enabled = false; self.remote_wakeup_enabled = false;
if let Some(h) = &self.handler { for h in &mut self.handlers {
h.remote_wakeup_enabled(false); h.remote_wakeup_enabled(false);
} }
OutResponse::Accepted OutResponse::Accepted
} }
(Request::SET_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { (Request::SET_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => {
self.remote_wakeup_enabled = true; self.remote_wakeup_enabled = true;
if let Some(h) = &self.handler { for h in &mut self.handlers {
h.remote_wakeup_enabled(true); h.remote_wakeup_enabled(true);
} }
OutResponse::Accepted OutResponse::Accepted
@ -429,7 +488,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
self.address = addr as u8; self.address = addr as u8;
self.set_address_pending = true; self.set_address_pending = true;
self.device_state = UsbDeviceState::Addressed; self.device_state = UsbDeviceState::Addressed;
if let Some(h) = &self.handler { for h in &mut self.handlers {
h.addressed(self.address); h.addressed(self.address);
} }
OutResponse::Accepted OutResponse::Accepted
@ -440,14 +499,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
// Enable all endpoints of selected alt settings. // Enable all endpoints of selected alt settings.
foreach_endpoint(self.config_descriptor, |ep| { 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 self.bus
.endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt);
}) })
.unwrap(); .unwrap();
// Notify handler. // Notify handlers.
if let Some(h) = &self.handler { for h in &mut self.handlers {
h.configured(true); h.configured(true);
} }
@ -465,8 +524,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
}) })
.unwrap(); .unwrap();
// Notify handler. // Notify handlers.
if let Some(h) = &self.handler { for h in &mut self.handlers {
h.configured(false); h.configured(false);
} }
@ -476,7 +535,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
_ => OutResponse::Rejected, _ => OutResponse::Rejected,
}, },
(RequestType::Standard, Recipient::Interface) => { (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, Some(iface) => iface,
None => return OutResponse::Rejected, None => return OutResponse::Rejected,
}; };
@ -494,7 +554,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
// Enable/disable EPs of this interface as needed. // Enable/disable EPs of this interface as needed.
foreach_endpoint(self.config_descriptor, |ep| { foreach_endpoint(self.config_descriptor, |ep| {
if ep.interface == req.index as u8 { if ep.interface == iface_num {
self.bus self.bus
.endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt);
} }
@ -503,8 +563,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
// TODO check it is valid (not out of range) // TODO check it is valid (not out of range)
if let Some(handler) = &mut iface.handler { for h in &mut self.handlers {
handler.set_alternate_setting(new_altsetting); h.set_alternate_setting(iface_num, new_altsetting);
} }
OutResponse::Accepted OutResponse::Accepted
} }
@ -524,17 +584,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
} }
_ => OutResponse::Rejected, _ => OutResponse::Rejected,
}, },
(RequestType::Class, Recipient::Interface) => { _ => self.handle_control_out_delegated(req, data),
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,
} }
} }
@ -579,11 +629,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
buf[0] = iface.current_alt_setting; buf[0] = iface.current_alt_setting;
InResponse::Accepted(&buf[..1]) InResponse::Accepted(&buf[..1])
} }
Request::GET_DESCRIPTOR => match &mut iface.handler { _ => self.handle_control_in_delegated(req, buf),
Some(handler) => handler.get_descriptor(req, buf),
None => InResponse::Rejected,
},
_ => InResponse::Rejected,
} }
} }
(RequestType::Standard, Recipient::Endpoint) => match req.request { (RequestType::Standard, Recipient::Endpoint) => match req.request {
@ -598,33 +644,47 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
} }
_ => InResponse::Rejected, _ => 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")] #[cfg(feature = "msos-descriptor")]
(RequestType::Vendor, Recipient::Device) => { (RequestType::Vendor, Recipient::Device) => {
if !self.msos_descriptor.is_empty() { if !self.msos_descriptor.is_empty()
if req.request == self.msos_descriptor.vendor_code() && req.index == 7 { && req.request == self.msos_descriptor.vendor_code()
&& req.index == 7
{
// Index 7 retrieves the MS OS Descriptor Set // Index 7 retrieves the MS OS Descriptor Set
InResponse::Accepted(self.msos_descriptor.descriptor()) InResponse::Accepted(self.msos_descriptor.descriptor())
} else { } 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<'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 InResponse::Rejected
} }
} else {
InResponse::Rejected
}
}
_ => InResponse::Rejected,
}
}
fn handle_get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { fn handle_get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
let (dtype, index) = req.descriptor_type_index(); let (dtype, index) = req.descriptor_type_index();
@ -646,30 +706,16 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
STRING_INDEX_PRODUCT => self.config.product, STRING_INDEX_PRODUCT => self.config.product,
STRING_INDEX_SERIAL_NUMBER => self.config.serial_number, STRING_INDEX_SERIAL_NUMBER => self.config.serial_number,
_ => { _ => {
// Find out which iface owns this string index. let mut s = None;
let mut index_left = index - STRING_INDEX_CUSTOM_START; for handler in &mut self.handlers {
let mut the_iface = None;
for iface in &mut self.interfaces {
if index_left < iface.num_strings {
the_iface = Some(iface);
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 index = StringIndex::new(index);
let lang_id = req.index; let lang_id = req.index;
handler.get_string(index, lang_id) if let Some(res) = handler.get_string(index, lang_id) {
} else { s = Some(res);
warn!("String requested to an interface with no handler."); break;
None
} }
} else {
warn!("String requested but didn't match to an interface.");
None
} }
s
} }
}; };

View File

@ -7,6 +7,7 @@
use core::mem::size_of; use core::mem::size_of;
use super::{capability_type, BosWriter}; use super::{capability_type, BosWriter};
use crate::types::InterfaceNumber;
/// A serialized Microsoft OS 2.0 Descriptor set. /// A serialized Microsoft OS 2.0 Descriptor set.
/// ///
@ -125,7 +126,7 @@ impl<'d> MsOsDescriptorWriter<'d> {
} }
/// Add a function subset. /// Add a function subset.
pub fn function(&mut self, first_interface: u8) { pub fn function(&mut self, first_interface: InterfaceNumber) {
assert!( assert!(
self.config_mark.is_some(), self.config_mark.is_some(),
"MsOsDescriptorWriter: function subset requires a configuration subset" "MsOsDescriptorWriter: function subset requires a configuration subset"
@ -376,14 +377,14 @@ impl DescriptorSet for ConfigurationSubsetHeader {
pub struct FunctionSubsetHeader { pub struct FunctionSubsetHeader {
wLength: u16, wLength: u16,
wDescriptorType: u16, wDescriptorType: u16,
bFirstInterface: u8, bFirstInterface: InterfaceNumber,
bReserved: u8, bReserved: u8,
wSubsetLength: u16, wSubsetLength: u16,
} }
impl FunctionSubsetHeader { impl FunctionSubsetHeader {
/// Creates a function subset header /// Creates a function subset header
pub fn new(first_interface: u8) -> Self { pub fn new(first_interface: InterfaceNumber) -> Self {
FunctionSubsetHeader { FunctionSubsetHeader {
wLength: (size_of::<Self>() as u16).to_le(), wLength: (size_of::<Self>() as u16).to_le(),
wDescriptorType: (Self::TYPE as u16).to_le(), wDescriptorType: (Self::TYPE as u16).to_le(),

View File

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

View File

@ -82,7 +82,6 @@ async fn main(spawner: Spawner) {
&mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..],
&mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..],
&mut singleton!([0; 128])[..], &mut singleton!([0; 128])[..],
None,
); );
// Our MAC addr. // Our MAC addr.

View File

@ -16,7 +16,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::signal::Signal; use embassy_sync::signal::Signal;
use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State};
use embassy_usb::control::OutResponse; use embassy_usb::control::OutResponse;
use embassy_usb::{Builder, Config, DeviceStateHandler}; use embassy_usb::{Builder, Config, Handler};
use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -52,7 +52,7 @@ async fn main(_spawner: Spawner) {
let mut bos_descriptor = [0; 256]; let mut bos_descriptor = [0; 256];
let mut control_buf = [0; 64]; let mut control_buf = [0; 64];
let request_handler = MyRequestHandler {}; let request_handler = MyRequestHandler {};
let device_state_handler = MyDeviceStateHandler::new(); let mut device_handler = MyDeviceHandler::new();
let mut state = State::new(); let mut state = State::new();
@ -63,9 +63,10 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
Some(&device_state_handler),
); );
builder.handler(&mut device_handler);
// Create classes on the builder. // Create classes on the builder.
let config = embassy_usb::class::hid::Config { let config = embassy_usb::class::hid::Config {
report_descriptor: KeyboardReport::desc(), report_descriptor: KeyboardReport::desc(),
@ -164,20 +165,20 @@ impl RequestHandler for MyRequestHandler {
} }
} }
struct MyDeviceStateHandler { struct MyDeviceHandler {
configured: AtomicBool, configured: AtomicBool,
} }
impl MyDeviceStateHandler { impl MyDeviceHandler {
fn new() -> Self { fn new() -> Self {
MyDeviceStateHandler { MyDeviceHandler {
configured: AtomicBool::new(false), configured: AtomicBool::new(false),
} }
} }
} }
impl DeviceStateHandler for MyDeviceStateHandler { impl Handler for MyDeviceHandler {
fn enabled(&self, enabled: bool) { fn enabled(&mut self, enabled: bool) {
self.configured.store(false, Ordering::Relaxed); self.configured.store(false, Ordering::Relaxed);
SUSPENDED.store(false, Ordering::Release); SUSPENDED.store(false, Ordering::Release);
if enabled { if enabled {
@ -187,17 +188,17 @@ impl DeviceStateHandler for MyDeviceStateHandler {
} }
} }
fn reset(&self) { fn reset(&mut self) {
self.configured.store(false, Ordering::Relaxed); self.configured.store(false, Ordering::Relaxed);
info!("Bus reset, the Vbus current limit is 100mA"); info!("Bus reset, the Vbus current limit is 100mA");
} }
fn addressed(&self, addr: u8) { fn addressed(&mut self, addr: u8) {
self.configured.store(false, Ordering::Relaxed); self.configured.store(false, Ordering::Relaxed);
info!("USB address set to: {}", addr); info!("USB address set to: {}", addr);
} }
fn configured(&self, configured: bool) { fn configured(&mut self, configured: bool) {
self.configured.store(configured, Ordering::Relaxed); self.configured.store(configured, Ordering::Relaxed);
if configured { if configured {
info!("Device configured, it may now draw up to the configured current limit from Vbus.") info!("Device configured, it may now draw up to the configured current limit from Vbus.")
@ -206,7 +207,7 @@ impl DeviceStateHandler for MyDeviceStateHandler {
} }
} }
fn suspended(&self, suspended: bool) { fn suspended(&mut self, suspended: bool) {
if suspended { if suspended {
info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled).");
SUSPENDED.store(true, Ordering::Release); SUSPENDED.store(true, Ordering::Release);

View File

@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -83,7 +83,6 @@ async fn main(spawner: Spawner) {
&mut res.config_descriptor, &mut res.config_descriptor,
&mut res.bos_descriptor, &mut res.bos_descriptor,
&mut res.control_buf, &mut res.control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -12,6 +12,7 @@ use embassy_nrf::{interrupt, pac};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::driver::EndpointError; use embassy_usb::driver::EndpointError;
use embassy_usb::msos::{self, windows_version}; use embassy_usb::msos::{self, windows_version};
use embassy_usb::types::InterfaceNumber;
use embassy_usb::{Builder, Config}; use embassy_usb::{Builder, Config};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
@ -65,7 +66,6 @@ async fn main(_spawner: Spawner) {
&mut bos_descriptor, &mut bos_descriptor,
&mut msos_descriptor, &mut msos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
builder.msos_descriptor(windows_version::WIN8_1, 2); builder.msos_descriptor(windows_version::WIN8_1, 2);
@ -78,7 +78,7 @@ async fn main(_spawner: Spawner) {
// Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead.
let msos_writer = builder.msos_writer(); let msos_writer = builder.msos_writer();
msos_writer.configuration(0); msos_writer.configuration(0);
msos_writer.function(0); msos_writer.function(InterfaceNumber(0));
msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new( msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs", "DeviceInterfaceGUIDs",

View File

@ -73,7 +73,6 @@ async fn main(spawner: Spawner) {
&mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..],
&mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..],
&mut singleton!([0; 128])[..], &mut singleton!([0; 128])[..],
None,
); );
// Our MAC addr. // Our MAC addr.

View File

@ -53,7 +53,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -58,7 +58,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -82,7 +82,6 @@ async fn main(spawner: Spawner) {
&mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..],
&mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..],
&mut singleton!([0; 128])[..], &mut singleton!([0; 128])[..],
None,
); );
// Our MAC addr. // Our MAC addr.

View File

@ -57,7 +57,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -58,7 +58,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -57,7 +57,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -79,7 +79,6 @@ async fn main(spawner: Spawner) {
&mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..],
&mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..],
&mut singleton!([0; 128])[..], &mut singleton!([0; 128])[..],
None,
); );
// Our MAC addr. // Our MAC addr.

View File

@ -51,7 +51,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.

View File

@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) {
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut control_buf, &mut control_buf,
None,
); );
// Create classes on the builder. // Create classes on the builder.