diff options
| -rw-r--r-- | embassy-usb/Cargo.toml | 13 | ||||
| -rw-r--r-- | embassy-usb/README.md | 24 | ||||
| -rw-r--r-- | embassy-usb/build.rs | 93 | ||||
| -rw-r--r-- | embassy-usb/gen_config.py | 73 | ||||
| -rw-r--r-- | embassy-usb/src/builder.rs | 5 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 9 |
6 files changed, 212 insertions, 5 deletions
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index eb9ba36f4..463e1268c 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml | |||
| @@ -16,6 +16,19 @@ 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 | # END AUTOGENERATED CONFIG FEATURES | ||
| 31 | |||
| 19 | [dependencies] | 32 | [dependencies] |
| 20 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 33 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 21 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } | 34 | 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..524bdc2f6 --- /dev/null +++ b/embassy-usb/build.rs | |||
| @@ -0,0 +1,93 @@ | |||
| 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 | // END AUTOGENERATED CONFIG FEATURES | ||
| 11 | ]; | ||
| 12 | |||
| 13 | struct ConfigState { | ||
| 14 | value: usize, | ||
| 15 | seen_feature: bool, | ||
| 16 | seen_env: bool, | ||
| 17 | } | ||
| 18 | |||
| 19 | fn main() { | ||
| 20 | let crate_name = env::var("CARGO_PKG_NAME") | ||
| 21 | .unwrap() | ||
| 22 | .to_ascii_uppercase() | ||
| 23 | .replace('-', "_"); | ||
| 24 | |||
| 25 | // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any | ||
| 26 | // other file changed. | ||
| 27 | println!("cargo:rerun-if-changed=build.rs"); | ||
| 28 | |||
| 29 | // Rebuild if config envvar changed. | ||
| 30 | for (name, _) in CONFIGS { | ||
| 31 | println!("cargo:rerun-if-env-changed={crate_name}_{name}"); | ||
| 32 | } | ||
| 33 | |||
| 34 | let mut configs = HashMap::new(); | ||
| 35 | for (name, default) in CONFIGS { | ||
| 36 | configs.insert( | ||
| 37 | *name, | ||
| 38 | ConfigState { | ||
| 39 | value: *default, | ||
| 40 | seen_env: false, | ||
| 41 | seen_feature: false, | ||
| 42 | }, | ||
| 43 | ); | ||
| 44 | } | ||
| 45 | |||
| 46 | let prefix = format!("{crate_name}_"); | ||
| 47 | for (var, value) in env::vars() { | ||
| 48 | if let Some(name) = var.strip_prefix(&prefix) { | ||
| 49 | let Some(cfg) = configs.get_mut(name) else { | ||
| 50 | panic!("Unknown env var {name}") | ||
| 51 | }; | ||
| 52 | |||
| 53 | let Ok(value) = value.parse::<usize>() else { | ||
| 54 | panic!("Invalid value for env var {name}: {value}") | ||
| 55 | }; | ||
| 56 | |||
| 57 | cfg.value = value; | ||
| 58 | cfg.seen_env = true; | ||
| 59 | } | ||
| 60 | |||
| 61 | if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") { | ||
| 62 | if let Some(i) = feature.rfind('_') { | ||
| 63 | let name = &feature[..i]; | ||
| 64 | let value = &feature[i + 1..]; | ||
| 65 | if let Some(cfg) = configs.get_mut(name) { | ||
| 66 | let Ok(value) = value.parse::<usize>() else { | ||
| 67 | panic!("Invalid value for feature {name}: {value}") | ||
| 68 | }; | ||
| 69 | |||
| 70 | // envvars take priority. | ||
| 71 | if !cfg.seen_env { | ||
| 72 | if cfg.seen_feature { | ||
| 73 | panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value); | ||
| 74 | } | ||
| 75 | |||
| 76 | cfg.value = value; | ||
| 77 | cfg.seen_feature = true; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | let mut data = String::new(); | ||
| 85 | |||
| 86 | for (name, cfg) in &configs { | ||
| 87 | writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap(); | ||
| 88 | } | ||
| 89 | |||
| 90 | let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 91 | let out_file = out_dir.join("config.rs").to_string_lossy().to_string(); | ||
| 92 | fs::write(out_file, data).unwrap(); | ||
| 93 | } | ||
diff --git a/embassy-usb/gen_config.py b/embassy-usb/gen_config.py new file mode 100644 index 000000000..55a7fa3c0 --- /dev/null +++ b/embassy-usb/gen_config.py | |||
| @@ -0,0 +1,73 @@ | |||
| 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 | |||
| 32 | # ========= Update Cargo.toml | ||
| 33 | |||
| 34 | things = "" | ||
| 35 | for f in features: | ||
| 36 | name = f["name"].replace("_", "-") | ||
| 37 | for val in f["vals"]: | ||
| 38 | things += f"{name}-{val} = []" | ||
| 39 | if val == f["default"]: | ||
| 40 | things += " # Default" | ||
| 41 | things += "\n" | ||
| 42 | things += "\n" | ||
| 43 | |||
| 44 | SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n" | ||
| 45 | SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n" | ||
| 46 | HELP = "# Generated by gen_config.py. DO NOT EDIT.\n" | ||
| 47 | with open("Cargo.toml", "r") as f: | ||
| 48 | data = f.read() | ||
| 49 | before, data = data.split(SEPARATOR_START, maxsplit=1) | ||
| 50 | _, after = data.split(SEPARATOR_END, maxsplit=1) | ||
| 51 | data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after | ||
| 52 | with open("Cargo.toml", "w") as f: | ||
| 53 | f.write(data) | ||
| 54 | |||
| 55 | |||
| 56 | # ========= Update build.rs | ||
| 57 | |||
| 58 | things = "" | ||
| 59 | for f in features: | ||
| 60 | name = f["name"].upper() | ||
| 61 | things += f' ("{name}", {f["default"]}),\n' | ||
| 62 | |||
| 63 | SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n" | ||
| 64 | SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n" | ||
| 65 | HELP = " // Generated by gen_config.py. DO NOT EDIT.\n" | ||
| 66 | with open("build.rs", "r") as f: | ||
| 67 | data = f.read() | ||
| 68 | before, data = data.split(SEPARATOR_START, maxsplit=1) | ||
| 69 | _, after = data.split(SEPARATOR_END, maxsplit=1) | ||
| 70 | data = before + SEPARATOR_START + HELP + \ | ||
| 71 | things + " " + SEPARATOR_END + after | ||
| 72 | with open("build.rs", "w") as f: | ||
| 73 | f.write(data) | ||
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index d1cbf674b..d89fc4017 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs | |||
| @@ -308,7 +308,10 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { | |||
| 308 | }; | 308 | }; |
| 309 | 309 | ||
| 310 | if self.builder.interfaces.push(iface).is_err() { | 310 | if self.builder.interfaces.push(iface).is_err() { |
| 311 | panic!("max interface count reached") | 311 | panic!( |
| 312 | "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}", | ||
| 313 | MAX_INTERFACE_COUNT | ||
| 314 | ) | ||
| 312 | } | 315 | } |
| 313 | 316 | ||
| 314 | InterfaceBuilder { | 317 | InterfaceBuilder { |
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index aec18524b..f8983318e 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,9 +77,6 @@ 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; |
