diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2023-02-07 23:31:24 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-02-07 23:31:24 +0000 |
| commit | 9d637070a516a9ed24c3e12a20082ffdf54f72fb (patch) | |
| tree | d5051ead9e65ea0a51f315a4302c867d07cb2449 | |
| parent | 4a224efe75c7986f5b3d8c5d6083fa17cb774f12 (diff) | |
| parent | 86487db5d1773d2a764ab340051d70cfa40e4714 (diff) | |
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 <[email protected]>
33 files changed, 601 insertions, 347 deletions
diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 6386e2096..1d8dd13ce 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs | |||
| @@ -74,7 +74,6 @@ impl<const N: usize> UsbLogger<N> { | |||
| 74 | &mut state.config_descriptor, | 74 | &mut state.config_descriptor, |
| 75 | &mut state.bos_descriptor, | 75 | &mut state.bos_descriptor, |
| 76 | &mut state.control_buf, | 76 | &mut state.control_buf, |
| 77 | None, | ||
| 78 | ); | 77 | ); |
| 79 | 78 | ||
| 80 | // Create classes on the builder. | 79 | // Create classes on the builder. |
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index eb9ba36f4..ae3f3ac37 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml | |||
| @@ -16,6 +16,28 @@ usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] | |||
| 16 | msos-descriptor = [] | 16 | msos-descriptor = [] |
| 17 | default = ["usbd-hid"] | 17 | default = ["usbd-hid"] |
| 18 | 18 | ||
| 19 | # BEGIN AUTOGENERATED CONFIG FEATURES | ||
| 20 | # Generated by gen_config.py. DO NOT EDIT. | ||
| 21 | max-interface-count-1 = [] | ||
| 22 | max-interface-count-2 = [] | ||
| 23 | max-interface-count-3 = [] | ||
| 24 | max-interface-count-4 = [] # Default | ||
| 25 | max-interface-count-5 = [] | ||
| 26 | max-interface-count-6 = [] | ||
| 27 | max-interface-count-7 = [] | ||
| 28 | max-interface-count-8 = [] | ||
| 29 | |||
| 30 | max-handler-count-1 = [] | ||
| 31 | max-handler-count-2 = [] | ||
| 32 | max-handler-count-3 = [] | ||
| 33 | max-handler-count-4 = [] # Default | ||
| 34 | max-handler-count-5 = [] | ||
| 35 | max-handler-count-6 = [] | ||
| 36 | max-handler-count-7 = [] | ||
| 37 | max-handler-count-8 = [] | ||
| 38 | |||
| 39 | # END AUTOGENERATED CONFIG FEATURES | ||
| 40 | |||
| 19 | [dependencies] | 41 | [dependencies] |
| 20 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 42 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 21 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } | 43 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } |
diff --git a/embassy-usb/README.md b/embassy-usb/README.md index 581c3290f..a3d45b561 100644 --- a/embassy-usb/README.md +++ b/embassy-usb/README.md | |||
| @@ -1,6 +1,28 @@ | |||
| 1 | # embassy-usb | 1 | # embassy-usb |
| 2 | 2 | ||
| 3 | TODO crate description/ | 3 | TODO crate description |
| 4 | |||
| 5 | ## Configuration | ||
| 6 | |||
| 7 | `embassy-usb` has some configuration settings that are set at compile time, affecting sizes | ||
| 8 | and counts of buffers. | ||
| 9 | |||
| 10 | They can be set in two ways: | ||
| 11 | |||
| 12 | - Via Cargo features: enable a feature like `<name>-<value>`. `name` must be in lowercase and | ||
| 13 | use dashes instead of underscores. For example. `max-interface-count-3`. Only a selection of values | ||
| 14 | is available, check `Cargo.toml` for the list. | ||
| 15 | - Via environment variables at build time: set the variable named `EMBASSY_USB_<value>`. For example | ||
| 16 | `EMBASSY_USB_MAX_INTERFACE_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. | ||
| 17 | Any value can be set, unlike with Cargo features. | ||
| 18 | |||
| 19 | Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting | ||
| 20 | with different values, compilation fails. | ||
| 21 | |||
| 22 | ### `MAX_INTERFACE_COUNT` | ||
| 23 | |||
| 24 | Max amount of interfaces that can be created in one device. Default: 4. | ||
| 25 | |||
| 4 | 26 | ||
| 5 | ## Interoperability | 27 | ## Interoperability |
| 6 | 28 | ||
diff --git a/embassy-usb/build.rs b/embassy-usb/build.rs new file mode 100644 index 000000000..33d32f7d3 --- /dev/null +++ b/embassy-usb/build.rs | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | use std::collections::HashMap; | ||
| 2 | use std::fmt::Write; | ||
| 3 | use std::path::PathBuf; | ||
| 4 | use std::{env, fs}; | ||
| 5 | |||
| 6 | static CONFIGS: &[(&str, usize)] = &[ | ||
| 7 | // BEGIN AUTOGENERATED CONFIG FEATURES | ||
| 8 | // Generated by gen_config.py. DO NOT EDIT. | ||
| 9 | ("MAX_INTERFACE_COUNT", 4), | ||
| 10 | ("MAX_HANDLER_COUNT", 4), | ||
| 11 | // END AUTOGENERATED CONFIG FEATURES | ||
| 12 | ]; | ||
| 13 | |||
| 14 | struct ConfigState { | ||
| 15 | value: usize, | ||
| 16 | seen_feature: bool, | ||
| 17 | seen_env: bool, | ||
| 18 | } | ||
| 19 | |||
| 20 | fn main() { | ||
| 21 | let crate_name = env::var("CARGO_PKG_NAME") | ||
| 22 | .unwrap() | ||
| 23 | .to_ascii_uppercase() | ||
| 24 | .replace('-', "_"); | ||
| 25 | |||
| 26 | // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any | ||
| 27 | // other file changed. | ||
| 28 | println!("cargo:rerun-if-changed=build.rs"); | ||
| 29 | |||
| 30 | // Rebuild if config envvar changed. | ||
| 31 | for (name, _) in CONFIGS { | ||
| 32 | println!("cargo:rerun-if-env-changed={crate_name}_{name}"); | ||
| 33 | } | ||
| 34 | |||
| 35 | let mut configs = HashMap::new(); | ||
| 36 | for (name, default) in CONFIGS { | ||
| 37 | configs.insert( | ||
| 38 | *name, | ||
| 39 | ConfigState { | ||
| 40 | value: *default, | ||
| 41 | seen_env: false, | ||
| 42 | seen_feature: false, | ||
| 43 | }, | ||
| 44 | ); | ||
| 45 | } | ||
| 46 | |||
| 47 | let prefix = format!("{crate_name}_"); | ||
| 48 | for (var, value) in env::vars() { | ||
| 49 | if let Some(name) = var.strip_prefix(&prefix) { | ||
| 50 | let Some(cfg) = configs.get_mut(name) else { | ||
| 51 | panic!("Unknown env var {name}") | ||
| 52 | }; | ||
| 53 | |||
| 54 | let Ok(value) = value.parse::<usize>() else { | ||
| 55 | panic!("Invalid value for env var {name}: {value}") | ||
| 56 | }; | ||
| 57 | |||
| 58 | cfg.value = value; | ||
| 59 | cfg.seen_env = true; | ||
| 60 | } | ||
| 61 | |||
| 62 | if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") { | ||
| 63 | if let Some(i) = feature.rfind('_') { | ||
| 64 | let name = &feature[..i]; | ||
| 65 | let value = &feature[i + 1..]; | ||
| 66 | if let Some(cfg) = configs.get_mut(name) { | ||
| 67 | let Ok(value) = value.parse::<usize>() else { | ||
| 68 | panic!("Invalid value for feature {name}: {value}") | ||
| 69 | }; | ||
| 70 | |||
| 71 | // envvars take priority. | ||
| 72 | if !cfg.seen_env { | ||
| 73 | if cfg.seen_feature { | ||
| 74 | panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value); | ||
| 75 | } | ||
| 76 | |||
| 77 | cfg.value = value; | ||
| 78 | cfg.seen_feature = true; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | let mut data = String::new(); | ||
| 86 | |||
| 87 | for (name, cfg) in &configs { | ||
| 88 | writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap(); | ||
| 89 | } | ||
| 90 | |||
| 91 | let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 92 | let out_file = out_dir.join("config.rs").to_string_lossy().to_string(); | ||
| 93 | fs::write(out_file, data).unwrap(); | ||
| 94 | } | ||
diff --git a/embassy-usb/gen_config.py b/embassy-usb/gen_config.py new file mode 100644 index 000000000..67ce359cd --- /dev/null +++ b/embassy-usb/gen_config.py | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | import os | ||
| 2 | |||
| 3 | abspath = os.path.abspath(__file__) | ||
| 4 | dname = os.path.dirname(abspath) | ||
| 5 | os.chdir(dname) | ||
| 6 | |||
| 7 | features = [] | ||
| 8 | |||
| 9 | |||
| 10 | def feature(name, default, min, max, pow2=None): | ||
| 11 | vals = set() | ||
| 12 | val = min | ||
| 13 | while val <= max: | ||
| 14 | vals.add(val) | ||
| 15 | if pow2 == True or (isinstance(pow2, int) and val >= pow2): | ||
| 16 | val *= 2 | ||
| 17 | else: | ||
| 18 | val += 1 | ||
| 19 | vals.add(default) | ||
| 20 | |||
| 21 | features.append( | ||
| 22 | { | ||
| 23 | "name": name, | ||
| 24 | "default": default, | ||
| 25 | "vals": sorted(list(vals)), | ||
| 26 | } | ||
| 27 | ) | ||
| 28 | |||
| 29 | |||
| 30 | feature("max_interface_count", default=4, min=1, max=8) | ||
| 31 | feature("max_handler_count", default=4, min=1, max=8) | ||
| 32 | |||
| 33 | # ========= Update Cargo.toml | ||
| 34 | |||
| 35 | things = "" | ||
| 36 | for f in features: | ||
| 37 | name = f["name"].replace("_", "-") | ||
| 38 | for val in f["vals"]: | ||
| 39 | things += f"{name}-{val} = []" | ||
| 40 | if val == f["default"]: | ||
| 41 | things += " # Default" | ||
| 42 | things += "\n" | ||
| 43 | things += "\n" | ||
| 44 | |||
| 45 | SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n" | ||
| 46 | SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n" | ||
| 47 | HELP = "# Generated by gen_config.py. DO NOT EDIT.\n" | ||
| 48 | with open("Cargo.toml", "r") as f: | ||
| 49 | data = f.read() | ||
| 50 | before, data = data.split(SEPARATOR_START, maxsplit=1) | ||
| 51 | _, after = data.split(SEPARATOR_END, maxsplit=1) | ||
| 52 | data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after | ||
| 53 | with open("Cargo.toml", "w") as f: | ||
| 54 | f.write(data) | ||
| 55 | |||
| 56 | |||
| 57 | # ========= Update build.rs | ||
| 58 | |||
| 59 | things = "" | ||
| 60 | for f in features: | ||
| 61 | name = f["name"].upper() | ||
| 62 | things += f' ("{name}", {f["default"]}),\n' | ||
| 63 | |||
| 64 | SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n" | ||
| 65 | SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n" | ||
| 66 | HELP = " // Generated by gen_config.py. DO NOT EDIT.\n" | ||
| 67 | with open("build.rs", "r") as f: | ||
| 68 | data = f.read() | ||
| 69 | before, data = data.split(SEPARATOR_START, maxsplit=1) | ||
| 70 | _, after = data.split(SEPARATOR_END, maxsplit=1) | ||
| 71 | data = before + SEPARATOR_START + HELP + \ | ||
| 72 | things + " " + SEPARATOR_END + after | ||
| 73 | with open("build.rs", "w") as f: | ||
| 74 | f.write(data) | ||
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index d1cbf674b..305dfa02e 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | use heapless::Vec; | 1 | use heapless::Vec; |
| 2 | 2 | ||
| 3 | use crate::control::ControlHandler; | 3 | use crate::config::*; |
| 4 | use crate::descriptor::{BosWriter, DescriptorWriter}; | 4 | use crate::descriptor::{BosWriter, DescriptorWriter}; |
| 5 | use crate::driver::{Driver, Endpoint, EndpointType}; | 5 | use crate::driver::{Driver, Endpoint, EndpointType}; |
| 6 | #[cfg(feature = "msos-descriptor")] | 6 | #[cfg(feature = "msos-descriptor")] |
| 7 | use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; | 7 | use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; |
| 8 | use crate::types::*; | 8 | use crate::types::*; |
| 9 | use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; | 9 | use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; |
| 10 | 10 | ||
| 11 | #[derive(Debug, Copy, Clone)] | 11 | #[derive(Debug, Copy, Clone)] |
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| @@ -122,8 +122,8 @@ impl<'a> Config<'a> { | |||
| 122 | /// [`UsbDevice`] builder. | 122 | /// [`UsbDevice`] builder. |
| 123 | pub struct Builder<'d, D: Driver<'d>> { | 123 | pub struct Builder<'d, D: Driver<'d>> { |
| 124 | config: Config<'d>, | 124 | config: Config<'d>, |
| 125 | handler: Option<&'d dyn DeviceStateHandler>, | 125 | handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, |
| 126 | interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, | 126 | interfaces: Vec<Interface, MAX_INTERFACE_COUNT>, |
| 127 | control_buf: &'d mut [u8], | 127 | control_buf: &'d mut [u8], |
| 128 | 128 | ||
| 129 | driver: D, | 129 | driver: D, |
| @@ -151,7 +151,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 151 | bos_descriptor_buf: &'d mut [u8], | 151 | bos_descriptor_buf: &'d mut [u8], |
| 152 | #[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], | 152 | #[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], |
| 153 | control_buf: &'d mut [u8], | 153 | control_buf: &'d mut [u8], |
| 154 | handler: Option<&'d dyn DeviceStateHandler>, | ||
| 155 | ) -> Self { | 154 | ) -> Self { |
| 156 | // Magic values specified in USB-IF ECN on IADs. | 155 | // Magic values specified in USB-IF ECN on IADs. |
| 157 | if config.composite_with_iads | 156 | if config.composite_with_iads |
| @@ -179,9 +178,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 179 | 178 | ||
| 180 | Builder { | 179 | Builder { |
| 181 | driver, | 180 | driver, |
| 182 | handler, | ||
| 183 | config, | 181 | config, |
| 184 | interfaces: Vec::new(), | 182 | interfaces: Vec::new(), |
| 183 | handlers: Vec::new(), | ||
| 185 | control_buf, | 184 | control_buf, |
| 186 | next_string_index: STRING_INDEX_CUSTOM_START, | 185 | next_string_index: STRING_INDEX_CUSTOM_START, |
| 187 | 186 | ||
| @@ -205,7 +204,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 205 | UsbDevice::build( | 204 | UsbDevice::build( |
| 206 | self.driver, | 205 | self.driver, |
| 207 | self.config, | 206 | self.config, |
| 208 | self.handler, | 207 | self.handlers, |
| 209 | self.device_descriptor.into_buf(), | 208 | self.device_descriptor.into_buf(), |
| 210 | self.config_descriptor.into_buf(), | 209 | self.config_descriptor.into_buf(), |
| 211 | self.bos_descriptor.writer.into_buf(), | 210 | self.bos_descriptor.writer.into_buf(), |
| @@ -248,6 +247,26 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 248 | } | 247 | } |
| 249 | } | 248 | } |
| 250 | 249 | ||
| 250 | /// Add a Handler. | ||
| 251 | /// | ||
| 252 | /// The Handler is called on some USB bus events, and to handle all control requests not already | ||
| 253 | /// handled by the USB stack. | ||
| 254 | pub fn handler(&mut self, handler: &'d mut dyn Handler) { | ||
| 255 | if self.handlers.push(handler).is_err() { | ||
| 256 | panic!( | ||
| 257 | "embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}", | ||
| 258 | MAX_HANDLER_COUNT | ||
| 259 | ) | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | /// Allocates a new string index. | ||
| 264 | pub fn string(&mut self) -> StringIndex { | ||
| 265 | let index = self.next_string_index; | ||
| 266 | self.next_string_index += 1; | ||
| 267 | StringIndex::new(index) | ||
| 268 | } | ||
| 269 | |||
| 251 | #[cfg(feature = "msos-descriptor")] | 270 | #[cfg(feature = "msos-descriptor")] |
| 252 | /// Add an MS OS 2.0 Descriptor Set. | 271 | /// Add an MS OS 2.0 Descriptor Set. |
| 253 | /// | 272 | /// |
| @@ -301,14 +320,15 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { | |||
| 301 | 320 | ||
| 302 | let number = self.builder.interfaces.len() as _; | 321 | let number = self.builder.interfaces.len() as _; |
| 303 | let iface = Interface { | 322 | let iface = Interface { |
| 304 | handler: None, | ||
| 305 | current_alt_setting: 0, | 323 | current_alt_setting: 0, |
| 306 | num_alt_settings: 0, | 324 | num_alt_settings: 0, |
| 307 | num_strings: 0, | ||
| 308 | }; | 325 | }; |
| 309 | 326 | ||
| 310 | if self.builder.interfaces.push(iface).is_err() { | 327 | if self.builder.interfaces.push(iface).is_err() { |
| 311 | panic!("max interface count reached") | 328 | panic!( |
| 329 | "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}", | ||
| 330 | MAX_INTERFACE_COUNT | ||
| 331 | ) | ||
| 312 | } | 332 | } |
| 313 | 333 | ||
| 314 | InterfaceBuilder { | 334 | InterfaceBuilder { |
| @@ -326,7 +346,7 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { | |||
| 326 | } | 346 | } |
| 327 | 347 | ||
| 328 | if !self.builder.msos_descriptor.is_in_function_subset() { | 348 | if !self.builder.msos_descriptor.is_in_function_subset() { |
| 329 | self.builder.msos_descriptor.function(self.first_interface.0); | 349 | self.builder.msos_descriptor.function(self.first_interface); |
| 330 | } | 350 | } |
| 331 | 351 | ||
| 332 | #[cfg(feature = "msos-descriptor")] | 352 | #[cfg(feature = "msos-descriptor")] |
| @@ -347,17 +367,9 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> { | |||
| 347 | self.interface_number | 367 | self.interface_number |
| 348 | } | 368 | } |
| 349 | 369 | ||
| 350 | pub fn handler(&mut self, handler: &'d mut dyn ControlHandler) { | ||
| 351 | self.builder.interfaces[self.interface_number.0 as usize].handler = Some(handler); | ||
| 352 | } | ||
| 353 | |||
| 354 | /// Allocates a new string index. | 370 | /// Allocates a new string index. |
| 355 | pub fn string(&mut self) -> StringIndex { | 371 | pub fn string(&mut self) -> StringIndex { |
| 356 | let index = self.builder.next_string_index; | 372 | self.builder.string() |
| 357 | self.builder.next_string_index += 1; | ||
| 358 | self.builder.interfaces[self.interface_number.0 as usize].num_strings += 1; | ||
| 359 | |||
| 360 | StringIndex::new(index) | ||
| 361 | } | 373 | } |
| 362 | 374 | ||
| 363 | /// Add an alternate setting to the interface and write its descriptor. | 375 | /// Add an alternate setting to the interface and write its descriptor. |
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index fb9eaeca7..ff82ad40d 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs | |||
| @@ -6,10 +6,10 @@ use core::sync::atomic::{AtomicBool, Ordering}; | |||
| 6 | 6 | ||
| 7 | use embassy_sync::blocking_mutex::CriticalSectionMutex; | 7 | use embassy_sync::blocking_mutex::CriticalSectionMutex; |
| 8 | 8 | ||
| 9 | use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; | 9 | use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; |
| 10 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; | 10 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; |
| 11 | use crate::types::*; | 11 | use crate::types::*; |
| 12 | use crate::Builder; | 12 | use crate::{Builder, Handler}; |
| 13 | 13 | ||
| 14 | /// This should be used as `device_class` when building the `UsbDevice`. | 14 | /// This should be used as `device_class` when building the `UsbDevice`. |
| 15 | pub const USB_CLASS_CDC: u8 = 0x02; | 15 | pub const USB_CLASS_CDC: u8 = 0x02; |
| @@ -67,6 +67,7 @@ pub struct CdcAcmClass<'d, D: Driver<'d>> { | |||
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | struct Control<'a> { | 69 | struct Control<'a> { |
| 70 | comm_if: InterfaceNumber, | ||
| 70 | shared: &'a ControlShared, | 71 | shared: &'a ControlShared, |
| 71 | } | 72 | } |
| 72 | 73 | ||
| @@ -98,7 +99,7 @@ impl<'a> Control<'a> { | |||
| 98 | } | 99 | } |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | impl<'d> ControlHandler for Control<'d> { | 102 | impl<'d> Handler for Control<'d> { |
| 102 | fn reset(&mut self) { | 103 | fn reset(&mut self) { |
| 103 | let shared = self.shared(); | 104 | let shared = self.shared(); |
| 104 | shared.line_coding.lock(|x| x.set(LineCoding::default())); | 105 | shared.line_coding.lock(|x| x.set(LineCoding::default())); |
| @@ -106,12 +107,18 @@ impl<'d> ControlHandler for Control<'d> { | |||
| 106 | shared.rts.store(false, Ordering::Relaxed); | 107 | shared.rts.store(false, Ordering::Relaxed); |
| 107 | } | 108 | } |
| 108 | 109 | ||
| 109 | fn control_out(&mut self, req: control::Request, data: &[u8]) -> OutResponse { | 110 | fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> { |
| 111 | if (req.request_type, req.recipient, req.index) | ||
| 112 | != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) | ||
| 113 | { | ||
| 114 | return None; | ||
| 115 | } | ||
| 116 | |||
| 110 | match req.request { | 117 | match req.request { |
| 111 | REQ_SEND_ENCAPSULATED_COMMAND => { | 118 | REQ_SEND_ENCAPSULATED_COMMAND => { |
| 112 | // We don't actually support encapsulated commands but pretend we do for standards | 119 | // We don't actually support encapsulated commands but pretend we do for standards |
| 113 | // compatibility. | 120 | // compatibility. |
| 114 | OutResponse::Accepted | 121 | Some(OutResponse::Accepted) |
| 115 | } | 122 | } |
| 116 | REQ_SET_LINE_CODING if data.len() >= 7 => { | 123 | REQ_SET_LINE_CODING if data.len() >= 7 => { |
| 117 | let coding = LineCoding { | 124 | let coding = LineCoding { |
| @@ -123,7 +130,7 @@ impl<'d> ControlHandler for Control<'d> { | |||
| 123 | self.shared().line_coding.lock(|x| x.set(coding)); | 130 | self.shared().line_coding.lock(|x| x.set(coding)); |
| 124 | debug!("Set line coding to: {:?}", coding); | 131 | debug!("Set line coding to: {:?}", coding); |
| 125 | 132 | ||
| 126 | OutResponse::Accepted | 133 | Some(OutResponse::Accepted) |
| 127 | } | 134 | } |
| 128 | REQ_SET_CONTROL_LINE_STATE => { | 135 | REQ_SET_CONTROL_LINE_STATE => { |
| 129 | let dtr = (req.value & 0x0001) != 0; | 136 | let dtr = (req.value & 0x0001) != 0; |
| @@ -134,13 +141,19 @@ impl<'d> ControlHandler for Control<'d> { | |||
| 134 | shared.rts.store(rts, Ordering::Relaxed); | 141 | shared.rts.store(rts, Ordering::Relaxed); |
| 135 | debug!("Set dtr {}, rts {}", dtr, rts); | 142 | debug!("Set dtr {}, rts {}", dtr, rts); |
| 136 | 143 | ||
| 137 | OutResponse::Accepted | 144 | Some(OutResponse::Accepted) |
| 138 | } | 145 | } |
| 139 | _ => OutResponse::Rejected, | 146 | _ => Some(OutResponse::Rejected), |
| 140 | } | 147 | } |
| 141 | } | 148 | } |
| 142 | 149 | ||
| 143 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | 150 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> { |
| 151 | if (req.request_type, req.recipient, req.index) | ||
| 152 | != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) | ||
| 153 | { | ||
| 154 | return None; | ||
| 155 | } | ||
| 156 | |||
| 144 | match req.request { | 157 | match req.request { |
| 145 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | 158 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. |
| 146 | REQ_GET_LINE_CODING if req.length == 7 => { | 159 | REQ_GET_LINE_CODING if req.length == 7 => { |
| @@ -151,9 +164,9 @@ impl<'d> ControlHandler for Control<'d> { | |||
| 151 | buf[4] = coding.stop_bits as u8; | 164 | buf[4] = coding.stop_bits as u8; |
| 152 | buf[5] = coding.parity_type as u8; | 165 | buf[5] = coding.parity_type as u8; |
| 153 | buf[6] = coding.data_bits; | 166 | buf[6] = coding.data_bits; |
| 154 | InResponse::Accepted(&buf[0..7]) | 167 | Some(InResponse::Accepted(&buf[0..7])) |
| 155 | } | 168 | } |
| 156 | _ => InResponse::Rejected, | 169 | _ => Some(InResponse::Rejected), |
| 157 | } | 170 | } |
| 158 | } | 171 | } |
| 159 | } | 172 | } |
| @@ -162,17 +175,12 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | |||
| 162 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | 175 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For |
| 163 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | 176 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. |
| 164 | pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self { | 177 | pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self { |
| 165 | let control = state.control.write(Control { shared: &state.shared }); | ||
| 166 | |||
| 167 | let control_shared = &state.shared; | ||
| 168 | |||
| 169 | assert!(builder.control_buf_len() >= 7); | 178 | assert!(builder.control_buf_len() >= 7); |
| 170 | 179 | ||
| 171 | let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE); | 180 | let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE); |
| 172 | 181 | ||
| 173 | // Control interface | 182 | // Control interface |
| 174 | let mut iface = func.interface(); | 183 | let mut iface = func.interface(); |
| 175 | iface.handler(control); | ||
| 176 | let comm_if = iface.interface_number(); | 184 | let comm_if = iface.interface_number(); |
| 177 | let data_if = u8::from(comm_if) + 1; | 185 | let data_if = u8::from(comm_if) + 1; |
| 178 | let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None); | 186 | 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> { | |||
| 213 | let read_ep = alt.endpoint_bulk_out(max_packet_size); | 221 | let read_ep = alt.endpoint_bulk_out(max_packet_size); |
| 214 | let write_ep = alt.endpoint_bulk_in(max_packet_size); | 222 | let write_ep = alt.endpoint_bulk_in(max_packet_size); |
| 215 | 223 | ||
| 224 | drop(func); | ||
| 225 | |||
| 226 | let control = state.control.write(Control { | ||
| 227 | shared: &state.shared, | ||
| 228 | comm_if, | ||
| 229 | }); | ||
| 230 | builder.handler(control); | ||
| 231 | |||
| 232 | let control_shared = &state.shared; | ||
| 233 | |||
| 216 | CdcAcmClass { | 234 | CdcAcmClass { |
| 217 | _comm_ep: comm_ep, | 235 | _comm_ep: comm_ep, |
| 218 | _data_if: data_if, | 236 | _data_if: data_if, |
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index d6c7d37e6..262499ccb 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs | |||
| @@ -17,10 +17,10 @@ | |||
| 17 | use core::intrinsics::copy_nonoverlapping; | 17 | use core::intrinsics::copy_nonoverlapping; |
| 18 | use core::mem::{size_of, MaybeUninit}; | 18 | use core::mem::{size_of, MaybeUninit}; |
| 19 | 19 | ||
| 20 | use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; | 20 | use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; |
| 21 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; | 21 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; |
| 22 | use crate::types::*; | 22 | use crate::types::*; |
| 23 | use crate::Builder; | 23 | use crate::{Builder, Handler}; |
| 24 | 24 | ||
| 25 | pub mod embassy_net; | 25 | pub mod embassy_net; |
| 26 | 26 | ||
| @@ -117,8 +117,7 @@ fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] { | |||
| 117 | 117 | ||
| 118 | /// Internal state for the CDC-NCM class. | 118 | /// Internal state for the CDC-NCM class. |
| 119 | pub struct State<'a> { | 119 | pub struct State<'a> { |
| 120 | comm_control: MaybeUninit<CommControl<'a>>, | 120 | control: MaybeUninit<Control<'a>>, |
| 121 | data_control: MaybeUninit<DataControl>, | ||
| 122 | shared: ControlShared, | 121 | shared: ControlShared, |
| 123 | } | 122 | } |
| 124 | 123 | ||
| @@ -126,8 +125,7 @@ impl<'a> State<'a> { | |||
| 126 | /// Create a new `State`. | 125 | /// Create a new `State`. |
| 127 | pub fn new() -> Self { | 126 | pub fn new() -> Self { |
| 128 | Self { | 127 | Self { |
| 129 | comm_control: MaybeUninit::uninit(), | 128 | control: MaybeUninit::uninit(), |
| 130 | data_control: MaybeUninit::uninit(), | ||
| 131 | shared: Default::default(), | 129 | shared: Default::default(), |
| 132 | } | 130 | } |
| 133 | } | 131 | } |
| @@ -144,29 +142,55 @@ impl Default for ControlShared { | |||
| 144 | } | 142 | } |
| 145 | } | 143 | } |
| 146 | 144 | ||
| 147 | struct CommControl<'a> { | 145 | struct Control<'a> { |
| 148 | mac_addr_string: StringIndex, | 146 | mac_addr_string: StringIndex, |
| 149 | shared: &'a ControlShared, | 147 | shared: &'a ControlShared, |
| 150 | mac_addr_str: [u8; 12], | 148 | mac_addr_str: [u8; 12], |
| 149 | comm_if: InterfaceNumber, | ||
| 150 | data_if: InterfaceNumber, | ||
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | impl<'d> ControlHandler for CommControl<'d> { | 153 | impl<'d> Handler for Control<'d> { |
| 154 | fn control_out(&mut self, req: control::Request, _data: &[u8]) -> OutResponse { | 154 | fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) { |
| 155 | if iface != self.data_if { | ||
| 156 | return; | ||
| 157 | } | ||
| 158 | |||
| 159 | match alternate_setting { | ||
| 160 | ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"), | ||
| 161 | ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"), | ||
| 162 | _ => unreachable!(), | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | fn control_out(&mut self, req: control::Request, _data: &[u8]) -> Option<OutResponse> { | ||
| 167 | if (req.request_type, req.recipient, req.index) | ||
| 168 | != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) | ||
| 169 | { | ||
| 170 | return None; | ||
| 171 | } | ||
| 172 | |||
| 155 | match req.request { | 173 | match req.request { |
| 156 | REQ_SEND_ENCAPSULATED_COMMAND => { | 174 | REQ_SEND_ENCAPSULATED_COMMAND => { |
| 157 | // We don't actually support encapsulated commands but pretend we do for standards | 175 | // We don't actually support encapsulated commands but pretend we do for standards |
| 158 | // compatibility. | 176 | // compatibility. |
| 159 | OutResponse::Accepted | 177 | Some(OutResponse::Accepted) |
| 160 | } | 178 | } |
| 161 | REQ_SET_NTB_INPUT_SIZE => { | 179 | REQ_SET_NTB_INPUT_SIZE => { |
| 162 | // TODO | 180 | // TODO |
| 163 | OutResponse::Accepted | 181 | Some(OutResponse::Accepted) |
| 164 | } | 182 | } |
| 165 | _ => OutResponse::Rejected, | 183 | _ => Some(OutResponse::Rejected), |
| 166 | } | 184 | } |
| 167 | } | 185 | } |
| 168 | 186 | ||
| 169 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | 187 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> { |
| 188 | if (req.request_type, req.recipient, req.index) | ||
| 189 | != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) | ||
| 190 | { | ||
| 191 | return None; | ||
| 192 | } | ||
| 193 | |||
| 170 | match req.request { | 194 | match req.request { |
| 171 | REQ_GET_NTB_PARAMETERS => { | 195 | REQ_GET_NTB_PARAMETERS => { |
| 172 | let res = NtbParameters { | 196 | let res = NtbParameters { |
| @@ -187,9 +211,9 @@ impl<'d> ControlHandler for CommControl<'d> { | |||
| 187 | max_datagram_count: 1, // We only decode 1 packet per NTB | 211 | max_datagram_count: 1, // We only decode 1 packet per NTB |
| 188 | }, | 212 | }, |
| 189 | }; | 213 | }; |
| 190 | InResponse::Accepted(byteify(buf, res)) | 214 | Some(InResponse::Accepted(byteify(buf, res))) |
| 191 | } | 215 | } |
| 192 | _ => InResponse::Rejected, | 216 | _ => Some(InResponse::Rejected), |
| 193 | } | 217 | } |
| 194 | } | 218 | } |
| 195 | 219 | ||
| @@ -214,18 +238,6 @@ impl<'d> ControlHandler for CommControl<'d> { | |||
| 214 | } | 238 | } |
| 215 | } | 239 | } |
| 216 | 240 | ||
| 217 | struct DataControl {} | ||
| 218 | |||
| 219 | impl ControlHandler for DataControl { | ||
| 220 | fn set_alternate_setting(&mut self, alternate_setting: u8) { | ||
| 221 | match alternate_setting { | ||
| 222 | ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"), | ||
| 223 | ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"), | ||
| 224 | _ => unreachable!(), | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | /// CDC-NCM class | 241 | /// CDC-NCM class |
| 230 | pub struct CdcNcmClass<'d, D: Driver<'d>> { | 242 | pub struct CdcNcmClass<'d, D: Driver<'d>> { |
| 231 | _comm_if: InterfaceNumber, | 243 | _comm_if: InterfaceNumber, |
| @@ -253,11 +265,6 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | |||
| 253 | // Control interface | 265 | // Control interface |
| 254 | let mut iface = func.interface(); | 266 | let mut iface = func.interface(); |
| 255 | let mac_addr_string = iface.string(); | 267 | let mac_addr_string = iface.string(); |
| 256 | iface.handler(state.comm_control.write(CommControl { | ||
| 257 | mac_addr_string, | ||
| 258 | shared: &state.shared, | ||
| 259 | mac_addr_str: [0; 12], | ||
| 260 | })); | ||
| 261 | let comm_if = iface.interface_number(); | 268 | let comm_if = iface.interface_number(); |
| 262 | let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None); | 269 | let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None); |
| 263 | 270 | ||
| @@ -307,13 +314,23 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | |||
| 307 | 314 | ||
| 308 | // Data interface | 315 | // Data interface |
| 309 | let mut iface = func.interface(); | 316 | let mut iface = func.interface(); |
| 310 | iface.handler(state.data_control.write(DataControl {})); | ||
| 311 | let data_if = iface.interface_number(); | 317 | let data_if = iface.interface_number(); |
| 312 | let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); | 318 | let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); |
| 313 | let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); | 319 | let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); |
| 314 | let read_ep = alt.endpoint_bulk_out(max_packet_size); | 320 | let read_ep = alt.endpoint_bulk_out(max_packet_size); |
| 315 | let write_ep = alt.endpoint_bulk_in(max_packet_size); | 321 | let write_ep = alt.endpoint_bulk_in(max_packet_size); |
| 316 | 322 | ||
| 323 | drop(func); | ||
| 324 | |||
| 325 | let control = state.control.write(Control { | ||
| 326 | mac_addr_string, | ||
| 327 | shared: &state.shared, | ||
| 328 | mac_addr_str: [0; 12], | ||
| 329 | comm_if, | ||
| 330 | data_if, | ||
| 331 | }); | ||
| 332 | builder.handler(control); | ||
| 333 | |||
| 317 | CdcNcmClass { | 334 | CdcNcmClass { |
| 318 | _comm_if: comm_if, | 335 | _comm_if: comm_if, |
| 319 | comm_ep, | 336 | comm_ep, |
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 0283c1124..974268c62 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs | |||
| @@ -9,9 +9,10 @@ use ssmarshal::serialize; | |||
| 9 | #[cfg(feature = "usbd-hid")] | 9 | #[cfg(feature = "usbd-hid")] |
| 10 | use usbd_hid::descriptor::AsInputReport; | 10 | use usbd_hid::descriptor::AsInputReport; |
| 11 | 11 | ||
| 12 | use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; | 12 | use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType}; |
| 13 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; | 13 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; |
| 14 | use crate::Builder; | 14 | use crate::types::InterfaceNumber; |
| 15 | use crate::{Builder, Handler}; | ||
| 15 | 16 | ||
| 16 | const USB_CLASS_HID: u8 = 0x03; | 17 | const USB_CLASS_HID: u8 = 0x03; |
| 17 | const USB_SUBCLASS_NONE: u8 = 0x00; | 18 | const USB_SUBCLASS_NONE: u8 = 0x00; |
| @@ -100,17 +101,11 @@ fn build<'d, D: Driver<'d>>( | |||
| 100 | config: Config<'d>, | 101 | config: Config<'d>, |
| 101 | with_out_endpoint: bool, | 102 | with_out_endpoint: bool, |
| 102 | ) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { | 103 | ) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { |
| 103 | let control = state.control.write(Control::new( | ||
| 104 | config.report_descriptor, | ||
| 105 | config.request_handler, | ||
| 106 | &state.out_report_offset, | ||
| 107 | )); | ||
| 108 | |||
| 109 | let len = config.report_descriptor.len(); | 104 | let len = config.report_descriptor.len(); |
| 110 | 105 | ||
| 111 | let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); | 106 | let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); |
| 112 | let mut iface = func.interface(); | 107 | let mut iface = func.interface(); |
| 113 | iface.handler(control); | 108 | let if_num = iface.interface_number(); |
| 114 | let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); | 109 | let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); |
| 115 | 110 | ||
| 116 | // HID descriptor | 111 | // HID descriptor |
| @@ -139,6 +134,16 @@ fn build<'d, D: Driver<'d>>( | |||
| 139 | None | 134 | None |
| 140 | }; | 135 | }; |
| 141 | 136 | ||
| 137 | drop(func); | ||
| 138 | |||
| 139 | let control = state.control.write(Control::new( | ||
| 140 | if_num, | ||
| 141 | config.report_descriptor, | ||
| 142 | config.request_handler, | ||
| 143 | &state.out_report_offset, | ||
| 144 | )); | ||
| 145 | builder.handler(control); | ||
| 146 | |||
| 142 | (ep_out, ep_in, &state.out_report_offset) | 147 | (ep_out, ep_in, &state.out_report_offset) |
| 143 | } | 148 | } |
| 144 | 149 | ||
| @@ -400,6 +405,7 @@ pub trait RequestHandler { | |||
| 400 | } | 405 | } |
| 401 | 406 | ||
| 402 | struct Control<'d> { | 407 | struct Control<'d> { |
| 408 | if_num: InterfaceNumber, | ||
| 403 | report_descriptor: &'d [u8], | 409 | report_descriptor: &'d [u8], |
| 404 | request_handler: Option<&'d dyn RequestHandler>, | 410 | request_handler: Option<&'d dyn RequestHandler>, |
| 405 | out_report_offset: &'d AtomicUsize, | 411 | out_report_offset: &'d AtomicUsize, |
| @@ -408,11 +414,13 @@ struct Control<'d> { | |||
| 408 | 414 | ||
| 409 | impl<'d> Control<'d> { | 415 | impl<'d> Control<'d> { |
| 410 | fn new( | 416 | fn new( |
| 417 | if_num: InterfaceNumber, | ||
| 411 | report_descriptor: &'d [u8], | 418 | report_descriptor: &'d [u8], |
| 412 | request_handler: Option<&'d dyn RequestHandler>, | 419 | request_handler: Option<&'d dyn RequestHandler>, |
| 413 | out_report_offset: &'d AtomicUsize, | 420 | out_report_offset: &'d AtomicUsize, |
| 414 | ) -> Self { | 421 | ) -> Self { |
| 415 | Control { | 422 | Control { |
| 423 | if_num, | ||
| 416 | report_descriptor, | 424 | report_descriptor, |
| 417 | request_handler, | 425 | request_handler, |
| 418 | out_report_offset, | 426 | out_report_offset, |
| @@ -438,88 +446,100 @@ impl<'d> Control<'d> { | |||
| 438 | } | 446 | } |
| 439 | } | 447 | } |
| 440 | 448 | ||
| 441 | impl<'d> ControlHandler for Control<'d> { | 449 | impl<'d> Handler for Control<'d> { |
| 442 | fn reset(&mut self) { | 450 | fn reset(&mut self) { |
| 443 | self.out_report_offset.store(0, Ordering::Release); | 451 | self.out_report_offset.store(0, Ordering::Release); |
| 444 | } | 452 | } |
| 445 | 453 | ||
| 446 | fn get_descriptor<'a>(&'a mut self, req: Request, _buf: &'a mut [u8]) -> InResponse<'a> { | 454 | fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> { |
| 447 | match (req.value >> 8) as u8 { | 455 | if (req.request_type, req.recipient, req.index) |
| 448 | HID_DESC_DESCTYPE_HID_REPORT => InResponse::Accepted(self.report_descriptor), | 456 | != (RequestType::Class, Recipient::Interface, self.if_num.0 as u16) |
| 449 | HID_DESC_DESCTYPE_HID => InResponse::Accepted(&self.hid_descriptor), | 457 | { |
| 450 | _ => InResponse::Rejected, | 458 | return None; |
| 451 | } | 459 | } |
| 452 | } | ||
| 453 | 460 | ||
| 454 | fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { | ||
| 455 | trace!("HID control_out {:?} {=[u8]:x}", req, data); | 461 | trace!("HID control_out {:?} {=[u8]:x}", req, data); |
| 456 | if let RequestType::Class = req.request_type { | 462 | match req.request { |
| 457 | match req.request { | 463 | HID_REQ_SET_IDLE => { |
| 458 | HID_REQ_SET_IDLE => { | 464 | if let Some(handler) = self.request_handler { |
| 459 | if let Some(handler) = self.request_handler { | 465 | let id = req.value as u8; |
| 460 | let id = req.value as u8; | 466 | let id = (id != 0).then(|| ReportId::In(id)); |
| 461 | let id = (id != 0).then(|| ReportId::In(id)); | 467 | let dur = u32::from(req.value >> 8); |
| 462 | let dur = u32::from(req.value >> 8); | 468 | let dur = if dur == 0 { u32::MAX } else { 4 * dur }; |
| 463 | let dur = if dur == 0 { u32::MAX } else { 4 * dur }; | 469 | handler.set_idle_ms(id, dur); |
| 464 | handler.set_idle_ms(id, dur); | ||
| 465 | } | ||
| 466 | OutResponse::Accepted | ||
| 467 | } | 470 | } |
| 468 | HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { | 471 | Some(OutResponse::Accepted) |
| 469 | (Ok(id), Some(handler)) => handler.set_report(id, data), | 472 | } |
| 470 | _ => OutResponse::Rejected, | 473 | HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { |
| 471 | }, | 474 | (Ok(id), Some(handler)) => Some(handler.set_report(id, data)), |
| 472 | HID_REQ_SET_PROTOCOL => { | 475 | _ => Some(OutResponse::Rejected), |
| 473 | if req.value == 1 { | 476 | }, |
| 474 | OutResponse::Accepted | 477 | HID_REQ_SET_PROTOCOL => { |
| 475 | } else { | 478 | if req.value == 1 { |
| 476 | warn!("HID Boot Protocol is unsupported."); | 479 | Some(OutResponse::Accepted) |
| 477 | OutResponse::Rejected // UNSUPPORTED: Boot Protocol | 480 | } else { |
| 478 | } | 481 | warn!("HID Boot Protocol is unsupported."); |
| 482 | Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol | ||
| 479 | } | 483 | } |
| 480 | _ => OutResponse::Rejected, | ||
| 481 | } | 484 | } |
| 482 | } else { | 485 | _ => Some(OutResponse::Rejected), |
| 483 | OutResponse::Rejected // UNSUPPORTED: SET_DESCRIPTOR | ||
| 484 | } | 486 | } |
| 485 | } | 487 | } |
| 486 | 488 | ||
| 487 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | 489 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> { |
| 488 | trace!("HID control_in {:?}", req); | 490 | if req.index != self.if_num.0 as u16 { |
| 489 | match req.request { | 491 | return None; |
| 490 | HID_REQ_GET_REPORT => { | 492 | } |
| 491 | let size = match ReportId::try_from(req.value) { | 493 | |
| 492 | Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), | 494 | match (req.request_type, req.recipient) { |
| 493 | Err(_) => None, | 495 | (RequestType::Standard, Recipient::Interface) => match req.request { |
| 494 | }; | 496 | Request::GET_DESCRIPTOR => match (req.value >> 8) as u8 { |
| 495 | 497 | HID_DESC_DESCTYPE_HID_REPORT => Some(InResponse::Accepted(self.report_descriptor)), | |
| 496 | if let Some(size) = size { | 498 | HID_DESC_DESCTYPE_HID => Some(InResponse::Accepted(&self.hid_descriptor)), |
| 497 | InResponse::Accepted(&buf[0..size]) | 499 | _ => Some(InResponse::Rejected), |
| 498 | } else { | 500 | }, |
| 499 | InResponse::Rejected | 501 | |
| 500 | } | 502 | _ => Some(InResponse::Rejected), |
| 501 | } | 503 | }, |
| 502 | HID_REQ_GET_IDLE => { | 504 | (RequestType::Class, Recipient::Interface) => { |
| 503 | if let Some(handler) = self.request_handler { | 505 | trace!("HID control_in {:?}", req); |
| 504 | let id = req.value as u8; | 506 | match req.request { |
| 505 | let id = (id != 0).then(|| ReportId::In(id)); | 507 | HID_REQ_GET_REPORT => { |
| 506 | if let Some(dur) = handler.get_idle_ms(id) { | 508 | let size = match ReportId::try_from(req.value) { |
| 507 | let dur = u8::try_from(dur / 4).unwrap_or(0); | 509 | Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), |
| 508 | buf[0] = dur; | 510 | Err(_) => None, |
| 509 | InResponse::Accepted(&buf[0..1]) | 511 | }; |
| 510 | } else { | 512 | |
| 511 | InResponse::Rejected | 513 | if let Some(size) = size { |
| 514 | Some(InResponse::Accepted(&buf[0..size])) | ||
| 515 | } else { | ||
| 516 | Some(InResponse::Rejected) | ||
| 517 | } | ||
| 512 | } | 518 | } |
| 513 | } else { | 519 | HID_REQ_GET_IDLE => { |
| 514 | InResponse::Rejected | 520 | if let Some(handler) = self.request_handler { |
| 521 | let id = req.value as u8; | ||
| 522 | let id = (id != 0).then(|| ReportId::In(id)); | ||
| 523 | if let Some(dur) = handler.get_idle_ms(id) { | ||
| 524 | let dur = u8::try_from(dur / 4).unwrap_or(0); | ||
| 525 | buf[0] = dur; | ||
| 526 | Some(InResponse::Accepted(&buf[0..1])) | ||
| 527 | } else { | ||
| 528 | Some(InResponse::Rejected) | ||
| 529 | } | ||
| 530 | } else { | ||
| 531 | Some(InResponse::Rejected) | ||
| 532 | } | ||
| 533 | } | ||
| 534 | HID_REQ_GET_PROTOCOL => { | ||
| 535 | // UNSUPPORTED: Boot Protocol | ||
| 536 | buf[0] = 1; | ||
| 537 | Some(InResponse::Accepted(&buf[0..1])) | ||
| 538 | } | ||
| 539 | _ => Some(InResponse::Rejected), | ||
| 515 | } | 540 | } |
| 516 | } | 541 | } |
| 517 | HID_REQ_GET_PROTOCOL => { | 542 | _ => None, |
| 518 | // UNSUPPORTED: Boot Protocol | ||
| 519 | buf[0] = 1; | ||
| 520 | InResponse::Accepted(&buf[0..1]) | ||
| 521 | } | ||
| 522 | _ => InResponse::Rejected, | ||
| 523 | } | 543 | } |
| 524 | } | 544 | } |
| 525 | } | 545 | } |
diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 39b499f03..ceccfd85b 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | use core::mem; | 2 | use core::mem; |
| 3 | 3 | ||
| 4 | use crate::driver::Direction; | 4 | use crate::driver::Direction; |
| 5 | use crate::types::StringIndex; | ||
| 6 | 5 | ||
| 7 | /// Control request type. | 6 | /// Control request type. |
| 8 | #[repr(u8)] | 7 | #[repr(u8)] |
| @@ -145,60 +144,3 @@ pub enum InResponse<'a> { | |||
| 145 | /// The request was rejected. | 144 | /// The request was rejected. |
| 146 | Rejected, | 145 | Rejected, |
| 147 | } | 146 | } |
| 148 | |||
| 149 | /// Handler for control requests. | ||
| 150 | /// | ||
| 151 | /// All methods are optional callbacks that will be called by | ||
| 152 | /// [`UsbDevice::run()`](crate::UsbDevice::run) | ||
| 153 | pub trait ControlHandler { | ||
| 154 | /// Called after a USB reset after the bus reset sequence is complete. | ||
| 155 | fn reset(&mut self) {} | ||
| 156 | |||
| 157 | /// Called when a "set alternate setting" control request is done on the interface. | ||
| 158 | fn set_alternate_setting(&mut self, alternate_setting: u8) { | ||
| 159 | let _ = alternate_setting; | ||
| 160 | } | ||
| 161 | |||
| 162 | /// Called when a control request is received with direction HostToDevice. | ||
| 163 | /// | ||
| 164 | /// # Arguments | ||
| 165 | /// | ||
| 166 | /// * `req` - The request from the SETUP packet. | ||
| 167 | /// * `data` - The data from the request. | ||
| 168 | fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { | ||
| 169 | let _ = (req, data); | ||
| 170 | OutResponse::Rejected | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Called when a control request is received with direction DeviceToHost. | ||
| 174 | /// | ||
| 175 | /// You should write the response somewhere (usually to `buf`, but you may use another buffer | ||
| 176 | /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`. | ||
| 177 | /// | ||
| 178 | /// # Arguments | ||
| 179 | /// | ||
| 180 | /// * `req` - The request from the SETUP packet. | ||
| 181 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | ||
| 182 | let _ = (req, buf); | ||
| 183 | InResponse::Rejected | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Called when a GET DESCRIPTOR control request is received on the interface. | ||
| 187 | /// | ||
| 188 | /// You should write the response somewhere (usually to `buf`, but you may use another buffer | ||
| 189 | /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`. | ||
| 190 | /// | ||
| 191 | /// # Arguments | ||
| 192 | /// | ||
| 193 | /// * `req` - The request from the SETUP packet. | ||
| 194 | fn get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | ||
| 195 | let _ = (req, buf); | ||
| 196 | InResponse::Rejected | ||
| 197 | } | ||
| 198 | |||
| 199 | /// Called when a GET_DESCRIPTOR STRING control request is received. | ||
| 200 | fn get_string(&mut self, index: StringIndex, lang_id: u16) -> Option<&str> { | ||
| 201 | let _ = (index, lang_id); | ||
| 202 | None | ||
| 203 | } | ||
| 204 | } | ||
diff --git a/embassy-usb/src/descriptor_reader.rs b/embassy-usb/src/descriptor_reader.rs index d64bcb73b..05adcce60 100644 --- a/embassy-usb/src/descriptor_reader.rs +++ b/embassy-usb/src/descriptor_reader.rs | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | use crate::descriptor::descriptor_type; | 1 | use crate::descriptor::descriptor_type; |
| 2 | use crate::driver::EndpointAddress; | 2 | use crate::driver::EndpointAddress; |
| 3 | use crate::types::InterfaceNumber; | ||
| 3 | 4 | ||
| 4 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] | 5 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 6 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| @@ -75,7 +76,7 @@ impl<'a, 'b> Iterator for DescriptorIter<'a, 'b> { | |||
| 75 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 76 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 76 | pub struct EndpointInfo { | 77 | pub struct EndpointInfo { |
| 77 | pub configuration: u8, | 78 | pub configuration: u8, |
| 78 | pub interface: u8, | 79 | pub interface: InterfaceNumber, |
| 79 | pub interface_alt: u8, | 80 | pub interface_alt: u8, |
| 80 | pub ep_address: EndpointAddress, | 81 | pub ep_address: EndpointAddress, |
| 81 | } | 82 | } |
| @@ -83,7 +84,7 @@ pub struct EndpointInfo { | |||
| 83 | pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<(), ReadError> { | 84 | pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<(), ReadError> { |
| 84 | let mut ep = EndpointInfo { | 85 | let mut ep = EndpointInfo { |
| 85 | configuration: 0, | 86 | configuration: 0, |
| 86 | interface: 0, | 87 | interface: InterfaceNumber(0), |
| 87 | interface_alt: 0, | 88 | interface_alt: 0, |
| 88 | ep_address: EndpointAddress::from(0), | 89 | ep_address: EndpointAddress::from(0), |
| 89 | }; | 90 | }; |
| @@ -96,7 +97,7 @@ pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result< | |||
| 96 | ep.configuration = r.read_u8()?; | 97 | ep.configuration = r.read_u8()?; |
| 97 | } | 98 | } |
| 98 | descriptor_type::INTERFACE => { | 99 | descriptor_type::INTERFACE => { |
| 99 | ep.interface = r.read_u8()?; | 100 | ep.interface = InterfaceNumber(r.read_u8()?); |
| 100 | ep.interface_alt = r.read_u8()?; | 101 | ep.interface_alt = r.read_u8()?; |
| 101 | } | 102 | } |
| 102 | descriptor_type::ENDPOINT => { | 103 | descriptor_type::ENDPOINT => { |
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index aec18524b..bfeccd5fe 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs | |||
| @@ -16,10 +16,16 @@ mod descriptor_reader; | |||
| 16 | pub mod msos; | 16 | pub mod msos; |
| 17 | pub mod types; | 17 | pub mod types; |
| 18 | 18 | ||
| 19 | mod config { | ||
| 20 | #![allow(unused)] | ||
| 21 | include!(concat!(env!("OUT_DIR"), "/config.rs")); | ||
| 22 | } | ||
| 23 | |||
| 19 | use embassy_futures::select::{select, Either}; | 24 | use embassy_futures::select::{select, Either}; |
| 20 | use heapless::Vec; | 25 | use heapless::Vec; |
| 21 | 26 | ||
| 22 | pub use crate::builder::{Builder, Config}; | 27 | pub use crate::builder::{Builder, Config}; |
| 28 | use crate::config::*; | ||
| 23 | use crate::control::*; | 29 | use crate::control::*; |
| 24 | use crate::descriptor::*; | 30 | use crate::descriptor::*; |
| 25 | use crate::descriptor_reader::foreach_endpoint; | 31 | use crate::descriptor_reader::foreach_endpoint; |
| @@ -71,40 +77,92 @@ pub const CONFIGURATION_NONE: u8 = 0; | |||
| 71 | /// The bConfiguration value for the single configuration supported by this device. | 77 | /// The bConfiguration value for the single configuration supported by this device. |
| 72 | pub const CONFIGURATION_VALUE: u8 = 1; | 78 | pub const CONFIGURATION_VALUE: u8 = 1; |
| 73 | 79 | ||
| 74 | /// Maximum interface count, configured at compile time. | ||
| 75 | pub const MAX_INTERFACE_COUNT: usize = 4; | ||
| 76 | |||
| 77 | const STRING_INDEX_MANUFACTURER: u8 = 1; | 80 | const STRING_INDEX_MANUFACTURER: u8 = 1; |
| 78 | const STRING_INDEX_PRODUCT: u8 = 2; | 81 | const STRING_INDEX_PRODUCT: u8 = 2; |
| 79 | const STRING_INDEX_SERIAL_NUMBER: u8 = 3; | 82 | const STRING_INDEX_SERIAL_NUMBER: u8 = 3; |
| 80 | const STRING_INDEX_CUSTOM_START: u8 = 4; | 83 | const STRING_INDEX_CUSTOM_START: u8 = 4; |
| 81 | 84 | ||
| 82 | /// A handler trait for changes in the device state of the [UsbDevice]. | 85 | /// Handler for device events and control requests. |
| 83 | pub trait DeviceStateHandler { | 86 | /// |
| 87 | /// All methods are optional callbacks that will be called by | ||
| 88 | /// [`UsbDevice::run()`](crate::UsbDevice::run) | ||
| 89 | pub trait Handler { | ||
| 84 | /// Called when the USB device has been enabled or disabled. | 90 | /// Called when the USB device has been enabled or disabled. |
| 85 | fn enabled(&self, _enabled: bool) {} | 91 | fn enabled(&mut self, _enabled: bool) {} |
| 86 | 92 | ||
| 87 | /// Called when the host resets the device. | 93 | /// Called after a USB reset after the bus reset sequence is complete. |
| 88 | fn reset(&self) {} | 94 | fn reset(&mut self) {} |
| 89 | 95 | ||
| 90 | /// Called when the host has set the address of the device to `addr`. | 96 | /// Called when the host has set the address of the device to `addr`. |
| 91 | fn addressed(&self, _addr: u8) {} | 97 | fn addressed(&mut self, _addr: u8) {} |
| 92 | 98 | ||
| 93 | /// Called when the host has enabled or disabled the configuration of the device. | 99 | /// Called when the host has enabled or disabled the configuration of the device. |
| 94 | fn configured(&self, _configured: bool) {} | 100 | fn configured(&mut self, _configured: bool) {} |
| 95 | 101 | ||
| 96 | /// Called when the bus has entered or exited the suspend state. | 102 | /// Called when the bus has entered or exited the suspend state. |
| 97 | fn suspended(&self, _suspended: bool) {} | 103 | fn suspended(&mut self, _suspended: bool) {} |
| 98 | 104 | ||
| 99 | /// Called when remote wakeup feature is enabled or disabled. | 105 | /// Called when remote wakeup feature is enabled or disabled. |
| 100 | fn remote_wakeup_enabled(&self, _enabled: bool) {} | 106 | fn remote_wakeup_enabled(&mut self, _enabled: bool) {} |
| 107 | |||
| 108 | /// Called when a "set alternate setting" control request is done on the interface. | ||
| 109 | fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) { | ||
| 110 | let _ = iface; | ||
| 111 | let _ = alternate_setting; | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Called when a control request is received with direction HostToDevice. | ||
| 115 | /// | ||
| 116 | /// # Arguments | ||
| 117 | /// | ||
| 118 | /// * `req` - The request from the SETUP packet. | ||
| 119 | /// * `data` - The data from the request. | ||
| 120 | /// | ||
| 121 | /// # Returns | ||
| 122 | /// | ||
| 123 | /// If you didn't handle this request (for example if it's for the wrong interface), return | ||
| 124 | /// `None`. In this case, the the USB stack will continue calling the other handlers, to see | ||
| 125 | /// if another handles it. | ||
| 126 | /// | ||
| 127 | /// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack | ||
| 128 | /// respond to the control request, and stop calling other handlers. | ||
| 129 | fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> { | ||
| 130 | let _ = (req, data); | ||
| 131 | None | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Called when a control request is received with direction DeviceToHost. | ||
| 135 | /// | ||
| 136 | /// You should write the response somewhere (usually to `buf`, but you may use another buffer | ||
| 137 | /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`. | ||
| 138 | /// | ||
| 139 | /// # Arguments | ||
| 140 | /// | ||
| 141 | /// * `req` - The request from the SETUP packet. | ||
| 142 | /// | ||
| 143 | /// # Returns | ||
| 144 | /// | ||
| 145 | /// If you didn't handle this request (for example if it's for the wrong interface), return | ||
| 146 | /// `None`. In this case, the the USB stack will continue calling the other handlers, to see | ||
| 147 | /// if another handles it. | ||
| 148 | /// | ||
| 149 | /// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack | ||
| 150 | /// respond to the control request, and stop calling other handlers. | ||
| 151 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> { | ||
| 152 | let _ = (req, buf); | ||
| 153 | None | ||
| 154 | } | ||
| 155 | |||
| 156 | /// Called when a GET_DESCRIPTOR STRING control request is received. | ||
| 157 | fn get_string(&mut self, index: StringIndex, lang_id: u16) -> Option<&str> { | ||
| 158 | let _ = (index, lang_id); | ||
| 159 | None | ||
| 160 | } | ||
| 101 | } | 161 | } |
| 102 | 162 | ||
| 103 | struct Interface<'d> { | 163 | struct Interface { |
| 104 | handler: Option<&'d mut dyn ControlHandler>, | ||
| 105 | current_alt_setting: u8, | 164 | current_alt_setting: u8, |
| 106 | num_alt_settings: u8, | 165 | num_alt_settings: u8, |
| 107 | num_strings: u8, | ||
| 108 | } | 166 | } |
| 109 | 167 | ||
| 110 | /// Main struct for the USB device stack. | 168 | /// Main struct for the USB device stack. |
| @@ -116,7 +174,6 @@ pub struct UsbDevice<'d, D: Driver<'d>> { | |||
| 116 | 174 | ||
| 117 | struct Inner<'d, D: Driver<'d>> { | 175 | struct Inner<'d, D: Driver<'d>> { |
| 118 | bus: D::Bus, | 176 | bus: D::Bus, |
| 119 | handler: Option<&'d dyn DeviceStateHandler>, | ||
| 120 | 177 | ||
| 121 | config: Config<'d>, | 178 | config: Config<'d>, |
| 122 | device_descriptor: &'d [u8], | 179 | device_descriptor: &'d [u8], |
| @@ -135,7 +192,9 @@ struct Inner<'d, D: Driver<'d>> { | |||
| 135 | /// instead of regular `accept()`. | 192 | /// instead of regular `accept()`. |
| 136 | set_address_pending: bool, | 193 | set_address_pending: bool, |
| 137 | 194 | ||
| 138 | interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, | 195 | interfaces: Vec<Interface, MAX_INTERFACE_COUNT>, |
| 196 | handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, | ||
| 197 | |||
| 139 | #[cfg(feature = "msos-descriptor")] | 198 | #[cfg(feature = "msos-descriptor")] |
| 140 | msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, | 199 | msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, |
| 141 | } | 200 | } |
| @@ -144,11 +203,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 144 | pub(crate) fn build( | 203 | pub(crate) fn build( |
| 145 | driver: D, | 204 | driver: D, |
| 146 | config: Config<'d>, | 205 | config: Config<'d>, |
| 147 | handler: Option<&'d dyn DeviceStateHandler>, | 206 | handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, |
| 148 | device_descriptor: &'d [u8], | 207 | device_descriptor: &'d [u8], |
| 149 | config_descriptor: &'d [u8], | 208 | config_descriptor: &'d [u8], |
| 150 | bos_descriptor: &'d [u8], | 209 | bos_descriptor: &'d [u8], |
| 151 | interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, | 210 | interfaces: Vec<Interface, MAX_INTERFACE_COUNT>, |
| 152 | control_buf: &'d mut [u8], | 211 | control_buf: &'d mut [u8], |
| 153 | #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, | 212 | #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, |
| 154 | ) -> UsbDevice<'d, D> { | 213 | ) -> UsbDevice<'d, D> { |
| @@ -162,7 +221,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 162 | inner: Inner { | 221 | inner: Inner { |
| 163 | bus, | 222 | bus, |
| 164 | config, | 223 | config, |
| 165 | handler, | ||
| 166 | device_descriptor, | 224 | device_descriptor, |
| 167 | config_descriptor, | 225 | config_descriptor, |
| 168 | bos_descriptor, | 226 | bos_descriptor, |
| @@ -174,6 +232,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 174 | address: 0, | 232 | address: 0, |
| 175 | set_address_pending: false, | 233 | set_address_pending: false, |
| 176 | interfaces, | 234 | interfaces, |
| 235 | handlers, | ||
| 177 | #[cfg(feature = "msos-descriptor")] | 236 | #[cfg(feature = "msos-descriptor")] |
| 178 | msos_descriptor, | 237 | msos_descriptor, |
| 179 | }, | 238 | }, |
| @@ -218,7 +277,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 218 | self.inner.suspended = false; | 277 | self.inner.suspended = false; |
| 219 | self.inner.remote_wakeup_enabled = false; | 278 | self.inner.remote_wakeup_enabled = false; |
| 220 | 279 | ||
| 221 | if let Some(h) = &self.inner.handler { | 280 | for h in &mut self.inner.handlers { |
| 222 | h.enabled(false); | 281 | h.enabled(false); |
| 223 | } | 282 | } |
| 224 | } | 283 | } |
| @@ -247,7 +306,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 247 | self.inner.bus.remote_wakeup().await?; | 306 | self.inner.bus.remote_wakeup().await?; |
| 248 | self.inner.suspended = false; | 307 | self.inner.suspended = false; |
| 249 | 308 | ||
| 250 | if let Some(h) = &self.inner.handler { | 309 | for h in &mut self.inner.handlers { |
| 251 | h.suspended(false); | 310 | h.suspended(false); |
| 252 | } | 311 | } |
| 253 | 312 | ||
| @@ -358,29 +417,29 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 358 | self.remote_wakeup_enabled = false; | 417 | self.remote_wakeup_enabled = false; |
| 359 | self.address = 0; | 418 | self.address = 0; |
| 360 | 419 | ||
| 361 | for iface in self.interfaces.iter_mut() { | 420 | for h in &mut self.handlers { |
| 362 | iface.current_alt_setting = 0; | 421 | h.reset(); |
| 363 | if let Some(h) = &mut iface.handler { | ||
| 364 | h.reset(); | ||
| 365 | h.set_alternate_setting(0); | ||
| 366 | } | ||
| 367 | } | 422 | } |
| 368 | 423 | ||
| 369 | if let Some(h) = &self.handler { | 424 | for (i, iface) in self.interfaces.iter_mut().enumerate() { |
| 370 | h.reset(); | 425 | iface.current_alt_setting = 0; |
| 426 | |||
| 427 | for h in &mut self.handlers { | ||
| 428 | h.set_alternate_setting(InterfaceNumber::new(i as _), 0); | ||
| 429 | } | ||
| 371 | } | 430 | } |
| 372 | } | 431 | } |
| 373 | Event::Resume => { | 432 | Event::Resume => { |
| 374 | trace!("usb: resume"); | 433 | trace!("usb: resume"); |
| 375 | self.suspended = false; | 434 | self.suspended = false; |
| 376 | if let Some(h) = &self.handler { | 435 | for h in &mut self.handlers { |
| 377 | h.suspended(false); | 436 | h.suspended(false); |
| 378 | } | 437 | } |
| 379 | } | 438 | } |
| 380 | Event::Suspend => { | 439 | Event::Suspend => { |
| 381 | trace!("usb: suspend"); | 440 | trace!("usb: suspend"); |
| 382 | self.suspended = true; | 441 | self.suspended = true; |
| 383 | if let Some(h) = &self.handler { | 442 | for h in &mut self.handlers { |
| 384 | h.suspended(true); | 443 | h.suspended(true); |
| 385 | } | 444 | } |
| 386 | } | 445 | } |
| @@ -389,7 +448,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 389 | self.bus.enable().await; | 448 | self.bus.enable().await; |
| 390 | self.device_state = UsbDeviceState::Default; | 449 | self.device_state = UsbDeviceState::Default; |
| 391 | 450 | ||
| 392 | if let Some(h) = &self.handler { | 451 | for h in &mut self.handlers { |
| 393 | h.enabled(true); | 452 | h.enabled(true); |
| 394 | } | 453 | } |
| 395 | } | 454 | } |
| @@ -398,7 +457,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 398 | self.bus.disable().await; | 457 | self.bus.disable().await; |
| 399 | self.device_state = UsbDeviceState::Unpowered; | 458 | self.device_state = UsbDeviceState::Unpowered; |
| 400 | 459 | ||
| 401 | if let Some(h) = &self.handler { | 460 | for h in &mut self.handlers { |
| 402 | h.enabled(false); | 461 | h.enabled(false); |
| 403 | } | 462 | } |
| 404 | } | 463 | } |
| @@ -413,14 +472,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 413 | (RequestType::Standard, Recipient::Device) => match (req.request, req.value) { | 472 | (RequestType::Standard, Recipient::Device) => match (req.request, req.value) { |
| 414 | (Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { | 473 | (Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { |
| 415 | self.remote_wakeup_enabled = false; | 474 | self.remote_wakeup_enabled = false; |
| 416 | if let Some(h) = &self.handler { | 475 | for h in &mut self.handlers { |
| 417 | h.remote_wakeup_enabled(false); | 476 | h.remote_wakeup_enabled(false); |
| 418 | } | 477 | } |
| 419 | OutResponse::Accepted | 478 | OutResponse::Accepted |
| 420 | } | 479 | } |
| 421 | (Request::SET_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { | 480 | (Request::SET_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { |
| 422 | self.remote_wakeup_enabled = true; | 481 | self.remote_wakeup_enabled = true; |
| 423 | if let Some(h) = &self.handler { | 482 | for h in &mut self.handlers { |
| 424 | h.remote_wakeup_enabled(true); | 483 | h.remote_wakeup_enabled(true); |
| 425 | } | 484 | } |
| 426 | OutResponse::Accepted | 485 | OutResponse::Accepted |
| @@ -429,7 +488,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 429 | self.address = addr as u8; | 488 | self.address = addr as u8; |
| 430 | self.set_address_pending = true; | 489 | self.set_address_pending = true; |
| 431 | self.device_state = UsbDeviceState::Addressed; | 490 | self.device_state = UsbDeviceState::Addressed; |
| 432 | if let Some(h) = &self.handler { | 491 | for h in &mut self.handlers { |
| 433 | h.addressed(self.address); | 492 | h.addressed(self.address); |
| 434 | } | 493 | } |
| 435 | OutResponse::Accepted | 494 | OutResponse::Accepted |
| @@ -440,14 +499,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 440 | 499 | ||
| 441 | // Enable all endpoints of selected alt settings. | 500 | // Enable all endpoints of selected alt settings. |
| 442 | foreach_endpoint(self.config_descriptor, |ep| { | 501 | foreach_endpoint(self.config_descriptor, |ep| { |
| 443 | let iface = &self.interfaces[ep.interface as usize]; | 502 | let iface = &self.interfaces[ep.interface.0 as usize]; |
| 444 | self.bus | 503 | self.bus |
| 445 | .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); | 504 | .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); |
| 446 | }) | 505 | }) |
| 447 | .unwrap(); | 506 | .unwrap(); |
| 448 | 507 | ||
| 449 | // Notify handler. | 508 | // Notify handlers. |
| 450 | if let Some(h) = &self.handler { | 509 | for h in &mut self.handlers { |
| 451 | h.configured(true); | 510 | h.configured(true); |
| 452 | } | 511 | } |
| 453 | 512 | ||
| @@ -465,8 +524,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 465 | }) | 524 | }) |
| 466 | .unwrap(); | 525 | .unwrap(); |
| 467 | 526 | ||
| 468 | // Notify handler. | 527 | // Notify handlers. |
| 469 | if let Some(h) = &self.handler { | 528 | for h in &mut self.handlers { |
| 470 | h.configured(false); | 529 | h.configured(false); |
| 471 | } | 530 | } |
| 472 | 531 | ||
| @@ -476,7 +535,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 476 | _ => OutResponse::Rejected, | 535 | _ => OutResponse::Rejected, |
| 477 | }, | 536 | }, |
| 478 | (RequestType::Standard, Recipient::Interface) => { | 537 | (RequestType::Standard, Recipient::Interface) => { |
| 479 | let iface = match self.interfaces.get_mut(req.index as usize) { | 538 | let iface_num = InterfaceNumber::new(req.index as _); |
| 539 | let iface = match self.interfaces.get_mut(iface_num.0 as usize) { | ||
| 480 | Some(iface) => iface, | 540 | Some(iface) => iface, |
| 481 | None => return OutResponse::Rejected, | 541 | None => return OutResponse::Rejected, |
| 482 | }; | 542 | }; |
| @@ -494,7 +554,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 494 | 554 | ||
| 495 | // Enable/disable EPs of this interface as needed. | 555 | // Enable/disable EPs of this interface as needed. |
| 496 | foreach_endpoint(self.config_descriptor, |ep| { | 556 | foreach_endpoint(self.config_descriptor, |ep| { |
| 497 | if ep.interface == req.index as u8 { | 557 | if ep.interface == iface_num { |
| 498 | self.bus | 558 | self.bus |
| 499 | .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); | 559 | .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); |
| 500 | } | 560 | } |
| @@ -503,8 +563,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 503 | 563 | ||
| 504 | // TODO check it is valid (not out of range) | 564 | // TODO check it is valid (not out of range) |
| 505 | 565 | ||
| 506 | if let Some(handler) = &mut iface.handler { | 566 | for h in &mut self.handlers { |
| 507 | handler.set_alternate_setting(new_altsetting); | 567 | h.set_alternate_setting(iface_num, new_altsetting); |
| 508 | } | 568 | } |
| 509 | OutResponse::Accepted | 569 | OutResponse::Accepted |
| 510 | } | 570 | } |
| @@ -524,17 +584,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 524 | } | 584 | } |
| 525 | _ => OutResponse::Rejected, | 585 | _ => OutResponse::Rejected, |
| 526 | }, | 586 | }, |
| 527 | (RequestType::Class, Recipient::Interface) => { | 587 | _ => self.handle_control_out_delegated(req, data), |
| 528 | let iface = match self.interfaces.get_mut(req.index as usize) { | ||
| 529 | Some(iface) => iface, | ||
| 530 | None => return OutResponse::Rejected, | ||
| 531 | }; | ||
| 532 | match &mut iface.handler { | ||
| 533 | Some(handler) => handler.control_out(req, data), | ||
| 534 | None => OutResponse::Rejected, | ||
| 535 | } | ||
| 536 | } | ||
| 537 | _ => OutResponse::Rejected, | ||
| 538 | } | 588 | } |
| 539 | } | 589 | } |
| 540 | 590 | ||
| @@ -579,11 +629,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 579 | buf[0] = iface.current_alt_setting; | 629 | buf[0] = iface.current_alt_setting; |
| 580 | InResponse::Accepted(&buf[..1]) | 630 | InResponse::Accepted(&buf[..1]) |
| 581 | } | 631 | } |
| 582 | Request::GET_DESCRIPTOR => match &mut iface.handler { | 632 | _ => self.handle_control_in_delegated(req, buf), |
| 583 | Some(handler) => handler.get_descriptor(req, buf), | ||
| 584 | None => InResponse::Rejected, | ||
| 585 | }, | ||
| 586 | _ => InResponse::Rejected, | ||
| 587 | } | 633 | } |
| 588 | } | 634 | } |
| 589 | (RequestType::Standard, Recipient::Endpoint) => match req.request { | 635 | (RequestType::Standard, Recipient::Endpoint) => match req.request { |
| @@ -598,32 +644,46 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 598 | } | 644 | } |
| 599 | _ => InResponse::Rejected, | 645 | _ => InResponse::Rejected, |
| 600 | }, | 646 | }, |
| 601 | (RequestType::Class, Recipient::Interface) => { | ||
| 602 | let iface = match self.interfaces.get_mut(req.index as usize) { | ||
| 603 | Some(iface) => iface, | ||
| 604 | None => return InResponse::Rejected, | ||
| 605 | }; | ||
| 606 | |||
| 607 | match &mut iface.handler { | ||
| 608 | Some(handler) => handler.control_in(req, buf), | ||
| 609 | None => InResponse::Rejected, | ||
| 610 | } | ||
| 611 | } | ||
| 612 | #[cfg(feature = "msos-descriptor")] | 647 | #[cfg(feature = "msos-descriptor")] |
| 613 | (RequestType::Vendor, Recipient::Device) => { | 648 | (RequestType::Vendor, Recipient::Device) => { |
| 614 | if !self.msos_descriptor.is_empty() { | 649 | if !self.msos_descriptor.is_empty() |
| 615 | if req.request == self.msos_descriptor.vendor_code() && req.index == 7 { | 650 | && req.request == self.msos_descriptor.vendor_code() |
| 616 | // Index 7 retrieves the MS OS Descriptor Set | 651 | && req.index == 7 |
| 617 | InResponse::Accepted(self.msos_descriptor.descriptor()) | 652 | { |
| 618 | } else { | 653 | // Index 7 retrieves the MS OS Descriptor Set |
| 619 | InResponse::Rejected | 654 | InResponse::Accepted(self.msos_descriptor.descriptor()) |
| 620 | } | ||
| 621 | } else { | 655 | } else { |
| 622 | InResponse::Rejected | 656 | self.handle_control_in_delegated(req, buf) |
| 623 | } | 657 | } |
| 624 | } | 658 | } |
| 625 | _ => InResponse::Rejected, | 659 | _ => self.handle_control_in_delegated(req, buf), |
| 660 | } | ||
| 661 | } | ||
| 662 | |||
| 663 | fn handle_control_out_delegated(&mut self, req: Request, data: &[u8]) -> OutResponse { | ||
| 664 | for h in &mut self.handlers { | ||
| 665 | if let Some(res) = h.control_out(req, data) { | ||
| 666 | return res; | ||
| 667 | } | ||
| 626 | } | 668 | } |
| 669 | OutResponse::Rejected | ||
| 670 | } | ||
| 671 | |||
| 672 | fn handle_control_in_delegated<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | ||
| 673 | unsafe fn extend_lifetime<'x, 'y>(r: InResponse<'x>) -> InResponse<'y> { | ||
| 674 | core::mem::transmute(r) | ||
| 675 | } | ||
| 676 | |||
| 677 | for h in &mut self.handlers { | ||
| 678 | if let Some(res) = h.control_in(req, buf) { | ||
| 679 | // safety: the borrow checker isn't smart enough to know this pattern (returning a | ||
| 680 | // borrowed value from inside the loop) is sound. Workaround by unsafely extending lifetime. | ||
| 681 | // Also, Polonius (the WIP new borrow checker) does accept it. | ||
| 682 | |||
| 683 | return unsafe { extend_lifetime(res) }; | ||
| 684 | } | ||
| 685 | } | ||
| 686 | InResponse::Rejected | ||
| 627 | } | 687 | } |
| 628 | 688 | ||
| 629 | fn handle_get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | 689 | fn handle_get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { |
| @@ -646,30 +706,16 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 646 | STRING_INDEX_PRODUCT => self.config.product, | 706 | STRING_INDEX_PRODUCT => self.config.product, |
| 647 | STRING_INDEX_SERIAL_NUMBER => self.config.serial_number, | 707 | STRING_INDEX_SERIAL_NUMBER => self.config.serial_number, |
| 648 | _ => { | 708 | _ => { |
| 649 | // Find out which iface owns this string index. | 709 | let mut s = None; |
| 650 | let mut index_left = index - STRING_INDEX_CUSTOM_START; | 710 | for handler in &mut self.handlers { |
| 651 | let mut the_iface = None; | 711 | let index = StringIndex::new(index); |
| 652 | for iface in &mut self.interfaces { | 712 | let lang_id = req.index; |
| 653 | if index_left < iface.num_strings { | 713 | if let Some(res) = handler.get_string(index, lang_id) { |
| 654 | the_iface = Some(iface); | 714 | s = Some(res); |
| 655 | break; | 715 | break; |
| 656 | } | 716 | } |
| 657 | index_left -= iface.num_strings; | ||
| 658 | } | ||
| 659 | |||
| 660 | if let Some(iface) = the_iface { | ||
| 661 | if let Some(handler) = &mut iface.handler { | ||
| 662 | let index = StringIndex::new(index); | ||
| 663 | let lang_id = req.index; | ||
| 664 | handler.get_string(index, lang_id) | ||
| 665 | } else { | ||
| 666 | warn!("String requested to an interface with no handler."); | ||
| 667 | None | ||
| 668 | } | ||
| 669 | } else { | ||
| 670 | warn!("String requested but didn't match to an interface."); | ||
| 671 | None | ||
| 672 | } | 717 | } |
| 718 | s | ||
| 673 | } | 719 | } |
| 674 | }; | 720 | }; |
| 675 | 721 | ||
diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 19ed34979..b1e0335ee 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | use core::mem::size_of; | 7 | use core::mem::size_of; |
| 8 | 8 | ||
| 9 | use super::{capability_type, BosWriter}; | 9 | use super::{capability_type, BosWriter}; |
| 10 | use crate::types::InterfaceNumber; | ||
| 10 | 11 | ||
| 11 | /// A serialized Microsoft OS 2.0 Descriptor set. | 12 | /// A serialized Microsoft OS 2.0 Descriptor set. |
| 12 | /// | 13 | /// |
| @@ -125,7 +126,7 @@ impl<'d> MsOsDescriptorWriter<'d> { | |||
| 125 | } | 126 | } |
| 126 | 127 | ||
| 127 | /// Add a function subset. | 128 | /// Add a function subset. |
| 128 | pub fn function(&mut self, first_interface: u8) { | 129 | pub fn function(&mut self, first_interface: InterfaceNumber) { |
| 129 | assert!( | 130 | assert!( |
| 130 | self.config_mark.is_some(), | 131 | self.config_mark.is_some(), |
| 131 | "MsOsDescriptorWriter: function subset requires a configuration subset" | 132 | "MsOsDescriptorWriter: function subset requires a configuration subset" |
| @@ -376,14 +377,14 @@ impl DescriptorSet for ConfigurationSubsetHeader { | |||
| 376 | pub struct FunctionSubsetHeader { | 377 | pub struct FunctionSubsetHeader { |
| 377 | wLength: u16, | 378 | wLength: u16, |
| 378 | wDescriptorType: u16, | 379 | wDescriptorType: u16, |
| 379 | bFirstInterface: u8, | 380 | bFirstInterface: InterfaceNumber, |
| 380 | bReserved: u8, | 381 | bReserved: u8, |
| 381 | wSubsetLength: u16, | 382 | wSubsetLength: u16, |
| 382 | } | 383 | } |
| 383 | 384 | ||
| 384 | impl FunctionSubsetHeader { | 385 | impl FunctionSubsetHeader { |
| 385 | /// Creates a function subset header | 386 | /// Creates a function subset header |
| 386 | pub fn new(first_interface: u8) -> Self { | 387 | pub fn new(first_interface: InterfaceNumber) -> Self { |
| 387 | FunctionSubsetHeader { | 388 | FunctionSubsetHeader { |
| 388 | wLength: (size_of::<Self>() as u16).to_le(), | 389 | wLength: (size_of::<Self>() as u16).to_le(), |
| 389 | wDescriptorType: (Self::TYPE as u16).to_le(), | 390 | wDescriptorType: (Self::TYPE as u16).to_le(), |
diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index 1743e61ff..c7a47f7e4 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs | |||
| @@ -1,9 +1,10 @@ | |||
| 1 | //! USB types. | 1 | //! USB types. |
| 2 | 2 | ||
| 3 | /// A handle for a USB interface that contains its number. | 3 | /// A handle for a USB interface that contains its number. |
| 4 | #[derive(Copy, Clone, Eq, PartialEq)] | 4 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 6 | pub struct InterfaceNumber(pub(crate) u8); | 6 | #[repr(transparent)] |
| 7 | pub struct InterfaceNumber(pub u8); | ||
| 7 | 8 | ||
| 8 | impl InterfaceNumber { | 9 | impl InterfaceNumber { |
| 9 | pub(crate) fn new(index: u8) -> InterfaceNumber { | 10 | pub(crate) fn new(index: u8) -> InterfaceNumber { |
| @@ -20,7 +21,8 @@ impl From<InterfaceNumber> for u8 { | |||
| 20 | /// A handle for a USB string descriptor that contains its index. | 21 | /// A handle for a USB string descriptor that contains its index. |
| 21 | #[derive(Copy, Clone, Eq, PartialEq)] | 22 | #[derive(Copy, Clone, Eq, PartialEq)] |
| 22 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 23 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 23 | pub struct StringIndex(u8); | 24 | #[repr(transparent)] |
| 25 | pub struct StringIndex(pub u8); | ||
| 24 | 26 | ||
| 25 | impl StringIndex { | 27 | impl StringIndex { |
| 26 | pub(crate) fn new(index: u8) -> StringIndex { | 28 | pub(crate) fn new(index: u8) -> StringIndex { |
diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 699666cee..979780896 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs | |||
| @@ -82,7 +82,6 @@ async fn main(spawner: Spawner) { | |||
| 82 | &mut singleton!([0; 256])[..], | 82 | &mut singleton!([0; 256])[..], |
| 83 | &mut singleton!([0; 256])[..], | 83 | &mut singleton!([0; 256])[..], |
| 84 | &mut singleton!([0; 128])[..], | 84 | &mut singleton!([0; 128])[..], |
| 85 | None, | ||
| 86 | ); | 85 | ); |
| 87 | 86 | ||
| 88 | // Our MAC addr. | 87 | // Our MAC addr. |
diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 017cac197..3d8a114cd 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs | |||
| @@ -16,7 +16,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | |||
| 16 | use embassy_sync::signal::Signal; | 16 | use embassy_sync::signal::Signal; |
| 17 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | 17 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; |
| 18 | use embassy_usb::control::OutResponse; | 18 | use embassy_usb::control::OutResponse; |
| 19 | use embassy_usb::{Builder, Config, DeviceStateHandler}; | 19 | use embassy_usb::{Builder, Config, Handler}; |
| 20 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | 20 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; |
| 21 | use {defmt_rtt as _, panic_probe as _}; | 21 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 22 | ||
| @@ -52,7 +52,7 @@ async fn main(_spawner: Spawner) { | |||
| 52 | let mut bos_descriptor = [0; 256]; | 52 | let mut bos_descriptor = [0; 256]; |
| 53 | let mut control_buf = [0; 64]; | 53 | let mut control_buf = [0; 64]; |
| 54 | let request_handler = MyRequestHandler {}; | 54 | let request_handler = MyRequestHandler {}; |
| 55 | let device_state_handler = MyDeviceStateHandler::new(); | 55 | let mut device_handler = MyDeviceHandler::new(); |
| 56 | 56 | ||
| 57 | let mut state = State::new(); | 57 | let mut state = State::new(); |
| 58 | 58 | ||
| @@ -63,9 +63,10 @@ async fn main(_spawner: Spawner) { | |||
| 63 | &mut config_descriptor, | 63 | &mut config_descriptor, |
| 64 | &mut bos_descriptor, | 64 | &mut bos_descriptor, |
| 65 | &mut control_buf, | 65 | &mut control_buf, |
| 66 | Some(&device_state_handler), | ||
| 67 | ); | 66 | ); |
| 68 | 67 | ||
| 68 | builder.handler(&mut device_handler); | ||
| 69 | |||
| 69 | // Create classes on the builder. | 70 | // Create classes on the builder. |
| 70 | let config = embassy_usb::class::hid::Config { | 71 | let config = embassy_usb::class::hid::Config { |
| 71 | report_descriptor: KeyboardReport::desc(), | 72 | report_descriptor: KeyboardReport::desc(), |
| @@ -164,20 +165,20 @@ impl RequestHandler for MyRequestHandler { | |||
| 164 | } | 165 | } |
| 165 | } | 166 | } |
| 166 | 167 | ||
| 167 | struct MyDeviceStateHandler { | 168 | struct MyDeviceHandler { |
| 168 | configured: AtomicBool, | 169 | configured: AtomicBool, |
| 169 | } | 170 | } |
| 170 | 171 | ||
| 171 | impl MyDeviceStateHandler { | 172 | impl MyDeviceHandler { |
| 172 | fn new() -> Self { | 173 | fn new() -> Self { |
| 173 | MyDeviceStateHandler { | 174 | MyDeviceHandler { |
| 174 | configured: AtomicBool::new(false), | 175 | configured: AtomicBool::new(false), |
| 175 | } | 176 | } |
| 176 | } | 177 | } |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | impl DeviceStateHandler for MyDeviceStateHandler { | 180 | impl Handler for MyDeviceHandler { |
| 180 | fn enabled(&self, enabled: bool) { | 181 | fn enabled(&mut self, enabled: bool) { |
| 181 | self.configured.store(false, Ordering::Relaxed); | 182 | self.configured.store(false, Ordering::Relaxed); |
| 182 | SUSPENDED.store(false, Ordering::Release); | 183 | SUSPENDED.store(false, Ordering::Release); |
| 183 | if enabled { | 184 | if enabled { |
| @@ -187,17 +188,17 @@ impl DeviceStateHandler for MyDeviceStateHandler { | |||
| 187 | } | 188 | } |
| 188 | } | 189 | } |
| 189 | 190 | ||
| 190 | fn reset(&self) { | 191 | fn reset(&mut self) { |
| 191 | self.configured.store(false, Ordering::Relaxed); | 192 | self.configured.store(false, Ordering::Relaxed); |
| 192 | info!("Bus reset, the Vbus current limit is 100mA"); | 193 | info!("Bus reset, the Vbus current limit is 100mA"); |
| 193 | } | 194 | } |
| 194 | 195 | ||
| 195 | fn addressed(&self, addr: u8) { | 196 | fn addressed(&mut self, addr: u8) { |
| 196 | self.configured.store(false, Ordering::Relaxed); | 197 | self.configured.store(false, Ordering::Relaxed); |
| 197 | info!("USB address set to: {}", addr); | 198 | info!("USB address set to: {}", addr); |
| 198 | } | 199 | } |
| 199 | 200 | ||
| 200 | fn configured(&self, configured: bool) { | 201 | fn configured(&mut self, configured: bool) { |
| 201 | self.configured.store(configured, Ordering::Relaxed); | 202 | self.configured.store(configured, Ordering::Relaxed); |
| 202 | if configured { | 203 | if configured { |
| 203 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") | 204 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") |
| @@ -206,7 +207,7 @@ impl DeviceStateHandler for MyDeviceStateHandler { | |||
| 206 | } | 207 | } |
| 207 | } | 208 | } |
| 208 | 209 | ||
| 209 | fn suspended(&self, suspended: bool) { | 210 | fn suspended(&mut self, suspended: bool) { |
| 210 | if suspended { | 211 | if suspended { |
| 211 | info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); | 212 | info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); |
| 212 | SUSPENDED.store(true, Ordering::Release); | 213 | SUSPENDED.store(true, Ordering::Release); |
diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index a5849129a..d7c9d55b7 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs | |||
| @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { | |||
| 55 | &mut config_descriptor, | 55 | &mut config_descriptor, |
| 56 | &mut bos_descriptor, | 56 | &mut bos_descriptor, |
| 57 | &mut control_buf, | 57 | &mut control_buf, |
| 58 | None, | ||
| 59 | ); | 58 | ); |
| 60 | 59 | ||
| 61 | // Create classes on the builder. | 60 | // Create classes on the builder. |
diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 18b6f25b9..102d7ea60 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs | |||
| @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { | |||
| 59 | &mut config_descriptor, | 59 | &mut config_descriptor, |
| 60 | &mut bos_descriptor, | 60 | &mut bos_descriptor, |
| 61 | &mut control_buf, | 61 | &mut control_buf, |
| 62 | None, | ||
| 63 | ); | 62 | ); |
| 64 | 63 | ||
| 65 | // Create classes on the builder. | 64 | // Create classes on the builder. |
diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 3532d3f82..558d4ba60 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs | |||
| @@ -83,7 +83,6 @@ async fn main(spawner: Spawner) { | |||
| 83 | &mut res.config_descriptor, | 83 | &mut res.config_descriptor, |
| 84 | &mut res.bos_descriptor, | 84 | &mut res.bos_descriptor, |
| 85 | &mut res.control_buf, | 85 | &mut res.control_buf, |
| 86 | None, | ||
| 87 | ); | 86 | ); |
| 88 | 87 | ||
| 89 | // Create classes on the builder. | 88 | // Create classes on the builder. |
diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index f4b828de6..6561fc3b4 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs | |||
| @@ -12,6 +12,7 @@ use embassy_nrf::{interrupt, pac}; | |||
| 12 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | 12 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; |
| 13 | use embassy_usb::driver::EndpointError; | 13 | use embassy_usb::driver::EndpointError; |
| 14 | use embassy_usb::msos::{self, windows_version}; | 14 | use embassy_usb::msos::{self, windows_version}; |
| 15 | use embassy_usb::types::InterfaceNumber; | ||
| 15 | use embassy_usb::{Builder, Config}; | 16 | use embassy_usb::{Builder, Config}; |
| 16 | use {defmt_rtt as _, panic_probe as _}; | 17 | use {defmt_rtt as _, panic_probe as _}; |
| 17 | 18 | ||
| @@ -65,7 +66,6 @@ async fn main(_spawner: Spawner) { | |||
| 65 | &mut bos_descriptor, | 66 | &mut bos_descriptor, |
| 66 | &mut msos_descriptor, | 67 | &mut msos_descriptor, |
| 67 | &mut control_buf, | 68 | &mut control_buf, |
| 68 | None, | ||
| 69 | ); | 69 | ); |
| 70 | 70 | ||
| 71 | builder.msos_descriptor(windows_version::WIN8_1, 2); | 71 | builder.msos_descriptor(windows_version::WIN8_1, 2); |
| @@ -78,7 +78,7 @@ async fn main(_spawner: Spawner) { | |||
| 78 | // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. | 78 | // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. |
| 79 | let msos_writer = builder.msos_writer(); | 79 | let msos_writer = builder.msos_writer(); |
| 80 | msos_writer.configuration(0); | 80 | msos_writer.configuration(0); |
| 81 | msos_writer.function(0); | 81 | msos_writer.function(InterfaceNumber(0)); |
| 82 | msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); | 82 | msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); |
| 83 | msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new( | 83 | msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new( |
| 84 | "DeviceInterfaceGUIDs", | 84 | "DeviceInterfaceGUIDs", |
diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 104b25d39..66a6ed4d0 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs | |||
| @@ -73,7 +73,6 @@ async fn main(spawner: Spawner) { | |||
| 73 | &mut singleton!([0; 256])[..], | 73 | &mut singleton!([0; 256])[..], |
| 74 | &mut singleton!([0; 256])[..], | 74 | &mut singleton!([0; 256])[..], |
| 75 | &mut singleton!([0; 128])[..], | 75 | &mut singleton!([0; 128])[..], |
| 76 | None, | ||
| 77 | ); | 76 | ); |
| 78 | 77 | ||
| 79 | // Our MAC addr. | 78 | // Our MAC addr. |
diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index b7d6493b4..a991082ee 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs | |||
| @@ -53,7 +53,6 @@ async fn main(_spawner: Spawner) { | |||
| 53 | &mut config_descriptor, | 53 | &mut config_descriptor, |
| 54 | &mut bos_descriptor, | 54 | &mut bos_descriptor, |
| 55 | &mut control_buf, | 55 | &mut control_buf, |
| 56 | None, | ||
| 57 | ); | 56 | ); |
| 58 | 57 | ||
| 59 | // Create classes on the builder. | 58 | // Create classes on the builder. |
diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index ad92cdeb2..07cad84ef 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs | |||
| @@ -58,7 +58,6 @@ async fn main(_spawner: Spawner) { | |||
| 58 | &mut config_descriptor, | 58 | &mut config_descriptor, |
| 59 | &mut bos_descriptor, | 59 | &mut bos_descriptor, |
| 60 | &mut control_buf, | 60 | &mut control_buf, |
| 61 | None, | ||
| 62 | ); | 61 | ); |
| 63 | 62 | ||
| 64 | // Create classes on the builder. | 63 | // Create classes on the builder. |
diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index f6d27c860..5b4e0a91a 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs | |||
| @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { | |||
| 55 | &mut config_descriptor, | 55 | &mut config_descriptor, |
| 56 | &mut bos_descriptor, | 56 | &mut bos_descriptor, |
| 57 | &mut control_buf, | 57 | &mut control_buf, |
| 58 | None, | ||
| 59 | ); | 58 | ); |
| 60 | 59 | ||
| 61 | // Create classes on the builder. | 60 | // Create classes on the builder. |
diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index cf2885ae5..4a16aac07 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs | |||
| @@ -82,7 +82,6 @@ async fn main(spawner: Spawner) { | |||
| 82 | &mut singleton!([0; 256])[..], | 82 | &mut singleton!([0; 256])[..], |
| 83 | &mut singleton!([0; 256])[..], | 83 | &mut singleton!([0; 256])[..], |
| 84 | &mut singleton!([0; 128])[..], | 84 | &mut singleton!([0; 128])[..], |
| 85 | None, | ||
| 86 | ); | 85 | ); |
| 87 | 86 | ||
| 88 | // Our MAC addr. | 87 | // Our MAC addr. |
diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index 014647762..baabc1a2d 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs | |||
| @@ -57,7 +57,6 @@ async fn main(_spawner: Spawner) { | |||
| 57 | &mut config_descriptor, | 57 | &mut config_descriptor, |
| 58 | &mut bos_descriptor, | 58 | &mut bos_descriptor, |
| 59 | &mut control_buf, | 59 | &mut control_buf, |
| 60 | None, | ||
| 61 | ); | 60 | ); |
| 62 | 61 | ||
| 63 | // Create classes on the builder. | 62 | // Create classes on the builder. |
diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 688bd0dab..5fd9d2ec9 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs | |||
| @@ -58,7 +58,6 @@ async fn main(_spawner: Spawner) { | |||
| 58 | &mut config_descriptor, | 58 | &mut config_descriptor, |
| 59 | &mut bos_descriptor, | 59 | &mut bos_descriptor, |
| 60 | &mut control_buf, | 60 | &mut control_buf, |
| 61 | None, | ||
| 62 | ); | 61 | ); |
| 63 | 62 | ||
| 64 | // Create classes on the builder. | 63 | // Create classes on the builder. |
diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index b319d12c3..9ef520ae2 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs | |||
| @@ -57,7 +57,6 @@ async fn main(_spawner: Spawner) { | |||
| 57 | &mut config_descriptor, | 57 | &mut config_descriptor, |
| 58 | &mut bos_descriptor, | 58 | &mut bos_descriptor, |
| 59 | &mut control_buf, | 59 | &mut control_buf, |
| 60 | None, | ||
| 61 | ); | 60 | ); |
| 62 | 61 | ||
| 63 | // Create classes on the builder. | 62 | // Create classes on the builder. |
diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index 3e38b10a3..663f60d52 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs | |||
| @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { | |||
| 59 | &mut config_descriptor, | 59 | &mut config_descriptor, |
| 60 | &mut bos_descriptor, | 60 | &mut bos_descriptor, |
| 61 | &mut control_buf, | 61 | &mut control_buf, |
| 62 | None, | ||
| 63 | ); | 62 | ); |
| 64 | 63 | ||
| 65 | // Create classes on the builder. | 64 | // Create classes on the builder. |
diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index e5a46b064..98ec0e836 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs | |||
| @@ -79,7 +79,6 @@ async fn main(spawner: Spawner) { | |||
| 79 | &mut singleton!([0; 256])[..], | 79 | &mut singleton!([0; 256])[..], |
| 80 | &mut singleton!([0; 256])[..], | 80 | &mut singleton!([0; 256])[..], |
| 81 | &mut singleton!([0; 128])[..], | 81 | &mut singleton!([0; 128])[..], |
| 82 | None, | ||
| 83 | ); | 82 | ); |
| 84 | 83 | ||
| 85 | // Our MAC addr. | 84 | // Our MAC addr. |
diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index d38ed7496..e3bbe9d09 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs | |||
| @@ -51,7 +51,6 @@ async fn main(_spawner: Spawner) { | |||
| 51 | &mut config_descriptor, | 51 | &mut config_descriptor, |
| 52 | &mut bos_descriptor, | 52 | &mut bos_descriptor, |
| 53 | &mut control_buf, | 53 | &mut control_buf, |
| 54 | None, | ||
| 55 | ); | 54 | ); |
| 56 | 55 | ||
| 57 | // Create classes on the builder. | 56 | // Create classes on the builder. |
diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 7562a4e96..66ccacb73 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs | |||
| @@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) { | |||
| 46 | &mut config_descriptor, | 46 | &mut config_descriptor, |
| 47 | &mut bos_descriptor, | 47 | &mut bos_descriptor, |
| 48 | &mut control_buf, | 48 | &mut control_buf, |
| 49 | None, | ||
| 50 | ); | 49 | ); |
| 51 | 50 | ||
| 52 | // Create classes on the builder. | 51 | // Create classes on the builder. |
diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index c846836b0..8cd3bf2f4 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs | |||
| @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { | |||
| 59 | &mut config_descriptor, | 59 | &mut config_descriptor, |
| 60 | &mut bos_descriptor, | 60 | &mut bos_descriptor, |
| 61 | &mut control_buf, | 61 | &mut control_buf, |
| 62 | None, | ||
| 63 | ); | 62 | ); |
| 64 | 63 | ||
| 65 | // Create classes on the builder. | 64 | // Create classes on the builder. |
