diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ff5e93d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "rust-analyzer.check.allTargets": false, + "rust-analyzer.cargo.target": "xtensa-esp32-none-elf", + "rust-analyzer.cargo.features": [ + "esp32" + ], + "editor.formatOnSave": true +} \ No newline at end of file diff --git a/README.md b/README.md index f9beae3..c874b86 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,21 @@ # Ferris on Air Ferris on Air (FoA) is an open source 802.11 stack for the ESP32 written in async rust, with the work of the [esp32-open-mac](https://esp32-open-mac.be/) project. The stack is intended to be used with [embassy](https://embassy.dev/) and is still in very early stages of development. We do not claim to be Wi-Fi certified, but implement the features specified by IEEE 802.11 to our best knowledge. + ## Design The main FoA crate acts as a multiplexer, that divides access to the hardware up into a number of virtual interfaces (VIF's). These can then be passed to interface implementations, like `foa_sta` or [`foa_dswifi`](https://github.com/mjwells2002/foa_dswifi). These interface implementations can coexist, enabling things like AP/STA operation in the future. + ## Structure The `foa` crate contains the LMAC, TX buffer management and RX ARC buffer management. `foa_sta` contains a rudimentary implementation of a station interface. `examples` contain a set of examples showing how to use different parts of the stack. + ## Usage For a concrete usage example, see `examples`. These examples can be run with `./run_example.sh [SSID] [LOG_LEVEL]`. + +- Install the ESP32 rust toolchain, by following https://docs.esp-rs.org/book/installation/index.html; for now we only support Xtensa targets, so follow those steps. We're using `no_std`, so you don't need to follow the `std` steps. +- Install `espflash` by running `cargo install espflash` +- CHIP can be esp32 or esp32s2 +- DEFMT_LOG accepts the following logging levels: error, warn, info, debug, trace. Enabling a logging level also enables higher severity logging levels + ## License This project is licensed under Apache 2.0 or MIT at your option. diff --git a/examples/.vscode/settings.json b/examples/.vscode/settings.json index a72c739..538a0b6 100644 --- a/examples/.vscode/settings.json +++ b/examples/.vscode/settings.json @@ -1,5 +1,8 @@ { "rust-analyzer.check.allTargets": false, "rust-analyzer.cargo.target": "xtensa-esp32-none-elf", - "editor.formatOnSave": true -} + "editor.formatOnSave": true, + "rust-analyzer.cargo.features": [ + "esp32" + ], +} \ No newline at end of file diff --git a/examples/Cargo.lock b/examples/Cargo.lock index d539da3..448ecbf 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -156,9 +156,9 @@ dependencies = [ [[package]] name = "bitfield-struct" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b" +checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" dependencies = [ "proc-macro2", "quote", @@ -440,7 +440,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid", "crypto-common", "subtle", ] @@ -454,19 +453,6 @@ dependencies = [ "litrs", ] -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", -] - [[package]] name = "elliptic-curve" version = "0.13.8" @@ -730,12 +716,14 @@ dependencies = [ [[package]] name = "embedded-tls" -version = "0.17.1" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6efb76fdd004a4ef787640177237b83449e6c5847765ea50bf15900061fd601" dependencies = [ "aes-gcm", + "atomic-polyfill", "defmt 0.3.100", "digest", - "ecdsa", "embedded-io", "embedded-io-async", "generic-array 0.14.7", @@ -744,10 +732,8 @@ dependencies = [ "hkdf", "hmac", "p256", - "portable-atomic", "rand_core 0.6.4", "sha2", - "signature", "typenum", ] @@ -974,14 +960,14 @@ dependencies = [ [[package]] name = "esp-wifi-hal" -version = "0.1.0-alpha.3" +version = "0.1.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ac4c1adf315e841418396ddb10e88ca67dbcfaed304417c31f8d93a5ea5fce" +checksum = "3df04720437afe1141e9cf264ead1ad452185e46a7cfb3a707677011dfc81b5e" dependencies = [ - "bitfield-struct 0.10.1", + "bitfield-struct 0.11.0", "cfg-if", "critical-section", - "defmt 0.3.100", + "defmt 1.0.1", "defmt-or-log", "embassy-futures", "embassy-sync", @@ -1062,6 +1048,7 @@ dependencies = [ "esp-println", "foa", "foa_awdl", + "foa_mesh", "foa_sta", "heapless 0.8.0", "rand_core 0.9.3", @@ -1126,6 +1113,25 @@ dependencies = [ "smoltcp", ] +[[package]] +name = "foa_mesh" +version = "0.1.0" +dependencies = [ + "defmt 0.3.100", + "defmt-or-log", + "embassy-futures", + "embassy-net-driver-channel", + "embassy-sync", + "embassy-time", + "esp-config", + "ethernet", + "foa", + "heapless 0.8.0", + "ieee80211", + "llc-rs", + "rand_core 0.6.4", +] + [[package]] name = "foa_sta" version = "0.1.0" @@ -1354,9 +1360,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "ieee80211" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c870f3ba5d4f36ffdfd3df5e870cada060cf8d926584e3c3ce274f8ced22109" +checksum = "06530bda0af5484a90b9a322dc7bb467a8b1f0d08d3bc50c510ba876fc516b48" dependencies = [ "bitfield-struct 0.8.0", "const_soft_float", @@ -1615,10 +1621,8 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "ecdsa", "elliptic-curve", "primeorder", - "sha2", ] [[package]] @@ -1774,6 +1778,7 @@ checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" [[package]] name = "reqwless" version = "0.13.0" +source = "git+https://github.com/Frostie314159/reqwless.git?branch=ipv6-url#83b28164a19a217999d6ccd14aef113261569bca" dependencies = [ "base64", "buffered-io", @@ -1790,16 +1795,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - [[package]] name = "riscv" version = "0.12.1" @@ -1958,16 +1953,6 @@ dependencies = [ "digest", ] -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - [[package]] name = "smoltcp" version = "0.12.0" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index f36e9d3..1856c24 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Frostie314159 "] edition = "2021" license = "MIT OR Apache-2.0" +rust-version = "1.65.0" [dependencies] esp-hal = "1.0.0-beta.0" @@ -23,13 +24,14 @@ embassy-executor = { version = "0.7.0", features = ["task-arena-size-32768", "de foa = { path = "../foa", features = ["defmt"] } foa_sta = { path = "../foa_sta", features = ["defmt"] } foa_awdl = { path = "../foa_awdl", features = ["defmt"] } +foa_mesh = { path = "../foa_mesh", features = ["defmt"] } static_cell = "2.1.0" heapless = "0.8.0" embedded-io-async = "0.6.1" defmt = "0.3.10" -reqwless = { path = "../../../rust/reqwless/", default-features = false, features = ["defmt"] } +reqwless = { git = "https://github.com/Frostie314159/reqwless.git", branch = "ipv6-url", default-features = false, features = ["defmt"] } # esp-mbedtls = { git = "https://github.com/esp-rs/esp-mbedtls.git", default-features = false, } rand_core = "0.9.3" embedded-io = "0.6.1" diff --git a/examples/src/bin/mesh_single.rs b/examples/src/bin/mesh_single.rs new file mode 100644 index 0000000..e48a2d1 --- /dev/null +++ b/examples/src/bin/mesh_single.rs @@ -0,0 +1,73 @@ +#![no_std] +#![no_main] + +use defmt::{debug, info}; +use embassy_executor::Spawner; +use embassy_time::Timer; +use esp_backtrace as _; +use esp_hal::{rng::Rng, timer::timg::TimerGroup}; +use esp_println as _; +use foa::{FoAResources, FoARunner, VirtualInterface}; +use foa_mesh::state::MeshResources; +use foa_mesh::MeshRunner; +use heapless::String; + +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} + +#[embassy_executor::task] +async fn foa_task(mut runner: FoARunner<'static>) -> ! { + runner.run().await; +} +#[embassy_executor::task] +async fn mesh_task(mut runner: MeshRunner<'static, 'static, Rng>) -> ! { + runner.run().await +} + +#[esp_hal_embassy::main] +async fn main(spawner: Spawner) { + info!("Welcome!"); + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + info!("Embassy!"); + esp_hal_embassy::init(timg0.timer0); + info!("After embassy!"); + + let foa_resources = mk_static!(FoAResources, FoAResources::new()); + let ([mesh_vif, ..], foa_runner) = foa::init( + foa_resources, + peripherals.WIFI, + peripherals.RADIO_CLK, + peripherals.ADC2, + ); + spawner.spawn(foa_task(foa_runner)).unwrap(); + let mesh_vif = mk_static!(VirtualInterface<'static>, mesh_vif); + let mesh_resources = mk_static!(MeshResources, MeshResources::new()); + + let (mut mesh_control, mesh_runner, _mesh_net_device) = foa_mesh::new_mesh_interface( + mesh_resources, + mesh_vif, + 2, + String::try_from("meshtest").unwrap(), + Rng::new(peripherals.RNG), + ); + info!("Before spawn!"); + spawner.spawn(mesh_task(mesh_runner)).unwrap(); + info!("Starting!"); + + mesh_control + .start() + .await + .expect("Failed to start Mesh interface."); + loop { + Timer::after_millis(1000).await; + debug!("still alive!"); + } +} diff --git a/foa/Cargo.lock b/foa/Cargo.lock index afeab2c..1bb6d21 100644 --- a/foa/Cargo.lock +++ b/foa/Cargo.lock @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "bitfield-struct" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b" +checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" dependencies = [ "proc-macro2", "quote", @@ -574,14 +574,14 @@ dependencies = [ [[package]] name = "esp-wifi-hal" -version = "0.1.0-alpha.3" +version = "0.1.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ac4c1adf315e841418396ddb10e88ca67dbcfaed304417c31f8d93a5ea5fce" +checksum = "3df04720437afe1141e9cf264ead1ad452185e46a7cfb3a707677011dfc81b5e" dependencies = [ - "bitfield-struct 0.10.1", + "bitfield-struct 0.11.0", "cfg-if", "critical-section", - "defmt 0.3.100", + "defmt 1.0.1", "defmt-or-log", "embassy-futures", "embassy-sync", @@ -756,9 +756,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "ieee80211" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c870f3ba5d4f36ffdfd3df5e870cada060cf8d926584e3c3ce274f8ced22109" +checksum = "06530bda0af5484a90b9a322dc7bb467a8b1f0d08d3bc50c510ba876fc516b48" dependencies = [ "bitfield-struct 0.8.0", "const_soft_float", diff --git a/foa/Cargo.toml b/foa/Cargo.toml index 47cc0c1..0c35243 100644 --- a/foa/Cargo.toml +++ b/foa/Cargo.toml @@ -8,7 +8,7 @@ embassy-sync = "0.6.0" embassy-futures = "0.1.1" esp-hal = "1.0.0-beta.0" -esp-wifi-hal = "0.1.0-alpha.3" +esp-wifi-hal = "0.1.0-alpha.4" static_cell = "2.1.0" critical-section = "1.2.0" portable-atomic = "1.10.0" @@ -16,7 +16,7 @@ esp-config= "0.3.0" defmt-or-log = { version = "0.2.1", default-features = false } defmt = { version = "0.3.10", optional = true } embassy-time = "0.4.0" -ieee80211 = { version = "0.5.4", default-features = false } +ieee80211 = { version = "0.5.5", default-features = false} heapless = "0.8.0" [build-dependencies] diff --git a/foa_awdl/Cargo.lock b/foa_awdl/Cargo.lock index c05eec0..d44df1b 100644 --- a/foa_awdl/Cargo.lock +++ b/foa_awdl/Cargo.lock @@ -91,9 +91,9 @@ dependencies = [ [[package]] name = "bitfield-struct" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b" +checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" dependencies = [ "proc-macro2", "quote", @@ -656,14 +656,14 @@ dependencies = [ [[package]] name = "esp-wifi-hal" -version = "0.1.0-alpha.3" +version = "0.1.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ac4c1adf315e841418396ddb10e88ca67dbcfaed304417c31f8d93a5ea5fce" +checksum = "3df04720437afe1141e9cf264ead1ad452185e46a7cfb3a707677011dfc81b5e" dependencies = [ - "bitfield-struct 0.10.1", + "bitfield-struct 0.11.0", "cfg-if", "critical-section", - "defmt 0.3.100", + "defmt 1.0.1", "defmt-or-log", "embassy-futures", "embassy-sync", @@ -745,6 +745,7 @@ dependencies = [ "esp-config", "esp-hal", "esp-wifi-hal", + "heapless 0.8.0", "ieee80211", "portable-atomic", "static_cell", @@ -897,9 +898,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "ieee80211" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c870f3ba5d4f36ffdfd3df5e870cada060cf8d926584e3c3ce274f8ced22109" +checksum = "06530bda0af5484a90b9a322dc7bb467a8b1f0d08d3bc50c510ba876fc516b48" dependencies = [ "bitfield-struct 0.8.0", "const_soft_float", @@ -946,9 +947,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "litrs" diff --git a/foa_awdl/Cargo.toml b/foa_awdl/Cargo.toml index 4efd705..e92db19 100644 --- a/foa_awdl/Cargo.toml +++ b/foa_awdl/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" embassy-futures = "0.1.1" embassy-sync = "0.6.2" embassy-time = "0.4.0" -ieee80211 = "0.5.4" +ieee80211 = "0.5.5" foa = { path = "../foa/" } heapless = "0.8.0" rand_core = "0.6.0" diff --git a/foa_mesh/.cargo/config.toml b/foa_mesh/.cargo/config.toml new file mode 100644 index 0000000..85dad2e --- /dev/null +++ b/foa_mesh/.cargo/config.toml @@ -0,0 +1,3 @@ +[unstable] +build-std = ["core"] + diff --git a/foa_mesh/.gitignore b/foa_mesh/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/foa_mesh/.gitignore @@ -0,0 +1 @@ +/target diff --git a/foa_mesh/.vscode/settings.json b/foa_mesh/.vscode/settings.json new file mode 100644 index 0000000..5e676b8 --- /dev/null +++ b/foa_mesh/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "rust-analyzer.check.allTargets": false, + "rust-analyzer.cargo.target": "xtensa-esp32-none-elf", + "rust-analyzer.cargo.features": ["esp32"], + "editor.formatOnSave": true +} diff --git a/foa_mesh/Cargo.lock b/foa_mesh/Cargo.lock new file mode 100644 index 0000000..6bf1c56 --- /dev/null +++ b/foa_mesh/Cargo.lock @@ -0,0 +1,1663 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + +[[package]] +name = "bitfield" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7e6caee68becd795bfd65f1a026e4d00d8f0c2bc9be5eb568e1015f9ce3c34" +dependencies = [ + "bitfield-macros", +] + +[[package]] +name = "bitfield-macros" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331afbb18ce7b644c0b428726d369c5dd37ca0b815d72a459fcc2896c3c8ad32" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitfield-struct" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de05f8756f1c68937349406d4632ae96ae35901019b5e59c508d9c38c64715fb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitfield-struct" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bytemuck" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "const_soft_float" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ca1caa64ef4ed453e68bb3db612e51cf1b2f5b871337f0fcab1c8f87cc3dff" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "defile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa3324a455850286c803c1c16d3835a44a9457765899417775e4dc6080cd942d" +dependencies = [ + "defile-proc_macros", +] + +[[package]] +name = "defile-proc_macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c5c1460bfb0f719e899feab6ae532e5e0f059cdc85913bab8e6a0d7980245e" + +[[package]] +name = "defmt" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" +dependencies = [ + "defmt 1.0.1", +] + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-or-log" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8370630b4dee85ab47d9087813771c5c7fe88d24fdd48649bbdfe6089da4c53a" +dependencies = [ + "defmt 0.3.100", + "defmt-or-log-macros", + "log", +] + +[[package]] +name = "defmt-or-log-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d675dd299edbb7c8e01d4e9f520a0d8f22a8fe4af812c211c3fad5e9dcf41763" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "delegate" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b6483c2bbed26f97861cf57651d4f2b731964a28cd2257f934a4b452480d21" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + +[[package]] +name = "embassy-embedded-hal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fea5ef5bed4d3468dfd44f5c9fa4cda8f54c86d4fb4ae683eacf9d39e2ea12" +dependencies = [ + "embassy-futures", + "embassy-sync", + "embassy-time", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-storage", + "embedded-storage-async", + "nb 1.1.0", +] + +[[package]] +name = "embassy-futures" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" + +[[package]] +name = "embassy-net-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" + +[[package]] +name = "embassy-net-driver-channel" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4818c32afec43e3cae234f324bad9a976c9aa7501022d26ff60a4017a1a006b7" +dependencies = [ + "embassy-futures", + "embassy-net-driver", + "embassy-sync", +] + +[[package]] +name = "embassy-sync" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-sink", + "futures-util", + "heapless 0.8.0", +] + +[[package]] +name = "embassy-time" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f820157f198ada183ad62e0a66f554c610cdcd1a9f27d4b316358103ced7a1f8" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-util", +] + +[[package]] +name = "embassy-time-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba" +dependencies = [ + "document-features", +] + +[[package]] +name = "embassy-usb-driver" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970" + +[[package]] +name = "embassy-usb-synopsys-otg" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08e753b23799329780c7ac434264026d0422044d6649ed70a73441b14a6436d7" +dependencies = [ + "critical-section", + "embassy-sync", + "embassy-usb-driver", +] + +[[package]] +name = "embedded-can" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io", +] + +[[package]] +name = "embedded-storage" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" + +[[package]] +name = "embedded-storage-async" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc" +dependencies = [ + "embedded-storage", +] + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enumset" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "esp-build" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aa1c8f9954c9506699cf1ca10a2adcc226ff10b6ae3cb9e875cf2c6a0b9a372" +dependencies = [ + "quote", + "syn", + "termcolor", +] + +[[package]] +name = "esp-config" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158dba334d3a2acd8d93873c0ae723ca1037cc78eefe5d6b4c5919b0ca28e38e" +dependencies = [ + "document-features", +] + +[[package]] +name = "esp-hal" +version = "1.0.0-beta.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9efaa9c1324ca20a22086aba2ce47a9bdc5bd65969af8b0cd5e879603b57bef" +dependencies = [ + "basic-toml", + "bitfield", + "bitflags 2.9.0", + "bytemuck", + "cfg-if", + "chrono", + "critical-section", + "delegate", + "document-features", + "embassy-embedded-hal", + "embassy-futures", + "embassy-sync", + "embassy-usb-driver", + "embassy-usb-synopsys-otg", + "embedded-can", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-io", + "embedded-io-async", + "enumset", + "esp-build", + "esp-config", + "esp-hal-procmacros", + "esp-metadata", + "esp-riscv-rt", + "esp-synopsys-usb-otg", + "esp32", + "esp32s2", + "fugit", + "instability", + "nb 1.1.0", + "paste", + "portable-atomic", + "rand_core", + "riscv", + "serde", + "strum 0.27.1", + "ufmt-write", + "usb-device", + "void", + "xtensa-lx", + "xtensa-lx-rt", +] + +[[package]] +name = "esp-hal-procmacros" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd340a20a7d546570af58fd9e2aae17466a42572680d8e70d35fc7c475c4ed8" +dependencies = [ + "darling", + "document-features", + "litrs", + "object", + "proc-macro-crate", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "esp-metadata" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b4bffc22b7b1222c9467f0cb90eb49dcb63de810ecb3300e4b3bbc4ac2423e" +dependencies = [ + "anyhow", + "basic-toml", + "serde", + "strum 0.26.3", +] + +[[package]] +name = "esp-riscv-rt" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec69987b3d7c48b65f8fb829220832a101478d766c518ae836720d040608d5dd" +dependencies = [ + "document-features", + "riscv", + "riscv-rt-macros", +] + +[[package]] +name = "esp-synopsys-usb-otg" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8938451cb19032f13365328ea66ab38c8d16deecdf322067442297110eb74468" +dependencies = [ + "critical-section", + "embedded-hal 0.2.7", + "ral-registers", + "usb-device", + "vcell", +] + +[[package]] +name = "esp-wifi-hal" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df04720437afe1141e9cf264ead1ad452185e46a7cfb3a707677011dfc81b5e" +dependencies = [ + "bitfield-struct 0.11.0", + "cfg-if", + "critical-section", + "defmt 1.0.1", + "defmt-or-log", + "embassy-futures", + "embassy-sync", + "embassy-time", + "esp-hal", + "esp-wifi-sys", + "esp32", + "esp32s2", + "macro-bits", + "portable-atomic", + "static_cell", +] + +[[package]] +name = "esp-wifi-sys" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b5438361891c431970194a733415006fb3d00b6eb70b3dcb66fd58f04d9b39" +dependencies = [ + "anyhow", +] + +[[package]] +name = "esp32" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d9b774d7a2c96550a5c25016c2abd33370ebac60e534484b7bca344ecb8a3d6" +dependencies = [ + "critical-section", + "vcell", +] + +[[package]] +name = "esp32s2" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22733ee6f4bb08e3113df6651b2c350f37c44314017476e354ec951a55465e9" +dependencies = [ + "critical-section", + "vcell", +] + +[[package]] +name = "ether-type" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bac650e88cff238d79c192cbdfa0a4f48d5c3772fd0440240e4faa740c1e155" +dependencies = [ + "macro-bits", +] + +[[package]] +name = "ethernet" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693cde2264aad94890b524e18558ec5bd02706ce66e56581ef2d47f678976b8a" +dependencies = [ + "ether-type", + "mac-parser", + "scroll", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foa" +version = "0.1.0-alpha.0" +dependencies = [ + "critical-section", + "defmt 0.3.100", + "defmt-or-log", + "embassy-futures", + "embassy-sync", + "embassy-time", + "esp-config", + "esp-hal", + "esp-wifi-hal", + "heapless 0.8.0", + "ieee80211", + "portable-atomic", + "static_cell", +] + +[[package]] +name = "foa_mesh" +version = "0.1.0" +dependencies = [ + "defmt 0.3.100", + "defmt-or-log", + "embassy-futures", + "embassy-net-driver-channel", + "embassy-sync", + "embassy-time", + "esp-config", + "ethernet", + "foa", + "heapless 0.8.0", + "ieee80211", + "llc-rs", + "rand_core", +] + +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "gcd", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32 0.2.1", + "rustc_version", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "ieee80211" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06530bda0af5484a90b9a322dc7bb467a8b1f0d08d3bc50c510ba876fc516b48" +dependencies = [ + "bitfield-struct 0.8.0", + "const_soft_float", + "crc32fast", + "defmt 0.3.100", + "hmac", + "mac-parser", + "macro-bits", + "num", + "pbkdf2", + "scroll", + "sha1", + "tlv-rs", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + +[[package]] +name = "instability" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "llc-rs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8e4ef2dfcffe25af1c817cd8ac207133563f4160a227d9bc57ab606c07685fc" +dependencies = [ + "ether-type", + "macro-bits", + "scroll", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "mac-parser" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0232db59d79d889d40df09c8d66cc39f594c05ad2e06da8cf70c1be21c4c82f7" +dependencies = [ + "defmt 0.3.100", + "scroll", +] + +[[package]] +name = "macro-bits" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eb7867e4591ecd9f96c60f33481a3a07783d6f1ab2de522d0f243c487260c9a" +dependencies = [ + "defile", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minijinja" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754" +dependencies = [ + "serde", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "no-panic" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113d1abd5bb3dc25a75d9b3a973f40e31eb03e0bae23c172b32cca4bcb9cfad2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +dependencies = [ + "critical-section", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r0" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" + +[[package]] +name = "ral-registers" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46b71a9d9206e8b46714c74255adcaea8b11e0350c1d8456165073c3f75fc81a" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "riscv" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", + "paste", + "riscv-macros", + "riscv-pac", +] + +[[package]] +name = "riscv-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "riscv-pac" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" + +[[package]] +name = "riscv-rt-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc71814687c45ba4cd1e47a54e03a2dbc62ca3667098fbae9cc6b423956758fa" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_cell" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +dependencies = [ + "strum_macros 0.27.1", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "strum_macros" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tlv-rs" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11652cddb16c63624ab8c0d49187b65a20c41b66664c9d16b4d36c4748d64ed0" +dependencies = [ + "heapless 0.7.17", + "no-panic", + "scroll", +] + +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "ufmt-write" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless 0.8.0", + "portable-atomic", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +dependencies = [ + "memchr", +] + +[[package]] +name = "xtensa-lx" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51cbb46c78cfd284c9378070ab90bae9d14d38b3766cb853a97c0a137f736d5b" +dependencies = [ + "critical-section", + "document-features", +] + +[[package]] +name = "xtensa-lx-rt" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "689c2ef159d9cd4fc9503603e9999968a84a30db9bde0f0f880d0cceea0190a9" +dependencies = [ + "anyhow", + "document-features", + "enum-as-inner", + "minijinja", + "r0", + "serde", + "strum 0.26.3", + "toml", + "xtensa-lx", + "xtensa-lx-rt-proc-macros", +] + +[[package]] +name = "xtensa-lx-rt-proc-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11277b1e4cbb7ffe44678c668518b249c843c81df249b8f096701757bc50d7ee" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] diff --git a/foa_mesh/Cargo.toml b/foa_mesh/Cargo.toml new file mode 100644 index 0000000..314a13c --- /dev/null +++ b/foa_mesh/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "foa_mesh" +version = "0.1.0" +edition = "2024" + +[dependencies] +embassy-futures = "0.1.1" +embassy-sync = "0.6.2" +embassy-time = "0.4.0" +embassy-net-driver-channel = "0.3.0" + +foa = { path = "../foa/" } + +defmt = { version = "0.3.10", optional = true } +defmt-or-log = { version = "0.2.1", default-features = false } +esp-config = "0.3.1" +heapless = "0.8.0" +rand_core = "0.6.0" + +llc-rs = "0.1.0" +ethernet = { version = "0.1.5", default-features = false } +ieee80211 = "0.5.5" + +[build-dependencies] +esp-config = { version = "0.3.1", features = ["build"] } + +[features] +esp32 = ["foa/esp32"] +esp32s2 = ["foa/esp32s2"] + +defmt = ["dep:defmt", "defmt-or-log/defmt", "ieee80211/defmt", "foa/defmt"] +log = ["defmt-or-log/log"] + + diff --git a/foa_mesh/build.rs b/foa_mesh/build.rs new file mode 100644 index 0000000..3afcf60 --- /dev/null +++ b/foa_mesh/build.rs @@ -0,0 +1,34 @@ +use esp_config::{Validator, Value, generate_config}; + +fn main() { + generate_config( + "foa_mesh", + &[ + ( + "RX_QUEUE_DEPTH", + "The depth of the user and background RX queues.", + Value::Integer(4), + Some(Validator::PositiveInteger), + ), + ( + "NET_TX_BUFFERS", + "The amount of TX buffers used for embassy_net_driver_channel.", + Value::Integer(4), + Some(Validator::PositiveInteger), + ), + ( + "NET_RX_BUFFERS", + "The amount of RX buffers used for embassy_net_driver_channel.", + Value::Integer(4), + Some(Validator::PositiveInteger), + ), + ( + "MAX_NUM_PEERS", + "Maximum amount of peers connected at the same time.", + Value::Integer(5), + Some(Validator::PositiveInteger), + ), + ], + true, + ); +} diff --git a/foa_mesh/config.toml b/foa_mesh/config.toml new file mode 100644 index 0000000..85dad2e --- /dev/null +++ b/foa_mesh/config.toml @@ -0,0 +1,3 @@ +[unstable] +build-std = ["core"] + diff --git a/foa_mesh/rust-toolchain.toml b/foa_mesh/rust-toolchain.toml new file mode 100644 index 0000000..a2f5ab5 --- /dev/null +++ b/foa_mesh/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "esp" diff --git a/foa_mesh/src/control.rs b/foa_mesh/src/control.rs new file mode 100644 index 0000000..92e4c59 --- /dev/null +++ b/foa_mesh/src/control.rs @@ -0,0 +1,72 @@ +use foa::{LMacInterfaceControl, esp_wifi_hal::RxFilterBank}; +use ieee80211::mac_parser::MACAddress; + +use crate::{CommonResources, MeshError, rx_router::MeshRxRouterEndpoint, state::MeshState}; + +pub struct MeshControl<'foa, 'vif> { + pub(crate) interface_control: &'vif LMacInterfaceControl<'foa>, + pub(crate) rx_router_endpoint: MeshRxRouterEndpoint<'foa, 'vif>, + pub(crate) common_resources: &'vif CommonResources, + + pub(crate) channel: u8, + pub(crate) mac_address: MACAddress, + pub(crate) mesh_id: heapless::String<32>, +} + +impl MeshControl<'_, '_> { + /// Set and enable all filters required for the interface. + fn enable_filters(&self) { + self.interface_control.set_filter_parameters( + RxFilterBank::ReceiverAddress, + *self.mac_address, + None, + ); + self.interface_control + .set_filter_status(RxFilterBank::ReceiverAddress, true); + } + /// Disable all filter for the interface. + fn disable_filters(&self) { + self.interface_control + .set_filter_status(RxFilterBank::ReceiverAddress, false); + } + /// Start a Mesh session. + /// + /// If a session is already in progress, it will be immediately aborted and the new session + /// takes over. + pub async fn start(&mut self) -> Result<(), MeshError> { + let bringup_operation = self + .interface_control + .begin_interface_bringup_operation(self.channel) + .map_err(|_| MeshError::FailedToAcquireChannelLock)?; + self.interface_control + .wait_for_off_channel_completion() + .await; + self.enable_filters(); + self.common_resources + .state_signal + .signal(MeshState::Active { + our_address: self.mac_address, + channel: self.channel, + mesh_id: self.mesh_id.clone(), + }); + bringup_operation.complete(); + Ok(()) + } + /// Stop the currently active Mesh session. + /// + /// If no session is in progress, this won't do anything. + pub fn stop(&mut self) { + self.interface_control.unlock_channel(); + self.disable_filters(); + self.common_resources + .state_signal + .signal(MeshState::Inactive); + } + + /// Set the MAC address of the interface. + /// + /// This will only take effect after restarting the interface. + pub fn set_mac_address(&mut self, mac_address: [u8; 6]) { + self.mac_address = MACAddress::new(mac_address); + } +} diff --git a/foa_mesh/src/lib.rs b/foa_mesh/src/lib.rs new file mode 100644 index 0000000..1b7b08d --- /dev/null +++ b/foa_mesh/src/lib.rs @@ -0,0 +1,72 @@ +#![no_std] +#![feature(cell_update)] + +mod control; +pub mod peer_state; +mod runner; +mod rx_router; +pub mod state; + +use embassy_net_driver_channel::{Device as NetDevice, driver::HardwareAddress}; +use esp_config::esp_config_int; +use foa::VirtualInterface; +use ieee80211::mac_parser::MACAddress; +use rand_core::RngCore; +use state::{CommonResources, MeshResources}; + +pub use {control::MeshControl, runner::MeshRunner}; + +pub(crate) const RX_QUEUE_DEPTH: usize = esp_config_int!(usize, "FOA_MESH_CONFIG_RX_QUEUE_DEPTH"); +pub(crate) const NET_TX_BUFFERS: usize = esp_config_int!(usize, "FOA_MESH_CONFIG_NET_TX_BUFFERS"); +pub(crate) const NET_RX_BUFFERS: usize = esp_config_int!(usize, "FOA_MESH_CONFIG_NET_RX_BUFFERS"); +pub(crate) const MAX_NUM_PEERS: usize = esp_config_int!(usize, "FOA_MESH_CONFIG_MAX_NUM_PEERS"); + +pub const MTU: usize = 1500; +pub type MeshNetDevice<'a> = NetDevice<'a, MTU>; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +/// Errors that can occur with the Mesh interface. +pub enum MeshError { + FailedToAcquireChannelLock, +} + +pub fn new_mesh_interface<'foa, 'vif, Rng: RngCore + Copy>( + resources: &'vif mut MeshResources<'foa>, + virtual_interface: &'vif mut VirtualInterface<'foa>, + channel: u8, + mesh_id: heapless::String<32>, + rng: Rng, +) -> ( + MeshControl<'foa, 'vif>, + MeshRunner<'foa, 'vif, Rng>, + MeshNetDevice<'vif>, +) { + virtual_interface.reset(); + + let (interface_control, interface_rx_queue_receiver) = virtual_interface.split(); + let (rx_router_input, [foreground_endpoint, background_endpoint]) = resources.rx_router.split(); + let (net_runner, net_device) = embassy_net_driver_channel::new( + &mut resources.net_state, + HardwareAddress::Ethernet(interface_control.get_factory_mac_for_interface()), + ); + ( + MeshControl { + interface_control, + rx_router_endpoint: foreground_endpoint, + common_resources: &resources.common_resources, + channel, + mac_address: MACAddress::new(interface_control.get_factory_mac_for_interface()), + mesh_id, + }, + MeshRunner::new( + net_runner, + background_endpoint, + rx_router_input, + interface_rx_queue_receiver, + interface_control, + &resources.common_resources, + rng.clone(), + ), + net_device, + ) +} diff --git a/foa_mesh/src/peer_state.rs b/foa_mesh/src/peer_state.rs new file mode 100644 index 0000000..ab9b57d --- /dev/null +++ b/foa_mesh/src/peer_state.rs @@ -0,0 +1,125 @@ +use core::ops::Index; + +use defmt_or_log::derive_format_or_debug; +use heapless::FnvIndexMap; +use ieee80211::{common::AssociationID, mac_parser::MACAddress}; + +use crate::{MAX_NUM_PEERS, state::MPMFSMState}; + +#[derive(Clone, PartialEq, Eq, Hash, Copy)] +#[derive_format_or_debug] +pub(crate) struct MeshPeerState { + pub(crate) mpm_state: MPMFSMState, +} + +impl Default for MeshPeerState { + fn default() -> Self { + Self { + mpm_state: MPMFSMState::Idle, + } + } +} + +pub(crate) struct PeerListFullError; + +pub(crate) trait MeshPeerList: + for<'a> Index<&'a MACAddress, Output = MeshPeerState> +{ + const UNINIT: Self; + + fn iter(&self) -> impl Iterator + '_; + fn retain(&mut self, pred: impl FnMut(&MACAddress, &mut MeshPeerState) -> bool); + fn get(&self, address: &MACAddress) -> Option<&MeshPeerState>; + fn get_mut(&mut self, address: &MACAddress) -> Option<&mut MeshPeerState>; + fn is_full(&self) -> bool; + fn insert( + &mut self, + address: MACAddress, + peer: MeshPeerState, + ) -> Result, (MACAddress, MeshPeerState)>; + fn clear(&mut self); + fn contains_key(&self, address: &MACAddress) -> bool; + + /// Modify the peer, or attempt to add it if it's not present. + /// + /// If the peer list is full a [PeerListFullError]. + fn modify_or_add_peer( + &mut self, + peer_address: &MACAddress, + mut modify: impl FnMut(&mut MeshPeerState), + add: impl FnOnce() -> Option, + ) -> Result { + Ok(match self.get_mut(peer_address) { + Some(peer) => { + (modify)(peer); + false + } + None => { + if self.is_full() { + return Err(PeerListFullError); + } else { + let Some(peer) = (add)() else { + return Ok(false); + }; + let _ = self.insert(*peer_address, peer); + true + } + } + }) + } + + /// Iterate over the mesh peers and execute the closure on them. + fn inspect_peers(&self, f: impl FnMut((&MACAddress, &MeshPeerState))) { + self.iter().for_each(f); + } + + fn get_or_create( + &mut self, + peer_address: &MACAddress, + ) -> Result { + Ok(match self.get(peer_address) { + Some(peer) => *peer, + None => { + if self.is_full() { + return Err(PeerListFullError); + } else { + let peer: MeshPeerState = Default::default(); + let _ = self.insert(*peer_address, peer); + peer + } + } + }) + } +} +pub type StaticMeshPeerList = FnvIndexMap; +impl MeshPeerList for StaticMeshPeerList { + const UNINIT: Self = Self::new(); + fn get(&self, peer_address: &MACAddress) -> Option<&MeshPeerState> { + self.get(peer_address) + } + fn get_mut(&mut self, peer_address: &MACAddress) -> Option<&mut MeshPeerState> { + self.get_mut(peer_address) + } + fn iter(&self) -> impl Iterator + '_ { + self.iter() + } + fn clear(&mut self) { + self.clear(); + } + fn retain(&mut self, pred: impl FnMut(&MACAddress, &mut MeshPeerState) -> bool) { + self.retain(pred); + } + fn insert( + &mut self, + peer_address: MACAddress, + peer: MeshPeerState, + ) -> Result, (MACAddress, MeshPeerState)> { + self.insert(peer_address, peer) + } + fn is_full(&self) -> bool { + self.capacity() == self.len() + } + fn contains_key(&self, peer_address: &MACAddress) -> bool { + self.contains_key(peer_address) + } +} diff --git a/foa_mesh/src/runner/management.rs b/foa_mesh/src/runner/management.rs new file mode 100644 index 0000000..929e086 --- /dev/null +++ b/foa_mesh/src/runner/management.rs @@ -0,0 +1,707 @@ +use core::marker::PhantomData; + +use defmt_or_log::debug; +use embassy_futures::select::{Either3, select3}; +use embassy_net_driver_channel::StateRunner as NetStateRunner; + +use foa::{ + LMacInterfaceControl, + esp_wifi_hal::{TxErrorBehaviour, TxParameters, WiFiRate}, +}; +use rand_core::RngCore; + +use crate::{ + peer_state::MeshPeerList, + rx_router::MeshRxRouterEndpoint, + state::{CommonResources, MPMFSMState, MPMFSMSubState}, +}; + +use embassy_time::{Duration, Ticker}; +use ieee80211::{ + common::{AssociationID, CapabilitiesInformation, IEEE80211Reason, TU}, + element_chain, + elements::{ + self, DSSSParameterSetElement, MeshIDElement, ReadElements, + mesh::{ + MeshCapability, MeshConfigurationActivePathSelectionMetricIdentifier, + MeshConfigurationActivePathSelectionProtocolIdentifier, + MeshConfigurationAuthenticationProtocolIdentifier, + MeshConfigurationCongestionControlModeIdentifier, MeshConfigurationElement, + MeshConfigurationSynchronizationMethodIdentifier, MeshFormationInfo, + MeshPeeringManagement, MeshPeeringProtocolIdentifier, + }, + rates::{EncodedRate, ExtendedSupportedRatesElement, SupportedRatesElement}, + tim::{TIMBitmap, TIMElement}, + }, + extended_supported_rates, + mac_parser::{BROADCAST, MACAddress}, + match_frames, mesh_id, + mgmt_frame::{ + BeaconFrame, ManagementFrameHeader, + body::{ + BeaconBody, + action::{ + MeshPeeringCloseBody, MeshPeeringCloseFrame, MeshPeeringConfirmBody, + MeshPeeringConfirmFrame, MeshPeeringOpenBody, MeshPeeringOpenFrame, + }, + }, + }, + scroll::Pwrite, + ssid, supported_rates, +}; + +const BEACON_INTERVAL_TU: u64 = 100; + +// TODO deduplicate this from foa_sta +const DEFAULT_SUPPORTED_RATES: SupportedRatesElement<[EncodedRate; 8]> = supported_rates![ + 1 B, + 2, + 5.5, + 11, + 6, + 9, + 12, + 18 +]; + +const DEFAULT_XRATES: ExtendedSupportedRatesElement<[EncodedRate; 4]> = + extended_supported_rates![24, 36, 48, 54]; + +// TODO make this settable from the consumer of this library +const MESH_ID: &str = "meshtest"; + +pub struct MeshManagementRunner<'foa, 'vif, Rng: RngCore + Copy> { + pub(crate) interface_control: &'vif LMacInterfaceControl<'foa>, + pub(crate) rx_router_endpoint: MeshRxRouterEndpoint<'foa, 'vif>, + pub(crate) net_state_runner: NetStateRunner<'vif>, + pub(crate) common_resources: &'vif CommonResources, + pub(crate) rng: Rng, +} + +impl MeshManagementRunner<'_, '_, Rng> { + fn generate_own_mesh_configuration_element() -> MeshConfigurationElement { + MeshConfigurationElement { + active_path_selection_protocol_identifier: + MeshConfigurationActivePathSelectionProtocolIdentifier::HWMP, + active_path_selection_metric_identifier: + MeshConfigurationActivePathSelectionMetricIdentifier::AirtimeLinkMetric, + congestion_control_mode_identifier: + MeshConfigurationCongestionControlModeIdentifier::NotActivated, + syncronization_method_identifier: + MeshConfigurationSynchronizationMethodIdentifier::NeighborOffsetSynchronization, + authentication_protocol_identifier: + MeshConfigurationAuthenticationProtocolIdentifier::NoAuthentication, + mesh_formation_info: MeshFormationInfo::new() + .with_connected_to_mesh_gate(false) // TODO fill this in once we have it + .with_num_peerings(0) // TODO fill this in + .with_connected_to_as(false), // 'Connected to authentication system' is always false in open / SAE mesh + mesh_capability: MeshCapability::new() + .with_accept_additional_mesh_peerings(true) // TODO fill this in + .with_forwarding(true), + } + } + + fn does_mesh_sta_configuration_match(elements: ReadElements) -> bool { + // Check if mesh profile is equal + if (elements + .get_first_element::() + .map(|a| (a.ssid() != MESH_ID))) + .unwrap_or(true) + { + return false; + } + + let Some(peer_config_element) = elements.get_first_element::() + else { + debug!("no mesh configuration element"); + return false; + }; + let own_config_element = Self::generate_own_mesh_configuration_element(); + if peer_config_element.active_path_selection_metric_identifier + != own_config_element.active_path_selection_metric_identifier + || peer_config_element.active_path_selection_protocol_identifier + != own_config_element.active_path_selection_protocol_identifier + || peer_config_element.authentication_protocol_identifier + != own_config_element.authentication_protocol_identifier + || peer_config_element.congestion_control_mode_identifier + != own_config_element.congestion_control_mode_identifier + || peer_config_element.syncronization_method_identifier + != own_config_element.syncronization_method_identifier + { + debug!("mesh configuration element mismatch"); + return false; + } + // TODO we should also check the peers EPD capability; but this is very uncommon on 2.4GHz or 5GHz + + // Check if all other fields of the mesh STA configuration matches + if (elements + .get_first_element::() + .map(|a| (a != DEFAULT_SUPPORTED_RATES))) + .unwrap_or(true) + { + debug!("default rates mismatch"); + return false; + } + if elements + .get_first_element::() + .map(|a| (a != DEFAULT_XRATES)) + .unwrap_or(true) + { + debug!("extended rates mismatch"); + return false; + } + + true + } + + pub async fn send_beacon_frame(&mut self, address: &MACAddress) { + let mut tx_buffer = self.interface_control.alloc_tx_buf().await; + + // TODO this is currently only for 802.11bg, but not N + // we could automatically adapt the contents of our beacon frames to other mesh stations + // so that we can peer with them (only permitted if mesh id, rates and MeshConfigurationElement match) + let beacon_frame = BeaconFrame { + header: ManagementFrameHeader { + receiver_address: BROADCAST, + transmitter_address: *address, + bssid: *address, + ..Default::default() + }, + body: BeaconBody { + timestamp: 0, // TODO let the hardware fill this in automatically + beacon_interval: BEACON_INTERVAL_TU as u16, + capabilities_info: CapabilitiesInformation::new(), + elements: element_chain! { + ssid!(""), // wildcard SSID + DEFAULT_SUPPORTED_RATES, + DSSSParameterSetElement { + current_channel: self.interface_control.home_channel().unwrap_or(1) + }, + TIMElement { + dtim_count: 1, // TODO oscillate this + dtim_period: 2, + bitmap: None::>, // TODO fill this in + _phantom: PhantomData + }, + DEFAULT_XRATES, + mesh_id!(MESH_ID), + Self::generate_own_mesh_configuration_element() + + }, + _phantom: PhantomData, + }, + }; + + let written = tx_buffer.pwrite(beacon_frame, 0).unwrap(); + let _ = self + .interface_control + .transmit( + &mut tx_buffer[..written], + &TxParameters { + rate: WiFiRate::PhyRate12M, + override_seq_num: true, + tx_error_behaviour: TxErrorBehaviour::Drop, + ..Default::default() + }, + false, + ) + .await; + } + + pub async fn send_mesh_peering_confirm( + &mut self, + our_address: &MACAddress, + dst_address: &MACAddress, + aid: AssociationID, + local_link_id: u16, + peer_link_id: u16, + ) { + let mut tx_buffer = self.interface_control.alloc_tx_buf().await; + let mesh_peering_confirm_frame = MeshPeeringConfirmFrame { + header: ManagementFrameHeader { + receiver_address: *dst_address, + transmitter_address: *our_address, + bssid: *our_address, + ..Default::default() + }, + body: MeshPeeringConfirmBody { + capabilities_info: CapabilitiesInformation::new(), + association_id: aid, + elements: element_chain! { + DEFAULT_SUPPORTED_RATES, + DEFAULT_XRATES, + mesh_id!(MESH_ID), + Self::generate_own_mesh_configuration_element(), + MeshPeeringManagement::new_confirm( + MeshPeeringProtocolIdentifier::MeshPeeringManagementProtocol, + local_link_id, peer_link_id, + None) + }, + _phantom: PhantomData, + }, + }; + + let written = tx_buffer.pwrite(mesh_peering_confirm_frame, 0).unwrap(); + let _ = self + .interface_control + .transmit( + &mut tx_buffer[..written], + &TxParameters { + rate: WiFiRate::PhyRate12M, + override_seq_num: true, + tx_error_behaviour: TxErrorBehaviour::Drop, + ..Default::default() + }, + false, + ) + .await; + } + + pub async fn send_mesh_peering_open( + &mut self, + our_address: &MACAddress, + dst_address: &MACAddress, + local_link_id: u16, + ) { + let mut tx_buffer = self.interface_control.alloc_tx_buf().await; + let mesh_peering_confirm_frame = MeshPeeringOpenFrame { + header: ManagementFrameHeader { + receiver_address: *dst_address, + transmitter_address: *our_address, + bssid: *our_address, + ..Default::default() + }, + body: MeshPeeringOpenBody { + capabilities_info: CapabilitiesInformation::new(), + elements: element_chain! { + DEFAULT_SUPPORTED_RATES, + DEFAULT_XRATES, + mesh_id!(MESH_ID), + Self::generate_own_mesh_configuration_element(), + MeshPeeringManagement::new_open( + MeshPeeringProtocolIdentifier::MeshPeeringManagementProtocol, + local_link_id, + None) + }, + _phantom: PhantomData, + }, + }; + + let written = tx_buffer.pwrite(mesh_peering_confirm_frame, 0).unwrap(); + let _ = self + .interface_control + .transmit( + &mut tx_buffer[..written], + &TxParameters { + rate: WiFiRate::PhyRate12M, + override_seq_num: true, + tx_error_behaviour: TxErrorBehaviour::Drop, + ..Default::default() + }, + false, + ) + .await; + } + + pub async fn send_mesh_peering_close( + &mut self, + our_address: &MACAddress, + dst_address: &MACAddress, + local_link_id: u16, + peer_link_id: Option, + reason: IEEE80211Reason, + ) { + let mut tx_buffer = self.interface_control.alloc_tx_buf().await; + let mesh_peering_close_frame = MeshPeeringCloseFrame { + header: ManagementFrameHeader { + receiver_address: *dst_address, + transmitter_address: *our_address, + bssid: *our_address, + ..Default::default() + }, + body: MeshPeeringCloseBody { + elements: element_chain! { + mesh_id!(MESH_ID), + MeshPeeringManagement::new_close( + MeshPeeringProtocolIdentifier::MeshPeeringManagementProtocol, + local_link_id, peer_link_id, + reason, + None) + }, + _phantom: PhantomData, + }, + }; + + let written = tx_buffer.pwrite(mesh_peering_close_frame, 0).unwrap(); + let _ = self + .interface_control + .transmit( + &mut tx_buffer[..written], + &TxParameters { + rate: WiFiRate::PhyRate12M, + override_seq_num: true, + tx_error_behaviour: TxErrorBehaviour::Drop, + ..Default::default() + }, + false, + ) + .await; + } + + pub fn generate_new_link_id(&self) -> u16 { + loop { + let candidate = u16::try_from(self.rng.clone().next_u32() & 0xFFFF).unwrap(); + // Check if there is a link in our peer list that already has that ID + if !self + .common_resources + .peer_list + .borrow() + .borrow() + .iter() + .map(|peer| match peer.1.mpm_state { + MPMFSMState::Idle => false, + MPMFSMState::Setup { + local_link_id, + peer_link_id, + .. + } => local_link_id == candidate || peer_link_id == candidate, + MPMFSMState::Estab { + local_link_id, + peer_link_id, + .. + } => local_link_id == candidate || peer_link_id == candidate, + MPMFSMState::Holding { + local_link_id, + peer_link_id, + .. + } => { + local_link_id == candidate + || peer_link_id.map(|id| id == candidate).unwrap_or(false) + } + }) + .fold(false, |acc, mk| acc || mk) + { + // This generated ID was not yet in use + return candidate; + } + } + } + + pub async fn process_mesh_peering_open( + &mut self, + mesh_peering_open_frame: &MeshPeeringOpenFrame<'_>, + our_address: &MACAddress, + ) -> Option<()> { + let addr = mesh_peering_open_frame.header.transmitter_address; + let peer_link_id = mesh_peering_open_frame + .body + .elements + .get_first_element::()? + .parse_as_open()? + .local_link_id; + if Self::does_mesh_sta_configuration_match(mesh_peering_open_frame.elements) { + // check that we still have space left for an extra association + let peer = { + self.common_resources + .lock_peer_list(|mut peer_list| peer_list.get_or_create(&addr)) + }; + let Ok(peer) = peer else { + self.send_mesh_peering_close( + our_address, + &addr, + peer_link_id, + None, + IEEE80211Reason::Unspecified, // TODO correct error code + ) + .await; + return None; + }; + + match peer.mpm_state { + MPMFSMState::Idle => { + let local_link_id = self.generate_new_link_id(); + let local_aid = self.common_resources.new_association_id(); + self.common_resources.lock_peer_list(|mut peer_list| { + let _ = peer_list.modify_or_add_peer( + &addr, + |peer| { + peer.mpm_state = MPMFSMState::Setup { + mac_addr: addr, + local_link_id: local_link_id, + peer_link_id: peer_link_id, + substate: MPMFSMSubState::OpnRcvd, + local_aid: local_aid, + remote_aid: None, + } + }, + || None, + ); + }); + + self.send_mesh_peering_confirm( + our_address, + &addr, + local_aid, + local_link_id, + peer_link_id, + ) + .await; + self.send_mesh_peering_open(our_address, &addr, local_link_id) + .await; + } + MPMFSMState::Setup { + mac_addr, + local_link_id, + peer_link_id, + substate, + local_aid, + remote_aid, + } => match substate { + MPMFSMSubState::OpnSnt => { + self.common_resources.lock_peer_list(|mut peer_list| { + let _ = peer_list.modify_or_add_peer( + &addr, + |peer| { + peer.mpm_state = MPMFSMState::Setup { + mac_addr, + local_link_id, + peer_link_id, + substate: MPMFSMSubState::OpnRcvd, + local_aid, + remote_aid, + } + }, + || None, + ); + }); + self.send_mesh_peering_confirm( + our_address, + &addr, + local_aid, + local_link_id, + peer_link_id, + ) + .await + } + MPMFSMSubState::CnfRcvd => { + self.common_resources.lock_peer_list(|mut peer_list| { + let _ = peer_list.modify_or_add_peer( + &addr, + |peer| { + peer.mpm_state = MPMFSMState::Estab { + mac_addr, + local_link_id, + peer_link_id, + local_aid, + remote_aid: remote_aid.unwrap(), + } + }, + || None, + ); + }); + self.send_mesh_peering_confirm( + our_address, + &addr, + local_aid, + local_link_id, + peer_link_id, + ) + .await; + } + MPMFSMSubState::OpnRcvd => { + self.send_mesh_peering_confirm( + our_address, + &addr, + local_aid, + local_link_id, + peer_link_id, + ) + .await; + } + }, + MPMFSMState::Estab { + local_link_id, + peer_link_id, + local_aid, + .. + } => { + self.send_mesh_peering_confirm( + our_address, + &addr, + local_aid, + local_link_id, + peer_link_id, + ) + .await; + } + MPMFSMState::Holding { + local_link_id, + peer_link_id, + .. + } => { + self.send_mesh_peering_close( + our_address, + &addr, + local_link_id, + peer_link_id, + IEEE80211Reason::Unspecified, // TODO correct error code + ) + .await; + } + }; + } else { + self.send_mesh_peering_close( + our_address, + &addr, + peer_link_id, + None, + IEEE80211Reason::Unspecified, // TODO correct error code + ) + .await; + } + + None + } + + pub async fn process_mesh_peering_confirm( + &mut self, + mesh_peering_confirm_frame: &MeshPeeringConfirmFrame<'_>, + our_address: &MACAddress, + ) -> Option<()> { + let addr = mesh_peering_confirm_frame.header.transmitter_address; + let mpm = mesh_peering_confirm_frame + .body + .elements + .get_first_element::()? + .parse_as_confirm()?; + let remote_aid = mesh_peering_confirm_frame.association_id; + if !Self::does_mesh_sta_configuration_match(mesh_peering_confirm_frame.elements) { + self.send_mesh_peering_close( + our_address, + &addr, + mpm.peer_link_id.unwrap_or(0), + Some(mpm.local_link_id), + IEEE80211Reason::Unspecified, // TODO should be MESH-INCONSISTENT-PARAMETERS. + ) + .await; + return None; + } + // mesh configuration matches + let peer = { + self.common_resources + .lock_peer_list(|mut peer_list| peer_list.get_or_create(&addr)) + }; + let Ok(peer) = peer else { + // Normally, we should be in a state where we know about the peer, so reject + self.send_mesh_peering_close( + our_address, + &addr, + mpm.peer_link_id.unwrap_or(0), + Some(mpm.local_link_id), + IEEE80211Reason::Unspecified, // TODO correct error code + ) + .await; + return None; + }; + + match peer.mpm_state { + MPMFSMState::Idle | MPMFSMState::Holding { .. } => { + self.send_mesh_peering_close( + our_address, + &addr, + mpm.peer_link_id.unwrap_or(0), + Some(mpm.local_link_id), + IEEE80211Reason::Unspecified, // TODO correct error code + ) + .await; + } + MPMFSMState::Estab { .. } => { + // Ignore + } + MPMFSMState::Setup { + mac_addr, + local_link_id: local_link_id_cached, + substate, + local_aid, + .. + } => match substate { + MPMFSMSubState::OpnSnt => { + self.common_resources.lock_peer_list(|mut peer_list| { + let _ = peer_list.modify_or_add_peer( + &addr, + |peer| { + peer.mpm_state = MPMFSMState::Setup { + mac_addr: mac_addr, + local_link_id: mpm.peer_link_id.unwrap_or(local_link_id_cached), + peer_link_id: mpm.local_link_id, + substate: MPMFSMSubState::CnfRcvd, + local_aid: local_aid, + remote_aid: Some(remote_aid), + }; + }, + || None, + ); + }); + } + MPMFSMSubState::OpnRcvd => { + self.common_resources.lock_peer_list(|mut peer_list| { + let _ = peer_list.modify_or_add_peer( + &addr, + |peer| { + peer.mpm_state = MPMFSMState::Estab { + mac_addr: mac_addr, + local_link_id: mpm.peer_link_id.unwrap_or(local_link_id_cached), + peer_link_id: mpm.local_link_id, + local_aid: local_aid, + remote_aid: remote_aid, + }; + }, + || None, + ); + }); + } + _ => { + // Ignored + } + }, + } + + None + } + + pub async fn run(&mut self, our_address: &MACAddress) -> ! { + let mut beacon_ticker = Ticker::every(Duration::from_micros( + BEACON_INTERVAL_TU * TU.as_micros() as u64, + )); + + loop { + match select3( + self.interface_control.wait_for_off_channel_request(), + self.rx_router_endpoint.receive(), + beacon_ticker.next(), + ) + .await + { + Either3::First(_off_channel_request) => {} + Either3::Second(buffer) => { + let _ = match_frames! { + buffer.mpdu_buffer(), + _beacon_frame = BeaconFrame => { + // TODO process beacon frames + } + mesh_peering_open_frame = MeshPeeringOpenFrame => { + self.process_mesh_peering_open(&mesh_peering_open_frame, our_address).await; + } + mesh_peering_confirm_frame = MeshPeeringConfirmFrame => { + self.process_mesh_peering_confirm(&mesh_peering_confirm_frame, our_address).await; + } + }; + } + Either3::Third(_) => { + // Time to send a beacon frame + self.send_beacon_frame(our_address).await; + } + } + } + } +} diff --git a/foa_mesh/src/runner/mod.rs b/foa_mesh/src/runner/mod.rs new file mode 100644 index 0000000..102d44d --- /dev/null +++ b/foa_mesh/src/runner/mod.rs @@ -0,0 +1,91 @@ +use defmt_or_log::debug; +use embassy_futures::select::select3; +use embassy_net_driver_channel::Runner as NetRunner; + +use foa::{LMacInterfaceControl, RxQueueReceiver}; +use management::MeshManagementRunner; +use rand_core::RngCore; +use rx::MeshRxRunner; +use tx::MeshTxRunner; + +use crate::{ + MTU, + rx_router::{MeshRxRouterEndpoint, MeshRxRouterInput}, + state::{CommonResources, MeshState}, +}; + +mod management; +mod rx; +mod tx; + +pub struct MeshRunner<'foa, 'vif, Rng: RngCore + Copy> { + pub(crate) management_runner: MeshManagementRunner<'foa, 'vif, Rng>, + pub(crate) tx_runner: MeshTxRunner<'foa, 'vif>, + pub(crate) rx_runner: MeshRxRunner<'foa, 'vif>, + pub(crate) common_resources: &'vif CommonResources, +} +impl<'foa, 'vif, Rng: RngCore + Copy> MeshRunner<'foa, 'vif, Rng> { + pub(crate) fn new( + net_runner: NetRunner<'vif, MTU>, + background_endpoint: MeshRxRouterEndpoint<'foa, 'vif>, + rx_router_input: MeshRxRouterInput<'foa, 'vif>, + interface_rx_queue_receiver: &'vif RxQueueReceiver<'foa>, + interface_control: &'vif LMacInterfaceControl<'foa>, + common_resources: &'vif CommonResources, + rng: Rng, + ) -> Self { + let (net_state_runner, net_rx_runner, net_tx_runner) = net_runner.split(); + Self { + management_runner: MeshManagementRunner { + interface_control, + rx_router_endpoint: background_endpoint, + net_state_runner, + common_resources, + rng: rng.clone(), + }, + tx_runner: MeshTxRunner { + interface_control, + net_tx_runner, + common_resources, + }, + rx_runner: MeshRxRunner { + rx_router_input, + interface_rx_queue_receiver, + net_rx_runner, + common_resources, + }, + common_resources, + } + } + pub async fn run(&mut self) -> ! { + debug!("Mesh runner active."); + let mut state = MeshState::Inactive; + + let our_address; + let channel; + let mesh_id; + loop { + let MeshState::Active { + our_address: our_address_, + channel: channel_, + mesh_id: mesh_id_, + } = state + else { + state = self.common_resources.state_signal.wait().await; + continue; + }; + our_address = our_address_; + channel = channel_; + mesh_id = mesh_id_; + break; + } + + let _ = select3( + self.management_runner.run(&our_address), + self.tx_runner.run(), + self.rx_runner.run(), + ) + .await; + unreachable!("a mesh task returned!") + } +} diff --git a/foa_mesh/src/runner/rx.rs b/foa_mesh/src/runner/rx.rs new file mode 100644 index 0000000..71a0c52 --- /dev/null +++ b/foa_mesh/src/runner/rx.rs @@ -0,0 +1,20 @@ +use embassy_net_driver_channel::RxRunner as NetRxRunner; +use foa::RxQueueReceiver; + +use crate::{MTU, rx_router::MeshRxRouterInput, state::CommonResources}; + +pub struct MeshRxRunner<'foa, 'vif> { + pub(crate) rx_router_input: MeshRxRouterInput<'foa, 'vif>, + pub(crate) interface_rx_queue_receiver: &'vif RxQueueReceiver<'foa>, + pub(crate) net_rx_runner: NetRxRunner<'vif, MTU>, + pub(crate) common_resources: &'vif CommonResources, +} +impl MeshRxRunner<'_, '_> { + pub async fn run(&mut self) -> ! { + loop { + let received_frame = self.interface_rx_queue_receiver.receive().await; + // Do some actual data RX handling here. + let _ = self.rx_router_input.route_frame(received_frame); + } + } +} diff --git a/foa_mesh/src/runner/tx.rs b/foa_mesh/src/runner/tx.rs new file mode 100644 index 0000000..7d63d13 --- /dev/null +++ b/foa_mesh/src/runner/tx.rs @@ -0,0 +1,21 @@ +use crate::{MTU, state::CommonResources}; +use defmt_or_log::debug; +use embassy_net_driver_channel::TxRunner as NetTxRunner; +use foa::LMacInterfaceControl; + +pub struct MeshTxRunner<'foa, 'vif> { + pub(crate) interface_control: &'vif LMacInterfaceControl<'foa>, + pub(crate) net_tx_runner: NetTxRunner<'vif, MTU>, + pub(crate) common_resources: &'vif CommonResources, +} +impl MeshTxRunner<'_, '_> { + pub async fn run(&mut self) -> ! { + loop { + debug!("Before TX"); + let _tx_buf = self.net_tx_runner.tx_buf().await; + debug!("after tx"); + self.net_tx_runner.tx_done(); + debug!("after tx done"); + } + } +} diff --git a/foa_mesh/src/rx_router.rs b/foa_mesh/src/rx_router.rs new file mode 100644 index 0000000..2438259 --- /dev/null +++ b/foa_mesh/src/rx_router.rs @@ -0,0 +1,30 @@ +use crate::RX_QUEUE_DEPTH; +use foa::util::rx_router::{ + HasScanOperation, RxRouter, RxRouterEndpoint, RxRouterInput, RxRouterOperation, +}; +use ieee80211::GenericFrame; +use ieee80211::common::{FrameType, ManagementFrameSubtype}; + +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum MeshRxRouterOperation { + Scanning, +} +impl HasScanOperation for MeshRxRouterOperation { + const SCAN_OPERATION: Self = Self::Scanning; +} +impl RxRouterOperation for MeshRxRouterOperation { + fn frame_relevant_for_operation(&self, generic_frame: &GenericFrame<'_>) -> bool { + let frame_type = generic_frame.frame_control_field().frame_type(); + match self { + MeshRxRouterOperation::Scanning => matches!( + frame_type, + FrameType::Management(ManagementFrameSubtype::Beacon) + ), + } + } +} +pub type MeshRxRouter<'foa> = RxRouter<'foa, RX_QUEUE_DEPTH, MeshRxRouterOperation>; +pub type MeshRxRouterEndpoint<'foa, 'router> = + RxRouterEndpoint<'foa, 'router, MeshRxRouterOperation>; +pub type MeshRxRouterInput<'foa, 'router> = RxRouterInput<'foa, 'router, MeshRxRouterOperation>; diff --git a/foa_mesh/src/state.rs b/foa_mesh/src/state.rs new file mode 100644 index 0000000..dd7a925 --- /dev/null +++ b/foa_mesh/src/state.rs @@ -0,0 +1,161 @@ +use core::cell::{Cell, RefCell, RefMut}; + +use defmt_or_log::derive_format_or_debug; +use embassy_net_driver_channel::State as NetState; +use embassy_sync::blocking_mutex::NoopMutex; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, signal::Signal}; +use foa::util::rx_router::RxRouter; +use ieee80211::common::AssociationID; +use ieee80211::mac_parser::MACAddress; + +use crate::NET_TX_BUFFERS; +use crate::peer_state::{MeshPeerList, StaticMeshPeerList}; +use crate::rx_router::MeshRxRouter; +use crate::{MTU, NET_RX_BUFFERS}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +/// State of the mesh interface pushed to the runner. +pub(crate) enum MeshState { + Active { + our_address: MACAddress, + channel: u8, + mesh_id: heapless::String<32>, + }, + Inactive, +} + +/// States of the Mesh Peering Management (MPM) Finite State Machine +// See also Figure 14-2 'Finite state machine of the MPM protocol' in the 2020 edition of the 802.11 standard +// I recommend looking at the figure, it's a lot clearer than the text +#[derive(Clone, PartialEq, Eq, Hash, Copy)] +#[derive_format_or_debug] +pub enum MPMFSMSubState { + // Open sent, but nothing received ye + OpnSnt, + // Received Confirm, but no Open yet => so also no Confirm sent yet + CnfRcvd, + // Received Open, but not Confirm => we also sent Confirm on receiving the Open + OpnRcvd, +} + +#[derive(Clone, PartialEq, Eq, Hash, Default, Copy)] +#[derive_format_or_debug] +pub enum MPMFSMState { + #[default] + Idle, + Setup { + mac_addr: MACAddress, + local_link_id: u16, + peer_link_id: u16, + substate: MPMFSMSubState, + local_aid: AssociationID, + remote_aid: Option, + }, + // Received Open and Confirm, also sent Open and Confirm + Estab { + mac_addr: MACAddress, + local_link_id: u16, + peer_link_id: u16, + local_aid: AssociationID, + remote_aid: AssociationID, + }, + // Closing the peering instance + Holding { + mac_addr: MACAddress, + local_link_id: u16, + peer_link_id: Option, + }, +} + +impl MPMFSMState { + pub fn get_mac_address(self) -> Option { + match self { + Self::Idle => None, + Self::Estab { mac_addr, .. } => Some(mac_addr), + Self::Setup { mac_addr, .. } => Some(mac_addr), + Self::Holding { mac_addr, .. } => Some(mac_addr), + } + } +} + +/// Parameters that may change over the course of a session. +pub(crate) struct DynamicSessionParameters { + pub(crate) is_mesh_gate: Cell, +} + +impl DynamicSessionParameters { + pub fn new() -> Self { + Self { + is_mesh_gate: Cell::new(false), + } + } +} +pub(crate) type MeshPeerListImplementation = StaticMeshPeerList; + +pub struct CommonResources { + // State signaling + /// Indicates the current status of the interface to the runner. + pub(crate) state_signal: Signal, + + // State + /// Dynamically changing parameters. + pub(crate) dynamic_session_parameters: DynamicSessionParameters, + /// Stores all currently known peers. + pub(crate) peer_list: NoopMutex>, + + association_id_ctr: Cell, +} + +impl CommonResources { + pub fn new() -> Self { + Self { + state_signal: Signal::new(), + dynamic_session_parameters: DynamicSessionParameters::new(), + peer_list: NoopMutex::new(RefCell::new(MeshPeerListImplementation::UNINIT)), + association_id_ctr: Cell::new(1), + } + } + /// Initialize the parameters for a new session. + pub fn initialize_session_parameters(&self, _channel: u8, _address: MACAddress) { + // TODO use _channel and _address + } + /// Acquire mutable access to the peer list in the closure. + pub fn lock_peer_list( + &self, + f: impl FnOnce(RefMut<'_, MeshPeerListImplementation>) -> O, + ) -> O { + self.peer_list.lock(|peer_list| (f)(peer_list.borrow_mut())) + } + + pub fn new_association_id(&self) -> AssociationID { + let counter = self.association_id_ctr.update(|counter| { + (counter + 1 % AssociationID::MAX_AID) + .clamp(AssociationID::MIN_AID, AssociationID::MAX_AID) + }); + + AssociationID::new_checked(counter).unwrap() + } +} + +/// Resources for the Mesh interface. +pub struct MeshResources<'foa> { + /// Resources common to all components. + pub common_resources: CommonResources, + /// Resources for embassy_net_driver_channel. + pub net_state: NetState, + pub rx_router: MeshRxRouter<'foa>, +} +impl MeshResources<'_> { + pub fn new() -> Self { + Self { + rx_router: RxRouter::new(), + common_resources: CommonResources::new(), + net_state: NetState::new(), + } + } +} +impl Default for MeshResources<'_> { + fn default() -> Self { + Self::new() + } +} diff --git a/foa_sta/Cargo.lock b/foa_sta/Cargo.lock index f129825..1245409 100644 --- a/foa_sta/Cargo.lock +++ b/foa_sta/Cargo.lock @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "bitfield-struct" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be5a46ba01b60005ae2c51a36a29cfe134bcacae2dd5cedcd4615fbaad1494b" +checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" dependencies = [ "proc-macro2", "quote", @@ -627,14 +627,14 @@ dependencies = [ [[package]] name = "esp-wifi-hal" -version = "0.1.0-alpha.3" +version = "0.1.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ac4c1adf315e841418396ddb10e88ca67dbcfaed304417c31f8d93a5ea5fce" +checksum = "3df04720437afe1141e9cf264ead1ad452185e46a7cfb3a707677011dfc81b5e" dependencies = [ - "bitfield-struct 0.10.1", + "bitfield-struct 0.11.0", "cfg-if", "critical-section", - "defmt 0.3.100", + "defmt 1.0.1", "defmt-or-log", "embassy-futures", "embassy-sync", @@ -848,9 +848,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "ieee80211" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c870f3ba5d4f36ffdfd3df5e870cada060cf8d926584e3c3ce274f8ced22109" +checksum = "06530bda0af5484a90b9a322dc7bb467a8b1f0d08d3bc50c510ba876fc516b48" dependencies = [ "bitfield-struct 0.8.0", "const_soft_float", diff --git a/foa_sta/Cargo.toml b/foa_sta/Cargo.toml index aed485c..bfe8af1 100644 --- a/foa_sta/Cargo.toml +++ b/foa_sta/Cargo.toml @@ -15,7 +15,7 @@ esp-config = "0.3.1" ethernet = { version = "0.1.5", default-features = false } foa = { path = "../foa/" } heapless = "0.8.0" -ieee80211 = { version = "0.5.4", default-features = false } +ieee80211 = { version = "0.5.5", default-features = false } llc-rs = "0.1.0" rand_core = "0.6.0"