Merge #1203
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:
commit
9d637070a5
@ -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.
|
||||||
|
@ -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" }
|
||||||
|
@ -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
94
embassy-usb/build.rs
Normal 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
74
embassy-usb/gen_config.py
Normal 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)
|
@ -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.
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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 => {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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",
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user