usb: make max interface count configurable at compile time.
This commit is contained in:
		@@ -16,6 +16,19 @@ usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"]
 | 
				
			|||||||
msos-descriptor = []
 | 
					msos-descriptor = []
 | 
				
			||||||
default = ["usbd-hid"]
 | 
					default = ["usbd-hid"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# BEGIN AUTOGENERATED CONFIG FEATURES
 | 
				
			||||||
 | 
					# Generated by gen_config.py. DO NOT EDIT.
 | 
				
			||||||
 | 
					max-interface-count-1 = []
 | 
				
			||||||
 | 
					max-interface-count-2 = []
 | 
				
			||||||
 | 
					max-interface-count-3 = []
 | 
				
			||||||
 | 
					max-interface-count-4 = [] # Default
 | 
				
			||||||
 | 
					max-interface-count-5 = []
 | 
				
			||||||
 | 
					max-interface-count-6 = []
 | 
				
			||||||
 | 
					max-interface-count-7 = []
 | 
				
			||||||
 | 
					max-interface-count-8 = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# END AUTOGENERATED CONFIG FEATURES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
 | 
					embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
 | 
				
			||||||
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
 | 
					embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,28 @@
 | 
				
			|||||||
# embassy-usb
 | 
					# embassy-usb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TODO crate description/
 | 
					TODO crate description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`embassy-usb` has some configuration settings that are set at compile time, affecting sizes
 | 
				
			||||||
 | 
					and counts of buffers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					They can be set in two ways:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Via Cargo features: enable a feature like `<name>-<value>`. `name` must be in lowercase and
 | 
				
			||||||
 | 
					use dashes instead of underscores. For example. `max-interface-count-3`. Only a selection of values
 | 
				
			||||||
 | 
					is available, check `Cargo.toml` for the list.
 | 
				
			||||||
 | 
					- Via environment variables at build time: set the variable named `EMBASSY_USB_<value>`. For example 
 | 
				
			||||||
 | 
					`EMBASSY_USB_MAX_INTERFACE_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. 
 | 
				
			||||||
 | 
					Any value can be set, unlike with Cargo features.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting
 | 
				
			||||||
 | 
					with different values, compilation fails.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `MAX_INTERFACE_COUNT`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Max amount of interfaces that can be created in one device. Default: 4.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Interoperability
 | 
					## Interoperability
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										93
									
								
								embassy-usb/build.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								embassy-usb/build.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					use std::fmt::Write;
 | 
				
			||||||
 | 
					use std::path::PathBuf;
 | 
				
			||||||
 | 
					use std::{env, fs};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static CONFIGS: &[(&str, usize)] = &[
 | 
				
			||||||
 | 
					    // BEGIN AUTOGENERATED CONFIG FEATURES
 | 
				
			||||||
 | 
					    // Generated by gen_config.py. DO NOT EDIT.
 | 
				
			||||||
 | 
					    ("MAX_INTERFACE_COUNT", 4),
 | 
				
			||||||
 | 
					    // END AUTOGENERATED CONFIG FEATURES
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ConfigState {
 | 
				
			||||||
 | 
					    value: usize,
 | 
				
			||||||
 | 
					    seen_feature: bool,
 | 
				
			||||||
 | 
					    seen_env: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let crate_name = env::var("CARGO_PKG_NAME")
 | 
				
			||||||
 | 
					        .unwrap()
 | 
				
			||||||
 | 
					        .to_ascii_uppercase()
 | 
				
			||||||
 | 
					        .replace('-', "_");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any
 | 
				
			||||||
 | 
					    // other file changed.
 | 
				
			||||||
 | 
					    println!("cargo:rerun-if-changed=build.rs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Rebuild if config envvar changed.
 | 
				
			||||||
 | 
					    for (name, _) in CONFIGS {
 | 
				
			||||||
 | 
					        println!("cargo:rerun-if-env-changed={crate_name}_{name}");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut configs = HashMap::new();
 | 
				
			||||||
 | 
					    for (name, default) in CONFIGS {
 | 
				
			||||||
 | 
					        configs.insert(
 | 
				
			||||||
 | 
					            *name,
 | 
				
			||||||
 | 
					            ConfigState {
 | 
				
			||||||
 | 
					                value: *default,
 | 
				
			||||||
 | 
					                seen_env: false,
 | 
				
			||||||
 | 
					                seen_feature: false,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let prefix = format!("{crate_name}_");
 | 
				
			||||||
 | 
					    for (var, value) in env::vars() {
 | 
				
			||||||
 | 
					        if let Some(name) = var.strip_prefix(&prefix) {
 | 
				
			||||||
 | 
					            let Some(cfg) = configs.get_mut(name) else {
 | 
				
			||||||
 | 
					                panic!("Unknown env var {name}")
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let Ok(value) = value.parse::<usize>() else {
 | 
				
			||||||
 | 
					                panic!("Invalid value for env var {name}: {value}")
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cfg.value = value;
 | 
				
			||||||
 | 
					            cfg.seen_env = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") {
 | 
				
			||||||
 | 
					            if let Some(i) = feature.rfind('_') {
 | 
				
			||||||
 | 
					                let name = &feature[..i];
 | 
				
			||||||
 | 
					                let value = &feature[i + 1..];
 | 
				
			||||||
 | 
					                if let Some(cfg) = configs.get_mut(name) {
 | 
				
			||||||
 | 
					                    let Ok(value) = value.parse::<usize>() else {
 | 
				
			||||||
 | 
					                        panic!("Invalid value for feature {name}: {value}")
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // envvars take priority.
 | 
				
			||||||
 | 
					                    if !cfg.seen_env {
 | 
				
			||||||
 | 
					                        if cfg.seen_feature {
 | 
				
			||||||
 | 
					                            panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        cfg.value = value;
 | 
				
			||||||
 | 
					                        cfg.seen_feature = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut data = String::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (name, cfg) in &configs {
 | 
				
			||||||
 | 
					        writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
 | 
				
			||||||
 | 
					    let out_file = out_dir.join("config.rs").to_string_lossy().to_string();
 | 
				
			||||||
 | 
					    fs::write(out_file, data).unwrap();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										73
									
								
								embassy-usb/gen_config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								embassy-usb/gen_config.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					abspath = os.path.abspath(__file__)
 | 
				
			||||||
 | 
					dname = os.path.dirname(abspath)
 | 
				
			||||||
 | 
					os.chdir(dname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					features = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def feature(name, default, min, max, pow2=None):
 | 
				
			||||||
 | 
					    vals = set()
 | 
				
			||||||
 | 
					    val = min
 | 
				
			||||||
 | 
					    while val <= max:
 | 
				
			||||||
 | 
					        vals.add(val)
 | 
				
			||||||
 | 
					        if pow2 == True or (isinstance(pow2, int) and val >= pow2):
 | 
				
			||||||
 | 
					            val *= 2
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            val += 1
 | 
				
			||||||
 | 
					    vals.add(default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    features.append(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "name": name,
 | 
				
			||||||
 | 
					            "default": default,
 | 
				
			||||||
 | 
					            "vals": sorted(list(vals)),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					feature("max_interface_count", default=4, min=1, max=8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ========= Update Cargo.toml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					things = ""
 | 
				
			||||||
 | 
					for f in features:
 | 
				
			||||||
 | 
					    name = f["name"].replace("_", "-")
 | 
				
			||||||
 | 
					    for val in f["vals"]:
 | 
				
			||||||
 | 
					        things += f"{name}-{val} = []"
 | 
				
			||||||
 | 
					        if val == f["default"]:
 | 
				
			||||||
 | 
					            things += " # Default"
 | 
				
			||||||
 | 
					        things += "\n"
 | 
				
			||||||
 | 
					    things += "\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n"
 | 
				
			||||||
 | 
					SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n"
 | 
				
			||||||
 | 
					HELP = "# Generated by gen_config.py. DO NOT EDIT.\n"
 | 
				
			||||||
 | 
					with open("Cargo.toml", "r") as f:
 | 
				
			||||||
 | 
					    data = f.read()
 | 
				
			||||||
 | 
					before, data = data.split(SEPARATOR_START, maxsplit=1)
 | 
				
			||||||
 | 
					_, after = data.split(SEPARATOR_END, maxsplit=1)
 | 
				
			||||||
 | 
					data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after
 | 
				
			||||||
 | 
					with open("Cargo.toml", "w") as f:
 | 
				
			||||||
 | 
					    f.write(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ========= Update build.rs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					things = ""
 | 
				
			||||||
 | 
					for f in features:
 | 
				
			||||||
 | 
					    name = f["name"].upper()
 | 
				
			||||||
 | 
					    things += f'    ("{name}", {f["default"]}),\n'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n"
 | 
				
			||||||
 | 
					SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n"
 | 
				
			||||||
 | 
					HELP = "    // Generated by gen_config.py. DO NOT EDIT.\n"
 | 
				
			||||||
 | 
					with open("build.rs", "r") as f:
 | 
				
			||||||
 | 
					    data = f.read()
 | 
				
			||||||
 | 
					before, data = data.split(SEPARATOR_START, maxsplit=1)
 | 
				
			||||||
 | 
					_, after = data.split(SEPARATOR_END, maxsplit=1)
 | 
				
			||||||
 | 
					data = before + SEPARATOR_START + HELP + \
 | 
				
			||||||
 | 
					    things + "    " + SEPARATOR_END + after
 | 
				
			||||||
 | 
					with open("build.rs", "w") as f:
 | 
				
			||||||
 | 
					    f.write(data)
 | 
				
			||||||
@@ -308,7 +308,10 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.builder.interfaces.push(iface).is_err() {
 | 
					        if self.builder.interfaces.push(iface).is_err() {
 | 
				
			||||||
            panic!("max interface count reached")
 | 
					            panic!(
 | 
				
			||||||
 | 
					                "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
 | 
				
			||||||
 | 
					                MAX_INTERFACE_COUNT
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        InterfaceBuilder {
 | 
					        InterfaceBuilder {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,10 +16,16 @@ mod descriptor_reader;
 | 
				
			|||||||
pub mod msos;
 | 
					pub mod msos;
 | 
				
			||||||
pub mod types;
 | 
					pub mod types;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod config {
 | 
				
			||||||
 | 
					    #![allow(unused)]
 | 
				
			||||||
 | 
					    include!(concat!(env!("OUT_DIR"), "/config.rs"));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use embassy_futures::select::{select, Either};
 | 
					use embassy_futures::select::{select, Either};
 | 
				
			||||||
use heapless::Vec;
 | 
					use heapless::Vec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use crate::builder::{Builder, Config};
 | 
					pub use crate::builder::{Builder, Config};
 | 
				
			||||||
 | 
					use crate::config::*;
 | 
				
			||||||
use crate::control::*;
 | 
					use crate::control::*;
 | 
				
			||||||
use crate::descriptor::*;
 | 
					use crate::descriptor::*;
 | 
				
			||||||
use crate::descriptor_reader::foreach_endpoint;
 | 
					use crate::descriptor_reader::foreach_endpoint;
 | 
				
			||||||
@@ -71,9 +77,6 @@ pub const CONFIGURATION_NONE: u8 = 0;
 | 
				
			|||||||
/// The bConfiguration value for the single configuration supported by this device.
 | 
					/// The bConfiguration value for the single configuration supported by this device.
 | 
				
			||||||
pub const CONFIGURATION_VALUE: u8 = 1;
 | 
					pub const CONFIGURATION_VALUE: u8 = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Maximum interface count, configured at compile time.
 | 
					 | 
				
			||||||
pub const MAX_INTERFACE_COUNT: usize = 4;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const STRING_INDEX_MANUFACTURER: u8 = 1;
 | 
					const STRING_INDEX_MANUFACTURER: u8 = 1;
 | 
				
			||||||
const STRING_INDEX_PRODUCT: u8 = 2;
 | 
					const STRING_INDEX_PRODUCT: u8 = 2;
 | 
				
			||||||
const STRING_INDEX_SERIAL_NUMBER: u8 = 3;
 | 
					const STRING_INDEX_SERIAL_NUMBER: u8 = 3;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user