From 4e769ad2b5bf4de7f0d1efc0c1738032eb869c8f Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Sun, 1 Jun 2025 16:12:50 +1000 Subject: [PATCH 01/86] Add SSH config storage support --- .cargo/config.toml | 1 + .pre-commit-config.yaml | 0 Cargo.toml | 29 +++-- src/config.rs | 282 ++++++++++++++++++++++++++++++++++++++++ src/espressif/hash.rs | 28 ++++ src/espressif/mod.rs | 3 + src/lib.rs | 2 + src/settings.rs | 2 + src/storage.rs | 21 +++ 9 files changed, 360 insertions(+), 8 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 src/config.rs create mode 100644 src/espressif/hash.rs create mode 100644 src/storage.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 4631d84..136fa3d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -37,6 +37,7 @@ rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32s3"'] [env] ESP_LOG="INFO" +[build] target = "riscv32imac-unknown-none-elf" [unstable] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e69de29 diff --git a/Cargo.toml b/Cargo.toml index 3efeb35..00615f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,3 @@ -# TODO: Potentially switch to cargo_xtask for easy re-targetting, i.e: -# https://github.com/card-io-ecg/card-io-fw/blob/main/Cargo.toml - [package] name = "ssh-stamp" version = "0.1.0" @@ -12,7 +9,7 @@ license = "MIT OR Apache-2.0" cfg-if = "1.0.0" ed25519-dalek = { version = "2", default-features = false } embassy-executor = { version = "0.7"} -embassy-net = { version = "0.6", features = ["tcp", "udp", "dhcpv4", "medium-ethernet"] } +embassy-net = { version = "0.7", features = ["tcp", "udp", "dhcpv4", "medium-ethernet"] } smoltcp = { version = "0.12", default-features = false, features = ["medium-ethernet", "socket-raw"]} embassy-time = { version = "0.4" } embedded-io-async = "0.6.0" @@ -33,12 +30,21 @@ ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } sunset = { version = "0.2.0", git="https://github.com/mkj/sunset", default-features = false, features = ["openssh-key", "embedded-io"]} sunset-embassy = { version = "0.2.0", git = "https://github.com/mkj/sunset", default-features = false} getrandom = { version = "0.2.10", features = ["custom"] } -embassy-sync = "0.6" +embassy-sync = "0.7" heapless = "0.8" embassy-futures = "0.1" -edge-dhcp = "0.5" +edge-dhcp = "0.6" edge-nal = "0.5" -edge-nal-embassy = "0.5" +edge-nal-embassy = "0.6" +sequential-storage = { version = "4.0.1", features = ["heapless"] } +esp-storage = { version = "0.5.0", features = ["storage"]} +embedded-storage = "0.3.1" +embassy-embedded-hal = "0.3.0" +bcrypt = { version = "0.17.0", default-features = false } +subtle = { version = "2.6.1", default-features = false } +hmac = { version = "0.12.1", default-features = false } +sha2 = { version = "0.10.9", default-features = false } +digest = { version = "0.10.7", default-features = false, features = ["rand_core", "subtle"] } [profile.dev] # Rust debug is too slow. @@ -60,7 +66,7 @@ opt-level = "s" # Optimize for size. [features] - +default = ["esp32c6"] # MCU options esp32 = [ "esp-hal/esp32", @@ -68,6 +74,7 @@ esp32 = [ "esp-wifi/esp32", "esp-hal-embassy/esp32", "esp-println/esp32", + "esp-storage/esp32", "embassy-executor/task-arena-size-40960", ] esp32c2 = [ @@ -76,6 +83,7 @@ esp32c2 = [ "esp-wifi/esp32c2", "esp-hal-embassy/esp32c2", "esp-println/esp32c2", + "esp-storage/esp32c2", "embassy-executor/task-arena-size-40960", ] esp32c3 = [ @@ -84,6 +92,7 @@ esp32c3 = [ "esp-wifi/esp32c3", "esp-hal-embassy/esp32c3", "esp-println/esp32c3", + "esp-storage/esp32c3", "embassy-executor/task-arena-size-40960", ] #esp32c5 = [ @@ -92,6 +101,7 @@ esp32c3 = [ # "esp-wifi/esp32c5", # "esp-hal-embassy/esp32c5", # "esp-println/esp32c5", +# "esp-storage/esp32c5", # "embassy-executor/task-arena-size-40960", #] esp32c6 = [ @@ -100,6 +110,7 @@ esp32c6 = [ "esp-wifi/esp32c6", "esp-hal-embassy/esp32c6", "esp-println/esp32c6", + "esp-storage/esp32c6", "embassy-executor/task-arena-size-40960", ] esp32s2 = [ @@ -108,6 +119,7 @@ esp32s2 = [ "esp-wifi/esp32s2", "esp-hal-embassy/esp32s2", "esp-println/esp32s2", + "esp-storage/esp32s2", "embassy-executor/task-arena-size-32768", ] esp32s3 = [ @@ -116,5 +128,6 @@ esp32s3 = [ "esp-wifi/esp32s3", "esp-hal-embassy/esp32s3", "esp-println/esp32s3", + "esp-storage/esp32s3", "embassy-executor/task-arena-size-40960", ] diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..75eb49f --- /dev/null +++ b/src/config.rs @@ -0,0 +1,282 @@ +use core::net::Ipv4Addr; +use embassy_net::{Ipv4Cidr, StaticConfigV4}; +use heapless::{String, Vec}; + +use esp_println::println; + +use bcrypt; +use hmac::{Hmac, Mac}; +use sha2::Sha256; +use subtle::ConstantTimeEq; + +use sunset::Result; +use sunset::{ + packets::Ed25519PubKey, + sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, + SignKey, +}; + +use crate::settings::KEY_SLOTS; + +// https://github.com/esp-rs/esp-storage/issues/39#issuecomment-1980991446 +// TODO: Review config.rs from sunset, has nice abstractions for this already: + +// https://github.com/mkj/sunset/blob/main/embassy/demos/common/src/config.rs +// https://github.com/mkj/sunset/blob/main/embassy/demos/picow/src/flashconfig.rs + +// https://github.com/Erik1000/car/blob/main/starter-firmware/src/ble.rs + +#[derive(Debug, Clone, PartialEq)] +pub struct SSHConfig { + pub hostkey: SignKey, + + // We just want passwords and/or keys on the SSH side, not UART side + // TODO: Ask @mkj the purpose for this... + // i.e:automatically filling a password when a device (cisco router) asks for it on a prompt? + // + // /// login password for serial + // pub console_pw: Option, + // pub console_keys: [Option; KEY_SLOTS], + // pub console_noauth: bool, + pub password_authentication: bool, + /// for ssh admin + pub admin_pw: Option, + pub admin_keys: [Option; KEY_SLOTS], + + /// WiFi SSID + pub wifi_ssid: String<32>, + /// WPA2 passphrase. None is Open network. + pub wifi_pw: Option>, + + /// For wl5500. cyw43 uses its own internal + /// TODO: Populate this field hardware? + pub mac: [u8; 6], + + /// `None` for DHCP + pub ip4_static: Option, +} + +// a private encoding specific to demo config, not SSH defined. +fn enc_signkey(k: &SignKey, s: &mut dyn SSHSink) -> WireResult<()> { + // need to add a variant field if we support more key types. + match k { + SignKey::Ed25519(k) => k.to_bytes().enc(s), + _ => Err(WireError::UnknownVariant), + } +} + +fn dec_signkey<'de, S>(s: &mut S) -> WireResult +where + S: SSHSource<'de>, +{ + let k: ed25519_dalek::SecretKey = SSHDecode::dec(s)?; + let k = ed25519_dalek::SigningKey::from_bytes(&k); + Ok(SignKey::Ed25519(k)) +} + +// encode Option as a bool then maybe a value +fn enc_option(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { + v.is_some().enc(s)?; + v.enc(s) +} + +fn dec_option<'de, S, T: SSHDecode<'de>>(s: &mut S) -> WireResult> +where + S: SSHSource<'de>, +{ + bool::dec(s)?.then(|| SSHDecode::dec(s)).transpose() +} + +fn enc_ip4config(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { + v.is_some().enc(s)?; + if let Some(v) = v { + v.address.address().to_bits().enc(s)?; + v.address.prefix_len().enc(s)?; + // to u32 + let gw = v.gateway.map(|a| a.to_bits()); + enc_option(&gw, s)?; + } + Ok(()) +} + +fn dec_ip4config<'de, S>(s: &mut S) -> WireResult> +where + S: SSHSource<'de>, +{ + let opt = bool::dec(s)?; + opt.then(|| { + let ad: u32 = SSHDecode::dec(s)?; + let ad = Ipv4Addr::from_bits(ad); + let prefix = SSHDecode::dec(s)?; + if prefix > 32 { + // emabassy panics, so test it here + return Err(WireError::PacketWrong); + } + let gw: Option = dec_option(s)?; + let gateway = gw.map(|gw| Ipv4Addr::from_bits(gw)); + Ok(StaticConfigV4 { + address: Ipv4Cidr::new(ad, prefix), + gateway, + dns_servers: Vec::new(), + }) + }) + .transpose() +} + +impl SSHEncode for SSHConfig { + fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { + println!("enc si"); + enc_signkey(&self.hostkey, s)?; + + // enc_option(&self.console_pw, s)?; + + // for k in self.console_keys.iter() { + // enc_option(k, s)?; + // } + + // self.console_noauth.enc(s)?; + + enc_option(&self.admin_pw, s)?; + + for k in self.admin_keys.iter() { + enc_option(k, s)?; + } + + self.wifi_ssid.as_str().enc(s)?; + enc_option(&self.wifi_pw, s)?; + + self.mac.enc(s)?; + + enc_ip4config(&self.ip4_static, s)?; + + Ok(()) + } +} + +impl<'de> SSHDecode<'de> for SSHConfig { + fn dec(s: &mut S) -> WireResult + where + S: SSHSource<'de>, + { + let hostkey = dec_signkey(s)?; + + // let console_pw = dec_option(s)?; + + // let mut console_keys = [None, None, None]; + // for k in console_keys.iter_mut() { + // *k = dec_option(s)?; + // } + + let admin_pw = dec_option(s)?; + + let mut admin_keys = [None, None, None]; + for k in admin_keys.iter_mut() { + *k = dec_option(s)?; + } + + let wifi_ssid = SSHDecode::dec(s)?; + let wifi_pw = dec_option(s)?; + + let mac = SSHDecode::dec(s)?; + + let ip4_static = dec_ip4config(s)?; + + // Decode password_authentication (missing in original code) + let password_authentication = SSHDecode::dec(s)?; + + Ok(Self { + hostkey, + // console_pw, + // console_keys, + // console_noauth, + password_authentication, + admin_pw, + admin_keys, + wifi_ssid, + wifi_pw, + mac, + ip4_static, + }) + } +} + +/// Stores a bcrypt password hash. +/// +/// We use bcrypt because it seems the best password hashing option where +/// memory hardness isn't possible (the rp2040 is smaller than CPU or GPU memory). +/// +/// The cost is currently set to 6, taking ~500ms on a 125mhz rp2040. +/// Time converges to roughly 8.6ms * 2**cost +/// +/// Passwords are pre-hashed to avoid bcrypt's 72 byte limit. +/// rust-bcrypt allows nulls in passwords. +/// We use an hmac rather than plain hash to avoid password shucking +/// (an attacker bcrypts known hashes from some other breach, then +/// brute forces the weaker hash for any that match). +//#[derive(Clone, SSHEncode, SSHDecode, PartialEq)] +#[derive(Clone, PartialEq)] +pub struct PwHash { + salt: [u8; 16], + hash: [u8; 24], + cost: u8, +} + +impl PwHash { + const COST: u8 = 6; + /// `pw` must not be empty. + pub fn new(pw: &str) -> Result { + if pw.is_empty() { + return sunset::error::BadUsage.fail(); + } + + let mut salt = [0u8; 16]; + sunset::random::fill_random(&mut salt)?; + let prehash = Self::prehash(pw, &salt); + let cost = Self::COST; + let hash = bcrypt::bcrypt(cost as u32, salt, &prehash); + Ok(Self { salt, hash, cost }) + } + + pub fn check(&self, pw: &str) -> bool { + if pw.is_empty() { + return false; + } + let prehash = Self::prehash(pw, &self.salt); + let check_hash = bcrypt::bcrypt(self.cost as u32, self.salt.clone(), &prehash); + check_hash.ct_eq(&self.hash).into() + } + + fn prehash(pw: &str, salt: &[u8]) -> [u8; 32] { + // OK unwrap: can't fail, accepts any length + // TODO: Generalise, not only Espressif esp_hal + let mut prehash = Hmac::::new_from_slice(&salt).unwrap(); + prehash.update(pw.as_bytes()); + prehash.finalize().into_bytes().into() + } +} + +impl core::fmt::Debug for PwHash { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("PwHash").finish_non_exhaustive() + } +} + +impl SSHEncode for PwHash { + fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { + self.salt.enc(s)?; + self.hash.enc(s)?; + self.cost.enc(s) + } +} + +impl<'de> SSHDecode<'de> for PwHash { + fn dec(s: &mut S) -> WireResult + where + S: SSHSource<'de>, + { + let salt = <[u8; 16]>::dec(s)?; + let hash = <[u8; 24]>::dec(s)?; + let cost = u8::dec(s)?; + Ok(PwHash { salt, hash, cost }) + } +} diff --git a/src/espressif/hash.rs b/src/espressif/hash.rs new file mode 100644 index 0000000..8672bdc --- /dev/null +++ b/src/espressif/hash.rs @@ -0,0 +1,28 @@ +use esp_hal::hmac::Hmac; +use hmac::Hmac; +use sha2::Sha256; + +pub trait EspressifHmac { + fn new_from_slice(key: &[u8]) -> Result + where + Self: Sized; + fn update(&mut self, data: &[u8]); + fn finalize(self) -> [u8; 32]; +} + +impl EspressifHmac for Hmac { + fn new_from_slice(key: &[u8]) -> Result { + Hmac::new_from_slice(key).map_err(|_| ()) + } + + fn update(&mut self, data: &[u8]) { + self.update(data); + } + + fn finalize(self) -> [u8; 32] { + let result = self.finalize(); + let mut arr = [0u8; 32]; + arr.copy_from_slice(&result.into_bytes()); + arr + } +} \ No newline at end of file diff --git a/src/espressif/mod.rs b/src/espressif/mod.rs index 88fc177..a86e6cc 100644 --- a/src/espressif/mod.rs +++ b/src/espressif/mod.rs @@ -1,3 +1,6 @@ pub mod buffered_uart; pub mod net; pub mod rng; +// TODO: Specialise for Espressif, tricky since it seems to require burning eFuses?: +// https://github.com/esp-rs/esp-hal/blob/main/examples/src/bin/hmac.rs +//pub mod hash; diff --git a/src/lib.rs b/src/lib.rs index f32497f..02f9e5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,10 @@ #![no_main] #![forbid(unsafe_code)] +pub mod config; pub mod espressif; pub mod keys; pub mod serial; pub mod serve; pub mod settings; +pub mod storage; diff --git a/src/settings.rs b/src/settings.rs index 098e322..cfd41c5 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -4,6 +4,8 @@ //pub(crate) const MTU: usize = 1536; //pub(crate) const PORT: u16 = 22; pub(crate) const _SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; +pub const KEY_SLOTS: usize = 3; // TODO: Document whether this a "reasonable default"? Justify why? +pub const PASSWORD_AUTHENTICATION: bool = true; // UART settings //pub(crate) const BAUD_RATE: u32 = 115200; diff --git a/src/storage.rs b/src/storage.rs new file mode 100644 index 0000000..5b7f096 --- /dev/null +++ b/src/storage.rs @@ -0,0 +1,21 @@ +use esp_println::println; +use esp_storage::FlashStorage; +//use sequential_storage::map::store_item; +use embassy_embedded_hal::adapter::BlockingAsync; +use embedded_storage::ReadStorage; + +use crate::config::SSHConfig; + +pub(crate) async fn _set_value(ssh_config: SSHConfig) -> Result<(), sunset::Error> { + let mut flash = FlashStorage::new(); + println!("Flash size = {}", flash.capacity()); + + let mut flash = BlockingAsync::new(flash); + + // TODO: Define suitable ranges and buffer sizes compatible with all Espressif targets + //store_item(&mut flash, flash_range, cache, data_buffer, key, item); + + todo!(); + + Ok(()) +} From c01b70dc811fc409419437677e680df258e40f0e Mon Sep 17 00:00:00 2001 From: brainstorm Date: Thu, 12 Jun 2025 20:34:03 +1000 Subject: [PATCH 02/86] Update references to sunset config system(s) --- Cargo.lock | 98 +++++++++++++++++++++++++++++++++++++++++++-------- src/config.rs | 4 +-- 2 files changed, 85 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78f5e92..e9f72bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.7.3" @@ -56,6 +62,17 @@ dependencies = [ "serde", ] +[[package]] +name = "bcrypt" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f" +dependencies = [ + "base64", + "blowfish", + "subtle", +] + [[package]] name = "bitfield" version = "0.18.1" @@ -97,6 +114,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + [[package]] name = "bytemuck" version = "1.22.0" @@ -168,6 +195,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] @@ -298,16 +326,15 @@ dependencies = [ [[package]] name = "edge-dhcp" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c14e92b3e72e93428f87e64f14e2b868b8adcd2e38e0ebf72baa158fe0f930" +checksum = "e2ccd3a181a33c710e07c3f04623d7a11e9969b1e44a7276ead7873b049720cb" dependencies = [ "edge-nal", "edge-raw", "embassy-futures", "embassy-time", "heapless", - "log", "num_enum", "rand_core", ] @@ -324,9 +351,9 @@ dependencies = [ [[package]] name = "edge-nal-embassy" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aacee123b1a4df16dfba7b46ea0912e9d99bc099fe81bd2a564013510471e41" +checksum = "252f89adf4f0016631977bec3ba50d768263a3a9fa9f023b4087088a619568ce" dependencies = [ "edge-nal", "embassy-futures", @@ -337,12 +364,9 @@ dependencies = [ [[package]] name = "edge-raw" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d80a91a0c4abb68a9bc4c0df607135b5d9b6885b49acfe2d2c881e3f9623324" -dependencies = [ - "log", -] +checksum = "6207c84e9bc8df8ef3c155196df290f2a51f010bd60c2e78366e51979988bdb5" [[package]] name = "embassy-embedded-hal" @@ -392,9 +416,9 @@ checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" [[package]] name = "embassy-net" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed041cc19a603d657124fddefdcbe5ef8bd60e77d972793ebb57de93394f5949" +checksum = "940c4b9fe5c1375b09a0c6722c0100d6b2ed46a717a34f632f26e8d7327c4383" dependencies = [ "document-features", "embassy-net-driver", @@ -440,6 +464,20 @@ dependencies = [ "heapless", ] +[[package]] +name = "embassy-sync" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef1a8a1ea892f9b656de0295532ac5d8067e9830d49ec75076291fd6066b136" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-sink", + "futures-util", + "heapless", +] + [[package]] name = "embassy-time" version = "0.4.0" @@ -786,6 +824,17 @@ dependencies = [ "riscv-rt-macros", ] +[[package]] +name = "esp-storage" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e98b5069bd1482fb85f9236f8ea6cd1b5b6b5a1c3c3fb8c938c97ccfbbb051b" +dependencies = [ + "critical-section", + "embedded-storage", + "esp-build", +] + [[package]] name = "esp-synopsys-usb-otg" version = "0.4.2" @@ -1403,6 +1452,16 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +[[package]] +name = "sequential-storage" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8e933f534642c25b7341338c10e2250187c8cd198c3957ef6894265eefda86" +dependencies = [ + "embedded-storage-async", + "heapless", +] + [[package]] name = "serde" version = "1.0.219" @@ -1434,9 +1493,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -1524,30 +1583,39 @@ dependencies = [ name = "ssh-stamp" version = "0.1.0" dependencies = [ + "bcrypt", "cfg-if", + "digest", "ed25519-dalek", "edge-dhcp", "edge-nal", "edge-nal-embassy", + "embassy-embedded-hal", "embassy-executor", "embassy-futures", "embassy-net", - "embassy-sync 0.6.2", + "embassy-sync 0.7.0", "embassy-time", "embedded-io-async", + "embedded-storage", "esp-alloc", "esp-backtrace", "esp-hal", "esp-hal-embassy", "esp-println", + "esp-storage", "esp-wifi", "getrandom", "heapless", "hex", + "hmac", "log", + "sequential-storage", + "sha2", "smoltcp", "ssh-key", "static_cell", + "subtle", "sunset", "sunset-embassy", ] diff --git a/src/config.rs b/src/config.rs index 75eb49f..e87dbdb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -21,8 +21,8 @@ use crate::settings::KEY_SLOTS; // https://github.com/esp-rs/esp-storage/issues/39#issuecomment-1980991446 // TODO: Review config.rs from sunset, has nice abstractions for this already: -// https://github.com/mkj/sunset/blob/main/embassy/demos/common/src/config.rs -// https://github.com/mkj/sunset/blob/main/embassy/demos/picow/src/flashconfig.rs +// https://github.com/mkj/sunset/blob/main/demo/common/src/config.rs +// https://github.com/mkj/sunset/blob/main/demo/picow/src/flashconfig.rs // https://github.com/Erik1000/car/blob/main/starter-firmware/src/ble.rs From dd0773124873171f6d22ae4129e72343cff10b67 Mon Sep 17 00:00:00 2001 From: brainstorm Date: Mon, 16 Jun 2025 15:38:01 +1000 Subject: [PATCH 03/86] [ci skip] Continue adapting config/storage/flashstorage interfaces from sunset for ssh-stamp... --- Cargo.lock | 1 + Cargo.toml | 1 + src/config.rs | 91 +++++++++++++++++++++++------------- src/settings.rs | 2 +- src/storage.rs | 122 ++++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 174 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e9f72bb..b65580d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1598,6 +1598,7 @@ dependencies = [ "embassy-time", "embedded-io-async", "embedded-storage", + "embedded-storage-async", "esp-alloc", "esp-backtrace", "esp-hal", diff --git a/Cargo.toml b/Cargo.toml index 00615f6..fee61f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ subtle = { version = "2.6.1", default-features = false } hmac = { version = "0.12.1", default-features = false } sha2 = { version = "0.10.9", default-features = false } digest = { version = "0.10.7", default-features = false, features = ["rand_core", "subtle"] } +embedded-storage-async = "0.4.1" [profile.dev] # Rust debug is too slow. diff --git a/src/config.rs b/src/config.rs index e87dbdb..bcdec8a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,37 +9,21 @@ use hmac::{Hmac, Mac}; use sha2::Sha256; use subtle::ConstantTimeEq; -use sunset::Result; +use sunset::error::TrapBug; +use sunset::{KeyType, Result}; use sunset::{ packets::Ed25519PubKey, sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, SignKey, }; -use crate::settings::KEY_SLOTS; - -// https://github.com/esp-rs/esp-storage/issues/39#issuecomment-1980991446 -// TODO: Review config.rs from sunset, has nice abstractions for this already: - -// https://github.com/mkj/sunset/blob/main/demo/common/src/config.rs -// https://github.com/mkj/sunset/blob/main/demo/picow/src/flashconfig.rs - -// https://github.com/Erik1000/car/blob/main/starter-firmware/src/ble.rs +use crate::settings::{KEY_SLOTS, SSH_SERVER_ID}; #[derive(Debug, Clone, PartialEq)] pub struct SSHConfig { pub hostkey: SignKey, - // We just want passwords and/or keys on the SSH side, not UART side - // TODO: Ask @mkj the purpose for this... - // i.e:automatically filling a password when a device (cisco router) asks for it on a prompt? - // - // /// login password for serial - // pub console_pw: Option, - // pub console_keys: [Option; KEY_SLOTS], - // pub console_noauth: bool, pub password_authentication: bool, - /// for ssh admin pub admin_pw: Option, pub admin_keys: [Option; KEY_SLOTS], @@ -48,14 +32,67 @@ pub struct SSHConfig { /// WPA2 passphrase. None is Open network. pub wifi_pw: Option>, - /// For wl5500. cyw43 uses its own internal - /// TODO: Populate this field hardware? + /// TODO: Populate this field from esp's hardware info or just refer it from HAL? + /// Only intended purpose I see for keeping it here is for spoofing? pub mac: [u8; 6], /// `None` for DHCP pub ip4_static: Option, } +impl SSHConfig { + /// Bump this when the format changes + pub const CURRENT_VERSION: u8 = 6; + /// A buffer this large will fit any SSHConfig. + // It can be updated by looking at + // `cargo test -- roundtrip_config` + // in the demos/common directory + pub const BUF_SIZE: usize = 460; + + /// Creates a new config with default parameters. + /// + /// Will only fail on RNG failure. + pub fn new() -> Result { + let hostkey = SignKey::generate(KeyType::Ed25519, None)?; + let wifi_ssid: String<32> = + option_env!("WIFI_SSID").unwrap_or(SSH_SERVER_ID).try_into().trap()?; + let wifi_pw: Option> = + option_env!("WIFI_PW").map(|s| s.try_into()).transpose().trap()?; + let mac = random_mac()?; + Ok(SSHConfig { + hostkey, + password_authentication: true, + admin_pw: None, + admin_keys: Default::default(), + wifi_ssid, + wifi_pw, + mac, + ip4_static: None, + }) + } + + pub fn set_admin_pw(&mut self, pw: Option<&str>) -> Result<()> { + self.admin_pw = pw.map(|p| PwHash::new(p)).transpose()?; + Ok(()) + } + + pub fn check_admin_pw(&mut self, pw: &str) -> bool { + if let Some(ref p) = self.admin_pw { + p.check(pw) + } else { + false + } + } +} + +fn random_mac() -> Result<[u8; 6]> { + let mut mac = [0u8; 6]; + sunset::random::fill_random(&mut mac)?; + // unicast, locally administered + mac[0] = (mac[0] & 0xfc) | 0x02; + Ok(mac) +} + // a private encoding specific to demo config, not SSH defined. fn enc_signkey(k: &SignKey, s: &mut dyn SSHSink) -> WireResult<()> { // need to add a variant field if we support more key types. @@ -160,13 +197,6 @@ impl<'de> SSHDecode<'de> for SSHConfig { { let hostkey = dec_signkey(s)?; - // let console_pw = dec_option(s)?; - - // let mut console_keys = [None, None, None]; - // for k in console_keys.iter_mut() { - // *k = dec_option(s)?; - // } - let admin_pw = dec_option(s)?; let mut admin_keys = [None, None, None]; @@ -186,9 +216,6 @@ impl<'de> SSHDecode<'de> for SSHConfig { Ok(Self { hostkey, - // console_pw, - // console_keys, - // console_noauth, password_authentication, admin_pw, admin_keys, @@ -279,4 +306,4 @@ impl<'de> SSHDecode<'de> for PwHash { let cost = u8::dec(s)?; Ok(PwHash { salt, hash, cost }) } -} +} \ No newline at end of file diff --git a/src/settings.rs b/src/settings.rs index cfd41c5..0744ff5 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -3,7 +3,7 @@ // SSH server settings //pub(crate) const MTU: usize = 1536; //pub(crate) const PORT: u16 = 22; -pub(crate) const _SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; +pub(crate) const SSH_SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; pub const KEY_SLOTS: usize = 3; // TODO: Document whether this a "reasonable default"? Justify why? pub const PASSWORD_AUTHENTICATION: bool = true; diff --git a/src/storage.rs b/src/storage.rs index 5b7f096..4836a34 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,21 +1,123 @@ -use esp_println::println; +use esp_println::{println, dbg}; use esp_storage::FlashStorage; -//use sequential_storage::map::store_item; -use embassy_embedded_hal::adapter::BlockingAsync; use embedded_storage::ReadStorage; +use sha2::Digest; + +use core::borrow::Borrow; + +use embedded_storage::Storage; +use embedded_storage_async::nor_flash::NorFlash; + +use sunset::error::Error; +use sunset::sshwire::{self, OwnOrBorrow}; + use crate::config::SSHConfig; -pub(crate) async fn _set_value(ssh_config: SSHConfig) -> Result<(), sunset::Error> { - let mut flash = FlashStorage::new(); - println!("Flash size = {}", flash.capacity()); +// TODO: Adapt those for Espressif targets... +const CONFIG_OFFSET: u32 = 0x150000; +pub const FLASH_SIZE: usize = 2 * 1024 * 1024; + +pub(crate) struct Fl { + flash: FlashStorage, + // Only a single task can write to flash at a time, + // keeping a buffer here saves duplicated buffer space in each task. + buf: [u8; FlashConfig::BUF_SIZE], +} + +impl<'a> Fl { + pub fn new(flash: FlashStorage) -> Self { + Self { flash, buf: [0u8; FlashConfig::BUF_SIZE] } + } +} + +// SSHConfig::CURRENT_VERSION must be bumped if any of this struct #[derive(SSHEncode, SSHDecode)] +struct FlashConfig<'a> { + version: u8, + config: OwnOrBorrow<'a, SSHConfig>, + /// sha256 hash of config + hash: [u8; 32], +} + +impl FlashConfig<'_> { + const BUF_SIZE: usize = 4 + SSHConfig::BUF_SIZE + 32; +} +const _: () = + assert!(FlashConfig::BUF_SIZE % 4 == 0, "flash reads must be a multiple of 4"); + +fn config_hash(config: &SSHConfig) -> Result<[u8; 32], Error> { + let mut h = sha2::Sha256::new(); + sshwire::hash_ser(&mut h, config)?; + Ok(h.finalize().into()) +} + +/// Loads a SSHConfig at startup. Good for persisting hostkeys. +pub async fn load_or_create(flash: &mut Fl) -> Result { + match load(flash).await { + Ok(c) => { + println!("Good existing config"); + return Ok(c); + } + Err(e) => println!("Existing config bad, making new. {e}"), + } + + create(flash).await +} + +pub async fn create(flash: &mut Fl) -> Result { + let c = SSHConfig::new()?; + if let Err(_) = save(flash, &c).await { + println!("Error writing config"); + } + Ok(c) +} + +pub async fn load(fl: &mut Fl) -> Result { + fl.flash.read(CONFIG_OFFSET, &mut fl.buf).await.map_err(|e| { + dbg!("flash read error 0x{CONFIG_OFFSET:x} {e:?}"); + Error::msg("flash error") + })?; + + let s: FlashConfig = sshwire::read_ssh(&fl.buf, None)?; + + if s.version != SSHConfig::CURRENT_VERSION { + return Err(Error::msg("wrong config version")); + } + + let calc_hash = config_hash(s.config.borrow())?; + if calc_hash != s.hash { + return Err(Error::msg("bad config hash")); + } + + if let OwnOrBorrow::Own(c) = s.config { + Ok(c) + } else { + // OK panic - OwnOrBorrow always decodes to Own variant + panic!() + } +} - let mut flash = BlockingAsync::new(flash); +pub async fn save(fl: &mut Fl, config: &SSHConfig) -> Result<(), Error> { + let sc = FlashConfig { + version: SSHConfig::CURRENT_VERSION, + config: OwnOrBorrow::Borrow(&config), + hash: config_hash(&config)?, + }; + let l = sshwire::write_ssh(&mut fl.buf, &sc)?; + let buf = &fl.buf[..l]; - // TODO: Define suitable ranges and buffer sizes compatible with all Espressif targets - //store_item(&mut flash, flash_range, cache, data_buffer, key, item); + dbg!("flash erase"); + fl.flash + .erase(CONFIG_OFFSET, CONFIG_OFFSET + ERASE_SIZE as u32) + .await + .map_err(|_| Error::msg("flash erase error"))?; - todo!(); + dbg!("flash write"); + fl.flash + .write(CONFIG_OFFSET, &buf) + .await + .map_err(|_| Error::msg("flash write error"))?; + println!("flash save done"); Ok(()) } From 359dedec13158a0c0d4eee2892ab0afcf8bb9462 Mon Sep 17 00:00:00 2001 From: brainstorm Date: Tue, 17 Jun 2025 15:23:39 +1000 Subject: [PATCH 04/86] [ci skip] Stubborn 'unresolved import' for SSHEncode/SSHDecode traits/proc_macros... --- Cargo.lock | 52 +++++++++++++++++++++++++++++++++++++------------- Cargo.toml | 16 +++++++++++----- src/storage.rs | 14 +++++++++----- 3 files changed, 59 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b65580d..b20024f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "ascii" @@ -671,7 +671,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4cd70abe47945c9116972781b5c05277ad855a5f5569fe2afd3e2e61a103cc0" dependencies = [ - "esp-build", + "esp-build 0.2.0", "esp-println", "semihosting", ] @@ -687,6 +687,17 @@ dependencies = [ "termcolor", ] +[[package]] +name = "esp-build" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "837020ff95fbf4c15c206541dda7994f1bbe6e1505e36a6a5ecb51fdb61656d7" +dependencies = [ + "quote", + "syn", + "termcolor", +] + [[package]] name = "esp-config" version = "0.3.1" @@ -722,10 +733,10 @@ dependencies = [ "embedded-io", "embedded-io-async", "enumset", - "esp-build", + "esp-build 0.2.0", "esp-config", "esp-hal-procmacros", - "esp-metadata", + "esp-metadata 0.6.0", "esp-riscv-rt", "esp-synopsys-usb-otg", "esp32", @@ -763,11 +774,11 @@ dependencies = [ "embassy-time", "embassy-time-driver", "embassy-time-queue-utils", - "esp-build", + "esp-build 0.2.0", "esp-config", "esp-hal", "esp-hal-procmacros", - "esp-metadata", + "esp-metadata 0.6.0", "portable-atomic", "static_cell", ] @@ -801,6 +812,18 @@ dependencies = [ "strum 0.26.3", ] +[[package]] +name = "esp-metadata" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0154d59933c2419ef25a01938517cc6969f47b6af53ebb34c279393aa20d9654" +dependencies = [ + "anyhow", + "basic-toml", + "serde", + "strum 0.27.1", +] + [[package]] name = "esp-println" version = "0.13.1" @@ -808,7 +831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "960703930f9f3c899ddedd122ea27a09d6a612c22323157e524af5b18876448e" dependencies = [ "critical-section", - "esp-build", + "esp-build 0.2.0", "log", "portable-atomic", ] @@ -826,13 +849,15 @@ dependencies = [ [[package]] name = "esp-storage" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e98b5069bd1482fb85f9236f8ea6cd1b5b6b5a1c3c3fb8c938c97ccfbbb051b" +checksum = "5b6502654e5750e8162e66512d7e8d84f7eb51d17ec6aa7e0da95cd102769425" dependencies = [ "critical-section", + "document-features", "embedded-storage", - "esp-build", + "esp-build 0.3.0", + "esp-metadata 0.7.0", ] [[package]] @@ -863,10 +888,10 @@ dependencies = [ "embedded-io-async", "enumset", "esp-alloc", - "esp-build", + "esp-build 0.2.0", "esp-config", "esp-hal", - "esp-metadata", + "esp-metadata 0.6.0", "esp-wifi-sys", "heapless", "libm", @@ -1619,6 +1644,7 @@ dependencies = [ "subtle", "sunset", "sunset-embassy", + "sunset-sshwire-derive", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fee61f6..3d7f7ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,13 +6,13 @@ edition = "2021" license = "MIT OR Apache-2.0" [dependencies] -cfg-if = "1.0.0" +cfg-if = "1" ed25519-dalek = { version = "2", default-features = false } embassy-executor = { version = "0.7"} embassy-net = { version = "0.7", features = ["tcp", "udp", "dhcpv4", "medium-ethernet"] } smoltcp = { version = "0.12", default-features = false, features = ["medium-ethernet", "socket-raw"]} embassy-time = { version = "0.4" } -embedded-io-async = "0.6.0" +embedded-io-async = "0.6" esp-alloc = "0.7" esp-backtrace = { version = "0.15", features = [ "exception-handler", @@ -29,6 +29,7 @@ static_cell = { version = "2", features = ["nightly"] } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } sunset = { version = "0.2.0", git="https://github.com/mkj/sunset", default-features = false, features = ["openssh-key", "embedded-io"]} sunset-embassy = { version = "0.2.0", git = "https://github.com/mkj/sunset", default-features = false} +sunset-sshwire-derive = { version = "0.2.0", git = "https://github.com/mkj/sunset", default-features = false } getrandom = { version = "0.2.10", features = ["custom"] } embassy-sync = "0.7" heapless = "0.8" @@ -37,7 +38,7 @@ edge-dhcp = "0.6" edge-nal = "0.5" edge-nal-embassy = "0.6" sequential-storage = { version = "4.0.1", features = ["heapless"] } -esp-storage = { version = "0.5.0", features = ["storage"]} +esp-storage = { version = "0.6" } embedded-storage = "0.3.1" embassy-embedded-hal = "0.3.0" bcrypt = { version = "0.17.0", default-features = false } @@ -55,16 +56,21 @@ opt-level = 3 [profile.release] codegen-units = 1 # LLVM can perform better optimizations using a single thread debug = 2 -debug-assertions = false +debug-assertions = true incremental = false lto = 'fat' opt-level = 3 -overflow-checks = false +overflow-checks = true [profile.esp32s2] inherits = "release" opt-level = "s" # Optimize for size. +[profile.dev.package.esp-storage] +opt-level = "s" + +[profile.dev.package.esp-wifi] +opt-level = "s" [features] default = ["esp32c6"] diff --git a/src/storage.rs b/src/storage.rs index 4836a34..27acff3 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -10,7 +10,9 @@ use embedded_storage::Storage; use embedded_storage_async::nor_flash::NorFlash; use sunset::error::Error; -use sunset::sshwire::{self, OwnOrBorrow}; +use sunset::sshwire; +use sunset::sshwire::OwnOrBorrow; +use sunset_sshwire_derive::*; use crate::config::SSHConfig; @@ -31,7 +33,8 @@ impl<'a> Fl { } } -// SSHConfig::CURRENT_VERSION must be bumped if any of this struct #[derive(SSHEncode, SSHDecode)] +// SSHConfig::CURRENT_VERSION must be bumped if any of this struct +#[derive(SSHEncode, SSHDecode)] struct FlashConfig<'a> { version: u8, config: OwnOrBorrow<'a, SSHConfig>, @@ -73,7 +76,7 @@ pub async fn create(flash: &mut Fl) -> Result { } pub async fn load(fl: &mut Fl) -> Result { - fl.flash.read(CONFIG_OFFSET, &mut fl.buf).await.map_err(|e| { + fl.flash.read(CONFIG_OFFSET, &mut fl.buf).map_err(|e| { dbg!("flash read error 0x{CONFIG_OFFSET:x} {e:?}"); Error::msg("flash error") })?; @@ -108,14 +111,15 @@ pub async fn save(fl: &mut Fl, config: &SSHConfig) -> Result<(), Error> { dbg!("flash erase"); fl.flash - .erase(CONFIG_OFFSET, CONFIG_OFFSET + ERASE_SIZE as u32) + // TODO: Adapt 4096, ERASE_SIZE in rp, what's in Espressif? + .erase(CONFIG_OFFSET, CONFIG_OFFSET + 4096 as u32) .await .map_err(|_| Error::msg("flash erase error"))?; dbg!("flash write"); fl.flash .write(CONFIG_OFFSET, &buf) - .await + //.await .map_err(|_| Error::msg("flash write error"))?; println!("flash save done"); From 717bed110a15868aaa8726fb696c980382ccaaac Mon Sep 17 00:00:00 2001 From: brainstorm Date: Tue, 17 Jun 2025 15:48:10 +1000 Subject: [PATCH 05/86] [ci skip] Same SSHDecode/SSHEncode 'unresolved import' issue /cc @mkj --- src/storage.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/storage.rs b/src/storage.rs index 27acff3..0c01b86 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -14,6 +14,8 @@ use sunset::sshwire; use sunset::sshwire::OwnOrBorrow; use sunset_sshwire_derive::*; +use sunset::sshwire::{SSHDecode, SSHEncode}; + use crate::config::SSHConfig; // TODO: Adapt those for Espressif targets... From 97f53383e0f29e2f874e9c5d858721393d2d6820 Mon Sep 17 00:00:00 2001 From: brainstorm Date: Wed, 18 Jun 2025 11:29:03 +1000 Subject: [PATCH 06/86] [ci skip] Apply @mkj's fix for sshwire derive, compiles now, thx :D --- Cargo.lock | 45 +++++++++++---------------------------------- Cargo.toml | 6 +++--- src/serve.rs | 9 +++++---- src/storage.rs | 13 ++++++------- 4 files changed, 25 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b20024f..44b0e83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,15 +26,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" -[[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" @@ -437,19 +428,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" -[[package]] -name = "embassy-sync" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd938f25c0798db4280fcd8026bf4c2f48789aebf8f77b6e5cf8a7693ba114ec" -dependencies = [ - "cfg-if", - "critical-section", - "embedded-io-async", - "futures-util", - "heapless", -] - [[package]] name = "embassy-sync" version = "0.6.2" @@ -1643,7 +1621,7 @@ dependencies = [ "static_cell", "subtle", "sunset", - "sunset-embassy", + "sunset-async", "sunset-sshwire-derive", ] @@ -1720,8 +1698,8 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sunset" -version = "0.2.0" -source = "git+https://github.com/mkj/sunset#b1d2c836f1893a7b880937a3fbe1313fb7978f5c" +version = "0.3.0" +source = "git+https://github.com/mkj/sunset?rev=1dcf2ec#1dcf2ec1f117bc51b899bd9af17822c1c35d8024" dependencies = [ "aes", "ascii", @@ -1750,23 +1728,22 @@ dependencies = [ ] [[package]] -name = "sunset-embassy" -version = "0.2.0" -source = "git+https://github.com/mkj/sunset#b1d2c836f1893a7b880937a3fbe1313fb7978f5c" +name = "sunset-async" +version = "0.3.0" +source = "git+https://github.com/mkj/sunset?rev=1dcf2ec#1dcf2ec1f117bc51b899bd9af17822c1c35d8024" dependencies = [ - "atomic-polyfill", "embassy-futures", - "embassy-sync 0.5.0", + "embassy-sync 0.7.0", "embedded-io-async", "log", - "pin-utils", + "portable-atomic", "sunset", ] [[package]] name = "sunset-sshwire-derive" version = "0.2.0" -source = "git+https://github.com/mkj/sunset#b1d2c836f1893a7b880937a3fbe1313fb7978f5c" +source = "git+https://github.com/mkj/sunset?rev=1dcf2ec#1dcf2ec1f117bc51b899bd9af17822c1c35d8024" dependencies = [ "virtue", ] @@ -1877,9 +1854,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "virtue" -version = "0.0.16" +version = "0.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b6826a786a78cf1bb0937507b5551fb6f827d66269a24b00af0de247b19bbc7" +checksum = "7302ac74a033bf17b6e609ceec0f891ca9200d502d31f02dc7908d3d98767c9d" [[package]] name = "void" diff --git a/Cargo.toml b/Cargo.toml index 3d7f7ac..a242ceb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,9 @@ hex = { version = "0.4", default-features = false } log = { version = "0.4" } static_cell = { version = "2", features = ["nightly"] } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } -sunset = { version = "0.2.0", git="https://github.com/mkj/sunset", default-features = false, features = ["openssh-key", "embedded-io"]} -sunset-embassy = { version = "0.2.0", git = "https://github.com/mkj/sunset", default-features = false} -sunset-sshwire-derive = { version = "0.2.0", git = "https://github.com/mkj/sunset", default-features = false } +sunset = { git="https://github.com/mkj/sunset", rev = "1dcf2ec", default-features = false, features = ["openssh-key", "embedded-io"]} +sunset-async = { git = "https://github.com/mkj/sunset", rev = "1dcf2ec", default-features = false} +sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "1dcf2ec", default-features = false } getrandom = { version = "0.2.10", features = ["custom"] } embassy-sync = "0.7" heapless = "0.8" diff --git a/src/serve.rs b/src/serve.rs index 58bc0b1..553caea 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -15,7 +15,7 @@ use embassy_sync::mutex::Mutex; use heapless::String; use sunset::{error, ChanHandle, ServEvent, SignKey}; -use sunset_embassy::{ProgressHolder, SSHServer}; +use sunset_async::{ProgressHolder, SSHServer}; use esp_println::{dbg, println}; @@ -36,7 +36,7 @@ async fn connection_loop( match ev { ServEvent::SessionShell(a) => { if let Some(ch) = session.take() { - debug_assert!(ch.num() == a.channel()?); + debug_assert!(ch.num() == a.channel()); a.succeed()?; dbg!("We got shell"); let _ = chan_pipe.try_send(ch); @@ -81,7 +81,8 @@ async fn connection_loop( println!("Expected caller to handle event"); //error!("Expected caller to handle {event:?}"); error::BadUsage.fail()? - } + }, + _ => () }; } } @@ -94,7 +95,7 @@ pub(crate) async fn handle_ssh_client( let mut inbuf = [0u8; 4096]; let mut outbuf = [0u8; 4096]; - let ssh_server = SSHServer::new(&mut inbuf, &mut outbuf)?; + let ssh_server = SSHServer::new(&mut inbuf, &mut outbuf); let (mut rsock, mut wsock) = stream.split(); let chan_pipe = Channel::::new(); diff --git a/src/storage.rs b/src/storage.rs index 0c01b86..8e4b77d 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -6,23 +6,22 @@ use sha2::Digest; use core::borrow::Borrow; -use embedded_storage::Storage; -use embedded_storage_async::nor_flash::NorFlash; +//use embedded_storage::Storage; +use embedded_storage::nor_flash::NorFlash; +//use embedded_storage_async::nor_flash::NorFlash; use sunset::error::Error; use sunset::sshwire; use sunset::sshwire::OwnOrBorrow; use sunset_sshwire_derive::*; -use sunset::sshwire::{SSHDecode, SSHEncode}; - use crate::config::SSHConfig; // TODO: Adapt those for Espressif targets... const CONFIG_OFFSET: u32 = 0x150000; pub const FLASH_SIZE: usize = 2 * 1024 * 1024; -pub(crate) struct Fl { +pub struct Fl { flash: FlashStorage, // Only a single task can write to flash at a time, // keeping a buffer here saves duplicated buffer space in each task. @@ -78,7 +77,7 @@ pub async fn create(flash: &mut Fl) -> Result { } pub async fn load(fl: &mut Fl) -> Result { - fl.flash.read(CONFIG_OFFSET, &mut fl.buf).map_err(|e| { + fl.flash.read(CONFIG_OFFSET, &mut fl.buf).map_err(|_e| { dbg!("flash read error 0x{CONFIG_OFFSET:x} {e:?}"); Error::msg("flash error") })?; @@ -115,7 +114,7 @@ pub async fn save(fl: &mut Fl, config: &SSHConfig) -> Result<(), Error> { fl.flash // TODO: Adapt 4096, ERASE_SIZE in rp, what's in Espressif? .erase(CONFIG_OFFSET, CONFIG_OFFSET + 4096 as u32) - .await + //.await .map_err(|_| Error::msg("flash erase error"))?; dbg!("flash write"); From d46b3d5ab7219216896ef66f04edbd995574d90d Mon Sep 17 00:00:00 2001 From: brainstorm Date: Sat, 21 Jun 2025 11:47:44 +1000 Subject: [PATCH 07/86] [ci skip] Init config/storage from main, will most probably refactor assumptions around mutexes and rename Fl/FlashCo fig struct names --- Cargo.lock | 6 +++--- Cargo.toml | 6 +++--- src/main.rs | 15 +++++++++++++-- src/storage.rs | 4 ---- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44b0e83..754e497 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1699,7 +1699,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sunset" version = "0.3.0" -source = "git+https://github.com/mkj/sunset?rev=1dcf2ec#1dcf2ec1f117bc51b899bd9af17822c1c35d8024" +source = "git+https://github.com/mkj/sunset?rev=d83ae8#d83ae8a6bf623e1294409daba2bbf601a66dffd6" dependencies = [ "aes", "ascii", @@ -1730,7 +1730,7 @@ dependencies = [ [[package]] name = "sunset-async" version = "0.3.0" -source = "git+https://github.com/mkj/sunset?rev=1dcf2ec#1dcf2ec1f117bc51b899bd9af17822c1c35d8024" +source = "git+https://github.com/mkj/sunset?rev=d83ae8#d83ae8a6bf623e1294409daba2bbf601a66dffd6" dependencies = [ "embassy-futures", "embassy-sync 0.7.0", @@ -1743,7 +1743,7 @@ dependencies = [ [[package]] name = "sunset-sshwire-derive" version = "0.2.0" -source = "git+https://github.com/mkj/sunset?rev=1dcf2ec#1dcf2ec1f117bc51b899bd9af17822c1c35d8024" +source = "git+https://github.com/mkj/sunset?rev=d83ae8#d83ae8a6bf623e1294409daba2bbf601a66dffd6" dependencies = [ "virtue", ] diff --git a/Cargo.toml b/Cargo.toml index a242ceb..232eb5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,9 @@ hex = { version = "0.4", default-features = false } log = { version = "0.4" } static_cell = { version = "2", features = ["nightly"] } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } -sunset = { git="https://github.com/mkj/sunset", rev = "1dcf2ec", default-features = false, features = ["openssh-key", "embedded-io"]} -sunset-async = { git = "https://github.com/mkj/sunset", rev = "1dcf2ec", default-features = false} -sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "1dcf2ec", default-features = false } +sunset = { git="https://github.com/mkj/sunset", rev = "d83ae8", default-features = false, features = ["openssh-key", "embedded-io"]} +sunset-async = { git = "https://github.com/mkj/sunset", rev = "d83ae8", default-features = false} +sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "d83ae8", default-features = false } getrandom = { version = "0.2.10", features = ["custom"] } embassy-sync = "0.7" heapless = "0.8" diff --git a/src/main.rs b/src/main.rs index 9998ffb..54cd68a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,13 +14,15 @@ use esp_hal::{ }; use esp_hal_embassy::InterruptExecutor; +use esp_storage::FlashStorage; use embassy_executor::Spawner; -use ssh_stamp::espressif::{ +use ssh_stamp::{config::SSHConfig, espressif::{ buffered_uart::BufferedUart, net::{accept_requests, if_up}, rng, -}; +}, storage::Fl}; use static_cell::StaticCell; +use sunset_async::SunsetMutex; #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { @@ -45,6 +47,15 @@ async fn main(spawner: Spawner) -> ! { } } + // Read SSH configuration from Flash (if it exists) + let mut flash = Fl::new(FlashStorage::new()); + let mut config = ssh_stamp::storage::load_or_create(&mut flash).await; + + static FLASH: StaticCell> = StaticCell::new(); + let flash = FLASH.init(SunsetMutex::new(flash)); + static CONFIG: StaticCell> = StaticCell::new(); + let config = CONFIG.init(SunsetMutex::new(config.unwrap())); + let wifi_controller = esp_wifi::init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); // Bring up the network interface and start accepting SSH connections. diff --git a/src/storage.rs b/src/storage.rs index 8e4b77d..45638f5 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -6,9 +6,7 @@ use sha2::Digest; use core::borrow::Borrow; -//use embedded_storage::Storage; use embedded_storage::nor_flash::NorFlash; -//use embedded_storage_async::nor_flash::NorFlash; use sunset::error::Error; use sunset::sshwire; @@ -114,13 +112,11 @@ pub async fn save(fl: &mut Fl, config: &SSHConfig) -> Result<(), Error> { fl.flash // TODO: Adapt 4096, ERASE_SIZE in rp, what's in Espressif? .erase(CONFIG_OFFSET, CONFIG_OFFSET + 4096 as u32) - //.await .map_err(|_| Error::msg("flash erase error"))?; dbg!("flash write"); fl.flash .write(CONFIG_OFFSET, &buf) - //.await .map_err(|_| Error::msg("flash write error"))?; println!("flash save done"); From 6bf057338f9bf5cef0500064730dddcc6c2dcc9b Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Thu, 26 Jun 2025 22:14:53 +1000 Subject: [PATCH 08/86] [ci skip] Pass config through tasks --- .cargo/config.toml | 2 +- Cargo.lock | 6 +++--- Cargo.toml | 8 ++++---- partitions.csv | 4 ++++ src/config.rs | 14 +++++++++----- src/espressif/net.rs | 29 ++++++++++++++++++----------- src/main.rs | 38 +++++++++++++++++++++----------------- src/storage.rs | 6 +++--- 8 files changed, 63 insertions(+), 44 deletions(-) create mode 100644 partitions.csv diff --git a/.cargo/config.toml b/.cargo/config.toml index 136fa3d..49bccbc 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -24,7 +24,7 @@ rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32"'] runner = "espflash flash --baud=921600 --monitor" rustflags = [ "-C", "force-frame-pointers"] [target.riscv32imac-unknown-none-elf] -runner = "espflash flash --baud=921600 --monitor" +runner = "espflash flash --baud=921600 --partition-table partitions.csv --monitor" rustflags = [ "-C", "force-frame-pointers"] [target.xtensa-esp32s2-none-elf] runner = "espflash flash --baud=921600 --monitor --chip esp32s2" diff --git a/Cargo.lock b/Cargo.lock index 754e497..d3af787 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1699,7 +1699,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sunset" version = "0.3.0" -source = "git+https://github.com/mkj/sunset?rev=d83ae8#d83ae8a6bf623e1294409daba2bbf601a66dffd6" +source = "git+https://github.com/mkj/sunset?rev=5ff44bb#5ff44bb1f62db25d4dc3da5b4c891bd4a1ba352e" dependencies = [ "aes", "ascii", @@ -1730,7 +1730,7 @@ dependencies = [ [[package]] name = "sunset-async" version = "0.3.0" -source = "git+https://github.com/mkj/sunset?rev=d83ae8#d83ae8a6bf623e1294409daba2bbf601a66dffd6" +source = "git+https://github.com/mkj/sunset?rev=5ff44bb#5ff44bb1f62db25d4dc3da5b4c891bd4a1ba352e" dependencies = [ "embassy-futures", "embassy-sync 0.7.0", @@ -1743,7 +1743,7 @@ dependencies = [ [[package]] name = "sunset-sshwire-derive" version = "0.2.0" -source = "git+https://github.com/mkj/sunset?rev=d83ae8#d83ae8a6bf623e1294409daba2bbf601a66dffd6" +source = "git+https://github.com/mkj/sunset?rev=5ff44bb#5ff44bb1f62db25d4dc3da5b4c891bd4a1ba352e" dependencies = [ "virtue", ] diff --git a/Cargo.toml b/Cargo.toml index 232eb5e..f1dee6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,9 @@ hex = { version = "0.4", default-features = false } log = { version = "0.4" } static_cell = { version = "2", features = ["nightly"] } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } -sunset = { git="https://github.com/mkj/sunset", rev = "d83ae8", default-features = false, features = ["openssh-key", "embedded-io"]} -sunset-async = { git = "https://github.com/mkj/sunset", rev = "d83ae8", default-features = false} -sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "d83ae8", default-features = false } +sunset = { git="https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false, features = ["openssh-key", "embedded-io"]} +sunset-async = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false} +sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false } getrandom = { version = "0.2.10", features = ["custom"] } embassy-sync = "0.7" heapless = "0.8" @@ -37,7 +37,7 @@ embassy-futures = "0.1" edge-dhcp = "0.6" edge-nal = "0.5" edge-nal-embassy = "0.6" -sequential-storage = { version = "4.0.1", features = ["heapless"] } +sequential-storage = { version = "4", features = ["heapless"] } esp-storage = { version = "0.6" } embedded-storage = "0.3.1" embassy-embedded-hal = "0.3.0" diff --git a/partitions.csv b/partitions.csv new file mode 100644 index 0000000..c3e6609 --- /dev/null +++ b/partitions.csv @@ -0,0 +1,4 @@ +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +config, data, nvs, 0x110000, 0x2000, diff --git a/src/config.rs b/src/config.rs index bcdec8a..abe1421 100644 --- a/src/config.rs +++ b/src/config.rs @@ -38,16 +38,13 @@ pub struct SSHConfig { /// `None` for DHCP pub ip4_static: Option, + pub uart_rx_pin: u8, + pub uart_tx_pin: u8 } impl SSHConfig { /// Bump this when the format changes pub const CURRENT_VERSION: u8 = 6; - /// A buffer this large will fit any SSHConfig. - // It can be updated by looking at - // `cargo test -- roundtrip_config` - // in the demos/common directory - pub const BUF_SIZE: usize = 460; /// Creates a new config with default parameters. /// @@ -68,6 +65,8 @@ impl SSHConfig { wifi_pw, mac, ip4_static: None, + uart_rx_pin: 1, + uart_tx_pin: 2, }) } @@ -214,6 +213,9 @@ impl<'de> SSHDecode<'de> for SSHConfig { // Decode password_authentication (missing in original code) let password_authentication = SSHDecode::dec(s)?; + let uart_rx_pin = SSHDecode::dec(s)?; + let uart_tx_pin = SSHDecode::dec(s)?; + Ok(Self { hostkey, password_authentication, @@ -223,6 +225,8 @@ impl<'de> SSHDecode<'de> for SSHConfig { wifi_pw, mac, ip4_static, + uart_rx_pin, + uart_tx_pin }) } } diff --git a/src/espressif/net.rs b/src/espressif/net.rs index eade2b9..7afa9bc 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -15,6 +15,7 @@ use esp_println::{dbg, println}; use esp_wifi::wifi::{AccessPointConfiguration, Configuration, WifiController, WifiDevice}; use esp_wifi::wifi::{WifiEvent, WifiState}; use esp_wifi::EspWifiController; +use sunset_async::SunsetMutex; use core::net::SocketAddrV4; use edge_dhcp; @@ -26,9 +27,9 @@ use edge_dhcp::{ use edge_nal::UdpBind; use edge_nal_embassy::{Udp, UdpBuffers}; -use super::buffered_uart::BufferedUart; +use crate::config::SSHConfig; -const GW_IP_ADDR_ENV: Option<&'static str> = option_env!("GATEWAY_IP"); +use super::buffered_uart::BufferedUart; // When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html macro_rules! mk_static { @@ -45,16 +46,22 @@ pub async fn if_up( wifi_controller: EspWifiController<'static>, wifi: impl Peripheral

+ 'static, rng: &mut Rng, + config: &'static SunsetMutex ) -> Result, sunset::Error> { let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); - let gw_ip_addr_str = GW_IP_ADDR_ENV.unwrap_or("192.168.0.1"); - let gw_ip_addr = Ipv4Addr::from_str(gw_ip_addr_str).expect("failed to parse gateway ip"); - - let config = embassy_net::Config::ipv4_static(StaticConfigV4 { - address: Ipv4Cidr::new(gw_ip_addr, 24), - gateway: Some(gw_ip_addr), + let gw_ip_addr_ipv4 = Ipv4Addr::from_str("192.168.0.1").expect("failed to parse gateway ip"); + + let _gw_ip_addr = if let Some(ref s) = config.lock().await.ip4_static { + embassy_net::Config::ipv4_static(s.clone()) + } else { + embassy_net::Config::dhcpv4(Default::default()) + }; + + let net_config = embassy_net::Config::ipv4_static(StaticConfigV4 { + address: Ipv4Cidr::new(gw_ip_addr_ipv4, 24), + gateway: Some(gw_ip_addr_ipv4), dns_servers: Default::default(), }); @@ -63,14 +70,14 @@ pub async fn if_up( // Init network stack let (ap_stack, runner) = embassy_net::new( interfaces.ap, - config, + net_config, mk_static!(StackResources<3>, StackResources::<3>::new()), seed, ); spawner.spawn(wifi_up(controller)).ok(); spawner.spawn(net_up(runner)).ok(); - spawner.spawn(dhcp_server(ap_stack, gw_ip_addr)).ok(); + spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); loop { println!("Checking if link is up...\n"); @@ -83,7 +90,7 @@ pub async fn if_up( // TODO: Use wifi_manager instead? println!( "Connect to the AP `ssh-stamp` as a DHCP client with IP: {}", - gw_ip_addr_str + gw_ip_addr_ipv4 ); Ok(ap_stack) diff --git a/src/main.rs b/src/main.rs index 54cd68a..5f40a8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,12 +5,7 @@ use core::marker::Sized; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - gpio::AnyPin, - interrupt::{software::SoftwareInterruptControl, Priority}, - peripherals::UART1, - rng::Rng, - timer::timg::TimerGroup, - uart::{Config, RxConfig, Uart}, + interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} }; use esp_hal_embassy::InterruptExecutor; @@ -49,37 +44,38 @@ async fn main(spawner: Spawner) -> ! { // Read SSH configuration from Flash (if it exists) let mut flash = Fl::new(FlashStorage::new()); - let mut config = ssh_stamp::storage::load_or_create(&mut flash).await; + let config = ssh_stamp::storage::load_or_create(&mut flash).await; static FLASH: StaticCell> = StaticCell::new(); - let flash = FLASH.init(SunsetMutex::new(flash)); + let _flash = FLASH.init(SunsetMutex::new(flash)); + static CONFIG: StaticCell> = StaticCell::new(); - let config = CONFIG.init(SunsetMutex::new(config.unwrap())); + let mut config = CONFIG.init(SunsetMutex::new(config.unwrap())); let wifi_controller = esp_wifi::init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); // Bring up the network interface and start accepting SSH connections. - let tcp_stack = if_up(spawner, wifi_controller, peripherals.WIFI, &mut rng) + let tcp_stack = if_up(spawner, wifi_controller, peripherals.WIFI, &mut rng, config) .await .unwrap(); // Set up software buffered UART to run in a higher priority InterruptExecutor let uart_buf = UART_BUF.init_with(BufferedUart::new); let software_interrupts = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); - let interrupt_exeuctor = + let interrupt_executor = INT_EXECUTOR.init_with(|| InterruptExecutor::new(software_interrupts.software_interrupt0)); cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { - let interrupt_spawner = interrupt_exeuctor.start(Priority::Priority1); + let interrupt_spawner = interrupt_executor.start(Priority::Priority1); } else { - let interrupt_spawner = interrupt_exeuctor.start(Priority::Priority10); + let interrupt_spawner = interrupt_executor.start(Priority::Priority10); } } cfg_if::cfg_if! { if #[cfg(not(feature = "esp32c2"))] { - interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1, peripherals.GPIO11.into(), peripherals.GPIO10.into())).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1, config)).unwrap(); } else { - interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1, peripherals.GPIO9.into(), peripherals.GPIO10.into())).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1, config)).unwrap(); } } accept_requests(tcp_stack, uart_buf).await; @@ -93,9 +89,17 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1, - rx_pin: AnyPin, - tx_pin: AnyPin, + config: &'static SunsetMutex ) { + // TODO: Find the "live reconfiguration" calls to change all parameters + // while firmware is running, including but not limited to mapped GPIOs. + + // TODO: Map input u8 to correct AnyPin/GPIO pin(s) + //config.lock().await.uart_rx_pin + let rx_pin_num = config.lock().await.uart_rx_pin; + let rx_pin = AnyPin + //let tx_pin = config.lock().await.uart_tx_pin; + // Hardware UART setup let uart_config = Config::default().with_rx( RxConfig::default() diff --git a/src/storage.rs b/src/storage.rs index 45638f5..345dab5 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -16,8 +16,8 @@ use sunset_sshwire_derive::*; use crate::config::SSHConfig; // TODO: Adapt those for Espressif targets... -const CONFIG_OFFSET: u32 = 0x150000; -pub const FLASH_SIZE: usize = 2 * 1024 * 1024; +pub const CONFIG_AREA_SIZE: usize = 460; +const CONFIG_OFFSET: u32 = 0x110000; pub struct Fl { flash: FlashStorage, @@ -42,7 +42,7 @@ struct FlashConfig<'a> { } impl FlashConfig<'_> { - const BUF_SIZE: usize = 4 + SSHConfig::BUF_SIZE + 32; + const BUF_SIZE: usize = 4 + CONFIG_AREA_SIZE + 32; } const _: () = assert!(FlashConfig::BUF_SIZE % 4 == 0, "flash reads must be a multiple of 4"); From 8077954471f78e331f4b0b6e5fd355be5abad38d Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 27 Jun 2025 20:51:46 +1000 Subject: [PATCH 09/86] [ci skip] AnyPin::steal is unsafe(), need to find a way to safely reconfigure the UART's pins at runtime (possible?) and also pass the config object around (safely/mutex-ing) too --- src/main.rs | 53 +++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/main.rs b/src/main.rs index e61a4c0..f3141bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use core::marker::Sized; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} + gpio::AnyPin, interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} }; use esp_hal_embassy::InterruptExecutor; @@ -58,7 +58,7 @@ async fn main(spawner: Spawner) -> ! { let _flash = FLASH.init(SunsetMutex::new(flash)); static CONFIG: StaticCell> = StaticCell::new(); - let mut config = CONFIG.init(SunsetMutex::new(config.unwrap())); + let config = CONFIG.init(SunsetMutex::new(config.unwrap())); let wifi_controller = esp_wifi::init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); @@ -93,34 +93,39 @@ static UART_BUF: StaticCell = StaticCell::new(); static INT_EXECUTOR: StaticCell> = StaticCell::new(); -#[embassy_executor::task] +#[embassy_executor::task()] async fn uart_task( buffer: &'static BufferedUart, - uart_periph: UART1, + uart_periph: UART1<'static>, config: &'static SunsetMutex ) { // TODO: Find the "live reconfiguration" calls to change all parameters // while firmware is running, including but not limited to mapped GPIOs. - // TODO: Map input u8 to correct AnyPin/GPIO pin(s) - //config.lock().await.uart_rx_pin - let rx_pin_num = config.lock().await.uart_rx_pin; - let rx_pin = AnyPin - //let tx_pin = config.lock().await.uart_tx_pin; - - // Hardware UART setup - let uart_config = Config::default().with_rx( - RxConfig::default() - .with_fifo_full_threshold(16) - .with_timeout(1), - ); - - let uart = Uart::new(uart_periph, uart_config) - .unwrap() - .with_rx(rx_pin) - .with_tx(tx_pin) - .into_async(); + // TODO: Yikes, unsafe code here, but we need to steal the pins to reconfigure the UART + // when config changes via SSH env vars... I need to find a better way for this :/ - // Run the main buffered TX/RX loop - buffer.run(uart).await; + let rx_pin_num = config.lock().await.uart_rx_pin; + let tx_pin_num = config.lock().await.uart_tx_pin; + unsafe { + let rx_pin = AnyPin::steal(rx_pin_num); + let tx_pin = AnyPin::steal(tx_pin_num); + + + // Hardware UART setup + let uart_config = Config::default().with_rx( + RxConfig::default() + .with_fifo_full_threshold(16) + .with_timeout(1) + ); + + let uart = Uart::new(uart_periph, uart_config) + .unwrap() + .with_rx(rx_pin) + .with_tx(tx_pin) + .into_async(); + + // Run the main buffered TX/RX loop + buffer.run(uart).await; + } } From c98100181abb87334a7ba1f7b7a9de2ffabeb144 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Sat, 28 Jun 2025 16:11:54 +1000 Subject: [PATCH 10/86] [ci skip] Switch to multi-thread (feature flag) in sunset-async to get a CriticalSectionRawMutex for the SSHConfig. Next up: figure out non-unsafe() ways to steal() pins for the UART pins --- .vscode/settings.json | 3 + Cargo.lock | 164 ++++++++++++++++++------------------------ Cargo.toml | 4 +- src/config.rs | 9 +-- src/espressif/net.rs | 13 ++-- src/main.rs | 19 +++-- 6 files changed, 101 insertions(+), 111 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 979d509..f9a1f42 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { "rust-analyzer.check.allTargets": false, + // "rust-analyzer.cargo.features": [ + // "esp32c6" + // ] } diff --git a/Cargo.lock b/Cargo.lock index 2fb1297..26f9ffa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,9 +34,9 @@ checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base64" @@ -46,9 +46,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "basic-toml" @@ -98,9 +98,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "block-buffer" @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" [[package]] name = "byteorder" @@ -135,9 +135,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "chacha20" @@ -183,7 +183,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "typenum", ] @@ -667,17 +667,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "esp-build" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "837020ff95fbf4c15c206541dda7994f1bbe6e1505e36a6a5ecb51fdb61656d7" -dependencies = [ - "quote", - "syn", - "termcolor", -] - [[package]] name = "esp-config" version = "0.4.0" @@ -697,7 +686,7 @@ checksum = "0d973697621cd3eef9c3f260fa8c1af77d8547cfc92734255d8e8ddf05c7d331" dependencies = [ "basic-toml", "bitfield", - "bitflags 2.9.0", + "bitflags 2.9.1", "bytemuck", "cfg-if", "critical-section", @@ -715,10 +704,10 @@ dependencies = [ "embedded-io", "embedded-io-async", "enumset", - "esp-build 0.2.0", + "esp-build", "esp-config", "esp-hal-procmacros", - "esp-metadata 0.6.0", + "esp-metadata", "esp-riscv-rt", "esp-synopsys-usb-otg", "esp32", @@ -756,11 +745,11 @@ dependencies = [ "embassy-time", "embassy-time-driver", "embassy-time-queue-utils", - "esp-build 0.2.0", + "esp-build", "esp-config", "esp-hal", "esp-hal-procmacros", - "esp-metadata 0.6.0", + "esp-metadata", "portable-atomic", "static_cell", ] @@ -794,18 +783,6 @@ dependencies = [ "strum", ] -[[package]] -name = "esp-metadata" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0154d59933c2419ef25a01938517cc6969f47b6af53ebb34c279393aa20d9654" -dependencies = [ - "anyhow", - "basic-toml", - "serde", - "strum 0.27.1", -] - [[package]] name = "esp-println" version = "0.14.0" @@ -840,8 +817,8 @@ dependencies = [ "critical-section", "document-features", "embedded-storage", - "esp-build 0.3.0", - "esp-metadata 0.7.0", + "esp-build", + "esp-metadata", ] [[package]] @@ -872,10 +849,10 @@ dependencies = [ "embedded-io-async", "enumset", "esp-alloc", - "esp-build 0.2.0", + "esp-build", "esp-config", "esp-hal", - "esp-metadata 0.6.0", + "esp-metadata", "esp-wifi-sys", "num-derive", "num-traits", @@ -1023,9 +1000,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -1043,9 +1020,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "heapless" @@ -1086,9 +1063,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -1130,9 +1107,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "linked_list_allocator" @@ -1163,15 +1140,15 @@ checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "minijinja" -version = "2.9.0" +version = "2.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754" +checksum = "dd72e8b4e42274540edabec853f607c015c73436159b06c39c7af85a20433155" dependencies = [ "serde", ] @@ -1213,18 +1190,19 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro2", "quote", @@ -1286,9 +1264,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable_atomic_enum" @@ -1445,9 +1423,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -1467,16 +1445,6 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" -[[package]] -name = "sequential-storage" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8e933f534642c25b7341338c10e2250187c8cd198c3957ef6894265eefda86" -dependencies = [ - "embedded-storage-async", - "heapless", -] - [[package]] name = "serde" version = "1.0.219" @@ -1511,9 +1479,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -1550,18 +1518,18 @@ dependencies = [ [[package]] name = "snafu" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +checksum = "320b01e011bf8d5d7a4a4a4be966d9160968935849c83b918827f6a435e7f627" dependencies = [ "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +checksum = "1961e2ef424c1424204d3a5d6975f934f56b6d50ff5732382d84ebf460e147f7" dependencies = [ "heck", "proc-macro2", @@ -1638,9 +1606,8 @@ dependencies = [ "hex", "hmac", "log", - "sequential-storage", - "sha2", "portable-atomic", + "sha2", "smoltcp", "ssh-key", "static_cell", @@ -1658,9 +1625,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static_cell" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e" +checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23" dependencies = [ "portable-atomic", ] @@ -1753,9 +1720,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -1773,9 +1740,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", @@ -1785,26 +1752,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "typenum" version = "1.18.0" @@ -1869,9 +1843,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "winapi-util" @@ -1957,9 +1931,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index e3a709c..20f6ed4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ log = { version = "0.4" } static_cell = { version = "2", features = ["nightly"] } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } sunset = { git="https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false, features = ["openssh-key", "embedded-io"]} -sunset-async = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false} +sunset-async = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false, features = ["multi-thread"]} sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false } getrandom = { version = "0.2.10", features = ["custom"] } embassy-sync = "0.7" @@ -37,7 +37,7 @@ embassy-futures = "0.1" edge-dhcp = "0.6" edge-nal = "0.5" edge-nal-embassy = "0.6" -sequential-storage = { version = "4", features = ["heapless"] } +#sequential-storage = { version = "4", features = ["heapless"] } esp-storage = { version = "0.6" } embedded-storage = "0.3.1" embassy-embedded-hal = "0.3" diff --git a/src/config.rs b/src/config.rs index abe1421..edea949 100644 --- a/src/config.rs +++ b/src/config.rs @@ -27,19 +27,20 @@ pub struct SSHConfig { pub admin_pw: Option, pub admin_keys: [Option; KEY_SLOTS], - /// WiFi SSID + /// WiFi pub wifi_ssid: String<32>, - /// WPA2 passphrase. None is Open network. pub wifi_pw: Option>, + /// Networking /// TODO: Populate this field from esp's hardware info or just refer it from HAL? /// Only intended purpose I see for keeping it here is for spoofing? pub mac: [u8; 6], - /// `None` for DHCP pub ip4_static: Option, + + /// UART pub uart_rx_pin: u8, - pub uart_tx_pin: u8 + pub uart_tx_pin: u8, } impl SSHConfig { diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 5fe0941..f2ce7ed 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -45,7 +45,7 @@ pub async fn if_up( wifi_controller: EspWifiController<'static>, wifi: WIFI<'static>, rng: &mut Rng, - config: &'static SunsetMutex + config: &'static mut SunsetMutex ) -> Result, sunset::Error> { let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); @@ -74,7 +74,7 @@ pub async fn if_up( seed, ); - spawner.spawn(wifi_up(controller)).ok(); + spawner.spawn(wifi_up(controller, config)).ok(); spawner.spawn(net_up(runner)).ok(); spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); @@ -126,8 +126,13 @@ pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { } #[embassy_executor::task] -async fn wifi_up(mut controller: WifiController<'static>) { +async fn wifi_up(mut controller: WifiController<'static>, config: &'static mut SunsetMutex) { println!("Device capabilities: {:?}", controller.capabilities()); + + let wifi_ssid = &config.lock().await.wifi_ssid; + // TODO: No wifi password(s) yet... + //let wifi_password = config.lock().await.wifi_pw; + loop { if esp_wifi::wifi::wifi_state() == WifiState::ApStarted { // wait until we're no longer connected @@ -136,7 +141,7 @@ async fn wifi_up(mut controller: WifiController<'static>) { } if !matches!(controller.is_started(), Ok(true)) { let client_config = Configuration::AccessPoint(AccessPointConfiguration { - ssid: "ssh-stamp".into(), + ssid: wifi_ssid.to_ascii_lowercase(), ..Default::default() }); controller.set_configuration(&client_config).unwrap(); diff --git a/src/main.rs b/src/main.rs index f3141bc..61c7b0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,23 +81,22 @@ async fn main(spawner: Spawner) -> ! { } cfg_if::cfg_if! { if #[cfg(not(feature = "esp32c2"))] { - interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1, config)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1)).unwrap(); //, _config)).unwrap(); } else { - interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1, config)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1)).unwrap(); //, config)).unwrap(); } } accept_requests(tcp_stack, uart_buf).await; } static UART_BUF: StaticCell = StaticCell::new(); - static INT_EXECUTOR: StaticCell> = StaticCell::new(); #[embassy_executor::task()] async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - config: &'static SunsetMutex + //config: &'static SunsetMutex ) { // TODO: Find the "live reconfiguration" calls to change all parameters // while firmware is running, including but not limited to mapped GPIOs. @@ -105,8 +104,16 @@ async fn uart_task( // TODO: Yikes, unsafe code here, but we need to steal the pins to reconfigure the UART // when config changes via SSH env vars... I need to find a better way for this :/ - let rx_pin_num = config.lock().await.uart_rx_pin; - let tx_pin_num = config.lock().await.uart_tx_pin; + // let rx_pin_num = config.lock().await.uart_rx_pin; + // let tx_pin_num = config.lock().await.uart_tx_pin; + + let rx_pin_num = 1; + let tx_pin_num = 2; + + // You can do all of this without steal by passing the pins and uart with .reborrow() appended (so that we borrow the pins/uart, not move), + // then if you drop the uart driver all the resources will still be there so you can construct again + // If you just need to set the config again, just call apply_config + unsafe { let rx_pin = AnyPin::steal(rx_pin_num); let tx_pin = AnyPin::steal(tx_pin_num); From e8b7cbed97370d613005a105236ffe4615b10649 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 1 Jul 2025 21:15:53 +1000 Subject: [PATCH 11/86] [ci skip] Experiment with suggestions given on esp-rs/esp-hal matrix channel, needs more work/thought... also PeripheralRef is not present in 1.0.0-beta.1 --- .cargo/config.toml | 12 +++++-- src/lib.rs | 1 + src/main.rs | 87 +++++++++++++++++++++++++++------------------- 3 files changed, 62 insertions(+), 38 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 49bccbc..8dfd732 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -33,9 +33,17 @@ rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32s2"'] runner = "espflash flash --baud=921600 --monitor --chip esp32s3" rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32s3"'] - +# https://docs.espressif.com/projects/rust/esp-hal/1.0.0-beta.1/esp32c6/esp_hal/index.html#additional-configuration [env] -ESP_LOG="INFO" +ESP_LOG = "INFO" +#ESP_HAL_CONFIG_PLACE_SWITCH_TABLES_IN_RAM=true +#ESP_HAL_CONFIG_PLACE_ANON_IN_RAM=false +#ESP_HAL_CONFIG_FLIP_LINK=false +#ESP_HAL_CONFIG_STACK_GUARD_OFFSET=4096 +#ESP_HAL_CONFIG_STACK_GUARD_VALUE=3740121773 +#ESP_HAL_CONFIG_IMPL_CRITICAL_SECTION=ture + + [build] target = "riscv32imac-unknown-none-elf" diff --git a/src/lib.rs b/src/lib.rs index 02f9e5f..f059835 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] #![forbid(unsafe_code)] +#[deny(clippy::mem_forget)] // avoids any UB, forces use of Drop impl instead pub mod config; pub mod espressif; diff --git a/src/main.rs b/src/main.rs index 61c7b0e..c5fab60 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use core::marker::Sized; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - gpio::AnyPin, interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} + gpio::AnyPin, interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::{self, UART1}, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} }; use esp_hal_embassy::InterruptExecutor; @@ -18,6 +18,7 @@ use ssh_stamp::{config::SSHConfig, espressif::{ }, storage::Fl}; use static_cell::StaticCell; use sunset_async::SunsetMutex; +use heapless::Vec; #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { @@ -79,11 +80,24 @@ async fn main(spawner: Spawner) -> ! { let interrupt_spawner = interrupt_executor.start(Priority::Priority10); } } + // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality + let uart1 = peripherals.UART1.reborrow(); + + // Potential pins to use for such UART, to be owned by uart_task. + // TODO: Unsure if that's what was referred in the conversations below... + let uart_pins = Vec::, 2>::from([ + peripherals.GPIO1, + peripherals.GPIO2 + ]); + + // let rx = config.lock().await.uart_rx_pin; + // let tx = config.lock().await.uart_tx_pin; + cfg_if::cfg_if! { if #[cfg(not(feature = "esp32c2"))] { - interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1)).unwrap(); //, _config)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins)).unwrap(); //, _config)).unwrap(); } else { - interrupt_spawner.spawn(uart_task(uart_buf, peripherals.UART1)).unwrap(); //, config)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins)).unwrap(); //, config)).unwrap(); } } accept_requests(tcp_stack, uart_buf).await; @@ -96,43 +110,44 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - //config: &'static SunsetMutex + uart_pins: &'static Vec, 2>, + config: &'static SunsetMutex ) { - // TODO: Find the "live reconfiguration" calls to change all parameters - // while firmware is running, including but not limited to mapped GPIOs. - - // TODO: Yikes, unsafe code here, but we need to steal the pins to reconfigure the UART - // when config changes via SSH env vars... I need to find a better way for this :/ - - // let rx_pin_num = config.lock().await.uart_rx_pin; - // let tx_pin_num = config.lock().await.uart_tx_pin; - - let rx_pin_num = 1; - let tx_pin_num = 2; + // Suggestions from esp-rs/esp-hal matrix channel by different authors on how to handle runtime UART pin changes, WIP: // You can do all of this without steal by passing the pins and uart with .reborrow() appended (so that we borrow the pins/uart, not move), // then if you drop the uart driver all the resources will still be there so you can construct again // If you just need to set the config again, just call apply_config - unsafe { - let rx_pin = AnyPin::steal(rx_pin_num); - let tx_pin = AnyPin::steal(tx_pin_num); - - - // Hardware UART setup - let uart_config = Config::default().with_rx( - RxConfig::default() - .with_fifo_full_threshold(16) - .with_timeout(1) - ); - - let uart = Uart::new(uart_periph, uart_config) - .unwrap() - .with_rx(rx_pin) - .with_tx(tx_pin) - .into_async(); - - // Run the main buffered TX/RX loop - buffer.run(uart).await; - } + // are you reading this from a non-rust file or something? I'd expect a cross-platform crate to take pins as some generic parameter and use a + // non-chip-specific trait to access them, not use integers. Should be straightforward enough at compile-time and if you're doing it at runtime + // that seems a bit weird. + + // I think to do that, you could have the uart task own all of the AnyPins that might be chosen for uart on that platform, as well as uart_periph = UART1. + // Turn the pins array into an array of PeripheralRef with .into_ref(), and also uart_periph.into_ref(). then when you want to configure, + // like mabez said you .reborrow() from chosen pins in that array. (might need an array of refcells?). then pass the reborrowed uart_tx/uart_rx into the + // .with_rx or .with_tx? Then when you next reconfigure, you drop the "reborrow" instances and then the borrow checker should let you borrow again. + + // yeah, and if you want it to put them to different uses then you can probably build a map to hold the pins you aren't currently using in a mutex or the like, + // so you can check that the configuration is sensible at runtime. + + // TODO: Probably better use a HashMap as suggested above instead of an array of pins? + let rx_pin_num = uart_pins[config.lock().await.uart_rx_pin as usize]; + let tx_pin_num = uart_pins[config.lock().await.uart_tx_pin as usize]; + + // Hardware UART setup + let uart_config = Config::default().with_rx( + RxConfig::default() + .with_fifo_full_threshold(16) + .with_timeout(1) + ); + + let uart = Uart::new(uart_periph, uart_config) + .unwrap() + .with_rx(rx_pin_num) + .with_tx(tx_pin_num) + .into_async(); + + // Run the main buffered TX/RX loop + buffer.run(uart).await; } From 7f98af9188dc43f348e0d99ecce8604cadcc105e Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Sun, 6 Jul 2025 20:55:02 +1000 Subject: [PATCH 12/86] [ci skip] This should be simpler, too much mutex/static cell/etc... --- src/main.rs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index c5fab60..9f519c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::marker::Sized; +use core::{any::Any, iter::Map, marker::Sized}; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ @@ -64,7 +64,9 @@ async fn main(spawner: Spawner) -> ! { let wifi_controller = esp_wifi::init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); // Bring up the network interface and start accepting SSH connections. - let tcp_stack = if_up(spawner, wifi_controller, peripherals.WIFI, &mut rng, config) + // Clone the reference to config to avoid borrow checker issues. + let config_for_network = config; + let tcp_stack = if_up(spawner, wifi_controller, peripherals.WIFI, &mut rng, config_for_network) .await .unwrap(); @@ -81,23 +83,25 @@ async fn main(spawner: Spawner) -> ! { } } // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality - let uart1 = peripherals.UART1.reborrow(); + let uart1 = peripherals.UART1; // Potential pins to use for such UART, to be owned by uart_task. // TODO: Unsure if that's what was referred in the conversations below... - let uart_pins = Vec::, 2>::from([ - peripherals.GPIO1, - peripherals.GPIO2 - ]); - - // let rx = config.lock().await.uart_rx_pin; - // let tx = config.lock().await.uart_tx_pin; + static UART_PINS: StaticCell, 2>> = StaticCell::new(); + let uart_pins = UART_PINS.init({ + let mut pins = Vec::, 2>::new(); + pins.push(peripherals.GPIO1.into()).unwrap(); + pins.push(peripherals.GPIO2.into()).unwrap(); + pins + }); + // let rx = config.lock().await.uart_rx_pin; + // Use the same config reference for UART task. cfg_if::cfg_if! { if #[cfg(not(feature = "esp32c2"))] { - interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins)).unwrap(); //, _config)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins, config)).unwrap(); //, _config)).unwrap(); } else { - interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins)).unwrap(); //, config)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins, config)).unwrap(); //, config)).unwrap(); } } accept_requests(tcp_stack, uart_buf).await; @@ -132,8 +136,8 @@ async fn uart_task( // so you can check that the configuration is sensible at runtime. // TODO: Probably better use a HashMap as suggested above instead of an array of pins? - let rx_pin_num = uart_pins[config.lock().await.uart_rx_pin as usize]; - let tx_pin_num = uart_pins[config.lock().await.uart_tx_pin as usize]; + let rx_pin_num = &uart_pins[config.lock().await.uart_rx_pin as usize]; + let tx_pin_num = &uart_pins[config.lock().await.uart_tx_pin as usize]; // Hardware UART setup let uart_config = Config::default().with_rx( From 30e2fe77b3ea76c86b1203d59cac0b3977294916 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 8 Jul 2025 20:26:56 +1000 Subject: [PATCH 13/86] Use SunsetMutex() for the UART reconfiguration scheme. Co-authored-by: Marko Malenic --- src/espressif/net.rs | 4 +-- src/main.rs | 60 +++++++++++++------------------------------- 2 files changed, 20 insertions(+), 44 deletions(-) diff --git a/src/espressif/net.rs b/src/espressif/net.rs index f2ce7ed..6ec5806 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -45,7 +45,7 @@ pub async fn if_up( wifi_controller: EspWifiController<'static>, wifi: WIFI<'static>, rng: &mut Rng, - config: &'static mut SunsetMutex + config: &'static SunsetMutex ) -> Result, sunset::Error> { let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); @@ -126,7 +126,7 @@ pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { } #[embassy_executor::task] -async fn wifi_up(mut controller: WifiController<'static>, config: &'static mut SunsetMutex) { +async fn wifi_up(mut controller: WifiController<'static>, config: &'static SunsetMutex) { println!("Device capabilities: {:?}", controller.capabilities()); let wifi_ssid = &config.lock().await.wifi_ssid; diff --git a/src/main.rs b/src/main.rs index 9f519c4..a4a093a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ #![no_std] #![no_main] -use core::{any::Any, iter::Map, marker::Sized}; +use core::marker::Sized; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - gpio::AnyPin, interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::{self, UART1}, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} + gpio::AnyPin, interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} }; use esp_hal_embassy::InterruptExecutor; @@ -19,7 +19,7 @@ use ssh_stamp::{config::SSHConfig, espressif::{ use static_cell::StaticCell; use sunset_async::SunsetMutex; use heapless::Vec; - + #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { cfg_if::cfg_if!( @@ -65,8 +65,7 @@ async fn main(spawner: Spawner) -> ! { // Bring up the network interface and start accepting SSH connections. // Clone the reference to config to avoid borrow checker issues. - let config_for_network = config; - let tcp_stack = if_up(spawner, wifi_controller, peripherals.WIFI, &mut rng, config_for_network) + let tcp_stack = if_up(spawner, wifi_controller, peripherals.WIFI, &mut rng, config) .await .unwrap(); @@ -87,23 +86,17 @@ async fn main(spawner: Spawner) -> ! { // Potential pins to use for such UART, to be owned by uart_task. // TODO: Unsure if that's what was referred in the conversations below... - static UART_PINS: StaticCell, 2>> = StaticCell::new(); + static UART_PINS: StaticCell>, 2>> = StaticCell::new(); let uart_pins = UART_PINS.init({ - let mut pins = Vec::, 2>::new(); - pins.push(peripherals.GPIO1.into()).unwrap(); - pins.push(peripherals.GPIO2.into()).unwrap(); + let mut pins = Vec::>, 2>::new(); + pins.push(SunsetMutex::new(peripherals.GPIO1.into())).unwrap(); + pins.push(SunsetMutex::new(peripherals.GPIO2.into())).unwrap(); pins }); - // let rx = config.lock().await.uart_rx_pin; // Use the same config reference for UART task. - cfg_if::cfg_if! { - if #[cfg(not(feature = "esp32c2"))] { - interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins, config)).unwrap(); //, _config)).unwrap(); - } else { - interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins, config)).unwrap(); //, config)).unwrap(); - } - } + interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins, config)).unwrap(); + accept_requests(tcp_stack, uart_buf).await; } @@ -114,31 +107,9 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - uart_pins: &'static Vec, 2>, + uart_pins: &'static Vec>, 2>, config: &'static SunsetMutex ) { - // Suggestions from esp-rs/esp-hal matrix channel by different authors on how to handle runtime UART pin changes, WIP: - - // You can do all of this without steal by passing the pins and uart with .reborrow() appended (so that we borrow the pins/uart, not move), - // then if you drop the uart driver all the resources will still be there so you can construct again - // If you just need to set the config again, just call apply_config - - // are you reading this from a non-rust file or something? I'd expect a cross-platform crate to take pins as some generic parameter and use a - // non-chip-specific trait to access them, not use integers. Should be straightforward enough at compile-time and if you're doing it at runtime - // that seems a bit weird. - - // I think to do that, you could have the uart task own all of the AnyPins that might be chosen for uart on that platform, as well as uart_periph = UART1. - // Turn the pins array into an array of PeripheralRef with .into_ref(), and also uart_periph.into_ref(). then when you want to configure, - // like mabez said you .reborrow() from chosen pins in that array. (might need an array of refcells?). then pass the reborrowed uart_tx/uart_rx into the - // .with_rx or .with_tx? Then when you next reconfigure, you drop the "reborrow" instances and then the borrow checker should let you borrow again. - - // yeah, and if you want it to put them to different uses then you can probably build a map to hold the pins you aren't currently using in a mutex or the like, - // so you can check that the configuration is sensible at runtime. - - // TODO: Probably better use a HashMap as suggested above instead of an array of pins? - let rx_pin_num = &uart_pins[config.lock().await.uart_rx_pin as usize]; - let tx_pin_num = &uart_pins[config.lock().await.uart_tx_pin as usize]; - // Hardware UART setup let uart_config = Config::default().with_rx( RxConfig::default() @@ -146,10 +117,15 @@ async fn uart_task( .with_timeout(1) ); + let config = config.lock().await; + // TODO: Double check how SunsetMutex works: lock/unlock patterns. + let mut uart_rx_pin = uart_pins[config.uart_rx_pin as usize].lock().await; + let mut uart_tx_pin = uart_pins[config.uart_tx_pin as usize].lock().await; + let uart = Uart::new(uart_periph, uart_config) .unwrap() - .with_rx(rx_pin_num) - .with_tx(tx_pin_num) + .with_rx(uart_rx_pin.reborrow()) + .with_tx(uart_tx_pin.reborrow()) .into_async(); // Run the main buffered TX/RX loop From d416aea054c315ab7504cd7d96594fc04085190b Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 9 Jul 2025 19:45:26 +1000 Subject: [PATCH 14/86] Change default SSID string (instead of using server id string) --- src/config.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/config.rs b/src/config.rs index edea949..3d0c9d5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,7 +17,7 @@ use sunset::{ SignKey, }; -use crate::settings::{KEY_SLOTS, SSH_SERVER_ID}; +use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; #[derive(Debug, Clone, PartialEq)] pub struct SSHConfig { @@ -53,7 +53,7 @@ impl SSHConfig { pub fn new() -> Result { let hostkey = SignKey::generate(KeyType::Ed25519, None)?; let wifi_ssid: String<32> = - option_env!("WIFI_SSID").unwrap_or(SSH_SERVER_ID).try_into().trap()?; + option_env!("WIFI_SSID").unwrap_or(DEFAULT_SSID).try_into().trap()?; let wifi_pw: Option> = option_env!("WIFI_PW").map(|s| s.try_into()).transpose().trap()?; let mac = random_mac()?; @@ -66,8 +66,8 @@ impl SSHConfig { wifi_pw, mac, ip4_static: None, - uart_rx_pin: 1, - uart_tx_pin: 2, + uart_rx_pin: 0, + uart_tx_pin: 1, }) } From 517eed8069d95e8ca7511c9d74dffd8f9801fddd Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 9 Jul 2025 19:48:41 +1000 Subject: [PATCH 15/86] When updating espflash to 4.x, esp_bootloader_esp_idf::esp_app_desc() call is now required. The partition checking/handling functionalities might be relevant to @jubeormk1 too, btw --- Cargo.toml | 1 + src/main.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 20f6ed4..cb9fabe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ sha2 = { version = "0.10", default-features = false } digest = { version = "0.10", default-features = false, features = ["rand_core", "subtle"] } embedded-storage-async = "0.4" portable-atomic = "1" +esp-bootloader-esp-idf = "0.1.0" [profile.dev] # Rust debug is too slow. diff --git a/src/main.rs b/src/main.rs index a4a093a..54b0385 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,7 @@ async fn main(spawner: Spawner) -> ! { esp_alloc::heap_allocator!(size: 72 * 1024); } ); + esp_bootloader_esp_idf::esp_app_desc!(); esp_println::logger::init_logger_from_env(); // System init From ff308946afb46ca17b328e8243697bc7ce5e39d5 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 9 Jul 2025 19:50:23 +1000 Subject: [PATCH 16/86] Disable custom partitions for now and SunsetMutex seems to interfere with the UART operation, need to debug further --- .cargo/config.toml | 3 +- Cargo.lock | 236 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- src/main.rs | 4 +- src/settings.rs | 7 +- 5 files changed, 245 insertions(+), 7 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 8dfd732..c02f196 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -24,7 +24,8 @@ rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32"'] runner = "espflash flash --baud=921600 --monitor" rustflags = [ "-C", "force-frame-pointers"] [target.riscv32imac-unknown-none-elf] -runner = "espflash flash --baud=921600 --partition-table partitions.csv --monitor" +#runner = "espflash flash --baud=921600 --partition-table partitions.csv --monitor" +runner = "espflash flash --baud=921600 --monitor" rustflags = [ "-C", "force-frame-pointers"] [target.xtensa-esp32s2-none-elf] runner = "espflash flash --baud=921600 --monitor --chip esp32s2" diff --git a/Cargo.lock b/Cargo.lock index 26f9ffa..7bc5d6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,21 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78200ac3468a57d333cd0ea5dd398e25111194dcacd49208afca95c629a6311d" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.98" @@ -121,6 +136,12 @@ dependencies = [ "cipher", ] +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "bytemuck" version = "1.23.1" @@ -133,6 +154,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cc" +version = "1.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.1" @@ -150,6 +180,18 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-link", +] + [[package]] name = "cipher" version = "0.4.4" @@ -161,6 +203,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -656,6 +704,20 @@ dependencies = [ "semihosting", ] +[[package]] +name = "esp-bootloader-esp-idf" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3cb352a8df9c459d0bdf06957cb8293b8bc574138e8c546949955b29c485769" +dependencies = [ + "chrono", + "document-features", + "embedded-storage", + "esp-config", + "md-5", + "strum", +] + [[package]] name = "esp-build" version = "0.3.0" @@ -1055,6 +1117,30 @@ dependencies = [ "digest", ] +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1105,6 +1191,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.174" @@ -1138,6 +1234,16 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.5" @@ -1218,6 +1324,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -1497,6 +1609,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signature" version = "2.2.0" @@ -1596,6 +1714,7 @@ dependencies = [ "embedded-storage-async", "esp-alloc", "esp-backtrace", + "esp-bootloader-esp-idf", "esp-hal", "esp-hal-embassy", "esp-println", @@ -1847,6 +1966,64 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + [[package]] name = "winapi-util" version = "0.1.9" @@ -1856,6 +2033,65 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.59.0" diff --git a/Cargo.toml b/Cargo.toml index cb9fabe..b618de5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,7 @@ opt-level = "s" opt-level = "s" [features] -default = ["esp32c6"] +#default = ["esp32c6"] # MCU options esp32 = [ "esp-hal/esp32", diff --git a/src/main.rs b/src/main.rs index 54b0385..da2d778 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,8 +90,8 @@ async fn main(spawner: Spawner) -> ! { static UART_PINS: StaticCell>, 2>> = StaticCell::new(); let uart_pins = UART_PINS.init({ let mut pins = Vec::>, 2>::new(); - pins.push(SunsetMutex::new(peripherals.GPIO1.into())).unwrap(); - pins.push(SunsetMutex::new(peripherals.GPIO2.into())).unwrap(); + pins.push(SunsetMutex::new(peripherals.GPIO10.into())).unwrap(); + pins.push(SunsetMutex::new(peripherals.GPIO11.into())).unwrap(); pins }); diff --git a/src/settings.rs b/src/settings.rs index 0744ff5..91ab42e 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -3,9 +3,10 @@ // SSH server settings //pub(crate) const MTU: usize = 1536; //pub(crate) const PORT: u16 = 22; -pub(crate) const SSH_SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; -pub const KEY_SLOTS: usize = 3; // TODO: Document whether this a "reasonable default"? Justify why? -pub const PASSWORD_AUTHENTICATION: bool = true; +pub(crate) const DEFAULT_SSID: &str = "ssh-stamp"; +//pub(crate) const SSH_SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; +pub(crate) const KEY_SLOTS: usize = 3; // TODO: Document whether this a "reasonable default"? Justify why? +//pub(crate) const PASSWORD_AUTHENTICATION: bool = true; // UART settings //pub(crate) const BAUD_RATE: u32 = 115200; From 0d6ee1ab775136e4ecb76ae1b58a9658b42dec2f Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 14 Jul 2025 20:53:13 +1000 Subject: [PATCH 17/86] Fix mutex issues with uart_task and draft PinConfig struct/impl for RX/TX/CTS/RTS... etc.. pin assignment and at-runtime validation et al Co-authored-by: Marko Malenic --- .cargo/config.toml | 2 +- Cargo.toml | 2 +- docs/nlnet/mou.md | 369 +++++++++++++++++++++++++++++++++ docs/nlnet/plan.md | 34 +++ src/config.rs | 31 +++ src/espressif/buffered_uart.rs | 1 + src/espressif/net.rs | 19 +- src/main.rs | 40 ++-- 8 files changed, 477 insertions(+), 21 deletions(-) create mode 100644 docs/nlnet/mou.md create mode 100644 docs/nlnet/plan.md diff --git a/.cargo/config.toml b/.cargo/config.toml index c02f196..1513072 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -42,7 +42,7 @@ ESP_LOG = "INFO" #ESP_HAL_CONFIG_FLIP_LINK=false #ESP_HAL_CONFIG_STACK_GUARD_OFFSET=4096 #ESP_HAL_CONFIG_STACK_GUARD_VALUE=3740121773 -#ESP_HAL_CONFIG_IMPL_CRITICAL_SECTION=ture +#ESP_HAL_CONFIG_IMPL_CRITICAL_SECTION=true diff --git a/Cargo.toml b/Cargo.toml index b618de5..cb9fabe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,7 @@ opt-level = "s" opt-level = "s" [features] -#default = ["esp32c6"] +default = ["esp32c6"] # MCU options esp32 = [ "esp-hal/esp32", diff --git a/docs/nlnet/mou.md b/docs/nlnet/mou.md new file mode 100644 index 0000000..38bc0ea --- /dev/null +++ b/docs/nlnet/mou.md @@ -0,0 +1,369 @@ +takentaal v1.0 + + + +# SSH Stamp + + + +The aim of this project (available at https://github.com/brainstorm/ssh-stamp) is to write software that executes code on a microprocessor and allows transferring data between the internet and various electronic interfaces. In other words: a logic "bridge" between the internet and low level electronic interfaces. + + + +In its conception, the SSH Stamp is a secure bridge between wireless and a serial port (ubiquitous interface also known as "UART"), implemented in the Rust programming language. The Rust programming language offers convenient memory safety guarantees that are meaningful for this project from a systems security perspective. + + + +From a practical standpoint, SSH Stamp allows its users (from different levels of expertise) to audit, monitor, defend, operate, understand and maintain a variety of low level electronics remotely (and securely) through the internet at large. + + + +## {240} Password-based authentication (GH issue #20) + + + +Currently, SSH Stamp accepts ANY arbitrary password when logging in via the SSH protocol. This is a well known and big security issue that requires attention to be put on the password authentication handler. + + + +On the server side, there should be a persistent setting that completely disables password-based authentication (only accepting safer key-based authentication method (see related GH issues #21 and #23)). + + + +- {120} Implement password authentication server-side toggle. + +- {120} Implement handler business logic and connect it with GH issue #23. + + + +## {400} Have the server generate a unique server keypair and store it persistently (on boot) (GH issue #38) + + + +Currently, SSH Stamp accepts any (hardcoded) private key, as implied in the top-level documentation. This is a well known and big security issue that requires attention to be put on the public/private key authentication handler. + + + +Server keys are used by the client to identify the server in a secure way. Ideally those keys should be generated at first boot and optionally re-generated if the user desires to do so. + + + +- {400} Implement in-device private key generation and storage, test and audit its logic. + + + +## {200} Public key-based authentication (GH issue #21) + + + +The user should be able to provide concatenate public keys as "authorized_keys" via environment variable(s) (see related GH issue #23). + + + +- {200} Implement pubkey authentication environment variable provisioning. + + + +## {1000} Provisioning based on (SSH) environment variables (GH issue #23) + + + +Many SSH clients pass environment variables to the server for many purposes. + + + +This task is meant to exploit this commonly used pattern to simplify provisioning of new SSH Stamps. Environment variables will be used to, among other functions: + + + +- {200} Define and persist the UART bridge parameters such as BAUD speed, stop bits. + +- {200} Compile a spreadsheet of commercially available "stamp boards" and set a default, unused UART physical pins pair. Allow the user to alter that default via SSH env settings (see GH issue #23). + +- {200} Pass the password and store it for subsequent sessions, disallowing un-authenticated password changes after first initialization. + +- {200} Pass the private key(s) and/or trigger in-device private key generation. + +- {200} Have an environment variable (or command) that instructs the processor to factory reset all non-volatile storage but keeps the firmware intact. + + + +This approach avoids having complicated device-programming-time deployment(s) of sensitive cryptographic material. + + + +Furthermore, provisioning the device this way yields a superior user experience: a device can be shipped on an unprovisioned state and subsequently be **NON INTERACTIVELY** provisioned via SSH env vars. + + + +The emphasis on non-interactivity (and idempotency) is due to time tested patterns on the IT automation industry (see: Ansible, Puppet, Chef, SaltStack, etc...). + + + +## {1700} Firmware update via SFTP (GH issue #24) + + + +Instead of regular "out of band" firmware OTA update mechanisms, SSH Stamp firmware will be updated via its own secure protocol: SCP/SFTP. + + + +The reason behind this approach is that we can leverage the existing SSH server AAA mechanisms to deploy new firmware securely. + + + +Ideally, this process shouldn't have to involve the bootloader since this should happen at a "userspace + reboot" level. + + + +- {1300} Implement SCP/SFTP as per IETF RFCs. Only the functionality needed to support firmware update. + +- {300} Implement OTA mechanism on device. + +- {100} Audit and thoroughly check that new firmware cannot be altered pre-auth. + + + +The changes described above will most likely need to be applied to SSH Stamp's main current "core" dependency: "sunset". This will therefore constitute an external (to SSH Stamp) OSS contribution that will be used in our software. + + + +## {2000} FSMs + SansIO refactor (GH issue #25) + + + +This is a relatively big refactor but needed pre-requisite to introduce other low level I/O (protocols) beside UART such as SPI/I2C/CAN... it is also a research task since there's uncertainty on whether such a bridge between a (relatively slow) network stack and (generally faster) electrical protocols will tolerate strict timing demands. + + + +Moving to compile-time validated finite state machines (FSMs) will hopefully make pre-auth attacks less likely to be successful on SSH Stamp. Such attacks have been unfortunately common in recent SSH server explorations, see: + + + +https://threatprotect.qualys.com/2025/04/21/erlang-otp-ssh-server-remote-code-execution-vulnerability-cve-2025-32433/ + + + +https://www.runzero.com/blog/sshamble-unexpected-exposures-in-the-secure-shell/ + + + +The trigger of this refactoring idea is https://www.firezone.dev/blog/sans-io + + + +This approach should ideally be combined with some kind of performance profiling to ensure we don't regress after incorporating those changes. + + + +- {1100} Implement a PoC state machine on std outside SSH Stamp, validate corner cases there. + +- {500} Adapt PoC for no_std usage and structure. + +- {400} Refactor codebase for SansIO + FSM. + + + +## {900} General performance engineering (GH issue #28) + + + +Profiling and optimisation are tangentially mentioned towards the end of issue #25, but better tooling/process is needed to maximise the performance of this firmware on different fronts: + + + +- {100} Choose appropriate overall and per-task heap size allocations, i.e: bisect heap size until finding a clear performance degradation under different loads. + +- {100} Perform benchmarks for different RX/TX buffer sizes on both SSH network stack and UART packet queues. + +- {100} Minimise the build for size as much as possible without discarding security checks (overflow protections). + +- {300} Add CI/CD instrumentation to track all of the above over time, avoiding future regressions. + +- {300} Profile hot spots across the app, including cryptographic primitives, and optimise them. + + + +Those performance engineering techniques should be appropriately documented (preferably with examples) and ideally implemented in CI to monitor regressions. + + + +## {1000} Documentation (GH issue #30) + + + +Mid project, when the code based reaches relative maturity and stability, we should make a concerted effort to write and review docs at all levels: user, dev, architecture.md + + + +- {200} Make sure onboarding (and quickstart) is fully documented and flawless. + +- {200} Refer and implement an ARCHITECTURE.md document. Advice taken from Matklad's blogpost: https://matklad.github.io/2021/02/06/ARCHITECTURE.md.html + +- {120} List and document all the effects of environment variables from GH issue #23. + +- {120} List and document both supported and unsupported devices and boards. + +- {120} List and document ways to debug/profile/tweak firmware for different feature flags and scenarios. + +- {120} Have at least one real world scenario documented. + +- {120} Define clearly what's our threat model, i.e: we'll not protect against any physical attacks. OTOH, pre-auth attacks (exploit bad code logic that Rust doesn't protect against) can be the worst case scenario for SSH Stamp. + + + +## {600} Testing (GH issue #37) + + + +Test that the firmware compiles, has good coverage and there are no performance regressions as mentioned in other tasks. This task is a more focused effort so that there is comprehensive testing in both software and hardware. + + + +- {200} Leverage embedded-test Rust crate for hardware testing (HIL). + +- {200} Physically build a test harness with most of the supported targets so that they can be easily tested (ideally via CI/CD hooks). + +- {200} Test that a IPV6 address is served over DHCPv6. ULA, DHCP-PD and other common scenarios work as they should. + + + +## {500} Compile project for all Espressif ESP32 WiFi targets (GH issue #18) + + + +The current project only compiles for the ESP32-C6, as in indicated in various feature flags and linker options. + + + +Find a non-intrusive way to compile for as many Espressif targets as possible. + + + +- {400} Implement cargo build targets for each Espressif microcontroller. + +- {100} Resolve compilation issues that arise for each target. + + + +## {800} Implement firmware support for alternative wifi enabled microcontrollers (other than Espressif) (GH issue #19) + + + +Implementing and testing support for other microcontrollers from a completely different manufacturer. + + + +- {200} Compile and flash SSH Stamp into this target with UART support. + +- {600} Try to support WiFi (currently not supported at the upstream HAL). + + + +## {2000} Add mlkem768x25519 (to sunset?) and integrate into SSH-Stamp (GH issue #34) + + + +Add mlkem768x25519 (post Quantum resistant crypto) to sunset dependency and integrate into SSH-Stamp. + + + +- {1500} Implement and test MLKEM upstream in sunset. + +- {500} Test in SSH Stamp. + + + +## {1080} Bootloader audit and UX improvements (GH issue #35) + + + +Some users will expect that the bridge is established against their own dev board USB2TTL converted UART0 port instead of custom discrete pins. + + + +The current approach is to use UART1 and custom pins (soon to be GPIO9 and GPIO10 by default) so that boot messages and early SSH2UART are not intermixed. + +This task will have to: + + + +- {600} Explore how the default-shipped ESP-IDF 5.x bootloader is packaged in esp-hal's firmware. + +- {240} Conditionally modify the bootloader accordingly so no boot messages are displayed when powering up the board and no println!/debug messages from the user level firmware itself are shown either. In other words: Only bridged SSH2UART messages should be conveyed. + +- {120} Make sure those bootloader modifications are not an impediment to basic operations such as re-flashing the IC (i.e: perhaps espflash expects some bootloader output to trigger a flashing operation?). + +- {120} Document a path towards a secure bootloader that is compatible with the HAL/device **and** respects the aforementioned points. + + + +This exploration into the bootloader inner workings can also pave the way to assess whether improvements in secure boot are interesting to pursue and how. + + + +## {840} Implement WiFi STA mode (GH issue #36) + + + +Currently SSH Stamp boots up in AP (Access Point) mode and expects DHCP clients. This tasks would put SSH Stamp in STA (Station mode) or acting as a WiFi client instead. + + + +This is attractive on more permanent installations where SSH Stamp can be part of a larger local wireless network. + + + +- {240} Make appropriate changes to device HAL initialization. + +- {240} Verify that those changes do not break prior assumptions. + +- {240} Align onboarding process for that mode's particularities. + +- {120} Make the setting configurable (see GH issue #23). + + + +## {200} Licensing (GH issue #22) + + + +There's some borrowed code from SSH Stamp's main dependency: sunset. Clarify this properly and/or find the right way to compose LICENSE wording. + + + +- {200} Process feedback and apply changes from NLNet licensing experts. + + + +## {2000} Implement bridging support for other electrical protocols such as: CAN, SPI and I2C. + + + +Encapsulate transaction-oriented protocols (CAN, SPI, I2C) over a plain byte stream. This task assumes that the the processor's timing characteristics are suitable. At a protocol level, for CAN packet encapsulation there's "slcan" and "GVRET" among other more exotic encapsulation protocols. + + + +Equivalents for SPI-over-Network or I2C-over-Network are a much more experimental and uncharted territory, perhaps the Bus Pirate command protocol would be the closest match. + + + +- {1000} Implement slcan and/or GVRET for SSH_to_CAN support. + +- {1000} Investigate and implement proof of concept for SPI and/or I2C. + + + +## {600} Final release + + + +Final review on all the work done and refinement, optimization and security audit handover. + + + +- {120} Stable release. + +- {240} Process feedback from security audit and accessibility scan. + +- {240} Final release with updated documentation. diff --git a/docs/nlnet/plan.md b/docs/nlnet/plan.md new file mode 100644 index 0000000..989acea --- /dev/null +++ b/docs/nlnet/plan.md @@ -0,0 +1,34 @@ +```mermaid +gantt + title SSH Stamp (a.k.a esp-ssh-rs) development plan under NLNet grant + dateFormat YYYY-MM-DD + excludes weekends + tickInterval 1day + weekday monday + todayMarker off + axisFormat %e + section Production + Fix password auth : passwd, 2025-05-01, 24h + Fix pubkey auth : pubkey, after passwd, 24h + UART perf : uart_intr, after pubkey, 12h + section Provisioning + Provisioning : prov, after uart_intr, 16h + OTA updates : ota, after prov, 12h + section Docs + usage docs : usage_docs, after ota, 4h + dev docs : dev_docs, after ota, 2h + section Robustness + #forbid(unsafe) : no_unsafe, after ota, 12h + sans-io refactor : sans_io, after uart_intr, 16h + section Multi-target + Espressif chips : all_espressif, after no_unsafe, 12h + Other chip1 : chip1, after all_espressif, 20h + Other chip2 : chip2, after chip1, 18h + section Testing + CI/CD : ci, after chip2, 16h + hardware in test loop : HIL, after ci, 21h + Users test : user_tests, after dev_docs, 9h + section Security + Self audit : self_sec_audit, after all_espressif, 10h + NLNet security audit? : nlnet_sec_audit, after all_espressif, 45h +``` diff --git a/src/config.rs b/src/config.rs index 3d0c9d5..33d61ca 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ use core::net::Ipv4Addr; use embassy_net::{Ipv4Cidr, StaticConfigV4}; +use esp_hal::gpio::AnyPin; use heapless::{String, Vec}; use esp_println::println; @@ -16,6 +17,7 @@ use sunset::{ sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, SignKey, }; +use sunset_async::SunsetMutex; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; @@ -41,6 +43,35 @@ pub struct SSHConfig { /// UART pub uart_rx_pin: u8, pub uart_tx_pin: u8, + + // 10 + pub tx: u8, + // 11 + pub rx: u8, + pub rts: Option, + pub cts: Option, +} + +// map! { +// 10 => peripheral.GPIO11, +// 11 => peripheral.GPI010, +// } + +struct PinConfig { + +} + +impl PinConfig { + pub fn into_rx_pin() -> SunsetMutex> { + + let pin_number = 11; + match pin_number { + 11 => peripheral.GPIO11, + 10 => peripheral.GPIO10, + } + + todo!() + } } impl SSHConfig { diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index 71b48d4..3dbcfcd 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -51,6 +51,7 @@ impl BufferedUart { let rd_from = async { loop { let n = uart_rx.read_async(&mut uart_rx_buf).await.unwrap(); + let mut rx_slice = &uart_rx_buf[..n]; // Write rx_slice to 'inward' pipe, dropping bytes rather than blocking if diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 6ec5806..9df84f2 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -52,11 +52,14 @@ pub async fn if_up( let gw_ip_addr_ipv4 = Ipv4Addr::from_str("192.168.0.1").expect("failed to parse gateway ip"); - let _gw_ip_addr = if let Some(ref s) = config.lock().await.ip4_static { - embassy_net::Config::ipv4_static(s.clone()) - } else { - embassy_net::Config::dhcpv4(Default::default()) - }; + // let _gw_ip_addr = { + // let guard = config.lock().await; + // if let Some(ref s) = guard.ip4_static { + // embassy_net::Config::ipv4_static(s.clone()) + // } else { + // embassy_net::Config::dhcpv4(Default::default()) + // } + // }; let net_config = embassy_net::Config::ipv4_static(StaticConfigV4 { address: Ipv4Cidr::new(gw_ip_addr_ipv4, 24), @@ -129,7 +132,11 @@ pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { async fn wifi_up(mut controller: WifiController<'static>, config: &'static SunsetMutex) { println!("Device capabilities: {:?}", controller.capabilities()); - let wifi_ssid = &config.lock().await.wifi_ssid; + let wifi_ssid = { + let guard = config.lock().await; + guard.wifi_ssid.clone() + // drop guard + }; // TODO: No wifi password(s) yet... //let wifi_password = config.lock().await.wifi_pw; diff --git a/src/main.rs b/src/main.rs index da2d778..aa0451c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use esp_hal::{ }; use esp_hal_embassy::InterruptExecutor; +use esp_println::dbg; use esp_storage::FlashStorage; use embassy_executor::Spawner; use ssh_stamp::{config::SSHConfig, espressif::{ @@ -20,6 +21,13 @@ use static_cell::StaticCell; use sunset_async::SunsetMutex; use heapless::Vec; +pub struct PinConfig { + pub tx: SunsetMutex>, + pub rx: SunsetMutex>, + pub rts: Option>>, + pub cts: Option>>, +} + #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { cfg_if::cfg_if!( @@ -87,16 +95,18 @@ async fn main(spawner: Spawner) -> ! { // Potential pins to use for such UART, to be owned by uart_task. // TODO: Unsure if that's what was referred in the conversations below... - static UART_PINS: StaticCell>, 2>> = StaticCell::new(); + static UART_PINS: StaticCell = StaticCell::new(); let uart_pins = UART_PINS.init({ - let mut pins = Vec::>, 2>::new(); - pins.push(SunsetMutex::new(peripherals.GPIO10.into())).unwrap(); - pins.push(SunsetMutex::new(peripherals.GPIO11.into())).unwrap(); - pins + PinConfig { + tx: SunsetMutex::new(map[config.tx].into()), + rx: SunsetMutex::new(map[config.rx].into()), + rts: None, + cts: None, + } }); // Use the same config reference for UART task. - interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins, config)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins)).unwrap(); accept_requests(tcp_stack, uart_buf).await; } @@ -108,8 +118,8 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - uart_pins: &'static Vec>, 2>, - config: &'static SunsetMutex + uart_pins: &'static PinConfig, + // config: &'static SunsetMutex ) { // Hardware UART setup let uart_config = Config::default().with_rx( @@ -117,11 +127,15 @@ async fn uart_task( .with_fifo_full_threshold(16) .with_timeout(1) ); - - let config = config.lock().await; - // TODO: Double check how SunsetMutex works: lock/unlock patterns. - let mut uart_rx_pin = uart_pins[config.uart_rx_pin as usize].lock().await; - let mut uart_tx_pin = uart_pins[config.uart_tx_pin as usize].lock().await; + + // let (rx_idx, tx_idx) = { + // let guard = config.lock().await; + // (guard.uart_rx_pin, guard.uart_tx_pin) + // }; + + let mut uart_rx_pin = uart_pins.rx.lock().await; + let mut uart_tx_pin = uart_pins.tx.lock().await; + dbg!(&uart_rx_pin); let uart = Uart::new(uart_periph, uart_config) .unwrap() From a0bebbdac19125fb4763033344b046c4094678b8 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 16 Jul 2025 21:24:59 +1000 Subject: [PATCH 18/86] Complex types such as Mutex complicate the serialization/deserialization "on the wire" of SSHConfig, unfortunately /cc @mmalenic --- src/config.rs | 65 +++++++++++++++++++++++++++------------------------ src/main.rs | 14 +---------- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/config.rs b/src/config.rs index 33d61ca..bc839f9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -21,7 +21,7 @@ use sunset_async::SunsetMutex; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug)] pub struct SSHConfig { pub hostkey: SignKey, @@ -41,36 +41,30 @@ pub struct SSHConfig { pub ip4_static: Option, /// UART - pub uart_rx_pin: u8, - pub uart_tx_pin: u8, - - // 10 - pub tx: u8, - // 11 - pub rx: u8, - pub rts: Option, - pub cts: Option, + pub uart_pins: PinConfig, } -// map! { -// 10 => peripheral.GPIO11, -// 11 => peripheral.GPI010, -// } - -struct PinConfig { - +#[derive(Debug)] +pub struct PinConfig { + pub tx: SunsetMutex>, + pub rx: SunsetMutex>, + pub rts: Option>>, + pub cts: Option>>, } impl PinConfig { - pub fn into_rx_pin() -> SunsetMutex> { - let pin_number = 11; - match pin_number { - 11 => peripheral.GPIO11, - 10 => peripheral.GPIO10, - } +} - todo!() +// TODO: Revisit this and compare them with esp-hal examples, see what they use for their HIL nowadays. +impl Default for PinConfig { + fn default() -> Self { + PinConfig { + rx: 10, + tx: 11, + rts: None, + cts: None, + } } } @@ -88,6 +82,9 @@ impl SSHConfig { let wifi_pw: Option> = option_env!("WIFI_PW").map(|s| s.try_into()).transpose().trap()?; let mac = random_mac()?; + + let uart_pins = PinConfig::default(); + Ok(SSHConfig { hostkey, password_authentication: true, @@ -97,8 +94,7 @@ impl SSHConfig { wifi_pw, mac, ip4_static: None, - uart_rx_pin: 0, - uart_tx_pin: 1, + uart_pins, }) } @@ -191,6 +187,17 @@ where .transpose() } +fn dec_uart_pins<'de, S>(s: &mut S) -> WireResult +where + S: SSHSource<'de>, +{ + let tx = u8::dec(s)?; + let rx = u8::dec(s)?; + let rts = dec_option(s)?; + let cts = dec_option(s)?; + Ok(PinConfig { tx, rx, rts, cts }) +} + impl SSHEncode for SSHConfig { fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { println!("enc si"); @@ -245,8 +252,7 @@ impl<'de> SSHDecode<'de> for SSHConfig { // Decode password_authentication (missing in original code) let password_authentication = SSHDecode::dec(s)?; - let uart_rx_pin = SSHDecode::dec(s)?; - let uart_tx_pin = SSHDecode::dec(s)?; + let uart_pins = dec_uart_pins(s)?; Ok(Self { hostkey, @@ -257,8 +263,7 @@ impl<'de> SSHDecode<'de> for SSHConfig { wifi_pw, mac, ip4_static, - uart_rx_pin, - uart_tx_pin + uart_pins, }) } } diff --git a/src/main.rs b/src/main.rs index aa0451c..1d0c5d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,13 +20,6 @@ use ssh_stamp::{config::SSHConfig, espressif::{ use static_cell::StaticCell; use sunset_async::SunsetMutex; use heapless::Vec; - -pub struct PinConfig { - pub tx: SunsetMutex>, - pub rx: SunsetMutex>, - pub rts: Option>>, - pub cts: Option>>, -} #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { @@ -97,12 +90,7 @@ async fn main(spawner: Spawner) -> ! { // TODO: Unsure if that's what was referred in the conversations below... static UART_PINS: StaticCell = StaticCell::new(); let uart_pins = UART_PINS.init({ - PinConfig { - tx: SunsetMutex::new(map[config.tx].into()), - rx: SunsetMutex::new(map[config.rx].into()), - rts: None, - cts: None, - } + PinConfig::default(); }); // Use the same config reference for UART task. From 24c009ed218e716021543176e690a183cfea6438 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 16 Jul 2025 21:25:10 +1000 Subject: [PATCH 19/86] Cargo.lock --- Cargo.lock | 97 ++++++++++++------------------------------------------ 1 file changed, 21 insertions(+), 76 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9001b97..f83aab1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -246,9 +246,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "373b7c5dbd637569a2cca66e8d66b8c446a1e7bf064ea321d265d7b3dfe7c97e" dependencies = [ "cfg-if", "cpufeatures", @@ -308,9 +308,9 @@ dependencies = [ [[package]] name = "delegate" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b6483c2bbed26f97861cf57651d4f2b731964a28cd2257f934a4b452480d21" +checksum = "6178a82cf56c836a3ba61a7935cdb1c49bfaa6fa4327cd5bf554a503087de26b" dependencies = [ "proc-macro2", "quote", @@ -348,9 +348,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", @@ -538,9 +538,12 @@ dependencies = [ [[package]] name = "embassy-usb-driver" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970" +checksum = "340c5ce591ef58c6449e43f51d2c53efe1bf0bb6a40cbf80afa0d259c7d52c76" +dependencies = [ + "embedded-io-async", +] [[package]] name = "embassy-usb-synopsys-otg" @@ -650,18 +653,18 @@ dependencies = [ [[package]] name = "enumset" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6b7c3d347de0a9f7bfd2f853be43fe32fa6fac30c70f6d6d67a1e936b87ee" +checksum = "d6ee17054f550fd7400e1906e2f9356c7672643ed34008a9e8abe147ccd2d821" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da3ea9e1d1a3b1593e15781f930120e72aa7501610b2f82e5b6739c72e8eac5" +checksum = "76d07902c93376f1e96c34abc4d507c0911df3816cef50b01f5a2ff3ad8c370d" dependencies = [ "darling", "proc-macro2", @@ -995,9 +998,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "fnv" @@ -1252,9 +1255,9 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "minijinja" -version = "2.10.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd72e8b4e42274540edabec853f607c015c73436159b06c39c7af85a20433155" +checksum = "4e60ac08614cc09062820e51d5d94c2fce16b94ea4e5003bb81b99a95f84e876" dependencies = [ "serde", ] @@ -2024,64 +2027,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - [[package]] name = "winapi-util" version = "0.1.9" @@ -2225,9 +2170,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] From 7f9f399da9b30c05888015993740e97649592baf Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Sat, 19 Jul 2025 11:20:38 +1000 Subject: [PATCH 20/86] [ci skip] Working through PinConfig types issues, small cleanup --- src/config.rs | 26 ++++++++++++++------------ src/main.rs | 8 +------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/config.rs b/src/config.rs index bc839f9..3686de1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,7 @@ use core::net::Ipv4Addr; use embassy_net::{Ipv4Cidr, StaticConfigV4}; use esp_hal::gpio::AnyPin; +use esp_hal::peripherals::{GPIO, GPIO0, GPIO1}; use heapless::{String, Vec}; use esp_println::println; @@ -53,15 +54,25 @@ pub struct PinConfig { } impl PinConfig { - + /// Resolves a u8 pin number into an AnyPin GPIO type. + /// Returns None if the pin number is invalid or unsupported. + pub fn resolve_pin(pin_num: u8) -> Option> { + match pin_num { + 0 => Some(AnyPin::GPIO0), + // (...) + _ => None + } + } } // TODO: Revisit this and compare them with esp-hal examples, see what they use for their HIL nowadays. impl Default for PinConfig { fn default() -> Self { + let rx = SunsetMutex::new(PinConfig::resolve_pin(10).expect("Invalid RX pin")); + let tx = SunsetMutex::new(PinConfig::resolve_pin(11).expect("Invalid TX pin")); PinConfig { - rx: 10, - tx: 11, + rx, + tx, rts: None, cts: None, } @@ -202,15 +213,6 @@ impl SSHEncode for SSHConfig { fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { println!("enc si"); enc_signkey(&self.hostkey, s)?; - - // enc_option(&self.console_pw, s)?; - - // for k in self.console_keys.iter() { - // enc_option(k, s)?; - // } - - // self.console_noauth.enc(s)?; - enc_option(&self.admin_pw, s)?; for k in self.admin_keys.iter() { diff --git a/src/main.rs b/src/main.rs index 1d0c5d6..7d3cba3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ use heapless::Vec; async fn main(spawner: Spawner) -> ! { cfg_if::cfg_if!( if #[cfg(feature = "esp32s2")] { - // TODO: This heap size will crash at runtime, we need to fix this + // TODO: This heap size will crash at runtime (only for the ESP32S2), we need to fix this // applying ideas from https://github.com/brainstorm/ssh-stamp/pull/41#issuecomment-2964775170 esp_alloc::heap_allocator!(size: 69 * 1024); } else { @@ -107,7 +107,6 @@ async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, uart_pins: &'static PinConfig, - // config: &'static SunsetMutex ) { // Hardware UART setup let uart_config = Config::default().with_rx( @@ -116,11 +115,6 @@ async fn uart_task( .with_timeout(1) ); - // let (rx_idx, tx_idx) = { - // let guard = config.lock().await; - // (guard.uart_rx_pin, guard.uart_tx_pin) - // }; - let mut uart_rx_pin = uart_pins.rx.lock().await; let mut uart_tx_pin = uart_pins.tx.lock().await; dbg!(&uart_rx_pin); From 6be7ce22572bb27b2847aded45f96e3e25280691 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 21 Jul 2025 19:48:38 +1000 Subject: [PATCH 21/86] Serializable type on Pinconfig. Introduced custom Errors Introduced env vars and started refactoring accordingly. Config and peripherals are borrow checker challenges. Co-authored-by: Marko Malenic --- Cargo.lock | 1 + Cargo.toml | 1 + src/config.rs | 129 ++++++++++++++++++++++++++++++++++++++------------ src/errors.rs | 10 ++++ src/lib.rs | 1 + src/main.rs | 23 +++++---- 6 files changed, 127 insertions(+), 38 deletions(-) create mode 100644 src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index f83aab1..0f0ae90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1731,6 +1731,7 @@ dependencies = [ "portable-atomic", "sha2", "smoltcp", + "snafu", "ssh-key", "static_cell", "subtle", diff --git a/Cargo.toml b/Cargo.toml index 25ac84f..5a190ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ digest = { version = "0.10", default-features = false, features = ["rand_core", embedded-storage-async = "0.4" portable-atomic = "1" esp-bootloader-esp-idf = "0.1" +snafu = { version = "0.8.6", default-features = false } [profile.dev] # Rust debug is too slow. diff --git a/src/config.rs b/src/config.rs index 3686de1..b34a255 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use core::net::Ipv4Addr; use embassy_net::{Ipv4Cidr, StaticConfigV4}; use esp_hal::gpio::AnyPin; -use esp_hal::peripherals::{GPIO, GPIO0, GPIO1}; +use esp_hal::peripherals::Peripherals; use heapless::{String, Vec}; use esp_println::println; @@ -20,6 +20,7 @@ use sunset::{ }; use sunset_async::SunsetMutex; +use crate::errors; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; #[derive(Debug)] @@ -42,43 +43,113 @@ pub struct SSHConfig { pub ip4_static: Option, /// UART - pub uart_pins: PinConfig, + pub uart_pins: SerdePinConfig, } -#[derive(Debug)] -pub struct PinConfig { - pub tx: SunsetMutex>, - pub rx: SunsetMutex>, - pub rts: Option>>, - pub cts: Option>>, +#[derive(Debug, Clone)] +pub struct SerdePinConfig { + pub tx: u8, + pub rx: u8, + pub rts: Option, + pub cts: Option, } -impl PinConfig { - /// Resolves a u8 pin number into an AnyPin GPIO type. - /// Returns None if the pin number is invalid or unsupported. - pub fn resolve_pin(pin_num: u8) -> Option> { - match pin_num { - 0 => Some(AnyPin::GPIO0), - // (...) - _ => None +impl Default for SerdePinConfig { + fn default() -> Self { + Self { + tx: 10, + rx: 11, + // tx: env!("SSH_STAMP_TX_PIN").parse().unwrap(), + // rx: env!("SSH_STAMP_RX_PIN").parse().unwrap(), + rts: option_env!("SSH_STAMP_RTS").map(|s| s.parse().unwrap()), + cts: option_env!("SSH_STAMP_CTS").map(|s| s.parse().unwrap()), } } } -// TODO: Revisit this and compare them with esp-hal examples, see what they use for their HIL nowadays. -impl Default for PinConfig { - fn default() -> Self { - let rx = SunsetMutex::new(PinConfig::resolve_pin(10).expect("Invalid RX pin")); - let tx = SunsetMutex::new(PinConfig::resolve_pin(11).expect("Invalid TX pin")); - PinConfig { - rx, - tx, - rts: None, - cts: None, +pub struct PinConfig<'a> { + pub pin_config_inner: SerdePinConfig, + pub peripherals: &'a mut Peripherals, +} + +impl<'a> PinConfig<'a> { + pub fn new(peripherals: &'a mut Peripherals, pin_config_inner: SerdePinConfig) -> Self { + Self { + pin_config_inner, + peripherals + } + } + + pub fn tx(&mut self) -> errors::Result>> { + Ok(SunsetMutex::new(Self::resolve_pin(self.pin_config_inner.tx, self.peripherals)?)) + } + + pub fn rx(&mut self) -> errors::Result>> { + Ok(SunsetMutex::new(Self::resolve_pin(self.pin_config_inner.rx, self.peripherals)?)) + } + + pub fn rts(&mut self) -> errors::Result>>> { + self.pin_config_inner.rts.map(|rts| Ok(SunsetMutex::new(Self::resolve_pin(rts, self.peripherals)?))).transpose() + } + + pub fn cts(&mut self) -> errors::Result>>> { + self.pin_config_inner.cts.map(|cts| Ok(SunsetMutex::new(Self::resolve_pin(cts, self.peripherals)?))).transpose() + } + + /// Resolves a u8 pin number into an AnyPin GPIO type. + /// Returns None if the pin number is invalid or unsupported. + pub fn resolve_pin(pin_num: u8, peripherals: &mut Peripherals) -> errors::Result> { + match pin_num { + 0 => Ok(peripherals.GPIO0.reborrow().into()), + 1 => Ok(peripherals.GPIO1.reborrow().into()), + 2 => Ok(peripherals.GPIO2.reborrow().into()), + 3 => Ok(peripherals.GPIO3.reborrow().into()), + 4 => Ok(peripherals.GPIO4.reborrow().into()), + 5 => Ok(peripherals.GPIO5.reborrow().into()), + 6 => Ok(peripherals.GPIO6.reborrow().into()), + 7 => Ok(peripherals.GPIO7.reborrow().into()), + 8 => Ok(peripherals.GPIO8.reborrow().into()), + 9 => Ok(peripherals.GPIO9.reborrow().into()), + 10 => Ok(peripherals.GPIO10.reborrow().into()), + 11 => Ok(peripherals.GPIO11.reborrow().into()), + 12 => Ok(peripherals.GPIO12.reborrow().into()), + 13 => Ok(peripherals.GPIO13.reborrow().into()), + 14 => Ok(peripherals.GPIO14.reborrow().into()), + 15 => Ok(peripherals.GPIO15.reborrow().into()), + 16 => Ok(peripherals.GPIO16.reborrow().into()), + 17 => Ok(peripherals.GPIO17.reborrow().into()), + 18 => Ok(peripherals.GPIO18.reborrow().into()), + 19 => Ok(peripherals.GPIO19.reborrow().into()), + 20 => Ok(peripherals.GPIO20.reborrow().into()), + 21 => Ok(peripherals.GPIO21.reborrow().into()), + 22 => Ok(peripherals.GPIO22.reborrow().into()), + 23 => Ok(peripherals.GPIO23.reborrow().into()), + 24 => Ok(peripherals.GPIO24.reborrow().into()), + 25 => Ok(peripherals.GPIO25.reborrow().into()), + 26 => Ok(peripherals.GPIO26.reborrow().into()), + 27 => Ok(peripherals.GPIO27.reborrow().into()), + 28 => Ok(peripherals.GPIO28.reborrow().into()), + 29 => Ok(peripherals.GPIO29.reborrow().into()), + 30 => Ok(peripherals.GPIO30.reborrow().into()), + _ => Err(errors::Error::InvalidPin), } } } +// // TODO: Revisit this and compare them with esp-hal examples, see what they use for their HIL nowadays. +// impl Default for PinConfig { +// fn default() -> Self { +// let rx = SunsetMutex::new(PinConfig::resolve_pin(10).expect("Invalid RX pin")); +// let tx = SunsetMutex::new(PinConfig::resolve_pin(11).expect("Invalid TX pin")); +// PinConfig { +// rx, +// tx, +// rts: None, +// cts: None, +// } +// } +// } + impl SSHConfig { /// Bump this when the format changes pub const CURRENT_VERSION: u8 = 6; @@ -94,7 +165,7 @@ impl SSHConfig { option_env!("WIFI_PW").map(|s| s.try_into()).transpose().trap()?; let mac = random_mac()?; - let uart_pins = PinConfig::default(); + let uart_pins = SerdePinConfig::default(); Ok(SSHConfig { hostkey, @@ -198,7 +269,7 @@ where .transpose() } -fn dec_uart_pins<'de, S>(s: &mut S) -> WireResult +fn dec_uart_pins<'de, S>(s: &mut S) -> WireResult where S: SSHSource<'de>, { @@ -206,7 +277,7 @@ where let rx = u8::dec(s)?; let rts = dec_option(s)?; let cts = dec_option(s)?; - Ok(PinConfig { tx, rx, rts, cts }) + Ok(SerdePinConfig { tx, rx, rts, cts }) } impl SSHEncode for SSHConfig { diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..65c498b --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,10 @@ +use snafu::Snafu; +use core::result; + +pub type Result = result::Result; + +#[derive(Debug, Snafu)] +pub enum Error { + #[snafu(display("Invalid PIN provided"))] + InvalidPin, +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index f059835..fea262a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,3 +10,4 @@ pub mod serial; pub mod serve; pub mod settings; pub mod storage; +pub mod errors; diff --git a/src/main.rs b/src/main.rs index 7d3cba3..81261d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use ssh_stamp::{config::SSHConfig, espressif::{ }, storage::Fl}; use static_cell::StaticCell; use sunset_async::SunsetMutex; -use heapless::Vec; +use ssh_stamp::config::PinConfig; #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { @@ -84,17 +84,22 @@ async fn main(spawner: Spawner) -> ! { } } // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality - let uart1 = peripherals.UART1; + // let uart1 = peripherals.UART1; + + let pin_config = { + let guard = config.lock().await; + guard.uart_pins.clone() + }; // Potential pins to use for such UART, to be owned by uart_task. // TODO: Unsure if that's what was referred in the conversations below... static UART_PINS: StaticCell = StaticCell::new(); let uart_pins = UART_PINS.init({ - PinConfig::default(); + PinConfig::new(&mut peripherals, pin_config) }); // Use the same config reference for UART task. - interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, uart_pins)).unwrap(); accept_requests(tcp_stack, uart_buf).await; } @@ -105,8 +110,8 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); #[embassy_executor::task()] async fn uart_task( buffer: &'static BufferedUart, - uart_periph: UART1<'static>, - uart_pins: &'static PinConfig, + // uart_periph: UART1<'static>, + uart_pins: &'static PinConfig<'static>, ) { // Hardware UART setup let uart_config = Config::default().with_rx( @@ -115,9 +120,9 @@ async fn uart_task( .with_timeout(1) ); - let mut uart_rx_pin = uart_pins.rx.lock().await; - let mut uart_tx_pin = uart_pins.tx.lock().await; - dbg!(&uart_rx_pin); + // let mut uart_rx_pin = uart_pins.rx.lock().await; + // let mut uart_tx_pin = uart_pins.tx.lock().await; + // dbg!(&uart_rx_pin); let uart = Uart::new(uart_periph, uart_config) .unwrap() From 6630c6485912cd5c48b344ce5714097a6b516bed Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 28 Jul 2025 20:43:21 +1000 Subject: [PATCH 22/86] [ci skip] Testing/refactoring idea proposed in https://github.com/brainstorm/ssh-stamp/pull/44#discussion_r2232536257... PinConfig no longer needs a new() method but OTOH resolve_pin() requires a bit more rework (or remove it altogether?) --- src/config.rs | 120 ++++++++++++++++++++++++++++++++------------------ src/main.rs | 16 +++---- 2 files changed, 84 insertions(+), 52 deletions(-) diff --git a/src/config.rs b/src/config.rs index b34a255..dee79c1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use core::net::Ipv4Addr; use embassy_net::{Ipv4Cidr, StaticConfigV4}; use esp_hal::gpio::AnyPin; -use esp_hal::peripherals::Peripherals; +use esp_hal::peripherals; use heapless::{String, Vec}; use esp_println::println; @@ -67,70 +67,102 @@ impl Default for SerdePinConfig { } } +// TODO: Yikes, this struct and resolve_pin() need to be re-thought for the different ICs and dev boards?.. implementing a suitable +// validation function for them and potentially writing a macro that adapts to each PAC (not all ICs have the same number +// of pins). pub struct PinConfig<'a> { pub pin_config_inner: SerdePinConfig, - pub peripherals: &'a mut Peripherals, + pub gpio0: Option>, + pub gpio1: Option>, + pub gpio2: Option>, + pub gpio3: Option>, + pub gpio4: Option>, + pub gpio5: Option>, + pub gpio6: Option>, + pub gpio7: Option>, + pub gpio8: Option>, + pub gpio9: Option>, + pub gpio10: Option>, + pub gpio11: Option>, + pub gpio12: Option>, + pub gpio13: Option>, + pub gpio14: Option>, + pub gpio15: Option>, + pub gpio16: Option>, + pub gpio17: Option>, + pub gpio18: Option>, + pub gpio19: Option>, + pub gpio20: Option>, + pub gpio21: Option>, + pub gpio22: Option>, + pub gpio23: Option>, + pub gpio24: Option>, + pub gpio25: Option>, + pub gpio26: Option>, + pub gpio27: Option>, + pub gpio28: Option>, + pub gpio29: Option>, + pub gpio30: Option>, } impl<'a> PinConfig<'a> { - pub fn new(peripherals: &'a mut Peripherals, pin_config_inner: SerdePinConfig) -> Self { - Self { - pin_config_inner, - peripherals - } - } + // pub fn new(pin_config_inner: SerdePinConfig) -> Self { + // Self { + // pin_config_inner, + // } + // } pub fn tx(&mut self) -> errors::Result>> { - Ok(SunsetMutex::new(Self::resolve_pin(self.pin_config_inner.tx, self.peripherals)?)) + Ok(SunsetMutex::new(self.resolve_pin(self.pin_config_inner.tx)?)) } pub fn rx(&mut self) -> errors::Result>> { - Ok(SunsetMutex::new(Self::resolve_pin(self.pin_config_inner.rx, self.peripherals)?)) + Ok(SunsetMutex::new(self.resolve_pin(self.pin_config_inner.rx)?)) } pub fn rts(&mut self) -> errors::Result>>> { - self.pin_config_inner.rts.map(|rts| Ok(SunsetMutex::new(Self::resolve_pin(rts, self.peripherals)?))).transpose() + self.pin_config_inner.rts.map(|rts| Ok(SunsetMutex::new(self.resolve_pin(rts)?))).transpose() } pub fn cts(&mut self) -> errors::Result>>> { - self.pin_config_inner.cts.map(|cts| Ok(SunsetMutex::new(Self::resolve_pin(cts, self.peripherals)?))).transpose() + self.pin_config_inner.cts.map(|cts| Ok(SunsetMutex::new(self.resolve_pin(cts)?))).transpose() } /// Resolves a u8 pin number into an AnyPin GPIO type. /// Returns None if the pin number is invalid or unsupported. - pub fn resolve_pin(pin_num: u8, peripherals: &mut Peripherals) -> errors::Result> { + pub fn resolve_pin(&mut self, pin_num: u8) -> errors::Result> { match pin_num { - 0 => Ok(peripherals.GPIO0.reborrow().into()), - 1 => Ok(peripherals.GPIO1.reborrow().into()), - 2 => Ok(peripherals.GPIO2.reborrow().into()), - 3 => Ok(peripherals.GPIO3.reborrow().into()), - 4 => Ok(peripherals.GPIO4.reborrow().into()), - 5 => Ok(peripherals.GPIO5.reborrow().into()), - 6 => Ok(peripherals.GPIO6.reborrow().into()), - 7 => Ok(peripherals.GPIO7.reborrow().into()), - 8 => Ok(peripherals.GPIO8.reborrow().into()), - 9 => Ok(peripherals.GPIO9.reborrow().into()), - 10 => Ok(peripherals.GPIO10.reborrow().into()), - 11 => Ok(peripherals.GPIO11.reborrow().into()), - 12 => Ok(peripherals.GPIO12.reborrow().into()), - 13 => Ok(peripherals.GPIO13.reborrow().into()), - 14 => Ok(peripherals.GPIO14.reborrow().into()), - 15 => Ok(peripherals.GPIO15.reborrow().into()), - 16 => Ok(peripherals.GPIO16.reborrow().into()), - 17 => Ok(peripherals.GPIO17.reborrow().into()), - 18 => Ok(peripherals.GPIO18.reborrow().into()), - 19 => Ok(peripherals.GPIO19.reborrow().into()), - 20 => Ok(peripherals.GPIO20.reborrow().into()), - 21 => Ok(peripherals.GPIO21.reborrow().into()), - 22 => Ok(peripherals.GPIO22.reborrow().into()), - 23 => Ok(peripherals.GPIO23.reborrow().into()), - 24 => Ok(peripherals.GPIO24.reborrow().into()), - 25 => Ok(peripherals.GPIO25.reborrow().into()), - 26 => Ok(peripherals.GPIO26.reborrow().into()), - 27 => Ok(peripherals.GPIO27.reborrow().into()), - 28 => Ok(peripherals.GPIO28.reborrow().into()), - 29 => Ok(peripherals.GPIO29.reborrow().into()), - 30 => Ok(peripherals.GPIO30.reborrow().into()), + 0 => self.gpio0.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 1 => self.gpio1.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 2 => self.gpio2.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 3 => self.gpio3.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 4 => self.gpio4.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 5 => self.gpio5.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 6 => self.gpio6.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 7 => self.gpio7.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 8 => self.gpio8.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 9 => self.gpio9.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 10 => self.gpio10.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 11 => self.gpio11.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 12 => self.gpio12.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 13 => self.gpio13.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 14 => self.gpio14.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 15 => self.gpio15.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 16 => self.gpio16.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 17 => self.gpio17.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 18 => self.gpio18.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 19 => self.gpio19.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 20 => self.gpio20.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 21 => self.gpio21.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 22 => self.gpio22.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 23 => self.gpio23.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 24 => self.gpio24.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 25 => self.gpio25.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 26 => self.gpio26.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 27 => self.gpio27.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 28 => self.gpio28.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 29 => self.gpio29.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + 30 => self.gpio30.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), _ => Err(errors::Error::InvalidPin), } } diff --git a/src/main.rs b/src/main.rs index 81261d4..a305bd7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,10 @@ use core::marker::Sized; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - gpio::AnyPin, interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} + interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} }; use esp_hal_embassy::InterruptExecutor; -use esp_println::dbg; use esp_storage::FlashStorage; use embassy_executor::Spawner; use ssh_stamp::{config::SSHConfig, espressif::{ @@ -84,7 +83,7 @@ async fn main(spawner: Spawner) -> ! { } } // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality - // let uart1 = peripherals.UART1; + let uart1 = peripherals.UART1; let pin_config = { let guard = config.lock().await; @@ -92,14 +91,15 @@ async fn main(spawner: Spawner) -> ! { }; // Potential pins to use for such UART, to be owned by uart_task. - // TODO: Unsure if that's what was referred in the conversations below... - static UART_PINS: StaticCell = StaticCell::new(); + static UART_PINS: StaticCell> = StaticCell::new(); let uart_pins = UART_PINS.init({ - PinConfig::new(&mut peripherals, pin_config) + // TODO: There shouldn't be a new() method at all because that implies initializing all GPIO pins... + // instead we focus on having take() and give() on the config-defined pins. + PinConfig::new(pin_config) }); // Use the same config reference for UART task. - interrupt_spawner.spawn(uart_task(uart_buf, uart_pins)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins)).unwrap(); accept_requests(tcp_stack, uart_buf).await; } @@ -110,7 +110,7 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); #[embassy_executor::task()] async fn uart_task( buffer: &'static BufferedUart, - // uart_periph: UART1<'static>, + uart_periph: UART1<'static>, uart_pins: &'static PinConfig<'static>, ) { // Hardware UART setup From c35a3c6aef1798f31d72efe5684eb41d50d10c7b Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 29 Jul 2025 19:02:44 +1000 Subject: [PATCH 23/86] [ci skip] Big mess, ignore macro-heavy experiment :-S Co-authored-by: Marko Malenic --- Cargo.lock | 1 + Cargo.toml | 1 + src/config.rs | 152 ++++++++++++++++++++++++++++++++++++++++++++++---- src/main.rs | 28 +++++++--- 4 files changed, 164 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f0ae90..d175704 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1728,6 +1728,7 @@ dependencies = [ "hex", "hmac", "log", + "paste", "portable-atomic", "sha2", "smoltcp", diff --git a/Cargo.toml b/Cargo.toml index 5a190ed..6c81f33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ embedded-storage-async = "0.4" portable-atomic = "1" esp-bootloader-esp-idf = "0.1" snafu = { version = "0.8.6", default-features = false } +paste = "1" [profile.dev] # Rust debug is too slow. diff --git a/src/config.rs b/src/config.rs index dee79c1..5cdb426 100644 --- a/src/config.rs +++ b/src/config.rs @@ -23,6 +23,18 @@ use sunset_async::SunsetMutex; use crate::errors; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; +macro_rules! generate_gpio_functions { + ($($n:expr),*) => { + $( + paste::paste! { + pub fn [](&mut self, []: peripherals::[]<'static>) { + self.[] = Some([]); + } + } + )* + }; +} + #[derive(Debug)] pub struct SSHConfig { pub hostkey: SignKey, @@ -70,6 +82,7 @@ impl Default for SerdePinConfig { // TODO: Yikes, this struct and resolve_pin() need to be re-thought for the different ICs and dev boards?.. implementing a suitable // validation function for them and potentially writing a macro that adapts to each PAC (not all ICs have the same number // of pins). +#[derive(Default)] pub struct PinConfig<'a> { pub pin_config_inner: SerdePinConfig, pub gpio0: Option>, @@ -106,31 +119,150 @@ pub struct PinConfig<'a> { } impl<'a> PinConfig<'a> { - // pub fn new(pin_config_inner: SerdePinConfig) -> Self { - // Self { - // pin_config_inner, - // } - // } + pub fn new(pin_config_inner: SerdePinConfig) -> Self { + Self { + pin_config_inner, + ..Default::default() + } + } pub fn tx(&mut self) -> errors::Result>> { - Ok(SunsetMutex::new(self.resolve_pin(self.pin_config_inner.tx)?)) + Ok(SunsetMutex::new(self.take_pin(self.pin_config_inner.tx)?)) } pub fn rx(&mut self) -> errors::Result>> { - Ok(SunsetMutex::new(self.resolve_pin(self.pin_config_inner.rx)?)) + Ok(SunsetMutex::new(self.take_pin(self.pin_config_inner.rx)?)) } pub fn rts(&mut self) -> errors::Result>>> { - self.pin_config_inner.rts.map(|rts| Ok(SunsetMutex::new(self.resolve_pin(rts)?))).transpose() + self.pin_config_inner.rts.map(|rts| Ok(SunsetMutex::new(self.take_pin(rts)?))).transpose() } pub fn cts(&mut self) -> errors::Result>>> { - self.pin_config_inner.cts.map(|cts| Ok(SunsetMutex::new(self.resolve_pin(cts)?))).transpose() + self.pin_config_inner.cts.map(|cts| Ok(SunsetMutex::new(self.take_pin(cts)?))).transpose() + } + + /// Resolves a u8 pin number into an AnyPin GPIO type. + /// Returns None if the pin number is invalid or unsupported. + // pub fn give_pin(&mut self, pin_num: u8, peripherals: &'a mut Peripherals) -> errors::Result<()> { + // match pin_num { + // 0 => self.gpio0 = Some(peripherals.GPIO0.reborrow()), + // 1 => self.gpio1 = Some(peripherals.GPIO1.reborrow()), + // 2 => self.gpio2 = Some(peripherals.GPIO2.reborrow()), + // 3 => self.gpio3 = Some(peripherals.GPIO3.reborrow()), + // 4 => self.gpio4 = Some(peripherals.GPIO4.reborrow()), + // 5 => self.gpio5 = Some(peripherals.GPIO5.reborrow()), + // 6 => self.gpio6 = Some(peripherals.GPIO6.reborrow()), + // 7 => self.gpio7 = Some(peripherals.GPIO7.reborrow()), + // 8 => self.gpio8 = Some(peripherals.GPIO8.reborrow()), + // 9 => self.gpio9 = Some(peripherals.GPIO9.reborrow()), + // 10 => self.gpio10 = Some(peripherals.GPIO10.reborrow()), + // 11 => self.gpio11 = Some(peripherals.GPIO11.reborrow()), + // 12 => self.gpio12 = Some(peripherals.GPIO12.reborrow()), + // 13 => self.gpio13 = Some(peripherals.GPIO13.reborrow()), + // 14 => self.gpio14 = Some(peripherals.GPIO14.reborrow()), + // 15 => self.gpio15 = Some(peripherals.GPIO15.reborrow()), + // 16 => self.gpio16 = Some(peripherals.GPIO16.reborrow()), + // 17 => self.gpio17 = Some(peripherals.GPIO17.reborrow()), + // 18 => self.gpio18 = Some(peripherals.GPIO18.reborrow()), + // 19 => self.gpio19 = Some(peripherals.GPIO19.reborrow()), + // 20 => self.gpio20 = Some(peripherals.GPIO20.reborrow()), + // 21 => self.gpio21 = Some(peripherals.GPIO21.reborrow()), + // 22 => self.gpio22 = Some(peripherals.GPIO22.reborrow()), + // 23 => self.gpio23 = Some(peripherals.GPIO23.reborrow()), + // 24 => self.gpio24 = Some(peripherals.GPIO24.reborrow()), + // 25 => self.gpio25 = Some(peripherals.GPIO25.reborrow()), + // 26 => self.gpio26 = Some(peripherals.GPIO26.reborrow()), + // 27 => self.gpio27 = Some(peripherals.GPIO27.reborrow()), + // 28 => self.gpio28 = Some(peripherals.GPIO28.reborrow()), + // 29 => self.gpio29 = Some(peripherals.GPIO29.reborrow()), + // 30 => self.gpio30 = Some(peripherals.GPIO30.reborrow()), + // _ => return Err(errors::Error::InvalidPin), + // } + // Ok(()) + // } + + generate_gpio_functions!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30); + + /// Resolves a u8 pin number into an AnyPin GPIO type. + /// Returns None if the pin number is invalid or unsupported. + pub fn give_pins(&mut self, mut peripherals: peripherals::Peripherals) -> errors::Result<()> { + match self.pin_config_inner.rx { + 0 => self.gpio0 = Some(unsafe { peripherals.GPIO0.clone_unchecked() }), + 1 => self.gpio1 = Some(peripherals.GPIO1), + 2 => self.gpio2 = Some(peripherals.GPIO2), + 3 => self.gpio3 = Some(peripherals.GPIO3), + 4 => self.gpio4 = Some(peripherals.GPIO4), + 5 => self.gpio5 = Some(peripherals.GPIO5), + 6 => self.gpio6 = Some(peripherals.GPIO6), + 7 => self.gpio7 = Some(peripherals.GPIO7), + 8 => self.gpio8 = Some(peripherals.GPIO8), + 9 => self.gpio9 = Some(peripherals.GPIO9), + 10 => self.gpio10 = Some(peripherals.GPIO10), + 11 => self.gpio11 = Some(peripherals.GPIO11), + 12 => self.gpio12 = Some(peripherals.GPIO12), + 13 => self.gpio13 = Some(peripherals.GPIO13), + 14 => self.gpio14 = Some(peripherals.GPIO14), + 15 => self.gpio15 = Some(peripherals.GPIO15), + 16 => self.gpio16 = Some(peripherals.GPIO16), + 17 => self.gpio17 = Some(peripherals.GPIO17), + 18 => self.gpio18 = Some(peripherals.GPIO18), + 19 => self.gpio19 = Some(peripherals.GPIO19), + 20 => self.gpio20 = Some(peripherals.GPIO20), + 21 => self.gpio21 = Some(peripherals.GPIO21), + 22 => self.gpio22 = Some(peripherals.GPIO22), + 23 => self.gpio23 = Some(peripherals.GPIO23), + 24 => self.gpio24 = Some(peripherals.GPIO24), + 25 => self.gpio25 = Some(peripherals.GPIO25), + 26 => self.gpio26 = Some(peripherals.GPIO26), + 27 => self.gpio27 = Some(peripherals.GPIO27), + 28 => self.gpio28 = Some(peripherals.GPIO28), + 29 => self.gpio29 = Some(peripherals.GPIO29), + 30 => self.gpio30 = Some(peripherals.GPIO30), + _ => return Err(errors::Error::InvalidPin), + }; + + match self.pin_config_inner.tx { + 0 => self.gpio0 = Some(peripherals.GPIO0), + 1 => self.gpio1 = Some(peripherals.GPIO1), + 2 => self.gpio2 = Some(peripherals.GPIO2), + 3 => self.gpio3 = Some(peripherals.GPIO3), + 4 => self.gpio4 = Some(peripherals.GPIO4), + 5 => self.gpio5 = Some(peripherals.GPIO5), + 6 => self.gpio6 = Some(peripherals.GPIO6), + 7 => self.gpio7 = Some(peripherals.GPIO7), + 8 => self.gpio8 = Some(peripherals.GPIO8), + 9 => self.gpio9 = Some(peripherals.GPIO9), + 10 => self.gpio10 = Some(peripherals.GPIO10), + 11 => self.gpio11 = Some(peripherals.GPIO11), + 12 => self.gpio12 = Some(peripherals.GPIO12), + 13 => self.gpio13 = Some(peripherals.GPIO13), + 14 => self.gpio14 = Some(peripherals.GPIO14), + 15 => self.gpio15 = Some(peripherals.GPIO15), + 16 => self.gpio16 = Some(peripherals.GPIO16), + 17 => self.gpio17 = Some(peripherals.GPIO17), + 18 => self.gpio18 = Some(peripherals.GPIO18), + 19 => self.gpio19 = Some(peripherals.GPIO19), + 20 => self.gpio20 = Some(peripherals.GPIO20), + 21 => self.gpio21 = Some(peripherals.GPIO21), + 22 => self.gpio22 = Some(peripherals.GPIO22), + 23 => self.gpio23 = Some(peripherals.GPIO23), + 24 => self.gpio24 = Some(peripherals.GPIO24), + 25 => self.gpio25 = Some(peripherals.GPIO25), + 26 => self.gpio26 = Some(peripherals.GPIO26), + 27 => self.gpio27 = Some(peripherals.GPIO27), + 28 => self.gpio28 = Some(peripherals.GPIO28), + 29 => self.gpio29 = Some(peripherals.GPIO29), + 30 => self.gpio30 = Some(peripherals.GPIO30), + _ => return Err(errors::Error::InvalidPin), + }; + + Ok(()) } /// Resolves a u8 pin number into an AnyPin GPIO type. /// Returns None if the pin number is invalid or unsupported. - pub fn resolve_pin(&mut self, pin_num: u8) -> errors::Result> { + pub fn take_pin(&mut self, pin_num: u8) -> errors::Result> { match pin_num { 0 => self.gpio0.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), 1 => self.gpio1.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), diff --git a/src/main.rs b/src/main.rs index a305bd7..7370294 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,7 @@ async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); // System init - let peripherals = esp_hal::init(esp_hal::Config::default()); + let mut peripherals = esp_hal::init(esp_hal::Config::default()); let mut rng = Rng::new(peripherals.RNG); let timg0 = TimerGroup::new(peripherals.TIMG0); @@ -82,10 +82,8 @@ async fn main(spawner: Spawner) -> ! { let interrupt_spawner = interrupt_executor.start(Priority::Priority10); } } - // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality - let uart1 = peripherals.UART1; - let pin_config = { + let serde_pin_config = { let guard = config.lock().await; guard.uart_pins.clone() }; @@ -95,9 +93,17 @@ async fn main(spawner: Spawner) -> ! { let uart_pins = UART_PINS.init({ // TODO: There shouldn't be a new() method at all because that implies initializing all GPIO pins... // instead we focus on having take() and give() on the config-defined pins. - PinConfig::new(pin_config) + let pin_config = PinConfig::new(serde_pin_config); + + pin_config.give_rx(&mut peripherals); + pin_config.give_tx(&mut peripherals); + + SunsetMutex::new(pin_config) }); + // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality + let uart1 = peripherals.UART1; + // Use the same config reference for UART task. interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins)).unwrap(); @@ -111,7 +117,7 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - uart_pins: &'static PinConfig<'static>, + uart_pins: &'static SunsetMutex>, ) { // Hardware UART setup let uart_config = Config::default().with_rx( @@ -124,10 +130,16 @@ async fn uart_task( // let mut uart_tx_pin = uart_pins.tx.lock().await; // dbg!(&uart_rx_pin); + let (rx, tx) = { + let guard = uart_pins.lock().await; + + (guard.rx(), guard.tx()) + }; + let uart = Uart::new(uart_periph, uart_config) .unwrap() - .with_rx(uart_rx_pin.reborrow()) - .with_tx(uart_tx_pin.reborrow()) + .with_rx(rx) + .with_tx(tx) .into_async(); // Run the main buffered TX/RX loop From becd503c2957cc6be4726227014634dcdad2a303 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 4 Aug 2025 21:07:39 +1000 Subject: [PATCH 24/86] Adding channels and realising that unsafe is almost surely needed for PinConfig::new()... Also channel cannot send at the end of uart_task() because there's an infinite loop right before. Co-authored-by: Marko Malenic --- src/config.rs | 319 +++++++++++++++++++++++--------------------------- src/lib.rs | 2 +- src/main.rs | 60 ++++++---- 3 files changed, 184 insertions(+), 197 deletions(-) diff --git a/src/config.rs b/src/config.rs index 5cdb426..5a792be 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,23 +18,10 @@ use sunset::{ sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, SignKey, }; -use sunset_async::SunsetMutex; use crate::errors; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; -macro_rules! generate_gpio_functions { - ($($n:expr),*) => { - $( - paste::paste! { - pub fn [](&mut self, []: peripherals::[]<'static>) { - self.[] = Some([]); - } - } - )* - }; -} - #[derive(Debug)] pub struct SSHConfig { pub hostkey: SignKey, @@ -79,71 +66,121 @@ impl Default for SerdePinConfig { } } +pub struct GPIOConfig { + pub gpio10: peripherals::GPIO10<'static>, + pub gpio11: peripherals::GPIO11<'static>, +} + // TODO: Yikes, this struct and resolve_pin() need to be re-thought for the different ICs and dev boards?.. implementing a suitable // validation function for them and potentially writing a macro that adapts to each PAC (not all ICs have the same number // of pins). -#[derive(Default)] -pub struct PinConfig<'a> { - pub pin_config_inner: SerdePinConfig, - pub gpio0: Option>, - pub gpio1: Option>, - pub gpio2: Option>, - pub gpio3: Option>, - pub gpio4: Option>, - pub gpio5: Option>, - pub gpio6: Option>, - pub gpio7: Option>, - pub gpio8: Option>, - pub gpio9: Option>, - pub gpio10: Option>, - pub gpio11: Option>, - pub gpio12: Option>, - pub gpio13: Option>, - pub gpio14: Option>, - pub gpio15: Option>, - pub gpio16: Option>, - pub gpio17: Option>, - pub gpio18: Option>, - pub gpio19: Option>, - pub gpio20: Option>, - pub gpio21: Option>, - pub gpio22: Option>, - pub gpio23: Option>, - pub gpio24: Option>, - pub gpio25: Option>, - pub gpio26: Option>, - pub gpio27: Option>, - pub gpio28: Option>, - pub gpio29: Option>, - pub gpio30: Option>, +pub struct PinConfig { + pub tx: AnyPin<'static>, + pub rx: AnyPin<'static>, + // pub gpio0: Option>, + // pub gpio1: Option>, + // pub gpio2: Option>, + // pub gpio3: Option>, + // pub gpio4: Option>, + // pub gpio5: Option>, + // pub gpio6: Option>, + // pub gpio7: Option>, + // pub gpio8: Option>, + // pub gpio9: Option>, + // pub gpio10: Option>, + // pub gpio11: Option>, + // pub gpio12: Option>, + // pub gpio13: Option>, + // pub gpio14: Option>, + // pub gpio15: Option>, + // pub gpio16: Option>, + // pub gpio17: Option>, + // pub gpio18: Option>, + // pub gpio19: Option>, + // pub gpio20: Option>, + // pub gpio21: Option>, + // pub gpio22: Option>, + // pub gpio23: Option>, + // pub gpio24: Option>, + // pub gpio25: Option>, + // pub gpio26: Option>, + // pub gpio27: Option>, + // pub gpio28: Option>, + // pub gpio29: Option>, + // pub gpio30: Option>, } -impl<'a> PinConfig<'a> { - pub fn new(pin_config_inner: SerdePinConfig) -> Self { +pub struct PinConfigAlt { + // pub gpio0: Option>, + // pub gpio1: Option>, + pub peripherals: peripherals::Peripherals, +} + +impl PinConfigAlt { + pub fn new(peripherals: peripherals::Peripherals) -> Self { Self { - pin_config_inner, - ..Default::default() + // gpio0: Some(peripherals.GPIO0.into()), + // gpio1: Some(peripherals.GPIO1.into()), + peripherals, } } - pub fn tx(&mut self) -> errors::Result>> { - Ok(SunsetMutex::new(self.take_pin(self.pin_config_inner.tx)?)) - } - - pub fn rx(&mut self) -> errors::Result>> { - Ok(SunsetMutex::new(self.take_pin(self.pin_config_inner.rx)?)) + pub fn take_pin<'a>(&'a mut self, pin: u8) -> AnyPin<'a> { + match pin { + 0 => self.peripherals.GPIO0.reborrow().into(), + 1 => self.peripherals.GPIO1.reborrow().into(), + _ => panic!(), + } } - pub fn rts(&mut self) -> errors::Result>>> { - self.pin_config_inner.rts.map(|rts| Ok(SunsetMutex::new(self.take_pin(rts)?))).transpose() - } + // pub fn give_pin(&'a mut self, peripherals: &'a mut peripherals::Peripherals, pin: u8) { + // match pin { + // 0 => self.gpio0 = Some(peripherals.GPIO0.reborrow().into()), + // 1 => self.gpio1 = Some(peripherals.GPIO1.reborrow().into()), + // _ => panic!(), + // } + // } +} - pub fn cts(&mut self) -> errors::Result>>> { - self.pin_config_inner.cts.map(|cts| Ok(SunsetMutex::new(self.take_pin(cts)?))).transpose() +impl PinConfig { + pub fn new(gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { + if config_inner.rx == config_inner.tx { + return Err(errors::Error::InvalidPin); + } + + // SAFETY: Safe because moved in peripherals. + Ok(Self { + rx: match config_inner.rx { + 10 => unsafe { gpio_config.gpio10.clone_unchecked().into() }, + 11 => unsafe { gpio_config.gpio11.clone_unchecked().into() }, + _ => return Err(errors::Error::InvalidPin), + }, + tx: match config_inner.tx { + 10 => unsafe { gpio_config.gpio10.clone_unchecked().into() }, + 11 => unsafe { gpio_config.gpio11.clone_unchecked().into() }, + _ => return Err(errors::Error::InvalidPin), + } + }) } - /// Resolves a u8 pin number into an AnyPin GPIO type. - /// Returns None if the pin number is invalid or unsupported. + // pub fn tx(&mut self) -> errors::Result>> { + // Ok(SunsetMutex::new(self.take_pin(self.pin_config_inner.tx)?)) + // } + + // pub fn rx(&mut self) -> errors::Result>> { + // Ok(SunsetMutex::new(self.take_pin(self.pin_config_inner.rx)?)) + // } + + // pub fn rts(&mut self) -> errors::Result>>> { + // self.pin_config_inner.rts.map(|rts| Ok(SunsetMutex::new(self.take_pin(rts)?))).transpose() + // } + + // pub fn cts(&mut self) -> errors::Result>>> { + // self.pin_config_inner.cts.map(|cts| Ok(SunsetMutex::new(self.take_pin(cts)?))).transpose() + // } + + // Resolves a u8 pin number into an AnyPin GPIO type. + // Returns None if the pin number is invalid or unsupported. // pub fn give_pin(&mut self, pin_num: u8, peripherals: &'a mut Peripherals) -> errors::Result<()> { // match pin_num { // 0 => self.gpio0 = Some(peripherals.GPIO0.reborrow()), @@ -182,122 +219,56 @@ impl<'a> PinConfig<'a> { // Ok(()) // } - generate_gpio_functions!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30); - - /// Resolves a u8 pin number into an AnyPin GPIO type. - /// Returns None if the pin number is invalid or unsupported. - pub fn give_pins(&mut self, mut peripherals: peripherals::Peripherals) -> errors::Result<()> { - match self.pin_config_inner.rx { - 0 => self.gpio0 = Some(unsafe { peripherals.GPIO0.clone_unchecked() }), - 1 => self.gpio1 = Some(peripherals.GPIO1), - 2 => self.gpio2 = Some(peripherals.GPIO2), - 3 => self.gpio3 = Some(peripherals.GPIO3), - 4 => self.gpio4 = Some(peripherals.GPIO4), - 5 => self.gpio5 = Some(peripherals.GPIO5), - 6 => self.gpio6 = Some(peripherals.GPIO6), - 7 => self.gpio7 = Some(peripherals.GPIO7), - 8 => self.gpio8 = Some(peripherals.GPIO8), - 9 => self.gpio9 = Some(peripherals.GPIO9), - 10 => self.gpio10 = Some(peripherals.GPIO10), - 11 => self.gpio11 = Some(peripherals.GPIO11), - 12 => self.gpio12 = Some(peripherals.GPIO12), - 13 => self.gpio13 = Some(peripherals.GPIO13), - 14 => self.gpio14 = Some(peripherals.GPIO14), - 15 => self.gpio15 = Some(peripherals.GPIO15), - 16 => self.gpio16 = Some(peripherals.GPIO16), - 17 => self.gpio17 = Some(peripherals.GPIO17), - 18 => self.gpio18 = Some(peripherals.GPIO18), - 19 => self.gpio19 = Some(peripherals.GPIO19), - 20 => self.gpio20 = Some(peripherals.GPIO20), - 21 => self.gpio21 = Some(peripherals.GPIO21), - 22 => self.gpio22 = Some(peripherals.GPIO22), - 23 => self.gpio23 = Some(peripherals.GPIO23), - 24 => self.gpio24 = Some(peripherals.GPIO24), - 25 => self.gpio25 = Some(peripherals.GPIO25), - 26 => self.gpio26 = Some(peripherals.GPIO26), - 27 => self.gpio27 = Some(peripherals.GPIO27), - 28 => self.gpio28 = Some(peripherals.GPIO28), - 29 => self.gpio29 = Some(peripherals.GPIO29), - 30 => self.gpio30 = Some(peripherals.GPIO30), - _ => return Err(errors::Error::InvalidPin), - }; - - match self.pin_config_inner.tx { - 0 => self.gpio0 = Some(peripherals.GPIO0), - 1 => self.gpio1 = Some(peripherals.GPIO1), - 2 => self.gpio2 = Some(peripherals.GPIO2), - 3 => self.gpio3 = Some(peripherals.GPIO3), - 4 => self.gpio4 = Some(peripherals.GPIO4), - 5 => self.gpio5 = Some(peripherals.GPIO5), - 6 => self.gpio6 = Some(peripherals.GPIO6), - 7 => self.gpio7 = Some(peripherals.GPIO7), - 8 => self.gpio8 = Some(peripherals.GPIO8), - 9 => self.gpio9 = Some(peripherals.GPIO9), - 10 => self.gpio10 = Some(peripherals.GPIO10), - 11 => self.gpio11 = Some(peripherals.GPIO11), - 12 => self.gpio12 = Some(peripherals.GPIO12), - 13 => self.gpio13 = Some(peripherals.GPIO13), - 14 => self.gpio14 = Some(peripherals.GPIO14), - 15 => self.gpio15 = Some(peripherals.GPIO15), - 16 => self.gpio16 = Some(peripherals.GPIO16), - 17 => self.gpio17 = Some(peripherals.GPIO17), - 18 => self.gpio18 = Some(peripherals.GPIO18), - 19 => self.gpio19 = Some(peripherals.GPIO19), - 20 => self.gpio20 = Some(peripherals.GPIO20), - 21 => self.gpio21 = Some(peripherals.GPIO21), - 22 => self.gpio22 = Some(peripherals.GPIO22), - 23 => self.gpio23 = Some(peripherals.GPIO23), - 24 => self.gpio24 = Some(peripherals.GPIO24), - 25 => self.gpio25 = Some(peripherals.GPIO25), - 26 => self.gpio26 = Some(peripherals.GPIO26), - 27 => self.gpio27 = Some(peripherals.GPIO27), - 28 => self.gpio28 = Some(peripherals.GPIO28), - 29 => self.gpio29 = Some(peripherals.GPIO29), - 30 => self.gpio30 = Some(peripherals.GPIO30), - _ => return Err(errors::Error::InvalidPin), - }; - - Ok(()) - } + // generate_gpio_functions!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30); /// Resolves a u8 pin number into an AnyPin GPIO type. /// Returns None if the pin number is invalid or unsupported. - pub fn take_pin(&mut self, pin_num: u8) -> errors::Result> { - match pin_num { - 0 => self.gpio0.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 1 => self.gpio1.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 2 => self.gpio2.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 3 => self.gpio3.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 4 => self.gpio4.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 5 => self.gpio5.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 6 => self.gpio6.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 7 => self.gpio7.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 8 => self.gpio8.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 9 => self.gpio9.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 10 => self.gpio10.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 11 => self.gpio11.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 12 => self.gpio12.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 13 => self.gpio13.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 14 => self.gpio14.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 15 => self.gpio15.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 16 => self.gpio16.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 17 => self.gpio17.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 18 => self.gpio18.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 19 => self.gpio19.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 20 => self.gpio20.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 21 => self.gpio21.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 22 => self.gpio22.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 23 => self.gpio23.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 24 => self.gpio24.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 25 => self.gpio25.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 26 => self.gpio26.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 27 => self.gpio27.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 28 => self.gpio28.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 29 => self.gpio29.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - 30 => self.gpio30.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + pub fn initialize_pin(peripherals: peripherals::Peripherals, pin_number: u8) -> errors::Result> { + match pin_number { + 0 => Ok(peripherals.GPIO0.into()), + _ => Err(errors::Error::InvalidPin), } } + + // /// Resolves a u8 pin number into an AnyPin GPIO type. + // /// Returns None if the pin number is invalid or unsupported. + // pub fn take_pin(&mut self, pin_num: u8) -> errors::Result> { + // match pin_num { + // 0 => self.gpio0.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 1 => self.gpio1.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 2 => self.gpio2.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 3 => self.gpio3.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 4 => self.gpio4.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 5 => self.gpio5.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 6 => self.gpio6.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 7 => self.gpio7.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 8 => self.gpio8.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 9 => self.gpio9.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 10 => self.gpio10.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 11 => self.gpio11.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 12 => self.gpio12.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 13 => self.gpio13.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 14 => self.gpio14.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 15 => self.gpio15.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 16 => self.gpio16.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 17 => self.gpio17.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 18 => self.gpio18.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 19 => self.gpio19.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 20 => self.gpio20.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 21 => self.gpio21.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 22 => self.gpio22.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 23 => self.gpio23.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 24 => self.gpio24.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 25 => self.gpio25.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 26 => self.gpio26.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 27 => self.gpio27.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 28 => self.gpio28.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 29 => self.gpio29.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // 30 => self.gpio30.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), + // _ => Err(errors::Error::InvalidPin), + // } + // } } // // TODO: Revisit this and compare them with esp-hal examples, see what they use for their HIL nowadays. diff --git a/src/lib.rs b/src/lib.rs index fea262a..ed48a46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] #![no_main] -#![forbid(unsafe_code)] +// #![forbid(unsafe_code)] #[deny(clippy::mem_forget)] // avoids any UB, forces use of Drop impl instead pub mod config; diff --git a/src/main.rs b/src/main.rs index 7370294..7d8bb0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,9 @@ use ssh_stamp::{config::SSHConfig, espressif::{ use static_cell::StaticCell; use sunset_async::SunsetMutex; use ssh_stamp::config::PinConfig; +use ssh_stamp::config::GPIOConfig; +use embassy_sync::channel::{ Sender, Receiver, Channel }; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { @@ -35,7 +38,7 @@ async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); // System init - let mut peripherals = esp_hal::init(esp_hal::Config::default()); + let peripherals = esp_hal::init(esp_hal::Config::default()); let mut rng = Rng::new(peripherals.RNG); let timg0 = TimerGroup::new(peripherals.TIMG0); @@ -89,23 +92,36 @@ async fn main(spawner: Spawner) -> ! { }; // Potential pins to use for such UART, to be owned by uart_task. - static UART_PINS: StaticCell> = StaticCell::new(); - let uart_pins = UART_PINS.init({ - // TODO: There shouldn't be a new() method at all because that implies initializing all GPIO pins... - // instead we focus on having take() and give() on the config-defined pins. - let pin_config = PinConfig::new(serde_pin_config); + // static UART_PINS: StaticCell> = StaticCell::new(); + // let uart_pins = UART_PINS.init({ + // // TODO: There shouldn't be a new() method at all because that implies initializing all GPIO pins... + // // instead we focus on having take() and give() on the config-defined pins. + // let pin_config = PinConfig::new(serde_pin_config); - pin_config.give_rx(&mut peripherals); - pin_config.give_tx(&mut peripherals); + // pin_config.give_rx(&mut peripherals); + // pin_config.give_tx(&mut peripherals); - SunsetMutex::new(pin_config) + // SunsetMutex::new(pin_config) + // }); + + static CHANNEL: StaticCell> = StaticCell::new(); + let channel = CHANNEL.init({ + Channel::::new() }); + let sender = channel.sender(); + let receiver = channel.receiver(); + + // Send first value for tasks. + sender.send(PinConfig::new(GPIOConfig { + gpio10: peripherals.GPIO10, + gpio11: peripherals.GPIO11, + }, serde_pin_config).unwrap()).await; // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality let uart1 = peripherals.UART1; // Use the same config reference for UART task. - interrupt_spawner.spawn(uart_task(uart_buf, uart1, uart_pins)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, uart1, sender, receiver)).unwrap(); accept_requests(tcp_stack, uart_buf).await; } @@ -117,7 +133,9 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - uart_pins: &'static SunsetMutex>, + sender: Sender<'static, CriticalSectionRawMutex, PinConfig, 1>, + receiver: Receiver<'static, CriticalSectionRawMutex, PinConfig, 1>, + // uart_pins: &'static SunsetMutex>, ) { // Hardware UART setup let uart_config = Config::default().with_rx( @@ -125,23 +143,21 @@ async fn uart_task( .with_fifo_full_threshold(16) .with_timeout(1) ); - - // let mut uart_rx_pin = uart_pins.rx.lock().await; - // let mut uart_tx_pin = uart_pins.tx.lock().await; - // dbg!(&uart_rx_pin); - let (rx, tx) = { - let guard = uart_pins.lock().await; - - (guard.rx(), guard.tx()) - }; + // lock whole pin config. + let mut pin_config = receiver.receive().await; let uart = Uart::new(uart_periph, uart_config) .unwrap() - .with_rx(rx) - .with_tx(tx) + .with_rx(pin_config.rx.reborrow()) + .with_tx(pin_config.tx.reborrow()) .into_async(); // Run the main buffered TX/RX loop buffer.run(uart).await; + + // FIXME: This is NEVER reached since hte run above is an infinite loop. + // Each GPIO pins for UART (rx/tx/cts/rts) needs to be guarded by an individual lock/mechanism so that not all + // of them are taken with one task that runs forever. + sender.send(pin_config).await; } From bb7cdfa3b2ba86863224d1bea0143c7b38bf1932 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 15 Aug 2025 20:57:16 +1000 Subject: [PATCH 25/86] Get rid of unsafe via .degrade and Option<> (thx @bugadani for the tip!) Use channels to send individual pins around... probably we should refactor to Signal instead since message size N=1. Co-authored-by: Marko Malenic --- src/config.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 60 +++++++++++++++++++++++-------------- 2 files changed, 113 insertions(+), 29 deletions(-) diff --git a/src/config.rs b/src/config.rs index 5a792be..906a957 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,6 +18,8 @@ use sunset::{ sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, SignKey, }; +use embassy_sync::channel::Channel; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use crate::errors; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; @@ -67,10 +69,76 @@ impl Default for SerdePinConfig { } pub struct GPIOConfig { - pub gpio10: peripherals::GPIO10<'static>, - pub gpio11: peripherals::GPIO11<'static>, + pub gpio10: Option>, + pub gpio11: Option>, } +pub struct PinChannel { + pub config: SerdePinConfig, + pub gpios: GPIOConfig, + pub tx: Channel::, + pub rx: Channel::, + // TODO: cts/rts pins +} + +impl PinChannel { + pub fn new(config: SerdePinConfig, gpios: GPIOConfig) -> Self { + Self { + config, + gpios, + tx: Channel::::new(), + rx: Channel::::new(), + } + } + + pub async fn recv_tx(&mut self) -> errors::Result> { + // tx needs to lock here. + self.tx.receive().await; + + Ok(match self.config.tx { + 10 => self.gpios.gpio10.take().ok_or_else(|| errors::Error::InvalidPin)?, + 11 => self.gpios.gpio11.take().ok_or_else(|| errors::Error::InvalidPin)?, + _ => return Err(errors::Error::InvalidPin) + }) + } + + pub async fn send_tx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + match self.config.tx { + 10 => self.gpios.gpio10 = Some(pin), + 11 => self.gpios.gpio11 = Some(pin), + _ => return Err(errors::Error::InvalidPin) + }; + + // tx lock needs to be released. + self.tx.send(()).await; + Ok(()) + } + + pub async fn recv_rx(&mut self) -> errors::Result> { + // rx needs to lock here. + self.rx.receive().await; + + Ok(match self.config.rx { + 10 => self.gpios.gpio10.take().ok_or_else(|| errors::Error::InvalidPin)?, + 11 => self.gpios.gpio11.take().ok_or_else(|| errors::Error::InvalidPin)?, + _ => return Err(errors::Error::InvalidPin) + }) + } + + pub async fn send_rx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + match self.config.rx { + 10 => self.gpios.gpio10 = Some(pin), + 11 => self.gpios.gpio11 = Some(pin), + _ => return Err(errors::Error::InvalidPin) + }; + + // rx lock needs to be released. + self.rx.send(()).await; + Ok(()) + } +} + + // TODO: Yikes, this struct and resolve_pin() need to be re-thought for the different ICs and dev boards?.. implementing a suitable // validation function for them and potentially writing a macro that adapts to each PAC (not all ICs have the same number // of pins). @@ -143,7 +211,7 @@ impl PinConfigAlt { } impl PinConfig { - pub fn new(gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { + pub fn new(mut gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { if config_inner.rx == config_inner.tx { return Err(errors::Error::InvalidPin); } @@ -151,13 +219,13 @@ impl PinConfig { // SAFETY: Safe because moved in peripherals. Ok(Self { rx: match config_inner.rx { - 10 => unsafe { gpio_config.gpio10.clone_unchecked().into() }, - 11 => unsafe { gpio_config.gpio11.clone_unchecked().into() }, + 10 => gpio_config.gpio10.take().unwrap().into(), + 11 => gpio_config.gpio11.take().unwrap().into(), _ => return Err(errors::Error::InvalidPin), }, tx: match config_inner.tx { - 10 => unsafe { gpio_config.gpio10.clone_unchecked().into() }, - 11 => unsafe { gpio_config.gpio11.clone_unchecked().into() }, + 10 => gpio_config.gpio10.take().unwrap().into(), + 11 => gpio_config.gpio11.take().unwrap().into(), _ => return Err(errors::Error::InvalidPin), } }) diff --git a/src/main.rs b/src/main.rs index 7d8bb0c..49e87be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use core::marker::Sized; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} + gpio::Pin, interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} }; use esp_hal_embassy::InterruptExecutor; @@ -18,10 +18,8 @@ use ssh_stamp::{config::SSHConfig, espressif::{ }, storage::Fl}; use static_cell::StaticCell; use sunset_async::SunsetMutex; -use ssh_stamp::config::PinConfig; use ssh_stamp::config::GPIOConfig; -use embassy_sync::channel::{ Sender, Receiver, Channel }; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use ssh_stamp::config::PinChannel; #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { @@ -104,24 +102,27 @@ async fn main(spawner: Spawner) -> ! { // SunsetMutex::new(pin_config) // }); - static CHANNEL: StaticCell> = StaticCell::new(); + let gpios = GPIOConfig { + gpio10: Some(peripherals.GPIO10.degrade()), + gpio11: Some(peripherals.GPIO11.degrade()), + }; + + static CHANNEL: StaticCell = StaticCell::new(); let channel = CHANNEL.init({ - Channel::::new() + PinChannel::new(serde_pin_config, gpios) }); - let sender = channel.sender(); - let receiver = channel.receiver(); // Send first value for tasks. - sender.send(PinConfig::new(GPIOConfig { - gpio10: peripherals.GPIO10, - gpio11: peripherals.GPIO11, - }, serde_pin_config).unwrap()).await; + // sender.send(PinConfig::new(GPIOConfig { + // gpio10: Some(peripherals.GPIO10.into()), + // gpio11: Some(peripherals.GPIO11.into()), + // }, serde_pin_config).unwrap()).await; // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality let uart1 = peripherals.UART1; // Use the same config reference for UART task. - interrupt_spawner.spawn(uart_task(uart_buf, uart1, sender, receiver)).unwrap(); + interrupt_spawner.spawn(uart_task(uart_buf, uart1, channel)).unwrap(); accept_requests(tcp_stack, uart_buf).await; } @@ -133,8 +134,9 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - sender: Sender<'static, CriticalSectionRawMutex, PinConfig, 1>, - receiver: Receiver<'static, CriticalSectionRawMutex, PinConfig, 1>, + channel: &'static mut PinChannel, + // sender: Sender<'static, CriticalSectionRawMutex, PinConfig, 1>, + // receiver: Receiver<'static, CriticalSectionRawMutex, PinConfig, 1>, // uart_pins: &'static SunsetMutex>, ) { // Hardware UART setup @@ -145,19 +147,33 @@ async fn uart_task( ); // lock whole pin config. - let mut pin_config = receiver.receive().await; + // let mut pin_config = receiver.receive().await; + + let mut rx = channel.recv_rx().await.unwrap(); + let mut tx = channel.recv_tx().await.unwrap(); let uart = Uart::new(uart_periph, uart_config) .unwrap() - .with_rx(pin_config.rx.reborrow()) - .with_tx(pin_config.tx.reborrow()) + .with_rx(rx.reborrow()) + .with_tx(tx.reborrow()) .into_async(); // Run the main buffered TX/RX loop buffer.run(uart).await; - // FIXME: This is NEVER reached since hte run above is an infinite loop. - // Each GPIO pins for UART (rx/tx/cts/rts) needs to be guarded by an individual lock/mechanism so that not all - // of them are taken with one task that runs forever. - sender.send(pin_config).await; + channel.send_rx(rx).await.unwrap(); + channel.send_tx(tx).await.unwrap(); + + // TODO: Better way + + // channel.with_rx_and_tx(|rx, tx| async { + // let uart = Uart::new(uart_periph, uart_config) + // .unwrap() + // .with_rx(rx.reborrow()) + // .with_tx(tx.reborrow()) + // .into_async(); + + // // Run the main buffered TX/RX loop + // buffer.run(uart).await; + // }); } From 7e6ec0eaa46545ffa8e3fd60ed1515cefe902e2e Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 20 Aug 2025 20:13:26 +1000 Subject: [PATCH 26/86] Cleanup before considering Signals (instead of channels) and how to pass SSH's env events --- .gitignore | 3 + src/config.rs | 160 +------------------------------------------------- 2 files changed, 6 insertions(+), 157 deletions(-) diff --git a/.gitignore b/.gitignore index 0dd24c9..3868ad5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ target/ # VSCode workspace(s) *.code-workspace + +# Temporarily ignore book from this branch +docs/book diff --git a/src/config.rs b/src/config.rs index 906a957..075d82b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -42,7 +42,6 @@ pub struct SSHConfig { pub mac: [u8; 6], /// `None` for DHCP pub ip4_static: Option, - /// UART pub uart_pins: SerdePinConfig, } @@ -60,6 +59,7 @@ impl Default for SerdePinConfig { Self { tx: 10, rx: 11, + // TODO: This env comes from SSH env events/packets, not from system's std::env / core::env (if any)... so it shouldn't be unsafe() // tx: env!("SSH_STAMP_TX_PIN").parse().unwrap(), // rx: env!("SSH_STAMP_RX_PIN").parse().unwrap(), rts: option_env!("SSH_STAMP_RTS").map(|s| s.parse().unwrap()), @@ -139,57 +139,22 @@ impl PinChannel { } -// TODO: Yikes, this struct and resolve_pin() need to be re-thought for the different ICs and dev boards?.. implementing a suitable +// TODO: This struct and resolve_pin() need to be re-thought for the different ICs and dev boards?.. implementing a suitable // validation function for them and potentially writing a macro that adapts to each PAC (not all ICs have the same number // of pins). pub struct PinConfig { pub tx: AnyPin<'static>, pub rx: AnyPin<'static>, - // pub gpio0: Option>, - // pub gpio1: Option>, - // pub gpio2: Option>, - // pub gpio3: Option>, - // pub gpio4: Option>, - // pub gpio5: Option>, - // pub gpio6: Option>, - // pub gpio7: Option>, - // pub gpio8: Option>, - // pub gpio9: Option>, - // pub gpio10: Option>, - // pub gpio11: Option>, - // pub gpio12: Option>, - // pub gpio13: Option>, - // pub gpio14: Option>, - // pub gpio15: Option>, - // pub gpio16: Option>, - // pub gpio17: Option>, - // pub gpio18: Option>, - // pub gpio19: Option>, - // pub gpio20: Option>, - // pub gpio21: Option>, - // pub gpio22: Option>, - // pub gpio23: Option>, - // pub gpio24: Option>, - // pub gpio25: Option>, - // pub gpio26: Option>, - // pub gpio27: Option>, - // pub gpio28: Option>, - // pub gpio29: Option>, - // pub gpio30: Option>, } pub struct PinConfigAlt { - // pub gpio0: Option>, - // pub gpio1: Option>, pub peripherals: peripherals::Peripherals, } impl PinConfigAlt { pub fn new(peripherals: peripherals::Peripherals) -> Self { Self { - // gpio0: Some(peripherals.GPIO0.into()), - // gpio1: Some(peripherals.GPIO1.into()), - peripherals, + peripherals, } } @@ -200,14 +165,6 @@ impl PinConfigAlt { _ => panic!(), } } - - // pub fn give_pin(&'a mut self, peripherals: &'a mut peripherals::Peripherals, pin: u8) { - // match pin { - // 0 => self.gpio0 = Some(peripherals.GPIO0.reborrow().into()), - // 1 => self.gpio1 = Some(peripherals.GPIO1.reborrow().into()), - // _ => panic!(), - // } - // } } impl PinConfig { @@ -231,64 +188,6 @@ impl PinConfig { }) } - // pub fn tx(&mut self) -> errors::Result>> { - // Ok(SunsetMutex::new(self.take_pin(self.pin_config_inner.tx)?)) - // } - - // pub fn rx(&mut self) -> errors::Result>> { - // Ok(SunsetMutex::new(self.take_pin(self.pin_config_inner.rx)?)) - // } - - // pub fn rts(&mut self) -> errors::Result>>> { - // self.pin_config_inner.rts.map(|rts| Ok(SunsetMutex::new(self.take_pin(rts)?))).transpose() - // } - - // pub fn cts(&mut self) -> errors::Result>>> { - // self.pin_config_inner.cts.map(|cts| Ok(SunsetMutex::new(self.take_pin(cts)?))).transpose() - // } - - // Resolves a u8 pin number into an AnyPin GPIO type. - // Returns None if the pin number is invalid or unsupported. - // pub fn give_pin(&mut self, pin_num: u8, peripherals: &'a mut Peripherals) -> errors::Result<()> { - // match pin_num { - // 0 => self.gpio0 = Some(peripherals.GPIO0.reborrow()), - // 1 => self.gpio1 = Some(peripherals.GPIO1.reborrow()), - // 2 => self.gpio2 = Some(peripherals.GPIO2.reborrow()), - // 3 => self.gpio3 = Some(peripherals.GPIO3.reborrow()), - // 4 => self.gpio4 = Some(peripherals.GPIO4.reborrow()), - // 5 => self.gpio5 = Some(peripherals.GPIO5.reborrow()), - // 6 => self.gpio6 = Some(peripherals.GPIO6.reborrow()), - // 7 => self.gpio7 = Some(peripherals.GPIO7.reborrow()), - // 8 => self.gpio8 = Some(peripherals.GPIO8.reborrow()), - // 9 => self.gpio9 = Some(peripherals.GPIO9.reborrow()), - // 10 => self.gpio10 = Some(peripherals.GPIO10.reborrow()), - // 11 => self.gpio11 = Some(peripherals.GPIO11.reborrow()), - // 12 => self.gpio12 = Some(peripherals.GPIO12.reborrow()), - // 13 => self.gpio13 = Some(peripherals.GPIO13.reborrow()), - // 14 => self.gpio14 = Some(peripherals.GPIO14.reborrow()), - // 15 => self.gpio15 = Some(peripherals.GPIO15.reborrow()), - // 16 => self.gpio16 = Some(peripherals.GPIO16.reborrow()), - // 17 => self.gpio17 = Some(peripherals.GPIO17.reborrow()), - // 18 => self.gpio18 = Some(peripherals.GPIO18.reborrow()), - // 19 => self.gpio19 = Some(peripherals.GPIO19.reborrow()), - // 20 => self.gpio20 = Some(peripherals.GPIO20.reborrow()), - // 21 => self.gpio21 = Some(peripherals.GPIO21.reborrow()), - // 22 => self.gpio22 = Some(peripherals.GPIO22.reborrow()), - // 23 => self.gpio23 = Some(peripherals.GPIO23.reborrow()), - // 24 => self.gpio24 = Some(peripherals.GPIO24.reborrow()), - // 25 => self.gpio25 = Some(peripherals.GPIO25.reborrow()), - // 26 => self.gpio26 = Some(peripherals.GPIO26.reborrow()), - // 27 => self.gpio27 = Some(peripherals.GPIO27.reborrow()), - // 28 => self.gpio28 = Some(peripherals.GPIO28.reborrow()), - // 29 => self.gpio29 = Some(peripherals.GPIO29.reborrow()), - // 30 => self.gpio30 = Some(peripherals.GPIO30.reborrow()), - // _ => return Err(errors::Error::InvalidPin), - // } - // Ok(()) - // } - - // generate_gpio_functions!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30); - /// Resolves a u8 pin number into an AnyPin GPIO type. /// Returns None if the pin number is invalid or unsupported. pub fn initialize_pin(peripherals: peripherals::Peripherals, pin_number: u8) -> errors::Result> { @@ -298,61 +197,8 @@ impl PinConfig { _ => Err(errors::Error::InvalidPin), } } - - // /// Resolves a u8 pin number into an AnyPin GPIO type. - // /// Returns None if the pin number is invalid or unsupported. - // pub fn take_pin(&mut self, pin_num: u8) -> errors::Result> { - // match pin_num { - // 0 => self.gpio0.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 1 => self.gpio1.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 2 => self.gpio2.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 3 => self.gpio3.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 4 => self.gpio4.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 5 => self.gpio5.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 6 => self.gpio6.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 7 => self.gpio7.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 8 => self.gpio8.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 9 => self.gpio9.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 10 => self.gpio10.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 11 => self.gpio11.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 12 => self.gpio12.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 13 => self.gpio13.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 14 => self.gpio14.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 15 => self.gpio15.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 16 => self.gpio16.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 17 => self.gpio17.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 18 => self.gpio18.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 19 => self.gpio19.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 20 => self.gpio20.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 21 => self.gpio21.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 22 => self.gpio22.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 23 => self.gpio23.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 24 => self.gpio24.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 25 => self.gpio25.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 26 => self.gpio26.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 27 => self.gpio27.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 28 => self.gpio28.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 29 => self.gpio29.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // 30 => self.gpio30.take().map(AnyPin::from).ok_or(errors::Error::InvalidPin), - // _ => Err(errors::Error::InvalidPin), - // } - // } } -// // TODO: Revisit this and compare them with esp-hal examples, see what they use for their HIL nowadays. -// impl Default for PinConfig { -// fn default() -> Self { -// let rx = SunsetMutex::new(PinConfig::resolve_pin(10).expect("Invalid RX pin")); -// let tx = SunsetMutex::new(PinConfig::resolve_pin(11).expect("Invalid TX pin")); -// PinConfig { -// rx, -// tx, -// rts: None, -// cts: None, -// } -// } -// } - impl SSHConfig { /// Bump this when the format changes pub const CURRENT_VERSION: u8 = 6; From 9968552c9a1a07a8a0ee024d50f4487d0d6c6cc4 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 25 Aug 2025 20:48:04 +1000 Subject: [PATCH 27/86] Attempt to use automatic Send/Receive PinChannel struct... issue with lifetimes and futures. Co-authored-by: Marko Malenic --- src/config.rs | 153 ++++++++++++++++++++++++++++++++++++-------------- src/main.rs | 63 ++++++--------------- 2 files changed, 129 insertions(+), 87 deletions(-) diff --git a/src/config.rs b/src/config.rs index 075d82b..7a19505 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,4 @@ +use core::future::Future; use core::net::Ipv4Addr; use embassy_net::{Ipv4Cidr, StaticConfigV4}; use esp_hal::gpio::AnyPin; @@ -68,21 +69,53 @@ impl Default for SerdePinConfig { } } -pub struct GPIOConfig { - pub gpio10: Option>, - pub gpio11: Option>, +// pub type RxPin<'a> = AnyPin<'a>; +// pub type TxPin<'a> = AnyPin<'a>; + +pub struct TxRxPin<'a> { + tx: Option>, + rx: Option>, + send_back: &'a mut PinChannel<'a> +} + + +impl<'a> Drop for TxRxPin<'a> { + fn drop(&mut self) { + let tx = self.tx.take().unwrap(); + let rx = self.rx.take().unwrap(); + + self.send_back.try_send_tx(tx).unwrap(); + self.send_back.try_send_rx(rx).unwrap(); + } +} + +pub struct RxPin<'a> { + pin: Option>, + send_back: &'a mut PinChannel<'a> +} + +impl<'a> Drop for RxPin<'a> { + fn drop(&mut self) { + let pin = self.pin.take().unwrap(); + self.send_back.try_send_rx(pin).unwrap(); + } +} + +pub struct GPIOConfig<'a> { + pub gpio10: Option>, + pub gpio11: Option>, } -pub struct PinChannel { +pub struct PinChannel<'a> { pub config: SerdePinConfig, - pub gpios: GPIOConfig, + pub gpios: GPIOConfig<'a>, pub tx: Channel::, pub rx: Channel::, // TODO: cts/rts pins } -impl PinChannel { - pub fn new(config: SerdePinConfig, gpios: GPIOConfig) -> Self { +impl<'a> PinChannel<'a> { + pub fn new(config: SerdePinConfig, gpios: GPIOConfig<'a>) -> Self { Self { config, gpios, @@ -91,7 +124,7 @@ impl PinChannel { } } - pub async fn recv_tx(&mut self) -> errors::Result> { + pub async fn recv_tx(&mut self) -> errors::Result> { // tx needs to lock here. self.tx.receive().await; @@ -102,7 +135,7 @@ impl PinChannel { }) } - pub async fn send_tx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + pub async fn send_tx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { match self.config.tx { 10 => self.gpios.gpio10 = Some(pin), 11 => self.gpios.gpio11 = Some(pin), @@ -114,7 +147,19 @@ impl PinChannel { Ok(()) } - pub async fn recv_rx(&mut self) -> errors::Result> { + pub fn try_send_tx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { + match self.config.tx { + 10 => self.gpios.gpio10 = Some(pin), + 11 => self.gpios.gpio11 = Some(pin), + _ => return Err(errors::Error::InvalidPin) + }; + + // tx lock needs to be released. + self.tx.try_send(()).unwrap(); + Ok(()) + } + + pub async fn recv_rx(&mut self) -> errors::Result> { // rx needs to lock here. self.rx.receive().await; @@ -125,7 +170,7 @@ impl PinChannel { }) } - pub async fn send_rx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + pub async fn send_rx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { match self.config.rx { 10 => self.gpios.gpio10 = Some(pin), 11 => self.gpios.gpio11 = Some(pin), @@ -136,6 +181,32 @@ impl PinChannel { self.rx.send(()).await; Ok(()) } + + pub fn try_send_rx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { + match self.config.rx { + 10 => self.gpios.gpio10 = Some(pin), + 11 => self.gpios.gpio11 = Some(pin), + _ => return Err(errors::Error::InvalidPin) + }; + + // rx lock needs to be released. + self.rx.try_send(()).unwrap(); + Ok(()) + } + + pub async fn with_channel(&'a mut self, f: F) -> errors::Result<()> + where F: FnOnce(TxRxPin) -> Fut, + Fut: Future { + let rx = self.recv_rx().await?; + let tx = self.recv_tx().await?; + + f(TxRxPin { tx: Some(tx), rx: Some(rx), send_back: self }).await; + + // self.send_rx(rx).await.unwrap(); + // self.send_tx(tx).await.unwrap(); + + Ok(()) + } } @@ -167,37 +238,37 @@ impl PinConfigAlt { } } -impl PinConfig { - pub fn new(mut gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { - if config_inner.rx == config_inner.tx { - return Err(errors::Error::InvalidPin); - } +// impl PinConfig { +// pub fn new(mut gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { +// if config_inner.rx == config_inner.tx { +// return Err(errors::Error::InvalidPin); +// } - // SAFETY: Safe because moved in peripherals. - Ok(Self { - rx: match config_inner.rx { - 10 => gpio_config.gpio10.take().unwrap().into(), - 11 => gpio_config.gpio11.take().unwrap().into(), - _ => return Err(errors::Error::InvalidPin), - }, - tx: match config_inner.tx { - 10 => gpio_config.gpio10.take().unwrap().into(), - 11 => gpio_config.gpio11.take().unwrap().into(), - _ => return Err(errors::Error::InvalidPin), - } - }) - } - - /// Resolves a u8 pin number into an AnyPin GPIO type. - /// Returns None if the pin number is invalid or unsupported. - pub fn initialize_pin(peripherals: peripherals::Peripherals, pin_number: u8) -> errors::Result> { - match pin_number { - 0 => Ok(peripherals.GPIO0.into()), - - _ => Err(errors::Error::InvalidPin), - } - } -} +// // SAFETY: Safe because moved in peripherals. +// Ok(Self { +// rx: match config_inner.rx { +// 10 => gpio_config.gpio10.take().unwrap().into(), +// 11 => gpio_config.gpio11.take().unwrap().into(), +// _ => return Err(errors::Error::InvalidPin), +// }, +// tx: match config_inner.tx { +// 10 => gpio_config.gpio10.take().unwrap().into(), +// 11 => gpio_config.gpio11.take().unwrap().into(), +// _ => return Err(errors::Error::InvalidPin), +// } +// }) +// } + +// /// Resolves a u8 pin number into an AnyPin GPIO type. +// /// Returns None if the pin number is invalid or unsupported. +// pub fn initialize_pin(peripherals: peripherals::Peripherals, pin_number: u8) -> errors::Result> { +// match pin_number { +// 0 => Ok(peripherals.GPIO0.into()), + +// _ => Err(errors::Error::InvalidPin), +// } +// } +// } impl SSHConfig { /// Bump this when the format changes diff --git a/src/main.rs b/src/main.rs index 49e87be..7806de0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -89,18 +89,6 @@ async fn main(spawner: Spawner) -> ! { guard.uart_pins.clone() }; - // Potential pins to use for such UART, to be owned by uart_task. - // static UART_PINS: StaticCell> = StaticCell::new(); - // let uart_pins = UART_PINS.init({ - // // TODO: There shouldn't be a new() method at all because that implies initializing all GPIO pins... - // // instead we focus on having take() and give() on the config-defined pins. - // let pin_config = PinConfig::new(serde_pin_config); - - // pin_config.give_rx(&mut peripherals); - // pin_config.give_tx(&mut peripherals); - - // SunsetMutex::new(pin_config) - // }); let gpios = GPIOConfig { gpio10: Some(peripherals.GPIO10.degrade()), @@ -112,12 +100,6 @@ async fn main(spawner: Spawner) -> ! { PinChannel::new(serde_pin_config, gpios) }); - // Send first value for tasks. - // sender.send(PinConfig::new(GPIOConfig { - // gpio10: Some(peripherals.GPIO10.into()), - // gpio11: Some(peripherals.GPIO11.into()), - // }, serde_pin_config).unwrap()).await; - // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality let uart1 = peripherals.UART1; @@ -134,10 +116,7 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - channel: &'static mut PinChannel, - // sender: Sender<'static, CriticalSectionRawMutex, PinConfig, 1>, - // receiver: Receiver<'static, CriticalSectionRawMutex, PinConfig, 1>, - // uart_pins: &'static SunsetMutex>, + channel: &'static mut PinChannel<'static>, ) { // Hardware UART setup let uart_config = Config::default().with_rx( @@ -146,34 +125,26 @@ async fn uart_task( .with_timeout(1) ); - // lock whole pin config. - // let mut pin_config = receiver.receive().await; - - let mut rx = channel.recv_rx().await.unwrap(); - let mut tx = channel.recv_tx().await.unwrap(); - - let uart = Uart::new(uart_periph, uart_config) - .unwrap() - .with_rx(rx.reborrow()) - .with_tx(tx.reborrow()) - .into_async(); - - // Run the main buffered TX/RX loop - buffer.run(uart).await; - - channel.send_rx(rx).await.unwrap(); - channel.send_tx(tx).await.unwrap(); - - // TODO: Better way - - // channel.with_rx_and_tx(|rx, tx| async { + // send_and_recv_channel!( // let uart = Uart::new(uart_periph, uart_config) // .unwrap() - // .with_rx(rx.reborrow()) - // .with_tx(tx.reborrow()) + // .with_rx(rx) + // .with_tx(tx) // .into_async(); // // Run the main buffered TX/RX loop // buffer.run(uart).await; - // }); + // ); + + // Sync pin config via channels + channel.with_channel(|rx, tx| async { + let uart = Uart::new(uart_periph, uart_config) + .unwrap() + .with_rx(rx) + .with_tx(tx) + .into_async(); + + // Run the main buffered TX/RX loop + buffer.run(uart).await; + }).await.unwrap(); } From 73e8c5c71d2981ab39ee0a2e4871ff97d8b94a9e Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 25 Aug 2025 20:49:38 +1000 Subject: [PATCH 28/86] Bit more cleanup --- src/main.rs | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/main.rs b/src/main.rs index 49e87be..911b2b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -112,12 +112,6 @@ async fn main(spawner: Spawner) -> ! { PinChannel::new(serde_pin_config, gpios) }); - // Send first value for tasks. - // sender.send(PinConfig::new(GPIOConfig { - // gpio10: Some(peripherals.GPIO10.into()), - // gpio11: Some(peripherals.GPIO11.into()), - // }, serde_pin_config).unwrap()).await; - // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality let uart1 = peripherals.UART1; @@ -135,9 +129,6 @@ async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, channel: &'static mut PinChannel, - // sender: Sender<'static, CriticalSectionRawMutex, PinConfig, 1>, - // receiver: Receiver<'static, CriticalSectionRawMutex, PinConfig, 1>, - // uart_pins: &'static SunsetMutex>, ) { // Hardware UART setup let uart_config = Config::default().with_rx( @@ -163,17 +154,4 @@ async fn uart_task( channel.send_rx(rx).await.unwrap(); channel.send_tx(tx).await.unwrap(); - - // TODO: Better way - - // channel.with_rx_and_tx(|rx, tx| async { - // let uart = Uart::new(uart_periph, uart_config) - // .unwrap() - // .with_rx(rx.reborrow()) - // .with_tx(tx.reborrow()) - // .into_async(); - - // // Run the main buffered TX/RX loop - // buffer.run(uart).await; - // }); } From a54ae13b2ea17abcb58296a4f5c2d4b74006fa82 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 25 Aug 2025 20:58:10 +1000 Subject: [PATCH 29/86] Nightly works, AsyncFnOnce should be stabilised around 6 months to 1 year from now. --- rust-toolchain.toml | 3 +- src/config.rs | 154 ++++++++++++++------------------------------ src/main.rs | 4 +- 3 files changed, 52 insertions(+), 109 deletions(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 17c702b..f3683df 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,5 @@ [toolchain] -channel = "stable-2025-04-03" +#channel = "stable-2025-04-03" +channel = "nightly" components = ["rust-src"] targets = ["riscv32imac-unknown-none-elf"] diff --git a/src/config.rs b/src/config.rs index 7a19505..28a6b7d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,3 @@ -use core::future::Future; use core::net::Ipv4Addr; use embassy_net::{Ipv4Cidr, StaticConfigV4}; use esp_hal::gpio::AnyPin; @@ -69,53 +68,21 @@ impl Default for SerdePinConfig { } } -// pub type RxPin<'a> = AnyPin<'a>; -// pub type TxPin<'a> = AnyPin<'a>; - -pub struct TxRxPin<'a> { - tx: Option>, - rx: Option>, - send_back: &'a mut PinChannel<'a> -} - - -impl<'a> Drop for TxRxPin<'a> { - fn drop(&mut self) { - let tx = self.tx.take().unwrap(); - let rx = self.rx.take().unwrap(); - - self.send_back.try_send_tx(tx).unwrap(); - self.send_back.try_send_rx(rx).unwrap(); - } -} - -pub struct RxPin<'a> { - pin: Option>, - send_back: &'a mut PinChannel<'a> -} - -impl<'a> Drop for RxPin<'a> { - fn drop(&mut self) { - let pin = self.pin.take().unwrap(); - self.send_back.try_send_rx(pin).unwrap(); - } +pub struct GPIOConfig { + pub gpio10: Option>, + pub gpio11: Option>, } -pub struct GPIOConfig<'a> { - pub gpio10: Option>, - pub gpio11: Option>, -} - -pub struct PinChannel<'a> { +pub struct PinChannel { pub config: SerdePinConfig, - pub gpios: GPIOConfig<'a>, + pub gpios: GPIOConfig, pub tx: Channel::, pub rx: Channel::, // TODO: cts/rts pins } -impl<'a> PinChannel<'a> { - pub fn new(config: SerdePinConfig, gpios: GPIOConfig<'a>) -> Self { +impl PinChannel { + pub fn new(config: SerdePinConfig, gpios: GPIOConfig) -> Self { Self { config, gpios, @@ -124,7 +91,7 @@ impl<'a> PinChannel<'a> { } } - pub async fn recv_tx(&mut self) -> errors::Result> { + pub async fn recv_tx(&mut self) -> errors::Result> { // tx needs to lock here. self.tx.receive().await; @@ -135,7 +102,7 @@ impl<'a> PinChannel<'a> { }) } - pub async fn send_tx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { + pub async fn send_tx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { match self.config.tx { 10 => self.gpios.gpio10 = Some(pin), 11 => self.gpios.gpio11 = Some(pin), @@ -147,19 +114,7 @@ impl<'a> PinChannel<'a> { Ok(()) } - pub fn try_send_tx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { - match self.config.tx { - 10 => self.gpios.gpio10 = Some(pin), - 11 => self.gpios.gpio11 = Some(pin), - _ => return Err(errors::Error::InvalidPin) - }; - - // tx lock needs to be released. - self.tx.try_send(()).unwrap(); - Ok(()) - } - - pub async fn recv_rx(&mut self) -> errors::Result> { + pub async fn recv_rx(&mut self) -> errors::Result> { // rx needs to lock here. self.rx.receive().await; @@ -170,7 +125,7 @@ impl<'a> PinChannel<'a> { }) } - pub async fn send_rx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { + pub async fn send_rx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { match self.config.rx { 10 => self.gpios.gpio10 = Some(pin), 11 => self.gpios.gpio11 = Some(pin), @@ -182,28 +137,15 @@ impl<'a> PinChannel<'a> { Ok(()) } - pub fn try_send_rx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { - match self.config.rx { - 10 => self.gpios.gpio10 = Some(pin), - 11 => self.gpios.gpio11 = Some(pin), - _ => return Err(errors::Error::InvalidPin) - }; - - // rx lock needs to be released. - self.rx.try_send(()).unwrap(); - Ok(()) - } + pub async fn with_channel(&mut self, f: F) -> errors::Result<()> + where F: for<'a> AsyncFnOnce(AnyPin<'a>, AnyPin<'a>) { + let mut rx = self.recv_rx().await?; + let mut tx = self.recv_tx().await?; - pub async fn with_channel(&'a mut self, f: F) -> errors::Result<()> - where F: FnOnce(TxRxPin) -> Fut, - Fut: Future { - let rx = self.recv_rx().await?; - let tx = self.recv_tx().await?; + f(rx.reborrow(), tx.reborrow()).await; - f(TxRxPin { tx: Some(tx), rx: Some(rx), send_back: self }).await; - - // self.send_rx(rx).await.unwrap(); - // self.send_tx(tx).await.unwrap(); + self.send_rx(rx).await.unwrap(); + self.send_tx(tx).await.unwrap(); Ok(()) } @@ -238,37 +180,37 @@ impl PinConfigAlt { } } -// impl PinConfig { -// pub fn new(mut gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { -// if config_inner.rx == config_inner.tx { -// return Err(errors::Error::InvalidPin); -// } +impl PinConfig { + pub fn new(mut gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { + if config_inner.rx == config_inner.tx { + return Err(errors::Error::InvalidPin); + } -// // SAFETY: Safe because moved in peripherals. -// Ok(Self { -// rx: match config_inner.rx { -// 10 => gpio_config.gpio10.take().unwrap().into(), -// 11 => gpio_config.gpio11.take().unwrap().into(), -// _ => return Err(errors::Error::InvalidPin), -// }, -// tx: match config_inner.tx { -// 10 => gpio_config.gpio10.take().unwrap().into(), -// 11 => gpio_config.gpio11.take().unwrap().into(), -// _ => return Err(errors::Error::InvalidPin), -// } -// }) -// } - -// /// Resolves a u8 pin number into an AnyPin GPIO type. -// /// Returns None if the pin number is invalid or unsupported. -// pub fn initialize_pin(peripherals: peripherals::Peripherals, pin_number: u8) -> errors::Result> { -// match pin_number { -// 0 => Ok(peripherals.GPIO0.into()), - -// _ => Err(errors::Error::InvalidPin), -// } -// } -// } + // SAFETY: Safe because moved in peripherals. + Ok(Self { + rx: match config_inner.rx { + 10 => gpio_config.gpio10.take().unwrap().into(), + 11 => gpio_config.gpio11.take().unwrap().into(), + _ => return Err(errors::Error::InvalidPin), + }, + tx: match config_inner.tx { + 10 => gpio_config.gpio10.take().unwrap().into(), + 11 => gpio_config.gpio11.take().unwrap().into(), + _ => return Err(errors::Error::InvalidPin), + } + }) + } + + /// Resolves a u8 pin number into an AnyPin GPIO type. + /// Returns None if the pin number is invalid or unsupported. + pub fn initialize_pin(peripherals: peripherals::Peripherals, pin_number: u8) -> errors::Result> { + match pin_number { + 0 => Ok(peripherals.GPIO0.into()), + + _ => Err(errors::Error::InvalidPin), + } + } +} impl SSHConfig { /// Bump this when the format changes diff --git a/src/main.rs b/src/main.rs index 7806de0..c784e6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -116,7 +116,7 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - channel: &'static mut PinChannel<'static>, + channel: &'static mut PinChannel, ) { // Hardware UART setup let uart_config = Config::default().with_rx( @@ -137,7 +137,7 @@ async fn uart_task( // ); // Sync pin config via channels - channel.with_channel(|rx, tx| async { + channel.with_channel(async |rx, tx| { let uart = Uart::new(uart_periph, uart_config) .unwrap() .with_rx(rx) From 1ea08e85af3dedf9735f1482d7011f5fba4ec32a Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 29 Aug 2025 16:00:19 +1000 Subject: [PATCH 30/86] Working on hardware now, the channels/signaling logic needs to be fixes as the current arrangement locks while waiting to send/receive pin configurations, /cc @mmalenic --- src/config.rs | 22 +++++++++++++++------- src/main.rs | 17 ++++++----------- src/serve.rs | 2 +- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/config.rs b/src/config.rs index 28a6b7d..607e7ec 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ use esp_hal::gpio::AnyPin; use esp_hal::peripherals; use heapless::{String, Vec}; -use esp_println::println; +use esp_println::{dbg, println}; use bcrypt; use hmac::{Hmac, Mac}; @@ -93,7 +93,7 @@ impl PinChannel { pub async fn recv_tx(&mut self) -> errors::Result> { // tx needs to lock here. - self.tx.receive().await; + //self.tx.receive().await; Ok(match self.config.tx { 10 => self.gpios.gpio10.take().ok_or_else(|| errors::Error::InvalidPin)?, @@ -115,14 +115,18 @@ impl PinChannel { } pub async fn recv_rx(&mut self) -> errors::Result> { - // rx needs to lock here. - self.rx.receive().await; - - Ok(match self.config.rx { + let res = Ok(match self.config.rx { 10 => self.gpios.gpio10.take().ok_or_else(|| errors::Error::InvalidPin)?, 11 => self.gpios.gpio11.take().ok_or_else(|| errors::Error::InvalidPin)?, _ => return Err(errors::Error::InvalidPin) - }) + }); + dbg!("recv_rx: no channel receive"); + // rx needs to lock here. + // dbg!("recv_rx: before rx.receive.await"); + // self.rx.receive().await; + // dbg!("recv_rx: after rx.receive.await"); + + res } pub async fn send_rx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { @@ -139,11 +143,15 @@ impl PinChannel { pub async fn with_channel(&mut self, f: F) -> errors::Result<()> where F: for<'a> AsyncFnOnce(AnyPin<'a>, AnyPin<'a>) { + dbg!("inner: with_channel begin, recv_rx call"); let mut rx = self.recv_rx().await?; + dbg!("inner: with_channel recv_tx call"); let mut tx = self.recv_tx().await?; + dbg!("inner: with_channel f-reborrow"); f(rx.reborrow(), tx.reborrow()).await; + dbg!("inner: with_channel, before send{rx/tx}"); self.send_rx(rx).await.unwrap(); self.send_tx(tx).await.unwrap(); diff --git a/src/main.rs b/src/main.rs index c784e6b..05394f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use esp_hal::{ }; use esp_hal_embassy::InterruptExecutor; +use esp_println::dbg; use esp_storage::FlashStorage; use embassy_executor::Spawner; use ssh_stamp::{config::SSHConfig, espressif::{ @@ -118,6 +119,7 @@ async fn uart_task( uart_periph: UART1<'static>, channel: &'static mut PinChannel, ) { + dbg!("spawning UART task..."); // Hardware UART setup let uart_config = Config::default().with_rx( RxConfig::default() @@ -125,19 +127,10 @@ async fn uart_task( .with_timeout(1) ); - // send_and_recv_channel!( - // let uart = Uart::new(uart_periph, uart_config) - // .unwrap() - // .with_rx(rx) - // .with_tx(tx) - // .into_async(); - - // // Run the main buffered TX/RX loop - // buffer.run(uart).await; - // ); - + dbg!("before with_channel"); // Sync pin config via channels channel.with_channel(async |rx, tx| { + dbg!("into with_channel"); let uart = Uart::new(uart_periph, uart_config) .unwrap() .with_rx(rx) @@ -145,6 +138,8 @@ async fn uart_task( .into_async(); // Run the main buffered TX/RX loop + dbg!("before buffer_run"); buffer.run(uart).await; + dbg!("after buffer_run"); }).await.unwrap(); } diff --git a/src/serve.rs b/src/serve.rs index 36ec1bc..8e8b073 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -31,7 +31,7 @@ async fn connection_loop( loop { let mut ph = ProgressHolder::new(); let ev = serv.progress(&mut ph).await?; - dbg!(&ev); + //dbg!(&ev); #[allow(unreachable_patterns)] match ev { ServEvent::SessionShell(a) => { From d4f84cc06bdaa04a402f477e6ea1e76f6913b64b Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 9 Sep 2025 21:54:40 +0700 Subject: [PATCH 31/86] Available gpios, not just gpios --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 05394f0..8810dce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -91,14 +91,14 @@ async fn main(spawner: Spawner) -> ! { }; - let gpios = GPIOConfig { + let available_gpios = GPIOConfig { gpio10: Some(peripherals.GPIO10.degrade()), gpio11: Some(peripherals.GPIO11.degrade()), }; static CHANNEL: StaticCell = StaticCell::new(); let channel = CHANNEL.init({ - PinChannel::new(serde_pin_config, gpios) + PinChannel::new(serde_pin_config, available_gpios) }); // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality From c19423ae222d180a5fca3926f00cb296eab8c184 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 9 Sep 2025 21:56:05 +0700 Subject: [PATCH 32/86] Event itself should trigger env/config/serialization --- Cargo.lock | 3 --- Cargo.toml | 9 ++++++--- src/serve.rs | 3 +++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d175704..7ae716c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1793,7 +1793,6 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sunset" version = "0.3.0" -source = "git+https://github.com/mkj/sunset?rev=5ff44bb#5ff44bb1f62db25d4dc3da5b4c891bd4a1ba352e" dependencies = [ "aes", "ascii", @@ -1824,7 +1823,6 @@ dependencies = [ [[package]] name = "sunset-async" version = "0.3.0" -source = "git+https://github.com/mkj/sunset?rev=5ff44bb#5ff44bb1f62db25d4dc3da5b4c891bd4a1ba352e" dependencies = [ "embassy-futures", "embassy-sync 0.7.0", @@ -1837,7 +1835,6 @@ dependencies = [ [[package]] name = "sunset-sshwire-derive" version = "0.2.0" -source = "git+https://github.com/mkj/sunset?rev=5ff44bb#5ff44bb1f62db25d4dc3da5b4c891bd4a1ba352e" dependencies = [ "virtue", ] diff --git a/Cargo.toml b/Cargo.toml index 6c81f33..87cfe9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,12 @@ hex = { version = "0.4", default-features = false } log = { version = "0.4" } static_cell = { version = "2", features = ["nightly"] } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } -sunset = { git="https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false, features = ["openssh-key", "embedded-io"]} -sunset-async = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false, features = ["multi-thread"]} -sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false } +# sunset = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false, features = ["openssh-key", "embedded-io"]} +# sunset-async = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false, features = ["multi-thread"]} +# sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false } +sunset = { path = "../sunset", default-features = false, features = ["openssh-key", "embedded-io"]} +sunset-async = { path = "../sunset/async", default-features = false, features = ["multi-thread"]} +sunset-sshwire-derive = { path = "../sunset/sshwire-derive", default-features = false } getrandom = { version = "0.2.10", features = ["custom"] } embassy-sync = "0.7" heapless = "0.8" diff --git a/src/serve.rs b/src/serve.rs index 8e8b073..f063bd9 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -81,6 +81,9 @@ async fn connection_loop( ServEvent::Defunct | ServEvent::SessionShell(_) => { println!("Expected caller to handle event"); error::BadUsage.fail()? + } + ServEvent::Environment(_) => { + // TODO: Logic to serialise/validate env vars? }, _ => () }; From bbcc09e9f6eb85e501e55b03208e8adc72271011 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Sun, 21 Sep 2025 17:09:17 +1000 Subject: [PATCH 33/86] Re-enabled partitions.csv since it's needed for NVS/SSHConfig/FlashConfig persistence, added a couple of OTA partitions to help out @jubeormk1. Narrowed down flash write errors (erase works now) to sector size alignment issues /cc @mmalenic. [ci skip] --- .cargo/config.toml | 4 ++-- Cargo.lock | 1 + Cargo.toml | 1 + partitions.csv | 12 +++++++---- src/config.rs | 7 ++++-- src/serve.rs | 15 ++++++++----- src/storage.rs | 54 +++++++++++++++++++++++++--------------------- 7 files changed, 57 insertions(+), 37 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 1513072..b334bf0 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -24,8 +24,8 @@ rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32"'] runner = "espflash flash --baud=921600 --monitor" rustflags = [ "-C", "force-frame-pointers"] [target.riscv32imac-unknown-none-elf] -#runner = "espflash flash --baud=921600 --partition-table partitions.csv --monitor" -runner = "espflash flash --baud=921600 --monitor" +runner = "espflash flash --baud=921600 --partition-table partitions.csv --monitor" +#runner = "espflash flash --baud=921600 --monitor" rustflags = [ "-C", "force-frame-pointers"] [target.xtensa-esp32s2-none-elf] runner = "espflash flash --baud=921600 --monitor --chip esp32s2" diff --git a/Cargo.lock b/Cargo.lock index 7ae716c..58e1342 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1730,6 +1730,7 @@ dependencies = [ "log", "paste", "portable-atomic", + "pretty-hex", "sha2", "smoltcp", "snafu", diff --git a/Cargo.toml b/Cargo.toml index 87cfe9e..a618cc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ portable-atomic = "1" esp-bootloader-esp-idf = "0.1" snafu = { version = "0.8.6", default-features = false } paste = "1" +pretty-hex = { version = "0.4", default-features = false } [profile.dev] # Rust debug is too slow. diff --git a/partitions.csv b/partitions.csv index c3e6609..1abad2e 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,4 +1,8 @@ -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 1M, -config, data, nvs, 0x110000, 0x2000, +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x2000, +otadata, data, ota, 0xd000, 0x2000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 0x200000, +ota_0, app, ota_0, 0x210000, 0x100000, +ota_1, app, ota_1, 0x310000, 0x100000, \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 607e7ec..324fb1f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ use esp_hal::gpio::AnyPin; use esp_hal::peripherals; use heapless::{String, Vec}; -use esp_println::{dbg, println}; +use esp_println::dbg; use bcrypt; use hmac::{Hmac, Mac}; @@ -262,6 +262,10 @@ impl SSHConfig { false } } + + // pub fn config_change(&mut self, conf: SSHConfig) -> Result<()> { + // ServEvent::ConfigChange(); + // } } fn random_mac() -> Result<[u8; 6]> { @@ -352,7 +356,6 @@ where impl SSHEncode for SSHConfig { fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { - println!("enc si"); enc_signkey(&self.hostkey, s)?; enc_option(&self.admin_pw, s)?; diff --git a/src/serve.rs b/src/serve.rs index f063bd9..c2b790c 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -72,19 +72,24 @@ async fn connection_loop( } } } + ServEvent::Environment(a) => { + // TODO: Logic to serialise/validate env vars? I.e: + // config.validate(a) + // config.save(a) + // SSHConfig c = a.validate(); // Checks the input variable, sanitizes, assigns a target subsystem + // a.config_change(c)?; + a.succeed()?; // FIXME: Not just succeed... + }, ServEvent::SessionPty(a) => { a.succeed()?; - } + }, ServEvent::SessionExec(a) => { a.fail()?; - } + }, ServEvent::Defunct | ServEvent::SessionShell(_) => { println!("Expected caller to handle event"); error::BadUsage.fail()? } - ServEvent::Environment(_) => { - // TODO: Logic to serialise/validate env vars? - }, _ => () }; } diff --git a/src/storage.rs b/src/storage.rs index 345dab5..df34934 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -2,6 +2,7 @@ use esp_println::{println, dbg}; use esp_storage::FlashStorage; use embedded_storage::ReadStorage; +use pretty_hex::PrettyHex; use sha2::Digest; use core::borrow::Borrow; @@ -9,15 +10,15 @@ use core::borrow::Borrow; use embedded_storage::nor_flash::NorFlash; use sunset::error::Error; -use sunset::sshwire; -use sunset::sshwire::OwnOrBorrow; +use sunset::sshwire::{self, OwnOrBorrow}; use sunset_sshwire_derive::*; use crate::config::SSHConfig; -// TODO: Adapt those for Espressif targets... -pub const CONFIG_AREA_SIZE: usize = 460; -const CONFIG_OFFSET: u32 = 0x110000; +pub const CONFIG_VERSION_SIZE: usize = 4; +pub const CONFIG_HASH_SIZE: usize = 32; +pub const CONFIG_AREA_SIZE: usize = 8192; +pub const CONFIG_OFFSET: usize = 0x9000; pub struct Fl { flash: FlashStorage, @@ -42,10 +43,13 @@ struct FlashConfig<'a> { } impl FlashConfig<'_> { - const BUF_SIZE: usize = 4 + CONFIG_AREA_SIZE + 32; + const BUF_SIZE: usize = CONFIG_VERSION_SIZE + CONFIG_AREA_SIZE + CONFIG_HASH_SIZE; } -const _: () = - assert!(FlashConfig::BUF_SIZE % 4 == 0, "flash reads must be a multiple of 4"); + +// TODO: Does not match esp_storage assumptions about SECTOR_SIZE alignment? +// +// const _: () = +// assert!(FlashConfig::BUF_SIZE % 4 == 0, "flash reads must be a multiple of 4"); fn config_hash(config: &SSHConfig) -> Result<[u8; 32], Error> { let mut h = sha2::Sha256::new(); @@ -68,30 +72,30 @@ pub async fn load_or_create(flash: &mut Fl) -> Result { pub async fn create(flash: &mut Fl) -> Result { let c = SSHConfig::new()?; - if let Err(_) = save(flash, &c).await { - println!("Error writing config"); - } + save(flash, &c).await?; + Ok(c) } pub async fn load(fl: &mut Fl) -> Result { - fl.flash.read(CONFIG_OFFSET, &mut fl.buf).map_err(|_e| { + fl.flash.read(CONFIG_OFFSET as u32, &mut fl.buf).map_err(|_e| { dbg!("flash read error 0x{CONFIG_OFFSET:x} {e:?}"); Error::msg("flash error") })?; - let s: FlashConfig = sshwire::read_ssh(&fl.buf, None)?; + let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None) + .map_err(|_| Error::msg("failed to decode flash config"))?; - if s.version != SSHConfig::CURRENT_VERSION { + if flash_config.version != SSHConfig::CURRENT_VERSION { return Err(Error::msg("wrong config version")); } - let calc_hash = config_hash(s.config.borrow())?; - if calc_hash != s.hash { + let calc_hash = config_hash(flash_config.config.borrow())?; + if calc_hash != flash_config.hash { return Err(Error::msg("bad config hash")); } - if let OwnOrBorrow::Own(c) = s.config { + if let OwnOrBorrow::Own(c) = flash_config.config { Ok(c) } else { // OK panic - OwnOrBorrow always decodes to Own variant @@ -108,16 +112,18 @@ pub async fn save(fl: &mut Fl, config: &SSHConfig) -> Result<(), Error> { let l = sshwire::write_ssh(&mut fl.buf, &sc)?; let buf = &fl.buf[..l]; - dbg!("flash erase"); + dbg!(&buf.hex_dump()); + fl.flash - // TODO: Adapt 4096, ERASE_SIZE in rp, what's in Espressif? - .erase(CONFIG_OFFSET, CONFIG_OFFSET + 4096 as u32) - .map_err(|_| Error::msg("flash erase error"))?; + //.erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + CONFIG_AREA_SIZE + 32) as u32) + .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + CONFIG_AREA_SIZE) as u32).unwrap(); + //.map_err(|_| Error::msg("flash erase error"))?; + + // TODO: Add padding to &buf to satisfy underlying nor_flash alignment requirements (4096-aligned SECTOR_SIZE) - dbg!("flash write"); fl.flash - .write(CONFIG_OFFSET, &buf) - .map_err(|_| Error::msg("flash write error"))?; + .write(CONFIG_OFFSET as u32, &buf).unwrap(); + //.map_err(|_| Error::msg("flash write error"))?; println!("flash save done"); Ok(()) From 53f192329d2e4e90ad4997b41dcfa7c1a494cbe7 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 22 Sep 2025 19:56:44 +1000 Subject: [PATCH 34/86] Separate sunset and ssh-stamp errors, add ipv6 stub to determine some nor flash writing offsets issue... hash payload on flash is 2 bytes off still and yields 'bad config hash' [ci skip] --- partitions.csv | 12 ++++---- src/config.rs | 22 ++++++++------ src/errors.rs | 2 ++ src/settings.rs | 2 +- src/storage.rs | 79 ++++++++++++++++++++++++++++++++----------------- 5 files changed, 74 insertions(+), 43 deletions(-) diff --git a/partitions.csv b/partitions.csv index 1abad2e..dcf4016 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,8 +1,8 @@ # ESP-IDF Partition Table # Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x2000, -otadata, data, ota, 0xd000, 0x2000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x200000, -ota_0, app, ota_0, 0x210000, 0x100000, -ota_1, app, ota_1, 0x310000, 0x100000, \ No newline at end of file +app_config, data, nvs, 0x9000, 0x2000, +otadata, data, ota, 0xd000, 0x2000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 0x200000, +ota_0, app, ota_0, 0x210000, 0x100000, +ota_1, app, ota_1, 0x310000, 0x100000, \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 324fb1f..d298134 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,5 @@ use core::net::Ipv4Addr; -use embassy_net::{Ipv4Cidr, StaticConfigV4}; +use embassy_net::{Ipv4Cidr, StaticConfigV4, StaticConfigV6}; use esp_hal::gpio::AnyPin; use esp_hal::peripherals; use heapless::{String, Vec}; @@ -41,7 +41,8 @@ pub struct SSHConfig { /// Only intended purpose I see for keeping it here is for spoofing? pub mac: [u8; 6], /// `None` for DHCP - pub ip4_static: Option, + pub ipv4_static: Option, + pub ipv6_static: Option, /// UART pub uart_pins: SerdePinConfig, } @@ -245,7 +246,8 @@ impl SSHConfig { wifi_ssid, wifi_pw, mac, - ip4_static: None, + ipv4_static: None, + ipv6_static: None, uart_pins, }) } @@ -307,7 +309,7 @@ where bool::dec(s)?.then(|| SSHDecode::dec(s)).transpose() } -fn enc_ip4config(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { +fn enc_ipv4_config(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { v.is_some().enc(s)?; if let Some(v) = v { v.address.address().to_bits().enc(s)?; @@ -319,7 +321,7 @@ fn enc_ip4config(v: &Option, s: &mut dyn SSHSink) -> WireResult< Ok(()) } -fn dec_ip4config<'de, S>(s: &mut S) -> WireResult> +fn dec_ipv4_config<'de, S>(s: &mut S) -> WireResult> where S: SSHSource<'de>, { @@ -368,7 +370,7 @@ impl SSHEncode for SSHConfig { self.mac.enc(s)?; - enc_ip4config(&self.ip4_static, s)?; + enc_ipv4_config(&self.ipv4_static, s)?; Ok(()) } @@ -393,9 +395,10 @@ impl<'de> SSHDecode<'de> for SSHConfig { let mac = SSHDecode::dec(s)?; - let ip4_static = dec_ip4config(s)?; + let ipv4_static = dec_ipv4_config(s)?; + let ipv6_static = None; // TODO: Decode ipv6_config - // Decode password_authentication (missing in original code) + // Decode password_authentication let password_authentication = SSHDecode::dec(s)?; let uart_pins = dec_uart_pins(s)?; @@ -408,7 +411,8 @@ impl<'de> SSHDecode<'de> for SSHConfig { wifi_ssid, wifi_pw, mac, - ip4_static, + ipv4_static, + ipv6_static, uart_pins, }) } diff --git a/src/errors.rs b/src/errors.rs index 65c498b..e0c9779 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -7,4 +7,6 @@ pub type Result = result::Result; pub enum Error { #[snafu(display("Invalid PIN provided"))] InvalidPin, + #[snafu(display("Flash storage error"))] + FlashStorageError, } \ No newline at end of file diff --git a/src/settings.rs b/src/settings.rs index 91ab42e..b935e27 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -10,4 +10,4 @@ pub(crate) const KEY_SLOTS: usize = 3; // TODO: Document whether this a "reasona // UART settings //pub(crate) const BAUD_RATE: u32 = 115200; -//pub(crate) const UART_SETTINGS: &str = "8N1"; +//pub(crate) const UART_SETTINGS: &str = "8N1"; \ No newline at end of file diff --git a/src/storage.rs b/src/storage.rs index df34934..549d200 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,3 +1,4 @@ +use esp_bootloader_esp_idf::partitions; use esp_println::{println, dbg}; use esp_storage::FlashStorage; use embedded_storage::ReadStorage; @@ -9,7 +10,9 @@ use core::borrow::Borrow; use embedded_storage::nor_flash::NorFlash; -use sunset::error::Error; +use sunset::error::Error as SunsetError; +use crate::errors::Error as SSHStampError; + use sunset::sshwire::{self, OwnOrBorrow}; use sunset_sshwire_derive::*; @@ -17,7 +20,7 @@ use crate::config::SSHConfig; pub const CONFIG_VERSION_SIZE: usize = 4; pub const CONFIG_HASH_SIZE: usize = 32; -pub const CONFIG_AREA_SIZE: usize = 8192; +pub const CONFIG_AREA_SIZE: usize = 4096; pub const CONFIG_OFFSET: usize = 0x9000; pub struct Fl { @@ -38,27 +41,46 @@ impl<'a> Fl { struct FlashConfig<'a> { version: u8, config: OwnOrBorrow<'a, SSHConfig>, + // _pad: [u8; 4], // example padding you may have missed /// sha256 hash of config hash: [u8; 32], } impl FlashConfig<'_> { const BUF_SIZE: usize = CONFIG_VERSION_SIZE + CONFIG_AREA_SIZE + CONFIG_HASH_SIZE; -} -// TODO: Does not match esp_storage assumptions about SECTOR_SIZE alignment? -// -// const _: () = -// assert!(FlashConfig::BUF_SIZE % 4 == 0, "flash reads must be a multiple of 4"); + // TODO: Rework Error mapping with esp_storage errors + /// Finds the NVS partitions and retrieves information about it. + pub fn find_config_partition() -> Result<(), SSHStampError> { + let mut flash = FlashStorage::new(); + println!("Flash size = {} bytes", flash.capacity()); + + let mut pt_mem = [0u8; partitions::PARTITION_TABLE_MAX_LEN]; + let pt = partitions::read_partition_table(&mut flash, &mut pt_mem).unwrap(); + let nvs = pt + .find_partition(partitions::PartitionType::Data( + partitions::DataPartitionSubType::Nvs, + )) + .unwrap() + .unwrap(); + + let nvs_partition = nvs.as_embedded_storage(&mut flash); + + println!("NVS partition size = {}", nvs_partition.capacity()); + println!("NVS partition offset = 0x{:x}", nvs.offset()); + + Ok(()) + } +} -fn config_hash(config: &SSHConfig) -> Result<[u8; 32], Error> { +fn config_hash(config: &SSHConfig) -> Result<[u8; 32], SunsetError> { let mut h = sha2::Sha256::new(); sshwire::hash_ser(&mut h, config)?; Ok(h.finalize().into()) } /// Loads a SSHConfig at startup. Good for persisting hostkeys. -pub async fn load_or_create(flash: &mut Fl) -> Result { +pub async fn load_or_create(flash: &mut Fl) -> Result { match load(flash).await { Ok(c) => { println!("Good existing config"); @@ -70,29 +92,33 @@ pub async fn load_or_create(flash: &mut Fl) -> Result { create(flash).await } -pub async fn create(flash: &mut Fl) -> Result { +pub async fn create(flash: &mut Fl) -> Result { let c = SSHConfig::new()?; save(flash, &c).await?; Ok(c) } -pub async fn load(fl: &mut Fl) -> Result { +pub async fn load(fl: &mut Fl) -> Result { fl.flash.read(CONFIG_OFFSET as u32, &mut fl.buf).map_err(|_e| { dbg!("flash read error 0x{CONFIG_OFFSET:x} {e:?}"); - Error::msg("flash error") + SunsetError::msg("flash error") })?; - let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None) - .map_err(|_| Error::msg("failed to decode flash config"))?; + let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).unwrap(); +// .map_err(|_| SunsetError::msg("failed to decode flash config"))?; if flash_config.version != SSHConfig::CURRENT_VERSION { - return Err(Error::msg("wrong config version")); + return Err(SunsetError::msg("wrong config version")); } let calc_hash = config_hash(flash_config.config.borrow())?; + + dbg!(&calc_hash.hex_dump()); + dbg!(&flash_config.hash.hex_dump()); + if calc_hash != flash_config.hash { - return Err(Error::msg("bad config hash")); + return Err(SunsetError::msg("bad config hash")); } if let OwnOrBorrow::Own(c) = flash_config.config { @@ -103,27 +129,26 @@ pub async fn load(fl: &mut Fl) -> Result { } } -pub async fn save(fl: &mut Fl, config: &SSHConfig) -> Result<(), Error> { +pub async fn save(fl: &mut Fl, config: &SSHConfig) -> Result<(), SunsetError> { let sc = FlashConfig { version: SSHConfig::CURRENT_VERSION, config: OwnOrBorrow::Borrow(&config), + // _pad: [0u8; 4], hash: config_hash(&config)?, }; - let l = sshwire::write_ssh(&mut fl.buf, &sc)?; - let buf = &fl.buf[..l]; - dbg!(&buf.hex_dump()); + FlashConfig::find_config_partition().unwrap(); - fl.flash - //.erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + CONFIG_AREA_SIZE + 32) as u32) - .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + CONFIG_AREA_SIZE) as u32).unwrap(); - //.map_err(|_| Error::msg("flash erase error"))?; + let _l = sshwire::write_ssh(&mut fl.buf, &sc)?; + // let buf = &fl.buf[..l]; + + dbg!(CONFIG_OFFSET + FlashConfig::BUF_SIZE); - // TODO: Add padding to &buf to satisfy underlying nor_flash alignment requirements (4096-aligned SECTOR_SIZE) + fl.flash + .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + FlashConfig::BUF_SIZE + 4060) as u32).unwrap(); fl.flash - .write(CONFIG_OFFSET as u32, &buf).unwrap(); - //.map_err(|_| Error::msg("flash write error"))?; + .write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); println!("flash save done"); Ok(()) From d4613779f3e7df6191307bac716971c54e829b92 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 23 Sep 2025 15:34:35 +1000 Subject: [PATCH 35/86] Config hash still on ~2 bytes offset, now getting PacketWrong while ssh_read-ing config. Add IPV6-specific address to SSHStampConfig (renamed from SSHConfig since potentially we'll have to DIY-decode ourselves due to the extra fields we'll change/introduce) [ci skip] --- src/config.rs | 52 ++++++++++++++++++++++++++++++++++++++------ src/espressif/net.rs | 6 ++--- src/main.rs | 4 ++-- src/storage.rs | 22 +++++++++---------- 4 files changed, 60 insertions(+), 24 deletions(-) diff --git a/src/config.rs b/src/config.rs index d298134..76716ea 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ use core::net::Ipv4Addr; -use embassy_net::{Ipv4Cidr, StaticConfigV4, StaticConfigV6}; +use core::net::Ipv6Addr; +use embassy_net::{Ipv4Cidr, Ipv6Cidr, StaticConfigV4, StaticConfigV6}; use esp_hal::gpio::AnyPin; use esp_hal::peripherals; use heapless::{String, Vec}; @@ -25,7 +26,7 @@ use crate::errors; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; #[derive(Debug)] -pub struct SSHConfig { +pub struct SSHStampConfig { pub hostkey: SignKey, pub password_authentication: bool, @@ -221,7 +222,7 @@ impl PinConfig { } } -impl SSHConfig { +impl SSHStampConfig { /// Bump this when the format changes pub const CURRENT_VERSION: u8 = 6; @@ -238,7 +239,7 @@ impl SSHConfig { let uart_pins = SerdePinConfig::default(); - Ok(SSHConfig { + Ok(SSHStampConfig { hostkey, password_authentication: true, admin_pw: None, @@ -321,6 +322,17 @@ fn enc_ipv4_config(v: &Option, s: &mut dyn SSHSink) -> WireResul Ok(()) } +fn enc_ipv6_config(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { + v.is_some().enc(s)?; + if let Some(v) = v { + v.address.address().to_bits().enc(s)?; + v.address.prefix_len().enc(s)?; + let gw = v.gateway.map(|a| a.to_bits()); + enc_option(&gw, s)?; + } + Ok(()) +} + fn dec_ipv4_config<'de, S>(s: &mut S) -> WireResult> where S: SSHSource<'de>, @@ -345,6 +357,30 @@ where .transpose() } +fn dec_ipv6_config<'de, S>(s: &mut S) -> WireResult> +where + S: SSHSource<'de>, +{ + let opt = bool::dec(s)?; + opt.then(|| { + let ad: u128 = SSHDecode::dec(s)?; + let ad = Ipv6Addr::from_bits(ad); + let prefix = SSHDecode::dec(s)?; + if prefix > 32 { + // embassy panics, so test it here + return Err(WireError::PacketWrong); + } + let gw: Option = dec_option(s)?; + let gateway = gw.map(|gw| Ipv6Addr::from_bits(gw)); + Ok(StaticConfigV6 { + address: Ipv6Cidr::new(ad, prefix), + gateway, + dns_servers: Vec::new(), + }) + }) + .transpose() +} + fn dec_uart_pins<'de, S>(s: &mut S) -> WireResult where S: SSHSource<'de>, @@ -356,7 +392,7 @@ where Ok(SerdePinConfig { tx, rx, rts, cts }) } -impl SSHEncode for SSHConfig { +impl SSHEncode for SSHStampConfig { fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { enc_signkey(&self.hostkey, s)?; enc_option(&self.admin_pw, s)?; @@ -371,12 +407,13 @@ impl SSHEncode for SSHConfig { self.mac.enc(s)?; enc_ipv4_config(&self.ipv4_static, s)?; + enc_ipv6_config(&self.ipv6_static, s)?; Ok(()) } } -impl<'de> SSHDecode<'de> for SSHConfig { +impl<'de> SSHDecode<'de> for SSHStampConfig { fn dec(s: &mut S) -> WireResult where S: SSHSource<'de>, @@ -394,9 +431,10 @@ impl<'de> SSHDecode<'de> for SSHConfig { let wifi_pw = dec_option(s)?; let mac = SSHDecode::dec(s)?; + //let _padding = [0u8; 3]; // ignore padding let ipv4_static = dec_ipv4_config(s)?; - let ipv6_static = None; // TODO: Decode ipv6_config + let ipv6_static = dec_ipv6_config(s)?; // Decode password_authentication let password_authentication = SSHDecode::dec(s)?; diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 9df84f2..75e0a2f 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -26,7 +26,7 @@ use edge_dhcp::{ use edge_nal::UdpBind; use edge_nal_embassy::{Udp, UdpBuffers}; -use crate::config::SSHConfig; +use crate::config::SSHStampConfig; use super::buffered_uart::BufferedUart; @@ -45,7 +45,7 @@ pub async fn if_up( wifi_controller: EspWifiController<'static>, wifi: WIFI<'static>, rng: &mut Rng, - config: &'static SunsetMutex + config: &'static SunsetMutex ) -> Result, sunset::Error> { let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); @@ -129,7 +129,7 @@ pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { } #[embassy_executor::task] -async fn wifi_up(mut controller: WifiController<'static>, config: &'static SunsetMutex) { +async fn wifi_up(mut controller: WifiController<'static>, config: &'static SunsetMutex) { println!("Device capabilities: {:?}", controller.capabilities()); let wifi_ssid = { diff --git a/src/main.rs b/src/main.rs index 8810dce..50d38c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use esp_hal_embassy::InterruptExecutor; use esp_println::dbg; use esp_storage::FlashStorage; use embassy_executor::Spawner; -use ssh_stamp::{config::SSHConfig, espressif::{ +use ssh_stamp::{config::SSHStampConfig, espressif::{ buffered_uart::BufferedUart, net::{accept_requests, if_up}, rng, @@ -61,7 +61,7 @@ async fn main(spawner: Spawner) -> ! { static FLASH: StaticCell> = StaticCell::new(); let _flash = FLASH.init(SunsetMutex::new(flash)); - static CONFIG: StaticCell> = StaticCell::new(); + static CONFIG: StaticCell> = StaticCell::new(); let config = CONFIG.init(SunsetMutex::new(config.unwrap())); let wifi_controller = esp_wifi::init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); diff --git a/src/storage.rs b/src/storage.rs index 549d200..bfe57d1 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -16,7 +16,7 @@ use crate::errors::Error as SSHStampError; use sunset::sshwire::{self, OwnOrBorrow}; use sunset_sshwire_derive::*; -use crate::config::SSHConfig; +use crate::config::SSHStampConfig; pub const CONFIG_VERSION_SIZE: usize = 4; pub const CONFIG_HASH_SIZE: usize = 32; @@ -40,8 +40,7 @@ impl<'a> Fl { #[derive(SSHEncode, SSHDecode)] struct FlashConfig<'a> { version: u8, - config: OwnOrBorrow<'a, SSHConfig>, - // _pad: [u8; 4], // example padding you may have missed + config: OwnOrBorrow<'a, SSHStampConfig>, /// sha256 hash of config hash: [u8; 32], } @@ -73,14 +72,14 @@ impl FlashConfig<'_> { } } -fn config_hash(config: &SSHConfig) -> Result<[u8; 32], SunsetError> { +fn config_hash(config: &SSHStampConfig) -> Result<[u8; 32], SunsetError> { let mut h = sha2::Sha256::new(); sshwire::hash_ser(&mut h, config)?; Ok(h.finalize().into()) } /// Loads a SSHConfig at startup. Good for persisting hostkeys. -pub async fn load_or_create(flash: &mut Fl) -> Result { +pub async fn load_or_create(flash: &mut Fl) -> Result { match load(flash).await { Ok(c) => { println!("Good existing config"); @@ -92,14 +91,14 @@ pub async fn load_or_create(flash: &mut Fl) -> Result { create(flash).await } -pub async fn create(flash: &mut Fl) -> Result { - let c = SSHConfig::new()?; +pub async fn create(flash: &mut Fl) -> Result { + let c = SSHStampConfig::new()?; save(flash, &c).await?; Ok(c) } -pub async fn load(fl: &mut Fl) -> Result { +pub async fn load(fl: &mut Fl) -> Result { fl.flash.read(CONFIG_OFFSET as u32, &mut fl.buf).map_err(|_e| { dbg!("flash read error 0x{CONFIG_OFFSET:x} {e:?}"); SunsetError::msg("flash error") @@ -108,7 +107,7 @@ pub async fn load(fl: &mut Fl) -> Result { let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).unwrap(); // .map_err(|_| SunsetError::msg("failed to decode flash config"))?; - if flash_config.version != SSHConfig::CURRENT_VERSION { + if flash_config.version != SSHStampConfig::CURRENT_VERSION { return Err(SunsetError::msg("wrong config version")); } @@ -129,11 +128,10 @@ pub async fn load(fl: &mut Fl) -> Result { } } -pub async fn save(fl: &mut Fl, config: &SSHConfig) -> Result<(), SunsetError> { +pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetError> { let sc = FlashConfig { - version: SSHConfig::CURRENT_VERSION, + version: SSHStampConfig::CURRENT_VERSION, config: OwnOrBorrow::Borrow(&config), - // _pad: [0u8; 4], hash: config_hash(&config)?, }; From d9ccdd3dbcadf441230c3761ea1b8a1eb598b4d5 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 1 Oct 2025 15:57:30 +1000 Subject: [PATCH 36/86] Feature-gate ipv6 for now, it was meant as an experiment to add/remove SSHStampConfig members [ci skip] --- Cargo.toml | 1 + src/config.rs | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a618cc5..bd04828 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ opt-level = "s" opt-level = "s" [features] +ipv6 = [] default = ["esp32c6"] # MCU options esp32 = [ diff --git a/src/config.rs b/src/config.rs index 76716ea..3fe3f9a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,9 @@ use core::net::Ipv4Addr; +#[cfg(feature = "ipv6")] use core::net::Ipv6Addr; -use embassy_net::{Ipv4Cidr, Ipv6Cidr, StaticConfigV4, StaticConfigV6}; +#[cfg(feature = "ipv6")] +use embassy_net::{Ipv6Cidr, StaticConfigV6}; +use embassy_net::{Ipv4Cidr, StaticConfigV4}; use esp_hal::gpio::AnyPin; use esp_hal::peripherals; use heapless::{String, Vec}; @@ -43,6 +46,7 @@ pub struct SSHStampConfig { pub mac: [u8; 6], /// `None` for DHCP pub ipv4_static: Option, + #[cfg(feature = "ipv6")] pub ipv6_static: Option, /// UART pub uart_pins: SerdePinConfig, @@ -248,6 +252,7 @@ impl SSHStampConfig { wifi_pw, mac, ipv4_static: None, + #[cfg(feature = "ipv6")] ipv6_static: None, uart_pins, }) @@ -322,6 +327,7 @@ fn enc_ipv4_config(v: &Option, s: &mut dyn SSHSink) -> WireResul Ok(()) } +#[cfg(feature = "ipv6")] fn enc_ipv6_config(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { v.is_some().enc(s)?; if let Some(v) = v { @@ -357,6 +363,7 @@ where .transpose() } +#[cfg(feature = "ipv6")] fn dec_ipv6_config<'de, S>(s: &mut S) -> WireResult> where S: SSHSource<'de>, @@ -407,6 +414,7 @@ impl SSHEncode for SSHStampConfig { self.mac.enc(s)?; enc_ipv4_config(&self.ipv4_static, s)?; + #[cfg(feature = "ipv6")] enc_ipv6_config(&self.ipv6_static, s)?; Ok(()) @@ -434,6 +442,7 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { //let _padding = [0u8; 3]; // ignore padding let ipv4_static = dec_ipv4_config(s)?; + #[cfg(feature = "ipv6")] let ipv6_static = dec_ipv6_config(s)?; // Decode password_authentication @@ -450,6 +459,7 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { wifi_pw, mac, ipv4_static, + #[cfg(feature = "ipv6")] ipv6_static, uart_pins, }) From d52ccff36675a605ebb4433e0db890a3733c13b4 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 6 Oct 2025 16:11:46 +1100 Subject: [PATCH 37/86] [ci skip] New config serialised correctly... unsure if format is correct still though... I (334) boot: Disabling RNG early entropy source... Existing config bad, making new. Wrong config version [src/storage.rs:96] "New config being serialised: " = "New config being serialised: " [src/storage.rs:96] &c = SSHStampConfig { hostkey: SignKey::Ed25519, password_authentication: true, admin_pw: None, admin_keys: [ None, None, None, ], wifi_ssid: "ssh-stamp", wifi_pw: None, mac: [ 218, 85, 73, 185, 109, 144, ], ipv4_static: None, uart_pins: SerdePinConfig { tx: 10, rx: 11, rts: None, cts: None, }, } Flash size = 8388608 bytes NVS partition size = 8192 NVS partition offset = 0x9000 [src/storage.rs:150] CONFIG_OFFSET + FlashConfig::BUF_SIZE = 40996 [src/storage.rs:152] "Erasing flash" = "Erasing flash" flash save done Checking if link is up... --- src/config.rs | 12 ++++++++---- src/storage.rs | 12 ++++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index 3fe3f9a..718bcb6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -38,7 +38,7 @@ pub struct SSHStampConfig { /// WiFi pub wifi_ssid: String<32>, - pub wifi_pw: Option>, + pub wifi_pw: Option>, // TODO: Why not 64? /// Networking /// TODO: Populate this field from esp's hardware info or just refer it from HAL? @@ -319,6 +319,7 @@ fn enc_ipv4_config(v: &Option, s: &mut dyn SSHSink) -> WireResul v.is_some().enc(s)?; if let Some(v) = v { v.address.address().to_bits().enc(s)?; + dbg!("enc_ipv4_config: prefix", &v.address.prefix_len()); v.address.prefix_len().enc(s)?; // to u32 let gw = v.gateway.map(|a| a.to_bits()); @@ -346,10 +347,13 @@ where let opt = bool::dec(s)?; opt.then(|| { let ad: u32 = SSHDecode::dec(s)?; + dbg!("dec_ipv4_config: address bits", &ad); let ad = Ipv4Addr::from_bits(ad); - let prefix = SSHDecode::dec(s)?; + dbg!("dec_ipv4_config: address", &ad); + let prefix: u8 = SSHDecode::dec(s)?; + dbg!("dec_ipv4_config: prefix", &prefix); if prefix > 32 { - // emabassy panics, so test it here + // embassy panics, so test it here return Err(WireError::PacketWrong); } let gw: Option = dec_option(s)?; @@ -427,6 +431,7 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { S: SSHSource<'de>, { let hostkey = dec_signkey(s)?; + dbg!("Hostkey: ", &hostkey); let admin_pw = dec_option(s)?; @@ -439,7 +444,6 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { let wifi_pw = dec_option(s)?; let mac = SSHDecode::dec(s)?; - //let _padding = [0u8; 3]; // ignore padding let ipv4_static = dec_ipv4_config(s)?; #[cfg(feature = "ipv6")] diff --git a/src/storage.rs b/src/storage.rs index bfe57d1..9836caa 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -93,6 +93,7 @@ pub async fn load_or_create(flash: &mut Fl) -> Result Result { let c = SSHStampConfig::new()?; + dbg!("New config being serialised: ", &c); save(flash, &c).await?; Ok(c) @@ -104,6 +105,12 @@ pub async fn load(fl: &mut Fl) -> Result { SunsetError::msg("flash error") })?; + if fl.buf[0] != SSHStampConfig::CURRENT_VERSION { + return Err(SunsetError::msg("Wrong config version")); + } + + dbg!("Marko's flash: {}", &fl.buf.hex_dump()); + let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).unwrap(); // .map_err(|_| SunsetError::msg("failed to decode flash config"))?; @@ -142,11 +149,12 @@ pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetErro dbg!(CONFIG_OFFSET + FlashConfig::BUF_SIZE); + dbg!("Erasing flash"); fl.flash .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + FlashConfig::BUF_SIZE + 4060) as u32).unwrap(); - fl.flash - .write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); + // fl.flash + // .write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); println!("flash save done"); Ok(()) From fa703691f5459b60aea316d81da872b53f0bbcde Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 6 Oct 2025 16:35:14 +1100 Subject: [PATCH 38/86] [ci skip] Back to shifted hash config error --- src/storage.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/storage.rs b/src/storage.rs index 9836caa..a0b8816 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -106,15 +106,17 @@ pub async fn load(fl: &mut Fl) -> Result { })?; if fl.buf[0] != SSHStampConfig::CURRENT_VERSION { + dbg!("Wrong config version pre-read_ssh decode: {}", fl.buf[0]); return Err(SunsetError::msg("Wrong config version")); } - dbg!("Marko's flash: {}", &fl.buf.hex_dump()); + //dbg!("Marko's flash: {}", &fl.buf.hex_dump()); let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).unwrap(); // .map_err(|_| SunsetError::msg("failed to decode flash config"))?; if flash_config.version != SSHStampConfig::CURRENT_VERSION { + dbg!("wrong config version on decode: {}", flash_config.version); return Err(SunsetError::msg("wrong config version")); } @@ -149,12 +151,12 @@ pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetErro dbg!(CONFIG_OFFSET + FlashConfig::BUF_SIZE); - dbg!("Erasing flash"); - fl.flash - .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + FlashConfig::BUF_SIZE + 4060) as u32).unwrap(); +// dbg!("Erasing flash"); +// fl.flash +// .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + FlashConfig::BUF_SIZE + 4060) as u32).unwrap(); - // fl.flash - // .write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); + fl.flash + .write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); println!("flash save done"); Ok(()) From 9a331fe804d15d5787db9750e6e6f6c86241f88d Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 7 Oct 2025 17:21:08 +1100 Subject: [PATCH 39/86] [ci skip] Fixed SSHEncode/Decode for SSHStampConfig, unfortunately it now throws RanOut on wifi_ssid dec(s)?: config.rs:462-463 Co-authored-by: Julio Beltran Ortega --- partitions.csv | 2 +- src/config.rs | 73 +++++++++++++++++++++++++++++++------------------- src/main.rs | 6 ++--- src/storage.rs | 19 +++++++------ 4 files changed, 61 insertions(+), 39 deletions(-) diff --git a/partitions.csv b/partitions.csv index dcf4016..8eaf718 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,6 +1,6 @@ # ESP-IDF Partition Table # Name, Type, SubType, Offset, Size, Flags -app_config, data, nvs, 0x9000, 0x2000, +app_config, data, nvs, 0x9000, 0x2000, otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x200000, diff --git a/src/config.rs b/src/config.rs index 718bcb6..1f4c0ff 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,6 +24,7 @@ use sunset::{ }; use embassy_sync::channel::Channel; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use sunset_sshwire_derive::SSHEncode; use crate::errors; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; @@ -52,7 +53,7 @@ pub struct SSHStampConfig { pub uart_pins: SerdePinConfig, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, SSHEncode)] pub struct SerdePinConfig { pub tx: u8, pub rx: u8, @@ -63,17 +64,29 @@ pub struct SerdePinConfig { impl Default for SerdePinConfig { fn default() -> Self { Self { + // TODO: This env comes from SSH env events/packets, not from system's std::env / core::env (if any)... so it shouldn't be unsafe() tx: 10, rx: 11, - // TODO: This env comes from SSH env events/packets, not from system's std::env / core::env (if any)... so it shouldn't be unsafe() - // tx: env!("SSH_STAMP_TX_PIN").parse().unwrap(), - // rx: env!("SSH_STAMP_RX_PIN").parse().unwrap(), - rts: option_env!("SSH_STAMP_RTS").map(|s| s.parse().unwrap()), - cts: option_env!("SSH_STAMP_CTS").map(|s| s.parse().unwrap()), + rts: None, + cts: None } } } +impl<'de> SSHDecode<'de> for SerdePinConfig { + fn dec(s: &mut S) -> WireResult + where + S: SSHSource<'de>, + { + let tx = u8::dec(s)?; + let rx = u8::dec(s)?; + // TODO: Decoding Options is problematic since encode only writes them if they exist + let rts = dec_option(s)?; + let cts = dec_option(s)?; + Ok(SerdePinConfig { tx, rx, rts, cts }) + } +} + pub struct GPIOConfig { pub gpio10: Option>, pub gpio11: Option>, @@ -277,10 +290,11 @@ impl SSHStampConfig { } fn random_mac() -> Result<[u8; 6]> { - let mut mac = [0u8; 6]; - sunset::random::fill_random(&mut mac)?; - // unicast, locally administered - mac[0] = (mac[0] & 0xfc) | 0x02; + // let mut mac = [0u8; 6]; + // sunset::random::fill_random(&mut mac)?; + // // unicast, locally administered + // mac[0] = (mac[0] & 0xfc) | 0x02; + let mac = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]; // TODO: Temporary fixed MAC for testing Ok(mac) } @@ -347,11 +361,8 @@ where let opt = bool::dec(s)?; opt.then(|| { let ad: u32 = SSHDecode::dec(s)?; - dbg!("dec_ipv4_config: address bits", &ad); let ad = Ipv4Addr::from_bits(ad); - dbg!("dec_ipv4_config: address", &ad); let prefix: u8 = SSHDecode::dec(s)?; - dbg!("dec_ipv4_config: prefix", &prefix); if prefix > 32 { // embassy panics, so test it here return Err(WireError::PacketWrong); @@ -392,20 +403,21 @@ where .transpose() } -fn dec_uart_pins<'de, S>(s: &mut S) -> WireResult -where - S: SSHSource<'de>, -{ - let tx = u8::dec(s)?; - let rx = u8::dec(s)?; - let rts = dec_option(s)?; - let cts = dec_option(s)?; - Ok(SerdePinConfig { tx, rx, rts, cts }) -} +// fn dec_uart_pins<'de, S>(s: &mut S) -> WireResult +// where +// S: SSHSource<'de>, +// { +// let tx = u8::dec(s)?; +// let rx = u8::dec(s)?; +// let rts = dec_option(s)?; +// let cts = dec_option(s)?; +// Ok(SerdePinConfig { tx, rx, rts, cts }) +// } impl SSHEncode for SSHStampConfig { fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { enc_signkey(&self.hostkey, s)?; + self.password_authentication.enc(s)?; enc_option(&self.admin_pw, s)?; for k in self.admin_keys.iter() { @@ -421,6 +433,9 @@ impl SSHEncode for SSHStampConfig { #[cfg(feature = "ipv6")] enc_ipv6_config(&self.ipv6_static, s)?; + // Encode PinConfig + self.uart_pins.enc(s)?; + Ok(()) } } @@ -430,9 +445,13 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { where S: SSHSource<'de>, { + let hostkey = dec_signkey(s)?; dbg!("Hostkey: ", &hostkey); + dbg!(s.remaining()); + let password_authentication = SSHDecode::dec(s)?; + dbg!(s.remaining()); let admin_pw = dec_option(s)?; let mut admin_keys = [None, None, None]; @@ -440,6 +459,7 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { *k = dec_option(s)?; } + dbg!(s.remaining()); let wifi_ssid = SSHDecode::dec(s)?; let wifi_pw = dec_option(s)?; @@ -449,10 +469,9 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { #[cfg(feature = "ipv6")] let ipv6_static = dec_ipv6_config(s)?; - // Decode password_authentication - let password_authentication = SSHDecode::dec(s)?; - - let uart_pins = dec_uart_pins(s)?; + // Not supported by sshwire-derive nor virtue (no Option support) + // let uart_pins = SSHDecode::dec(s)?; + let uart_pins = SSHDecode::dec(s)?; Ok(Self { hostkey, diff --git a/src/main.rs b/src/main.rs index 50d38c4..7c502aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,11 +55,11 @@ async fn main(spawner: Spawner) -> ! { } // Read SSH configuration from Flash (if it exists) - let mut flash = Fl::new(FlashStorage::new()); - let config = ssh_stamp::storage::load_or_create(&mut flash).await; + let mut flash_storage = Fl::new(FlashStorage::new()); + let config = ssh_stamp::storage::load_or_create(&mut flash_storage).await; static FLASH: StaticCell> = StaticCell::new(); - let _flash = FLASH.init(SunsetMutex::new(flash)); + let _flash = FLASH.init(SunsetMutex::new(flash_storage)); static CONFIG: StaticCell> = StaticCell::new(); let config = CONFIG.init(SunsetMutex::new(config.unwrap())); diff --git a/src/storage.rs b/src/storage.rs index a0b8816..bd02260 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -46,7 +46,7 @@ struct FlashConfig<'a> { } impl FlashConfig<'_> { - const BUF_SIZE: usize = CONFIG_VERSION_SIZE + CONFIG_AREA_SIZE + CONFIG_HASH_SIZE; + const BUF_SIZE: usize = 460; // Must be enough to hold the whole config // TODO: Rework Error mapping with esp_storage errors /// Finds the NVS partitions and retrieves information about it. @@ -110,7 +110,7 @@ pub async fn load(fl: &mut Fl) -> Result { return Err(SunsetError::msg("Wrong config version")); } - //dbg!("Marko's flash: {}", &fl.buf.hex_dump()); + dbg!("Marko's flash: {}", &fl.buf.hex_dump()); let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).unwrap(); // .map_err(|_| SunsetError::msg("failed to decode flash config"))?; @@ -146,17 +146,20 @@ pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetErro FlashConfig::find_config_partition().unwrap(); - let _l = sshwire::write_ssh(&mut fl.buf, &sc)?; - // let buf = &fl.buf[..l]; + let l = sshwire::write_ssh(&mut fl.buf, &sc)?; + let buf = &fl.buf[..l]; dbg!(CONFIG_OFFSET + FlashConfig::BUF_SIZE); -// dbg!("Erasing flash"); -// fl.flash -// .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + FlashConfig::BUF_SIZE + 4060) as u32).unwrap(); + dbg!("Erasing flash"); + + assert!(CONFIG_AREA_SIZE > FlashConfig::BUF_SIZE); + + fl.flash + .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + CONFIG_AREA_SIZE) as u32).unwrap(); fl.flash - .write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); + .write(CONFIG_OFFSET as u32, &buf).unwrap(); println!("flash save done"); Ok(()) From e2ee15111f7f8ef2e1509fb7d7b36582de4bd27d Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 8 Oct 2025 15:03:53 +1100 Subject: [PATCH 40/86] [ci skip] Try to experiment with probe-rs debugging support on ESP targets after almost a year... will it work? --- .cargo/config.toml | 6 ++++-- Cargo.toml | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index b334bf0..e949dcc 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -4,7 +4,8 @@ build-esp32c2 = "build --release --target riscv32imc-unknown-none-elf --features build-esp32c3 = "build --release --target riscv32imc-unknown-none-elf --features esp32c3" # Available but not supported by esp-hal (yet). #build-esp32c5 = "build --release --target riscv32imac-unknown-none-elf --features esp32c5" -build-esp32c6 = "build --release --target riscv32imac-unknown-none-elf --features esp32c6" +#build-esp32c6 = "build --release --target riscv32imac-unknown-none-elf --features esp32c6" +build-esp32c6 = "build --target riscv32imac-unknown-none-elf --features esp32c6" build-esp32s2 = "build --profile esp32s2 --target xtensa-esp32s2-none-elf --features esp32s2" build-esp32s3 = "build --release --target xtensa-esp32s3-none-elf --features esp32s3" @@ -13,7 +14,8 @@ run-esp32c2 = "run --release --target riscv32imc-unknown-none-elf --features esp run-esp32c3 = "run --release --target riscv32imc-unknown-none-elf --features esp32c3" # Available but not supported by esp-hal (yet). #run-esp32c5 = "run --release --target riscv32imac-unknown-none-elf --features esp32c5" -run-esp32c6 = "run --release --target riscv32imac-unknown-none-elf --features esp32c6" +#run-esp32c6 = "run --release --target riscv32imac-unknown-none-elf --features esp32c6" +run-esp32c6 = "run --target riscv32imac-unknown-none-elf --features esp32c6" run-esp32s2 = "run --profile esp32s2 --target xtensa-esp32s2-none-elf --features esp32s2" run-esp32s3 = "run --release --target xtensa-esp32s3-none-elf --features esp32s3" diff --git a/Cargo.toml b/Cargo.toml index bd04828..80125fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,9 @@ pretty-hex = { version = "0.4", default-features = false } [profile.dev] # Rust debug is too slow. # For debug builds always builds with some optimization -opt-level = 3 +opt-level = 0 +debug = 2 +debug-assertions = true [profile.release] codegen-units = 1 # LLVM can perform better optimizations using a single thread From df554ee4f5a7cdca3a74c980536907e33d19bdf3 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 10 Nov 2025 15:22:16 +1100 Subject: [PATCH 41/86] Experiments with vscode/probe-rs debugging failed due to dev tooling immaturity, increasing verbosity of hash-related bytes to determine bad hash errors... most probably related to read-ssh/write-ssh enc/dec SSHEncode/SSHDecode machinery --- .cargo/config.toml | 10 ++++---- src/config.rs | 58 ++++++++++++++++++++++++++++++++++++---------- src/storage.rs | 11 +++++---- 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index e949dcc..310ba2a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -4,8 +4,9 @@ build-esp32c2 = "build --release --target riscv32imc-unknown-none-elf --features build-esp32c3 = "build --release --target riscv32imc-unknown-none-elf --features esp32c3" # Available but not supported by esp-hal (yet). #build-esp32c5 = "build --release --target riscv32imac-unknown-none-elf --features esp32c5" -#build-esp32c6 = "build --release --target riscv32imac-unknown-none-elf --features esp32c6" -build-esp32c6 = "build --target riscv32imac-unknown-none-elf --features esp32c6" +build-esp32c6 = "build --release --target riscv32imac-unknown-none-elf --features esp32c6" +# Debug build (too big for target and probe-rs doesn't work anyway) +#build-esp32c6 = "build --target riscv32imac-unknown-none-elf --features esp32c6" build-esp32s2 = "build --profile esp32s2 --target xtensa-esp32s2-none-elf --features esp32s2" build-esp32s3 = "build --release --target xtensa-esp32s3-none-elf --features esp32s3" @@ -14,8 +15,9 @@ run-esp32c2 = "run --release --target riscv32imc-unknown-none-elf --features esp run-esp32c3 = "run --release --target riscv32imc-unknown-none-elf --features esp32c3" # Available but not supported by esp-hal (yet). #run-esp32c5 = "run --release --target riscv32imac-unknown-none-elf --features esp32c5" -#run-esp32c6 = "run --release --target riscv32imac-unknown-none-elf --features esp32c6" -run-esp32c6 = "run --target riscv32imac-unknown-none-elf --features esp32c6" +run-esp32c6 = "run --release --target riscv32imac-unknown-none-elf --features esp32c6" +# Debug build (too big for target and probe-rs doesn't work anyway) +#run-esp32c6 = "run --target riscv32imac-unknown-none-elf --features esp32c6" run-esp32s2 = "run --profile esp32s2 --target xtensa-esp32s2-none-elf --features esp32s2" run-esp32s3 = "run --release --target xtensa-esp32s3-none-elf --features esp32s3" diff --git a/src/config.rs b/src/config.rs index 1f4c0ff..464e78d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,7 +24,6 @@ use sunset::{ }; use embassy_sync::channel::Channel; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use sunset_sshwire_derive::SSHEncode; use crate::errors; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; @@ -53,13 +52,31 @@ pub struct SSHStampConfig { pub uart_pins: SerdePinConfig, } -#[derive(Debug, Clone, SSHEncode)] +#[derive(Debug, Clone)] pub struct SerdePinConfig { pub tx: u8, pub rx: u8, pub rts: Option, pub cts: Option, } +// impl SerdePinConfig { +// // During encoding flags will be prepended to the options fields to only deserialise if they exist. +// pub(crate) fn flags (&self) -> u8 { +// let mut flags = 0u8; +// if self.rts.is_some() { +// flags |= SerdePinConfigOptions::RtsPresent as u8; +// } +// if self.cts.is_some() { +// flags |= SerdePinConfigOptions::CtsPresent as u8; +// } +// flags +// } +// } + +// enum SerdePinConfigOptions { +// RtsPresent = 1, +// CtsPresent = 2, +// } impl Default for SerdePinConfig { fn default() -> Self { @@ -73,17 +90,38 @@ impl Default for SerdePinConfig { } } +impl SSHEncode for SerdePinConfig { + fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { + self.tx.enc(s)?; + self.rx.enc(s)?; + //self.flags().enc(s)?; + enc_option(&self.rts, s)?; + enc_option(&self.cts, s) + } +} + impl<'de> SSHDecode<'de> for SerdePinConfig { fn dec(s: &mut S) -> WireResult where S: SSHSource<'de>, { - let tx = u8::dec(s)?; - let rx = u8::dec(s)?; - // TODO: Decoding Options is problematic since encode only writes them if they exist - let rts = dec_option(s)?; - let cts = dec_option(s)?; - Ok(SerdePinConfig { tx, rx, rts, cts }) + // Decoding Options is problematic since encode only writes them if they exist. + let mut pin_config = SerdePinConfig::default(); + pin_config.rx = u8::dec(s)?; + pin_config.tx = u8::dec(s)?; + + // Decode flags to know which options are present + //let flags = u8::dec(s)?; + + //if flags & (SerdePinConfigOptions::RtsPresent as u8) != 0 { + pin_config.rts = dec_option(s)?; + //} + + //if flags & (SerdePinConfigOptions::CtsPresent as u8) != 0 { + pin_config.cts = dec_option(s)?; + //} + + Ok(pin_config) } } @@ -426,7 +464,6 @@ impl SSHEncode for SSHStampConfig { self.wifi_ssid.as_str().enc(s)?; enc_option(&self.wifi_pw, s)?; - self.mac.enc(s)?; enc_ipv4_config(&self.ipv4_static, s)?; @@ -449,9 +486,7 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { let hostkey = dec_signkey(s)?; dbg!("Hostkey: ", &hostkey); - dbg!(s.remaining()); let password_authentication = SSHDecode::dec(s)?; - dbg!(s.remaining()); let admin_pw = dec_option(s)?; let mut admin_keys = [None, None, None]; @@ -459,7 +494,6 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { *k = dec_option(s)?; } - dbg!(s.remaining()); let wifi_ssid = SSHDecode::dec(s)?; let wifi_pw = dec_option(s)?; diff --git a/src/storage.rs b/src/storage.rs index bd02260..6938800 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -93,7 +93,7 @@ pub async fn load_or_create(flash: &mut Fl) -> Result Result { let c = SSHStampConfig::new()?; - dbg!("New config being serialised: ", &c); + //dbg!("New config being serialised: ", &c); save(flash, &c).await?; Ok(c) @@ -110,7 +110,7 @@ pub async fn load(fl: &mut Fl) -> Result { return Err(SunsetError::msg("Wrong config version")); } - dbg!("Marko's flash: {}", &fl.buf.hex_dump()); + dbg!("Loaded flash: {}", &fl.buf.hex_dump()); let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).unwrap(); // .map_err(|_| SunsetError::msg("failed to decode flash config"))?; @@ -120,7 +120,7 @@ pub async fn load(fl: &mut Fl) -> Result { return Err(SunsetError::msg("wrong config version")); } - let calc_hash = config_hash(flash_config.config.borrow())?; + let calc_hash = config_hash(&flash_config.config.borrow()).unwrap(); dbg!(&calc_hash.hex_dump()); dbg!(&flash_config.hash.hex_dump()); @@ -146,8 +146,11 @@ pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetErro FlashConfig::find_config_partition().unwrap(); +// dbg!("Saving config: ", &config); + dbg!("Before write_ssh, with hash: ", &sc.hash.hex_dump()); let l = sshwire::write_ssh(&mut fl.buf, &sc)?; let buf = &fl.buf[..l]; + dbg!("Saved flash (after write_ssh): {}", &buf.hex_dump()); dbg!(CONFIG_OFFSET + FlashConfig::BUF_SIZE); @@ -159,7 +162,7 @@ pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetErro .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + CONFIG_AREA_SIZE) as u32).unwrap(); fl.flash - .write(CONFIG_OFFSET as u32, &buf).unwrap(); + .write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); println!("flash save done"); Ok(()) From 7cf834a74d73eedd62fd58119ed2cee2137aadeb Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 11 Nov 2025 12:27:35 +1100 Subject: [PATCH 42/86] Only one admin key, do not use option_env because we'll be using ssh env packets, we must not re-generate hostkey on each boot --- src/config.rs | 19 +++++++++++-------- src/settings.rs | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/config.rs b/src/config.rs index 464e78d..fa5d1e3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,10 +15,9 @@ use hmac::{Hmac, Mac}; use sha2::Sha256; use subtle::ConstantTimeEq; -use sunset::error::TrapBug; +use sunset::packets::Ed25519PubKey; use sunset::{KeyType, Result}; use sunset::{ - packets::Ed25519PubKey, sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, SignKey, }; @@ -32,6 +31,7 @@ use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; pub struct SSHStampConfig { pub hostkey: SignKey, + /// Authentication pub password_authentication: bool, pub admin_pw: Option, pub admin_keys: [Option; KEY_SLOTS], @@ -286,11 +286,12 @@ impl SSHStampConfig { /// Will only fail on RNG failure. pub fn new() -> Result { let hostkey = SignKey::generate(KeyType::Ed25519, None)?; - let wifi_ssid: String<32> = - option_env!("WIFI_SSID").unwrap_or(DEFAULT_SSID).try_into().trap()?; - let wifi_pw: Option> = - option_env!("WIFI_PW").map(|s| s.try_into()).transpose().trap()?; + + // TODO: Those env events come from system's std::env / core::env (if any)... so it shouldn't be unsafe() + let wifi_ssid_str = String::try_from(DEFAULT_SSID).unwrap(); + let wifi_ssid: String<32> = wifi_ssid_str.into(); let mac = random_mac()?; + let wifi_pw = None; let uart_pins = SerdePinConfig::default(); @@ -455,6 +456,8 @@ where impl SSHEncode for SSHStampConfig { fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { enc_signkey(&self.hostkey, s)?; + + // Authentication self.password_authentication.enc(s)?; enc_option(&self.admin_pw, s)?; @@ -486,10 +489,10 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { let hostkey = dec_signkey(s)?; dbg!("Hostkey: ", &hostkey); + // Authentication let password_authentication = SSHDecode::dec(s)?; let admin_pw = dec_option(s)?; - - let mut admin_keys = [None, None, None]; + let mut admin_keys = [None; KEY_SLOTS]; for k in admin_keys.iter_mut() { *k = dec_option(s)?; } diff --git a/src/settings.rs b/src/settings.rs index b935e27..168aaad 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -5,7 +5,7 @@ //pub(crate) const PORT: u16 = 22; pub(crate) const DEFAULT_SSID: &str = "ssh-stamp"; //pub(crate) const SSH_SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; -pub(crate) const KEY_SLOTS: usize = 3; // TODO: Document whether this a "reasonable default"? Justify why? +pub(crate) const KEY_SLOTS: usize = 1; // TODO: Document whether this a "reasonable default"? Justify why? //pub(crate) const PASSWORD_AUTHENTICATION: bool = true; // UART settings From 821126b7f7c28cf87615472f9982bd9cb6d4c41f Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 11 Nov 2025 14:29:32 +1100 Subject: [PATCH 43/86] =?UTF-8?q?Turns=20out=20that=20calc=5Fhash=20was=20?= =?UTF-8?q?failing=20because=20we=20had=20SerdePinConfig=20with=20tx/rx=20?= =?UTF-8?q?flipped...=20=F0=9F=A4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Julio Beltran Ortega --- src/config.rs | 21 ++++++++++++++++----- src/main.rs | 3 +++ src/storage.rs | 9 ++------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/config.rs b/src/config.rs index fa5d1e3..6676f6c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,7 +16,7 @@ use sha2::Sha256; use subtle::ConstantTimeEq; use sunset::packets::Ed25519PubKey; -use sunset::{KeyType, Result}; +use sunset::{sshwire, KeyType, Result}; use sunset::{ sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, SignKey, @@ -27,8 +27,9 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use crate::errors; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct SSHStampConfig { + //pub first_boot: bool, pub hostkey: SignKey, /// Authentication @@ -52,7 +53,7 @@ pub struct SSHStampConfig { pub uart_pins: SerdePinConfig, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct SerdePinConfig { pub tx: u8, pub rx: u8, @@ -107,9 +108,9 @@ impl<'de> SSHDecode<'de> for SerdePinConfig { { // Decoding Options is problematic since encode only writes them if they exist. let mut pin_config = SerdePinConfig::default(); - pin_config.rx = u8::dec(s)?; pin_config.tx = u8::dec(s)?; - + pin_config.rx = u8::dec(s)?; + // Decode flags to know which options are present //let flags = u8::dec(s)?; @@ -605,4 +606,14 @@ impl<'de> SSHDecode<'de> for PwHash { let cost = u8::dec(s)?; Ok(PwHash { salt, hash, cost }) } +} + +pub fn roundtrip_config() { + // default config + let c1 = SSHStampConfig::new().unwrap(); + let mut buf = [0u8; 1000]; + let l = sshwire::write_ssh(&mut buf, &c1).unwrap(); + let v = &buf[..l]; + let c2: SSHStampConfig = sshwire::read_ssh(v, None).unwrap(); + assert_eq!(c1, c2); } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7c502aa..ca59b83 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,6 +54,9 @@ async fn main(spawner: Spawner) -> ! { } } + // Quick roundtrip test for SSHStampConfig + ssh_stamp::config::roundtrip_config(); + // Read SSH configuration from Flash (if it exists) let mut flash_storage = Fl::new(FlashStorage::new()); let config = ssh_stamp::storage::load_or_create(&mut flash_storage).await; diff --git a/src/storage.rs b/src/storage.rs index 6938800..dc06bb8 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -93,8 +93,8 @@ pub async fn load_or_create(flash: &mut Fl) -> Result Result { let c = SSHStampConfig::new()?; - //dbg!("New config being serialised: ", &c); save(flash, &c).await?; + dbg!("Created new config: ", &c); Ok(c) } @@ -105,11 +105,6 @@ pub async fn load(fl: &mut Fl) -> Result { SunsetError::msg("flash error") })?; - if fl.buf[0] != SSHStampConfig::CURRENT_VERSION { - dbg!("Wrong config version pre-read_ssh decode: {}", fl.buf[0]); - return Err(SunsetError::msg("Wrong config version")); - } - dbg!("Loaded flash: {}", &fl.buf.hex_dump()); let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).unwrap(); @@ -120,7 +115,7 @@ pub async fn load(fl: &mut Fl) -> Result { return Err(SunsetError::msg("wrong config version")); } - let calc_hash = config_hash(&flash_config.config.borrow()).unwrap(); + let calc_hash = config_hash(flash_config.config.borrow()).unwrap(); dbg!(&calc_hash.hex_dump()); dbg!(&flash_config.hash.hex_dump()); From 7a6eb55de43c9f7e7533f877954fd77b3c0f3720 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 11 Nov 2025 14:40:46 +1100 Subject: [PATCH 44/86] Thorough dbg cleanup... --- src/config.rs | 15 ++++----------- src/main.rs | 9 +++------ src/storage.rs | 8 +------- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/config.rs b/src/config.rs index 6676f6c..9daa749 100644 --- a/src/config.rs +++ b/src/config.rs @@ -178,7 +178,6 @@ impl PinChannel { 11 => self.gpios.gpio11.take().ok_or_else(|| errors::Error::InvalidPin)?, _ => return Err(errors::Error::InvalidPin) }); - dbg!("recv_rx: no channel receive"); // rx needs to lock here. // dbg!("recv_rx: before rx.receive.await"); // self.rx.receive().await; @@ -201,15 +200,11 @@ impl PinChannel { pub async fn with_channel(&mut self, f: F) -> errors::Result<()> where F: for<'a> AsyncFnOnce(AnyPin<'a>, AnyPin<'a>) { - dbg!("inner: with_channel begin, recv_rx call"); let mut rx = self.recv_rx().await?; - dbg!("inner: with_channel recv_tx call"); let mut tx = self.recv_tx().await?; - dbg!("inner: with_channel f-reborrow"); f(rx.reborrow(), tx.reborrow()).await; - dbg!("inner: with_channel, before send{rx/tx}"); self.send_rx(rx).await.unwrap(); self.send_tx(tx).await.unwrap(); @@ -330,11 +325,10 @@ impl SSHStampConfig { } fn random_mac() -> Result<[u8; 6]> { - // let mut mac = [0u8; 6]; - // sunset::random::fill_random(&mut mac)?; - // // unicast, locally administered - // mac[0] = (mac[0] & 0xfc) | 0x02; - let mac = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]; // TODO: Temporary fixed MAC for testing + let mut mac = [0u8; 6]; + sunset::random::fill_random(&mut mac)?; + // unicast, locally administered + mac[0] = (mac[0] & 0xfc) | 0x02; Ok(mac) } @@ -488,7 +482,6 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { { let hostkey = dec_signkey(s)?; - dbg!("Hostkey: ", &hostkey); // Authentication let password_authentication = SSHDecode::dec(s)?; diff --git a/src/main.rs b/src/main.rs index ca59b83..a7f6fd3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,8 +54,9 @@ async fn main(spawner: Spawner) -> ! { } } + // TODO: Migrate this function/test to embedded-test. // Quick roundtrip test for SSHStampConfig - ssh_stamp::config::roundtrip_config(); + // ssh_stamp::config::roundtrip_config(); // Read SSH configuration from Flash (if it exists) let mut flash_storage = Fl::new(FlashStorage::new()); @@ -122,7 +123,7 @@ async fn uart_task( uart_periph: UART1<'static>, channel: &'static mut PinChannel, ) { - dbg!("spawning UART task..."); + dbg!("Spawning UART task..."); // Hardware UART setup let uart_config = Config::default().with_rx( RxConfig::default() @@ -130,10 +131,8 @@ async fn uart_task( .with_timeout(1) ); - dbg!("before with_channel"); // Sync pin config via channels channel.with_channel(async |rx, tx| { - dbg!("into with_channel"); let uart = Uart::new(uart_periph, uart_config) .unwrap() .with_rx(rx) @@ -141,8 +140,6 @@ async fn uart_task( .into_async(); // Run the main buffered TX/RX loop - dbg!("before buffer_run"); buffer.run(uart).await; - dbg!("after buffer_run"); }).await.unwrap(); } diff --git a/src/storage.rs b/src/storage.rs index dc06bb8..68447e0 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -105,10 +105,7 @@ pub async fn load(fl: &mut Fl) -> Result { SunsetError::msg("flash error") })?; - dbg!("Loaded flash: {}", &fl.buf.hex_dump()); - - let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).unwrap(); -// .map_err(|_| SunsetError::msg("failed to decode flash config"))?; + let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).map_err(|_| SunsetError::msg("failed to decode flash config"))?; if flash_config.version != SSHStampConfig::CURRENT_VERSION { dbg!("wrong config version on decode: {}", flash_config.version); @@ -117,9 +114,6 @@ pub async fn load(fl: &mut Fl) -> Result { let calc_hash = config_hash(flash_config.config.borrow()).unwrap(); - dbg!(&calc_hash.hex_dump()); - dbg!(&flash_config.hash.hex_dump()); - if calc_hash != flash_config.hash { return Err(SunsetError::msg("bad config hash")); } From aba0244af74d67d8afcb453070a31ba2661db523 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Tue, 11 Nov 2025 16:31:06 +1100 Subject: [PATCH 45/86] Refactor to accomodate SessionType and share channels between types. --- src/serve.rs | 56 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/src/serve.rs b/src/serve.rs index c2b790c..ebf6500 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -13,15 +13,22 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; +use embedded_io_async::Read; use heapless::String; use sunset::{error, ChanHandle, ServEvent, SignKey}; use sunset_async::{ProgressHolder, SSHServer}; use esp_println::{dbg, println}; +enum SessionType { + Bridge(ChanHandle), + Env(ChanHandle), + Sftp(ChanHandle), +} + async fn connection_loop( serv: &SSHServer<'_>, - chan_pipe: &Channel, + chan_pipe: &Channel, ) -> Result<(), sunset::Error> { let username = Mutex::::new(String::<20>::new()); let mut session: Option = None; @@ -31,7 +38,7 @@ async fn connection_loop( loop { let mut ph = ProgressHolder::new(); let ev = serv.progress(&mut ph).await?; - //dbg!(&ev); + dbg!(&ev); #[allow(unreachable_patterns)] match ev { ServEvent::SessionShell(a) => { @@ -40,7 +47,7 @@ async fn connection_loop( a.succeed()?; dbg!("We got shell"); - let _ = chan_pipe.try_send(ch); + let _ = chan_pipe.try_send(SessionType::Bridge(ch)); } else { a.fail()?; } @@ -78,7 +85,17 @@ async fn connection_loop( // config.save(a) // SSHConfig c = a.validate(); // Checks the input variable, sanitizes, assigns a target subsystem // a.config_change(c)?; - a.succeed()?; // FIXME: Not just succeed... + + // Obtain the current session channel handle and use it to get stdio. + // if let Some(ch) = session.take() { + // debug_assert!(ch.num() == a.channel()); + + // a.succeed()?; + // let _ = chan_pipe.try_send(SessionType::Env(ch)); + // } else { + // a.fail()?; + // } + a.succeed()?; }, ServEvent::SessionPty(a) => { a.succeed()?; @@ -106,7 +123,7 @@ pub(crate) async fn handle_ssh_client( let ssh_server = SSHServer::new(&mut inbuf, &mut outbuf); let (mut rsock, mut wsock) = stream.split(); - let chan_pipe = Channel::::new(); + let chan_pipe = Channel::::new(); println!("Calling connection_loop from handle_ssh_client"); let conn_loop = connection_loop(&ssh_server, &chan_pipe); @@ -114,11 +131,32 @@ pub(crate) async fn handle_ssh_client( let server = ssh_server.run(&mut rsock, &mut wsock); println!("Setting up serial bridge"); + + // TODO: Maybe loop forever here and/or handle disconnection/terminations gracefully? let bridge = async { - let ch = chan_pipe.receive().await; - let stdio = ssh_server.stdio(ch).await?; - let stdio2 = stdio.clone(); - serial_bridge(stdio, stdio2, uart).await + let session_type = chan_pipe.receive().await; + + match session_type { + SessionType::Bridge(ch) => { + let stdio = ssh_server.stdio(ch).await?; + let stdio2 = stdio.clone(); + serial_bridge(stdio, stdio2, uart).await? + }, + SessionType::Env(ch) => { + // Handle environment variable session + let mut stdio = ssh_server.stdio(ch).await?; + let mut buf = [0u8; 256]; + dbg!("Waiting to read ENV session data"); + let n = stdio.read(&mut buf).await?; + dbg!("Got ENV session"); + dbg!("Name/Value of ENV is: ", &buf[..n]); + }, + SessionType::Sftp(_ch) => { + // Handle SFTP session + todo!() + }, + }; + Ok(()) }; println!("Main select() in handle_ssh_client()"); From e79b485a4761bd29a5e2eeb18676fd95065ca94f Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 17 Nov 2025 13:50:24 +1100 Subject: [PATCH 46/86] cargo fmt --- src/config.rs | 250 ++----------------------------------------- src/errors.rs | 4 +- src/espressif/net.rs | 9 +- src/lib.rs | 4 +- src/main.rs | 63 ++++++----- src/serve.rs | 77 ++++++++----- src/settings.rs | 4 +- src/storage.rs | 49 +++++---- 8 files changed, 137 insertions(+), 323 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9daa749..aef6203 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,11 +1,9 @@ use core::net::Ipv4Addr; #[cfg(feature = "ipv6")] use core::net::Ipv6Addr; +use embassy_net::{Ipv4Cidr, StaticConfigV4}; #[cfg(feature = "ipv6")] use embassy_net::{Ipv6Cidr, StaticConfigV6}; -use embassy_net::{Ipv4Cidr, StaticConfigV4}; -use esp_hal::gpio::AnyPin; -use esp_hal::peripherals; use heapless::{String, Vec}; use esp_println::dbg; @@ -21,10 +19,8 @@ use sunset::{ sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, SignKey, }; -use embassy_sync::channel::Channel; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use crate::errors; +use crate::pins::SerdePinConfig; use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; #[derive(Debug, PartialEq)] @@ -53,226 +49,6 @@ pub struct SSHStampConfig { pub uart_pins: SerdePinConfig, } -#[derive(Debug, Clone, PartialEq)] -pub struct SerdePinConfig { - pub tx: u8, - pub rx: u8, - pub rts: Option, - pub cts: Option, -} -// impl SerdePinConfig { -// // During encoding flags will be prepended to the options fields to only deserialise if they exist. -// pub(crate) fn flags (&self) -> u8 { -// let mut flags = 0u8; -// if self.rts.is_some() { -// flags |= SerdePinConfigOptions::RtsPresent as u8; -// } -// if self.cts.is_some() { -// flags |= SerdePinConfigOptions::CtsPresent as u8; -// } -// flags -// } -// } - -// enum SerdePinConfigOptions { -// RtsPresent = 1, -// CtsPresent = 2, -// } - -impl Default for SerdePinConfig { - fn default() -> Self { - Self { - // TODO: This env comes from SSH env events/packets, not from system's std::env / core::env (if any)... so it shouldn't be unsafe() - tx: 10, - rx: 11, - rts: None, - cts: None - } - } -} - -impl SSHEncode for SerdePinConfig { - fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { - self.tx.enc(s)?; - self.rx.enc(s)?; - //self.flags().enc(s)?; - enc_option(&self.rts, s)?; - enc_option(&self.cts, s) - } -} - -impl<'de> SSHDecode<'de> for SerdePinConfig { - fn dec(s: &mut S) -> WireResult - where - S: SSHSource<'de>, - { - // Decoding Options is problematic since encode only writes them if they exist. - let mut pin_config = SerdePinConfig::default(); - pin_config.tx = u8::dec(s)?; - pin_config.rx = u8::dec(s)?; - - // Decode flags to know which options are present - //let flags = u8::dec(s)?; - - //if flags & (SerdePinConfigOptions::RtsPresent as u8) != 0 { - pin_config.rts = dec_option(s)?; - //} - - //if flags & (SerdePinConfigOptions::CtsPresent as u8) != 0 { - pin_config.cts = dec_option(s)?; - //} - - Ok(pin_config) - } -} - -pub struct GPIOConfig { - pub gpio10: Option>, - pub gpio11: Option>, -} - -pub struct PinChannel { - pub config: SerdePinConfig, - pub gpios: GPIOConfig, - pub tx: Channel::, - pub rx: Channel::, - // TODO: cts/rts pins -} - -impl PinChannel { - pub fn new(config: SerdePinConfig, gpios: GPIOConfig) -> Self { - Self { - config, - gpios, - tx: Channel::::new(), - rx: Channel::::new(), - } - } - - pub async fn recv_tx(&mut self) -> errors::Result> { - // tx needs to lock here. - //self.tx.receive().await; - - Ok(match self.config.tx { - 10 => self.gpios.gpio10.take().ok_or_else(|| errors::Error::InvalidPin)?, - 11 => self.gpios.gpio11.take().ok_or_else(|| errors::Error::InvalidPin)?, - _ => return Err(errors::Error::InvalidPin) - }) - } - - pub async fn send_tx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { - match self.config.tx { - 10 => self.gpios.gpio10 = Some(pin), - 11 => self.gpios.gpio11 = Some(pin), - _ => return Err(errors::Error::InvalidPin) - }; - - // tx lock needs to be released. - self.tx.send(()).await; - Ok(()) - } - - pub async fn recv_rx(&mut self) -> errors::Result> { - let res = Ok(match self.config.rx { - 10 => self.gpios.gpio10.take().ok_or_else(|| errors::Error::InvalidPin)?, - 11 => self.gpios.gpio11.take().ok_or_else(|| errors::Error::InvalidPin)?, - _ => return Err(errors::Error::InvalidPin) - }); - // rx needs to lock here. - // dbg!("recv_rx: before rx.receive.await"); - // self.rx.receive().await; - // dbg!("recv_rx: after rx.receive.await"); - - res - } - - pub async fn send_rx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { - match self.config.rx { - 10 => self.gpios.gpio10 = Some(pin), - 11 => self.gpios.gpio11 = Some(pin), - _ => return Err(errors::Error::InvalidPin) - }; - - // rx lock needs to be released. - self.rx.send(()).await; - Ok(()) - } - - pub async fn with_channel(&mut self, f: F) -> errors::Result<()> - where F: for<'a> AsyncFnOnce(AnyPin<'a>, AnyPin<'a>) { - let mut rx = self.recv_rx().await?; - let mut tx = self.recv_tx().await?; - - f(rx.reborrow(), tx.reborrow()).await; - - self.send_rx(rx).await.unwrap(); - self.send_tx(tx).await.unwrap(); - - Ok(()) - } -} - - -// TODO: This struct and resolve_pin() need to be re-thought for the different ICs and dev boards?.. implementing a suitable -// validation function for them and potentially writing a macro that adapts to each PAC (not all ICs have the same number -// of pins). -pub struct PinConfig { - pub tx: AnyPin<'static>, - pub rx: AnyPin<'static>, -} - -pub struct PinConfigAlt { - pub peripherals: peripherals::Peripherals, -} - -impl PinConfigAlt { - pub fn new(peripherals: peripherals::Peripherals) -> Self { - Self { - peripherals, - } - } - - pub fn take_pin<'a>(&'a mut self, pin: u8) -> AnyPin<'a> { - match pin { - 0 => self.peripherals.GPIO0.reborrow().into(), - 1 => self.peripherals.GPIO1.reborrow().into(), - _ => panic!(), - } - } -} - -impl PinConfig { - pub fn new(mut gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { - if config_inner.rx == config_inner.tx { - return Err(errors::Error::InvalidPin); - } - - // SAFETY: Safe because moved in peripherals. - Ok(Self { - rx: match config_inner.rx { - 10 => gpio_config.gpio10.take().unwrap().into(), - 11 => gpio_config.gpio11.take().unwrap().into(), - _ => return Err(errors::Error::InvalidPin), - }, - tx: match config_inner.tx { - 10 => gpio_config.gpio10.take().unwrap().into(), - 11 => gpio_config.gpio11.take().unwrap().into(), - _ => return Err(errors::Error::InvalidPin), - } - }) - } - - /// Resolves a u8 pin number into an AnyPin GPIO type. - /// Returns None if the pin number is invalid or unsupported. - pub fn initialize_pin(peripherals: peripherals::Peripherals, pin_number: u8) -> errors::Result> { - match pin_number { - 0 => Ok(peripherals.GPIO0.into()), - - _ => Err(errors::Error::InvalidPin), - } - } -} - impl SSHStampConfig { /// Bump this when the format changes pub const CURRENT_VERSION: u8 = 6; @@ -282,7 +58,7 @@ impl SSHStampConfig { /// Will only fail on RNG failure. pub fn new() -> Result { let hostkey = SignKey::generate(KeyType::Ed25519, None)?; - + // TODO: Those env events come from system's std::env / core::env (if any)... so it shouldn't be unsafe() let wifi_ssid_str = String::try_from(DEFAULT_SSID).unwrap(); let wifi_ssid: String<32> = wifi_ssid_str.into(); @@ -351,12 +127,12 @@ where } // encode Option as a bool then maybe a value -fn enc_option(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { +pub(crate) fn enc_option(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { v.is_some().enc(s)?; v.enc(s) } -fn dec_option<'de, S, T: SSHDecode<'de>>(s: &mut S) -> WireResult> +pub(crate) fn dec_option<'de, S, T: SSHDecode<'de>>(s: &mut S) -> WireResult> where S: SSHSource<'de>, { @@ -437,17 +213,6 @@ where .transpose() } -// fn dec_uart_pins<'de, S>(s: &mut S) -> WireResult -// where -// S: SSHSource<'de>, -// { -// let tx = u8::dec(s)?; -// let rx = u8::dec(s)?; -// let rts = dec_option(s)?; -// let cts = dec_option(s)?; -// Ok(SerdePinConfig { tx, rx, rts, cts }) -// } - impl SSHEncode for SSHStampConfig { fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { enc_signkey(&self.hostkey, s)?; @@ -480,10 +245,9 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { where S: SSHSource<'de>, { - let hostkey = dec_signkey(s)?; - // Authentication + // Authentication let password_authentication = SSHDecode::dec(s)?; let admin_pw = dec_option(s)?; let mut admin_keys = [None; KEY_SLOTS]; @@ -609,4 +373,4 @@ pub fn roundtrip_config() { let v = &buf[..l]; let c2: SSHStampConfig = sshwire::read_ssh(v, None).unwrap(); assert_eq!(c1, c2); -} \ No newline at end of file +} diff --git a/src/errors.rs b/src/errors.rs index e0c9779..8b69a87 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,5 @@ -use snafu::Snafu; use core::result; +use snafu::Snafu; pub type Result = result::Result; @@ -9,4 +9,4 @@ pub enum Error { InvalidPin, #[snafu(display("Flash storage error"))] FlashStorageError, -} \ No newline at end of file +} diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 75e0a2f..8393223 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -45,7 +45,7 @@ pub async fn if_up( wifi_controller: EspWifiController<'static>, wifi: WIFI<'static>, rng: &mut Rng, - config: &'static SunsetMutex + config: &'static SunsetMutex, ) -> Result, sunset::Error> { let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); @@ -60,7 +60,7 @@ pub async fn if_up( // embassy_net::Config::dhcpv4(Default::default()) // } // }; - + let net_config = embassy_net::Config::ipv4_static(StaticConfigV4 { address: Ipv4Cidr::new(gw_ip_addr_ipv4, 24), gateway: Some(gw_ip_addr_ipv4), @@ -129,7 +129,10 @@ pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { } #[embassy_executor::task] -async fn wifi_up(mut controller: WifiController<'static>, config: &'static SunsetMutex) { +async fn wifi_up( + mut controller: WifiController<'static>, + config: &'static SunsetMutex, +) { println!("Device capabilities: {:?}", controller.capabilities()); let wifi_ssid = { diff --git a/src/lib.rs b/src/lib.rs index ed48a46..3b4c225 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,12 @@ #![no_main] // #![forbid(unsafe_code)] #[deny(clippy::mem_forget)] // avoids any UB, forces use of Drop impl instead - pub mod config; +pub mod pins; +pub mod errors; pub mod espressif; pub mod keys; pub mod serial; pub mod serve; pub mod settings; pub mod storage; -pub mod errors; diff --git a/src/main.rs b/src/main.rs index a7f6fd3..4b55931 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,22 +5,31 @@ use core::marker::Sized; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - gpio::Pin, interrupt::{software::SoftwareInterruptControl, Priority}, peripherals::UART1, rng::Rng, timer::timg::TimerGroup, uart::{Config, RxConfig, Uart} + gpio::Pin, + interrupt::{software::SoftwareInterruptControl, Priority}, + peripherals::UART1, + rng::Rng, + timer::timg::TimerGroup, + uart::{Config, RxConfig, Uart}, }; use esp_hal_embassy::InterruptExecutor; +use embassy_executor::Spawner; use esp_println::dbg; use esp_storage::FlashStorage; -use embassy_executor::Spawner; -use ssh_stamp::{config::SSHStampConfig, espressif::{ - buffered_uart::BufferedUart, - net::{accept_requests, if_up}, - rng, -}, storage::Fl}; +use ssh_stamp::pins::GPIOConfig; +use ssh_stamp::pins::PinChannel; +use ssh_stamp::{ + config::SSHStampConfig, + espressif::{ + buffered_uart::BufferedUart, + net::{accept_requests, if_up}, + rng, + }, + storage::Fl, +}; use static_cell::StaticCell; use sunset_async::SunsetMutex; -use ssh_stamp::config::GPIOConfig; -use ssh_stamp::config::PinChannel; #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { @@ -94,22 +103,21 @@ async fn main(spawner: Spawner) -> ! { guard.uart_pins.clone() }; - let available_gpios = GPIOConfig { gpio10: Some(peripherals.GPIO10.degrade()), gpio11: Some(peripherals.GPIO11.degrade()), }; - + static CHANNEL: StaticCell = StaticCell::new(); - let channel = CHANNEL.init({ - PinChannel::new(serde_pin_config, available_gpios) - }); + let channel = CHANNEL.init(PinChannel::new(serde_pin_config, available_gpios)); // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality let uart1 = peripherals.UART1; // Use the same config reference for UART task. - interrupt_spawner.spawn(uart_task(uart_buf, uart1, channel)).unwrap(); + interrupt_spawner + .spawn(uart_task(uart_buf, uart1, channel)) + .unwrap(); accept_requests(tcp_stack, uart_buf).await; } @@ -128,18 +136,21 @@ async fn uart_task( let uart_config = Config::default().with_rx( RxConfig::default() .with_fifo_full_threshold(16) - .with_timeout(1) + .with_timeout(1), ); // Sync pin config via channels - channel.with_channel(async |rx, tx| { - let uart = Uart::new(uart_periph, uart_config) - .unwrap() - .with_rx(rx) - .with_tx(tx) - .into_async(); - - // Run the main buffered TX/RX loop - buffer.run(uart).await; - }).await.unwrap(); + channel + .with_channel(async |rx, tx| { + let uart = Uart::new(uart_periph, uart_config) + .unwrap() + .with_rx(rx) + .with_tx(tx) + .into_async(); + + // Run the main buffered TX/RX loop + buffer.run(uart).await; + }) + .await + .unwrap(); } diff --git a/src/serve.rs b/src/serve.rs index ebf6500..61384d9 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -2,6 +2,7 @@ use core::option::Option::{self, None, Some}; use core::result::Result; use core::writeln; +use crate::pins::{PinChannel, SerdePinConfig}; use crate::espressif::buffered_uart::BufferedUart; use crate::keys; use crate::serial::serial_bridge; @@ -21,9 +22,9 @@ use sunset_async::{ProgressHolder, SSHServer}; use esp_println::{dbg, println}; enum SessionType { - Bridge(ChanHandle), - Env(ChanHandle), - Sftp(ChanHandle), + Bridge(ChanHandle), + Env(ChanHandle), + Sftp(ChanHandle), } async fn connection_loop( @@ -80,34 +81,60 @@ async fn connection_loop( } } ServEvent::Environment(a) => { + dbg!("Got ENV request"); + dbg!(a.name()?); + dbg!(a.value()?); + + let mut uart_pins = PinChannel::new(SerdePinConfig::new(), Default::default()); // TODO: Logic to serialise/validate env vars? I.e: - // config.validate(a) - // config.save(a) - // SSHConfig c = a.validate(); // Checks the input variable, sanitizes, assigns a target subsystem - // a.config_change(c)?; - - // Obtain the current session channel handle and use it to get stdio. - // if let Some(ch) = session.take() { - // debug_assert!(ch.num() == a.channel()); - - // a.succeed()?; - // let _ = chan_pipe.try_send(SessionType::Env(ch)); - // } else { - // a.fail()?; - // } + // a.name.validate(); // Checks the input variable, sanitizes, assigns a target subsystem + // + // config.change(c): Apply the config change to the relevant subsystem. + // i.e: if UART_TX_PIN or UART_RX_PIN, we update the PinChannel with with_channel() to change pins live. + match a.name()? { + "SAVE_CONFIG" => { + if a.value()? == "1" { + dbg!("Triggering config save..."); + todo!("Implement config save to flash"); + } + } + // If the env var is UART_TX_PIN or UART_RX_PIN + "UART_TX_PIN" => { + let tx = a.value()?; + // Update PinChannel with new TX pin + dbg!("Updating UART TX pin to ", a.value()?); + uart_pins + .with_channel(async |_, tx| { + dbg!("Setting new TX pin in PinChannel to: ", tx); + }) + .await + .unwrap(); + } + "UART_RX_PIN" => { + // Update PinChannel with new RX pin + dbg!("Updating UART RX pin to ", a.value()?); + } + _ => { + dbg!("Unknown/unsupported ENV var"); + } + } + + // config.save(a): Potentially an optional special environment variable SAVE_CONFIG=1 + // that serialises current config to flash + a.succeed()?; - }, + } ServEvent::SessionPty(a) => { a.succeed()?; - }, + } ServEvent::SessionExec(a) => { a.fail()?; - }, + } ServEvent::Defunct | ServEvent::SessionShell(_) => { println!("Expected caller to handle event"); error::BadUsage.fail()? } - _ => () + _ => (), }; } } @@ -135,13 +162,13 @@ pub(crate) async fn handle_ssh_client( // TODO: Maybe loop forever here and/or handle disconnection/terminations gracefully? let bridge = async { let session_type = chan_pipe.receive().await; - + match session_type { SessionType::Bridge(ch) => { let stdio = ssh_server.stdio(ch).await?; let stdio2 = stdio.clone(); serial_bridge(stdio, stdio2, uart).await? - }, + } SessionType::Env(ch) => { // Handle environment variable session let mut stdio = ssh_server.stdio(ch).await?; @@ -150,11 +177,11 @@ pub(crate) async fn handle_ssh_client( let n = stdio.read(&mut buf).await?; dbg!("Got ENV session"); dbg!("Name/Value of ENV is: ", &buf[..n]); - }, + } SessionType::Sftp(_ch) => { // Handle SFTP session todo!() - }, + } }; Ok(()) }; diff --git a/src/settings.rs b/src/settings.rs index 168aaad..e67485f 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -6,8 +6,8 @@ pub(crate) const DEFAULT_SSID: &str = "ssh-stamp"; //pub(crate) const SSH_SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; pub(crate) const KEY_SLOTS: usize = 1; // TODO: Document whether this a "reasonable default"? Justify why? -//pub(crate) const PASSWORD_AUTHENTICATION: bool = true; + //pub(crate) const PASSWORD_AUTHENTICATION: bool = true; // UART settings //pub(crate) const BAUD_RATE: u32 = 115200; -//pub(crate) const UART_SETTINGS: &str = "8N1"; \ No newline at end of file +//pub(crate) const UART_SETTINGS: &str = "8N1"; diff --git a/src/storage.rs b/src/storage.rs index 68447e0..fd0f7b5 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,7 +1,7 @@ +use embedded_storage::ReadStorage; use esp_bootloader_esp_idf::partitions; -use esp_println::{println, dbg}; +use esp_println::{dbg, println}; use esp_storage::FlashStorage; -use embedded_storage::ReadStorage; use pretty_hex::PrettyHex; use sha2::Digest; @@ -10,8 +10,8 @@ use core::borrow::Borrow; use embedded_storage::nor_flash::NorFlash; -use sunset::error::Error as SunsetError; use crate::errors::Error as SSHStampError; +use sunset::error::Error as SunsetError; use sunset::sshwire::{self, OwnOrBorrow}; use sunset_sshwire_derive::*; @@ -32,7 +32,10 @@ pub struct Fl { impl<'a> Fl { pub fn new(flash: FlashStorage) -> Self { - Self { flash, buf: [0u8; FlashConfig::BUF_SIZE] } + Self { + flash, + buf: [0u8; FlashConfig::BUF_SIZE], + } } } @@ -57,11 +60,11 @@ impl FlashConfig<'_> { let mut pt_mem = [0u8; partitions::PARTITION_TABLE_MAX_LEN]; let pt = partitions::read_partition_table(&mut flash, &mut pt_mem).unwrap(); let nvs = pt - .find_partition(partitions::PartitionType::Data( - partitions::DataPartitionSubType::Nvs, - )) - .unwrap() - .unwrap(); + .find_partition(partitions::PartitionType::Data( + partitions::DataPartitionSubType::Nvs, + )) + .unwrap() + .unwrap(); let nvs_partition = nvs.as_embedded_storage(&mut flash); @@ -95,17 +98,20 @@ pub async fn create(flash: &mut Fl) -> Result { let c = SSHStampConfig::new()?; save(flash, &c).await?; dbg!("Created new config: ", &c); - + Ok(c) } pub async fn load(fl: &mut Fl) -> Result { - fl.flash.read(CONFIG_OFFSET as u32, &mut fl.buf).map_err(|_e| { - dbg!("flash read error 0x{CONFIG_OFFSET:x} {e:?}"); - SunsetError::msg("flash error") - })?; + fl.flash + .read(CONFIG_OFFSET as u32, &mut fl.buf) + .map_err(|_e| { + dbg!("flash read error 0x{CONFIG_OFFSET:x} {e:?}"); + SunsetError::msg("flash error") + })?; - let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None).map_err(|_| SunsetError::msg("failed to decode flash config"))?; + let flash_config: FlashConfig = sshwire::read_ssh(&fl.buf, None) + .map_err(|_| SunsetError::msg("failed to decode flash config"))?; if flash_config.version != SSHStampConfig::CURRENT_VERSION { dbg!("wrong config version on decode: {}", flash_config.version); @@ -113,7 +119,7 @@ pub async fn load(fl: &mut Fl) -> Result { } let calc_hash = config_hash(flash_config.config.borrow()).unwrap(); - + if calc_hash != flash_config.hash { return Err(SunsetError::msg("bad config hash")); } @@ -135,7 +141,7 @@ pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetErro FlashConfig::find_config_partition().unwrap(); -// dbg!("Saving config: ", &config); + // dbg!("Saving config: ", &config); dbg!("Before write_ssh, with hash: ", &sc.hash.hex_dump()); let l = sshwire::write_ssh(&mut fl.buf, &sc)?; let buf = &fl.buf[..l]; @@ -148,10 +154,13 @@ pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetErro assert!(CONFIG_AREA_SIZE > FlashConfig::BUF_SIZE); fl.flash - .erase(CONFIG_OFFSET as u32, (CONFIG_OFFSET + CONFIG_AREA_SIZE) as u32).unwrap(); + .erase( + CONFIG_OFFSET as u32, + (CONFIG_OFFSET + CONFIG_AREA_SIZE) as u32, + ) + .unwrap(); - fl.flash - .write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); + fl.flash.write(CONFIG_OFFSET as u32, &fl.buf).unwrap(); println!("flash save done"); Ok(()) From 70177c298efc18fcebacb23f27506d198e363e52 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 17 Nov 2025 13:50:54 +1100 Subject: [PATCH 47/86] Separate 'regular' SSHConfig from Pins-related configuration and operations --- src/pins.rs | 249 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 src/pins.rs diff --git a/src/pins.rs b/src/pins.rs new file mode 100644 index 0000000..6759118 --- /dev/null +++ b/src/pins.rs @@ -0,0 +1,249 @@ +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel}; +use esp_hal::gpio::AnyPin; +use esp_hal::peripherals; +use sunset::sshwire::{ SSHDecode, SSHEncode, SSHSink, SSHSource, WireResult }; + +use crate::{config::{dec_option, enc_option}, errors}; + +#[derive(Debug, Clone, PartialEq)] +pub struct SerdePinConfig { + pub tx: u8, + pub rx: u8, + pub rts: Option, + pub cts: Option, +} + +impl SerdePinConfig { + /// Create a new SerdePinConfig with default values. + pub fn new() -> Self { + Self::default() + } + + /// Set the TX pin, returning an updated SerdePinConfig (builder style). + pub fn with_tx(mut self, tx: u8) -> Self { + self.tx = tx; + self + } + + /// Set the RX pin, returning an updated SerdePinConfig (builder style). + pub fn with_rx(mut self, rx: u8) -> Self { + self.rx = rx; + self + } + + /// Set the RTS pin (optional), returning an updated SerdePinConfig. + pub fn with_rts(mut self, rts: Option) -> Self { + self.rts = rts; + self + } + + /// Set the CTS pin (optional), returning an updated SerdePinConfig. + pub fn with_cts(mut self, cts: Option) -> Self { + self.cts = cts; + self + } +} + +impl Default for SerdePinConfig { + fn default() -> Self { + Self { + tx: 10, + rx: 11, + rts: None, + cts: None, + } + } +} + +impl SSHEncode for SerdePinConfig { + fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { + self.tx.enc(s)?; + self.rx.enc(s)?; + enc_option(&self.rts, s)?; + enc_option(&self.cts, s) + } +} + + +impl<'de> SSHDecode<'de> for SerdePinConfig { + fn dec(s: &mut S) -> WireResult + where + S: SSHSource<'de>, + { + // Decoding Options is problematic since encode only writes them if they exist. + let mut pin_config = SerdePinConfig::default(); + pin_config.tx = u8::dec(s)?; + pin_config.rx = u8::dec(s)?; + + pin_config.rts = dec_option(s)?; + pin_config.cts = dec_option(s)?; + + Ok(pin_config) + } +} + +#[derive(Default)] +pub struct GPIOConfig { + pub gpio10: Option>, + pub gpio11: Option>, +} + +pub struct PinChannel { + pub config: SerdePinConfig, + pub gpios: GPIOConfig, + pub tx: Channel, + pub rx: Channel, + // TODO: cts/rts pins +} + +impl PinChannel { + pub fn new(config: SerdePinConfig, gpios: GPIOConfig) -> Self { + Self { + config, + gpios, + tx: Channel::::new(), + rx: Channel::::new(), + } + } + + pub async fn recv_tx(&mut self) -> errors::Result> { + // tx needs to lock here. + //self.tx.receive().await; + + Ok(match self.config.tx { + 10 => self + .gpios + .gpio10 + .take() + .ok_or_else(|| errors::Error::InvalidPin)?, + 11 => self + .gpios + .gpio11 + .take() + .ok_or_else(|| errors::Error::InvalidPin)?, + _ => return Err(errors::Error::InvalidPin), + }) + } + + pub async fn send_tx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + match self.config.tx { + 10 => self.gpios.gpio10 = Some(pin), + 11 => self.gpios.gpio11 = Some(pin), + _ => return Err(errors::Error::InvalidPin), + }; + + // tx lock needs to be released. + self.tx.send(()).await; + Ok(()) + } + + pub async fn recv_rx(&mut self) -> errors::Result> { + let res = Ok(match self.config.rx { + 10 => self + .gpios + .gpio10 + .take() + .ok_or_else(|| errors::Error::InvalidPin)?, + 11 => self + .gpios + .gpio11 + .take() + .ok_or_else(|| errors::Error::InvalidPin)?, + _ => return Err(errors::Error::InvalidPin), + }); + // rx needs to lock here. + // dbg!("recv_rx: before rx.receive.await"); + // self.rx.receive().await; + // dbg!("recv_rx: after rx.receive.await"); + + res + } + + pub async fn send_rx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + match self.config.rx { + 10 => self.gpios.gpio10 = Some(pin), + 11 => self.gpios.gpio11 = Some(pin), + _ => return Err(errors::Error::InvalidPin), + }; + + // rx lock needs to be released. + self.rx.send(()).await; + Ok(()) + } + + pub async fn with_channel(&mut self, f: F) -> errors::Result<()> + where + F: for<'a> AsyncFnOnce(AnyPin<'a>, AnyPin<'a>), + { + let mut rx = self.recv_rx().await?; + let mut tx = self.recv_tx().await?; + + f(rx.reborrow(), tx.reborrow()).await; + + self.send_rx(rx).await.unwrap(); + self.send_tx(tx).await.unwrap(); + + Ok(()) + } +} + +// TODO: This struct and resolve_pin() need to be re-thought for the different ICs and dev boards?.. implementing a suitable +// validation function for them and potentially writing a macro that adapts to each PAC (not all ICs have the same number +// of pins). +pub struct PinConfig { + pub tx: AnyPin<'static>, + pub rx: AnyPin<'static>, +} + +pub struct PinConfigAlt { + pub peripherals: peripherals::Peripherals, +} + +impl PinConfigAlt { + pub fn new(peripherals: peripherals::Peripherals) -> Self { + Self { peripherals } + } + + pub fn take_pin<'a>(&'a mut self, pin: u8) -> AnyPin<'a> { + match pin { + 0 => self.peripherals.GPIO0.reborrow().into(), + 1 => self.peripherals.GPIO1.reborrow().into(), + _ => panic!(), + } + } +} + +impl PinConfig { + pub fn new(mut gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { + if config_inner.rx == config_inner.tx { + return Err(errors::Error::InvalidPin); + } + + // SAFETY: Safe because moved in peripherals. + Ok(Self { + rx: match config_inner.rx { + 10 => gpio_config.gpio10.take().unwrap().into(), + 11 => gpio_config.gpio11.take().unwrap().into(), + _ => return Err(errors::Error::InvalidPin), + }, + tx: match config_inner.tx { + 10 => gpio_config.gpio10.take().unwrap().into(), + 11 => gpio_config.gpio11.take().unwrap().into(), + _ => return Err(errors::Error::InvalidPin), + }, + }) + } + + /// Resolves a u8 pin number into an AnyPin GPIO type. + /// Returns None if the pin number is invalid or unsupported. + pub fn initialize_pin( + peripherals: peripherals::Peripherals, + pin_number: u8, + ) -> errors::Result> { + match pin_number { + 0 => Ok(peripherals.GPIO0.into()), + + _ => Err(errors::Error::InvalidPin), + } + } +} \ No newline at end of file From e74124255535fa24c263ff1d3428c53b622b2342 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 17 Nov 2025 15:12:50 +1100 Subject: [PATCH 48/86] Comment out SessionType(s) not needed for now --- src/serve.rs | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/serve.rs b/src/serve.rs index 61384d9..d2d922f 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -14,7 +14,6 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; -use embedded_io_async::Read; use heapless::String; use sunset::{error, ChanHandle, ServEvent, SignKey}; use sunset_async::{ProgressHolder, SSHServer}; @@ -23,8 +22,7 @@ use esp_println::{dbg, println}; enum SessionType { Bridge(ChanHandle), - Env(ChanHandle), - Sftp(ChanHandle), + //Sftp(ChanHandle), } async fn connection_loop( @@ -119,6 +117,8 @@ async fn connection_loop( } } + + // config.save(a): Potentially an optional special environment variable SAVE_CONFIG=1 // that serialises current config to flash @@ -169,19 +169,10 @@ pub(crate) async fn handle_ssh_client( let stdio2 = stdio.clone(); serial_bridge(stdio, stdio2, uart).await? } - SessionType::Env(ch) => { - // Handle environment variable session - let mut stdio = ssh_server.stdio(ch).await?; - let mut buf = [0u8; 256]; - dbg!("Waiting to read ENV session data"); - let n = stdio.read(&mut buf).await?; - dbg!("Got ENV session"); - dbg!("Name/Value of ENV is: ", &buf[..n]); - } - SessionType::Sftp(_ch) => { - // Handle SFTP session - todo!() - } + // SessionType::Sftp(_ch) => { + // // Handle SFTP session + // todo!() + // } }; Ok(()) }; From 8a8fe3d617804c95ec007f5c16e190546dea1b57 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Mon, 17 Nov 2025 15:51:10 +1100 Subject: [PATCH 49/86] Fix up PinChannel logic so that pins are updated on the connection_loop, when receiving appropriate env vars (to be validated on device). --- src/espressif/net.rs | 8 ++++++-- src/main.rs | 24 +++++++++++++++++------ src/pins.rs | 44 ++++++++++++++++++++++++++++++++++++++++--- src/serve.rs | 45 +++++++++++++++++++++++++++++--------------- 4 files changed, 95 insertions(+), 26 deletions(-) diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 8393223..3ce401c 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -98,7 +98,11 @@ pub async fn if_up( Ok(ap_stack) } -pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { +pub async fn accept_requests( + stack: Stack<'static>, + uart: &BufferedUart, + pin_channel_ref: &'static sunset_async::SunsetMutex, +) -> ! { let rx_buffer = mk_static!([u8; 1536], [0; 1536]); let tx_buffer = mk_static!([u8; 1536], [0; 1536]); @@ -119,7 +123,7 @@ pub async fn accept_requests(stack: Stack<'static>, uart: &BufferedUart) -> ! { } println!("Connected, port 22"); - match crate::serve::handle_ssh_client(&mut socket, uart).await { + match crate::serve::handle_ssh_client(&mut socket, uart, pin_channel_ref).await { Ok(_) => (), Err(e) => { println!("SSH client fatal error: {}", e); diff --git a/src/main.rs b/src/main.rs index 4b55931..62c0b07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use esp_hal_embassy::InterruptExecutor; use embassy_executor::Spawner; use esp_println::dbg; use esp_storage::FlashStorage; +use ssh_stamp::pins; use ssh_stamp::pins::GPIOConfig; use ssh_stamp::pins::PinChannel; use ssh_stamp::{ @@ -108,18 +109,26 @@ async fn main(spawner: Spawner) -> ! { gpio11: Some(peripherals.GPIO11.degrade()), }; - static CHANNEL: StaticCell = StaticCell::new(); - let channel = CHANNEL.init(PinChannel::new(serde_pin_config, available_gpios)); + // Initialize the global pin channel and keep the &'static reference so we can + // pass it to tasks that need to mutate pins (no unsafe globals). + let pin_channel_ref = pins::init_global_channel(PinChannel::new( + serde_pin_config, + available_gpios, + )); // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality let uart1 = peripherals.UART1; // Use the same config reference for UART task. + // Pass pin_channel_ref into the UART task so it can acquire/release pins. interrupt_spawner - .spawn(uart_task(uart_buf, uart1, channel)) + .spawn(uart_task(uart_buf, uart1, pin_channel_ref)) .unwrap(); - accept_requests(tcp_stack, uart_buf).await; + // Pass pin_channel_ref into accept_requests (so SSH handlers can use it). + // NOTE: accept_requests signature must accept this arg; if it doesn't, + // thread the reference into whatever code spawns handle_ssh_client. + accept_requests(tcp_stack, uart_buf, pin_channel_ref).await; } static UART_BUF: StaticCell = StaticCell::new(); @@ -129,7 +138,7 @@ static INT_EXECUTOR: StaticCell> = StaticCell::new(); async fn uart_task( buffer: &'static BufferedUart, uart_periph: UART1<'static>, - channel: &'static mut PinChannel, + pin_channel_ref: &'static SunsetMutex, ) { dbg!("Spawning UART task..."); // Hardware UART setup @@ -139,8 +148,11 @@ async fn uart_task( .with_timeout(1), ); + // Use the pinned reference passed in from main. + let mut pin_chan = pin_channel_ref.lock().await; + // Sync pin config via channels - channel + pin_chan .with_channel(async |rx, tx| { let uart = Uart::new(uart_periph, uart_config) .unwrap() diff --git a/src/pins.rs b/src/pins.rs index 6759118..73c47f1 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -2,6 +2,8 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channe use esp_hal::gpio::AnyPin; use esp_hal::peripherals; use sunset::sshwire::{ SSHDecode, SSHEncode, SSHSink, SSHSource, WireResult }; +use static_cell::StaticCell; +use sunset_async::SunsetMutex; use crate::{config::{dec_option, enc_option}, errors}; @@ -185,11 +187,47 @@ impl PinChannel { Ok(()) } + + // Update the runtime config's TX pin number. This only changes the + // u8 config; actual AnyPin movement happens when the uart task next + // reacquires pins via recv_tx/recv_rx. + pub fn set_tx_pin(&mut self, tx: u8) -> errors::Result<()> { + if tx == self.config.rx { + return Err(errors::Error::InvalidPin); + } + match tx { + 10 | 11 => { + self.config.tx = tx; + Ok(()) + } + _ => Err(errors::Error::InvalidPin), + } + } + + pub fn set_rx_pin(&mut self, rx: u8) -> errors::Result<()> { + if rx == self.config.tx { + return Err(errors::Error::InvalidPin); + } + match rx { + 10 | 11 => { + self.config.rx = rx; + Ok(()) + } + _ => Err(errors::Error::InvalidPin), + } + } +} + +// Global PinChannel holder: initialize from main() and access from other modules. +// We keep a StaticCell but avoid any unsafe global pointer; callers receive +// the &'static SunsetMutex returned by init_global_channel and must retain it. +static GLOBAL_PIN_CHANNEL: StaticCell> = StaticCell::new(); + +pub fn init_global_channel(ch: PinChannel) -> &'static SunsetMutex { + // Initialize the StaticCell and return the &'static reference. + GLOBAL_PIN_CHANNEL.init(SunsetMutex::new(ch)) } -// TODO: This struct and resolve_pin() need to be re-thought for the different ICs and dev boards?.. implementing a suitable -// validation function for them and potentially writing a macro that adapts to each PAC (not all ICs have the same number -// of pins). pub struct PinConfig { pub tx: AnyPin<'static>, pub rx: AnyPin<'static>, diff --git a/src/serve.rs b/src/serve.rs index d2d922f..fa72515 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -2,7 +2,7 @@ use core::option::Option::{self, None, Some}; use core::result::Result; use core::writeln; -use crate::pins::{PinChannel, SerdePinConfig}; +use crate::pins::PinChannel; use crate::espressif::buffered_uart::BufferedUart; use crate::keys; use crate::serial::serial_bridge; @@ -13,6 +13,7 @@ use embassy_net::tcp::TcpSocket; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; +use sunset_async::SunsetMutex; use heapless::String; use sunset::{error, ChanHandle, ServEvent, SignKey}; @@ -28,6 +29,7 @@ enum SessionType { async fn connection_loop( serv: &SSHServer<'_>, chan_pipe: &Channel, + pin_channel_ref: &'static SunsetMutex, ) -> Result<(), sunset::Error> { let username = Mutex::::new(String::<20>::new()); let mut session: Option = None; @@ -83,7 +85,6 @@ async fn connection_loop( dbg!(a.name()?); dbg!(a.value()?); - let mut uart_pins = PinChannel::new(SerdePinConfig::new(), Default::default()); // TODO: Logic to serialise/validate env vars? I.e: // a.name.validate(); // Checks the input variable, sanitizes, assigns a target subsystem // @@ -98,26 +99,39 @@ async fn connection_loop( } // If the env var is UART_TX_PIN or UART_RX_PIN "UART_TX_PIN" => { - let tx = a.value()?; - // Update PinChannel with new TX pin - dbg!("Updating UART TX pin to ", a.value()?); - uart_pins - .with_channel(async |_, tx| { - dbg!("Setting new TX pin in PinChannel to: ", tx); - }) - .await - .unwrap(); + let val = a.value()?; + dbg!("Updating UART TX pin to ", val); + if let Ok(pin_num) = val.parse::() { + let mut ch = pin_channel_ref.lock().await; + if ch.set_tx_pin(pin_num).is_err() { + dbg!("Failed to update TX pin"); + } else { + dbg!("TX pin updated"); + } + } else { + dbg!("Invalid TX pin value"); + } } "UART_RX_PIN" => { - // Update PinChannel with new RX pin - dbg!("Updating UART RX pin to ", a.value()?); + let val = a.value()?; + dbg!("Updating UART RX pin to ", val); + if let Ok(pin_num) = val.parse::() { + let mut ch = pin_channel_ref.lock().await; + if ch.set_rx_pin(pin_num).is_err() { + dbg!("Failed to update RX pin"); + } else { + dbg!("RX pin updated"); + } + } else { + dbg!("Invalid RX pin value"); + } } _ => { dbg!("Unknown/unsupported ENV var"); } } - + // config.save(a): Potentially an optional special environment variable SAVE_CONFIG=1 // that serialises current config to flash @@ -142,6 +156,7 @@ async fn connection_loop( pub(crate) async fn handle_ssh_client( stream: &mut TcpSocket<'_>, uart: &BufferedUart, + pin_channel_ref: &'static SunsetMutex, ) -> Result<(), sunset::Error> { // Spawn network tasks to handle incoming connections with demo_common::session() let mut inbuf = [0u8; 4096]; @@ -153,7 +168,7 @@ pub(crate) async fn handle_ssh_client( let chan_pipe = Channel::::new(); println!("Calling connection_loop from handle_ssh_client"); - let conn_loop = connection_loop(&ssh_server, &chan_pipe); + let conn_loop = connection_loop(&ssh_server, &chan_pipe, pin_channel_ref); println!("Running server from handle_ssh_client()"); let server = ssh_server.run(&mut rsock, &mut wsock); From cfa619a99c0ab865a0e7cc7c435fc984eae78bb4 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Tue, 9 Dec 2025 14:30:48 +1100 Subject: [PATCH 50/86] Initial implementation of Hierarchical State Machine (HSM) to replace main program flow. Still need to implement error result loops and cleanup functions to ensure correct transitions through each level of the HSM. Removed many static lifetimes to ensure each variable only exists at the level of HSM that is required. serve.rs: ServEvent::Environment doesn't exist. Probably a Sunset dependency version issue. Replaced calls to new embassy tasks with async functions to reduce concurrency and confine to each HSM level. --- src/espressif/net.rs | 78 +++--- src/main.rs | 627 +++++++++++++++++++++++++++++++++++-------- src/pins.rs | 46 ++-- src/serial.rs | 2 +- src/serve.rs | 128 ++++----- 5 files changed, 650 insertions(+), 231 deletions(-) diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 3ce401c..dfd5f20 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -40,15 +40,19 @@ macro_rules! mk_static { }}; } -pub async fn if_up( +use heapless::String; + +pub async fn if_up<'a>( spawner: Spawner, - wifi_controller: EspWifiController<'static>, - wifi: WIFI<'static>, + wifi_controller: EspWifiController<'a>, + wifi: WIFI<'a>, rng: &mut Rng, - config: &'static SunsetMutex, -) -> Result, sunset::Error> { - let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); - let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); + // config: &'static SunsetMutex, + wifi_ssid: String<32>, +) -> Result, sunset::Error> { + // let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); + // let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); + let (controller, interfaces) = esp_wifi::wifi::new(&wifi_controller, wifi).unwrap(); let gw_ip_addr_ipv4 = Ipv4Addr::from_str("192.168.0.1").expect("failed to parse gateway ip"); @@ -77,9 +81,11 @@ pub async fn if_up( seed, ); - spawner.spawn(wifi_up(controller, config)).ok(); - spawner.spawn(net_up(runner)).ok(); - spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); + // spawner.spawn(wifi_up(controller, wifi_ssid)).ok(); + // spawner.spawn(net_up(runner)).ok(); + // spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); + wifi_up(controller, wifi_ssid); + dhcp_server(ap_stack, gw_ip_addr_ipv4); loop { println!("Checking if link is up...\n"); @@ -98,16 +104,19 @@ pub async fn if_up( Ok(ap_stack) } -pub async fn accept_requests( - stack: Stack<'static>, +pub async fn accept_requests<'a>( + stack: Stack<'a>, uart: &BufferedUart, - pin_channel_ref: &'static sunset_async::SunsetMutex, + // pin_channel_ref: &'a sunset_async::SunsetMutex>, + pin_channel_ref: &crate::pins::PinChannel<'a>, ) -> ! { - let rx_buffer = mk_static!([u8; 1536], [0; 1536]); - let tx_buffer = mk_static!([u8; 1536], [0; 1536]); - + // let rx_buffer = mk_static!([u8; 1536], [0; 1536]); + // let tx_buffer = mk_static!([u8; 1536], [0; 1536]); + let mut rx_buffer = [0u8; 1536]; + let mut tx_buffer = [0u8; 1536]; loop { - let mut socket = TcpSocket::new(stack, rx_buffer, tx_buffer); + // let mut socket = TcpSocket::new(stack, rx_buffer, tx_buffer); + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); println!("Waiting for SSH client..."); @@ -132,18 +141,19 @@ pub async fn accept_requests( } } -#[embassy_executor::task] +// #[embassy_executor::task] async fn wifi_up( - mut controller: WifiController<'static>, - config: &'static SunsetMutex, + // mut controller: WifiController<'static>, + mut controller: WifiController<'_>, + // config: &'static SunsetMutex, + wifi_ssid: String<32>, ) { println!("Device capabilities: {:?}", controller.capabilities()); - - let wifi_ssid = { - let guard = config.lock().await; - guard.wifi_ssid.clone() - // drop guard - }; + // let wifi_ssid = { + // let guard = config.lock().await; + // guard.wifi_ssid.clone() + // // drop guard + // }; // TODO: No wifi password(s) yet... //let wifi_password = config.lock().await.wifi_pw; @@ -160,21 +170,21 @@ async fn wifi_up( }); controller.set_configuration(&client_config).unwrap(); println!("Starting wifi"); - controller.start_async().await.unwrap(); + // controller.start_async().await.unwrap(); println!("Wifi started!"); } Timer::after(Duration::from_millis(10)).await; } } -#[embassy_executor::task] -async fn net_up(mut runner: Runner<'static, WifiDevice<'static>>) { - println!("Bringing up network stack...\n"); - runner.run().await -} +// #[embassy_executor::task] +// async fn net_up<'a>(mut runner: Runner<'a, WifiDevice<'a>>) { + // println!("Bringing up network stack...\n"); + // runner.run().await +// } -#[embassy_executor::task] -async fn dhcp_server(stack: Stack<'static>, ip: Ipv4Addr) { +// #[embassy_executor::task] +async fn dhcp_server<'a>(stack: Stack<'a>, ip: Ipv4Addr) { let mut buf = [0u8; 1500]; let mut gw_buf = [Ipv4Addr::UNSPECIFIED]; diff --git a/src/main.rs b/src/main.rs index 62c0b07..444b1ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,36 +1,215 @@ #![no_std] #![no_main] - -use core::marker::Sized; -use esp_alloc as _; -use esp_backtrace as _; +use core::result::Result; +use core::result::Result::Ok; +use core::result::Result::Err; +// use core::error::Error; +use core::future::Future; +use embassy_futures::select::{select3, Either3}; +use embassy_executor::Spawner; +use embassy_net::{ Stack, tcp::TcpSocket}; +use embassy_sync::{ + blocking_mutex::raw::NoopRawMutex, + channel::Channel +}; +use esp_println::println; use esp_hal::{ gpio::Pin, - interrupt::{software::SoftwareInterruptControl, Priority}, - peripherals::UART1, + // interrupt::{software::SoftwareInterruptControl, Priority}, + // peripherals::Peripherals, + // peripherals::UART1, + peripherals::{RADIO_CLK, WIFI, TIMG0}, + peripherals::{GPIO10,GPIO11}, rng::Rng, timer::timg::TimerGroup, - uart::{Config, RxConfig, Uart}, + // uart::{Config, RxConfig, Uart}, }; -use esp_hal_embassy::InterruptExecutor; - -use embassy_executor::Spawner; -use esp_println::dbg; +use heapless::String; +use static_cell::StaticCell; +use esp_wifi::{InitializationError, EspWifiController}; use esp_storage::FlashStorage; -use ssh_stamp::pins; -use ssh_stamp::pins::GPIOConfig; -use ssh_stamp::pins::PinChannel; +use sunset_async::{SunsetMutex, SSHServer}; use ssh_stamp::{ + pins::{GPIOConfig, PinChannel}, config::SSHStampConfig, espressif::{ buffered_uart::BufferedUart, - net::{accept_requests, if_up}, + // net::accept_requests, + net::if_up, rng, }, storage::Fl, + serve, }; -use static_cell::StaticCell; -use sunset_async::SunsetMutex; + +const UART_BUFFER_SIZE: usize = 4096; +static UART_BUF: StaticCell = StaticCell::new(); + + + +pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a>{ + let peripherals = esp_hal::init(esp_hal::Config::default()); + let rng = Rng::new(peripherals.RNG); + rng::register_custom_rng(rng); + let timg0 = TimerGroup::new(peripherals.TIMG0); + let radio_clock = peripherals.RADIO_CLK; + + // Read SSH configuration from Flash (if it exists) + let mut flash_storage = Fl::new(FlashStorage::new()); + let config = ssh_stamp::storage::load_or_create(&mut flash_storage).await; + + static FLASH: StaticCell> = StaticCell::new(); + let _flash = FLASH.init(SunsetMutex::new(flash_storage)); + + static CONFIG: StaticCell> = StaticCell::new(); + let config = CONFIG.init(SunsetMutex::new(config.unwrap())); + + let wifi = peripherals.WIFI; + let gpio10 = peripherals.GPIO10; + let gpio11 = peripherals.GPIO11; + let ssh_stamp_peripherals = SshStampPeripherals { + rng: rng, + timg0: timg0, + radio_clock: radio_clock, + wifi: wifi, + config: config, + gpio10: gpio10, + gpio11: gpio11, + }; + ssh_stamp_peripherals +} + +pub async fn peripherals_disable() -> (){ + // drop peripherals +} + +pub async fn wifi_wait_for_initialisation(s: PeripheralsEnabledConsumed<'_>) -> Result, InitializationError>{ + let rng = s.rng; + let timg0 = s.timg0; + let radio_clock = s.radio_clock; + let wifi_controller: Result, InitializationError> = esp_wifi::init(timg0.timer0, rng, radio_clock); + wifi_controller +} + +pub async fn wifi_disable() -> (){ + // drop wifi controller +} + +pub struct TcpStackReturn<'a> { + pub config: SSHStampConfig, + pub tcp_stack: Stack<'a>, +} +pub async fn tcp_wait_for_initialisation<'a>(s: WifiEnabledConsumed<'a>) -> Stack<'a> { + let spawner: Spawner = s.spawner; + let wifi_controller = s.wifi_controller; + let wifi: WIFI = s.wifi; + let mut rng = s.rng; + let wifi_ssid = s.wifi_ssid; + let tcp_stack = if_up(spawner, wifi_controller, wifi, &mut rng, wifi_ssid) + .await + .unwrap(); + tcp_stack +} + +pub async fn tcp_disable() -> (){ + // drop tcp stack +} + +pub async fn ssh_wait_for_initialisation<'server>(inbuf: &'server mut [u8; UART_BUFFER_SIZE], outbuf: &'server mut [u8; UART_BUFFER_SIZE]) -> SSHServer<'server>{ + let ssh_server = SSHServer::new(inbuf, outbuf); + ssh_server +} + +pub async fn ssh_disable() -> (){ + + // drop wifi controller +} + +pub async fn uart_pins_wait_for_config<'a>(s: SshEnabledConsumed<'a>) -> PinChannel<'a> { + let serde_pin_config = { + let guard = s.config.lock().await; + guard.uart_pins.clone() + }; + let pin10 = s.gpio10.degrade(); + let pin11 = s.gpio11.degrade(); + + let available_gpios = GPIOConfig { + gpio10: Some(pin10), + gpio11: Some(pin11), + }; + let pin_channel_ref = PinChannel::new(serde_pin_config, available_gpios); + pin_channel_ref +} + +pub async fn uart_pins_disable() -> (){ + // disable uart pins +} + +pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { + UART_BUF.init_with(BufferedUart::new) +} + +pub async fn uart_buffer_disable() -> () { + // disable uart buffer +} + + +pub async fn idle_wait_for_connection<'a, 'b>(s: UartEnabledConsumed<'a>, ssh_server: &'b SSHServer<'a>) -> Result<(), sunset::Error> where 'a:'b{ + let chan_pipe = s.chan_pipe; + serve::connection_loop(ssh_server, &chan_pipe).await +} + +pub async fn idle_disable() -> () { + // disable idle +} + +use ssh_stamp::serial::serial_bridge; +pub async fn bridge_wait_for_initialisation<'a, 'b>(s: ClientConnectedConsumed<'a, 'b>) -> Result<(), sunset::Error>{ + let chan_pipe = Channel::::new(); + let bridge = { + let session_type = chan_pipe.receive().await; + + match session_type { + serve::SessionType::Bridge(ch) => { + let stdio = s.ssh_server.stdio(ch).await?; + let stdio2 = stdio.clone(); + serial_bridge(stdio, stdio2, s.uart_buff).await? + } + // SessionType::Sftp(_ch) => { + // // Handle SFTP session + // todo!() + // } + }; + Ok(()) + }; + bridge +} + +pub async fn bridge_disable() -> () { + // disable bridge +} + + +pub struct SshStampPeripherals<'a> { + pub rng: Rng, + pub timg0: TimerGroup<'a, TIMG0<'a>>, + pub radio_clock: RADIO_CLK<'a>, + pub wifi: WIFI<'a>, + pub config: &'a SunsetMutex, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, +} + +pub struct SshStampInit<'a> { + pub rng: Rng, + pub timg0: TimerGroup<'a, TIMG0<'a>>, + pub radio_clock: RADIO_CLK<'a>, + pub wifi: WIFI<'a>, + pub config: &'a SunsetMutex, + pub spawner: Spawner, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, +} #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { @@ -46,123 +225,351 @@ async fn main(spawner: Spawner) -> ! { esp_bootloader_esp_idf::esp_app_desc!(); esp_println::logger::init_logger_from_env(); - // System init - let peripherals = esp_hal::init(esp_hal::Config::default()); - let mut rng = Rng::new(peripherals.RNG); - let timg0 = TimerGroup::new(peripherals.TIMG0); - - rng::register_custom_rng(rng); + let peripherals = peripherals_wait_for_initialisation().await; cfg_if::cfg_if! { - if #[cfg(feature = "esp32")] { + if #[cfg(feature = "esp32")] { let timg1 = TimerGroup::new(peripherals.TIMG1); esp_hal_embassy::init(timg1.timer0); - } else { - use esp_hal::timer::systimer::SystemTimer; - let systimer = SystemTimer::new(peripherals.SYSTIMER); - esp_hal_embassy::init(systimer.alarm0); - } + } else { + use esp_hal::timer::systimer::SystemTimer; + let systimer = SystemTimer::new(peripherals.SYSTIMER); + esp_hal_embassy::init(systimer.alarm0); } // TODO: Migrate this function/test to embedded-test. // Quick roundtrip test for SSHStampConfig // ssh_stamp::config::roundtrip_config(); - // Read SSH configuration from Flash (if it exists) - let mut flash_storage = Fl::new(FlashStorage::new()); - let config = ssh_stamp::storage::load_or_create(&mut flash_storage).await; + let peripherals_enabled_struct = SshStampInit { + rng: peripherals.rng, + timg0: peripherals.timg0, + radio_clock: peripherals.radio_clock, + wifi: peripherals.wifi, + config: peripherals.config, + spawner: spawner, + gpio10: peripherals.gpio10, + gpio11: peripherals.gpio11, + }; - static FLASH: StaticCell> = StaticCell::new(); - let _flash = FLASH.init(SunsetMutex::new(flash_storage)); + match peripherals_enabled(peripherals_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("Peripheral error: {}", e); + } + } - static CONFIG: StaticCell> = StaticCell::new(); - let config = CONFIG.init(SunsetMutex::new(config.unwrap())); + peripherals_disable().await; + loop{} + //reset? - let wifi_controller = esp_wifi::init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); +} - // Bring up the network interface and start accepting SSH connections. - // Clone the reference to config to avoid borrow checker issues. - let tcp_stack = if_up(spawner, wifi_controller, peripherals.WIFI, &mut rng, config) - .await - .unwrap(); - // Set up software buffered UART to run in a higher priority InterruptExecutor - let uart_buf = UART_BUF.init_with(BufferedUart::new); - let software_interrupts = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); - let interrupt_executor = - INT_EXECUTOR.init_with(|| InterruptExecutor::new(software_interrupts.software_interrupt0)); - cfg_if::cfg_if! { - if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { - let interrupt_spawner = interrupt_executor.start(Priority::Priority1); - } else { - let interrupt_spawner = interrupt_executor.start(Priority::Priority10); +pub struct PeripheralsEnabledConsumed<'a> { + pub rng: Rng, + pub timg0: TimerGroup<'a, TIMG0<'a>>, + pub radio_clock: RADIO_CLK<'a>, +} +pub struct PeripheralsEnabled<'a> { + pub rng: Rng, + pub wifi: WIFI<'a>, + pub config: &'a SunsetMutex, + pub spawner: Spawner, + pub wifi_controller: EspWifiController<'a>, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, +} +async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Error> { + let peripherals_enabled_consumed = PeripheralsEnabledConsumed { + rng: s.rng, + timg0: s.timg0, + radio_clock: s.radio_clock, + }; + let wifi_controller = wifi_wait_for_initialisation(peripherals_enabled_consumed).await; + + let peripherals_enabled_struct = PeripheralsEnabled { + rng: s.rng, + wifi: s.wifi, + config: s.config, + spawner: s.spawner, + wifi_controller: wifi_controller.unwrap(), + gpio10: s.gpio10, + gpio11: s.gpio11, + }; + match wifi_enabled(peripherals_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("TCP error: {}", e); } } - let serde_pin_config = { - let guard = config.lock().await; - guard.uart_pins.clone() - }; + tcp_disable().await; + Ok(()) +} - let available_gpios = GPIOConfig { - gpio10: Some(peripherals.GPIO10.degrade()), - gpio11: Some(peripherals.GPIO11.degrade()), - }; +pub struct WifiEnabledConsumed<'a> { + pub rng: Rng, + pub wifi: WIFI<'a>, + // pub config: &'a SunsetMutex, + pub wifi_ssid: String<32>, + pub spawner: Spawner, + pub wifi_controller: EspWifiController<'a>, +} +pub struct WifiEnabled<'a> { + pub rng: Rng, + pub config: &'a SunsetMutex, + pub tcp_stack: Stack<'a>, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, +} +async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { + let wifi_ssid_config = { + let guard = s.config.lock().await; + guard.wifi_ssid.clone() + }; + let wifi_enabled_consumed = WifiEnabledConsumed { + rng: s.rng, + wifi: s.wifi, + // config: s.config, + wifi_ssid: wifi_ssid_config, + spawner: s.spawner, + wifi_controller: s.wifi_controller, + }; + let tcp_stack = tcp_wait_for_initialisation(wifi_enabled_consumed).await; + let wifi_enabled_struct = WifiEnabled { + rng: s.rng, + config: s.config, + tcp_stack: tcp_stack, + gpio10: s.gpio10, + gpio11: s.gpio11, + }; + match tcp_enabled(wifi_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("SSH error: {}", e); + } + } + ssh_disable().await; + Ok(()) - // Initialize the global pin channel and keep the &'static reference so we can - // pass it to tasks that need to mutate pins (no unsafe globals). - let pin_channel_ref = pins::init_global_channel(PinChannel::new( - serde_pin_config, - available_gpios, - )); +} - // Grab UART1, typically not connected to dev board's TTL2USB IC nor builtin JTAG functionality - let uart1 = peripherals.UART1; +pub struct TCPEnabled<'a> { + pub rng: Rng, + pub config: &'a SunsetMutex, + pub tcp_stack: Stack<'a>, + pub ssh_server: SSHServer<'a>, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, +} +async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { + // loop { + let mut inbuf = [0u8; UART_BUFFER_SIZE]; + let mut outbuf = [0u8; UART_BUFFER_SIZE]; + let ssh_server = ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; + let wifi_enabled_struct = TCPEnabled { + rng: s.rng, + config: s.config, + tcp_stack: s.tcp_stack, + ssh_server: ssh_server, + gpio10: s.gpio10, + gpio11: s.gpio11, + }; + match ssh_enabled(wifi_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("Wifi error: {}", e); + } + } - // Use the same config reference for UART task. - // Pass pin_channel_ref into the UART task so it can acquire/release pins. - interrupt_spawner - .spawn(uart_task(uart_buf, uart1, pin_channel_ref)) - .unwrap(); + wifi_disable().await; + // } + Ok(()) - // Pass pin_channel_ref into accept_requests (so SSH handlers can use it). - // NOTE: accept_requests signature must accept this arg; if it doesn't, - // thread the reference into whatever code spawns handle_ssh_client. - accept_requests(tcp_stack, uart_buf, pin_channel_ref).await; } -static UART_BUF: StaticCell = StaticCell::new(); -static INT_EXECUTOR: StaticCell> = StaticCell::new(); - -#[embassy_executor::task()] -async fn uart_task( - buffer: &'static BufferedUart, - uart_periph: UART1<'static>, - pin_channel_ref: &'static SunsetMutex, -) { - dbg!("Spawning UART task..."); - // Hardware UART setup - let uart_config = Config::default().with_rx( - RxConfig::default() - .with_fifo_full_threshold(16) - .with_timeout(1), - ); - // Use the pinned reference passed in from main. - let mut pin_chan = pin_channel_ref.lock().await; - - // Sync pin config via channels - pin_chan - .with_channel(async |rx, tx| { - let uart = Uart::new(uart_periph, uart_config) - .unwrap() - .with_rx(rx) - .with_tx(tx) - .into_async(); - - // Run the main buffered TX/RX loop - buffer.run(uart).await; - }) - .await - .unwrap(); +pub struct SshEnabledConsumed<'a> { + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, + pub config: &'a SunsetMutex, +} + +pub struct SshEnabled<'a> { + pub rng: Rng, + pub tcp_stack: Stack<'a>, + pub ssh_server: SSHServer<'a>, + pub uart_pins: PinChannel<'a>, +} + +async fn ssh_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { + // loop { + let ssh_enabled_consumed = SshEnabledConsumed { + config: s.config, + gpio10: s.gpio10, + gpio11: s.gpio11, + }; + + let uart_pins = uart_pins_wait_for_config(ssh_enabled_consumed).await; + + let ssh_enabled_struct = SshEnabled { + rng: s.rng, + tcp_stack: s.tcp_stack, + ssh_server: s.ssh_server, + uart_pins: uart_pins, + }; + match uart_configured(ssh_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("UART pin error: {}", e); + } + } + + uart_pins_disable().await; + // } + Ok(()) + +} + +pub struct UartConfigured<'a> { + pub rng: Rng, + pub tcp_stack: Stack<'a>, + pub ssh_server: SSHServer<'a>, + pub uart_pins: PinChannel<'a>, + pub uart_buff: &'a BufferedUart, +} +async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> { + // loop { + let uart_buff = uart_buffer_wait_for_initialisation().await; + let uart_configured_struct = UartConfigured { + rng: s.rng, + tcp_stack: s.tcp_stack, + ssh_server: s.ssh_server, + uart_pins: s.uart_pins, + uart_buff: uart_buff, + }; + match uart_enabled(uart_configured_struct).await { + Ok(_) => (), + Err(e) => { + println!("Uart buffer error: {}", e); + } + } + + uart_buffer_disable().await; + // } + Ok(()) +} + + + +pub struct UartEnabledConsumed<'a>{ + pub rng: Rng, + pub tcp_stack: Stack<'a>, + pub uart_buff: &'a BufferedUart, + pub chan_pipe: Channel, +} +pub struct UartEnabled<'a, 'b, CL> where CL: Future>{ + pub rng: Rng, + pub tcp_stack: Stack<'a>, + pub ssh_server: &'b SSHServer<'a>, + pub uart_pins: PinChannel<'a>, + pub uart_buff: &'a BufferedUart, + pub connection_loop: CL, +} +async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error> where 'a: 'b{ + // loop { + let chan_pipe = Channel::::new(); + + let uart_enabled_consumed = UartEnabledConsumed { + rng: s.rng, + tcp_stack: s.tcp_stack, + uart_buff: s.uart_buff, + chan_pipe: chan_pipe, + }; + let connection = idle_wait_for_connection(uart_enabled_consumed, &s.ssh_server); + + let uart_enabled_struct = UartEnabled { + rng: s.rng, + tcp_stack: s.tcp_stack, + ssh_server: &s.ssh_server, + uart_pins: s.uart_pins, + uart_buff: s.uart_buff, + connection_loop: connection, + }; + match client_connected(uart_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("Client connection error: {}", e); + } + } + + bridge_disable().await; + // } + Ok(()) +} + +pub struct ClientConnectedConsumed<'a, 'b> { + pub rng: Rng, + pub uart_pins: PinChannel<'a>, + pub uart_buff: &'a BufferedUart, + pub ssh_server: &'b SSHServer<'a>, +} +pub struct ClientConnected<'a, 'b, CL, BR> where CL: Future>, BR: Future>{ + pub ssh_server: &'b SSHServer<'a>, + pub bridge: BR, + pub connection_loop: CL, + pub tcp_socket: TcpSocket<'b>, + +} + +async fn client_connected<'a, 'b, CL>(s: UartEnabled<'a, 'b, CL> ) -> Result<(), sunset::Error> where CL: Future>, 'a:'b{ + // loop { + let mut rx_buffer = [0u8; 1536]; + let mut tx_buffer = [0u8; 1536]; + let socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); + let client_connected_consumed = ClientConnectedConsumed { + rng: s.rng, + uart_pins: s.uart_pins, + uart_buff: s.uart_buff, + ssh_server: s.ssh_server, + }; + + let bridge = bridge_wait_for_initialisation(client_connected_consumed); + let client_connected_struct = ClientConnected { + ssh_server: s.ssh_server, + bridge: bridge, + connection_loop: s.connection_loop, + tcp_socket: socket, + }; + match bridge_connected(client_connected_struct).await { + Ok(_) => (), + Err(e) => { + println!("Bridge error: {}", e); + } + } + + bridge_disable().await; + // } + + Ok(()) +} + +async fn bridge_connected<'a, 'b, CL, BR>(s:ClientConnected<'a, 'b, CL, BR>) -> Result<(), sunset::Error> where CL: Future>, BR: Future>, 'a:'b{ + let mut tcp_socket = s.tcp_socket; + let (mut rsock, mut wsock) = tcp_socket.split(); + let server = s.ssh_server.run(&mut rsock, &mut wsock); + let connection_loop = s.connection_loop; + let bridge = s.bridge; + match select3(server, connection_loop, bridge).await { + Either3::First(r) => r, + Either3::Second(r) => r, + Either3::Third(r) => r, + }?; + Result::Ok(()) +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} } diff --git a/src/pins.rs b/src/pins.rs index 73c47f1..0cd533f 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -85,21 +85,21 @@ impl<'de> SSHDecode<'de> for SerdePinConfig { } #[derive(Default)] -pub struct GPIOConfig { - pub gpio10: Option>, - pub gpio11: Option>, +pub struct GPIOConfig<'a> { + pub gpio10: Option>, + pub gpio11: Option>, } -pub struct PinChannel { +pub struct PinChannel<'a> { pub config: SerdePinConfig, - pub gpios: GPIOConfig, + pub gpios: GPIOConfig<'a>, pub tx: Channel, pub rx: Channel, // TODO: cts/rts pins } -impl PinChannel { - pub fn new(config: SerdePinConfig, gpios: GPIOConfig) -> Self { +impl <'a>PinChannel<'a> { + pub fn new(config: SerdePinConfig, gpios: GPIOConfig<'a>) -> Self { Self { config, gpios, @@ -108,7 +108,7 @@ impl PinChannel { } } - pub async fn recv_tx(&mut self) -> errors::Result> { + pub async fn recv_tx(&mut self) -> errors::Result> { // tx needs to lock here. //self.tx.receive().await; @@ -127,7 +127,7 @@ impl PinChannel { }) } - pub async fn send_tx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + pub async fn send_tx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { match self.config.tx { 10 => self.gpios.gpio10 = Some(pin), 11 => self.gpios.gpio11 = Some(pin), @@ -139,7 +139,7 @@ impl PinChannel { Ok(()) } - pub async fn recv_rx(&mut self) -> errors::Result> { + pub async fn recv_rx(&mut self) -> errors::Result> { let res = Ok(match self.config.rx { 10 => self .gpios @@ -161,7 +161,7 @@ impl PinChannel { res } - pub async fn send_rx(&mut self, pin: AnyPin<'static>) -> errors::Result<()> { + pub async fn send_rx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { match self.config.rx { 10 => self.gpios.gpio10 = Some(pin), 11 => self.gpios.gpio11 = Some(pin), @@ -175,7 +175,7 @@ impl PinChannel { pub async fn with_channel(&mut self, f: F) -> errors::Result<()> where - F: for<'a> AsyncFnOnce(AnyPin<'a>, AnyPin<'a>), + F: for<'b> AsyncFnOnce(AnyPin<'b>, AnyPin<'b>), { let mut rx = self.recv_rx().await?; let mut tx = self.recv_tx().await?; @@ -223,14 +223,14 @@ impl PinChannel { // the &'static SunsetMutex returned by init_global_channel and must retain it. static GLOBAL_PIN_CHANNEL: StaticCell> = StaticCell::new(); -pub fn init_global_channel(ch: PinChannel) -> &'static SunsetMutex { - // Initialize the StaticCell and return the &'static reference. - GLOBAL_PIN_CHANNEL.init(SunsetMutex::new(ch)) -} +// pub fn init_global_channel(ch: PinChannel) -> &SunsetMutex { +// // Initialize the StaticCell and return the &'static reference. +// GLOBAL_PIN_CHANNEL.init(SunsetMutex::new(ch)) +// } -pub struct PinConfig { - pub tx: AnyPin<'static>, - pub rx: AnyPin<'static>, +pub struct PinConfig<'a> { + pub tx: AnyPin<'a>, + pub rx: AnyPin<'a>, } pub struct PinConfigAlt { @@ -251,8 +251,8 @@ impl PinConfigAlt { } } -impl PinConfig { - pub fn new(mut gpio_config: GPIOConfig, config_inner: SerdePinConfig) -> errors::Result { +impl <'a>PinConfig<'a> { + pub fn new(mut gpio_config: GPIOConfig<'a>, config_inner: SerdePinConfig) -> errors::Result { if config_inner.rx == config_inner.tx { return Err(errors::Error::InvalidPin); } @@ -277,11 +277,11 @@ impl PinConfig { pub fn initialize_pin( peripherals: peripherals::Peripherals, pin_number: u8, - ) -> errors::Result> { + ) -> errors::Result> { match pin_number { 0 => Ok(peripherals.GPIO0.into()), _ => Err(errors::Error::InvalidPin), } } -} \ No newline at end of file +} diff --git a/src/serial.rs b/src/serial.rs index 4533719..4f63730 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -7,7 +7,7 @@ use esp_println::println; /// Forwards an incoming SSH connection to/from the local UART, until /// the connection drops -pub(crate) async fn serial_bridge( +pub async fn serial_bridge( chanr: impl Read, chanw: impl Write, uart: &BufferedUart, diff --git a/src/serve.rs b/src/serve.rs index fa72515..8e6d11c 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -21,15 +21,16 @@ use sunset_async::{ProgressHolder, SSHServer}; use esp_println::{dbg, println}; -enum SessionType { +pub enum SessionType { Bridge(ChanHandle), //Sftp(ChanHandle), } -async fn connection_loop( +pub async fn connection_loop<'a>( serv: &SSHServer<'_>, chan_pipe: &Channel, - pin_channel_ref: &'static SunsetMutex, + // pin_channel_ref: &'a SunsetMutex>, + // pin_channel_ref: &PinChannel<'a>, ) -> Result<(), sunset::Error> { let username = Mutex::::new(String::<20>::new()); let mut session: Option = None; @@ -80,64 +81,64 @@ async fn connection_loop( } } } - ServEvent::Environment(a) => { - dbg!("Got ENV request"); - dbg!(a.name()?); - dbg!(a.value()?); - - // TODO: Logic to serialise/validate env vars? I.e: - // a.name.validate(); // Checks the input variable, sanitizes, assigns a target subsystem - // - // config.change(c): Apply the config change to the relevant subsystem. - // i.e: if UART_TX_PIN or UART_RX_PIN, we update the PinChannel with with_channel() to change pins live. - match a.name()? { - "SAVE_CONFIG" => { - if a.value()? == "1" { - dbg!("Triggering config save..."); - todo!("Implement config save to flash"); - } - } - // If the env var is UART_TX_PIN or UART_RX_PIN - "UART_TX_PIN" => { - let val = a.value()?; - dbg!("Updating UART TX pin to ", val); - if let Ok(pin_num) = val.parse::() { - let mut ch = pin_channel_ref.lock().await; - if ch.set_tx_pin(pin_num).is_err() { - dbg!("Failed to update TX pin"); - } else { - dbg!("TX pin updated"); - } - } else { - dbg!("Invalid TX pin value"); - } - } - "UART_RX_PIN" => { - let val = a.value()?; - dbg!("Updating UART RX pin to ", val); - if let Ok(pin_num) = val.parse::() { - let mut ch = pin_channel_ref.lock().await; - if ch.set_rx_pin(pin_num).is_err() { - dbg!("Failed to update RX pin"); - } else { - dbg!("RX pin updated"); - } - } else { - dbg!("Invalid RX pin value"); - } - } - _ => { - dbg!("Unknown/unsupported ENV var"); - } - } - - - - // config.save(a): Potentially an optional special environment variable SAVE_CONFIG=1 - // that serialises current config to flash - - a.succeed()?; - } + // ServEvent::Environment(a) => { + // dbg!("Got ENV request"); + // dbg!(a.name()?); + // dbg!(a.value()?); + + // // TODO: Logic to serialise/validate env vars? I.e: + // // a.name.validate(); // Checks the input variable, sanitizes, assigns a target subsystem + // // + // // config.change(c): Apply the config change to the relevant subsystem. + // // i.e: if UART_TX_PIN or UART_RX_PIN, we update the PinChannel with with_channel() to change pins live. + // match a.name()? { + // "SAVE_CONFIG" => { + // if a.value()? == "1" { + // dbg!("Triggering config save..."); + // todo!("Implement config save to flash"); + // } + // } + // // If the env var is UART_TX_PIN or UART_RX_PIN + // "UART_TX_PIN" => { + // let val = a.value()?; + // dbg!("Updating UART TX pin to ", val); + // if let Ok(pin_num) = val.parse::() { + // let mut ch = pin_channel_ref.lock().await; + // if ch.set_tx_pin(pin_num).is_err() { + // dbg!("Failed to update TX pin"); + // } else { + // dbg!("TX pin updated"); + // } + // } else { + // dbg!("Invalid TX pin value"); + // } + // } + // "UART_RX_PIN" => { + // let val = a.value()?; + // dbg!("Updating UART RX pin to ", val); + // if let Ok(pin_num) = val.parse::() { + // let mut ch = pin_channel_ref.lock().await; + // if ch.set_rx_pin(pin_num).is_err() { + // dbg!("Failed to update RX pin"); + // } else { + // dbg!("RX pin updated"); + // } + // } else { + // dbg!("Invalid RX pin value"); + // } + // } + // _ => { + // dbg!("Unknown/unsupported ENV var"); + // } + // } + + + + // // // config.save(a): Potentially an optional special environment variable SAVE_CONFIG=1 + // // // that serialises current config to flash + + // a.succeed()?; + // } ServEvent::SessionPty(a) => { a.succeed()?; } @@ -156,7 +157,7 @@ async fn connection_loop( pub(crate) async fn handle_ssh_client( stream: &mut TcpSocket<'_>, uart: &BufferedUart, - pin_channel_ref: &'static SunsetMutex, + _pin_channel_ref: &PinChannel<'_>, ) -> Result<(), sunset::Error> { // Spawn network tasks to handle incoming connections with demo_common::session() let mut inbuf = [0u8; 4096]; @@ -168,7 +169,8 @@ pub(crate) async fn handle_ssh_client( let chan_pipe = Channel::::new(); println!("Calling connection_loop from handle_ssh_client"); - let conn_loop = connection_loop(&ssh_server, &chan_pipe, pin_channel_ref); + // let conn_loop = connection_loop(&ssh_server, &chan_pipe, pin_channel_ref); + let conn_loop = connection_loop(&ssh_server, &chan_pipe); println!("Running server from handle_ssh_client()"); let server = ssh_server.run(&mut rsock, &mut wsock); From 0b41d1a94478fb75c52f0d3485d702ea0c273fe7 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:15:55 +1100 Subject: [PATCH 51/86] Cleanup various warnings. --- src/espressif/net.rs | 16 ++++++++-------- src/main.rs | 13 ++++++++++--- src/pins.rs | 6 +++--- src/serve.rs | 2 +- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/espressif/net.rs b/src/espressif/net.rs index dfd5f20..dbe68f8 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -3,7 +3,7 @@ use core::str::FromStr; use embassy_executor::Spawner; use embassy_net::{tcp::TcpSocket, Stack, StackResources}; -use embassy_net::{IpListenEndpoint, Ipv4Cidr, Runner, StaticConfigV4}; +use embassy_net::{IpListenEndpoint, Ipv4Cidr, StaticConfigV4}; use embassy_time::{Duration, Timer}; use esp_hal::peripherals::WIFI; @@ -11,10 +11,10 @@ use esp_hal::peripherals::WIFI; use esp_hal::rng::Rng; use esp_println::{dbg, println}; -use esp_wifi::wifi::{AccessPointConfiguration, Configuration, WifiController, WifiDevice}; +use esp_wifi::wifi::{AccessPointConfiguration, Configuration, WifiController}; use esp_wifi::wifi::{WifiEvent, WifiState}; use esp_wifi::EspWifiController; -use sunset_async::SunsetMutex; +// use sunset_async::SunsetMutex; use core::net::SocketAddrV4; use edge_dhcp; @@ -26,7 +26,7 @@ use edge_dhcp::{ use edge_nal::UdpBind; use edge_nal_embassy::{Udp, UdpBuffers}; -use crate::config::SSHStampConfig; +// use crate::config::SSHStampConfig; use super::buffered_uart::BufferedUart; @@ -43,7 +43,7 @@ macro_rules! mk_static { use heapless::String; pub async fn if_up<'a>( - spawner: Spawner, + _spawner: Spawner, wifi_controller: EspWifiController<'a>, wifi: WIFI<'a>, rng: &mut Rng, @@ -74,7 +74,7 @@ pub async fn if_up<'a>( let seed = (rng.random() as u64) << 32 | rng.random() as u64; // Init network stack - let (ap_stack, runner) = embassy_net::new( + let (ap_stack, _runner) = embassy_net::new( interfaces.ap, net_config, mk_static!(StackResources<3>, StackResources::<3>::new()), @@ -84,8 +84,8 @@ pub async fn if_up<'a>( // spawner.spawn(wifi_up(controller, wifi_ssid)).ok(); // spawner.spawn(net_up(runner)).ok(); // spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); - wifi_up(controller, wifi_ssid); - dhcp_server(ap_stack, gw_ip_addr_ipv4); + wifi_up(controller, wifi_ssid).await; + dhcp_server(ap_stack, gw_ip_addr_ipv4).await; loop { println!("Checking if link is up...\n"); diff --git a/src/main.rs b/src/main.rs index 444b1ca..9fc3371 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use esp_hal::{ // interrupt::{software::SoftwareInterruptControl, Priority}, // peripherals::Peripherals, // peripherals::UART1, - peripherals::{RADIO_CLK, WIFI, TIMG0}, + peripherals::{RADIO_CLK, WIFI, TIMG0, TIMG1, SYSTIMER}, peripherals::{GPIO10,GPIO11}, rng::Rng, timer::timg::TimerGroup, @@ -52,6 +52,8 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a let rng = Rng::new(peripherals.RNG); rng::register_custom_rng(rng); let timg0 = TimerGroup::new(peripherals.TIMG0); + let timg1 = peripherals.TIMG1; + let systimer = peripherals.SYSTIMER; let radio_clock = peripherals.RADIO_CLK; // Read SSH configuration from Flash (if it exists) @@ -75,6 +77,8 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a config: config, gpio10: gpio10, gpio11: gpio11, + timg1: timg1, + systimer: systimer, }; ssh_stamp_peripherals } @@ -198,6 +202,8 @@ pub struct SshStampPeripherals<'a> { pub config: &'a SunsetMutex, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, + pub timg1: TIMG1<'a>, + pub systimer: SYSTIMER<'a>, } pub struct SshStampInit<'a> { @@ -229,12 +235,13 @@ async fn main(spawner: Spawner) -> ! { cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { - let timg1 = TimerGroup::new(peripherals.TIMG1); + let timg1 = TimerGroup::new(peripherals.timg1); esp_hal_embassy::init(timg1.timer0); } else { use esp_hal::timer::systimer::SystemTimer; - let systimer = SystemTimer::new(peripherals.SYSTIMER); + let systimer = SystemTimer::new(peripherals.systimer); esp_hal_embassy::init(systimer.alarm0); + } } // TODO: Migrate this function/test to embedded-test. diff --git a/src/pins.rs b/src/pins.rs index 0cd533f..4de1559 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -2,8 +2,8 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channe use esp_hal::gpio::AnyPin; use esp_hal::peripherals; use sunset::sshwire::{ SSHDecode, SSHEncode, SSHSink, SSHSource, WireResult }; -use static_cell::StaticCell; -use sunset_async::SunsetMutex; +// use static_cell::StaticCell; +// use sunset_async::SunsetMutex; use crate::{config::{dec_option, enc_option}, errors}; @@ -221,7 +221,7 @@ impl <'a>PinChannel<'a> { // Global PinChannel holder: initialize from main() and access from other modules. // We keep a StaticCell but avoid any unsafe global pointer; callers receive // the &'static SunsetMutex returned by init_global_channel and must retain it. -static GLOBAL_PIN_CHANNEL: StaticCell> = StaticCell::new(); +// static GLOBAL_PIN_CHANNEL: StaticCell> = StaticCell::new(); // pub fn init_global_channel(ch: PinChannel) -> &SunsetMutex { // // Initialize the StaticCell and return the &'static reference. diff --git a/src/serve.rs b/src/serve.rs index 8e6d11c..f332672 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -13,7 +13,7 @@ use embassy_net::tcp::TcpSocket; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; -use sunset_async::SunsetMutex; +// use sunset_async::SunsetMutex; use heapless::String; use sunset::{error, ChanHandle, ServEvent, SignKey}; From f1e7aa44d718a207c94ade76480e9895fabe8b8c Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 29 Jan 2026 01:14:45 +1100 Subject: [PATCH 52/86] Restore SSH pin config by passing pin_channel rather than pin_channel_ref to connection_loop as it is not required for future HSM states. --- Cargo.lock | 12 ++- Cargo.toml | 9 +-- src/espressif/net.rs | 18 ++--- src/main.rs | 16 ++-- src/serve.rs | 171 +++++++++++++++---------------------------- 5 files changed, 88 insertions(+), 138 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58e1342..b09d8d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1793,7 +1793,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sunset" -version = "0.3.0" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fae1be9253047b033ccbedf6da75657b1c0f83e99e3c7953cbccf064e534bb10" dependencies = [ "aes", "ascii", @@ -1823,7 +1825,9 @@ dependencies = [ [[package]] name = "sunset-async" -version = "0.3.0" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38504564d099a37a2bf3ef6a7c5ae4f268fadf2e30819c59fc736bf4a6c7382" dependencies = [ "embassy-futures", "embassy-sync 0.7.0", @@ -1835,7 +1839,9 @@ dependencies = [ [[package]] name = "sunset-sshwire-derive" -version = "0.2.0" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe2cd7ddcc98aed78da9ed97bcd2630b761f1ea4377d9922dc4097f76a8bdff" dependencies = [ "virtue", ] diff --git a/Cargo.toml b/Cargo.toml index 80125fa..469f4b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,12 +27,9 @@ hex = { version = "0.4", default-features = false } log = { version = "0.4" } static_cell = { version = "2", features = ["nightly"] } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } -# sunset = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false, features = ["openssh-key", "embedded-io"]} -# sunset-async = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false, features = ["multi-thread"]} -# sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", rev = "5ff44bb", default-features = false } -sunset = { path = "../sunset", default-features = false, features = ["openssh-key", "embedded-io"]} -sunset-async = { path = "../sunset/async", default-features = false, features = ["multi-thread"]} -sunset-sshwire-derive = { path = "../sunset/sshwire-derive", default-features = false } +sunset = { version = "0.4.0" , default-features = false, features = ["openssh-key", "embedded-io"]} +sunset-async = { version = "0.4.0", default-features = false, features = ["multi-thread"]} +sunset-sshwire-derive = { version = "0.2.1", default-features = false } getrandom = { version = "0.2.10", features = ["custom"] } embassy-sync = "0.7" heapless = "0.8" diff --git a/src/espressif/net.rs b/src/espressif/net.rs index dbe68f8..2a6b85a 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -106,9 +106,9 @@ pub async fn if_up<'a>( pub async fn accept_requests<'a>( stack: Stack<'a>, - uart: &BufferedUart, + _uart: &BufferedUart, // pin_channel_ref: &'a sunset_async::SunsetMutex>, - pin_channel_ref: &crate::pins::PinChannel<'a>, + _pin_channel_ref: &crate::pins::PinChannel<'a>, ) -> ! { // let rx_buffer = mk_static!([u8; 1536], [0; 1536]); // let tx_buffer = mk_static!([u8; 1536], [0; 1536]); @@ -132,12 +132,12 @@ pub async fn accept_requests<'a>( } println!("Connected, port 22"); - match crate::serve::handle_ssh_client(&mut socket, uart, pin_channel_ref).await { - Ok(_) => (), - Err(e) => { - println!("SSH client fatal error: {}", e); - } - }; + // match crate::serve::handle_ssh_client(&mut socket, uart, pin_channel_ref).await { + // Ok(_) => (), + // Err(e) => { + // println!("SSH client fatal error: {}", e); + // } + // }; } } @@ -170,7 +170,7 @@ async fn wifi_up( }); controller.set_configuration(&client_config).unwrap(); println!("Starting wifi"); - // controller.start_async().await.unwrap(); + controller.start_async().await.unwrap(); println!("Wifi started!"); } Timer::after(Duration::from_millis(10)).await; diff --git a/src/main.rs b/src/main.rs index 9fc3371..623b2b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -157,10 +157,9 @@ pub async fn uart_buffer_disable() -> () { // disable uart buffer } - -pub async fn idle_wait_for_connection<'a, 'b>(s: UartEnabledConsumed<'a>, ssh_server: &'b SSHServer<'a>) -> Result<(), sunset::Error> where 'a:'b{ +pub async fn idle_wait_for_connection<'a, 'b>(s: UartEnabledConsumed<'a>, ssh_server: &'b SSHServer<'a>, pin_channel: PinChannel<'a>) -> Result<(), sunset::Error> where 'a:'b{ let chan_pipe = s.chan_pipe; - serve::connection_loop(ssh_server, &chan_pipe).await + serve::connection_loop(ssh_server, &chan_pipe, pin_channel).await } pub async fn idle_disable() -> () { @@ -168,6 +167,7 @@ pub async fn idle_disable() -> () { } use ssh_stamp::serial::serial_bridge; +use sunset_async::ChanInOut; pub async fn bridge_wait_for_initialisation<'a, 'b>(s: ClientConnectedConsumed<'a, 'b>) -> Result<(), sunset::Error>{ let chan_pipe = Channel::::new(); let bridge = { @@ -175,7 +175,7 @@ pub async fn bridge_wait_for_initialisation<'a, 'b>(s: ClientConnectedConsumed<' match session_type { serve::SessionType::Bridge(ch) => { - let stdio = s.ssh_server.stdio(ch).await?; + let stdio: ChanInOut<'_> = s.ssh_server.stdio(ch).await?; let stdio2 = stdio.clone(); serial_bridge(stdio, stdio2, s.uart_buff).await? } @@ -480,11 +480,10 @@ pub struct UartEnabled<'a, 'b, CL> where CL: Future, pub ssh_server: &'b SSHServer<'a>, - pub uart_pins: PinChannel<'a>, pub uart_buff: &'a BufferedUart, pub connection_loop: CL, } -async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error> where 'a: 'b{ +async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error> where 'b: 'a{ // loop { let chan_pipe = Channel::::new(); @@ -494,13 +493,12 @@ async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error uart_buff: s.uart_buff, chan_pipe: chan_pipe, }; - let connection = idle_wait_for_connection(uart_enabled_consumed, &s.ssh_server); + let connection = idle_wait_for_connection(uart_enabled_consumed, &s.ssh_server, s.uart_pins); let uart_enabled_struct = UartEnabled { rng: s.rng, tcp_stack: s.tcp_stack, ssh_server: &s.ssh_server, - uart_pins: s.uart_pins, uart_buff: s.uart_buff, connection_loop: connection, }; @@ -518,7 +516,6 @@ async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error pub struct ClientConnectedConsumed<'a, 'b> { pub rng: Rng, - pub uart_pins: PinChannel<'a>, pub uart_buff: &'a BufferedUart, pub ssh_server: &'b SSHServer<'a>, } @@ -537,7 +534,6 @@ async fn client_connected<'a, 'b, CL>(s: UartEnabled<'a, 'b, CL> ) -> Result<() let socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); let client_connected_consumed = ClientConnectedConsumed { rng: s.rng, - uart_pins: s.uart_pins, uart_buff: s.uart_buff, ssh_server: s.ssh_server, }; diff --git a/src/serve.rs b/src/serve.rs index f332672..961edaf 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -3,13 +3,9 @@ use core::result::Result; use core::writeln; use crate::pins::PinChannel; -use crate::espressif::buffered_uart::BufferedUart; use crate::keys; -use crate::serial::serial_bridge; // Embassy -use embassy_futures::select::{select3, Either3}; -use embassy_net::tcp::TcpSocket; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; @@ -20,6 +16,7 @@ use sunset::{error, ChanHandle, ServEvent, SignKey}; use sunset_async::{ProgressHolder, SSHServer}; use esp_println::{dbg, println}; +use esp_hal::system::software_reset; pub enum SessionType { Bridge(ChanHandle), @@ -31,6 +28,7 @@ pub async fn connection_loop<'a>( chan_pipe: &Channel, // pin_channel_ref: &'a SunsetMutex>, // pin_channel_ref: &PinChannel<'a>, + pin_channel: PinChannel<'a>, ) -> Result<(), sunset::Error> { let username = Mutex::::new(String::<20>::new()); let mut session: Option = None; @@ -81,64 +79,67 @@ pub async fn connection_loop<'a>( } } } - // ServEvent::Environment(a) => { - // dbg!("Got ENV request"); - // dbg!(a.name()?); - // dbg!(a.value()?); - - // // TODO: Logic to serialise/validate env vars? I.e: - // // a.name.validate(); // Checks the input variable, sanitizes, assigns a target subsystem - // // - // // config.change(c): Apply the config change to the relevant subsystem. - // // i.e: if UART_TX_PIN or UART_RX_PIN, we update the PinChannel with with_channel() to change pins live. - // match a.name()? { - // "SAVE_CONFIG" => { - // if a.value()? == "1" { - // dbg!("Triggering config save..."); - // todo!("Implement config save to flash"); - // } - // } - // // If the env var is UART_TX_PIN or UART_RX_PIN - // "UART_TX_PIN" => { - // let val = a.value()?; - // dbg!("Updating UART TX pin to ", val); - // if let Ok(pin_num) = val.parse::() { - // let mut ch = pin_channel_ref.lock().await; - // if ch.set_tx_pin(pin_num).is_err() { - // dbg!("Failed to update TX pin"); - // } else { - // dbg!("TX pin updated"); - // } - // } else { - // dbg!("Invalid TX pin value"); - // } - // } - // "UART_RX_PIN" => { - // let val = a.value()?; - // dbg!("Updating UART RX pin to ", val); - // if let Ok(pin_num) = val.parse::() { - // let mut ch = pin_channel_ref.lock().await; - // if ch.set_rx_pin(pin_num).is_err() { - // dbg!("Failed to update RX pin"); - // } else { - // dbg!("RX pin updated"); - // } - // } else { - // dbg!("Invalid RX pin value"); - // } - // } - // _ => { - // dbg!("Unknown/unsupported ENV var"); - // } - // } - - + ServEvent::SessionEnv(a) => { + dbg!("Got ENV request"); + dbg!(a.name()?); + dbg!(a.value()?); + + // TODO: Logic to serialise/validate env vars? I.e: + // a.name.validate(); // Checks the input variable, sanitizes, assigns a target subsystem + // + // config.change(c): Apply the config change to the relevant subsystem. + // i.e: if UART_TX_PIN or UART_RX_PIN, we update the PinChannel with with_channel() to change pins live. + match a.name()? { + "SAVE_CONFIG" => { + if a.value()? == "1" { + dbg!("Triggering config save..."); + todo!("Implement config save to flash"); + } + } + // If the env var is UART_TX_PIN or UART_RX_PIN + "UART_TX_PIN" => { + let val = a.value()?; + dbg!("Updating UART TX pin to ", val); + if let Ok(pin_num) = val.parse::() { + // let mut ch = pin_channel_ref.lock().await; + // let mut ch: &mut PinChannel<'_> = pin_channel_ref; + let mut ch = pin_channel; + if ch.set_tx_pin(pin_num).is_err() { + dbg!("Failed to update TX pin"); + } else { + dbg!("TX pin updated"); + } + software_reset(); + } else { + dbg!("Invalid TX pin value"); + } + } + "UART_RX_PIN" => { + let val = a.value()?; + dbg!("Updating UART RX pin to ", val); + if let Ok(pin_num) = val.parse::() { + // let mut ch = pin_channel_ref.lock().await; + let mut ch = pin_channel; + if ch.set_rx_pin(pin_num).is_err() { + dbg!("Failed to update RX pin"); + } else { + dbg!("RX pin updated"); + } + software_reset(); + } else { + dbg!("Invalid RX pin value"); + } + } + _ => { + dbg!("Unknown/unsupported ENV var"); + } + } - // // // config.save(a): Potentially an optional special environment variable SAVE_CONFIG=1 - // // // that serialises current config to flash + // config.save(a): Potentially an optional special environment variable SAVE_CONFIG=1 + // that serialises current config to flash - // a.succeed()?; - // } + a.succeed()?; + } ServEvent::SessionPty(a) => { a.succeed()?; } @@ -153,53 +154,3 @@ pub async fn connection_loop<'a>( }; } } - -pub(crate) async fn handle_ssh_client( - stream: &mut TcpSocket<'_>, - uart: &BufferedUart, - _pin_channel_ref: &PinChannel<'_>, -) -> Result<(), sunset::Error> { - // Spawn network tasks to handle incoming connections with demo_common::session() - let mut inbuf = [0u8; 4096]; - let mut outbuf = [0u8; 4096]; - - let ssh_server = SSHServer::new(&mut inbuf, &mut outbuf); - let (mut rsock, mut wsock) = stream.split(); - - let chan_pipe = Channel::::new(); - - println!("Calling connection_loop from handle_ssh_client"); - // let conn_loop = connection_loop(&ssh_server, &chan_pipe, pin_channel_ref); - let conn_loop = connection_loop(&ssh_server, &chan_pipe); - println!("Running server from handle_ssh_client()"); - let server = ssh_server.run(&mut rsock, &mut wsock); - - println!("Setting up serial bridge"); - - // TODO: Maybe loop forever here and/or handle disconnection/terminations gracefully? - let bridge = async { - let session_type = chan_pipe.receive().await; - - match session_type { - SessionType::Bridge(ch) => { - let stdio = ssh_server.stdio(ch).await?; - let stdio2 = stdio.clone(); - serial_bridge(stdio, stdio2, uart).await? - } - // SessionType::Sftp(_ch) => { - // // Handle SFTP session - // todo!() - // } - }; - Ok(()) - }; - - println!("Main select() in handle_ssh_client()"); - match select3(conn_loop, server, bridge).await { - Either3::First(r) => r, - Either3::Second(r) => r, - Either3::Third(r) => r, - }?; - - Ok(()) -} From a693dd8623673af7c0778cb1ac3946f51ddbb15f Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 29 Jan 2026 01:21:39 +1100 Subject: [PATCH 53/86] Implement additional 'Tcp socket enabled' state, making tcp_socket be passed through HSM rather than tcp_stack. --- src/main.rs | 141 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 95 insertions(+), 46 deletions(-) diff --git a/src/main.rs b/src/main.rs index 623b2b4..18e5e04 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use core::result::Result::Err; use core::future::Future; use embassy_futures::select::{select3, Either3}; use embassy_executor::Spawner; -use embassy_net::{ Stack, tcp::TcpSocket}; +use embassy_net::{ Stack, tcp::TcpSocket, IpListenEndpoint}; use embassy_sync::{ blocking_mutex::raw::NoopRawMutex, channel::Channel @@ -119,6 +119,11 @@ pub async fn tcp_disable() -> (){ // drop tcp stack } +pub async fn socket_disable() -> (){ + // drop socket +} + + pub async fn ssh_wait_for_initialisation<'server>(inbuf: &'server mut [u8; UART_BUFFER_SIZE], outbuf: &'server mut [u8; UART_BUFFER_SIZE]) -> SSHServer<'server>{ let ssh_server = SSHServer::new(inbuf, outbuf); ssh_server @@ -311,8 +316,8 @@ async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Erro } } - tcp_disable().await; Ok(()) + wifi_disable().await; } pub struct WifiEnabledConsumed<'a> { @@ -331,54 +336,96 @@ pub struct WifiEnabled<'a> { pub gpio11: GPIO11<'a>, } async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { - let wifi_ssid_config = { - let guard = s.config.lock().await; - guard.wifi_ssid.clone() + let wifi_ssid_config = { + let guard = s.config.lock().await; + guard.wifi_ssid.clone() + }; + let wifi_enabled_consumed = WifiEnabledConsumed { + rng: s.rng, + wifi: s.wifi, + // config: s.config, + wifi_ssid: wifi_ssid_config, + spawner: s.spawner, + wifi_controller: s.wifi_controller, }; - let wifi_enabled_consumed = WifiEnabledConsumed { - rng: s.rng, - wifi: s.wifi, - // config: s.config, - wifi_ssid: wifi_ssid_config, - spawner: s.spawner, - wifi_controller: s.wifi_controller, - }; - let tcp_stack = tcp_wait_for_initialisation(wifi_enabled_consumed).await; - let wifi_enabled_struct = WifiEnabled { - rng: s.rng, - config: s.config, - tcp_stack: tcp_stack, - gpio10: s.gpio10, - gpio11: s.gpio11, - }; - match tcp_enabled(wifi_enabled_struct).await { - Ok(_) => (), - Err(e) => { - println!("SSH error: {}", e); - } + let tcp_stack = tcp_wait_for_initialisation(wifi_enabled_consumed).await; + let wifi_enabled_struct = WifiEnabled { + rng: s.rng, + config: s.config, + tcp_stack: tcp_stack, + gpio10: s.gpio10, + gpio11: s.gpio11, + }; + match tcp_enabled(wifi_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("SSH error: {}", e); } - ssh_disable().await; Ok(()) - + } + tcp_disable().await; } pub struct TCPEnabled<'a> { pub rng: Rng, pub config: &'a SunsetMutex, - pub tcp_stack: Stack<'a>, - pub ssh_server: SSHServer<'a>, + pub tcp_socket: TcpSocket<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, } async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { + + let mut rx_buffer = [0u8; 1536]; + let mut tx_buffer = [0u8; 1536]; + let mut tcp_socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); + + println!("Waiting for SSH client..."); + if let Err(e) = tcp_socket + .accept(IpListenEndpoint { + addr: None, + port: 22, + }) + .await + { + println!("connect error: {:?}", e); + socket_disable().await; + } + println!("Connected, port 22"); + let tcp_enabled_struct = TCPEnabled { + rng: s.rng, + config: s.config, + tcp_socket: tcp_socket, + gpio10: s.gpio10, + gpio11: s.gpio11, + }; + match socket_enabled(tcp_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("Wifi error: {}", e); + } + } + socket_disable().await; + Ok(()) // todo!() return relevant value + } + +pub struct SocketEnabled<'a> { + pub rng: Rng, + pub config: &'a SunsetMutex, + pub tcp_socket: TcpSocket<'a>, + pub ssh_server: SSHServer<'a>, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, +} +async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { // loop { + // Spawn network tasks to handle incoming connections with demo_common::session() let mut inbuf = [0u8; UART_BUFFER_SIZE]; let mut outbuf = [0u8; UART_BUFFER_SIZE]; let ssh_server = ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; - let wifi_enabled_struct = TCPEnabled { + let wifi_enabled_struct = SocketEnabled { rng: s.rng, config: s.config, - tcp_stack: s.tcp_stack, + tcp_socket: s.tcp_socket, ssh_server: ssh_server, gpio10: s.gpio10, gpio11: s.gpio11, @@ -390,7 +437,7 @@ async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { } } - wifi_disable().await; + ssh_disable().await; // } Ok(()) @@ -405,12 +452,12 @@ pub struct SshEnabledConsumed<'a> { pub struct SshEnabled<'a> { pub rng: Rng, - pub tcp_stack: Stack<'a>, + pub tcp_socket: TcpSocket<'a>, pub ssh_server: SSHServer<'a>, pub uart_pins: PinChannel<'a>, } -async fn ssh_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { +async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> { // loop { let ssh_enabled_consumed = SshEnabledConsumed { config: s.config, @@ -422,7 +469,7 @@ async fn ssh_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { let ssh_enabled_struct = SshEnabled { rng: s.rng, - tcp_stack: s.tcp_stack, + tcp_socket: s.tcp_socket, ssh_server: s.ssh_server, uart_pins: uart_pins, }; @@ -441,17 +488,17 @@ async fn ssh_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { pub struct UartConfigured<'a> { pub rng: Rng, - pub tcp_stack: Stack<'a>, + pub tcp_socket: TcpSocket<'a>, pub ssh_server: SSHServer<'a>, pub uart_pins: PinChannel<'a>, pub uart_buff: &'a BufferedUart, } -async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> { +async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> {// where 'b: 'a { // loop { let uart_buff = uart_buffer_wait_for_initialisation().await; let uart_configured_struct = UartConfigured { rng: s.rng, - tcp_stack: s.tcp_stack, + tcp_socket: s.tcp_socket, ssh_server: s.ssh_server, uart_pins: s.uart_pins, uart_buff: uart_buff, @@ -472,13 +519,12 @@ async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> { pub struct UartEnabledConsumed<'a>{ pub rng: Rng, - pub tcp_stack: Stack<'a>, pub uart_buff: &'a BufferedUart, pub chan_pipe: Channel, } pub struct UartEnabled<'a, 'b, CL> where CL: Future>{ pub rng: Rng, - pub tcp_stack: Stack<'a>, + pub tcp_socket: TcpSocket<'a>, pub ssh_server: &'b SSHServer<'a>, pub uart_buff: &'a BufferedUart, pub connection_loop: CL, @@ -489,15 +535,16 @@ async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error let uart_enabled_consumed = UartEnabledConsumed { rng: s.rng, - tcp_stack: s.tcp_stack, uart_buff: s.uart_buff, chan_pipe: chan_pipe, }; + + println!("Calling connection_loop from uart_enabled"); let connection = idle_wait_for_connection(uart_enabled_consumed, &s.ssh_server, s.uart_pins); let uart_enabled_struct = UartEnabled { rng: s.rng, - tcp_stack: s.tcp_stack, + tcp_socket: s.tcp_socket, ssh_server: &s.ssh_server, uart_buff: s.uart_buff, connection_loop: connection, @@ -509,9 +556,9 @@ async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error } } - bridge_disable().await; + idle_disable().await; // } - Ok(()) + Ok(()) // todo!() return relevant value } pub struct ClientConnectedConsumed<'a, 'b> { @@ -523,7 +570,7 @@ pub struct ClientConnected<'a, 'b, CL, BR> where CL: Future, pub bridge: BR, pub connection_loop: CL, - pub tcp_socket: TcpSocket<'b>, + pub tcp_socket: TcpSocket<'a>, } @@ -561,9 +608,11 @@ async fn client_connected<'a, 'b, CL>(s: UartEnabled<'a, 'b, CL> ) -> Result<() async fn bridge_connected<'a, 'b, CL, BR>(s:ClientConnected<'a, 'b, CL, BR>) -> Result<(), sunset::Error> where CL: Future>, BR: Future>, 'a:'b{ let mut tcp_socket = s.tcp_socket; let (mut rsock, mut wsock) = tcp_socket.split(); + println!("Running server from handle_ssh_client()"); let server = s.ssh_server.run(&mut rsock, &mut wsock); let connection_loop = s.connection_loop; let bridge = s.bridge; + println!("Main select() in bridge_connected()"); match select3(server, connection_loop, bridge).await { Either3::First(r) => r, Either3::Second(r) => r, From 8b86e3db320e6dfe41c27828269932d3752b2f8a Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 29 Jan 2026 01:26:55 +1100 Subject: [PATCH 54/86] Added temporary software reset to all HSM cleanup functions. Future implementation to incorporate relevant cleanup and return values. --- src/main.rs | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index 18e5e04..ac755d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,6 +41,7 @@ use ssh_stamp::{ storage::Fl, serve, }; +use esp_hal::system::software_reset; const UART_BUFFER_SIZE: usize = 4096; static UART_BUF: StaticCell = StaticCell::new(); @@ -85,6 +86,7 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a pub async fn peripherals_disable() -> (){ // drop peripherals + software_reset(); } pub async fn wifi_wait_for_initialisation(s: PeripheralsEnabledConsumed<'_>) -> Result, InitializationError>{ @@ -96,7 +98,12 @@ pub async fn wifi_wait_for_initialisation(s: PeripheralsEnabledConsumed<'_>) -> } pub async fn wifi_disable() -> (){ + // TODO: Correctly disable wifi controller +// pub async fn wifi_disable(wifi_controller: EspWifiController<'_>) -> (){ // drop wifi controller + // esp_wifi::deinit_unchecked() + // wifi_controller.deinit_unchecked() + software_reset(); } pub struct TcpStackReturn<'a> { @@ -117,10 +124,12 @@ pub async fn tcp_wait_for_initialisation<'a>(s: WifiEnabledConsumed<'a>) -> Sta pub async fn tcp_disable() -> (){ // drop tcp stack + software_reset(); } pub async fn socket_disable() -> (){ // drop socket + software_reset(); } @@ -130,8 +139,8 @@ pub async fn ssh_wait_for_initialisation<'server>(inbuf: &'server mut [u8; UART_ } pub async fn ssh_disable() -> (){ - // drop wifi controller + software_reset(); } pub async fn uart_pins_wait_for_config<'a>(s: SshEnabledConsumed<'a>) -> PinChannel<'a> { @@ -152,6 +161,7 @@ pub async fn uart_pins_wait_for_config<'a>(s: SshEnabledConsumed<'a>) -> PinCha pub async fn uart_pins_disable() -> (){ // disable uart pins + software_reset(); } pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { @@ -160,6 +170,7 @@ pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { pub async fn uart_buffer_disable() -> () { // disable uart buffer + software_reset(); } pub async fn idle_wait_for_connection<'a, 'b>(s: UartEnabledConsumed<'a>, ssh_server: &'b SSHServer<'a>, pin_channel: PinChannel<'a>) -> Result<(), sunset::Error> where 'a:'b{ @@ -169,6 +180,7 @@ pub async fn idle_wait_for_connection<'a, 'b>(s: UartEnabledConsumed<'a>, ssh_s pub async fn idle_disable() -> () { // disable idle + software_reset(); } use ssh_stamp::serial::serial_bridge; @@ -196,6 +208,7 @@ pub async fn bridge_wait_for_initialisation<'a, 'b>(s: ClientConnectedConsumed<' pub async fn bridge_disable() -> () { // disable bridge + software_reset(); } @@ -272,9 +285,8 @@ async fn main(spawner: Spawner) -> ! { } peripherals_disable().await; - loop{} - //reset? - + // loop{} + software_reset(); } @@ -316,8 +328,8 @@ async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Erro } } - Ok(()) wifi_disable().await; + Ok(()) // todo!() return relevant value } pub struct WifiEnabledConsumed<'a> { @@ -361,9 +373,9 @@ async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error Err(e) => { println!("SSH error: {}", e); } - Ok(()) } tcp_disable().await; + Ok(()) // todo!() return relevant value } pub struct TCPEnabled<'a> { @@ -439,8 +451,7 @@ async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { ssh_disable().await; // } - Ok(()) - + Ok(()) // todo!() return relevant value } @@ -482,8 +493,7 @@ async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> { uart_pins_disable().await; // } - Ok(()) - + Ok(()) // todo!() return relevant value } pub struct UartConfigured<'a> { @@ -512,7 +522,7 @@ async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> {// uart_buffer_disable().await; // } - Ok(()) + Ok(()) // todo!() return relevant value } @@ -602,7 +612,7 @@ async fn client_connected<'a, 'b, CL>(s: UartEnabled<'a, 'b, CL> ) -> Result<() bridge_disable().await; // } - Ok(()) + Ok(()) // todo!() return relevant value } async fn bridge_connected<'a, 'b, CL, BR>(s:ClientConnected<'a, 'b, CL, BR>) -> Result<(), sunset::Error> where CL: Future>, BR: Future>, 'a:'b{ From d9fd1cbc0ba6bdc36f1f29c9c9543a0abc0f4cc4 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 29 Jan 2026 01:31:24 +1100 Subject: [PATCH 55/86] Rust format cleanup --- src/espressif/net.rs | 8 +- src/lib.rs | 2 +- src/main.rs | 357 +++++++++++++++++++++++-------------------- src/pins.rs | 17 ++- src/serve.rs | 8 +- 5 files changed, 210 insertions(+), 182 deletions(-) diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 2a6b85a..04aaf16 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -48,7 +48,7 @@ pub async fn if_up<'a>( wifi: WIFI<'a>, rng: &mut Rng, // config: &'static SunsetMutex, - wifi_ssid: String<32>, + wifi_ssid: String<32>, ) -> Result, sunset::Error> { // let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); // let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); @@ -146,7 +146,7 @@ async fn wifi_up( // mut controller: WifiController<'static>, mut controller: WifiController<'_>, // config: &'static SunsetMutex, - wifi_ssid: String<32>, + wifi_ssid: String<32>, ) { println!("Device capabilities: {:?}", controller.capabilities()); // let wifi_ssid = { @@ -179,8 +179,8 @@ async fn wifi_up( // #[embassy_executor::task] // async fn net_up<'a>(mut runner: Runner<'a, WifiDevice<'a>>) { - // println!("Bringing up network stack...\n"); - // runner.run().await +// println!("Bringing up network stack...\n"); +// runner.run().await // } // #[embassy_executor::task] diff --git a/src/lib.rs b/src/lib.rs index 3b4c225..dae7da3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,10 @@ // #![forbid(unsafe_code)] #[deny(clippy::mem_forget)] // avoids any UB, forces use of Drop impl instead pub mod config; -pub mod pins; pub mod errors; pub mod espressif; pub mod keys; +pub mod pins; pub mod serial; pub mod serve; pub mod settings; diff --git a/src/main.rs b/src/main.rs index ac755d4..2ca82b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,36 +1,31 @@ #![no_std] #![no_main] use core::result::Result; -use core::result::Result::Ok; use core::result::Result::Err; +use core::result::Result::Ok; // use core::error::Error; use core::future::Future; -use embassy_futures::select::{select3, Either3}; use embassy_executor::Spawner; -use embassy_net::{ Stack, tcp::TcpSocket, IpListenEndpoint}; -use embassy_sync::{ - blocking_mutex::raw::NoopRawMutex, - channel::Channel -}; -use esp_println::println; +use embassy_futures::select::{select3, Either3}; +use embassy_net::{tcp::TcpSocket, IpListenEndpoint, Stack}; +use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; +use esp_hal::system::software_reset; use esp_hal::{ gpio::Pin, + peripherals::{GPIO10, GPIO11}, // interrupt::{software::SoftwareInterruptControl, Priority}, // peripherals::Peripherals, // peripherals::UART1, - peripherals::{RADIO_CLK, WIFI, TIMG0, TIMG1, SYSTIMER}, - peripherals::{GPIO10,GPIO11}, + peripherals::{RADIO_CLK, SYSTIMER, TIMG0, TIMG1, WIFI}, rng::Rng, timer::timg::TimerGroup, // uart::{Config, RxConfig, Uart}, }; -use heapless::String; -use static_cell::StaticCell; -use esp_wifi::{InitializationError, EspWifiController}; +use esp_println::println; use esp_storage::FlashStorage; -use sunset_async::{SunsetMutex, SSHServer}; +use esp_wifi::{EspWifiController, InitializationError}; +use heapless::String; use ssh_stamp::{ - pins::{GPIOConfig, PinChannel}, config::SSHStampConfig, espressif::{ buffered_uart::BufferedUart, @@ -38,17 +33,17 @@ use ssh_stamp::{ net::if_up, rng, }, - storage::Fl, + pins::{GPIOConfig, PinChannel}, serve, + storage::Fl, }; -use esp_hal::system::software_reset; +use static_cell::StaticCell; +use sunset_async::{SSHServer, SunsetMutex}; const UART_BUFFER_SIZE: usize = 4096; static UART_BUF: StaticCell = StaticCell::new(); - - -pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a>{ +pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a> { let peripherals = esp_hal::init(esp_hal::Config::default()); let rng = Rng::new(peripherals.RNG); rng::register_custom_rng(rng); @@ -58,14 +53,14 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a let radio_clock = peripherals.RADIO_CLK; // Read SSH configuration from Flash (if it exists) - let mut flash_storage = Fl::new(FlashStorage::new()); - let config = ssh_stamp::storage::load_or_create(&mut flash_storage).await; + let mut flash_storage = Fl::new(FlashStorage::new()); + let config = ssh_stamp::storage::load_or_create(&mut flash_storage).await; - static FLASH: StaticCell> = StaticCell::new(); - let _flash = FLASH.init(SunsetMutex::new(flash_storage)); + static FLASH: StaticCell> = StaticCell::new(); + let _flash = FLASH.init(SunsetMutex::new(flash_storage)); - static CONFIG: StaticCell> = StaticCell::new(); - let config = CONFIG.init(SunsetMutex::new(config.unwrap())); + static CONFIG: StaticCell> = StaticCell::new(); + let config = CONFIG.init(SunsetMutex::new(config.unwrap())); let wifi = peripherals.WIFI; let gpio10 = peripherals.GPIO10; @@ -84,22 +79,25 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a ssh_stamp_peripherals } -pub async fn peripherals_disable() -> (){ +pub async fn peripherals_disable() -> () { // drop peripherals software_reset(); } -pub async fn wifi_wait_for_initialisation(s: PeripheralsEnabledConsumed<'_>) -> Result, InitializationError>{ +pub async fn wifi_wait_for_initialisation( + s: PeripheralsEnabledConsumed<'_>, +) -> Result, InitializationError> { let rng = s.rng; let timg0 = s.timg0; let radio_clock = s.radio_clock; - let wifi_controller: Result, InitializationError> = esp_wifi::init(timg0.timer0, rng, radio_clock); + let wifi_controller: Result, InitializationError> = + esp_wifi::init(timg0.timer0, rng, radio_clock); wifi_controller } -pub async fn wifi_disable() -> (){ +pub async fn wifi_disable() -> () { // TODO: Correctly disable wifi controller -// pub async fn wifi_disable(wifi_controller: EspWifiController<'_>) -> (){ + // pub async fn wifi_disable(wifi_controller: EspWifiController<'_>) -> (){ // drop wifi controller // esp_wifi::deinit_unchecked() // wifi_controller.deinit_unchecked() @@ -110,40 +108,42 @@ pub struct TcpStackReturn<'a> { pub config: SSHStampConfig, pub tcp_stack: Stack<'a>, } -pub async fn tcp_wait_for_initialisation<'a>(s: WifiEnabledConsumed<'a>) -> Stack<'a> { +pub async fn tcp_wait_for_initialisation<'a>(s: WifiEnabledConsumed<'a>) -> Stack<'a> { let spawner: Spawner = s.spawner; let wifi_controller = s.wifi_controller; let wifi: WIFI = s.wifi; let mut rng = s.rng; let wifi_ssid = s.wifi_ssid; let tcp_stack = if_up(spawner, wifi_controller, wifi, &mut rng, wifi_ssid) - .await - .unwrap(); + .await + .unwrap(); tcp_stack } -pub async fn tcp_disable() -> (){ +pub async fn tcp_disable() -> () { // drop tcp stack software_reset(); } -pub async fn socket_disable() -> (){ +pub async fn socket_disable() -> () { // drop socket software_reset(); } - -pub async fn ssh_wait_for_initialisation<'server>(inbuf: &'server mut [u8; UART_BUFFER_SIZE], outbuf: &'server mut [u8; UART_BUFFER_SIZE]) -> SSHServer<'server>{ +pub async fn ssh_wait_for_initialisation<'server>( + inbuf: &'server mut [u8; UART_BUFFER_SIZE], + outbuf: &'server mut [u8; UART_BUFFER_SIZE], +) -> SSHServer<'server> { let ssh_server = SSHServer::new(inbuf, outbuf); ssh_server } -pub async fn ssh_disable() -> (){ +pub async fn ssh_disable() -> () { // drop wifi controller software_reset(); } -pub async fn uart_pins_wait_for_config<'a>(s: SshEnabledConsumed<'a>) -> PinChannel<'a> { +pub async fn uart_pins_wait_for_config<'a>(s: SshEnabledConsumed<'a>) -> PinChannel<'a> { let serde_pin_config = { let guard = s.config.lock().await; guard.uart_pins.clone() @@ -159,7 +159,7 @@ pub async fn uart_pins_wait_for_config<'a>(s: SshEnabledConsumed<'a>) -> PinCha pin_channel_ref } -pub async fn uart_pins_disable() -> (){ +pub async fn uart_pins_disable() -> () { // disable uart pins software_reset(); } @@ -173,7 +173,14 @@ pub async fn uart_buffer_disable() -> () { software_reset(); } -pub async fn idle_wait_for_connection<'a, 'b>(s: UartEnabledConsumed<'a>, ssh_server: &'b SSHServer<'a>, pin_channel: PinChannel<'a>) -> Result<(), sunset::Error> where 'a:'b{ +pub async fn idle_wait_for_connection<'a, 'b>( + s: UartEnabledConsumed<'a>, + ssh_server: &'b SSHServer<'a>, + pin_channel: PinChannel<'a>, +) -> Result<(), sunset::Error> +where + 'a: 'b, +{ let chan_pipe = s.chan_pipe; serve::connection_loop(ssh_server, &chan_pipe, pin_channel).await } @@ -185,7 +192,9 @@ pub async fn idle_disable() -> () { use ssh_stamp::serial::serial_bridge; use sunset_async::ChanInOut; -pub async fn bridge_wait_for_initialisation<'a, 'b>(s: ClientConnectedConsumed<'a, 'b>) -> Result<(), sunset::Error>{ +pub async fn bridge_wait_for_initialisation<'a, 'b>( + s: ClientConnectedConsumed<'a, 'b>, +) -> Result<(), sunset::Error> { let chan_pipe = Channel::::new(); let bridge = { let session_type = chan_pipe.receive().await; @@ -195,11 +204,10 @@ pub async fn bridge_wait_for_initialisation<'a, 'b>(s: ClientConnectedConsumed<' let stdio: ChanInOut<'_> = s.ssh_server.stdio(ch).await?; let stdio2 = stdio.clone(); serial_bridge(stdio, stdio2, s.uart_buff).await? - } - // SessionType::Sftp(_ch) => { - // // Handle SFTP session - // todo!() - // } + } // SessionType::Sftp(_ch) => { + // // Handle SFTP session + // todo!() + // } }; Ok(()) }; @@ -211,13 +219,12 @@ pub async fn bridge_disable() -> () { software_reset(); } - pub struct SshStampPeripherals<'a> { pub rng: Rng, pub timg0: TimerGroup<'a, TIMG0<'a>>, pub radio_clock: RADIO_CLK<'a>, pub wifi: WIFI<'a>, - pub config: &'a SunsetMutex, + pub config: &'a SunsetMutex, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, pub timg1: TIMG1<'a>, @@ -229,7 +236,7 @@ pub struct SshStampInit<'a> { pub timg0: TimerGroup<'a, TIMG0<'a>>, pub radio_clock: RADIO_CLK<'a>, pub wifi: WIFI<'a>, - pub config: &'a SunsetMutex, + pub config: &'a SunsetMutex, pub spawner: Spawner, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, @@ -275,7 +282,7 @@ async fn main(spawner: Spawner) -> ! { spawner: spawner, gpio10: peripherals.gpio10, gpio11: peripherals.gpio11, - }; + }; match peripherals_enabled(peripherals_enabled_struct).await { Ok(_) => (), @@ -289,7 +296,6 @@ async fn main(spawner: Spawner) -> ! { software_reset(); } - pub struct PeripheralsEnabledConsumed<'a> { pub rng: Rng, pub timg0: TimerGroup<'a, TIMG0<'a>>, @@ -298,7 +304,7 @@ pub struct PeripheralsEnabledConsumed<'a> { pub struct PeripheralsEnabled<'a> { pub rng: Rng, pub wifi: WIFI<'a>, - pub config: &'a SunsetMutex, + pub config: &'a SunsetMutex, pub spawner: Spawner, pub wifi_controller: EspWifiController<'a>, pub gpio10: GPIO10<'a>, @@ -309,7 +315,7 @@ async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Erro rng: s.rng, timg0: s.timg0, radio_clock: s.radio_clock, - }; + }; let wifi_controller = wifi_wait_for_initialisation(peripherals_enabled_consumed).await; let peripherals_enabled_struct = PeripheralsEnabled { @@ -320,7 +326,7 @@ async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Erro wifi_controller: wifi_controller.unwrap(), gpio10: s.gpio10, gpio11: s.gpio11, - }; + }; match wifi_enabled(peripherals_enabled_struct).await { Ok(_) => (), Err(e) => { @@ -342,7 +348,7 @@ pub struct WifiEnabledConsumed<'a> { } pub struct WifiEnabled<'a> { pub rng: Rng, - pub config: &'a SunsetMutex, + pub config: &'a SunsetMutex, pub tcp_stack: Stack<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, @@ -359,7 +365,7 @@ async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error wifi_ssid: wifi_ssid_config, spawner: s.spawner, wifi_controller: s.wifi_controller, - }; + }; let tcp_stack = tcp_wait_for_initialisation(wifi_enabled_consumed).await; let wifi_enabled_struct = WifiEnabled { rng: s.rng, @@ -367,7 +373,7 @@ async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error tcp_stack: tcp_stack, gpio10: s.gpio10, gpio11: s.gpio11, - }; + }; match tcp_enabled(wifi_enabled_struct).await { Ok(_) => (), Err(e) => { @@ -386,7 +392,6 @@ pub struct TCPEnabled<'a> { pub gpio11: GPIO11<'a>, } async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { - let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; let mut tcp_socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); @@ -409,7 +414,7 @@ async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { tcp_socket: tcp_socket, gpio10: s.gpio10, gpio11: s.gpio11, - }; + }; match socket_enabled(tcp_enabled_struct).await { Ok(_) => (), Err(e) => { @@ -418,7 +423,7 @@ async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { } socket_disable().await; Ok(()) // todo!() return relevant value - } +} pub struct SocketEnabled<'a> { pub rng: Rng, @@ -430,31 +435,30 @@ pub struct SocketEnabled<'a> { } async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { // loop { - // Spawn network tasks to handle incoming connections with demo_common::session() - let mut inbuf = [0u8; UART_BUFFER_SIZE]; - let mut outbuf = [0u8; UART_BUFFER_SIZE]; - let ssh_server = ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; - let wifi_enabled_struct = SocketEnabled { - rng: s.rng, - config: s.config, - tcp_socket: s.tcp_socket, - ssh_server: ssh_server, - gpio10: s.gpio10, - gpio11: s.gpio11, - }; - match ssh_enabled(wifi_enabled_struct).await { - Ok(_) => (), - Err(e) => { - println!("Wifi error: {}", e); - } + // Spawn network tasks to handle incoming connections with demo_common::session() + let mut inbuf = [0u8; UART_BUFFER_SIZE]; + let mut outbuf = [0u8; UART_BUFFER_SIZE]; + let ssh_server = ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; + let wifi_enabled_struct = SocketEnabled { + rng: s.rng, + config: s.config, + tcp_socket: s.tcp_socket, + ssh_server: ssh_server, + gpio10: s.gpio10, + gpio11: s.gpio11, + }; + match ssh_enabled(wifi_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("Wifi error: {}", e); } + } - ssh_disable().await; + ssh_disable().await; // } Ok(()) // todo!() return relevant value } - pub struct SshEnabledConsumed<'a> { pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, @@ -470,28 +474,28 @@ pub struct SshEnabled<'a> { async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> { // loop { - let ssh_enabled_consumed = SshEnabledConsumed { - config: s.config, - gpio10: s.gpio10, - gpio11: s.gpio11, - }; + let ssh_enabled_consumed = SshEnabledConsumed { + config: s.config, + gpio10: s.gpio10, + gpio11: s.gpio11, + }; - let uart_pins = uart_pins_wait_for_config(ssh_enabled_consumed).await; + let uart_pins = uart_pins_wait_for_config(ssh_enabled_consumed).await; - let ssh_enabled_struct = SshEnabled { - rng: s.rng, - tcp_socket: s.tcp_socket, - ssh_server: s.ssh_server, - uart_pins: uart_pins, - }; - match uart_configured(ssh_enabled_struct).await { - Ok(_) => (), - Err(e) => { - println!("UART pin error: {}", e); - } + let ssh_enabled_struct = SshEnabled { + rng: s.rng, + tcp_socket: s.tcp_socket, + ssh_server: s.ssh_server, + uart_pins: uart_pins, + }; + match uart_configured(ssh_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("UART pin error: {}", e); } + } - uart_pins_disable().await; + uart_pins_disable().await; // } Ok(()) // todo!() return relevant value } @@ -503,70 +507,75 @@ pub struct UartConfigured<'a> { pub uart_pins: PinChannel<'a>, pub uart_buff: &'a BufferedUart, } -async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> {// where 'b: 'a { +async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> { + // where 'b: 'a { // loop { - let uart_buff = uart_buffer_wait_for_initialisation().await; - let uart_configured_struct = UartConfigured { - rng: s.rng, - tcp_socket: s.tcp_socket, - ssh_server: s.ssh_server, - uart_pins: s.uart_pins, - uart_buff: uart_buff, - }; - match uart_enabled(uart_configured_struct).await { - Ok(_) => (), - Err(e) => { - println!("Uart buffer error: {}", e); - } + let uart_buff = uart_buffer_wait_for_initialisation().await; + let uart_configured_struct = UartConfigured { + rng: s.rng, + tcp_socket: s.tcp_socket, + ssh_server: s.ssh_server, + uart_pins: s.uart_pins, + uart_buff: uart_buff, + }; + match uart_enabled(uart_configured_struct).await { + Ok(_) => (), + Err(e) => { + println!("Uart buffer error: {}", e); } + } - uart_buffer_disable().await; + uart_buffer_disable().await; // } Ok(()) // todo!() return relevant value } - - -pub struct UartEnabledConsumed<'a>{ +pub struct UartEnabledConsumed<'a> { pub rng: Rng, pub uart_buff: &'a BufferedUart, pub chan_pipe: Channel, } -pub struct UartEnabled<'a, 'b, CL> where CL: Future>{ +pub struct UartEnabled<'a, 'b, CL> +where + CL: Future>, +{ pub rng: Rng, pub tcp_socket: TcpSocket<'a>, pub ssh_server: &'b SSHServer<'a>, pub uart_buff: &'a BufferedUart, - pub connection_loop: CL, + pub connection_loop: CL, } -async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error> where 'b: 'a{ +async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error> +where + 'b: 'a, +{ // loop { - let chan_pipe = Channel::::new(); + let chan_pipe = Channel::::new(); - let uart_enabled_consumed = UartEnabledConsumed { - rng: s.rng, - uart_buff: s.uart_buff, - chan_pipe: chan_pipe, - }; + let uart_enabled_consumed = UartEnabledConsumed { + rng: s.rng, + uart_buff: s.uart_buff, + chan_pipe: chan_pipe, + }; - println!("Calling connection_loop from uart_enabled"); - let connection = idle_wait_for_connection(uart_enabled_consumed, &s.ssh_server, s.uart_pins); + println!("Calling connection_loop from uart_enabled"); + let connection = idle_wait_for_connection(uart_enabled_consumed, &s.ssh_server, s.uart_pins); - let uart_enabled_struct = UartEnabled { - rng: s.rng, - tcp_socket: s.tcp_socket, - ssh_server: &s.ssh_server, - uart_buff: s.uart_buff, - connection_loop: connection, - }; - match client_connected(uart_enabled_struct).await { - Ok(_) => (), - Err(e) => { - println!("Client connection error: {}", e); - } + let uart_enabled_struct = UartEnabled { + rng: s.rng, + tcp_socket: s.tcp_socket, + ssh_server: &s.ssh_server, + uart_buff: s.uart_buff, + connection_loop: connection, + }; + match client_connected(uart_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("Client connection error: {}", e); } + } - idle_disable().await; + idle_disable().await; // } Ok(()) // todo!() return relevant value } @@ -576,46 +585,60 @@ pub struct ClientConnectedConsumed<'a, 'b> { pub uart_buff: &'a BufferedUart, pub ssh_server: &'b SSHServer<'a>, } -pub struct ClientConnected<'a, 'b, CL, BR> where CL: Future>, BR: Future>{ +pub struct ClientConnected<'a, 'b, CL, BR> +where + CL: Future>, + BR: Future>, +{ pub ssh_server: &'b SSHServer<'a>, - pub bridge: BR, + pub bridge: BR, pub connection_loop: CL, pub tcp_socket: TcpSocket<'a>, - } -async fn client_connected<'a, 'b, CL>(s: UartEnabled<'a, 'b, CL> ) -> Result<(), sunset::Error> where CL: Future>, 'a:'b{ +async fn client_connected<'a, 'b, CL>(s: UartEnabled<'a, 'b, CL>) -> Result<(), sunset::Error> +where + CL: Future>, + 'a: 'b, +{ // loop { - let mut rx_buffer = [0u8; 1536]; - let mut tx_buffer = [0u8; 1536]; - let socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); - let client_connected_consumed = ClientConnectedConsumed { - rng: s.rng, - uart_buff: s.uart_buff, - ssh_server: s.ssh_server, - }; - let bridge = bridge_wait_for_initialisation(client_connected_consumed); - let client_connected_struct = ClientConnected { - ssh_server: s.ssh_server, - bridge: bridge, - connection_loop: s.connection_loop, - tcp_socket: socket, - }; - match bridge_connected(client_connected_struct).await { - Ok(_) => (), - Err(e) => { - println!("Bridge error: {}", e); - } + let client_connected_consumed = ClientConnectedConsumed { + rng: s.rng, + uart_buff: s.uart_buff, + ssh_server: s.ssh_server, + }; + + println!("Setting up serial bridge"); + let bridge = bridge_wait_for_initialisation(client_connected_consumed); + + let client_connected_struct = ClientConnected { + ssh_server: s.ssh_server, + bridge: bridge, + connection_loop: s.connection_loop, + tcp_socket: s.tcp_socket, + }; + match bridge_connected(client_connected_struct).await { + Ok(_) => (), + Err(e) => { + println!("Bridge error: {}", e); } + } - bridge_disable().await; + bridge_disable().await; // } Ok(()) // todo!() return relevant value } -async fn bridge_connected<'a, 'b, CL, BR>(s:ClientConnected<'a, 'b, CL, BR>) -> Result<(), sunset::Error> where CL: Future>, BR: Future>, 'a:'b{ +async fn bridge_connected<'a, 'b, CL, BR>( + s: ClientConnected<'a, 'b, CL, BR>, +) -> Result<(), sunset::Error> +where + CL: Future>, + BR: Future>, + 'a: 'b, +{ let mut tcp_socket = s.tcp_socket; let (mut rsock, mut wsock) = tcp_socket.split(); println!("Running server from handle_ssh_client()"); diff --git a/src/pins.rs b/src/pins.rs index 4de1559..3c7cb74 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -1,11 +1,14 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel}; use esp_hal::gpio::AnyPin; use esp_hal::peripherals; -use sunset::sshwire::{ SSHDecode, SSHEncode, SSHSink, SSHSource, WireResult }; +use sunset::sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireResult}; // use static_cell::StaticCell; // use sunset_async::SunsetMutex; -use crate::{config::{dec_option, enc_option}, errors}; +use crate::{ + config::{dec_option, enc_option}, + errors, +}; #[derive(Debug, Clone, PartialEq)] pub struct SerdePinConfig { @@ -66,7 +69,6 @@ impl SSHEncode for SerdePinConfig { } } - impl<'de> SSHDecode<'de> for SerdePinConfig { fn dec(s: &mut S) -> WireResult where @@ -98,7 +100,7 @@ pub struct PinChannel<'a> { // TODO: cts/rts pins } -impl <'a>PinChannel<'a> { +impl<'a> PinChannel<'a> { pub fn new(config: SerdePinConfig, gpios: GPIOConfig<'a>) -> Self { Self { config, @@ -251,8 +253,11 @@ impl PinConfigAlt { } } -impl <'a>PinConfig<'a> { - pub fn new(mut gpio_config: GPIOConfig<'a>, config_inner: SerdePinConfig) -> errors::Result { +impl<'a> PinConfig<'a> { + pub fn new( + mut gpio_config: GPIOConfig<'a>, + config_inner: SerdePinConfig, + ) -> errors::Result { if config_inner.rx == config_inner.tx { return Err(errors::Error::InvalidPin); } diff --git a/src/serve.rs b/src/serve.rs index 961edaf..c390af2 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -2,8 +2,8 @@ use core::option::Option::{self, None, Some}; use core::result::Result; use core::writeln; -use crate::pins::PinChannel; use crate::keys; +use crate::pins::PinChannel; // Embassy use embassy_sync::blocking_mutex::raw::NoopRawMutex; @@ -15,8 +15,8 @@ use heapless::String; use sunset::{error, ChanHandle, ServEvent, SignKey}; use sunset_async::{ProgressHolder, SSHServer}; -use esp_println::{dbg, println}; use esp_hal::system::software_reset; +use esp_println::{dbg, println}; pub enum SessionType { Bridge(ChanHandle), @@ -103,7 +103,7 @@ pub async fn connection_loop<'a>( if let Ok(pin_num) = val.parse::() { // let mut ch = pin_channel_ref.lock().await; // let mut ch: &mut PinChannel<'_> = pin_channel_ref; - let mut ch = pin_channel; + let mut ch = pin_channel; if ch.set_tx_pin(pin_num).is_err() { dbg!("Failed to update TX pin"); } else { @@ -119,7 +119,7 @@ pub async fn connection_loop<'a>( dbg!("Updating UART RX pin to ", val); if let Ok(pin_num) = val.parse::() { // let mut ch = pin_channel_ref.lock().await; - let mut ch = pin_channel; + let mut ch = pin_channel; if ch.set_rx_pin(pin_num).is_err() { dbg!("Failed to update RX pin"); } else { From 40de634a8961e5aeb533a400a228d1496b18147f Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 29 Jan 2026 12:00:11 +1100 Subject: [PATCH 56/86] Use same chan_pipe for connection loop and bridge. --- src/main.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2ca82b8..3e97804 100644 --- a/src/main.rs +++ b/src/main.rs @@ -174,15 +174,14 @@ pub async fn uart_buffer_disable() -> () { } pub async fn idle_wait_for_connection<'a, 'b>( - s: UartEnabledConsumed<'a>, ssh_server: &'b SSHServer<'a>, pin_channel: PinChannel<'a>, + chan_pipe: &Channel, ) -> Result<(), sunset::Error> where 'a: 'b, { - let chan_pipe = s.chan_pipe; - serve::connection_loop(ssh_server, &chan_pipe, pin_channel).await + serve::connection_loop(ssh_server, chan_pipe, pin_channel).await } pub async fn idle_disable() -> () { @@ -195,8 +194,8 @@ use sunset_async::ChanInOut; pub async fn bridge_wait_for_initialisation<'a, 'b>( s: ClientConnectedConsumed<'a, 'b>, ) -> Result<(), sunset::Error> { - let chan_pipe = Channel::::new(); let bridge = { + let chan_pipe = s.chan_pipe; let session_type = chan_pipe.receive().await; match session_type { @@ -530,11 +529,6 @@ async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> { Ok(()) // todo!() return relevant value } -pub struct UartEnabledConsumed<'a> { - pub rng: Rng, - pub uart_buff: &'a BufferedUart, - pub chan_pipe: Channel, -} pub struct UartEnabled<'a, 'b, CL> where CL: Future>, @@ -543,6 +537,7 @@ where pub tcp_socket: TcpSocket<'a>, pub ssh_server: &'b SSHServer<'a>, pub uart_buff: &'a BufferedUart, + pub chan_pipe: &'b Channel, pub connection_loop: CL, } async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error> @@ -552,20 +547,15 @@ where // loop { let chan_pipe = Channel::::new(); - let uart_enabled_consumed = UartEnabledConsumed { - rng: s.rng, - uart_buff: s.uart_buff, - chan_pipe: chan_pipe, - }; - println!("Calling connection_loop from uart_enabled"); - let connection = idle_wait_for_connection(uart_enabled_consumed, &s.ssh_server, s.uart_pins); + let connection = idle_wait_for_connection(&s.ssh_server, s.uart_pins, &chan_pipe); let uart_enabled_struct = UartEnabled { rng: s.rng, tcp_socket: s.tcp_socket, ssh_server: &s.ssh_server, uart_buff: s.uart_buff, + chan_pipe: &chan_pipe, connection_loop: connection, }; match client_connected(uart_enabled_struct).await { @@ -584,6 +574,7 @@ pub struct ClientConnectedConsumed<'a, 'b> { pub rng: Rng, pub uart_buff: &'a BufferedUart, pub ssh_server: &'b SSHServer<'a>, + pub chan_pipe: &'b Channel, } pub struct ClientConnected<'a, 'b, CL, BR> where @@ -607,6 +598,7 @@ where rng: s.rng, uart_buff: s.uart_buff, ssh_server: s.ssh_server, + chan_pipe: s.chan_pipe, }; println!("Setting up serial bridge"); From 49c55ba61546ec577542b58fcb303776c0c8a9dd Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Tue, 3 Feb 2026 01:00:03 +1100 Subject: [PATCH 57/86] Start UART after ssh connection is made. This means that SSH ENV requests are received first, config can be set, and then correct the pins are used when starting UART (pins don't need to be updated). Remove use of pins.rs / PinChannel as GPIO are only used when starting UART. Store SSH ENV pins as u8 value in config. Save config to flash after all SSH ENV requests are completed. --- src/config.rs | 10 ++ src/main.rs | 252 +++++++++++++++++++++++++++++++++----------------- src/serve.rs | 51 +++++----- 3 files changed, 200 insertions(+), 113 deletions(-) diff --git a/src/config.rs b/src/config.rs index aef6203..8956d1e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -47,6 +47,8 @@ pub struct SSHStampConfig { pub ipv6_static: Option, /// UART pub uart_pins: SerdePinConfig, + pub tx_pin: u8, + pub rx_pin: u8, } impl SSHStampConfig { @@ -66,6 +68,8 @@ impl SSHStampConfig { let wifi_pw = None; let uart_pins = SerdePinConfig::default(); + let tx_pin = 10; + let rx_pin = 11; Ok(SSHStampConfig { hostkey, @@ -79,6 +83,8 @@ impl SSHStampConfig { #[cfg(feature = "ipv6")] ipv6_static: None, uart_pins, + tx_pin, + rx_pin, }) } @@ -267,6 +273,8 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { // Not supported by sshwire-derive nor virtue (no Option support) // let uart_pins = SSHDecode::dec(s)?; let uart_pins = SSHDecode::dec(s)?; + let tx_pin = 10; + let rx_pin = 11; Ok(Self { hostkey, @@ -280,6 +288,8 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { #[cfg(feature = "ipv6")] ipv6_static, uart_pins, + tx_pin, + rx_pin, }) } } diff --git a/src/main.rs b/src/main.rs index 3e97804..9fa6715 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use embassy_executor::Spawner; use embassy_futures::select::{select3, Either3}; use embassy_net::{tcp::TcpSocket, IpListenEndpoint, Stack}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; +use esp_hal::peripherals::UART1; use esp_hal::system::software_reset; use esp_hal::{ gpio::Pin, @@ -19,7 +20,7 @@ use esp_hal::{ peripherals::{RADIO_CLK, SYSTIMER, TIMG0, TIMG1, WIFI}, rng::Rng, timer::timg::TimerGroup, - // uart::{Config, RxConfig, Uart}, + uart::{Config, RxConfig, Uart}, }; use esp_println::println; use esp_storage::FlashStorage; @@ -33,7 +34,7 @@ use ssh_stamp::{ net::if_up, rng, }, - pins::{GPIOConfig, PinChannel}, + // pins::{GPIOConfig, PinChannel}, serve, storage::Fl, }; @@ -57,7 +58,7 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a let config = ssh_stamp::storage::load_or_create(&mut flash_storage).await; static FLASH: StaticCell> = StaticCell::new(); - let _flash = FLASH.init(SunsetMutex::new(flash_storage)); + let flash = FLASH.init(SunsetMutex::new(flash_storage)); static CONFIG: StaticCell> = StaticCell::new(); let config = CONFIG.init(SunsetMutex::new(config.unwrap())); @@ -65,6 +66,7 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a let wifi = peripherals.WIFI; let gpio10 = peripherals.GPIO10; let gpio11 = peripherals.GPIO11; + let uart1 = peripherals.UART1; let ssh_stamp_peripherals = SshStampPeripherals { rng: rng, timg0: timg0, @@ -73,8 +75,10 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a config: config, gpio10: gpio10, gpio11: gpio11, + uart1: uart1, timg1: timg1, systimer: systimer, + flash: flash, }; ssh_stamp_peripherals } @@ -143,48 +147,80 @@ pub async fn ssh_disable() -> () { software_reset(); } -pub async fn uart_pins_wait_for_config<'a>(s: SshEnabledConsumed<'a>) -> PinChannel<'a> { - let serde_pin_config = { - let guard = s.config.lock().await; - guard.uart_pins.clone() - }; - let pin10 = s.gpio10.degrade(); - let pin11 = s.gpio11.degrade(); - - let available_gpios = GPIOConfig { - gpio10: Some(pin10), - gpio11: Some(pin11), - }; - let pin_channel_ref = PinChannel::new(serde_pin_config, available_gpios); - pin_channel_ref +pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { + UART_BUF.init_with(BufferedUart::new) } -pub async fn uart_pins_disable() -> () { - // disable uart pins +pub async fn uart_buffer_disable() -> () { + // disable uart buffer software_reset(); } -pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { - UART_BUF.init_with(BufferedUart::new) +pub async fn enable_uart<'a, 'b>( + uart_buf: &'a BufferedUart, + uart1: UART1<'a>, + // pin_channel: &'b mut PinChannel<'a>, + config: &'a SunsetMutex, + gpio10: GPIO10<'_>, + gpio11: GPIO11<'_>, +) where + 'a: 'b, +{ + // uart_task(uart_buf, uart1, pin_channel_ref + // dbg!("Spawning UART task..."); + let config_lock = config.lock().await; + let rx: u8 = config_lock.rx_pin; + let tx: u8 = config_lock.tx_pin; + if rx != tx { + let mut holder10 = Some(gpio10); + let mut holder11 = Some(gpio11); + let rx_pin = match rx { + 10 => holder10.take().unwrap().degrade(), + 11 => holder11.take().unwrap().degrade(), + _ => holder10.take().unwrap().degrade(), + }; + let tx_pin = match tx { + 10 => holder10.take().unwrap().degrade(), + 11 => holder11.take().unwrap().degrade(), + _ => holder11.take().unwrap().degrade(), + }; + + // Hardware UART setup + let uart_config = Config::default().with_rx( + RxConfig::default() + .with_fifo_full_threshold(16) + .with_timeout(1), + ); + + let uart = Uart::new(uart1, uart_config) + .unwrap() + .with_rx(rx_pin) + .with_tx(tx_pin) + .into_async(); + // Run the main buffered TX/RX loop + uart_buf.run(uart).await; + } + // TODO: Pin config error } -pub async fn uart_buffer_disable() -> () { - // disable uart buffer +pub async fn uart_disable() -> () { + // disable uart software_reset(); } pub async fn idle_wait_for_connection<'a, 'b>( ssh_server: &'b SSHServer<'a>, - pin_channel: PinChannel<'a>, chan_pipe: &Channel, + config: &'a SunsetMutex, + flash: &'a SunsetMutex, ) -> Result<(), sunset::Error> where 'a: 'b, { - serve::connection_loop(ssh_server, chan_pipe, pin_channel).await + serve::connection_loop(ssh_server, chan_pipe, config, flash).await } -pub async fn idle_disable() -> () { +pub async fn connection_disable() -> () { // disable idle software_reset(); } @@ -192,7 +228,7 @@ pub async fn idle_disable() -> () { use ssh_stamp::serial::serial_bridge; use sunset_async::ChanInOut; pub async fn bridge_wait_for_initialisation<'a, 'b>( - s: ClientConnectedConsumed<'a, 'b>, + s: UartEnabledConsumed<'a, 'b>, ) -> Result<(), sunset::Error> { let bridge = { let chan_pipe = s.chan_pipe; @@ -226,8 +262,10 @@ pub struct SshStampPeripherals<'a> { pub config: &'a SunsetMutex, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, pub timg1: TIMG1<'a>, pub systimer: SYSTIMER<'a>, + pub flash: &'a SunsetMutex, } pub struct SshStampInit<'a> { @@ -239,6 +277,8 @@ pub struct SshStampInit<'a> { pub spawner: Spawner, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, + pub flash: &'a SunsetMutex, } #[esp_hal_embassy::main] @@ -281,6 +321,8 @@ async fn main(spawner: Spawner) -> ! { spawner: spawner, gpio10: peripherals.gpio10, gpio11: peripherals.gpio11, + uart1: peripherals.uart1, + flash: peripherals.flash, }; match peripherals_enabled(peripherals_enabled_struct).await { @@ -308,6 +350,8 @@ pub struct PeripheralsEnabled<'a> { pub wifi_controller: EspWifiController<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, + pub flash: &'a SunsetMutex, } async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Error> { let peripherals_enabled_consumed = PeripheralsEnabledConsumed { @@ -325,11 +369,13 @@ async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Erro wifi_controller: wifi_controller.unwrap(), gpio10: s.gpio10, gpio11: s.gpio11, + uart1: s.uart1, + flash: s.flash, }; match wifi_enabled(peripherals_enabled_struct).await { Ok(_) => (), Err(e) => { - println!("TCP error: {}", e); + println!("Wifi error: {}", e); } } @@ -351,6 +397,8 @@ pub struct WifiEnabled<'a> { pub tcp_stack: Stack<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, + pub flash: &'a SunsetMutex, } async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { let wifi_ssid_config = { @@ -372,11 +420,13 @@ async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error tcp_stack: tcp_stack, gpio10: s.gpio10, gpio11: s.gpio11, + uart1: s.uart1, + flash: s.flash, }; match tcp_enabled(wifi_enabled_struct).await { Ok(_) => (), Err(e) => { - println!("SSH error: {}", e); + println!("TCP stack error: {}", e); } } tcp_disable().await; @@ -389,6 +439,8 @@ pub struct TCPEnabled<'a> { pub tcp_socket: TcpSocket<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, + pub flash: &'a SunsetMutex, } async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { let mut rx_buffer = [0u8; 1536]; @@ -413,11 +465,13 @@ async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { tcp_socket: tcp_socket, gpio10: s.gpio10, gpio11: s.gpio11, + uart1: s.uart1, + flash: s.flash, }; match socket_enabled(tcp_enabled_struct).await { Ok(_) => (), Err(e) => { - println!("Wifi error: {}", e); + println!("TCP socket error: {}", e); } } socket_disable().await; @@ -431,6 +485,8 @@ pub struct SocketEnabled<'a> { pub ssh_server: SSHServer<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, + pub flash: &'a SunsetMutex, } async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { // loop { @@ -438,18 +494,20 @@ async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { let mut inbuf = [0u8; UART_BUFFER_SIZE]; let mut outbuf = [0u8; UART_BUFFER_SIZE]; let ssh_server = ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; - let wifi_enabled_struct = SocketEnabled { + let socket_enabled_struct = SocketEnabled { rng: s.rng, config: s.config, tcp_socket: s.tcp_socket, ssh_server: ssh_server, gpio10: s.gpio10, gpio11: s.gpio11, + uart1: s.uart1, + flash: s.flash, }; - match ssh_enabled(wifi_enabled_struct).await { + match ssh_enabled(socket_enabled_struct).await { Ok(_) => (), Err(e) => { - println!("Wifi error: {}", e); + println!("SSH server error: {}", e); } } @@ -458,69 +516,91 @@ async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { Ok(()) // todo!() return relevant value } -pub struct SshEnabledConsumed<'a> { - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, - pub config: &'a SunsetMutex, -} - -pub struct SshEnabled<'a> { +pub struct SshEnabled<'a, 'b, CL> +where + CL: Future>, +{ pub rng: Rng, pub tcp_socket: TcpSocket<'a>, - pub ssh_server: SSHServer<'a>, - pub uart_pins: PinChannel<'a>, + pub ssh_server: &'b SSHServer<'a>, + pub uart1: UART1<'a>, + pub config: &'a SunsetMutex, + pub chan_pipe: &'b Channel, + pub connection_loop: CL, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, } - -async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> { +async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> +// where + // 'b: 'a, +{ // loop { - let ssh_enabled_consumed = SshEnabledConsumed { - config: s.config, - gpio10: s.gpio10, - gpio11: s.gpio11, - }; + let chan_pipe = Channel::::new(); - let uart_pins = uart_pins_wait_for_config(ssh_enabled_consumed).await; + println!("Calling connection_loop from uart_enabled"); + let connection = idle_wait_for_connection(&s.ssh_server, &chan_pipe, &s.config, &s.flash); let ssh_enabled_struct = SshEnabled { rng: s.rng, tcp_socket: s.tcp_socket, - ssh_server: s.ssh_server, - uart_pins: uart_pins, + ssh_server: &s.ssh_server, + config: s.config, + chan_pipe: &chan_pipe, + connection_loop: connection, + uart1: s.uart1, + gpio10: s.gpio10, + gpio11: s.gpio11, }; - match uart_configured(ssh_enabled_struct).await { + match client_connected(ssh_enabled_struct).await { Ok(_) => (), Err(e) => { - println!("UART pin error: {}", e); + println!("Client connection error: {}", e); } } - uart_pins_disable().await; + connection_disable().await; // } Ok(()) // todo!() return relevant value } -pub struct UartConfigured<'a> { +pub struct ClientConnected<'a, 'b, CL> +where + CL: Future>, +{ pub rng: Rng, pub tcp_socket: TcpSocket<'a>, - pub ssh_server: SSHServer<'a>, - pub uart_pins: PinChannel<'a>, + pub ssh_server: &'b SSHServer<'a>, + pub chan_pipe: &'b Channel, + pub connection_loop: CL, pub uart_buff: &'a BufferedUart, + pub uart1: UART1<'a>, + pub config: &'a SunsetMutex, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, } -async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> { - // where 'b: 'a { +async fn client_connected<'a, 'b, CL>(s: SshEnabled<'a, 'b, CL>) -> Result<(), sunset::Error> +// where 'b: 'a { +where + CL: Future>, +{ // loop { let uart_buff = uart_buffer_wait_for_initialisation().await; - let uart_configured_struct = UartConfigured { + let client_connected_struct = ClientConnected { rng: s.rng, tcp_socket: s.tcp_socket, ssh_server: s.ssh_server, - uart_pins: s.uart_pins, + chan_pipe: s.chan_pipe, + connection_loop: s.connection_loop, uart_buff: uart_buff, + uart1: s.uart1, + config: s.config, + gpio10: s.gpio10, + gpio11: s.gpio11, }; - match uart_enabled(uart_configured_struct).await { + match uart_buffer_ready(client_connected_struct).await { Ok(_) => (), Err(e) => { - println!("Uart buffer error: {}", e); + println!("UART buffer error: {}", e); } } @@ -529,7 +609,7 @@ async fn uart_configured<'a>(s: SshEnabled<'a>) -> Result<(), sunset::Error> { Ok(()) // todo!() return relevant value } -pub struct UartEnabled<'a, 'b, CL> +pub struct UartBufferReady<'a, 'b, CL> where CL: Future>, { @@ -540,43 +620,42 @@ where pub chan_pipe: &'b Channel, pub connection_loop: CL, } -async fn uart_enabled<'a, 'b>(s: UartConfigured<'a>) -> Result<(), sunset::Error> +async fn uart_buffer_ready<'a, 'b, CL>(s: ClientConnected<'a, 'b, CL>) -> Result<(), sunset::Error> where - 'b: 'a, + // 'b: 'a, + CL: Future>, { // loop { - let chan_pipe = Channel::::new(); + let _uart = enable_uart(s.uart_buff, s.uart1, &s.config, s.gpio10, s.gpio11); - println!("Calling connection_loop from uart_enabled"); - let connection = idle_wait_for_connection(&s.ssh_server, s.uart_pins, &chan_pipe); - - let uart_enabled_struct = UartEnabled { + let uart_buffer_ready_struct = UartBufferReady { rng: s.rng, tcp_socket: s.tcp_socket, - ssh_server: &s.ssh_server, + ssh_server: s.ssh_server, + chan_pipe: s.chan_pipe, + // uart_pins: &s.uart_pins, uart_buff: s.uart_buff, - chan_pipe: &chan_pipe, - connection_loop: connection, + connection_loop: s.connection_loop, }; - match client_connected(uart_enabled_struct).await { + match uart_enabled(uart_buffer_ready_struct).await { Ok(_) => (), Err(e) => { - println!("Client connection error: {}", e); + println!("UART error: {}", e); } } - idle_disable().await; + uart_disable().await; // } Ok(()) // todo!() return relevant value } -pub struct ClientConnectedConsumed<'a, 'b> { +pub struct UartEnabledConsumed<'a, 'b> { pub rng: Rng, pub uart_buff: &'a BufferedUart, pub ssh_server: &'b SSHServer<'a>, pub chan_pipe: &'b Channel, } -pub struct ClientConnected<'a, 'b, CL, BR> +pub struct UartEnabled<'a, 'b, CL, BR> where CL: Future>, BR: Future>, @@ -587,14 +666,13 @@ where pub tcp_socket: TcpSocket<'a>, } -async fn client_connected<'a, 'b, CL>(s: UartEnabled<'a, 'b, CL>) -> Result<(), sunset::Error> +async fn uart_enabled<'a, 'b, CL>(s: UartBufferReady<'a, 'b, CL>) -> Result<(), sunset::Error> where CL: Future>, 'a: 'b, { // loop { - - let client_connected_consumed = ClientConnectedConsumed { + let uart_enabled_consumed = UartEnabledConsumed { rng: s.rng, uart_buff: s.uart_buff, ssh_server: s.ssh_server, @@ -602,15 +680,15 @@ where }; println!("Setting up serial bridge"); - let bridge = bridge_wait_for_initialisation(client_connected_consumed); + let bridge = bridge_wait_for_initialisation(uart_enabled_consumed); - let client_connected_struct = ClientConnected { + let uart_enabled_struct = UartEnabled { ssh_server: s.ssh_server, bridge: bridge, connection_loop: s.connection_loop, tcp_socket: s.tcp_socket, }; - match bridge_connected(client_connected_struct).await { + match bridge_connected(uart_enabled_struct).await { Ok(_) => (), Err(e) => { println!("Bridge error: {}", e); @@ -624,7 +702,7 @@ where } async fn bridge_connected<'a, 'b, CL, BR>( - s: ClientConnected<'a, 'b, CL, BR>, + s: UartEnabled<'a, 'b, CL, BR>, ) -> Result<(), sunset::Error> where CL: Future>, diff --git a/src/serve.rs b/src/serve.rs index c390af2..9344fcb 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -2,20 +2,23 @@ use core::option::Option::{self, None, Some}; use core::result::Result; use core::writeln; +use crate::config::SSHStampConfig; use crate::keys; -use crate::pins::PinChannel; - +// use crate::pins::PinChannel; +use crate::storage; +use crate::storage::Fl; // Embassy use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; -// use sunset_async::SunsetMutex; +// use embedded_storage::Storage; +use sunset_async::SunsetMutex; use heapless::String; +// use sunset::sshwire::SSHEncode; use sunset::{error, ChanHandle, ServEvent, SignKey}; use sunset_async::{ProgressHolder, SSHServer}; -use esp_hal::system::software_reset; use esp_println::{dbg, println}; pub enum SessionType { @@ -26,15 +29,14 @@ pub enum SessionType { pub async fn connection_loop<'a>( serv: &SSHServer<'_>, chan_pipe: &Channel, - // pin_channel_ref: &'a SunsetMutex>, - // pin_channel_ref: &PinChannel<'a>, - pin_channel: PinChannel<'a>, + config: &'a SunsetMutex, + flash: &'a SunsetMutex, ) -> Result<(), sunset::Error> { let username = Mutex::::new(String::<20>::new()); let mut session: Option = None; println!("Entering connection_loop and prog_loop is next..."); - + let mut config_changed: bool = false; loop { let mut ph = ProgressHolder::new(); let ev = serv.progress(&mut ph).await?; @@ -43,8 +45,13 @@ pub async fn connection_loop<'a>( match ev { ServEvent::SessionShell(a) => { if let Some(ch) = session.take() { + // Save config after connection successful (SessionEnv completed) + if config_changed { + let config_guard = config.lock().await; + let mut flash_guard = flash.lock().await; + let _result = storage::save(&mut flash_guard, &config_guard).await; + } debug_assert!(ch.num() == a.channel()); - a.succeed()?; dbg!("We got shell"); let _ = chan_pipe.try_send(SessionType::Bridge(ch)); @@ -101,15 +108,10 @@ pub async fn connection_loop<'a>( let val = a.value()?; dbg!("Updating UART TX pin to ", val); if let Ok(pin_num) = val.parse::() { - // let mut ch = pin_channel_ref.lock().await; - // let mut ch: &mut PinChannel<'_> = pin_channel_ref; - let mut ch = pin_channel; - if ch.set_tx_pin(pin_num).is_err() { - dbg!("Failed to update TX pin"); - } else { - dbg!("TX pin updated"); - } - software_reset(); + let mut config_lock = config.lock().await; + config_lock.tx_pin = pin_num; + config_changed = true; + dbg!("TX pin updated"); } else { dbg!("Invalid TX pin value"); } @@ -118,14 +120,10 @@ pub async fn connection_loop<'a>( let val = a.value()?; dbg!("Updating UART RX pin to ", val); if let Ok(pin_num) = val.parse::() { - // let mut ch = pin_channel_ref.lock().await; - let mut ch = pin_channel; - if ch.set_rx_pin(pin_num).is_err() { - dbg!("Failed to update RX pin"); - } else { - dbg!("RX pin updated"); - } - software_reset(); + let mut config_lock = config.lock().await; + config_lock.rx_pin = pin_num; + config_changed = true; + dbg!("RX pin updated"); } else { dbg!("Invalid RX pin value"); } @@ -137,6 +135,7 @@ pub async fn connection_loop<'a>( // config.save(a): Potentially an optional special environment variable SAVE_CONFIG=1 // that serialises current config to flash + // Only save once all ENV requests have been recorded? a.succeed()?; } From 3c1db13d653e03bddf76fd63495672d21265e095 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:27:48 +1100 Subject: [PATCH 58/86] Merged https://github.com/brainstorm/ssh-stamp/commit/522ba5c15250bfd92b0572b6a050a30357519bc5 changes into HSM. Renamed storage.rs to config_storage.rs to resolve namespace conflict with storage workspace. config.rs: Made uart_pins rx & tx public. RNG, TIMG0 and RADIO_CLK not required for esp_radio::init(). References in main.rs removed. Serve.rs: Flash accessed from storage workspace. References in main.rs removed. Serve.rs: Merge Sftp SessionType and ServEvent::SessionSubsystem from https://github.com/jubeormk1/ssh-stamp/commit/694fbd4390b4cb092bf661cf08f8157120cdda51 --- .cargo/config.toml | 2 - Cargo.lock | 1107 +++++++++++++------------ Cargo.toml | 122 ++- src/config.rs | 71 +- src/{storage.rs => config_storage.rs} | 65 +- src/espressif/net.rs | 48 +- src/lib.rs | 3 +- src/main.rs | 154 ++-- src/serve.rs | 47 +- src/settings.rs | 5 + storage/Cargo.toml | 28 + storage/src/esp.rs | 12 + storage/src/lib.rs | 78 ++ 13 files changed, 954 insertions(+), 788 deletions(-) rename src/{storage.rs => config_storage.rs} (72%) create mode 100644 storage/Cargo.toml create mode 100644 storage/src/esp.rs create mode 100644 storage/src/lib.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 310ba2a..5a3765e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -48,8 +48,6 @@ ESP_LOG = "INFO" #ESP_HAL_CONFIG_STACK_GUARD_VALUE=3740121773 #ESP_HAL_CONFIG_IMPL_CRITICAL_SECTION=true - - [build] target = "riscv32imac-unknown-none-elf" diff --git a/Cargo.lock b/Cargo.lock index b09d8d7..29a15be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,21 +20,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78200ac3468a57d333cd0ea5dd398e25111194dcacd49208afca95c629a6311d" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anyhow" version = "1.0.98" @@ -53,6 +38,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.22.1" @@ -65,22 +56,13 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" -[[package]] -name = "basic-toml" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" -dependencies = [ - "serde", -] - [[package]] name = "bcrypt" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f" dependencies = [ - "base64", + "base64 0.22.1", "blowfish", "subtle", ] @@ -102,7 +84,7 @@ checksum = "3787a07661997bfc05dd3431e379c0188573f78857080cf682e1393ab8e4d64c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -136,17 +118,11 @@ dependencies = [ "cipher", ] -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" [[package]] name = "byteorder" @@ -154,15 +130,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "cc" -version = "1.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" -dependencies = [ - "shlex", -] - [[package]] name = "cfg-if" version = "1.0.1" @@ -180,18 +147,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "chrono" -version = "0.4.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "windows-link", -] - [[package]] name = "cipher" version = "0.4.4" @@ -204,10 +159,10 @@ dependencies = [ ] [[package]] -name = "core-foundation-sys" -version = "0.8.7" +name = "const-default" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" [[package]] name = "cpufeatures" @@ -268,7 +223,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -277,8 +232,18 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -292,7 +257,20 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.104", ] [[package]] @@ -301,9 +279,20 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.104", ] [[package]] @@ -314,7 +303,7 @@ checksum = "6178a82cf56c836a3ba61a7935cdb1c49bfaa6fa4327cd5bf554a503087de26b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -369,8 +358,8 @@ dependencies = [ "edge-nal", "edge-raw", "embassy-futures", - "embassy-time", - "heapless", + "embassy-time 0.4.0", + "heapless 0.8.0", "num_enum", "rand_core 0.6.4", ] @@ -381,21 +370,21 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac19c3edcdad839c71cb919cd09a632d9915d630760b37f0b74290188c08f248" dependencies = [ - "embassy-time", - "embedded-io-async", + "embassy-time 0.4.0", + "embedded-io-async 0.6.1", ] [[package]] name = "edge-nal-embassy" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252f89adf4f0016631977bec3ba50d768263a3a9fa9f023b4087088a619568ce" +checksum = "fb09ea0c604bbcb694ecf9cf6596bc5f59bbb5360b68c3e471b61ae9a55a8533" dependencies = [ "edge-nal", "embassy-futures", "embassy-net", - "embedded-io-async", - "heapless", + "embedded-io-async 0.6.1", + "heapless 0.8.0", ] [[package]] @@ -406,13 +395,13 @@ checksum = "6207c84e9bc8df8ef3c155196df290f2a51f010bd60c2e78366e51979988bdb5" [[package]] name = "embassy-embedded-hal" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fea5ef5bed4d3468dfd44f5c9fa4cda8f54c86d4fb4ae683eacf9d39e2ea12" +checksum = "554e3e840696f54b4c9afcf28a0f24da431c927f4151040020416e7393d6d0d8" dependencies = [ "embassy-futures", - "embassy-sync 0.6.2", - "embassy-time", + "embassy-hal-internal", + "embassy-sync 0.7.2", "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", @@ -423,32 +412,48 @@ dependencies = [ [[package]] name = "embassy-executor" -version = "0.7.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90327bcc66333a507f89ecc4e2d911b265c45f5c9bc241f98eee076752d35ac6" +checksum = "06070468370195e0e86f241c8e5004356d696590a678d47d6676795b2e439c6b" dependencies = [ "critical-section", "document-features", "embassy-executor-macros", + "embassy-executor-timer-queue", ] [[package]] name = "embassy-executor-macros" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf" +checksum = "dfdddc3a04226828316bf31393b6903ee162238576b1584ee2669af215d55472" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] +[[package]] +name = "embassy-executor-timer-queue" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc328bf943af66b80b98755db9106bf7e7471b0cf47dc8559cd9a6be504cc9c" + [[package]] name = "embassy-futures" -version = "0.1.1" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" + +[[package]] +name = "embassy-hal-internal" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" +checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a" +dependencies = [ + "num-traits", +] [[package]] name = "embassy-net" @@ -459,10 +464,10 @@ dependencies = [ "document-features", "embassy-net-driver", "embassy-sync 0.6.2", - "embassy-time", - "embedded-io-async", + "embassy-time 0.4.0", + "embedded-io-async 0.6.1", "embedded-nal-async", - "heapless", + "heapless 0.8.0", "managed", "smoltcp", ] @@ -481,24 +486,24 @@ checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049" dependencies = [ "cfg-if", "critical-section", - "embedded-io-async", + "embedded-io-async 0.6.1", "futures-sink", "futures-util", - "heapless", + "heapless 0.8.0", ] [[package]] name = "embassy-sync" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef1a8a1ea892f9b656de0295532ac5d8067e9830d49ec75076291fd6066b136" +checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" dependencies = [ "cfg-if", "critical-section", - "embedded-io-async", + "embedded-io-async 0.6.1", + "futures-core", "futures-sink", - "futures-util", - "heapless", + "heapless 0.8.0", ] [[package]] @@ -517,42 +522,58 @@ dependencies = [ "futures-util", ] +[[package]] +name = "embassy-time" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-core", +] + [[package]] name = "embassy-time-driver" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba" +checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" dependencies = [ "document-features", ] [[package]] name = "embassy-time-queue-utils" -version = "0.1.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc55c748d16908a65b166d09ce976575fb8852cf60ccd06174092b41064d8f83" +checksum = "80e2ee86063bd028a420a5fb5898c18c87a8898026da1d4c852af2c443d0a454" dependencies = [ - "embassy-executor", - "heapless", + "embassy-executor-timer-queue", + "heapless 0.8.0", ] [[package]] name = "embassy-usb-driver" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340c5ce591ef58c6449e43f51d2c53efe1bf0bb6a40cbf80afa0d259c7d52c76" +checksum = "17119855ccc2d1f7470a39756b12068454ae27a3eabb037d940b5c03d9c77b7a" dependencies = [ - "embedded-io-async", + "embedded-io-async 0.6.1", ] [[package]] name = "embassy-usb-synopsys-otg" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08e753b23799329780c7ac434264026d0422044d6649ed70a73441b14a6436d7" +checksum = "288751f8eaa44a5cf2613f13cee0ca8e06e6638cb96e897e6834702c79084b23" dependencies = [ "critical-section", - "embassy-sync 0.6.2", + "embassy-sync 0.7.2", "embassy-usb-driver", ] @@ -596,13 +617,28 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "embedded-io" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7" + [[package]] name = "embedded-io-async" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" dependencies = [ - "embedded-io", + "embedded-io 0.6.1", +] + +[[package]] +name = "embedded-io-async" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564b9f813c544241430e147d8bc454815ef9ac998878d30cc3055449f7fd4c0" +dependencies = [ + "embedded-io 0.7.1", ] [[package]] @@ -620,7 +656,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76959917cd2b86f40a98c28dd5624eddd1fa69d746241c8257eac428d83cb211" dependencies = [ - "embedded-io-async", + "embedded-io-async 0.6.1", "embedded-nal", ] @@ -639,18 +675,6 @@ 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.7" @@ -666,10 +690,10 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d07902c93376f1e96c34abc4d507c0911df3816cef50b01f5a2ff3ad8c370d" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -680,76 +704,73 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "esp-alloc" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e95f1de57ce5a6600368f3d3c931b0dfe00501661e96f5ab83bc5cdee031784" +checksum = "641e43d6a60244429117ef2fa7a47182120c7561336ea01f6fb08d634f46bae1" dependencies = [ "allocator-api2", "cfg-if", - "critical-section", "document-features", "enumset", + "esp-config", + "esp-sync", "linked_list_allocator", + "rlsf", ] [[package]] name = "esp-backtrace" -version = "0.16.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c304bbe17df32db8bc0027a9da989aa3efebbd4e7a79d58850deb29e2af577f" +checksum = "3318413fb566c7227387f67736cf70cd74d80a11f2bb31c7b95a9eb48d079669" dependencies = [ "cfg-if", - "esp-build", + "document-features", "esp-config", - "esp-metadata", + "esp-metadata-generated", "esp-println", - "heapless", + "heapless 0.9.2", + "riscv", "semihosting", + "xtensa-lx", ] [[package]] name = "esp-bootloader-esp-idf" -version = "0.1.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3cb352a8df9c459d0bdf06957cb8293b8bc574138e8c546949955b29c485769" +checksum = "02a56964ab5479ac20c9cf76fa3b0d3f2233b20b5d8554e81ef5d65f63c20567" dependencies = [ - "chrono", + "cfg-if", "document-features", "embedded-storage", "esp-config", - "md-5", + "esp-hal-procmacros", + "esp-metadata-generated", + "esp-rom-sys", + "jiff", "strum", ] -[[package]] -name = "esp-build" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "837020ff95fbf4c15c206541dda7994f1bbe6e1505e36a6a5ecb51fdb61656d7" -dependencies = [ - "quote", - "syn", - "termcolor", -] - [[package]] name = "esp-config" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8c4c95d8d6243ddb39efe1fcf2524c9becd0f86bb3e24048ed30b4f553609f" +checksum = "102871054f8dd98202177b9890cb4b71d0c6fe1f1413b7a379a8e0841fc2473c" dependencies = [ "document-features", + "esp-metadata-generated", "serde", - "serde_json", + "serde_yaml", + "somni-expr", ] [[package]] name = "esp-hal" -version = "1.0.0-beta.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d973697621cd3eef9c3f260fa8c1af77d8547cfc92734255d8e8ddf05c7d331" +checksum = "54786287c0a61ca0f78cb0c338a39427551d1be229103b4444591796c579e093" dependencies = [ - "basic-toml", "bitfield", "bitflags 2.9.1", "bytemuck", @@ -760,36 +781,42 @@ dependencies = [ "document-features", "embassy-embedded-hal", "embassy-futures", - "embassy-sync 0.6.2", + "embassy-sync 0.7.2", "embassy-usb-driver", "embassy-usb-synopsys-otg", "embedded-can", "embedded-hal 1.0.0", "embedded-hal-async", - "embedded-io", - "embedded-io-async", + "embedded-io 0.6.1", + "embedded-io 0.7.1", + "embedded-io-async 0.6.1", + "embedded-io-async 0.7.0", "enumset", - "esp-build", "esp-config", "esp-hal-procmacros", - "esp-metadata", + "esp-metadata-generated", "esp-riscv-rt", + "esp-rom-sys", + "esp-sync", "esp-synopsys-usb-otg", "esp32", "esp32c2", "esp32c3", "esp32c6", + "esp32h2", "esp32s2", "esp32s3", "fugit", "instability", + "log", "nb 1.1.0", "paste", "portable-atomic", "rand_core 0.6.4", "rand_core 0.9.3", "riscv", - "serde", + "sha1", + "sha2", "strum", "ufmt-write", "xtensa-lx", @@ -797,93 +824,162 @@ dependencies = [ ] [[package]] -name = "esp-hal-embassy" -version = "0.8.1" +name = "esp-hal-procmacros" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e025a7a7a0affdb4ff913b5c4494aef96ee03d085bf83c27453ae3a71d50da6" +dependencies = [ + "document-features", + "object", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", + "termcolor", +] + +[[package]] +name = "esp-metadata-generated" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a93e39c8ad8d390d248dc7b9f4b59a873f313bf535218b8e2351356972399e3" + +[[package]] +name = "esp-phy" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87807cdf22124ba46dbac7d7f6d3ee2e7f06c4e461f3783443464d868101647d" +checksum = "6b1facf348e1e251517278fc0f5dc134e95e518251f5796cfbb532ca226a29bf" dependencies = [ "cfg-if", - "critical-section", "document-features", - "embassy-executor", - "embassy-sync 0.6.2", - "embassy-time", - "embassy-time-driver", - "embassy-time-queue-utils", - "esp-build", + "esp-config", + "esp-hal", + "esp-metadata-generated", + "esp-sync", + "esp-wifi-sys", +] + +[[package]] +name = "esp-println" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a30e6c9fbcc01c348d46706fef8131c7775ab84c254a3cd65d0cd3f6414d592" +dependencies = [ + "document-features", + "esp-metadata-generated", + "esp-sync", + "log", + "portable-atomic", +] + +[[package]] +name = "esp-radio" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684c4de2f8907b73c9b891fbda65286a86d34fced4b856f36a7896c211f2f265" +dependencies = [ + "allocator-api2", + "cfg-if", + "document-features", + "embassy-net-driver", + "enumset", + "esp-alloc", "esp-config", "esp-hal", "esp-hal-procmacros", - "esp-metadata", + "esp-metadata-generated", + "esp-phy", + "esp-radio-rtos-driver", + "esp-sync", + "esp-wifi-sys", + "heapless 0.9.2", + "instability", + "num-derive", + "num-traits", "portable-atomic", - "static_cell", + "portable_atomic_enum", + "xtensa-lx-rt", ] [[package]] -name = "esp-hal-procmacros" -version = "0.18.0" +name = "esp-radio-rtos-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "543bc31d1851afd062357e7810c1a9633f282fd3993583499a841ab497cbca6c" + +[[package]] +name = "esp-riscv-rt" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73164008cb2eada2ef85e6b0e459001d851f9b8e65e96e0d594bdfa8cf1b813b" +checksum = "502744a5b1e7268d27fd2a4e56ad45efe42ead517d6c517a6961540de949b0ee" dependencies = [ - "darling", "document-features", - "litrs", - "object", - "proc-macro-crate", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn", + "riscv", + "riscv-rt", ] [[package]] -name = "esp-metadata" -version = "0.7.0" +name = "esp-rom-sys" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0154d59933c2419ef25a01938517cc6969f47b6af53ebb34c279393aa20d9654" +checksum = "cd66cccc6dd2d13e9f33668a57717ab14a6d217180ec112e6be533de93e7ecbf" dependencies = [ - "anyhow", - "basic-toml", - "serde", - "strum", + "cfg-if", + "document-features", + "esp-metadata-generated", ] [[package]] -name = "esp-println" -version = "0.14.0" +name = "esp-rtos" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fae8b38d5fdc1d29d823c4737f18edfb0ccf0406985cf893f87c0cfc26a6ab33" +checksum = "162ec711c8d06e79c67b75d01595539e86b0aac209643af98ca87a12250428b3" dependencies = [ - "critical-section", + "allocator-api2", + "cfg-if", "document-features", - "esp-build", - "esp-metadata", + "embassy-executor", + "embassy-sync 0.7.2", + "embassy-time-driver", + "embassy-time-queue-utils", + "esp-config", + "esp-hal", + "esp-hal-procmacros", + "esp-metadata-generated", + "esp-radio-rtos-driver", + "esp-sync", "log", "portable-atomic", ] [[package]] -name = "esp-riscv-rt" -version = "0.11.0" +name = "esp-storage" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05c2badd16cbd6307d463090615332b77c17a6766b41ba5fe5bb783310e8af6" +checksum = "1495fc1f5549bdd840b52d9ceb201746200e1620d2636f46958c11e765623b80" dependencies = [ "document-features", - "riscv", - "riscv-rt-macros", + "embedded-storage", + "esp-hal", + "esp-hal-procmacros", + "esp-metadata-generated", + "esp-rom-sys", + "esp-sync", ] [[package]] -name = "esp-storage" -version = "0.6.0" +name = "esp-sync" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6502654e5750e8162e66512d7e8d84f7eb51d17ec6aa7e0da95cd102769425" +checksum = "d44974639b4e88914f83fe60d2832c00276657d7d857628fdfc966cc7302e8a8" dependencies = [ - "critical-section", + "cfg-if", "document-features", - "embedded-storage", - "esp-build", - "esp-metadata", + "embassy-sync 0.6.2", + "embassy-sync 0.7.2", + "esp-metadata-generated", + "riscv", + "xtensa-lx", ] [[package]] @@ -899,48 +995,20 @@ dependencies = [ "vcell", ] -[[package]] -name = "esp-wifi" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3700028d3d2ee57e6d2a5c5f60544711052f8d394e73a6f534b538fbfb9d058c" -dependencies = [ - "allocator-api2", - "cfg-if", - "critical-section", - "document-features", - "embassy-net-driver", - "embedded-io", - "embedded-io-async", - "enumset", - "esp-alloc", - "esp-build", - "esp-config", - "esp-hal", - "esp-metadata", - "esp-wifi-sys", - "num-derive", - "num-traits", - "portable-atomic", - "portable_atomic_enum", - "rand_core 0.9.3", - "xtensa-lx-rt", -] - [[package]] name = "esp-wifi-sys" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b5438361891c431970194a733415006fb3d00b6eb70b3dcb66fd58f04d9b39" +checksum = "89b6544f6f0cb86169d1f93ba2101a8d50358a040c5043676ed86b793e09b12c" dependencies = [ "anyhow", ] [[package]] name = "esp32" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83ddfdb413ef0739d84bd3fce771e6e0feb6457ac1bcdd7a1dcd6326061e919d" +checksum = "b76170a463d18f888a1ad258031901036fd827a9ef126733053ba5f8739fb0c8" dependencies = [ "critical-section", "vcell", @@ -948,9 +1016,9 @@ dependencies = [ [[package]] name = "esp32c2" -version = "0.26.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14ec669dc34787e73c8a7d99059fcdbd800cb0699c2ea9cd0a3d99cb5e9caefb" +checksum = "e62cf8932966b8d445b6f1832977b468178f0a84effb2e9fda89f60c24d45aa3" dependencies = [ "critical-section", "vcell", @@ -958,9 +1026,9 @@ dependencies = [ [[package]] name = "esp32c3" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c0b2a8e8efa1755a28ca3ef54c85436f76531ba93dc79b55d5330349d067d7" +checksum = "356af3771d0d6536c735bf71136594f4d1cbb506abf6e0c51a6639e9bf4e7988" dependencies = [ "critical-section", "vcell", @@ -968,9 +1036,19 @@ dependencies = [ [[package]] name = "esp32c6" -version = "0.20.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8009092b2a8f41532ce836ea7403b657eca7cd396c4513c650ade648831ed76" +checksum = "8f5e511df672d79cd63365c92045135e01ba952b6bddd25b660baff5e1110f6b" +dependencies = [ + "critical-section", + "vcell", +] + +[[package]] +name = "esp32h2" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed4a50bbd1380931e095e0973b9b12f782a9c481f2edf1f7c42e7eb4ff736d6d" dependencies = [ "critical-section", "vcell", @@ -978,9 +1056,9 @@ dependencies = [ [[package]] name = "esp32s2" -version = "0.28.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4391e38b14eb59a675e816598bde7d9b8c8f43a695a284bd97bcd0cf3092404" +checksum = "98574d4c577fbe888fe3e6df7fc80d25a05624d9998f7d7de1500ae21fcca78f" dependencies = [ "critical-section", "vcell", @@ -988,9 +1066,9 @@ dependencies = [ [[package]] name = "esp32s3" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a4c6fd31207a297fc29d2b8f4da27facf45f8c83041f7c0f978aa65ab367c9" +checksum = "1810d8ee4845ef87542af981e38eb80ab531d0ef1061e1486014ab7af74c337a" dependencies = [ "critical-section", "vcell", @@ -1085,15 +1163,25 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] [[package]] name = "heapless" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" dependencies = [ "hash32", "stable_deref_trait", @@ -1120,30 +1208,6 @@ dependencies = [ "digest", ] -[[package]] -name = "iana-time-zone" -version = "0.1.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1152,9 +1216,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown", @@ -1177,15 +1241,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.7" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" dependencies = [ - "darling", + "darling 0.23.0", "indoc", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1195,13 +1259,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] -name = "js-sys" -version = "0.3.77" +name = "jiff" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "d89a5b5e10d5a9ad6e5d1f4bd58225f655d6fe9767575a5e8ac5a6fe64e04495" dependencies = [ - "once_cell", - "wasm-bindgen", + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff7a39c8862fc1369215ccf0a8f12dd4598c7f6484704359f0351bd617034dbf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] @@ -1221,9 +1299,6 @@ name = "litrs" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" -dependencies = [ - "proc-macro2", -] [[package]] name = "log" @@ -1237,31 +1312,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" -[[package]] -name = "minijinja" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e60ac08614cc09062820e51d5d94c2fce16b94ea4e5003bb81b99a95f84e876" -dependencies = [ - "serde", -] - [[package]] name = "nb" version = "0.1.3" @@ -1285,7 +1341,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1315,14 +1371,14 @@ checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -1332,6 +1388,10 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "opaque-debug" @@ -1383,6 +1443,15 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +[[package]] +name = "portable-atomic-util" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +dependencies = [ + "portable-atomic", +] + [[package]] name = "portable_atomic_enum" version = "0.3.1" @@ -1401,7 +1470,7 @@ checksum = "a33fa6ec7f2047f572d49317cca19c87195de99c6e5b6ee492da701cfe02b053" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1412,35 +1481,13 @@ checksum = "bbc83ee4a840062f368f9096d80077a9841ec117e17e7f700df81958f1451254" [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" 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" @@ -1459,12 +1506,6 @@ 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" @@ -1488,9 +1529,9 @@ checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" [[package]] name = "riscv" -version = "0.12.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7" +checksum = "b05cfa3f7b30c84536a9025150d44d26b8e1cc20ddf436448d74cd9591eefb25" dependencies = [ "critical-section", "embedded-hal 1.0.0", @@ -1501,13 +1542,13 @@ dependencies = [ [[package]] name = "riscv-macros" -version = "0.1.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25" +checksum = "7d323d13972c1b104aa036bc692cd08b822c8bbf23d79a27c526095856499799" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1516,15 +1557,46 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436" +[[package]] +name = "riscv-rt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d07b9f3a0eff773fc4df11f44ada4fa302e529bff4b7fe7e6a4b98a65ce9174" +dependencies = [ + "riscv", + "riscv-pac", + "riscv-rt-macros", + "riscv-target-parser", +] + [[package]] name = "riscv-rt-macros" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc71814687c45ba4cd1e47a54e03a2dbc62ca3667098fbae9cc6b423956758fa" +checksum = "def519ddeeb5e43c2b4fc3952c27b3a86782fc05192f322b2309125cd85b1fc3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "riscv-target-parser" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1376b15f3ff160e9b1e8ea564ce427f2f6fcf77528cc0a8bf405cb476f9cea7" + +[[package]] +name = "rlsf" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1646a59a9734b8b7a0ac51689388a60fe1625d4b956348e9de07591a1478457a" +dependencies = [ + "cfg-if", + "const-default", + "libc", + "rustversion", + "svgbobdoc", ] [[package]] @@ -1562,43 +1634,56 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] -name = "serde_json" -version = "1.0.140" +name = "serde_yaml" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ + "indexmap", "itoa", - "memchr", "ryu", "serde", + "unsafe-libyaml", ] [[package]] -name = "serde_spanned" -version = "0.6.9" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "serde", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] @@ -1612,12 +1697,6 @@ dependencies = [ "digest", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "signature" version = "2.2.0" @@ -1633,7 +1712,7 @@ dependencies = [ "bitflags 1.3.2", "byteorder", "cfg-if", - "heapless", + "heapless 0.8.0", "managed", ] @@ -1655,9 +1734,24 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "somni-expr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed9b7648d5e8b2df6c5e49940c54bcdd2b4dd71eafc6e8f1c714eb4581b0f53" +dependencies = [ + "somni-parser", ] +[[package]] +name = "somni-parser" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f368519fc6c85fc1afdb769fb5a51123f6158013e143656e25a3485a0d401c" + [[package]] name = "ssh-cipher" version = "0.2.0" @@ -1697,7 +1791,7 @@ dependencies = [ [[package]] name = "ssh-stamp" -version = "0.1.0" +version = "0.2.0" dependencies = [ "bcrypt", "cfg-if", @@ -1710,21 +1804,22 @@ dependencies = [ "embassy-executor", "embassy-futures", "embassy-net", - "embassy-sync 0.7.0", - "embassy-time", - "embedded-io-async", + "embassy-sync 0.7.2", + "embassy-time 0.5.0", + "embedded-io-async 0.6.1", "embedded-storage", "embedded-storage-async", "esp-alloc", "esp-backtrace", "esp-bootloader-esp-idf", "esp-hal", - "esp-hal-embassy", "esp-println", + "esp-radio", + "esp-rom-sys", + "esp-rtos", "esp-storage", - "esp-wifi", "getrandom", - "heapless", + "heapless 0.8.0", "hex", "hmac", "log", @@ -1736,6 +1831,7 @@ dependencies = [ "snafu", "ssh-key", "static_cell", + "storage", "subtle", "sunset", "sunset-async", @@ -1757,6 +1853,19 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "storage" +version = "0.1.0" +dependencies = [ + "embedded-storage", + "esp-bootloader-esp-idf", + "esp-hal", + "esp-storage", + "log", + "once_cell", + "sunset-async", +] + [[package]] name = "strsim" version = "0.11.1" @@ -1782,7 +1891,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.104", ] [[package]] @@ -1794,8 +1903,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sunset" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fae1be9253047b033ccbedf6da75657b1c0f83e99e3c7953cbccf064e534bb10" +source = "git+https://github.com/mkj/sunset#6b4e77f5210fd375f7ba7bf49940d7f1ac80c230" dependencies = [ "aes", "ascii", @@ -1805,9 +1913,9 @@ dependencies = [ "curve25519-dalek", "digest", "ed25519-dalek", - "embedded-io", + "embedded-io 0.6.1", "getrandom", - "heapless", + "heapless 0.8.0", "hmac", "log", "poly1305", @@ -1826,12 +1934,11 @@ dependencies = [ [[package]] name = "sunset-async" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38504564d099a37a2bf3ef6a7c5ae4f268fadf2e30819c59fc736bf4a6c7382" +source = "git+https://github.com/mkj/sunset#6b4e77f5210fd375f7ba7bf49940d7f1ac80c230" dependencies = [ "embassy-futures", - "embassy-sync 0.7.0", - "embedded-io-async", + "embassy-sync 0.7.2", + "embedded-io-async 0.6.1", "log", "portable-atomic", "sunset", @@ -1840,12 +1947,35 @@ dependencies = [ [[package]] name = "sunset-sshwire-derive" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe2cd7ddcc98aed78da9ed97bcd2630b761f1ea4377d9922dc4097f76a8bdff" +source = "git+https://github.com/mkj/sunset#6b4e77f5210fd375f7ba7bf49940d7f1ac80c230" dependencies = [ "virtue", ] +[[package]] +name = "svgbobdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" +dependencies = [ + "base64 0.13.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-width", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.104" @@ -1866,46 +1996,35 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", - "serde", - "serde_spanned", "toml_datetime", - "toml_write", + "toml_parser", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_parser" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] [[package]] name = "typenum" @@ -1925,6 +2044,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "universal-hash" version = "0.5.1" @@ -1935,13 +2060,19 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "usb-device" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" dependencies = [ - "heapless", + "heapless 0.8.0", "portable-atomic", ] @@ -1975,64 +2106,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - [[package]] name = "winapi-util" version = "0.1.9" @@ -2042,65 +2115,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.59.0" @@ -2176,9 +2190,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -2196,42 +2210,33 @@ dependencies = [ [[package]] name = "xtensa-lx" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68737a6c8f32ddcd97476acf68ddc6d411697fd94f64a601af16854b74967dff" +checksum = "e012d667b0aa6d2592ace8ef145a98bff3e76cca7a644f4181ecd7a916ed289b" dependencies = [ "critical-section", - "document-features", ] [[package]] name = "xtensa-lx-rt" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235815f34d1bf9c2f9c07917e2b63efbcab5ca5ce9d8faddb97b7105eed1ade3" +checksum = "8709f037fb123fe7ff146d2bce86f9dc0dfc53045c016bfd9d703317b6502845" dependencies = [ - "anyhow", "document-features", - "enum-as-inner", - "minijinja", - "r0", - "serde", - "strum", - "toml", "xtensa-lx", "xtensa-lx-rt-proc-macros", ] [[package]] name = "xtensa-lx-rt-proc-macros" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1ab67b22f0576b953a25c43bdfed0ff84af2e01ced85e95c29e7bac6bf2180" +checksum = "96fb42cd29c42f8744c74276e9f5bee7b06685bbe5b88df891516d72cb320450" dependencies = [ - "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -2251,5 +2256,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] diff --git a/Cargo.toml b/Cargo.toml index 469f4b4..9902f07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,46 +1,75 @@ [package] name = "ssh-stamp" -version = "0.1.0" -authors = ["Roman Valls Guimera "] +version = "0.2.0" +authors = [ "Julio Beltran Ortega ", + "Anthony Tambasco ", + "Roman Valls Guimera "] edition = "2021" license = "MIT OR Apache-2.0" +[workspace] +members = ["storage"] + +[workspace.dependencies] +sha2 = { version = "0.10", default-features = false } +log = "0.4" + +esp-hal = { version = "1.0.0", features = ["unstable", "log-04"] } +embedded-storage = "0.3.1" +esp-storage = { version = "0.8.0" } +esp-bootloader-esp-idf = { version = "0.4.0" } + +sunset = { git = "https://github.com/mkj/sunset", default-features = false, features = ["openssh-key", "embedded-io"]} +sunset-async = { git = "https://github.com/mkj/sunset", default-features = false, features = ["multi-thread"]} +sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", default-features = false } +# sunset = { path = "../sunset", default-features = false, features = ["openssh-key", "embedded-io"]} +# sunset-async = { path = "../sunset/async", default-features = false, features = ["multi-thread"]} +# sunset-sshwire-derive = { path = "../sunset/sshwire-derive", default-features = false } + +static_cell = { version = "2" } +# Used for storage::flash singleton +once_cell = { version = "1", features = [ + "critical-section", +], default-features = false } + + [dependencies] +storage = { path = "storage" } cfg-if = "1" ed25519-dalek = { version = "2", default-features = false } -embassy-executor = { version = "0.7"} +embassy-executor = { version = "0.9"} embassy-net = { version = "0.7", features = ["tcp", "udp", "dhcpv4", "medium-ethernet"] } smoltcp = { version = "0.12", default-features = false, features = ["medium-ethernet", "socket-raw"]} -embassy-time = { version = "0.4" } +embassy-time = { version = "0.5" } embedded-io-async = "0.6" -esp-alloc = "0.8" -esp-backtrace = { version = "0.16", features = [ - "exception-handler", +esp-alloc = "0.9" +esp-backtrace = { version = "0.18", features = [ "panic-handler", "println", ] } -esp-hal = { version = "1.0.0-beta.1", features = [ "unstable" ] } -esp-hal-embassy = { version = "0.8"} -esp-println = { version = "0.14", features = ["log-04"]} -esp-wifi = { version = "0.14", features = ["wifi", "esp-alloc"] } +esp-hal = { version = "1.0.0", features = [ "unstable" ] } +#esp-hal-embassy = { version = "0.9" } +esp-println = { version = "0.16", features = ["log-04"]} +esp-radio = { version = "0.17", features = ["wifi"] } +esp-rtos = { version = "0.2.0", features = ["embassy", "log-04", "esp-radio"] } hex = { version = "0.4", default-features = false } log = { version = "0.4" } static_cell = { version = "2", features = ["nightly"] } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } -sunset = { version = "0.4.0" , default-features = false, features = ["openssh-key", "embedded-io"]} -sunset-async = { version = "0.4.0", default-features = false, features = ["multi-thread"]} -sunset-sshwire-derive = { version = "0.2.1", default-features = false } +sunset = { workspace = true } +sunset-async = { workspace = true } +sunset-sshwire-derive = { workspace = true } getrandom = { version = "0.2.10", features = ["custom"] } embassy-sync = "0.7" heapless = "0.8" embassy-futures = "0.1" edge-dhcp = "0.6" edge-nal = "0.5" -edge-nal-embassy = "0.6" +edge-nal-embassy = "0.7" #sequential-storage = { version = "4", features = ["heapless"] } -esp-storage = { version = "0.6" } -embedded-storage = "0.3.1" -embassy-embedded-hal = "0.3" +esp-storage = { version = "0.8" } +embedded-storage = "0.3" +embassy-embedded-hal = "0.5" bcrypt = { version = "0.17", default-features = false } subtle = { version = "2", default-features = false } hmac = { version = "0.12", default-features = false } @@ -48,10 +77,11 @@ sha2 = { version = "0.10", default-features = false } digest = { version = "0.10", default-features = false, features = ["rand_core", "subtle"] } embedded-storage-async = "0.4" portable-atomic = "1" -esp-bootloader-esp-idf = "0.1" -snafu = { version = "0.8.6", default-features = false } +esp-bootloader-esp-idf = { version = "0.4", features = ["esp-rom-sys"] } +snafu = { version = "0.8", default-features = false } paste = "1" pretty-hex = { version = "0.4", default-features = false } +esp-rom-sys = { version = "0.1" } [profile.dev] # Rust debug is too slow. @@ -76,7 +106,7 @@ opt-level = "s" # Optimize for size. [profile.dev.package.esp-storage] opt-level = "s" -[profile.dev.package.esp-wifi] +[profile.dev.package.esp-radio] opt-level = "s" [features] @@ -86,34 +116,40 @@ default = ["esp32c6"] esp32 = [ "esp-hal/esp32", "esp-backtrace/esp32", - "esp-wifi/esp32", - "esp-hal-embassy/esp32", + "esp-radio/esp32", + "esp-rtos/esp32", + "esp-alloc/esp32", + "esp-rom-sys/esp32", + "esp-bootloader-esp-idf/esp32", "esp-println/esp32", "esp-storage/esp32", - "embassy-executor/task-arena-size-40960", ] esp32c2 = [ "esp-hal/esp32c2", "esp-backtrace/esp32c2", - "esp-wifi/esp32c2", - "esp-hal-embassy/esp32c2", + "esp-radio/esp32c2", + "esp-rtos/esp32c2", + "esp-alloc/esp32c2", + "esp-rom-sys/esp32c2", + "esp-bootloader-esp-idf/esp32c2", "esp-println/esp32c2", "esp-storage/esp32c2", - "embassy-executor/task-arena-size-40960", ] esp32c3 = [ "esp-hal/esp32c3", "esp-backtrace/esp32c3", - "esp-wifi/esp32c3", - "esp-hal-embassy/esp32c3", + "esp-radio/esp32c3", + "esp-rtos/esp32c3", + "esp-alloc/esp32c3", + "esp-rom-sys/esp32c3", + "esp-bootloader-esp-idf/esp32c3", "esp-println/esp32c3", "esp-storage/esp32c3", - "embassy-executor/task-arena-size-40960", ] #esp32c5 = [ # "esp-hal/esp32c5", # "esp-backtrace/esp32c5", -# "esp-wifi/esp32c5", +# "esp-radio/esp32c5", # "esp-hal-embassy/esp32c5", # "esp-println/esp32c5", # "esp-storage/esp32c5", @@ -122,27 +158,33 @@ esp32c3 = [ esp32c6 = [ "esp-hal/esp32c6", "esp-backtrace/esp32c6", - "esp-wifi/esp32c6", - "esp-hal-embassy/esp32c6", + "esp-radio/esp32c6", + "esp-rtos/esp32c6", + "esp-alloc/esp32c6", + "esp-rom-sys/esp32c6", + "esp-bootloader-esp-idf/esp32c6", "esp-println/esp32c6", "esp-storage/esp32c6", - "embassy-executor/task-arena-size-40960", ] esp32s2 = [ "esp-hal/esp32s2", "esp-backtrace/esp32s2", - "esp-wifi/esp32s2", - "esp-hal-embassy/esp32s2", + "esp-radio/esp32s2", + "esp-rtos/esp32s2", + "esp-alloc/esp32s2", + "esp-rom-sys/esp32s2", + "esp-bootloader-esp-idf/esp32s2", "esp-println/esp32s2", "esp-storage/esp32s2", - "embassy-executor/task-arena-size-32768", ] esp32s3 = [ "esp-hal/esp32s3", "esp-backtrace/esp32s3", - "esp-wifi/esp32s3", - "esp-hal-embassy/esp32s3", + "esp-radio/esp32s3", + "esp-rtos/esp32s3", + "esp-alloc/esp32s3", + "esp-rom-sys/esp32s3", + "esp-bootloader-esp-idf/esp32s3", "esp-println/esp32s3", "esp-storage/esp32s3", - "embassy-executor/task-arena-size-40960", ] diff --git a/src/config.rs b/src/config.rs index 8956d1e..135631d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ use core::net::Ipv6Addr; use embassy_net::{Ipv4Cidr, StaticConfigV4}; #[cfg(feature = "ipv6")] use embassy_net::{Ipv6Cidr, StaticConfigV6}; -use heapless::{String, Vec}; +use heapless::String; use esp_println::dbg; @@ -20,8 +20,7 @@ use sunset::{ SignKey, }; -use crate::pins::SerdePinConfig; -use crate::settings::{DEFAULT_SSID, KEY_SLOTS}; +use crate::settings::{DEFAULT_SSID, DEFAULT_UART_RX_PIN, DEFAULT_UART_TX_PIN, KEY_SLOTS}; #[derive(Debug, PartialEq)] pub struct SSHStampConfig { @@ -35,7 +34,7 @@ pub struct SSHStampConfig { /// WiFi pub wifi_ssid: String<32>, - pub wifi_pw: Option>, // TODO: Why not 64? + pub wifi_pw: Option>, // Max 64 characters including null-terminator? /// Networking /// TODO: Populate this field from esp's hardware info or just refer it from HAL? @@ -46,9 +45,23 @@ pub struct SSHStampConfig { #[cfg(feature = "ipv6")] pub ipv6_static: Option, /// UART - pub uart_pins: SerdePinConfig, - pub tx_pin: u8, - pub rx_pin: u8, + pub uart_pins: UartPins, +} + +#[derive(Debug, PartialEq)] +pub struct UartPins { + pub rx: u8, + pub tx: u8, +} + +impl Default for UartPins { + fn default() -> Self { + // sensible defaults for UART pins; adjust if your board uses different pins + UartPins { + rx: DEFAULT_UART_RX_PIN, + tx: DEFAULT_UART_TX_PIN, + } + } } impl SSHStampConfig { @@ -62,14 +75,11 @@ impl SSHStampConfig { let hostkey = SignKey::generate(KeyType::Ed25519, None)?; // TODO: Those env events come from system's std::env / core::env (if any)... so it shouldn't be unsafe() - let wifi_ssid_str = String::try_from(DEFAULT_SSID).unwrap(); - let wifi_ssid: String<32> = wifi_ssid_str.into(); + let wifi_ssid = Self::default_ssid(); let mac = random_mac()?; let wifi_pw = None; - let uart_pins = SerdePinConfig::default(); - let tx_pin = 10; - let rx_pin = 11; + let uart_pins = UartPins::default(); Ok(SSHStampConfig { hostkey, @@ -83,8 +93,6 @@ impl SSHStampConfig { #[cfg(feature = "ipv6")] ipv6_static: None, uart_pins, - tx_pin, - rx_pin, }) } @@ -101,6 +109,12 @@ impl SSHStampConfig { } } + pub(crate) fn default_ssid() -> String<32> { + let mut s = String::<32>::new(); + s.push_str(DEFAULT_SSID).unwrap(); + s + } + // pub fn config_change(&mut self, conf: SSHConfig) -> Result<()> { // ServEvent::ConfigChange(); // } @@ -145,6 +159,18 @@ where bool::dec(s)?.then(|| SSHDecode::dec(s)).transpose() } +// encode Option> as a bool then the &str contents (heapless::String doesn't implement SSHEncode) +pub(crate) fn enc_option_str( + v: &Option>, + s: &mut dyn SSHSink, +) -> WireResult<()> { + v.is_some().enc(s)?; + if let Some(ref st) = v { + st.as_str().enc(s)?; + } + Ok(()) +} + fn enc_ipv4_config(v: &Option, s: &mut dyn SSHSink) -> WireResult<()> { v.is_some().enc(s)?; if let Some(v) = v { @@ -188,7 +214,7 @@ where Ok(StaticConfigV4 { address: Ipv4Cidr::new(ad, prefix), gateway, - dns_servers: Vec::new(), + dns_servers: Default::default(), }) }) .transpose() @@ -232,15 +258,16 @@ impl SSHEncode for SSHStampConfig { } self.wifi_ssid.as_str().enc(s)?; - enc_option(&self.wifi_pw, s)?; + enc_option_str::<63>(&self.wifi_pw, s)?; self.mac.enc(s)?; enc_ipv4_config(&self.ipv4_static, s)?; #[cfg(feature = "ipv6")] enc_ipv6_config(&self.ipv6_static, s)?; - // Encode PinConfig - self.uart_pins.enc(s)?; + // Encode UartPins + self.uart_pins.rx.enc(s)?; + self.uart_pins.tx.enc(s)?; Ok(()) } @@ -272,9 +299,9 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { // Not supported by sshwire-derive nor virtue (no Option support) // let uart_pins = SSHDecode::dec(s)?; - let uart_pins = SSHDecode::dec(s)?; - let tx_pin = 10; - let rx_pin = 11; + let rx: u8 = SSHDecode::dec(s)?; + let tx: u8 = SSHDecode::dec(s)?; + let uart_pins = UartPins { rx, tx }; Ok(Self { hostkey, @@ -288,8 +315,6 @@ impl<'de> SSHDecode<'de> for SSHStampConfig { #[cfg(feature = "ipv6")] ipv6_static, uart_pins, - tx_pin, - rx_pin, }) } } diff --git a/src/storage.rs b/src/config_storage.rs similarity index 72% rename from src/storage.rs rename to src/config_storage.rs index fd0f7b5..e2cbedc 100644 --- a/src/storage.rs +++ b/src/config_storage.rs @@ -1,44 +1,27 @@ +use embedded_storage::nor_flash::NorFlash; use embedded_storage::ReadStorage; use esp_bootloader_esp_idf::partitions; use esp_println::{dbg, println}; -use esp_storage::FlashStorage; use pretty_hex::PrettyHex; use sha2::Digest; use core::borrow::Borrow; -use embedded_storage::nor_flash::NorFlash; - use crate::errors::Error as SSHStampError; use sunset::error::Error as SunsetError; +use crate::config::SSHStampConfig; +use storage::flash::FlashBuffer; + use sunset::sshwire::{self, OwnOrBorrow}; use sunset_sshwire_derive::*; -use crate::config::SSHStampConfig; - pub const CONFIG_VERSION_SIZE: usize = 4; pub const CONFIG_HASH_SIZE: usize = 32; pub const CONFIG_AREA_SIZE: usize = 4096; pub const CONFIG_OFFSET: usize = 0x9000; -pub struct Fl { - flash: FlashStorage, - // Only a single task can write to flash at a time, - // keeping a buffer here saves duplicated buffer space in each task. - buf: [u8; FlashConfig::BUF_SIZE], -} - -impl<'a> Fl { - pub fn new(flash: FlashStorage) -> Self { - Self { - flash, - buf: [0u8; FlashConfig::BUF_SIZE], - } - } -} - // SSHConfig::CURRENT_VERSION must be bumped if any of this struct #[derive(SSHEncode, SSHDecode)] struct FlashConfig<'a> { @@ -53,12 +36,17 @@ impl FlashConfig<'_> { // TODO: Rework Error mapping with esp_storage errors /// Finds the NVS partitions and retrieves information about it. - pub fn find_config_partition() -> Result<(), SSHStampError> { - let mut flash = FlashStorage::new(); - println!("Flash size = {} bytes", flash.capacity()); - - let mut pt_mem = [0u8; partitions::PARTITION_TABLE_MAX_LEN]; - let pt = partitions::read_partition_table(&mut flash, &mut pt_mem).unwrap(); + pub fn find_config_partition(fb: &mut FlashBuffer) -> Result<(), SSHStampError> { + println!("Flash size = {} Mb", fb.flash.capacity() / (1024 * 1024)); + println!("Flash storage : {:?}", fb.flash); + let pt = partitions::read_partition_table( + &mut fb.flash, + &mut fb.buf[..esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN], + ) + .map_err(|e| { + println!("Failed to read partition table: {:?}", e); + SSHStampError::FlashStorageError + })?; let nvs = pt .find_partition(partitions::PartitionType::Data( partitions::DataPartitionSubType::Nvs, @@ -66,7 +54,7 @@ impl FlashConfig<'_> { .unwrap() .unwrap(); - let nvs_partition = nvs.as_embedded_storage(&mut flash); + let nvs_partition = nvs.as_embedded_storage(&mut fb.flash); println!("NVS partition size = {}", nvs_partition.capacity()); println!("NVS partition offset = 0x{:x}", nvs.offset()); @@ -82,7 +70,7 @@ fn config_hash(config: &SSHStampConfig) -> Result<[u8; 32], SunsetError> { } /// Loads a SSHConfig at startup. Good for persisting hostkeys. -pub async fn load_or_create(flash: &mut Fl) -> Result { +pub async fn load_or_create(flash: &mut FlashBuffer<'_>) -> Result { match load(flash).await { Ok(c) => { println!("Good existing config"); @@ -94,7 +82,7 @@ pub async fn load_or_create(flash: &mut Fl) -> Result Result { +pub async fn create(flash: &mut FlashBuffer<'_>) -> Result { let c = SSHStampConfig::new()?; save(flash, &c).await?; dbg!("Created new config: ", &c); @@ -102,7 +90,7 @@ pub async fn create(flash: &mut Fl) -> Result { Ok(c) } -pub async fn load(fl: &mut Fl) -> Result { +pub async fn load(fl: &mut FlashBuffer<'_>) -> Result { fl.flash .read(CONFIG_OFFSET as u32, &mut fl.buf) .map_err(|_e| { @@ -132,14 +120,19 @@ pub async fn load(fl: &mut Fl) -> Result { } } -pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetError> { +pub async fn save(fl: &mut FlashBuffer<'_>, config: &SSHStampConfig) -> Result<(), SunsetError> { let sc = FlashConfig { version: SSHStampConfig::CURRENT_VERSION, - config: OwnOrBorrow::Borrow(&config), - hash: config_hash(&config)?, + config: OwnOrBorrow::Borrow(config), + hash: config_hash(config)?, }; - FlashConfig::find_config_partition().unwrap(); + let Ok(()) = FlashConfig::find_config_partition(fl) else { + dbg!("Failed to find NVS partition"); + return Err(SunsetError::Custom { + msg: "Failde to find NVS partition", + }); + }; // dbg!("Saving config: ", &config); dbg!("Before write_ssh, with hash: ", &sc.hash.hex_dump()); @@ -151,7 +144,7 @@ pub async fn save(fl: &mut Fl, config: &SSHStampConfig) -> Result<(), SunsetErro dbg!("Erasing flash"); - assert!(CONFIG_AREA_SIZE > FlashConfig::BUF_SIZE); + const { assert!(CONFIG_AREA_SIZE > FlashConfig::BUF_SIZE) }; fl.flash .erase( diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 04aaf16..153114c 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -1,7 +1,11 @@ +// SPDX-FileCopyrightText: 2025 Roman Valls, 2025 +// +// SPDX-License-Identifier: GPL-3.0-or-later + use core::net::Ipv4Addr; use core::str::FromStr; -use embassy_executor::Spawner; +// use embassy_executor::Spawner; use embassy_net::{tcp::TcpSocket, Stack, StackResources}; use embassy_net::{IpListenEndpoint, Ipv4Cidr, StaticConfigV4}; use embassy_time::{Duration, Timer}; @@ -11,9 +15,9 @@ use esp_hal::peripherals::WIFI; use esp_hal::rng::Rng; use esp_println::{dbg, println}; -use esp_wifi::wifi::{AccessPointConfiguration, Configuration, WifiController}; -use esp_wifi::wifi::{WifiEvent, WifiState}; -use esp_wifi::EspWifiController; +use esp_radio::wifi::WifiEvent; +use esp_radio::wifi::{AccessPointConfig, ModeConfig, WifiApState, WifiController}; +use esp_radio::Controller; // use sunset_async::SunsetMutex; use core::net::SocketAddrV4; @@ -27,6 +31,8 @@ use edge_nal::UdpBind; use edge_nal_embassy::{Udp, UdpBuffers}; // use crate::config::SSHStampConfig; +// use crate::settings::DEFAULT_SSID; +use crate::settings::{DEFAULT_IP, DEFAULT_SSID}; use super::buffered_uart::BufferedUart; @@ -43,18 +49,20 @@ macro_rules! mk_static { use heapless::String; pub async fn if_up<'a>( - _spawner: Spawner, - wifi_controller: EspWifiController<'a>, + wifi_controller: Controller<'a>, wifi: WIFI<'a>, rng: &mut Rng, // config: &'static SunsetMutex, wifi_ssid: String<32>, ) -> Result, sunset::Error> { - // let wifi_init = &*mk_static!(EspWifiController<'static>, wifi_controller); - // let (controller, interfaces) = esp_wifi::wifi::new(wifi_init, wifi).unwrap(); - let (controller, interfaces) = esp_wifi::wifi::new(&wifi_controller, wifi).unwrap(); + let (mut controller, interfaces) = + esp_radio::wifi::new(&wifi_controller, wifi, Default::default()).unwrap(); + let ap_config = + ModeConfig::AccessPoint(AccessPointConfig::default().with_ssid(DEFAULT_SSID.into())); + let res = controller.set_config(&ap_config); + println!("wifi_set_configuration returned {:?}", res); - let gw_ip_addr_ipv4 = Ipv4Addr::from_str("192.168.0.1").expect("failed to parse gateway ip"); + let gw_ip_addr_ipv4 = DEFAULT_IP.clone(); // let _gw_ip_addr = { // let guard = config.lock().await; @@ -104,12 +112,7 @@ pub async fn if_up<'a>( Ok(ap_stack) } -pub async fn accept_requests<'a>( - stack: Stack<'a>, - _uart: &BufferedUart, - // pin_channel_ref: &'a sunset_async::SunsetMutex>, - _pin_channel_ref: &crate::pins::PinChannel<'a>, -) -> ! { +pub async fn accept_requests<'a>(stack: Stack<'a>, _uart: &BufferedUart) -> ! { // let rx_buffer = mk_static!([u8; 1536], [0; 1536]); // let tx_buffer = mk_static!([u8; 1536], [0; 1536]); let mut rx_buffer = [0u8; 1536]; @@ -158,17 +161,18 @@ async fn wifi_up( //let wifi_password = config.lock().await.wifi_pw; loop { - if esp_wifi::wifi::wifi_state() == WifiState::ApStarted { + if esp_radio::wifi::ap_state() == WifiApState::Started { // wait until we're no longer connected controller.wait_for_event(WifiEvent::ApStop).await; Timer::after(Duration::from_millis(5000)).await } if !matches!(controller.is_started(), Ok(true)) { - let client_config = Configuration::AccessPoint(AccessPointConfiguration { - ssid: wifi_ssid.to_ascii_lowercase(), - ..Default::default() - }); - controller.set_configuration(&client_config).unwrap(); + let ssid_string = String::<63>::from_str(&wifi_ssid) + .unwrap() + .to_ascii_lowercase(); + let client_config = + ModeConfig::AccessPoint(AccessPointConfig::default().with_ssid(ssid_string)); + controller.set_config(&client_config).unwrap(); println!("Starting wifi"); controller.start_async().await.unwrap(); println!("Wifi started!"); diff --git a/src/lib.rs b/src/lib.rs index dae7da3..cf2cd8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,8 +6,7 @@ pub mod config; pub mod errors; pub mod espressif; pub mod keys; -pub mod pins; pub mod serial; pub mod serve; pub mod settings; -pub mod storage; +pub mod config_storage; diff --git a/src/main.rs b/src/main.rs index 9fa6715..2600776 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,10 @@ #![no_std] #![no_main] + +// SPDX-FileCopyrightText: 2025 Julio Beltran Ortega, Anthony Tambasco, Roman Valls Guimera, 2025 +// +// SPDX-License-Identifier: GPL-3.0-or-later + use core::result::Result; use core::result::Result::Err; use core::result::Result::Ok; @@ -9,22 +14,18 @@ use embassy_executor::Spawner; use embassy_futures::select::{select3, Either3}; use embassy_net::{tcp::TcpSocket, IpListenEndpoint, Stack}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; -use esp_hal::peripherals::UART1; use esp_hal::system::software_reset; use esp_hal::{ gpio::Pin, - peripherals::{GPIO10, GPIO11}, - // interrupt::{software::SoftwareInterruptControl, Priority}, - // peripherals::Peripherals, - // peripherals::UART1, - peripherals::{RADIO_CLK, SYSTIMER, TIMG0, TIMG1, WIFI}, + interrupt::software::SoftwareInterruptControl, + peripherals::{GPIO10, GPIO11, UART1}, + peripherals::{SW_INTERRUPT, SYSTIMER, TIMG1, WIFI}, rng::Rng, - timer::timg::TimerGroup, uart::{Config, RxConfig, Uart}, }; +use esp_println::dbg; use esp_println::println; -use esp_storage::FlashStorage; -use esp_wifi::{EspWifiController, InitializationError}; +use esp_radio::{Controller, InitializationError}; use heapless::String; use ssh_stamp::{ config::SSHStampConfig, @@ -34,10 +35,10 @@ use ssh_stamp::{ net::if_up, rng, }, - // pins::{GPIOConfig, PinChannel}, serve, - storage::Fl, }; +use storage::flash; + use static_cell::StaticCell; use sunset_async::{SSHServer, SunsetMutex}; @@ -46,31 +47,35 @@ static UART_BUF: StaticCell = StaticCell::new(); pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a> { let peripherals = esp_hal::init(esp_hal::Config::default()); - let rng = Rng::new(peripherals.RNG); + let rng = Rng::new(); rng::register_custom_rng(rng); - let timg0 = TimerGroup::new(peripherals.TIMG0); let timg1 = peripherals.TIMG1; let systimer = peripherals.SYSTIMER; - let radio_clock = peripherals.RADIO_CLK; // Read SSH configuration from Flash (if it exists) - let mut flash_storage = Fl::new(FlashStorage::new()); - let config = ssh_stamp::storage::load_or_create(&mut flash_storage).await; - - static FLASH: StaticCell> = StaticCell::new(); - let flash = FLASH.init(SunsetMutex::new(flash_storage)); + flash::init(peripherals.FLASH); + let config = { + let Some(flash_storage_guard) = flash::get_flash_n_buffer() else { + panic!("Could not acquire flash storage lock"); + }; + let mut flash_storage = flash_storage_guard.lock().await; + // TODO: Migrate this function/test to embedded-test. + // Quick roundtrip test for SSHStampConfig + // ssh_stamp::config::roundtrip_config(); + ssh_stamp::config_storage::load_or_create(&mut flash_storage).await + } + .expect("Could not load or create SSHStampConfig"); static CONFIG: StaticCell> = StaticCell::new(); - let config = CONFIG.init(SunsetMutex::new(config.unwrap())); + let config = CONFIG.init(SunsetMutex::new(config)); let wifi = peripherals.WIFI; let gpio10 = peripherals.GPIO10; let gpio11 = peripherals.GPIO11; let uart1 = peripherals.UART1; + let sw_interrupt = peripherals.SW_INTERRUPT; let ssh_stamp_peripherals = SshStampPeripherals { rng: rng, - timg0: timg0, - radio_clock: radio_clock, wifi: wifi, config: config, gpio10: gpio10, @@ -78,7 +83,7 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a uart1: uart1, timg1: timg1, systimer: systimer, - flash: flash, + sw_interrupt: sw_interrupt, }; ssh_stamp_peripherals } @@ -88,14 +93,8 @@ pub async fn peripherals_disable() -> () { software_reset(); } -pub async fn wifi_wait_for_initialisation( - s: PeripheralsEnabledConsumed<'_>, -) -> Result, InitializationError> { - let rng = s.rng; - let timg0 = s.timg0; - let radio_clock = s.radio_clock; - let wifi_controller: Result, InitializationError> = - esp_wifi::init(timg0.timer0, rng, radio_clock); +pub async fn wifi_wait_for_initialisation<'a>() -> Result, InitializationError> { + let wifi_controller: Result, InitializationError> = esp_radio::init(); wifi_controller } @@ -113,12 +112,11 @@ pub struct TcpStackReturn<'a> { pub tcp_stack: Stack<'a>, } pub async fn tcp_wait_for_initialisation<'a>(s: WifiEnabledConsumed<'a>) -> Stack<'a> { - let spawner: Spawner = s.spawner; let wifi_controller = s.wifi_controller; let wifi: WIFI = s.wifi; let mut rng = s.rng; let wifi_ssid = s.wifi_ssid; - let tcp_stack = if_up(spawner, wifi_controller, wifi, &mut rng, wifi_ssid) + let tcp_stack = if_up(wifi_controller, wifi, &mut rng, wifi_ssid) .await .unwrap(); tcp_stack @@ -166,11 +164,10 @@ pub async fn enable_uart<'a, 'b>( ) where 'a: 'b, { - // uart_task(uart_buf, uart1, pin_channel_ref - // dbg!("Spawning UART task..."); + dbg!("Configuring UART"); let config_lock = config.lock().await; - let rx: u8 = config_lock.rx_pin; - let tx: u8 = config_lock.tx_pin; + let rx: u8 = config_lock.uart_pins.rx; + let tx: u8 = config_lock.uart_pins.tx; if rx != tx { let mut holder10 = Some(gpio10); let mut holder11 = Some(gpio11); @@ -212,12 +209,11 @@ pub async fn idle_wait_for_connection<'a, 'b>( ssh_server: &'b SSHServer<'a>, chan_pipe: &Channel, config: &'a SunsetMutex, - flash: &'a SunsetMutex, ) -> Result<(), sunset::Error> where 'a: 'b, { - serve::connection_loop(ssh_server, chan_pipe, config, flash).await + serve::connection_loop(ssh_server, chan_pipe, config).await } pub async fn connection_disable() -> () { @@ -225,6 +221,7 @@ pub async fn connection_disable() -> () { software_reset(); } +use crate::serve::SessionType; use ssh_stamp::serial::serial_bridge; use sunset_async::ChanInOut; pub async fn bridge_wait_for_initialisation<'a, 'b>( @@ -239,10 +236,11 @@ pub async fn bridge_wait_for_initialisation<'a, 'b>( let stdio: ChanInOut<'_> = s.ssh_server.stdio(ch).await?; let stdio2 = stdio.clone(); serial_bridge(stdio, stdio2, s.uart_buff).await? - } // SessionType::Sftp(_ch) => { - // // Handle SFTP session - // todo!() - // } + } + SessionType::Sftp(_ch) => { + // Handle SFTP session + // todo!() + } }; Ok(()) }; @@ -256,8 +254,6 @@ pub async fn bridge_disable() -> () { pub struct SshStampPeripherals<'a> { pub rng: Rng, - pub timg0: TimerGroup<'a, TIMG0<'a>>, - pub radio_clock: RADIO_CLK<'a>, pub wifi: WIFI<'a>, pub config: &'a SunsetMutex, pub gpio10: GPIO10<'a>, @@ -265,27 +261,23 @@ pub struct SshStampPeripherals<'a> { pub uart1: UART1<'a>, pub timg1: TIMG1<'a>, pub systimer: SYSTIMER<'a>, - pub flash: &'a SunsetMutex, + pub sw_interrupt: SW_INTERRUPT<'a>, } pub struct SshStampInit<'a> { pub rng: Rng, - pub timg0: TimerGroup<'a, TIMG0<'a>>, - pub radio_clock: RADIO_CLK<'a>, pub wifi: WIFI<'a>, pub config: &'a SunsetMutex, - pub spawner: Spawner, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, pub uart1: UART1<'a>, - pub flash: &'a SunsetMutex, } -#[esp_hal_embassy::main] -async fn main(spawner: Spawner) -> ! { +#[esp_rtos::main] +async fn main(_spawner: Spawner) -> ! { cfg_if::cfg_if!( if #[cfg(feature = "esp32s2")] { - // TODO: This heap size will crash at runtime (only for the ESP32S2), we need to fix this + // TODO: This heap size will crash at runtime, we need to fix this // applying ideas from https://github.com/brainstorm/ssh-stamp/pull/41#issuecomment-2964775170 esp_alloc::heap_allocator!(size: 69 * 1024); } else { @@ -297,14 +289,16 @@ async fn main(spawner: Spawner) -> ! { let peripherals = peripherals_wait_for_initialisation().await; + let sw_int = SoftwareInterruptControl::new(peripherals.sw_interrupt); + cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { let timg1 = TimerGroup::new(peripherals.timg1); - esp_hal_embassy::init(timg1.timer0); + esp_rtos::start(timg1.timer0, sw_int); } else { use esp_hal::timer::systimer::SystemTimer; let systimer = SystemTimer::new(peripherals.systimer); - esp_hal_embassy::init(systimer.alarm0); + esp_rtos::start(systimer.alarm0, sw_int.software_interrupt0); } } @@ -314,15 +308,11 @@ async fn main(spawner: Spawner) -> ! { let peripherals_enabled_struct = SshStampInit { rng: peripherals.rng, - timg0: peripherals.timg0, - radio_clock: peripherals.radio_clock, wifi: peripherals.wifi, config: peripherals.config, - spawner: spawner, gpio10: peripherals.gpio10, gpio11: peripherals.gpio11, uart1: peripherals.uart1, - flash: peripherals.flash, }; match peripherals_enabled(peripherals_enabled_struct).await { @@ -337,40 +327,26 @@ async fn main(spawner: Spawner) -> ! { software_reset(); } -pub struct PeripheralsEnabledConsumed<'a> { - pub rng: Rng, - pub timg0: TimerGroup<'a, TIMG0<'a>>, - pub radio_clock: RADIO_CLK<'a>, -} pub struct PeripheralsEnabled<'a> { pub rng: Rng, pub wifi: WIFI<'a>, pub config: &'a SunsetMutex, - pub spawner: Spawner, - pub wifi_controller: EspWifiController<'a>, + pub wifi_controller: Controller<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, pub uart1: UART1<'a>, - pub flash: &'a SunsetMutex, } async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Error> { - let peripherals_enabled_consumed = PeripheralsEnabledConsumed { - rng: s.rng, - timg0: s.timg0, - radio_clock: s.radio_clock, - }; - let wifi_controller = wifi_wait_for_initialisation(peripherals_enabled_consumed).await; + let wifi_controller = wifi_wait_for_initialisation().await; let peripherals_enabled_struct = PeripheralsEnabled { rng: s.rng, wifi: s.wifi, config: s.config, - spawner: s.spawner, wifi_controller: wifi_controller.unwrap(), gpio10: s.gpio10, gpio11: s.gpio11, uart1: s.uart1, - flash: s.flash, }; match wifi_enabled(peripherals_enabled_struct).await { Ok(_) => (), @@ -386,19 +362,15 @@ async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Erro pub struct WifiEnabledConsumed<'a> { pub rng: Rng, pub wifi: WIFI<'a>, - // pub config: &'a SunsetMutex, pub wifi_ssid: String<32>, - pub spawner: Spawner, - pub wifi_controller: EspWifiController<'a>, + pub wifi_controller: Controller<'a>, } pub struct WifiEnabled<'a> { - pub rng: Rng, pub config: &'a SunsetMutex, pub tcp_stack: Stack<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, pub uart1: UART1<'a>, - pub flash: &'a SunsetMutex, } async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { let wifi_ssid_config = { @@ -408,20 +380,16 @@ async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error let wifi_enabled_consumed = WifiEnabledConsumed { rng: s.rng, wifi: s.wifi, - // config: s.config, wifi_ssid: wifi_ssid_config, - spawner: s.spawner, wifi_controller: s.wifi_controller, }; let tcp_stack = tcp_wait_for_initialisation(wifi_enabled_consumed).await; let wifi_enabled_struct = WifiEnabled { - rng: s.rng, config: s.config, tcp_stack: tcp_stack, gpio10: s.gpio10, gpio11: s.gpio11, uart1: s.uart1, - flash: s.flash, }; match tcp_enabled(wifi_enabled_struct).await { Ok(_) => (), @@ -434,13 +402,11 @@ async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error } pub struct TCPEnabled<'a> { - pub rng: Rng, pub config: &'a SunsetMutex, pub tcp_socket: TcpSocket<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, pub uart1: UART1<'a>, - pub flash: &'a SunsetMutex, } async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { let mut rx_buffer = [0u8; 1536]; @@ -460,13 +426,11 @@ async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { } println!("Connected, port 22"); let tcp_enabled_struct = TCPEnabled { - rng: s.rng, config: s.config, tcp_socket: tcp_socket, gpio10: s.gpio10, gpio11: s.gpio11, uart1: s.uart1, - flash: s.flash, }; match socket_enabled(tcp_enabled_struct).await { Ok(_) => (), @@ -479,14 +443,12 @@ async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { } pub struct SocketEnabled<'a> { - pub rng: Rng, pub config: &'a SunsetMutex, pub tcp_socket: TcpSocket<'a>, pub ssh_server: SSHServer<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, pub uart1: UART1<'a>, - pub flash: &'a SunsetMutex, } async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { // loop { @@ -495,14 +457,12 @@ async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { let mut outbuf = [0u8; UART_BUFFER_SIZE]; let ssh_server = ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; let socket_enabled_struct = SocketEnabled { - rng: s.rng, config: s.config, tcp_socket: s.tcp_socket, ssh_server: ssh_server, gpio10: s.gpio10, gpio11: s.gpio11, uart1: s.uart1, - flash: s.flash, }; match ssh_enabled(socket_enabled_struct).await { Ok(_) => (), @@ -520,7 +480,6 @@ pub struct SshEnabled<'a, 'b, CL> where CL: Future>, { - pub rng: Rng, pub tcp_socket: TcpSocket<'a>, pub ssh_server: &'b SSHServer<'a>, pub uart1: UART1<'a>, @@ -538,10 +497,9 @@ async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> let chan_pipe = Channel::::new(); println!("Calling connection_loop from uart_enabled"); - let connection = idle_wait_for_connection(&s.ssh_server, &chan_pipe, &s.config, &s.flash); + let connection = idle_wait_for_connection(&s.ssh_server, &chan_pipe, &s.config); let ssh_enabled_struct = SshEnabled { - rng: s.rng, tcp_socket: s.tcp_socket, ssh_server: &s.ssh_server, config: s.config, @@ -567,7 +525,6 @@ pub struct ClientConnected<'a, 'b, CL> where CL: Future>, { - pub rng: Rng, pub tcp_socket: TcpSocket<'a>, pub ssh_server: &'b SSHServer<'a>, pub chan_pipe: &'b Channel, @@ -586,7 +543,6 @@ where // loop { let uart_buff = uart_buffer_wait_for_initialisation().await; let client_connected_struct = ClientConnected { - rng: s.rng, tcp_socket: s.tcp_socket, ssh_server: s.ssh_server, chan_pipe: s.chan_pipe, @@ -613,7 +569,6 @@ pub struct UartBufferReady<'a, 'b, CL> where CL: Future>, { - pub rng: Rng, pub tcp_socket: TcpSocket<'a>, pub ssh_server: &'b SSHServer<'a>, pub uart_buff: &'a BufferedUart, @@ -629,7 +584,6 @@ where let _uart = enable_uart(s.uart_buff, s.uart1, &s.config, s.gpio10, s.gpio11); let uart_buffer_ready_struct = UartBufferReady { - rng: s.rng, tcp_socket: s.tcp_socket, ssh_server: s.ssh_server, chan_pipe: s.chan_pipe, @@ -650,7 +604,6 @@ where } pub struct UartEnabledConsumed<'a, 'b> { - pub rng: Rng, pub uart_buff: &'a BufferedUart, pub ssh_server: &'b SSHServer<'a>, pub chan_pipe: &'b Channel, @@ -673,7 +626,6 @@ where { // loop { let uart_enabled_consumed = UartEnabledConsumed { - rng: s.rng, uart_buff: s.uart_buff, ssh_server: s.ssh_server, chan_pipe: s.chan_pipe, diff --git a/src/serve.rs b/src/serve.rs index 9344fcb..d6a9344 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -1,12 +1,15 @@ +// SPDX-FileCopyrightText: 2025 Roman Valls, 2025 +// +// SPDX-License-Identifier: GPL-3.0-or-later + use core::option::Option::{self, None, Some}; use core::result::Result; -use core::writeln; use crate::config::SSHStampConfig; +use crate::config_storage; use crate::keys; -// use crate::pins::PinChannel; -use crate::storage; -use crate::storage::Fl; +use storage::flash; + // Embassy use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; @@ -23,14 +26,13 @@ use esp_println::{dbg, println}; pub enum SessionType { Bridge(ChanHandle), - //Sftp(ChanHandle), + Sftp(ChanHandle), } pub async fn connection_loop<'a>( serv: &SSHServer<'_>, chan_pipe: &Channel, config: &'a SunsetMutex, - flash: &'a SunsetMutex, ) -> Result<(), sunset::Error> { let username = Mutex::::new(String::<20>::new()); let mut session: Option = None; @@ -43,13 +45,35 @@ pub async fn connection_loop<'a>( dbg!(&ev); #[allow(unreachable_patterns)] match ev { + // #[cfg(feature = "sftp-ota")] + ServEvent::SessionSubsystem(a) => { + if a.command()?.to_lowercase().as_str() == "sftp" { + if let Some(ch) = session.take() { + debug_assert!(ch.num() == a.channel()); + + a.succeed()?; + dbg!("We got SFTP subsystem"); + let _ = chan_pipe.try_send(SessionType::Sftp(ch)); + } else { + a.fail()?; + } + } else { + a.fail()?; + } + } ServEvent::SessionShell(a) => { if let Some(ch) = session.take() { // Save config after connection successful (SessionEnv completed) if config_changed { let config_guard = config.lock().await; - let mut flash_guard = flash.lock().await; - let _result = storage::save(&mut flash_guard, &config_guard).await; + let Some(flash_storage_guard) = flash::get_flash_n_buffer() else { + panic!("Could not acquire flash storage lock"); + }; + let mut flash_storage = flash_storage_guard.lock().await; + // TODO: Migrate this function/test to embedded-test. + // Quick roundtrip test for SSHStampConfig + // ssh_stamp::config::roundtrip_config(); + let _result = config_storage::save(&mut flash_storage, &config_guard).await; } debug_assert!(ch.num() == a.channel()); a.succeed()?; @@ -109,7 +133,7 @@ pub async fn connection_loop<'a>( dbg!("Updating UART TX pin to ", val); if let Ok(pin_num) = val.parse::() { let mut config_lock = config.lock().await; - config_lock.tx_pin = pin_num; + config_lock.uart_pins.tx = pin_num; config_changed = true; dbg!("TX pin updated"); } else { @@ -121,7 +145,7 @@ pub async fn connection_loop<'a>( dbg!("Updating UART RX pin to ", val); if let Ok(pin_num) = val.parse::() { let mut config_lock = config.lock().await; - config_lock.rx_pin = pin_num; + config_lock.uart_pins.rx = pin_num; config_changed = true; dbg!("RX pin updated"); } else { @@ -149,7 +173,8 @@ pub async fn connection_loop<'a>( println!("Expected caller to handle event"); error::BadUsage.fail()? } + ServEvent::PollAgain => (), _ => (), - }; + } } } diff --git a/src/settings.rs b/src/settings.rs index e67485f..0f3097b 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,5 +1,7 @@ // Static settings +use core::net::Ipv4Addr; + // SSH server settings //pub(crate) const MTU: usize = 1536; //pub(crate) const PORT: u16 = 22; @@ -7,7 +9,10 @@ pub(crate) const DEFAULT_SSID: &str = "ssh-stamp"; //pub(crate) const SSH_SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; pub(crate) const KEY_SLOTS: usize = 1; // TODO: Document whether this a "reasonable default"? Justify why? //pub(crate) const PASSWORD_AUTHENTICATION: bool = true; +pub(crate) const DEFAULT_IP: &Ipv4Addr = &Ipv4Addr::new(192, 168, 4, 1); // UART settings //pub(crate) const BAUD_RATE: u32 = 115200; //pub(crate) const UART_SETTINGS: &str = "8N1"; +pub(crate) const DEFAULT_UART_TX_PIN: u8 = 10; +pub(crate) const DEFAULT_UART_RX_PIN: u8 = 11; diff --git a/storage/Cargo.toml b/storage/Cargo.toml new file mode 100644 index 0000000..123d5ae --- /dev/null +++ b/storage/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "storage" +version = "0.1.0" +edition = "2024" + +[dependencies] + +log.workspace = true +once_cell.workspace = true +sunset-async.workspace = true + +# Avoids issues with ota-packer building for x86_64-unknown-linux-gnu target +[target.'cfg(target_os = "none")'.dependencies] +esp-bootloader-esp-idf.workspace = true +embedded-storage.workspace = true +esp-storage.workspace = true +esp-hal.workspace = true + +[features] + +default = ["esp32c6"] + +esp32 = [] +esp32s2 = [] +esp32s3 = [] +esp32c2 = [] +esp32c3 = [] +esp32c6 = [] diff --git a/storage/src/esp.rs b/storage/src/esp.rs new file mode 100644 index 0000000..e9813af --- /dev/null +++ b/storage/src/esp.rs @@ -0,0 +1,12 @@ +// #[allow(unused_imports)] +// use log::{debug, error, info, warn}; + +// use crate::flash; +// use embedded_storage::Storage; +// use esp_bootloader_esp_idf; +// use esp_hal::system; + +// TODO: This file contained many OTA-related +// functionality, it should be named as such +// i.e: esp_ota.rs or similar, not just +// esp.rs diff --git a/storage/src/lib.rs b/storage/src/lib.rs new file mode 100644 index 0000000..cd29fce --- /dev/null +++ b/storage/src/lib.rs @@ -0,0 +1,78 @@ +#![no_std] +// SPDX-FileCopyrightText: 2025 Roman Valls, 2025 +// +// SPDX-License-Identifier: GPL-3.0-or-later + +/// Module defining the ESP32-specific storage traits implementations for OTA updates +#[cfg(any( + feature = "esp32", + feature = "esp32s2", + feature = "esp32s3", + feature = "esp32c3", + feature = "esp32c6" +))] +pub mod esp; + +// TODO: When the time comes, generalise the flash so it can be used with all supported targets +/// [[flash]] is a packet to provide safe access to the Flash storage used by SSH-Stamp +/// +/// It does so by storing the FlashStorage and a buffer for read/write operations in a single structure +/// protected by a SunsetMutex for safe concurrent access in async contexts. +pub mod flash { + use esp_hal::peripherals::FLASH; + use esp_storage::FlashStorage; + const FLASH_BUF_SIZE: usize = esp_storage::FlashStorage::SECTOR_SIZE as usize; + static FLASH_STORAGE: OnceCell> = OnceCell::new(); + + #[allow(unused_imports)] + use log::{debug, error, info, warn}; + use once_cell::sync::OnceCell; + use sunset_async::SunsetMutex; + + /// A structure that holds both the FlashStorage and a buffer for read/write operations + /// + /// The buffer is stored here to avoid allocating multiple buffers in different parts of the code. + /// It has a fixed size defined by FLASH_BUF_SIZE. + #[derive(Debug)] + pub struct FlashBuffer<'d> { + pub flash: FlashStorage<'d>, + pub buf: [u8; FLASH_BUF_SIZE], + } + + impl<'d> FlashBuffer<'d> { + pub fn new(flash: FlashStorage<'static>) -> Self { + Self { + flash, + buf: [0u8; FLASH_BUF_SIZE], + } + } + + /// For cases where it is necessary to use both flash and buffer mutably at the same time + pub fn split_ref_mut(&mut self) -> (&mut FlashStorage<'d>, &mut [u8]) { + (&mut self.flash, &mut self.buf) + } + } + + /// Shall be called at startup to avoid lazy initialization during runtime + /// + /// Calls to [`with_flash`] or [`get_flash`] will initialize the flash storage if not already done. + /// + /// Multiple calls to init() are safe and will have no effect after the first one. + pub fn init(flash: FLASH<'static>) { + let fl = FlashBuffer::new(FlashStorage::new(flash)); + + let Ok(()) = FLASH_STORAGE.set(SunsetMutex::new(fl)) else { + warn!("Flash storage already initialized"); + return; + }; + } + + /// Static accessor for the flash storage mutex. Warning: It will fail if not initialized. + /// + /// call [`init()`] at startup. + /// + /// It is expected that the user will drop the lock on the mutex after use... + pub fn get_flash_n_buffer() -> Option<&'static SunsetMutex>> { + FLASH_STORAGE.get() + } +} From c9cc4a11cd3279599034a23c110a3ddfd09e8079 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Mon, 9 Feb 2026 18:20:24 +1100 Subject: [PATCH 59/86] Rename config_storage.rs to store.rs --- src/lib.rs | 2 +- src/main.rs | 2 +- src/serve.rs | 4 ++-- src/{config_storage.rs => store.rs} | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename src/{config_storage.rs => store.rs} (100%) diff --git a/src/lib.rs b/src/lib.rs index cf2cd8e..c36a6b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,4 +9,4 @@ pub mod keys; pub mod serial; pub mod serve; pub mod settings; -pub mod config_storage; +pub mod store; diff --git a/src/main.rs b/src/main.rs index 2600776..868f84e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,7 +62,7 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a // TODO: Migrate this function/test to embedded-test. // Quick roundtrip test for SSHStampConfig // ssh_stamp::config::roundtrip_config(); - ssh_stamp::config_storage::load_or_create(&mut flash_storage).await + ssh_stamp::store::load_or_create(&mut flash_storage).await } .expect("Could not load or create SSHStampConfig"); diff --git a/src/serve.rs b/src/serve.rs index d6a9344..bb7af0b 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -6,8 +6,8 @@ use core::option::Option::{self, None, Some}; use core::result::Result; use crate::config::SSHStampConfig; -use crate::config_storage; use crate::keys; +use crate::store; use storage::flash; // Embassy @@ -73,7 +73,7 @@ pub async fn connection_loop<'a>( // TODO: Migrate this function/test to embedded-test. // Quick roundtrip test for SSHStampConfig // ssh_stamp::config::roundtrip_config(); - let _result = config_storage::save(&mut flash_storage, &config_guard).await; + let _result = store::save(&mut flash_storage, &config_guard).await; } debug_assert!(ch.num() == a.channel()); a.succeed()?; diff --git a/src/config_storage.rs b/src/store.rs similarity index 100% rename from src/config_storage.rs rename to src/store.rs From 3d45d767be5e64c71383079fa9ffe16dc2856501 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Tue, 10 Feb 2026 01:38:26 +1100 Subject: [PATCH 60/86] net::if_up split into separate steps: prepare_ap_stack -> wifi_up -> net_up -> run_dchp -> check_link HSM runs each function in consecutively to avoid using task spawner in net.rs --- src/espressif/net.rs | 135 ++++++++++++++++++-------- src/main.rs | 219 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 273 insertions(+), 81 deletions(-) diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 153114c..62edbbf 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -7,7 +7,7 @@ use core::str::FromStr; // use embassy_executor::Spawner; use embassy_net::{tcp::TcpSocket, Stack, StackResources}; -use embassy_net::{IpListenEndpoint, Ipv4Cidr, StaticConfigV4}; +use embassy_net::{IpListenEndpoint, Ipv4Cidr, Runner, StaticConfigV4}; use embassy_time::{Duration, Timer}; use esp_hal::peripherals::WIFI; @@ -47,32 +47,28 @@ macro_rules! mk_static { } use heapless::String; - -pub async fn if_up<'a>( - wifi_controller: Controller<'a>, +pub async fn prepare_ap_stack<'a>( + controller: &'a Controller<'a>, wifi: WIFI<'a>, - rng: &mut Rng, - // config: &'static SunsetMutex, - wifi_ssid: String<32>, -) -> Result, sunset::Error> { - let (mut controller, interfaces) = - esp_radio::wifi::new(&wifi_controller, wifi, Default::default()).unwrap(); + rng: Rng, +) -> Result< + ( + Stack<'a>, + Runner<'a, WifiDevice<'a>>, + WifiController<'a>, + Ipv4Addr, + ), + sunset::Error, +> { + let (mut wifi_controller, interfaces) = + esp_radio::wifi::new(controller, wifi, Default::default()).unwrap(); let ap_config = ModeConfig::AccessPoint(AccessPointConfig::default().with_ssid(DEFAULT_SSID.into())); - let res = controller.set_config(&ap_config); + let res = wifi_controller.set_config(&ap_config); println!("wifi_set_configuration returned {:?}", res); let gw_ip_addr_ipv4 = DEFAULT_IP.clone(); - // let _gw_ip_addr = { - // let guard = config.lock().await; - // if let Some(ref s) = guard.ip4_static { - // embassy_net::Config::ipv4_static(s.clone()) - // } else { - // embassy_net::Config::dhcpv4(Default::default()) - // } - // }; - let net_config = embassy_net::Config::ipv4_static(StaticConfigV4 { address: Ipv4Cidr::new(gw_ip_addr_ipv4, 24), gateway: Some(gw_ip_addr_ipv4), @@ -82,19 +78,20 @@ pub async fn if_up<'a>( let seed = (rng.random() as u64) << 32 | rng.random() as u64; // Init network stack - let (ap_stack, _runner) = embassy_net::new( + let (ap_stack, runner) = embassy_net::new( interfaces.ap, net_config, mk_static!(StackResources<3>, StackResources::<3>::new()), seed, ); - // spawner.spawn(wifi_up(controller, wifi_ssid)).ok(); - // spawner.spawn(net_up(runner)).ok(); - // spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); - wifi_up(controller, wifi_ssid).await; - dhcp_server(ap_stack, gw_ip_addr_ipv4).await; + Ok((ap_stack, runner, wifi_controller, gw_ip_addr_ipv4)) +} +pub async fn check_link<'a>( + ap_stack: Stack<'a>, + gw_ip_addr_ipv4: Ipv4Addr, +) -> Result, sunset::Error> { loop { println!("Checking if link is up...\n"); if ap_stack.is_link_up() { @@ -112,6 +109,62 @@ pub async fn if_up<'a>( Ok(ap_stack) } +// +// if_up split into separate steps: prepare_ap_stack -> wifi_up -> net_up -> run_dchp -> check_link +// HSM runs each separately to avoid using task spawner +// +// pub async fn if_up<'a>( +// wifi_controller: Controller<'a>, +// wifi: WIFI<'a>, +// rng: &mut Rng, +// config: &'static SunsetMutex, +// ) -> Result, sunset::Error> { +// let (mut controller, interfaces) = +// esp_radio::wifi::new(&wifi_controller, wifi, Default::default()).unwrap(); +// let ap_config = +// ModeConfig::AccessPoint(AccessPointConfig::default().with_ssid(DEFAULT_SSID.into())); +// let res = controller.set_config(&ap_config); +// println!("wifi_set_configuration returned {:?}", res); + +// let gw_ip_addr_ipv4 = DEFAULT_IP.clone(); + +// let net_config = embassy_net::Config::ipv4_static(StaticConfigV4 { +// address: Ipv4Cidr::new(gw_ip_addr_ipv4, 24), +// gateway: Some(gw_ip_addr_ipv4), +// dns_servers: Default::default(), +// }); + +// let seed = (rng.random() as u64) << 32 | rng.random() as u64; + +// // Init network stack +// let (ap_stack, _runner) = embassy_net::new( +// interfaces.ap, +// net_config, +// mk_static!(StackResources<3>, StackResources::<3>::new()), +// seed, +// ); + +// // spawner.spawn(wifi_up(controller, wifi_ssid)).ok(); +// // spawner.spawn(net_up(runner)).ok(); +// // spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); + +// loop { +// println!("Checking if link is up...\n"); +// if ap_stack.is_link_up() { +// break; +// } +// Timer::after(Duration::from_millis(500)).await; +// } + +// // TODO: Use wifi_manager instead? +// println!( +// "Connect to the AP `ssh-stamp` as a DHCP client with IP: {}", +// gw_ip_addr_ipv4 +// ); + +// Ok(ap_stack) +// } + pub async fn accept_requests<'a>(stack: Stack<'a>, _uart: &BufferedUart) -> ! { // let rx_buffer = mk_static!([u8; 1536], [0; 1536]); // let tx_buffer = mk_static!([u8; 1536], [0; 1536]); @@ -145,13 +198,13 @@ pub async fn accept_requests<'a>(stack: Stack<'a>, _uart: &BufferedUart) -> ! { } // #[embassy_executor::task] -async fn wifi_up( +pub async fn wifi_up( // mut controller: WifiController<'static>, - mut controller: WifiController<'_>, + mut wifi_controller: WifiController<'_>, // config: &'static SunsetMutex, wifi_ssid: String<32>, ) { - println!("Device capabilities: {:?}", controller.capabilities()); + println!("Device capabilities: {:?}", wifi_controller.capabilities()); // let wifi_ssid = { // let guard = config.lock().await; // guard.wifi_ssid.clone() @@ -160,32 +213,38 @@ async fn wifi_up( // TODO: No wifi password(s) yet... //let wifi_password = config.lock().await.wifi_pw; + println!("Starting Wifi"); loop { if esp_radio::wifi::ap_state() == WifiApState::Started { // wait until we're no longer connected - controller.wait_for_event(WifiEvent::ApStop).await; + wifi_controller.wait_for_event(WifiEvent::ApStop).await; Timer::after(Duration::from_millis(5000)).await } - if !matches!(controller.is_started(), Ok(true)) { + if !matches!(wifi_controller.is_started(), Ok(true)) { let ssid_string = String::<63>::from_str(&wifi_ssid) .unwrap() .to_ascii_lowercase(); let client_config = ModeConfig::AccessPoint(AccessPointConfig::default().with_ssid(ssid_string)); - controller.set_config(&client_config).unwrap(); + wifi_controller.set_config(&client_config).unwrap(); println!("Starting wifi"); - controller.start_async().await.unwrap(); + wifi_controller.start_async().await.unwrap(); println!("Wifi started!"); } Timer::after(Duration::from_millis(10)).await; } } - +use esp_radio::wifi::WifiDevice; // #[embassy_executor::task] -// async fn net_up<'a>(mut runner: Runner<'a, WifiDevice<'a>>) { -// println!("Bringing up network stack...\n"); -// runner.run().await -// } +pub async fn net_up<'a>(mut runner: Runner<'a, WifiDevice<'a>>) { + println!("Bringing up network stack...\n"); + runner.run().await +} + +pub async fn run_dchp<'a>(ap_stack: Stack<'a>, gw_ip_addr_ipv4: Ipv4Addr) { + // let gw_ip_addr_ipv4 = DEFAULT_IP.clone(); + dhcp_server(ap_stack, gw_ip_addr_ipv4).await; +} // #[embassy_executor::task] async fn dhcp_server<'a>(stack: Stack<'a>, ip: Ipv4Addr) { diff --git a/src/main.rs b/src/main.rs index 868f84e..1399f14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,9 +10,10 @@ use core::result::Result::Err; use core::result::Result::Ok; // use core::error::Error; use core::future::Future; +use core::net::Ipv4Addr; use embassy_executor::Spawner; use embassy_futures::select::{select3, Either3}; -use embassy_net::{tcp::TcpSocket, IpListenEndpoint, Stack}; +use embassy_net::{tcp::TcpSocket, IpListenEndpoint, Runner, Stack}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use esp_hal::system::software_reset; use esp_hal::{ @@ -25,14 +26,17 @@ use esp_hal::{ }; use esp_println::dbg; use esp_println::println; +use esp_radio::wifi::WifiController; +// use esp_radio::wifi::WifiController; +// use esp_radio::Controller +use esp_radio::wifi::WifiDevice; use esp_radio::{Controller, InitializationError}; -use heapless::String; use ssh_stamp::{ config::SSHStampConfig, espressif::{ buffered_uart::BufferedUart, // net::accept_requests, - net::if_up, + net, rng, }, serve, @@ -98,7 +102,7 @@ pub async fn wifi_wait_for_initialisation<'a>() -> Result, Initia wifi_controller } -pub async fn wifi_disable() -> () { +pub async fn wifi_controller_disable() -> () { // TODO: Correctly disable wifi controller // pub async fn wifi_disable(wifi_controller: EspWifiController<'_>) -> (){ // drop wifi controller @@ -107,28 +111,33 @@ pub async fn wifi_disable() -> () { software_reset(); } -pub struct TcpStackReturn<'a> { - pub config: SSHStampConfig, - pub tcp_stack: Stack<'a>, +pub async fn ap_stack_disable() -> () { + // drop ap_stack + software_reset(); } -pub async fn tcp_wait_for_initialisation<'a>(s: WifiEnabledConsumed<'a>) -> Stack<'a> { - let wifi_controller = s.wifi_controller; - let wifi: WIFI = s.wifi; - let mut rng = s.rng; - let wifi_ssid = s.wifi_ssid; - let tcp_stack = if_up(wifi_controller, wifi, &mut rng, wifi_ssid) - .await - .unwrap(); - tcp_stack + +pub async fn wifi_disable() -> () { + // drop wifi + software_reset(); +} + +pub async fn network_disable() -> () { + // drop network + software_reset(); +} + +pub async fn dhcp_disable() -> () { + // drop dhcp + software_reset(); } -pub async fn tcp_disable() -> () { +pub async fn tcp_stack_disable() -> () { // drop tcp stack software_reset(); } -pub async fn socket_disable() -> () { - // drop socket +pub async fn tcp_socket_disable() -> () { + // drop tcp stack software_reset(); } @@ -331,73 +340,196 @@ pub struct PeripheralsEnabled<'a> { pub rng: Rng, pub wifi: WIFI<'a>, pub config: &'a SunsetMutex, - pub wifi_controller: Controller<'a>, + pub controller: Controller<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, pub uart1: UART1<'a>, } async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Error> { - let wifi_controller = wifi_wait_for_initialisation().await; + let controller = wifi_wait_for_initialisation().await; let peripherals_enabled_struct = PeripheralsEnabled { rng: s.rng, wifi: s.wifi, config: s.config, - wifi_controller: wifi_controller.unwrap(), + controller: controller.unwrap(), gpio10: s.gpio10, gpio11: s.gpio11, uart1: s.uart1, }; - match wifi_enabled(peripherals_enabled_struct).await { + match wifi_controller_enabled(peripherals_enabled_struct).await { Ok(_) => (), Err(e) => { - println!("Wifi error: {}", e); + println!("Wifi controller error: {}", e); } } - wifi_disable().await; + wifi_controller_disable().await; Ok(()) // todo!() return relevant value } -pub struct WifiEnabledConsumed<'a> { +pub struct WifiControllerEnabled<'a> { pub rng: Rng, - pub wifi: WIFI<'a>, - pub wifi_ssid: String<32>, - pub wifi_controller: Controller<'a>, + pub config: &'a SunsetMutex, + pub wifi_controller: WifiController<'a>, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, + pub ap_stack: Stack<'a>, + pub runner: Runner<'a, WifiDevice<'a>>, + pub ip: Ipv4Addr, +} +pub async fn wifi_controller_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { + let (ap_stack, runner, wifi_controller, ip) = + net::prepare_ap_stack(&s.controller, s.wifi, s.rng) + .await + .unwrap(); + let wifi_controller_enabled_stack = WifiControllerEnabled { + config: s.config, + rng: s.rng, + wifi_controller: wifi_controller, + gpio10: s.gpio10, + gpio11: s.gpio11, + uart1: s.uart1, + ap_stack: ap_stack, + runner: runner, + ip: ip, + }; + match ap_stack_enabled(wifi_controller_enabled_stack).await { + Ok(_) => (), + Err(e) => { + println!("AP Stack error: {}", e); + } + } + ap_stack_disable().await; + Ok(()) // todo!() return relevant value } -pub struct WifiEnabled<'a> { + +pub struct ApStackEnabled<'a> { + pub rng: Rng, pub config: &'a SunsetMutex, - pub tcp_stack: Stack<'a>, pub gpio10: GPIO10<'a>, pub gpio11: GPIO11<'a>, pub uart1: UART1<'a>, + pub ap_stack: Stack<'a>, + pub runner: Runner<'a, WifiDevice<'a>>, + pub ip: Ipv4Addr, } -async fn wifi_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { + +pub async fn ap_stack_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Error> { let wifi_ssid_config = { let guard = s.config.lock().await; guard.wifi_ssid.clone() }; - let wifi_enabled_consumed = WifiEnabledConsumed { + + net::wifi_up(s.wifi_controller, wifi_ssid_config).await; + + let ap_stack_enabled_stack = ApStackEnabled { + config: s.config, rng: s.rng, - wifi: s.wifi, - wifi_ssid: wifi_ssid_config, - wifi_controller: s.wifi_controller, + gpio10: s.gpio10, + gpio11: s.gpio11, + uart1: s.uart1, + ap_stack: s.ap_stack, + runner: s.runner, + ip: s.ip, + }; + match wifi_enabled(ap_stack_enabled_stack).await { + Ok(_) => (), + Err(e) => { + println!("AP Stack error: {}", e); + } + } + wifi_disable().await; // drop wifi + Ok(()) // todo!() return relevant value +} + +pub struct WifiEnabled<'a> { + pub rng: Rng, + pub config: &'a SunsetMutex, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, + pub ap_stack: Stack<'a>, + pub ip: Ipv4Addr, +} + +pub async fn wifi_enabled<'a>(s: ApStackEnabled<'a>) -> Result<(), sunset::Error> { + net::net_up(s.runner).await; + + let wifi_enabled_stack = WifiEnabled { + config: s.config, + rng: s.rng, + gpio10: s.gpio10, + gpio11: s.gpio11, + uart1: s.uart1, + ap_stack: s.ap_stack, + ip: s.ip, }; - let tcp_stack = tcp_wait_for_initialisation(wifi_enabled_consumed).await; - let wifi_enabled_struct = WifiEnabled { + match network_enabled(wifi_enabled_stack).await { + Ok(_) => (), + Err(e) => { + println!("Network error: {}", e); + } + } + network_disable().await; // drop network + Ok(()) // todo!() return relevant value +} + +pub struct NetworkEnabled<'a> { + pub config: &'a SunsetMutex, + pub rng: Rng, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, + pub ap_stack: Stack<'a>, + pub ip: Ipv4Addr, +} +pub async fn network_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { + net::run_dchp(s.ap_stack, s.ip).await; + + let network_enabled_struct = NetworkEnabled { + config: s.config, + ap_stack: s.ap_stack, + rng: s.rng, + gpio10: s.gpio10, + gpio11: s.gpio11, + uart1: s.uart1, + ip: s.ip, + }; + match dhcp_enabled(network_enabled_struct).await { + Ok(_) => (), + Err(e) => { + println!("DHCP error: {}", e); + } + } + dhcp_disable().await; + Ok(()) // todo!() return relevant value +} + +pub struct DhcpEnabled<'a> { + pub config: &'a SunsetMutex, + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, + pub uart1: UART1<'a>, + pub tcp_stack: Stack<'a>, +} +async fn dhcp_enabled<'a>(s: NetworkEnabled<'a>) -> Result<(), sunset::Error> { + let tcp_stack = net::check_link(s.ap_stack, s.ip).await.unwrap(); + let dhcp_enabled_struct = DhcpEnabled { config: s.config, tcp_stack: tcp_stack, gpio10: s.gpio10, gpio11: s.gpio11, uart1: s.uart1, }; - match tcp_enabled(wifi_enabled_struct).await { + match tcp_enabled(dhcp_enabled_struct).await { Ok(_) => (), Err(e) => { println!("TCP stack error: {}", e); } } - tcp_disable().await; + tcp_stack_disable().await; Ok(()) // todo!() return relevant value } @@ -408,7 +540,8 @@ pub struct TCPEnabled<'a> { pub gpio11: GPIO11<'a>, pub uart1: UART1<'a>, } -async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { + +async fn tcp_enabled<'a>(s: DhcpEnabled<'a>) -> Result<(), sunset::Error> { let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; let mut tcp_socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); @@ -422,7 +555,7 @@ async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { .await { println!("connect error: {:?}", e); - socket_disable().await; + tcp_socket_disable().await; } println!("Connected, port 22"); let tcp_enabled_struct = TCPEnabled { @@ -438,7 +571,7 @@ async fn tcp_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { println!("TCP socket error: {}", e); } } - socket_disable().await; + tcp_socket_disable().await; Ok(()) // todo!() return relevant value } From c9c3c53425288b1794eb41a8ff560b50c14c4ef8 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Wed, 11 Feb 2026 00:46:40 +1100 Subject: [PATCH 61/86] Revert to using embassy_executor tasks in net.rs due to being stuck wifi startup loop. main.rs structs required to be static lifetime. Put both gpio required for uart into GPIOS struct. Add features to fix interrupts for ESP32, ESP32-S2 and ESP32-S3 build. Cannot build for ESP32/S2/S3 without manually modifying with config.toml build-std. --- .cargo/config.toml | 73 +++++--- Cargo.lock | 427 +++++++++++++++++++++++-------------------- Cargo.toml | 128 +++++++------ rust-toolchain.toml | 6 +- src/config.rs | 6 +- src/espressif/net.rs | 131 ++++--------- src/main.rs | 9 +- 7 files changed, 394 insertions(+), 386 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 5a3765e..c7f99a2 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,46 +1,61 @@ +# SPDX-FileCopyrightText: 2025 Roman Valls, 2025 +# +# SPDX-License-Identifier: GPL-3.0-or-later + [alias] -build-esp32 = "build --release --target xtensa-esp32-none-elf --features esp32" -build-esp32c2 = "build --release --target riscv32imc-unknown-none-elf --features esp32c2" -build-esp32c3 = "build --release --target riscv32imc-unknown-none-elf --features esp32c3" +# No default features avoids selecting the default target (esp32c6) which would conflict with other target features. +# -Z only works on nightly +#build-esp32 = "build --release --target xtensa-esp32-none-elf --no-default-features --features esp32" +build-esp32 = "build --release --target xtensa-esp32-none-elf --no-default-features --features esp32 -Z build-std=core,alloc" +build-esp32c2 = "build --release --target riscv32imc-unknown-none-elf --no-default-features --features esp32c2 " +build-esp32c3 = "build --release --target riscv32imc-unknown-none-elf --no-default-features --features esp32c3 " # Available but not supported by esp-hal (yet). -#build-esp32c5 = "build --release --target riscv32imac-unknown-none-elf --features esp32c5" -build-esp32c6 = "build --release --target riscv32imac-unknown-none-elf --features esp32c6" +#build-esp32c5 = "build --release --target riscv32imac-unknown-none-elf --no-default-features --features esp32c5" +build-esp32c6 = "build --release --target riscv32imac-unknown-none-elf --no-default-features --features esp32c6" # Debug build (too big for target and probe-rs doesn't work anyway) -#build-esp32c6 = "build --target riscv32imac-unknown-none-elf --features esp32c6" -build-esp32s2 = "build --profile esp32s2 --target xtensa-esp32s2-none-elf --features esp32s2" -build-esp32s3 = "build --release --target xtensa-esp32s3-none-elf --features esp32s3" +#build-esp32c6 = "build --target riscv32imac-unknown-none-elf --no-default-features --features esp32c6" +build-esp32s2 = "build --profile esp32s2 --target xtensa-esp32s2-none-elf --no-default-features --features esp32s2 -Z build-std=core,alloc" +build-esp32s3 = "build --release --target xtensa-esp32s3-none-elf --no-default-features --features esp32s3 -Z build-std=core,alloc" -run-esp32 = "run --release --target xtensa-esp32-none-elf --features esp32" -run-esp32c2 = "run --release --target riscv32imc-unknown-none-elf --features esp32c2" -run-esp32c3 = "run --release --target riscv32imc-unknown-none-elf --features esp32c3" +run-esp32 = "run --release --target xtensa-esp32-none-elf --no-default-features --features esp32 " +run-esp32c2 = "run --release --target riscv32imc-unknown-none-elf --no-default-features --features esp32c2 " +run-esp32c3 = "run --release --target riscv32imc-unknown-none-elf --no-default-features --features esp32c3 " # Available but not supported by esp-hal (yet). -#run-esp32c5 = "run --release --target riscv32imac-unknown-none-elf --features esp32c5" -run-esp32c6 = "run --release --target riscv32imac-unknown-none-elf --features esp32c6" +#run-esp32c5 = "run --release --target riscv32imac-unknown-none-elf --no-default-features --features esp32c5" +run-esp32c6 = "run --release --target riscv32imac-unknown-none-elf --no-default-features --features esp32c6" + # Debug build (too big for target and probe-rs doesn't work anyway) -#run-esp32c6 = "run --target riscv32imac-unknown-none-elf --features esp32c6" -run-esp32s2 = "run --profile esp32s2 --target xtensa-esp32s2-none-elf --features esp32s2" -run-esp32s3 = "run --release --target xtensa-esp32s3-none-elf --features esp32s3" +#run-esp32c6 = "run --target riscv32imac-unknown-none-elf --no-default-features --features esp32c6" +run-esp32s2 = "run --profile esp32s2 --target xtensa-esp32s2-none-elf --no-default-features --features esp32s2 " +run-esp32s3 = "run --release --target xtensa-esp32s3-none-elf --no-default-features --features esp32s3 " + +# Build ota-packer alias: I might need to modify it so when run it can run a build for target architecture, extract the binary from the elf image and then pack the ota file. +ota-packer = "run --package ota --bin ota-packer --target x86_64-unknown-linux-gnu" + +# Test alias +test-ota = "test --package ota --target x86_64-unknown-linux-gnu" -[target.xtensa-esp32-none-elf] +[target.xtensa-esp32-none-elf] # ESP32 runner = "espflash flash --baud=921600 --monitor --chip esp32" rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32"'] -[target.riscv32imc-unknown-none-elf] +[target.riscv32imc-unknown-none-elf] # ESP32-C2 / ESP32-C3 runner = "espflash flash --baud=921600 --monitor" -rustflags = [ "-C", "force-frame-pointers"] -[target.riscv32imac-unknown-none-elf] +rustflags = ["-C", "force-frame-pointers"] +[target.riscv32imac-unknown-none-elf] # ESP32C6 runner = "espflash flash --baud=921600 --partition-table partitions.csv --monitor" -#runner = "espflash flash --baud=921600 --monitor" -rustflags = [ "-C", "force-frame-pointers"] -[target.xtensa-esp32s2-none-elf] +rustflags = ["-C", "force-frame-pointers"] + +[target.xtensa-esp32s2-none-elf] # ESP32-S2 runner = "espflash flash --baud=921600 --monitor --chip esp32s2" rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32s2"'] -[target.xtensa-esp32s3-none-elf] +[target.xtensa-esp32s3-none-elf] # ESP32-S3 runner = "espflash flash --baud=921600 --monitor --chip esp32s3" rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32s3"'] + # https://docs.espressif.com/projects/rust/esp-hal/1.0.0-beta.1/esp32c6/esp_hal/index.html#additional-configuration [env] -ESP_LOG = "INFO" +ESP_LOG = "info" #ESP_HAL_CONFIG_PLACE_SWITCH_TABLES_IN_RAM=true #ESP_HAL_CONFIG_PLACE_ANON_IN_RAM=false #ESP_HAL_CONFIG_FLIP_LINK=false @@ -48,8 +63,14 @@ ESP_LOG = "INFO" #ESP_HAL_CONFIG_STACK_GUARD_VALUE=3740121773 #ESP_HAL_CONFIG_IMPL_CRITICAL_SECTION=true + [build] target = "riscv32imac-unknown-none-elf" +# target = "x86_64-unknown-linux-gnu" [unstable] -build-std = ["core", "alloc"] +build-std = [] +# TODO Resolve build-std for different targets on stable channel. +# build-std = ["core","alloc"] # For ESP32, ESP32-S2, ESP32-S3: When using stable channel enable this. -Z option only works on nightly channel. +# build-std core and alloc has been moved as per alias parameter since +# `unstable` section MUST be global and cannot be defined per target. diff --git a/Cargo.lock b/Cargo.lock index 29a15be..fc32c00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,15 +16,15 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78200ac3468a57d333cd0ea5dd398e25111194dcacd49208afca95c629a6311d" +checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d" [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" [[package]] name = "ascii" @@ -52,15 +52,15 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bcrypt" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f" +checksum = "abaf6da45c74385272ddf00e1ac074c7d8a6c1a1dda376902bd6a427522a8b2c" dependencies = [ "base64 0.22.1", "blowfish", @@ -69,22 +69,22 @@ dependencies = [ [[package]] name = "bitfield" -version = "0.19.1" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db1bcd90f88eabbf0cadbfb87a45bceeaebcd3b4bc9e43da379cd2ef0162590d" +checksum = "21ba6517c6b0f2bf08be60e187ab64b038438f22dd755614d8fe4d4098c46419" dependencies = [ "bitfield-macros", ] [[package]] name = "bitfield-macros" -version = "0.19.1" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3787a07661997bfc05dd3431e379c0188573f78857080cf682e1393ab8e4d64c" +checksum = "f48d6ace212fdf1b45fd6b566bb40808415344642b76c3224c07c8df9da81e97" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -95,9 +95,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "block-buffer" @@ -132,9 +132,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chacha20" @@ -181,9 +181,9 @@ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.2.0" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373b7c5dbd637569a2cca66e8d66b8c446a1e7bf064ea321d265d7b3dfe7c97e" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", @@ -223,7 +223,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -236,6 +236,16 @@ dependencies = [ "darling_macro 0.20.11", ] +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + [[package]] name = "darling" version = "0.23.0" @@ -257,7 +267,20 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.114", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] @@ -270,7 +293,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -281,7 +304,18 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.104", + "syn 2.0.114", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.114", ] [[package]] @@ -292,18 +326,18 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] name = "delegate" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6178a82cf56c836a3ba61a7935cdb1c49bfaa6fa4327cd5bf554a503087de26b" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -319,9 +353,9 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -376,9 +410,9 @@ dependencies = [ [[package]] name = "edge-nal-embassy" -version = "0.7.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb09ea0c604bbcb694ecf9cf6596bc5f59bbb5360b68c3e471b61ae9a55a8533" +checksum = "252f89adf4f0016631977bec3ba50d768263a3a9fa9f023b4087088a619568ce" dependencies = [ "edge-nal", "embassy-futures", @@ -393,6 +427,41 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6207c84e9bc8df8ef3c155196df290f2a51f010bd60c2e78366e51979988bdb5" +[[package]] +name = "embassy-embedded-hal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c62a3bf127e03832fb97d8b01a058775e617653bc89e2a12c256485a7fb54c1" +dependencies = [ + "embassy-embedded-hal 0.4.0", + "embassy-futures", + "embassy-sync 0.6.2", + "embassy-time 0.4.0", + "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-embedded-hal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1611b7a7ab5d1fbed84c338df26d56fd9bded58006ebb029075112ed2c5e039" +dependencies = [ + "embassy-futures", + "embassy-hal-internal", + "embassy-sync 0.7.2", + "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-embedded-hal" version = "0.5.0" @@ -431,7 +500,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -457,14 +526,14 @@ dependencies = [ [[package]] name = "embassy-net" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940c4b9fe5c1375b09a0c6722c0100d6b2ed46a717a34f632f26e8d7327c4383" +checksum = "0558a231a47e7d4a06a28b5278c92e860f1200f24821d2f365a2f40fe3f3c7b2" dependencies = [ "document-features", "embassy-net-driver", - "embassy-sync 0.6.2", - "embassy-time 0.4.0", + "embassy-sync 0.7.2", + "embassy-time 0.5.0", "embedded-io-async 0.6.1", "embedded-nal-async", "heapless 0.8.0", @@ -677,23 +746,23 @@ dependencies = [ [[package]] name = "enumset" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ee17054f550fd7400e1906e2f9356c7672643ed34008a9e8abe147ccd2d821" +checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d07902c93376f1e96c34abc4d507c0911df3816cef50b01f5a2ff3ad8c370d" +checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" dependencies = [ - "darling 0.20.11", + "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -772,14 +841,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54786287c0a61ca0f78cb0c338a39427551d1be229103b4444591796c579e093" dependencies = [ "bitfield", - "bitflags 2.9.1", + "bitflags 2.10.0", "bytemuck", "cfg-if", "critical-section", "delegate", "digest", "document-features", - "embassy-embedded-hal", + "embassy-embedded-hal 0.5.0", "embassy-futures", "embassy-sync 0.7.2", "embassy-usb-driver", @@ -813,7 +882,7 @@ dependencies = [ "paste", "portable-atomic", "rand_core 0.6.4", - "rand_core 0.9.3", + "rand_core 0.9.5", "riscv", "sha1", "sha2", @@ -834,7 +903,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", "termcolor", ] @@ -857,6 +926,7 @@ dependencies = [ "esp-metadata-generated", "esp-sync", "esp-wifi-sys", + "log", ] [[package]] @@ -894,6 +964,7 @@ dependencies = [ "esp-wifi-sys", "heapless 0.9.2", "instability", + "log", "num-derive", "num-traits", "portable-atomic", @@ -1002,6 +1073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b6544f6f0cb86169d1f93ba2101a8d50358a040c5043676ed86b793e09b12c" dependencies = [ "anyhow", + "log", ] [[package]] @@ -1076,9 +1148,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.3.0" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fnv" @@ -1088,9 +1160,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fugit" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +checksum = "4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6" dependencies = [ "gcd", ] @@ -1143,9 +1215,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", @@ -1226,9 +1298,12 @@ dependencies = [ [[package]] name = "indoc" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "inout" @@ -1249,14 +1324,14 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" @@ -1279,14 +1354,14 @@ checksum = "ff7a39c8862fc1369215ccf0a8f12dd4598c7f6484704359f0351bd617034dbf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] name = "libc" -version = "0.2.174" +version = "0.2.181" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "459427e2af2b9c839b132acb702a1c654d95e10f8c326bfc2ad11310e458b1c5" [[package]] name = "linked_list_allocator" @@ -1296,15 +1371,15 @@ checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" [[package]] name = "litrs" -version = "0.4.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "managed" @@ -1314,9 +1389,9 @@ checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" [[package]] name = "memchr" -version = "2.7.5" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "nb" @@ -1341,7 +1416,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -1355,9 +1430,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -1365,13 +1440,13 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -1439,9 +1514,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" @@ -1470,7 +1545,7 @@ checksum = "a33fa6ec7f2047f572d49317cca19c87195de99c6e5b6ee492da701cfe02b053" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -1490,18 +1565,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -1523,9 +1598,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" [[package]] name = "riscv" @@ -1548,7 +1623,7 @@ checksum = "7d323d13972c1b104aa036bc692cd08b822c8bbf23d79a27c526095856499799" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -1577,7 +1652,7 @@ checksum = "def519ddeeb5e43c2b4fc3952c27b3a86782fc05192f322b2309125cd85b1fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -1599,6 +1674,12 @@ dependencies = [ "svgbobdoc", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -1610,27 +1691,27 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "semihosting" -version = "0.1.20" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e1c7d2b77d80283c750a39c52f1ab4d17234e8f30bca43550f5b2375f41d5f" +checksum = "e896488941756e5de3ee8449495464dbbf2201c85d2d1ace47d4fb81c74b99ec" [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" @@ -1659,7 +1740,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -1718,23 +1799,23 @@ dependencies = [ [[package]] name = "snafu" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320b01e011bf8d5d7a4a4a4be966d9160968935849c83b918827f6a435e7f627" +checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" dependencies = [ "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1961e2ef424c1424204d3a5d6975f934f56b6d50ff5732382d84ebf460e147f7" +checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -1800,7 +1881,7 @@ dependencies = [ "edge-dhcp", "edge-nal", "edge-nal-embassy", - "embassy-embedded-hal", + "embassy-embedded-hal 0.3.2", "embassy-executor", "embassy-futures", "embassy-net", @@ -1815,7 +1896,6 @@ dependencies = [ "esp-hal", "esp-println", "esp-radio", - "esp-rom-sys", "esp-rtos", "esp-storage", "getrandom", @@ -1826,6 +1906,7 @@ dependencies = [ "paste", "portable-atomic", "pretty-hex", + "rustc-hash", "sha2", "smoltcp", "snafu", @@ -1835,14 +1916,15 @@ dependencies = [ "subtle", "sunset", "sunset-async", + "sunset-sftp", "sunset-sshwire-derive", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_cell" @@ -1874,24 +1956,23 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] @@ -1902,8 +1983,8 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sunset" -version = "0.4.0" -source = "git+https://github.com/mkj/sunset#6b4e77f5210fd375f7ba7bf49940d7f1ac80c230" +version = "0.3.0" +source = "git+https://github.com/jubeormk1/sunset?branch=dev%2Fsftp-start#9d76de8a261736e8b4b8bfc35b61d6feccbf63a8" dependencies = [ "aes", "ascii", @@ -1933,8 +2014,8 @@ dependencies = [ [[package]] name = "sunset-async" -version = "0.4.0" -source = "git+https://github.com/mkj/sunset#6b4e77f5210fd375f7ba7bf49940d7f1ac80c230" +version = "0.3.0" +source = "git+https://github.com/jubeormk1/sunset?branch=dev%2Fsftp-start#9d76de8a261736e8b4b8bfc35b61d6feccbf63a8" dependencies = [ "embassy-futures", "embassy-sync 0.7.2", @@ -1944,10 +2025,26 @@ dependencies = [ "sunset", ] +[[package]] +name = "sunset-sftp" +version = "0.1.2" +source = "git+https://github.com/jubeormk1/sunset?branch=dev%2Fsftp-start#9d76de8a261736e8b4b8bfc35b61d6feccbf63a8" +dependencies = [ + "embassy-futures", + "embassy-sync 0.7.2", + "embedded-io-async 0.6.1", + "log", + "num_enum", + "paste", + "sunset", + "sunset-async", + "sunset-sshwire-derive", +] + [[package]] name = "sunset-sshwire-derive" -version = "0.2.1" -source = "git+https://github.com/mkj/sunset#6b4e77f5210fd375f7ba7bf49940d7f1ac80c230" +version = "0.2.0" +source = "git+https://github.com/jubeormk1/sunset?branch=dev%2Fsftp-start#9d76de8a261736e8b4b8bfc35b61d6feccbf63a8" dependencies = [ "virtue", ] @@ -1978,9 +2075,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -2028,9 +2125,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ufmt-write" @@ -2040,9 +2137,9 @@ checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" [[package]] name = "unicode-width" @@ -2108,86 +2205,28 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ "windows-sys", ] [[package]] -name = "windows-sys" -version = "0.59.0" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-targets" -version = "0.52.6" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 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", + "windows-link", ] -[[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.14" @@ -2236,25 +2275,25 @@ checksum = "96fb42cd29c42f8744c74276e9f5bee7b06685bbe5b88df891516d72cb320450" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.114", ] diff --git a/Cargo.toml b/Cargo.toml index 9902f07..e100737 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,32 @@ +# SPDX-FileCopyrightText: 2025 Roman Valls, 2025 +# +# SPDX-License-Identifier: GPL-3.0-or-later + [package] name = "ssh-stamp" version = "0.2.0" authors = [ "Julio Beltran Ortega ", "Anthony Tambasco ", "Roman Valls Guimera "] -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" [workspace] members = ["storage"] [workspace.dependencies] +sunset = { git = "https://github.com/jubeormk1/sunset", branch = "dev/sftp-start", default-features = false, features = [ + "openssh-key", + "embedded-io", +] } +sunset-async = { git = "https://github.com/jubeormk1/sunset", branch = "dev/sftp-start", default-features = false, features = [ + "multi-thread", +] } +sunset-sshwire-derive = { git = "https://github.com/jubeormk1/sunset", branch = "dev/sftp-start", default-features = false } +sunset-sftp = { git = "https://github.com/jubeormk1/sunset", branch = "dev/sftp-start", default-features = false } + sha2 = { version = "0.10", default-features = false } +rustc-hash = { version = "2.1", default-features = false } # For hash in the OTA SFTP server TODO: For the optimisation task: Remove this dependency and use sha2 instead? log = "0.4" esp-hal = { version = "1.0.0", features = ["unstable", "log-04"] } @@ -19,69 +34,71 @@ embedded-storage = "0.3.1" esp-storage = { version = "0.8.0" } esp-bootloader-esp-idf = { version = "0.4.0" } -sunset = { git = "https://github.com/mkj/sunset", default-features = false, features = ["openssh-key", "embedded-io"]} -sunset-async = { git = "https://github.com/mkj/sunset", default-features = false, features = ["multi-thread"]} -sunset-sshwire-derive = { git = "https://github.com/mkj/sunset", default-features = false } -# sunset = { path = "../sunset", default-features = false, features = ["openssh-key", "embedded-io"]} -# sunset-async = { path = "../sunset/async", default-features = false, features = ["multi-thread"]} -# sunset-sshwire-derive = { path = "../sunset/sshwire-derive", default-features = false } - -static_cell = { version = "2" } +static_cell = { version = "2.1" } # Used for storage::flash singleton once_cell = { version = "1", features = [ "critical-section", ], default-features = false } - [dependencies] storage = { path = "storage" } +sha2 = { workspace = true } +rustc-hash = { workspace = true } + cfg-if = "1" ed25519-dalek = { version = "2", default-features = false } -embassy-executor = { version = "0.9"} -embassy-net = { version = "0.7", features = ["tcp", "udp", "dhcpv4", "medium-ethernet"] } -smoltcp = { version = "0.12", default-features = false, features = ["medium-ethernet", "socket-raw"]} -embassy-time = { version = "0.5" } -embedded-io-async = "0.6" -esp-alloc = "0.9" -esp-backtrace = { version = "0.18", features = [ - "panic-handler", - "println", +embassy-executor = { version = "0.9.0" } +embassy-net = { version = "0.7", features = [ + "tcp", + "udp", + "dhcpv4", + "medium-ethernet", ] } -esp-hal = { version = "1.0.0", features = [ "unstable" ] } -#esp-hal-embassy = { version = "0.9" } -esp-println = { version = "0.16", features = ["log-04"]} -esp-radio = { version = "0.17", features = ["wifi"] } +smoltcp = { version = "0.12", default-features = false, features = [ + "medium-ethernet", + "socket-raw", +] } +embassy-time = { version = "0.5.0" } +embedded-io-async = "0.6.1" +esp-alloc = { version ="0.9.0", features = ["compat", "esp32"]} +esp-backtrace = { version = "0.18.0", features = ["panic-handler", "println"] } +esp-hal = { workspace = true } esp-rtos = { version = "0.2.0", features = ["embassy", "log-04", "esp-radio"] } +esp-radio = { version = "0.17.0", features = ["wifi", "log-04"] } +esp-println = { version = "0.16", features = ["log-04"] } hex = { version = "0.4", default-features = false } log = { version = "0.4" } -static_cell = { version = "2", features = ["nightly"] } +static_cell = { workspace = true } ssh-key = { version = "0.6", default-features = false, features = ["ed25519"] } sunset = { workspace = true } sunset-async = { workspace = true } sunset-sshwire-derive = { workspace = true } +sunset-sftp = { workspace = true } getrandom = { version = "0.2.10", features = ["custom"] } embassy-sync = "0.7" heapless = "0.8" -embassy-futures = "0.1" +embassy-futures = "0.1.2" edge-dhcp = "0.6" edge-nal = "0.5" -edge-nal-embassy = "0.7" +edge-nal-embassy = "0.6" #sequential-storage = { version = "4", features = ["heapless"] } -esp-storage = { version = "0.8" } -embedded-storage = "0.3" -embassy-embedded-hal = "0.5" +esp-storage = { workspace = true } +embedded-storage = { workspace = true } +embassy-embedded-hal = "0.3" bcrypt = { version = "0.17", default-features = false } subtle = { version = "2", default-features = false } hmac = { version = "0.12", default-features = false } -sha2 = { version = "0.10", default-features = false } -digest = { version = "0.10", default-features = false, features = ["rand_core", "subtle"] } +# sha2 = { version = "0.10", default-features = false } +digest = { version = "0.10", default-features = false, features = [ + "rand_core", + "subtle", +] } embedded-storage-async = "0.4" portable-atomic = "1" -esp-bootloader-esp-idf = { version = "0.4", features = ["esp-rom-sys"] } +esp-bootloader-esp-idf = { workspace = true } snafu = { version = "0.8", default-features = false } paste = "1" pretty-hex = { version = "0.4", default-features = false } -esp-rom-sys = { version = "0.1" } [profile.dev] # Rust debug is too slow. @@ -91,7 +108,7 @@ debug = 2 debug-assertions = true [profile.release] -codegen-units = 1 # LLVM can perform better optimizations using a single thread +codegen-units = 1 # LLVM can perform better optimizations using a single thread debug = 2 debug-assertions = true incremental = false @@ -101,7 +118,7 @@ overflow-checks = true [profile.esp32s2] inherits = "release" -opt-level = "s" # Optimize for size. +opt-level = "s" # Optimize for size. [profile.dev.package.esp-storage] opt-level = "s" @@ -111,80 +128,79 @@ opt-level = "s" [features] ipv6 = [] -default = ["esp32c6"] +default = ["esp32c6", "sftp-ota"] + +# Enables the SFTP OTA Subsystem. Use ota-packer to pack a binary and PUT it over sftp +sftp-ota = [] + # MCU options esp32 = [ "esp-hal/esp32", + "esp-alloc/esp32", "esp-backtrace/esp32", "esp-radio/esp32", "esp-rtos/esp32", - "esp-alloc/esp32", - "esp-rom-sys/esp32", - "esp-bootloader-esp-idf/esp32", "esp-println/esp32", "esp-storage/esp32", + "esp-bootloader-esp-idf/esp32", ] esp32c2 = [ "esp-hal/esp32c2", + "esp-alloc/esp32c2", "esp-backtrace/esp32c2", "esp-radio/esp32c2", "esp-rtos/esp32c2", - "esp-alloc/esp32c2", - "esp-rom-sys/esp32c2", - "esp-bootloader-esp-idf/esp32c2", "esp-println/esp32c2", "esp-storage/esp32c2", + "esp-bootloader-esp-idf/esp32c2", ] esp32c3 = [ "esp-hal/esp32c3", + "esp-alloc/esp32c3", "esp-backtrace/esp32c3", "esp-radio/esp32c3", "esp-rtos/esp32c3", - "esp-alloc/esp32c3", - "esp-rom-sys/esp32c3", - "esp-bootloader-esp-idf/esp32c3", "esp-println/esp32c3", "esp-storage/esp32c3", + "esp-bootloader-esp-idf/esp32c3", ] #esp32c5 = [ # "esp-hal/esp32c5", +# "esp-alloc/esp32c5", # "esp-backtrace/esp32c5", # "esp-radio/esp32c5", -# "esp-hal-embassy/esp32c5", +# "esp-rtos/esp32c5", # "esp-println/esp32c5", # "esp-storage/esp32c5", -# "embassy-executor/task-arena-size-40960", +# "esp-bootloader-esp-idf/esp32c5", #] esp32c6 = [ "esp-hal/esp32c6", + "esp-alloc/esp32c6", "esp-backtrace/esp32c6", "esp-radio/esp32c6", "esp-rtos/esp32c6", - "esp-alloc/esp32c6", - "esp-rom-sys/esp32c6", - "esp-bootloader-esp-idf/esp32c6", "esp-println/esp32c6", "esp-storage/esp32c6", + "esp-bootloader-esp-idf/esp32c6", ] esp32s2 = [ "esp-hal/esp32s2", + "esp-alloc/esp32s2", "esp-backtrace/esp32s2", "esp-radio/esp32s2", "esp-rtos/esp32s2", - "esp-alloc/esp32s2", - "esp-rom-sys/esp32s2", - "esp-bootloader-esp-idf/esp32s2", "esp-println/esp32s2", "esp-storage/esp32s2", + "esp-bootloader-esp-idf/esp32s2", ] esp32s3 = [ "esp-hal/esp32s3", + "esp-alloc/esp32s3", "esp-backtrace/esp32s3", "esp-radio/esp32s3", "esp-rtos/esp32s3", - "esp-alloc/esp32s3", - "esp-rom-sys/esp32s3", - "esp-bootloader-esp-idf/esp32s3", "esp-println/esp32s3", "esp-storage/esp32s3", + "esp-bootloader-esp-idf/esp32s3", ] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f3683df..5f7ec45 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,9 @@ +# SPDX-FileCopyrightText: 2025 Roman Valls, 2025 +# +# SPDX-License-Identifier: GPL-3.0-or-later + [toolchain] -#channel = "stable-2025-04-03" channel = "nightly" +#channel = "stable" components = ["rust-src"] targets = ["riscv32imac-unknown-none-elf"] diff --git a/src/config.rs b/src/config.rs index 135631d..b4c270c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,10 +14,10 @@ use sha2::Sha256; use subtle::ConstantTimeEq; use sunset::packets::Ed25519PubKey; -use sunset::{sshwire, KeyType, Result}; +use sunset::{KeyType, Result, sshwire}; use sunset::{ - sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, SignKey, + sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireError, WireResult}, }; use crate::settings::{DEFAULT_SSID, DEFAULT_UART_RX_PIN, DEFAULT_UART_TX_PIN, KEY_SLOTS}; @@ -165,7 +165,7 @@ pub(crate) fn enc_option_str( s: &mut dyn SSHSink, ) -> WireResult<()> { v.is_some().enc(s)?; - if let Some(ref st) = v { + if let Some(st) = v { st.as_str().enc(s)?; } Ok(()) diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 62edbbf..0f7d778 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -5,9 +5,9 @@ use core::net::Ipv4Addr; use core::str::FromStr; -// use embassy_executor::Spawner; -use embassy_net::{tcp::TcpSocket, Stack, StackResources}; +use embassy_executor::Spawner; use embassy_net::{IpListenEndpoint, Ipv4Cidr, Runner, StaticConfigV4}; +use embassy_net::{Stack, StackResources, tcp::TcpSocket}; use embassy_time::{Duration, Timer}; use esp_hal::peripherals::WIFI; @@ -15,10 +15,10 @@ use esp_hal::peripherals::WIFI; use esp_hal::rng::Rng; use esp_println::{dbg, println}; +use esp_radio::Controller; use esp_radio::wifi::WifiEvent; use esp_radio::wifi::{AccessPointConfig, ModeConfig, WifiApState, WifiController}; -use esp_radio::Controller; -// use sunset_async::SunsetMutex; +use sunset_async::SunsetMutex; use core::net::SocketAddrV4; use edge_dhcp; @@ -30,7 +30,7 @@ use edge_dhcp::{ use edge_nal::UdpBind; use edge_nal_embassy::{Udp, UdpBuffers}; -// use crate::config::SSHStampConfig; +use crate::config::SSHStampConfig; // use crate::settings::DEFAULT_SSID; use crate::settings::{DEFAULT_IP, DEFAULT_SSID}; @@ -47,21 +47,18 @@ macro_rules! mk_static { } use heapless::String; -pub async fn prepare_ap_stack<'a>( - controller: &'a Controller<'a>, - wifi: WIFI<'a>, + +pub async fn if_up( + spawner: Spawner, + controller: Controller<'static>, + wifi: WIFI<'static>, rng: Rng, -) -> Result< - ( - Stack<'a>, - Runner<'a, WifiDevice<'a>>, - WifiController<'a>, - Ipv4Addr, - ), - sunset::Error, -> { + config: &'static SunsetMutex, +) -> Result, sunset::Error> { + let wifi_init = &*mk_static!(Controller<'static>, controller); let (mut wifi_controller, interfaces) = - esp_radio::wifi::new(controller, wifi, Default::default()).unwrap(); + esp_radio::wifi::new(wifi_init, wifi, Default::default()).unwrap(); + let ap_config = ModeConfig::AccessPoint(AccessPointConfig::default().with_ssid(DEFAULT_SSID.into())); let res = wifi_controller.set_config(&ap_config); @@ -85,13 +82,10 @@ pub async fn prepare_ap_stack<'a>( seed, ); - Ok((ap_stack, runner, wifi_controller, gw_ip_addr_ipv4)) -} + spawner.spawn(wifi_up(wifi_controller, config)).ok(); + spawner.spawn(net_up(runner)).ok(); + spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); -pub async fn check_link<'a>( - ap_stack: Stack<'a>, - gw_ip_addr_ipv4: Ipv4Addr, -) -> Result, sunset::Error> { loop { println!("Checking if link is up...\n"); if ap_stack.is_link_up() { @@ -109,62 +103,6 @@ pub async fn check_link<'a>( Ok(ap_stack) } -// -// if_up split into separate steps: prepare_ap_stack -> wifi_up -> net_up -> run_dchp -> check_link -// HSM runs each separately to avoid using task spawner -// -// pub async fn if_up<'a>( -// wifi_controller: Controller<'a>, -// wifi: WIFI<'a>, -// rng: &mut Rng, -// config: &'static SunsetMutex, -// ) -> Result, sunset::Error> { -// let (mut controller, interfaces) = -// esp_radio::wifi::new(&wifi_controller, wifi, Default::default()).unwrap(); -// let ap_config = -// ModeConfig::AccessPoint(AccessPointConfig::default().with_ssid(DEFAULT_SSID.into())); -// let res = controller.set_config(&ap_config); -// println!("wifi_set_configuration returned {:?}", res); - -// let gw_ip_addr_ipv4 = DEFAULT_IP.clone(); - -// let net_config = embassy_net::Config::ipv4_static(StaticConfigV4 { -// address: Ipv4Cidr::new(gw_ip_addr_ipv4, 24), -// gateway: Some(gw_ip_addr_ipv4), -// dns_servers: Default::default(), -// }); - -// let seed = (rng.random() as u64) << 32 | rng.random() as u64; - -// // Init network stack -// let (ap_stack, _runner) = embassy_net::new( -// interfaces.ap, -// net_config, -// mk_static!(StackResources<3>, StackResources::<3>::new()), -// seed, -// ); - -// // spawner.spawn(wifi_up(controller, wifi_ssid)).ok(); -// // spawner.spawn(net_up(runner)).ok(); -// // spawner.spawn(dhcp_server(ap_stack, gw_ip_addr_ipv4)).ok(); - -// loop { -// println!("Checking if link is up...\n"); -// if ap_stack.is_link_up() { -// break; -// } -// Timer::after(Duration::from_millis(500)).await; -// } - -// // TODO: Use wifi_manager instead? -// println!( -// "Connect to the AP `ssh-stamp` as a DHCP client with IP: {}", -// gw_ip_addr_ipv4 -// ); - -// Ok(ap_stack) -// } - pub async fn accept_requests<'a>(stack: Stack<'a>, _uart: &BufferedUart) -> ! { // let rx_buffer = mk_static!([u8; 1536], [0; 1536]); // let tx_buffer = mk_static!([u8; 1536], [0; 1536]); @@ -197,23 +135,22 @@ pub async fn accept_requests<'a>(stack: Stack<'a>, _uart: &BufferedUart) -> ! { } } -// #[embassy_executor::task] +#[embassy_executor::task] pub async fn wifi_up( - // mut controller: WifiController<'static>, - mut wifi_controller: WifiController<'_>, - // config: &'static SunsetMutex, - wifi_ssid: String<32>, + mut wifi_controller: WifiController<'static>, + config: &'static SunsetMutex, ) { println!("Device capabilities: {:?}", wifi_controller.capabilities()); - // let wifi_ssid = { - // let guard = config.lock().await; - // guard.wifi_ssid.clone() - // // drop guard - // }; + let wifi_ssid = { + let guard = config.lock().await; + guard.wifi_ssid.clone() + // drop guard + }; // TODO: No wifi password(s) yet... //let wifi_password = config.lock().await.wifi_pw; println!("Starting Wifi"); + loop { if esp_radio::wifi::ap_state() == WifiApState::Started { // wait until we're no longer connected @@ -234,20 +171,16 @@ pub async fn wifi_up( Timer::after(Duration::from_millis(10)).await; } } + use esp_radio::wifi::WifiDevice; -// #[embassy_executor::task] -pub async fn net_up<'a>(mut runner: Runner<'a, WifiDevice<'a>>) { +#[embassy_executor::task] +async fn net_up(mut runner: Runner<'static, WifiDevice<'static>>) { println!("Bringing up network stack...\n"); runner.run().await } -pub async fn run_dchp<'a>(ap_stack: Stack<'a>, gw_ip_addr_ipv4: Ipv4Addr) { - // let gw_ip_addr_ipv4 = DEFAULT_IP.clone(); - dhcp_server(ap_stack, gw_ip_addr_ipv4).await; -} - -// #[embassy_executor::task] -async fn dhcp_server<'a>(stack: Stack<'a>, ip: Ipv4Addr) { +#[embassy_executor::task] +async fn dhcp_server(stack: Stack<'static>, ip: Ipv4Addr) { let mut buf = [0u8; 1500]; let mut gw_buf = [Ipv4Addr::UNSPECIFIED]; diff --git a/src/main.rs b/src/main.rs index 1399f14..3e1a4d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,10 +10,9 @@ use core::result::Result::Err; use core::result::Result::Ok; // use core::error::Error; use core::future::Future; -use core::net::Ipv4Addr; use embassy_executor::Spawner; -use embassy_futures::select::{select3, Either3}; -use embassy_net::{tcp::TcpSocket, IpListenEndpoint, Runner, Stack}; +use embassy_futures::select::{Either3, select3}; +use embassy_net::{IpListenEndpoint, Stack, tcp::TcpSocket}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use esp_hal::system::software_reset; use esp_hal::{ @@ -26,10 +25,6 @@ use esp_hal::{ }; use esp_println::dbg; use esp_println::println; -use esp_radio::wifi::WifiController; -// use esp_radio::wifi::WifiController; -// use esp_radio::Controller -use esp_radio::wifi::WifiDevice; use esp_radio::{Controller, InitializationError}; use ssh_stamp::{ config::SSHStampConfig, From ab806cf0e676e6ad92e70a3335c764ab0fbc30d4 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Wed, 11 Feb 2026 15:50:44 +1100 Subject: [PATCH 62/86] Remove ESP32 feature from esp-alloc --- Cargo.lock | 4 +- Cargo.toml | 4 +- src/main.rs | 359 ++++++++++++++++++++-------------------------------- 3 files changed, 141 insertions(+), 226 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc32c00..552960e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,9 +410,9 @@ dependencies = [ [[package]] name = "edge-nal-embassy" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252f89adf4f0016631977bec3ba50d768263a3a9fa9f023b4087088a619568ce" +checksum = "fb09ea0c604bbcb694ecf9cf6596bc5f59bbb5360b68c3e471b61ae9a55a8533" dependencies = [ "edge-nal", "embassy-futures", diff --git a/Cargo.toml b/Cargo.toml index e100737..8c36b99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ smoltcp = { version = "0.12", default-features = false, features = [ ] } embassy-time = { version = "0.5.0" } embedded-io-async = "0.6.1" -esp-alloc = { version ="0.9.0", features = ["compat", "esp32"]} +esp-alloc = { version ="0.9.0"} esp-backtrace = { version = "0.18.0", features = ["panic-handler", "println"] } esp-hal = { workspace = true } esp-rtos = { version = "0.2.0", features = ["embassy", "log-04", "esp-radio"] } @@ -80,7 +80,7 @@ heapless = "0.8" embassy-futures = "0.1.2" edge-dhcp = "0.6" edge-nal = "0.5" -edge-nal-embassy = "0.6" +edge-nal-embassy = "0.7" #sequential-storage = { version = "4", features = ["heapless"] } esp-storage = { workspace = true } embedded-storage = { workspace = true } diff --git a/src/main.rs b/src/main.rs index 3e1a4d2..1078a66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,14 +15,30 @@ use embassy_futures::select::{Either3, select3}; use embassy_net::{IpListenEndpoint, Stack, tcp::TcpSocket}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use esp_hal::system::software_reset; + use esp_hal::{ gpio::Pin, - interrupt::software::SoftwareInterruptControl, - peripherals::{GPIO10, GPIO11, UART1}, - peripherals::{SW_INTERRUPT, SYSTIMER, TIMG1, WIFI}, + peripherals::{GPIO1, GPIO3, UART1}, + peripherals::{SW_INTERRUPT, WIFI}, rng::Rng, uart::{Config, RxConfig, Uart}, }; + +cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + use esp_hal::timer::timg::TimerGroup; + use esp_hal::peripherals::TIMG1; + } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { + use esp_hal::peripherals::SYSTIMER; + } else if #[cfg(any(feature = "esp32c2"))] { + use esp_hal::interrupt::software::SoftwareInterruptControl; + use esp_hal::peripherals::SYSTIMER; + } else { + use esp_hal::interrupt::software::SoftwareInterruptControl; + use esp_hal::peripherals::SYSTIMER; + } +} + use esp_println::dbg; use esp_println::println; use esp_radio::{Controller, InitializationError}; @@ -48,8 +64,14 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a let peripherals = esp_hal::init(esp_hal::Config::default()); let rng = Rng::new(); rng::register_custom_rng(rng); - let timg1 = peripherals.TIMG1; - let systimer = peripherals.SYSTIMER; + + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let timg1 = peripherals.TIMG1; + } else { + let systimer = peripherals.SYSTIMER; + } + ); // Read SSH configuration from Flash (if it exists) flash::init(peripherals.FLASH); @@ -69,21 +91,36 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a let config = CONFIG.init(SunsetMutex::new(config)); let wifi = peripherals.WIFI; - let gpio10 = peripherals.GPIO10; - let gpio11 = peripherals.GPIO11; let uart1 = peripherals.UART1; let sw_interrupt = peripherals.SW_INTERRUPT; - let ssh_stamp_peripherals = SshStampPeripherals { - rng: rng, - wifi: wifi, - config: config, - gpio10: gpio10, - gpio11: gpio11, - uart1: uart1, - timg1: timg1, - systimer: systimer, - sw_interrupt: sw_interrupt, + let gpios = GPIOS { + gpio1: peripherals.GPIO1, + gpio3: peripherals.GPIO3, }; + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let ssh_stamp_peripherals = SshStampPeripherals { + rng: rng, + wifi: wifi, + config: config, + gpios: gpios, + uart1: uart1, + timg1: timg1, + sw_interrupt: sw_interrupt, + }; + } else { + let ssh_stamp_peripherals = SshStampPeripherals { + rng: rng, + wifi: wifi, + config: config, + gpios: gpios, + uart1: uart1, + systimer: systimer, + sw_interrupt: sw_interrupt, + }; + } + ); + ssh_stamp_peripherals } @@ -163,8 +200,7 @@ pub async fn enable_uart<'a, 'b>( uart1: UART1<'a>, // pin_channel: &'b mut PinChannel<'a>, config: &'a SunsetMutex, - gpio10: GPIO10<'_>, - gpio11: GPIO11<'_>, + gpios: GPIOS<'_>, ) where 'a: 'b, { @@ -173,17 +209,17 @@ pub async fn enable_uart<'a, 'b>( let rx: u8 = config_lock.uart_pins.rx; let tx: u8 = config_lock.uart_pins.tx; if rx != tx { - let mut holder10 = Some(gpio10); - let mut holder11 = Some(gpio11); + let mut holder1 = Some(gpios.gpio1); + let mut holder3 = Some(gpios.gpio3); let rx_pin = match rx { - 10 => holder10.take().unwrap().degrade(), - 11 => holder11.take().unwrap().degrade(), - _ => holder10.take().unwrap().degrade(), + 1 => holder1.take().unwrap().degrade(), + 3 => holder3.take().unwrap().degrade(), + _ => holder1.take().unwrap().degrade(), }; let tx_pin = match tx { - 10 => holder10.take().unwrap().degrade(), - 11 => holder11.take().unwrap().degrade(), - _ => holder11.take().unwrap().degrade(), + 1 => holder1.take().unwrap().degrade(), + 3 => holder3.take().unwrap().degrade(), + _ => holder3.take().unwrap().degrade(), }; // Hardware UART setup @@ -256,29 +292,45 @@ pub async fn bridge_disable() -> () { software_reset(); } -pub struct SshStampPeripherals<'a> { - pub rng: Rng, - pub wifi: WIFI<'a>, - pub config: &'a SunsetMutex, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, - pub uart1: UART1<'a>, - pub timg1: TIMG1<'a>, - pub systimer: SYSTIMER<'a>, - pub sw_interrupt: SW_INTERRUPT<'a>, -} +cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + pub struct SshStampPeripherals<'a> { + pub rng: Rng, + pub wifi: WIFI<'a>, + pub config: &'a SunsetMutex, + pub gpios: GPIOS<'a>, + pub uart1: UART1<'a>, + pub timg1: TIMG1<'a>, + pub sw_interrupt: SW_INTERRUPT<'a>, + } + } else { + pub struct SshStampPeripherals<'a> { + pub rng: Rng, + pub wifi: WIFI<'a>, + pub config: &'a SunsetMutex, + pub gpios: GPIOS<'a>, + pub uart1: UART1<'a>, + pub systimer: SYSTIMER<'a>, + pub sw_interrupt: SW_INTERRUPT<'a>, + } + } +); +pub struct GPIOS<'a> { + pub gpio1: GPIO1<'a>, + pub gpio3: GPIO3<'a>, +} pub struct SshStampInit<'a> { pub rng: Rng, pub wifi: WIFI<'a>, pub config: &'a SunsetMutex, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, + pub gpios: GPIOS<'a>, pub uart1: UART1<'a>, + pub spawner: Spawner, } #[esp_rtos::main] -async fn main(_spawner: Spawner) -> ! { +async fn main(spawner: Spawner) -> ! { cfg_if::cfg_if!( if #[cfg(feature = "esp32s2")] { // TODO: This heap size will crash at runtime, we need to fix this @@ -293,17 +345,22 @@ async fn main(_spawner: Spawner) -> ! { let peripherals = peripherals_wait_for_initialisation().await; - let sw_int = SoftwareInterruptControl::new(peripherals.sw_interrupt); - cfg_if::cfg_if! { - if #[cfg(feature = "esp32")] { + if #[cfg(feature = "esp32")] { + // TODO: Test this feature configuration let timg1 = TimerGroup::new(peripherals.timg1); - esp_rtos::start(timg1.timer0, sw_int); - } else { - use esp_hal::timer::systimer::SystemTimer; - let systimer = SystemTimer::new(peripherals.systimer); - esp_rtos::start(systimer.alarm0, sw_int.software_interrupt0); - } + esp_rtos::start(timg1.timer0); + } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { + // TODO: Test this feature configuration + use esp_hal::timer::systimer::SystemTimer; + let systimer = SystemTimer::new(peripherals.systimer); + esp_rtos::start(systimer.alarm0); + } else { + let sw_int = SoftwareInterruptControl::new(peripherals.sw_interrupt); + use esp_hal::timer::systimer::SystemTimer; + let systimer = SystemTimer::new(peripherals.systimer); + esp_rtos::start(systimer.alarm0, sw_int.software_interrupt0); + } } // TODO: Migrate this function/test to embedded-test. @@ -314,9 +371,9 @@ async fn main(_spawner: Spawner) -> ! { rng: peripherals.rng, wifi: peripherals.wifi, config: peripherals.config, - gpio10: peripherals.gpio10, - gpio11: peripherals.gpio11, + gpios: peripherals.gpios, uart1: peripherals.uart1, + spawner: spawner, }; match peripherals_enabled(peripherals_enabled_struct).await { @@ -335,12 +392,12 @@ pub struct PeripheralsEnabled<'a> { pub rng: Rng, pub wifi: WIFI<'a>, pub config: &'a SunsetMutex, - pub controller: Controller<'a>, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, + pub controller: Controller<'static>, + pub gpios: GPIOS<'a>, pub uart1: UART1<'a>, + pub spawner: Spawner, } -async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Error> { +async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Error> { let controller = wifi_wait_for_initialisation().await; let peripherals_enabled_struct = PeripheralsEnabled { @@ -348,9 +405,9 @@ async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Erro wifi: s.wifi, config: s.config, controller: controller.unwrap(), - gpio10: s.gpio10, - gpio11: s.gpio11, + gpios: s.gpios, uart1: s.uart1, + spawner: s.spawner, }; match wifi_controller_enabled(peripherals_enabled_struct).await { Ok(_) => (), @@ -366,177 +423,42 @@ async fn peripherals_enabled<'a>(s: SshStampInit<'a>) -> Result<(), sunset::Erro pub struct WifiControllerEnabled<'a> { pub rng: Rng, pub config: &'a SunsetMutex, - pub wifi_controller: WifiController<'a>, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, + pub gpios: GPIOS<'a>, pub uart1: UART1<'a>, - pub ap_stack: Stack<'a>, - pub runner: Runner<'a, WifiDevice<'a>>, - pub ip: Ipv4Addr, -} -pub async fn wifi_controller_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { - let (ap_stack, runner, wifi_controller, ip) = - net::prepare_ap_stack(&s.controller, s.wifi, s.rng) - .await - .unwrap(); - let wifi_controller_enabled_stack = WifiControllerEnabled { - config: s.config, - rng: s.rng, - wifi_controller: wifi_controller, - gpio10: s.gpio10, - gpio11: s.gpio11, - uart1: s.uart1, - ap_stack: ap_stack, - runner: runner, - ip: ip, - }; - match ap_stack_enabled(wifi_controller_enabled_stack).await { - Ok(_) => (), - Err(e) => { - println!("AP Stack error: {}", e); - } - } - ap_stack_disable().await; - Ok(()) // todo!() return relevant value -} - -pub struct ApStackEnabled<'a> { - pub rng: Rng, - pub config: &'a SunsetMutex, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, - pub uart1: UART1<'a>, - pub ap_stack: Stack<'a>, - pub runner: Runner<'a, WifiDevice<'a>>, - pub ip: Ipv4Addr, -} - -pub async fn ap_stack_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Error> { - let wifi_ssid_config = { - let guard = s.config.lock().await; - guard.wifi_ssid.clone() - }; - - net::wifi_up(s.wifi_controller, wifi_ssid_config).await; - - let ap_stack_enabled_stack = ApStackEnabled { - config: s.config, - rng: s.rng, - gpio10: s.gpio10, - gpio11: s.gpio11, - uart1: s.uart1, - ap_stack: s.ap_stack, - runner: s.runner, - ip: s.ip, - }; - match wifi_enabled(ap_stack_enabled_stack).await { - Ok(_) => (), - Err(e) => { - println!("AP Stack error: {}", e); - } - } - wifi_disable().await; // drop wifi - Ok(()) // todo!() return relevant value -} - -pub struct WifiEnabled<'a> { - pub rng: Rng, - pub config: &'a SunsetMutex, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, - pub uart1: UART1<'a>, - pub ap_stack: Stack<'a>, - pub ip: Ipv4Addr, -} - -pub async fn wifi_enabled<'a>(s: ApStackEnabled<'a>) -> Result<(), sunset::Error> { - net::net_up(s.runner).await; - - let wifi_enabled_stack = WifiEnabled { - config: s.config, - rng: s.rng, - gpio10: s.gpio10, - gpio11: s.gpio11, - uart1: s.uart1, - ap_stack: s.ap_stack, - ip: s.ip, - }; - match network_enabled(wifi_enabled_stack).await { - Ok(_) => (), - Err(e) => { - println!("Network error: {}", e); - } - } - network_disable().await; // drop network - Ok(()) // todo!() return relevant value + pub tcp_stack: Stack<'a>, } -pub struct NetworkEnabled<'a> { - pub config: &'a SunsetMutex, - pub rng: Rng, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, - pub uart1: UART1<'a>, - pub ap_stack: Stack<'a>, - pub ip: Ipv4Addr, -} -pub async fn network_enabled<'a>(s: WifiEnabled<'a>) -> Result<(), sunset::Error> { - net::run_dchp(s.ap_stack, s.ip).await; +// pub async fn wifi_controller_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { +pub async fn wifi_controller_enabled(s: PeripheralsEnabled<'static>) -> Result<(), sunset::Error> { + let tcp_stack = net::if_up(s.spawner, s.controller, s.wifi, s.rng, s.config) + .await + .unwrap(); - let network_enabled_struct = NetworkEnabled { + let wifi_controller_enabled_stack = WifiControllerEnabled { config: s.config, - ap_stack: s.ap_stack, rng: s.rng, - gpio10: s.gpio10, - gpio11: s.gpio11, + gpios: s.gpios, uart1: s.uart1, - ip: s.ip, - }; - match dhcp_enabled(network_enabled_struct).await { - Ok(_) => (), - Err(e) => { - println!("DHCP error: {}", e); - } - } - dhcp_disable().await; - Ok(()) // todo!() return relevant value -} - -pub struct DhcpEnabled<'a> { - pub config: &'a SunsetMutex, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, - pub uart1: UART1<'a>, - pub tcp_stack: Stack<'a>, -} -async fn dhcp_enabled<'a>(s: NetworkEnabled<'a>) -> Result<(), sunset::Error> { - let tcp_stack = net::check_link(s.ap_stack, s.ip).await.unwrap(); - let dhcp_enabled_struct = DhcpEnabled { - config: s.config, tcp_stack: tcp_stack, - gpio10: s.gpio10, - gpio11: s.gpio11, - uart1: s.uart1, }; - match tcp_enabled(dhcp_enabled_struct).await { + match tcp_enabled(wifi_controller_enabled_stack).await { Ok(_) => (), Err(e) => { - println!("TCP stack error: {}", e); + println!("AP Stack error: {}", e); } } - tcp_stack_disable().await; + ap_stack_disable().await; Ok(()) // todo!() return relevant value } pub struct TCPEnabled<'a> { pub config: &'a SunsetMutex, pub tcp_socket: TcpSocket<'a>, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, + pub gpios: GPIOS<'a>, pub uart1: UART1<'a>, } -async fn tcp_enabled<'a>(s: DhcpEnabled<'a>) -> Result<(), sunset::Error> { +async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Error> { let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; let mut tcp_socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); @@ -556,8 +478,7 @@ async fn tcp_enabled<'a>(s: DhcpEnabled<'a>) -> Result<(), sunset::Error> { let tcp_enabled_struct = TCPEnabled { config: s.config, tcp_socket: tcp_socket, - gpio10: s.gpio10, - gpio11: s.gpio11, + gpios: s.gpios, uart1: s.uart1, }; match socket_enabled(tcp_enabled_struct).await { @@ -574,8 +495,7 @@ pub struct SocketEnabled<'a> { pub config: &'a SunsetMutex, pub tcp_socket: TcpSocket<'a>, pub ssh_server: SSHServer<'a>, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, + pub gpios: GPIOS<'a>, pub uart1: UART1<'a>, } async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { @@ -588,8 +508,7 @@ async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { config: s.config, tcp_socket: s.tcp_socket, ssh_server: ssh_server, - gpio10: s.gpio10, - gpio11: s.gpio11, + gpios: s.gpios, uart1: s.uart1, }; match ssh_enabled(socket_enabled_struct).await { @@ -614,8 +533,7 @@ where pub config: &'a SunsetMutex, pub chan_pipe: &'b Channel, pub connection_loop: CL, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, + pub gpios: GPIOS<'a>, } async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> // where @@ -634,8 +552,7 @@ async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> chan_pipe: &chan_pipe, connection_loop: connection, uart1: s.uart1, - gpio10: s.gpio10, - gpio11: s.gpio11, + gpios: s.gpios, }; match client_connected(ssh_enabled_struct).await { Ok(_) => (), @@ -660,8 +577,7 @@ where pub uart_buff: &'a BufferedUart, pub uart1: UART1<'a>, pub config: &'a SunsetMutex, - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, + pub gpios: GPIOS<'a>, } async fn client_connected<'a, 'b, CL>(s: SshEnabled<'a, 'b, CL>) -> Result<(), sunset::Error> // where 'b: 'a { @@ -678,8 +594,7 @@ where uart_buff: uart_buff, uart1: s.uart1, config: s.config, - gpio10: s.gpio10, - gpio11: s.gpio11, + gpios: s.gpios, }; match uart_buffer_ready(client_connected_struct).await { Ok(_) => (), @@ -709,7 +624,7 @@ where CL: Future>, { // loop { - let _uart = enable_uart(s.uart_buff, s.uart1, &s.config, s.gpio10, s.gpio11); + let _uart = enable_uart(s.uart_buff, s.uart1, &s.config, s.gpios); let uart_buffer_ready_struct = UartBufferReady { tcp_socket: s.tcp_socket, From 854c1f62ff75279e62d0b06217cfcab09b444dd4 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Wed, 11 Feb 2026 15:52:53 +1100 Subject: [PATCH 63/86] Update config.toml --- .cargo/config.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index c7f99a2..66d0a6b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -69,7 +69,8 @@ target = "riscv32imac-unknown-none-elf" # target = "x86_64-unknown-linux-gnu" [unstable] -build-std = [] +build-std = ["core","alloc"] +# build-std = [] # TODO Resolve build-std for different targets on stable channel. # build-std = ["core","alloc"] # For ESP32, ESP32-S2, ESP32-S3: When using stable channel enable this. -Z option only works on nightly channel. # build-std core and alloc has been moved as per alias parameter since From 38927ba0e31fa80c01a6c16d146914a626fc23fb Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 12 Feb 2026 00:03:06 +1100 Subject: [PATCH 64/86] Add different GPIO for ESP32 vs other ESP32 targets. --- src/main.rs | 78 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1078a66..b8c7344 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use esp_hal::system::software_reset; use esp_hal::{ gpio::Pin, - peripherals::{GPIO1, GPIO3, UART1}, + peripherals::UART1, peripherals::{SW_INTERRUPT, WIFI}, rng::Rng, uart::{Config, RxConfig, Uart}, @@ -27,15 +27,14 @@ use esp_hal::{ cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { use esp_hal::timer::timg::TimerGroup; - use esp_hal::peripherals::TIMG1; + use esp_hal::peripherals::{TIMG1, GPIO1, GPIO3}; } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { - use esp_hal::peripherals::SYSTIMER; + use esp_hal::peripherals::{SYSTIMER, GPIO10, GPIO11}; } else if #[cfg(any(feature = "esp32c2"))] { - use esp_hal::interrupt::software::SoftwareInterruptControl; - use esp_hal::peripherals::SYSTIMER; + use esp_hal::peripherals::{SYSTIMER, GPIO10, GPIO11}; } else { - use esp_hal::interrupt::software::SoftwareInterruptControl; - use esp_hal::peripherals::SYSTIMER; + // use esp_hal::interrupt::software::SoftwareInterruptControl; + use esp_hal::peripherals::{SYSTIMER, GPIO10, GPIO11}; } } @@ -93,10 +92,21 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a let wifi = peripherals.WIFI; let uart1 = peripherals.UART1; let sw_interrupt = peripherals.SW_INTERRUPT; - let gpios = GPIOS { - gpio1: peripherals.GPIO1, - gpio3: peripherals.GPIO3, - }; + + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let gpios = GPIOS { + gpio1: peripherals.GPIO1, + gpio3: peripherals.GPIO3, + }; + } else { + let gpios = GPIOS { + gpio10: peripherals.GPIO10, + gpio11: peripherals.GPIO11, + }; + } + ); + cfg_if::cfg_if!( if #[cfg(feature = "esp32")] { let ssh_stamp_peripherals = SshStampPeripherals { @@ -209,17 +219,29 @@ pub async fn enable_uart<'a, 'b>( let rx: u8 = config_lock.uart_pins.rx; let tx: u8 = config_lock.uart_pins.tx; if rx != tx { - let mut holder1 = Some(gpios.gpio1); - let mut holder3 = Some(gpios.gpio3); + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let mut holder0 = Some(gpios.gpio1); + let mut holder1 = Some(gpios.gpio3); + } else { + let mut holder0 = Some(gpios.gpio10); + let mut holder1 = Some(gpios.gpio11); + } + ); + let rx_pin = match rx { - 1 => holder1.take().unwrap().degrade(), - 3 => holder3.take().unwrap().degrade(), - _ => holder1.take().unwrap().degrade(), + 1 => holder0.take().unwrap().degrade(), + 3 => holder1.take().unwrap().degrade(), + 10 => holder0.take().unwrap().degrade(), + 11 => holder1.take().unwrap().degrade(), + _ => holder0.take().unwrap().degrade(), }; let tx_pin = match tx { - 1 => holder1.take().unwrap().degrade(), - 3 => holder3.take().unwrap().degrade(), - _ => holder3.take().unwrap().degrade(), + 1 => holder0.take().unwrap().degrade(), + 3 => holder1.take().unwrap().degrade(), + 10 => holder0.take().unwrap().degrade(), + 11 => holder1.take().unwrap().degrade(), + _ => holder1.take().unwrap().degrade(), }; // Hardware UART setup @@ -316,10 +338,20 @@ cfg_if::cfg_if!( } ); -pub struct GPIOS<'a> { - pub gpio1: GPIO1<'a>, - pub gpio3: GPIO3<'a>, -} +cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + pub struct GPIOS<'a> { + pub gpio1: GPIO1<'a>, + pub gpio3: GPIO3<'a>, + } + } else { + pub struct GPIOS<'a> { + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, + } + } +); + pub struct SshStampInit<'a> { pub rng: Rng, pub wifi: WIFI<'a>, From 585679631c1c9cf145293ff17efe8b3604fadf37 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:12:15 +1100 Subject: [PATCH 65/86] Change uart_task to embassy future, and poll at last HSM state in select4. --- src/main.rs | 67 +++++++++++++++++++++++++++++++++++----------------- src/serve.rs | 14 +++++++++-- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/main.rs b/src/main.rs index b8c7344..569b392 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use core::result::Result::Ok; // use core::error::Error; use core::future::Future; use embassy_executor::Spawner; -use embassy_futures::select::{Either3, select3}; +use embassy_futures::select::{Either4, select4}; use embassy_net::{IpListenEndpoint, Stack, tcp::TcpSocket}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use esp_hal::system::software_reset; @@ -31,9 +31,10 @@ cfg_if::cfg_if! { } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { use esp_hal::peripherals::{SYSTIMER, GPIO10, GPIO11}; } else if #[cfg(any(feature = "esp32c2"))] { + use esp_hal::interrupt::software::SoftwareInterruptControl; use esp_hal::peripherals::{SYSTIMER, GPIO10, GPIO11}; } else { - // use esp_hal::interrupt::software::SoftwareInterruptControl; + use esp_hal::interrupt::software::SoftwareInterruptControl; use esp_hal::peripherals::{SYSTIMER, GPIO10, GPIO11}; } } @@ -205,15 +206,12 @@ pub async fn uart_buffer_disable() -> () { software_reset(); } -pub async fn enable_uart<'a, 'b>( +pub async fn uart_task<'a>( uart_buf: &'a BufferedUart, uart1: UART1<'a>, - // pin_channel: &'b mut PinChannel<'a>, config: &'a SunsetMutex, gpios: GPIOS<'_>, -) where - 'a: 'b, -{ +) -> Result<(), sunset::Error> { dbg!("Configuring UART"); let config_lock = config.lock().await; let rx: u8 = config_lock.uart_pins.rx; @@ -260,6 +258,7 @@ pub async fn enable_uart<'a, 'b>( uart_buf.run(uart).await; } // TODO: Pin config error + Ok(()) } pub async fn uart_disable() -> () { @@ -360,7 +359,6 @@ pub struct SshStampInit<'a> { pub uart1: UART1<'a>, pub spawner: Spawner, } - #[esp_rtos::main] async fn main(spawner: Spawner) -> ! { cfg_if::cfg_if!( @@ -376,7 +374,6 @@ async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); let peripherals = peripherals_wait_for_initialisation().await; - cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { // TODO: Test this feature configuration @@ -430,6 +427,7 @@ pub struct PeripheralsEnabled<'a> { pub spawner: Spawner, } async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Error> { + println!("HSM: peripherals_enabled"); let controller = wifi_wait_for_initialisation().await; let peripherals_enabled_struct = PeripheralsEnabled { @@ -462,6 +460,7 @@ pub struct WifiControllerEnabled<'a> { // pub async fn wifi_controller_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { pub async fn wifi_controller_enabled(s: PeripheralsEnabled<'static>) -> Result<(), sunset::Error> { + println!("HSM: wifi_controller_enabled"); let tcp_stack = net::if_up(s.spawner, s.controller, s.wifi, s.rng, s.config) .await .unwrap(); @@ -491,6 +490,7 @@ pub struct TCPEnabled<'a> { } async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Error> { + println!("HSM: tcp_enabled"); let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; let mut tcp_socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); @@ -531,11 +531,15 @@ pub struct SocketEnabled<'a> { pub uart1: UART1<'a>, } async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { + println!("HSM: socket_enabled"); // loop { // Spawn network tasks to handle incoming connections with demo_common::session() let mut inbuf = [0u8; UART_BUFFER_SIZE]; let mut outbuf = [0u8; UART_BUFFER_SIZE]; + println!("Starting ssh_server"); let ssh_server = ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; + println!("Started ssh_server"); + let socket_enabled_struct = SocketEnabled { config: s.config, tcp_socket: s.tcp_socket, @@ -567,15 +571,18 @@ where pub connection_loop: CL, pub gpios: GPIOS<'a>, } + async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> // where // 'b: 'a, { + println!("HSM: ssh_enabled"); // loop { + println!("Starting channel pipe"); let chan_pipe = Channel::::new(); - - println!("Calling connection_loop from uart_enabled"); + println!("Started channel pipe. Calling connection_loop from ssh_enabled"); let connection = idle_wait_for_connection(&s.ssh_server, &chan_pipe, &s.config); + println!("Started connection loop"); let ssh_enabled_struct = SshEnabled { tcp_socket: s.tcp_socket, @@ -616,8 +623,10 @@ async fn client_connected<'a, 'b, CL>(s: SshEnabled<'a, 'b, CL>) -> Result<(), s where CL: Future>, { + println!("HSM: client_connected"); // loop { let uart_buff = uart_buffer_wait_for_initialisation().await; + let client_connected_struct = ClientConnected { tcp_socket: s.tcp_socket, ssh_server: s.ssh_server, @@ -640,23 +649,28 @@ where Ok(()) // todo!() return relevant value } -pub struct UartBufferReady<'a, 'b, CL> +pub struct UartBufferReady<'a, 'b, CL, U> where CL: Future>, + U: Future>, { pub tcp_socket: TcpSocket<'a>, pub ssh_server: &'b SSHServer<'a>, pub uart_buff: &'a BufferedUart, pub chan_pipe: &'b Channel, pub connection_loop: CL, + pub uart: U, } + async fn uart_buffer_ready<'a, 'b, CL>(s: ClientConnected<'a, 'b, CL>) -> Result<(), sunset::Error> where // 'b: 'a, CL: Future>, { + println!("HSM: uart_buffer_ready"); + // loop { - let _uart = enable_uart(s.uart_buff, s.uart1, &s.config, s.gpios); + let uart = uart_task(s.uart_buff, s.uart1, &s.config, s.gpios); let uart_buffer_ready_struct = UartBufferReady { tcp_socket: s.tcp_socket, @@ -665,6 +679,7 @@ where // uart_pins: &s.uart_pins, uart_buff: s.uart_buff, connection_loop: s.connection_loop, + uart: uart, }; match uart_enabled(uart_buffer_ready_struct).await { Ok(_) => (), @@ -683,23 +698,27 @@ pub struct UartEnabledConsumed<'a, 'b> { pub ssh_server: &'b SSHServer<'a>, pub chan_pipe: &'b Channel, } -pub struct UartEnabled<'a, 'b, CL, BR> +pub struct UartEnabled<'a, 'b, CL, U, BR> where CL: Future>, + U: Future>, BR: Future>, { pub ssh_server: &'b SSHServer<'a>, pub bridge: BR, pub connection_loop: CL, + pub uart: U, pub tcp_socket: TcpSocket<'a>, } -async fn uart_enabled<'a, 'b, CL>(s: UartBufferReady<'a, 'b, CL>) -> Result<(), sunset::Error> +async fn uart_enabled<'a, 'b, CL, U>(s: UartBufferReady<'a, 'b, CL, U>) -> Result<(), sunset::Error> where CL: Future>, + U: Future>, 'a: 'b, { - // loop { + println!("HSM: uart_enabled"); + // // loop { let uart_enabled_consumed = UartEnabledConsumed { uart_buff: s.uart_buff, ssh_server: s.ssh_server, @@ -713,6 +732,7 @@ where ssh_server: s.ssh_server, bridge: bridge, connection_loop: s.connection_loop, + uart: s.uart, tcp_socket: s.tcp_socket, }; match bridge_connected(uart_enabled_struct).await { @@ -728,14 +748,16 @@ where Ok(()) // todo!() return relevant value } -async fn bridge_connected<'a, 'b, CL, BR>( - s: UartEnabled<'a, 'b, CL, BR>, +async fn bridge_connected<'a, 'b, CL, U, BR>( + s: UartEnabled<'a, 'b, CL, U, BR>, ) -> Result<(), sunset::Error> where CL: Future>, + U: Future>, BR: Future>, 'a: 'b, { + println!("HSM: bridge_connected"); let mut tcp_socket = s.tcp_socket; let (mut rsock, mut wsock) = tcp_socket.split(); println!("Running server from handle_ssh_client()"); @@ -743,10 +765,11 @@ where let connection_loop = s.connection_loop; let bridge = s.bridge; println!("Main select() in bridge_connected()"); - match select3(server, connection_loop, bridge).await { - Either3::First(r) => r, - Either3::Second(r) => r, - Either3::Third(r) => r, + match select4(server, connection_loop, bridge, s.uart).await { + Either4::First(r) => r, + Either4::Second(r) => r, + Either4::Third(r) => r, + Either4::Fourth(r) => r, }?; Result::Ok(()) } diff --git a/src/serve.rs b/src/serve.rs index bb7af0b..02044b3 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -19,7 +19,7 @@ use sunset_async::SunsetMutex; use heapless::String; // use sunset::sshwire::SSHEncode; -use sunset::{error, ChanHandle, ServEvent, SignKey}; +use sunset::{ChanHandle, ServEvent, SignKey, error}; use sunset_async::{ProgressHolder, SSHServer}; use esp_println::{dbg, println}; @@ -47,6 +47,7 @@ pub async fn connection_loop<'a>( match ev { // #[cfg(feature = "sftp-ota")] ServEvent::SessionSubsystem(a) => { + println!("ServEvent::SessionSubsystem"); if a.command()?.to_lowercase().as_str() == "sftp" { if let Some(ch) = session.take() { debug_assert!(ch.num() == a.channel()); @@ -62,6 +63,7 @@ pub async fn connection_loop<'a>( } } ServEvent::SessionShell(a) => { + println!("ServEvent::SessionShell"); if let Some(ch) = session.take() { // Save config after connection successful (SessionEnv completed) if config_changed { @@ -84,22 +86,27 @@ pub async fn connection_loop<'a>( } } ServEvent::FirstAuth(ref a) => { + println!("ServEvent::FirstAuth"); // record the username if username.lock().await.push_str(a.username()?).is_err() { println!("Too long username") } } ServEvent::Hostkeys(h) => { + println!("ServEvent::Hostkeys"); let signkey: SignKey = SignKey::from_openssh(keys::HOST_SECRET_KEY)?; h.hostkeys(&[&signkey])?; } ServEvent::PasswordAuth(a) => { + println!("ServEvent::PasswordAuth"); a.allow()?; } ServEvent::PubkeyAuth(a) => { + println!("ServEvent::PubkeyAuth"); a.allow()?; } ServEvent::OpenSession(a) => { + println!("ServEvent::OpenSession"); match session { Some(_) => { todo!("Can't have two sessions"); @@ -164,6 +171,7 @@ pub async fn connection_loop<'a>( a.succeed()?; } ServEvent::SessionPty(a) => { + println!("ServEvent::SessionPty"); a.succeed()?; } ServEvent::SessionExec(a) => { @@ -173,7 +181,9 @@ pub async fn connection_loop<'a>( println!("Expected caller to handle event"); error::BadUsage.fail()? } - ServEvent::PollAgain => (), + ServEvent::PollAgain => { + println!("ServEvent::PollAgain"); + } _ => (), } } From 640cc6ab36196816bfde496fe3b000a90f36b460 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:50:58 +1100 Subject: [PATCH 66/86] Move various functions from main.rs into relevant files. --- src/espressif/buffered_uart.rs | 105 +++++++++++- src/espressif/net.rs | 67 ++++++-- src/main.rs | 291 +++++---------------------------- src/serve.rs | 57 ++++++- 4 files changed, 253 insertions(+), 267 deletions(-) diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index 3dbcfcd..edfb2cb 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -1,5 +1,6 @@ use portable_atomic::{AtomicUsize, Ordering}; +use crate::config::SSHStampConfig; //, espressif::buffered_uart::BufferedUart}; use embassy_futures::select::select; use embassy_sync::pipe::TryWriteError; /// Wrapper around bidirectional embassy-sync Pipes, in order to handle UART @@ -8,8 +9,16 @@ use embassy_sync::pipe::TryWriteError; /// Doesn't implement the InterruptExecutor, in the task in the app should await /// the 'run' async function. use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; -use esp_hal::uart::Uart; use esp_hal::Async; +use esp_hal::system::software_reset; +use esp_hal::{ + gpio::Pin, + peripherals::UART1, + uart::{RxConfig, Uart}, +}; +use esp_println::dbg; +use static_cell::StaticCell; +use sunset_async::SunsetMutex; // Sizes of the software buffers. Inward is more // important as an overrun here drops bytes. A full outward @@ -113,3 +122,97 @@ impl Default for BufferedUart { Self::new() } } + +cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + use esp_hal::peripherals::{GPIO1, GPIO3}; + } else { + use esp_hal::peripherals::{GPIO10, GPIO11}; + } +} + +cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + pub struct GPIOS<'a> { + pub gpio1: GPIO1<'a>, + pub gpio3: GPIO3<'a>, + } + } else { + pub struct GPIOS<'a> { + pub gpio10: GPIO10<'a>, + pub gpio11: GPIO11<'a>, + } + } +); + +pub const UART_BUFFER_SIZE: usize = 4096; +static UART_BUF: StaticCell = StaticCell::new(); + +pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { + UART_BUF.init_with(BufferedUart::new) +} + +pub async fn uart_buffer_disable() -> () { + // disable uart buffer + software_reset(); +} + +pub async fn uart_task<'a>( + uart_buf: &'a BufferedUart, + uart1: UART1<'a>, + config: &'a SunsetMutex, + gpios: GPIOS<'_>, +) -> Result<(), sunset::Error> { + dbg!("Configuring UART"); + let config_lock = config.lock().await; + let rx: u8 = config_lock.uart_pins.rx; + let tx: u8 = config_lock.uart_pins.tx; + if rx != tx { + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let mut holder0 = Some(gpios.gpio1); + let mut holder1 = Some(gpios.gpio3); + } else { + let mut holder0 = Some(gpios.gpio10); + let mut holder1 = Some(gpios.gpio11); + } + ); + + let rx_pin = match rx { + 1 => holder0.take().unwrap().degrade(), + 3 => holder1.take().unwrap().degrade(), + 10 => holder0.take().unwrap().degrade(), + 11 => holder1.take().unwrap().degrade(), + _ => holder0.take().unwrap().degrade(), + }; + let tx_pin = match tx { + 1 => holder0.take().unwrap().degrade(), + 3 => holder1.take().unwrap().degrade(), + 10 => holder0.take().unwrap().degrade(), + 11 => holder1.take().unwrap().degrade(), + _ => holder1.take().unwrap().degrade(), + }; + + // Hardware UART setup + let uart_config = esp_hal::uart::Config::default().with_rx( + RxConfig::default() + .with_fifo_full_threshold(16) + .with_timeout(1), + ); + + let uart = Uart::new(uart1, uart_config) + .unwrap() + .with_rx(rx_pin) + .with_tx(tx_pin) + .into_async(); + // Run the main buffered TX/RX loop + uart_buf.run(uart).await; + } + // TODO: Pin config error + Ok(()) +} + +pub async fn uart_disable() -> () { + // disable uart + software_reset(); +} diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 0f7d778..d186120 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -3,35 +3,30 @@ // SPDX-License-Identifier: GPL-3.0-or-later use core::net::Ipv4Addr; +use core::net::SocketAddrV4; use core::str::FromStr; - +use edge_dhcp; +use edge_dhcp::{ + io::{self, DEFAULT_SERVER_PORT}, + server::{Server, ServerOptions}, +}; +use edge_nal::UdpBind; +use edge_nal_embassy::{Udp, UdpBuffers}; use embassy_executor::Spawner; use embassy_net::{IpListenEndpoint, Ipv4Cidr, Runner, StaticConfigV4}; use embassy_net::{Stack, StackResources, tcp::TcpSocket}; use embassy_time::{Duration, Timer}; - use esp_hal::peripherals::WIFI; - use esp_hal::rng::Rng; +use esp_hal::system::software_reset; use esp_println::{dbg, println}; - use esp_radio::Controller; use esp_radio::wifi::WifiEvent; use esp_radio::wifi::{AccessPointConfig, ModeConfig, WifiApState, WifiController}; +use heapless::String; use sunset_async::SunsetMutex; -use core::net::SocketAddrV4; -use edge_dhcp; - -use edge_dhcp::{ - io::{self, DEFAULT_SERVER_PORT}, - server::{Server, ServerOptions}, -}; -use edge_nal::UdpBind; -use edge_nal_embassy::{Udp, UdpBuffers}; - use crate::config::SSHStampConfig; -// use crate::settings::DEFAULT_SSID; use crate::settings::{DEFAULT_IP, DEFAULT_SSID}; use super::buffered_uart::BufferedUart; @@ -46,8 +41,6 @@ macro_rules! mk_static { }}; } -use heapless::String; - pub async fn if_up( spawner: Spawner, controller: Controller<'static>, @@ -103,6 +96,37 @@ pub async fn if_up( Ok(ap_stack) } +pub async fn ap_stack_disable() -> () { + // drop ap_stack + software_reset(); +} + +pub async fn create_tcp_socket<'a>( + tcp_stack: Stack<'a>, + rx_buffer: &'a mut [u8], + tx_buffer: &'a mut [u8], +) -> TcpSocket<'a> { + let mut tcp_socket = TcpSocket::new(tcp_stack, rx_buffer, tx_buffer); + + println!("Waiting for SSH client..."); + if let Err(e) = tcp_socket + .accept(IpListenEndpoint { + addr: None, + port: 22, + }) + .await + { + println!("connect error: {:?}", e); + tcp_socket_disable().await; + } + tcp_socket +} + +pub async fn tcp_socket_disable() -> () { + // drop tcp stack + software_reset(); +} + pub async fn accept_requests<'a>(stack: Stack<'a>, _uart: &BufferedUart) -> ! { // let rx_buffer = mk_static!([u8; 1536], [0; 1536]); // let tx_buffer = mk_static!([u8; 1536], [0; 1536]); @@ -172,6 +196,15 @@ pub async fn wifi_up( } } +pub async fn wifi_controller_disable() -> () { + // TODO: Correctly disable wifi controller + // pub async fn wifi_disable(wifi_controller: EspWifiController<'_>) -> (){ + // drop wifi controller + // esp_wifi::deinit_unchecked() + // wifi_controller.deinit_unchecked() + software_reset(); +} + use esp_radio::wifi::WifiDevice; #[embassy_executor::task] async fn net_up(mut runner: Runner<'static, WifiDevice<'static>>) { diff --git a/src/main.rs b/src/main.rs index 569b392..53aeb49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,56 +12,55 @@ use core::result::Result::Ok; use core::future::Future; use embassy_executor::Spawner; use embassy_futures::select::{Either4, select4}; -use embassy_net::{IpListenEndpoint, Stack, tcp::TcpSocket}; +use embassy_net::{Stack, tcp::TcpSocket}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use esp_hal::system::software_reset; use esp_hal::{ - gpio::Pin, peripherals::UART1, peripherals::{SW_INTERRUPT, WIFI}, rng::Rng, - uart::{Config, RxConfig, Uart}, }; cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { use esp_hal::timer::timg::TimerGroup; - use esp_hal::peripherals::{TIMG1, GPIO1, GPIO3}; + use esp_hal::peripherals::{TIMG1}; } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { - use esp_hal::peripherals::{SYSTIMER, GPIO10, GPIO11}; + use esp_hal::peripherals::{SYSTIMER}; } else if #[cfg(any(feature = "esp32c2"))] { use esp_hal::interrupt::software::SoftwareInterruptControl; - use esp_hal::peripherals::{SYSTIMER, GPIO10, GPIO11}; + use esp_hal::peripherals::{SYSTIMER}; } else { use esp_hal::interrupt::software::SoftwareInterruptControl; - use esp_hal::peripherals::{SYSTIMER, GPIO10, GPIO11}; + use esp_hal::peripherals::{SYSTIMER}; } } -use esp_println::dbg; use esp_println::println; -use esp_radio::{Controller, InitializationError}; +use esp_radio::Controller; +// use ssh_stamp::espressif; use ssh_stamp::{ config::SSHStampConfig, espressif::{ + buffered_uart, buffered_uart::BufferedUart, + buffered_uart::GPIOS, // net::accept_requests, net, rng, }, serve, }; -use storage::flash; - use static_cell::StaticCell; +use storage::flash; use sunset_async::{SSHServer, SunsetMutex}; -const UART_BUFFER_SIZE: usize = 4096; -static UART_BUF: StaticCell = StaticCell::new(); - pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a> { let peripherals = esp_hal::init(esp_hal::Config::default()); + + println!("Initialising rng "); + let rng = Rng::new(); rng::register_custom_rng(rng); @@ -73,8 +72,11 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a } ); + println!("Initialising flash "); // Read SSH configuration from Flash (if it exists) flash::init(peripherals.FLASH); + + println!("Loading config "); let config = { let Some(flash_storage_guard) = flash::get_flash_n_buffer() else { panic!("Could not acquire flash storage lock"); @@ -87,6 +89,8 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a } .expect("Could not load or create SSHStampConfig"); + println!("Initialising config "); + static CONFIG: StaticCell> = StaticCell::new(); let config = CONFIG.init(SunsetMutex::new(config)); @@ -94,6 +98,7 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a let uart1 = peripherals.UART1; let sw_interrupt = peripherals.SW_INTERRUPT; + println!("Initialising gpio "); cfg_if::cfg_if!( if #[cfg(feature = "esp32")] { let gpios = GPIOS { @@ -140,179 +145,6 @@ pub async fn peripherals_disable() -> () { software_reset(); } -pub async fn wifi_wait_for_initialisation<'a>() -> Result, InitializationError> { - let wifi_controller: Result, InitializationError> = esp_radio::init(); - wifi_controller -} - -pub async fn wifi_controller_disable() -> () { - // TODO: Correctly disable wifi controller - // pub async fn wifi_disable(wifi_controller: EspWifiController<'_>) -> (){ - // drop wifi controller - // esp_wifi::deinit_unchecked() - // wifi_controller.deinit_unchecked() - software_reset(); -} - -pub async fn ap_stack_disable() -> () { - // drop ap_stack - software_reset(); -} - -pub async fn wifi_disable() -> () { - // drop wifi - software_reset(); -} - -pub async fn network_disable() -> () { - // drop network - software_reset(); -} - -pub async fn dhcp_disable() -> () { - // drop dhcp - software_reset(); -} - -pub async fn tcp_stack_disable() -> () { - // drop tcp stack - software_reset(); -} - -pub async fn tcp_socket_disable() -> () { - // drop tcp stack - software_reset(); -} - -pub async fn ssh_wait_for_initialisation<'server>( - inbuf: &'server mut [u8; UART_BUFFER_SIZE], - outbuf: &'server mut [u8; UART_BUFFER_SIZE], -) -> SSHServer<'server> { - let ssh_server = SSHServer::new(inbuf, outbuf); - ssh_server -} - -pub async fn ssh_disable() -> () { - // drop wifi controller - software_reset(); -} - -pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { - UART_BUF.init_with(BufferedUart::new) -} - -pub async fn uart_buffer_disable() -> () { - // disable uart buffer - software_reset(); -} - -pub async fn uart_task<'a>( - uart_buf: &'a BufferedUart, - uart1: UART1<'a>, - config: &'a SunsetMutex, - gpios: GPIOS<'_>, -) -> Result<(), sunset::Error> { - dbg!("Configuring UART"); - let config_lock = config.lock().await; - let rx: u8 = config_lock.uart_pins.rx; - let tx: u8 = config_lock.uart_pins.tx; - if rx != tx { - cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - let mut holder0 = Some(gpios.gpio1); - let mut holder1 = Some(gpios.gpio3); - } else { - let mut holder0 = Some(gpios.gpio10); - let mut holder1 = Some(gpios.gpio11); - } - ); - - let rx_pin = match rx { - 1 => holder0.take().unwrap().degrade(), - 3 => holder1.take().unwrap().degrade(), - 10 => holder0.take().unwrap().degrade(), - 11 => holder1.take().unwrap().degrade(), - _ => holder0.take().unwrap().degrade(), - }; - let tx_pin = match tx { - 1 => holder0.take().unwrap().degrade(), - 3 => holder1.take().unwrap().degrade(), - 10 => holder0.take().unwrap().degrade(), - 11 => holder1.take().unwrap().degrade(), - _ => holder1.take().unwrap().degrade(), - }; - - // Hardware UART setup - let uart_config = Config::default().with_rx( - RxConfig::default() - .with_fifo_full_threshold(16) - .with_timeout(1), - ); - - let uart = Uart::new(uart1, uart_config) - .unwrap() - .with_rx(rx_pin) - .with_tx(tx_pin) - .into_async(); - // Run the main buffered TX/RX loop - uart_buf.run(uart).await; - } - // TODO: Pin config error - Ok(()) -} - -pub async fn uart_disable() -> () { - // disable uart - software_reset(); -} - -pub async fn idle_wait_for_connection<'a, 'b>( - ssh_server: &'b SSHServer<'a>, - chan_pipe: &Channel, - config: &'a SunsetMutex, -) -> Result<(), sunset::Error> -where - 'a: 'b, -{ - serve::connection_loop(ssh_server, chan_pipe, config).await -} - -pub async fn connection_disable() -> () { - // disable idle - software_reset(); -} - -use crate::serve::SessionType; -use ssh_stamp::serial::serial_bridge; -use sunset_async::ChanInOut; -pub async fn bridge_wait_for_initialisation<'a, 'b>( - s: UartEnabledConsumed<'a, 'b>, -) -> Result<(), sunset::Error> { - let bridge = { - let chan_pipe = s.chan_pipe; - let session_type = chan_pipe.receive().await; - - match session_type { - serve::SessionType::Bridge(ch) => { - let stdio: ChanInOut<'_> = s.ssh_server.stdio(ch).await?; - let stdio2 = stdio.clone(); - serial_bridge(stdio, stdio2, s.uart_buff).await? - } - SessionType::Sftp(_ch) => { - // Handle SFTP session - // todo!() - } - }; - Ok(()) - }; - bridge -} - -pub async fn bridge_disable() -> () { - // disable bridge - software_reset(); -} - cfg_if::cfg_if!( if #[cfg(feature = "esp32")] { pub struct SshStampPeripherals<'a> { @@ -337,20 +169,6 @@ cfg_if::cfg_if!( } ); -cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - pub struct GPIOS<'a> { - pub gpio1: GPIO1<'a>, - pub gpio3: GPIO3<'a>, - } - } else { - pub struct GPIOS<'a> { - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, - } - } -); - pub struct SshStampInit<'a> { pub rng: Rng, pub wifi: WIFI<'a>, @@ -361,6 +179,7 @@ pub struct SshStampInit<'a> { } #[esp_rtos::main] async fn main(spawner: Spawner) -> ! { + println!("HSM: main"); cfg_if::cfg_if!( if #[cfg(feature = "esp32s2")] { // TODO: This heap size will crash at runtime, we need to fix this @@ -372,8 +191,10 @@ async fn main(spawner: Spawner) -> ! { ); esp_bootloader_esp_idf::esp_app_desc!(); esp_println::logger::init_logger_from_env(); - + println!("HSM: Initialising peripherals "); let peripherals = peripherals_wait_for_initialisation().await; + + println!("HSM: Initialising timers "); cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { // TODO: Test this feature configuration @@ -428,13 +249,13 @@ pub struct PeripheralsEnabled<'a> { } async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Error> { println!("HSM: peripherals_enabled"); - let controller = wifi_wait_for_initialisation().await; + let controller = esp_radio::init().unwrap(); let peripherals_enabled_struct = PeripheralsEnabled { rng: s.rng, wifi: s.wifi, config: s.config, - controller: controller.unwrap(), + controller: controller, gpios: s.gpios, uart1: s.uart1, spawner: s.spawner, @@ -446,7 +267,7 @@ async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Err } } - wifi_controller_disable().await; + net::wifi_controller_disable().await; Ok(()) // todo!() return relevant value } @@ -458,7 +279,6 @@ pub struct WifiControllerEnabled<'a> { pub tcp_stack: Stack<'a>, } -// pub async fn wifi_controller_enabled<'a>(s: PeripheralsEnabled<'a>) -> Result<(), sunset::Error> { pub async fn wifi_controller_enabled(s: PeripheralsEnabled<'static>) -> Result<(), sunset::Error> { println!("HSM: wifi_controller_enabled"); let tcp_stack = net::if_up(s.spawner, s.controller, s.wifi, s.rng, s.config) @@ -478,7 +298,7 @@ pub async fn wifi_controller_enabled(s: PeripheralsEnabled<'static>) -> Result<( println!("AP Stack error: {}", e); } } - ap_stack_disable().await; + net::ap_stack_disable().await; Ok(()) // todo!() return relevant value } @@ -493,19 +313,7 @@ async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Err println!("HSM: tcp_enabled"); let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; - let mut tcp_socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); - - println!("Waiting for SSH client..."); - if let Err(e) = tcp_socket - .accept(IpListenEndpoint { - addr: None, - port: 22, - }) - .await - { - println!("connect error: {:?}", e); - tcp_socket_disable().await; - } + let tcp_socket = net::create_tcp_socket(s.tcp_stack, &mut rx_buffer, &mut tx_buffer).await; println!("Connected, port 22"); let tcp_enabled_struct = TCPEnabled { config: s.config, @@ -519,7 +327,7 @@ async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Err println!("TCP socket error: {}", e); } } - tcp_socket_disable().await; + net::tcp_socket_disable().await; Ok(()) // todo!() return relevant value } @@ -530,6 +338,8 @@ pub struct SocketEnabled<'a> { pub gpios: GPIOS<'a>, pub uart1: UART1<'a>, } + +use buffered_uart::UART_BUFFER_SIZE; async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { println!("HSM: socket_enabled"); // loop { @@ -537,7 +347,7 @@ async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { let mut inbuf = [0u8; UART_BUFFER_SIZE]; let mut outbuf = [0u8; UART_BUFFER_SIZE]; println!("Starting ssh_server"); - let ssh_server = ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; + let ssh_server = serve::ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; println!("Started ssh_server"); let socket_enabled_struct = SocketEnabled { @@ -554,7 +364,7 @@ async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { } } - ssh_disable().await; + serve::ssh_disable().await; // } Ok(()) // todo!() return relevant value } @@ -578,11 +388,11 @@ async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> { println!("HSM: ssh_enabled"); // loop { - println!("Starting channel pipe"); + println!("HSM: Starting channel pipe"); let chan_pipe = Channel::::new(); - println!("Started channel pipe. Calling connection_loop from ssh_enabled"); - let connection = idle_wait_for_connection(&s.ssh_server, &chan_pipe, &s.config); - println!("Started connection loop"); + println!("HSM: Started channel pipe. Calling connection_loop from ssh_enabled"); + let connection = serve::connection_loop(&s.ssh_server, &chan_pipe, &s.config); + println!("HSM: Started connection loop"); let ssh_enabled_struct = SshEnabled { tcp_socket: s.tcp_socket, @@ -600,7 +410,7 @@ async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> } } - connection_disable().await; + serve::connection_disable().await; // } Ok(()) // todo!() return relevant value } @@ -625,7 +435,7 @@ where { println!("HSM: client_connected"); // loop { - let uart_buff = uart_buffer_wait_for_initialisation().await; + let uart_buff = buffered_uart::uart_buffer_wait_for_initialisation().await; let client_connected_struct = ClientConnected { tcp_socket: s.tcp_socket, @@ -644,7 +454,7 @@ where } } - uart_buffer_disable().await; + buffered_uart::uart_buffer_disable().await; // } Ok(()) // todo!() return relevant value } @@ -668,15 +478,13 @@ where CL: Future>, { println!("HSM: uart_buffer_ready"); - // loop { - let uart = uart_task(s.uart_buff, s.uart1, &s.config, s.gpios); + let uart = buffered_uart::uart_task(s.uart_buff, s.uart1, &s.config, s.gpios); let uart_buffer_ready_struct = UartBufferReady { tcp_socket: s.tcp_socket, ssh_server: s.ssh_server, chan_pipe: s.chan_pipe, - // uart_pins: &s.uart_pins, uart_buff: s.uart_buff, connection_loop: s.connection_loop, uart: uart, @@ -688,16 +496,11 @@ where } } - uart_disable().await; + buffered_uart::uart_disable().await; // } Ok(()) // todo!() return relevant value } -pub struct UartEnabledConsumed<'a, 'b> { - pub uart_buff: &'a BufferedUart, - pub ssh_server: &'b SSHServer<'a>, - pub chan_pipe: &'b Channel, -} pub struct UartEnabled<'a, 'b, CL, U, BR> where CL: Future>, @@ -719,14 +522,8 @@ where { println!("HSM: uart_enabled"); // // loop { - let uart_enabled_consumed = UartEnabledConsumed { - uart_buff: s.uart_buff, - ssh_server: s.ssh_server, - chan_pipe: s.chan_pipe, - }; - println!("Setting up serial bridge"); - let bridge = bridge_wait_for_initialisation(uart_enabled_consumed); + let bridge = serve::bridge_wait_for_initialisation(s.uart_buff, s.ssh_server, s.chan_pipe); let uart_enabled_struct = UartEnabled { ssh_server: s.ssh_server, @@ -742,7 +539,7 @@ where } } - bridge_disable().await; + serve::bridge_disable().await; // } Ok(()) // todo!() return relevant value diff --git a/src/serve.rs b/src/serve.rs index 02044b3..36861a2 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -15,9 +15,9 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; // use embedded_storage::Storage; -use sunset_async::SunsetMutex; - +use esp_hal::system::software_reset; use heapless::String; +use sunset_async::SunsetMutex; // use sunset::sshwire::SSHEncode; use sunset::{ChanHandle, ServEvent, SignKey, error}; use sunset_async::{ProgressHolder, SSHServer}; @@ -188,3 +188,56 @@ pub async fn connection_loop<'a>( } } } + +pub async fn connection_disable() -> () { + // disable connection loop + software_reset(); +} + +pub async fn ssh_wait_for_initialisation<'server>( + inbuf: &'server mut [u8; crate::espressif::buffered_uart::UART_BUFFER_SIZE], + outbuf: &'server mut [u8; crate::espressif::buffered_uart::UART_BUFFER_SIZE], +) -> SSHServer<'server> { + let ssh_server = SSHServer::new(inbuf, outbuf); + ssh_server +} + +pub async fn ssh_disable() -> () { + // drop ssh server + software_reset(); +} + +// use crate::serve::SessionType; +use crate::espressif::buffered_uart::BufferedUart; +use crate::serial::serial_bridge; +use sunset_async::ChanInOut; + +pub async fn bridge_wait_for_initialisation<'a, 'b>( + uart_buff: &'a BufferedUart, + ssh_server: &'b SSHServer<'a>, + chan_pipe: &'b Channel, +) -> Result<(), sunset::Error> { + let bridge = { + let chan_pipe = chan_pipe; + let session_type = chan_pipe.receive().await; + + match session_type { + SessionType::Bridge(ch) => { + let stdio: ChanInOut<'_> = ssh_server.stdio(ch).await?; + let stdio2 = stdio.clone(); + serial_bridge(stdio, stdio2, uart_buff).await? + } + SessionType::Sftp(_ch) => { + // Handle SFTP session + // todo!() + } + }; + Ok(()) + }; + bridge +} + +pub async fn bridge_disable() -> () { + // disable bridge + software_reset(); +} From 6573b3ad6ea0762366fa77827d20fe873ea8e24d Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:24:00 +1100 Subject: [PATCH 67/86] Restore tcp_socket to main.rs due to flash init block. --- src/main.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 53aeb49..9e562f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use core::result::Result::Ok; use core::future::Future; use embassy_executor::Spawner; use embassy_futures::select::{Either4, select4}; +use embassy_net::IpListenEndpoint; use embassy_net::{Stack, tcp::TcpSocket}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use esp_hal::system::software_reset; @@ -36,7 +37,6 @@ cfg_if::cfg_if! { use esp_hal::peripherals::{SYSTIMER}; } } - use esp_println::println; use esp_radio::Controller; // use ssh_stamp::espressif; @@ -313,7 +313,20 @@ async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Err println!("HSM: tcp_enabled"); let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; - let tcp_socket = net::create_tcp_socket(s.tcp_stack, &mut rx_buffer, &mut tx_buffer).await; + let mut tcp_socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); + + println!("Waiting for SSH client..."); + if let Err(e) = tcp_socket + .accept(IpListenEndpoint { + addr: None, + port: 22, + }) + .await + { + println!("connect error: {:?}", e); + net::tcp_socket_disable().await; + } + // let tcp_socket = net::create_tcp_socket(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); println!("Connected, port 22"); let tcp_enabled_struct = TCPEnabled { config: s.config, From 2f199e0d00862328addebf9d19384104e535ba4f Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Tue, 17 Feb 2026 16:00:49 +1100 Subject: [PATCH 68/86] Return uart_task back into main.rs as embassy task. Start task processing from HSM via embassy_sync signal. Change ESP32 uart pins to 13 & 14 due to hardware conflict. --- src/espressif/buffered_uart.rs | 141 ++++++---------- src/main.rs | 292 +++++++++++++++++++++++++-------- src/serial.rs | 8 +- src/serve.rs | 11 +- 4 files changed, 285 insertions(+), 167 deletions(-) diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index edfb2cb..629ba2d 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -1,6 +1,9 @@ +// SPDX-FileCopyrightText: 2025 Roman Valls, 2025 +// +// SPDX-License-Identifier: GPL-3.0-or-later + use portable_atomic::{AtomicUsize, Ordering}; -use crate::config::SSHStampConfig; //, espressif::buffered_uart::BufferedUart}; use embassy_futures::select::select; use embassy_sync::pipe::TryWriteError; /// Wrapper around bidirectional embassy-sync Pipes, in order to handle UART @@ -11,14 +14,8 @@ use embassy_sync::pipe::TryWriteError; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; use esp_hal::Async; use esp_hal::system::software_reset; -use esp_hal::{ - gpio::Pin, - peripherals::UART1, - uart::{RxConfig, Uart}, -}; +use esp_hal::uart::Uart; use esp_println::dbg; -use static_cell::StaticCell; -use sunset_async::SunsetMutex; // Sizes of the software buffers. Inward is more // important as an overrun here drops bytes. A full outward @@ -59,16 +56,26 @@ impl BufferedUart { loop { let rd_from = async { loop { + dbg!("BufferedUart run rd_from"); let n = uart_rx.read_async(&mut uart_rx_buf).await.unwrap(); + dbg!("rd_to uart_rx read"); let mut rx_slice = &uart_rx_buf[..n]; + dbg!("rd_to rx_slice"); // Write rx_slice to 'inward' pipe, dropping bytes rather than blocking if // the pipe is full while !rx_slice.is_empty() { + dbg!("rd_to rx_slice not empty"); + dbg!(rx_slice); + rx_slice = match self.inward.try_write(rx_slice) { - Ok(w) => &rx_slice[w..], + Ok(w) => { + dbg!("rd_to rx_slice try write ok"); + &rx_slice[w..] + } Err(TryWriteError::Full) => { + dbg!("rd_to rx_slice TryWriteError Full"); // If the receive buffer is full (no SSH client, or network congestion) then // drop the oldest bytes from the pipe so we can still write the newest ones. let mut drop_buf = [0u8; UART_BUF_SZ]; @@ -84,26 +91,34 @@ impl BufferedUart { rx_slice } }; + dbg!("rd_to rx_slice updated"); + dbg!(rx_slice); } } }; let rd_to = async { loop { + dbg!("BufferedUart run rd_to read"); let n = self.outward.read(&mut uart_tx_buf).await; // TODO: handle write errors + dbg!("BufferedUart run rd_to write_async"); let _ = uart_tx.write_async(&uart_tx_buf[..n]).await; } }; + dbg!("BufferedUart run select"); select(rd_from, rd_to).await; } } pub async fn read(&self, buf: &mut [u8]) -> usize { + dbg!("BufferedUart read"); self.inward.read(buf).await } pub async fn write(&self, buf: &[u8]) { + dbg!("BufferedUart write start"); self.outward.write_all(buf).await; + dbg!("BufferedUart write complete"); } /// Return the number of dropped bytes (if any) since the last check, @@ -123,96 +138,40 @@ impl Default for BufferedUart { } } -cfg_if::cfg_if! { - if #[cfg(feature = "esp32")] { - use esp_hal::peripherals::{GPIO1, GPIO3}; - } else { - use esp_hal::peripherals::{GPIO10, GPIO11}; - } -} - -cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - pub struct GPIOS<'a> { - pub gpio1: GPIO1<'a>, - pub gpio3: GPIO3<'a>, - } - } else { - pub struct GPIOS<'a> { - pub gpio10: GPIO10<'a>, - pub gpio11: GPIO11<'a>, - } - } -); - pub const UART_BUFFER_SIZE: usize = 4096; -static UART_BUF: StaticCell = StaticCell::new(); - -pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { - UART_BUF.init_with(BufferedUart::new) -} pub async fn uart_buffer_disable() -> () { // disable uart buffer software_reset(); } - -pub async fn uart_task<'a>( - uart_buf: &'a BufferedUart, - uart1: UART1<'a>, - config: &'a SunsetMutex, - gpios: GPIOS<'_>, -) -> Result<(), sunset::Error> { - dbg!("Configuring UART"); - let config_lock = config.lock().await; - let rx: u8 = config_lock.uart_pins.rx; - let tx: u8 = config_lock.uart_pins.tx; - if rx != tx { - cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - let mut holder0 = Some(gpios.gpio1); - let mut holder1 = Some(gpios.gpio3); - } else { - let mut holder0 = Some(gpios.gpio10); - let mut holder1 = Some(gpios.gpio11); - } - ); - - let rx_pin = match rx { - 1 => holder0.take().unwrap().degrade(), - 3 => holder1.take().unwrap().degrade(), - 10 => holder0.take().unwrap().degrade(), - 11 => holder1.take().unwrap().degrade(), - _ => holder0.take().unwrap().degrade(), - }; - let tx_pin = match tx { - 1 => holder0.take().unwrap().degrade(), - 3 => holder1.take().unwrap().degrade(), - 10 => holder0.take().unwrap().degrade(), - 11 => holder1.take().unwrap().degrade(), - _ => holder1.take().unwrap().degrade(), - }; - - // Hardware UART setup - let uart_config = esp_hal::uart::Config::default().with_rx( - RxConfig::default() - .with_fifo_full_threshold(16) - .with_timeout(1), - ); - - let uart = Uart::new(uart1, uart_config) - .unwrap() - .with_rx(rx_pin) - .with_tx(tx_pin) - .into_async(); - // Run the main buffered TX/RX loop - uart_buf.run(uart).await; - } - // TODO: Pin config error - Ok(()) -} +// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; pub async fn uart_disable() -> () { // disable uart software_reset(); } + +// use esp_hal::uart::{Config, RxConfig, Uart}; + +// async fn uart_task( +// buffer: &'static BufferedUart, +// uart_periph: UART1<'static>, +// rx_pin: AnyPin<'static>, +// tx_pin: AnyPin<'static>, +// ) { +// // Hardware UART setup +// let uart_config = Config::default().with_rx( +// RxConfig::default() +// .with_fifo_full_threshold(16) +// .with_timeout(1), +// ); + +// let uart = Uart::new(uart_periph, uart_config) +// .unwrap() +// .with_rx(rx_pin) +// .with_tx(tx_pin) +// .into_async(); + +// // Run the main buffered TX/RX loop +// buffer.run(uart).await; +// } diff --git a/src/main.rs b/src/main.rs index 9e562f6..a70fd72 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,11 +11,13 @@ use core::result::Result::Ok; // use core::error::Error; use core::future::Future; use embassy_executor::Spawner; -use embassy_futures::select::{Either4, select4}; +use embassy_futures::select::{Either3, select3}; use embassy_net::IpListenEndpoint; use embassy_net::{Stack, tcp::TcpSocket}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; +use esp_hal::gpio::AnyPin; use esp_hal::system::software_reset; +use esp_rtos::embassy::InterruptExecutor; use esp_hal::{ peripherals::UART1, @@ -30,22 +32,21 @@ cfg_if::cfg_if! { } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { use esp_hal::peripherals::{SYSTIMER}; } else if #[cfg(any(feature = "esp32c2"))] { - use esp_hal::interrupt::software::SoftwareInterruptControl; + // use esp_hal::interrupt::software::SoftwareInterruptControl; use esp_hal::peripherals::{SYSTIMER}; } else { - use esp_hal::interrupt::software::SoftwareInterruptControl; + // use esp_hal::interrupt::software::SoftwareInterruptControl; use esp_hal::peripherals::{SYSTIMER}; } } use esp_println::println; use esp_radio::Controller; -// use ssh_stamp::espressif; use ssh_stamp::{ config::SSHStampConfig, espressif::{ buffered_uart, buffered_uart::BufferedUart, - buffered_uart::GPIOS, + // buffered_uart::GPIOS, // net::accept_requests, net, rng, @@ -102,13 +103,13 @@ pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a cfg_if::cfg_if!( if #[cfg(feature = "esp32")] { let gpios = GPIOS { - gpio1: peripherals.GPIO1, - gpio3: peripherals.GPIO3, + gpio13: peripherals.GPIO13.into(), + gpio14: peripherals.GPIO14.into(), }; } else { let gpios = GPIOS { - gpio10: peripherals.GPIO10, - gpio11: peripherals.GPIO11, + gpio10: peripherals.GPIO10.into(), + gpio11: peripherals.GPIO11.into(), }; } ); @@ -168,15 +169,17 @@ cfg_if::cfg_if!( } } ); - +// use esp_hal::interrupt::software::SoftwareInterrupt; +use esp_hal::interrupt::{Priority, software::SoftwareInterruptControl}; pub struct SshStampInit<'a> { pub rng: Rng, pub wifi: WIFI<'a>, pub config: &'a SunsetMutex, - pub gpios: GPIOS<'a>, - pub uart1: UART1<'a>, + // pub gpios: GPIOS<'a>, + pub uart_buf: &'a BufferedUart, pub spawner: Spawner, } +static INT_EXECUTOR: StaticCell> = StaticCell::new(); // 0 is used for esp_rtos #[esp_rtos::main] async fn main(spawner: Spawner) -> ! { println!("HSM: main"); @@ -195,6 +198,8 @@ async fn main(spawner: Spawner) -> ! { let peripherals = peripherals_wait_for_initialisation().await; println!("HSM: Initialising timers "); + let software_interrupts = SoftwareInterruptControl::new(peripherals.sw_interrupt); + cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { // TODO: Test this feature configuration @@ -206,13 +211,52 @@ async fn main(spawner: Spawner) -> ! { let systimer = SystemTimer::new(peripherals.systimer); esp_rtos::start(systimer.alarm0); } else { - let sw_int = SoftwareInterruptControl::new(peripherals.sw_interrupt); + // let sw_int = SoftwareInterruptControl::new(peripherals.sw_interrupt); use esp_hal::timer::systimer::SystemTimer; let systimer = SystemTimer::new(peripherals.systimer); - esp_rtos::start(systimer.alarm0, sw_int.software_interrupt0); + esp_rtos::start(systimer.alarm0, software_interrupts.software_interrupt0); } } + // Set up software buffered UART to run in a higher priority InterruptExecutor + let uart_buf = UART_BUF.init_with(BufferedUart::new); + let interrupt_executor = + INT_EXECUTOR.init_with(|| InterruptExecutor::new(software_interrupts.software_interrupt1)); + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { + let interrupt_spawner = interrupt_executor.start(Priority::Priority1); + } else { + let interrupt_spawner = interrupt_executor.start(Priority::Priority10); + } + } + + // Use the same config reference for UART task. + // Pass pin_channel_ref into the UART task so it can acquire/release pins. + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + interrupt_spawner + .spawn(uart_task( + uart_buf, + peripherals.uart1, + &peripherals.config, + // peripherals.gpios, + peripherals.gpios.gpio13.into(), + peripherals.gpios.gpio14.into(), + )) + .unwrap(); + } else { + interrupt_spawner + .spawn(uart_task( + uart_buf, + peripherals.uart1, + &peripherals.config, + // peripherals.gpios, + peripherals.gpios.gpio10.into(), + peripherals.gpios.gpio11.into(), + )) + .unwrap(); + } + } // TODO: Migrate this function/test to embedded-test. // Quick roundtrip test for SSHStampConfig // ssh_stamp::config::roundtrip_config(); @@ -221,9 +265,8 @@ async fn main(spawner: Spawner) -> ! { rng: peripherals.rng, wifi: peripherals.wifi, config: peripherals.config, - gpios: peripherals.gpios, - uart1: peripherals.uart1, spawner: spawner, + uart_buf: uart_buf, }; match peripherals_enabled(peripherals_enabled_struct).await { @@ -243,8 +286,7 @@ pub struct PeripheralsEnabled<'a> { pub wifi: WIFI<'a>, pub config: &'a SunsetMutex, pub controller: Controller<'static>, - pub gpios: GPIOS<'a>, - pub uart1: UART1<'a>, + pub uart_buf: &'a BufferedUart, pub spawner: Spawner, } async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Error> { @@ -256,8 +298,7 @@ async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Err wifi: s.wifi, config: s.config, controller: controller, - gpios: s.gpios, - uart1: s.uart1, + uart_buf: s.uart_buf, spawner: s.spawner, }; match wifi_controller_enabled(peripherals_enabled_struct).await { @@ -274,8 +315,7 @@ async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Err pub struct WifiControllerEnabled<'a> { pub rng: Rng, pub config: &'a SunsetMutex, - pub gpios: GPIOS<'a>, - pub uart1: UART1<'a>, + pub uart_buf: &'a BufferedUart, pub tcp_stack: Stack<'a>, } @@ -288,8 +328,7 @@ pub async fn wifi_controller_enabled(s: PeripheralsEnabled<'static>) -> Result<( let wifi_controller_enabled_stack = WifiControllerEnabled { config: s.config, rng: s.rng, - gpios: s.gpios, - uart1: s.uart1, + uart_buf: s.uart_buf, tcp_stack: tcp_stack, }; match tcp_enabled(wifi_controller_enabled_stack).await { @@ -305,8 +344,7 @@ pub async fn wifi_controller_enabled(s: PeripheralsEnabled<'static>) -> Result<( pub struct TCPEnabled<'a> { pub config: &'a SunsetMutex, pub tcp_socket: TcpSocket<'a>, - pub gpios: GPIOS<'a>, - pub uart1: UART1<'a>, + pub uart_buf: &'a BufferedUart, } async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Error> { @@ -331,8 +369,7 @@ async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Err let tcp_enabled_struct = TCPEnabled { config: s.config, tcp_socket: tcp_socket, - gpios: s.gpios, - uart1: s.uart1, + uart_buf: s.uart_buf, }; match socket_enabled(tcp_enabled_struct).await { Ok(_) => (), @@ -348,8 +385,7 @@ pub struct SocketEnabled<'a> { pub config: &'a SunsetMutex, pub tcp_socket: TcpSocket<'a>, pub ssh_server: SSHServer<'a>, - pub gpios: GPIOS<'a>, - pub uart1: UART1<'a>, + pub uart_buf: &'a BufferedUart, } use buffered_uart::UART_BUFFER_SIZE; @@ -359,16 +395,15 @@ async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { // Spawn network tasks to handle incoming connections with demo_common::session() let mut inbuf = [0u8; UART_BUFFER_SIZE]; let mut outbuf = [0u8; UART_BUFFER_SIZE]; - println!("Starting ssh_server"); + println!("HSM: Starting ssh_server"); let ssh_server = serve::ssh_wait_for_initialisation(&mut inbuf, &mut outbuf).await; - println!("Started ssh_server"); + println!("HSM: Started ssh_server"); let socket_enabled_struct = SocketEnabled { config: s.config, tcp_socket: s.tcp_socket, ssh_server: ssh_server, - gpios: s.gpios, - uart1: s.uart1, + uart_buf: s.uart_buf, }; match ssh_enabled(socket_enabled_struct).await { Ok(_) => (), @@ -388,13 +423,12 @@ where { pub tcp_socket: TcpSocket<'a>, pub ssh_server: &'b SSHServer<'a>, - pub uart1: UART1<'a>, + pub uart_buf: &'a BufferedUart, pub config: &'a SunsetMutex, pub chan_pipe: &'b Channel, pub connection_loop: CL, - pub gpios: GPIOS<'a>, } - +use embassy_time::{Duration, Timer}; async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> // where // 'b: 'a, @@ -406,15 +440,14 @@ async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> println!("HSM: Started channel pipe. Calling connection_loop from ssh_enabled"); let connection = serve::connection_loop(&s.ssh_server, &chan_pipe, &s.config); println!("HSM: Started connection loop"); - + Timer::after(Duration::from_millis(500)).await; let ssh_enabled_struct = SshEnabled { tcp_socket: s.tcp_socket, ssh_server: &s.ssh_server, config: s.config, + uart_buf: s.uart_buf, chan_pipe: &chan_pipe, connection_loop: connection, - uart1: s.uart1, - gpios: s.gpios, }; match client_connected(ssh_enabled_struct).await { Ok(_) => (), @@ -436,10 +469,8 @@ where pub ssh_server: &'b SSHServer<'a>, pub chan_pipe: &'b Channel, pub connection_loop: CL, - pub uart_buff: &'a BufferedUart, - pub uart1: UART1<'a>, + pub uart_buf: &'a BufferedUart, pub config: &'a SunsetMutex, - pub gpios: GPIOS<'a>, } async fn client_connected<'a, 'b, CL>(s: SshEnabled<'a, 'b, CL>) -> Result<(), sunset::Error> // where 'b: 'a { @@ -448,17 +479,16 @@ where { println!("HSM: client_connected"); // loop { - let uart_buff = buffered_uart::uart_buffer_wait_for_initialisation().await; + // let uart_buff = buffered_uart::uart_buffer_wait_for_initialisation().await; + Timer::after(Duration::from_millis(500)).await; let client_connected_struct = ClientConnected { tcp_socket: s.tcp_socket, ssh_server: s.ssh_server, chan_pipe: s.chan_pipe, connection_loop: s.connection_loop, - uart_buff: uart_buff, - uart1: s.uart1, + uart_buf: s.uart_buf, config: s.config, - gpios: s.gpios, }; match uart_buffer_ready(client_connected_struct).await { Ok(_) => (), @@ -472,17 +502,15 @@ where Ok(()) // todo!() return relevant value } -pub struct UartBufferReady<'a, 'b, CL, U> +pub struct UartBufferReady<'a, 'b, CL> where CL: Future>, - U: Future>, { pub tcp_socket: TcpSocket<'a>, pub ssh_server: &'b SSHServer<'a>, - pub uart_buff: &'a BufferedUart, + pub uart_buf: &'a BufferedUart, pub chan_pipe: &'b Channel, pub connection_loop: CL, - pub uart: U, } async fn uart_buffer_ready<'a, 'b, CL>(s: ClientConnected<'a, 'b, CL>) -> Result<(), sunset::Error> @@ -491,16 +519,21 @@ where CL: Future>, { println!("HSM: uart_buffer_ready"); + Timer::after(Duration::from_millis(500)).await; + // loop { - let uart = buffered_uart::uart_task(s.uart_buff, s.uart1, &s.config, s.gpios); + // let uart = buffered_uart::uart_task(s.uart_buff, s.uart1, &s.config, s.gpios); + // Signal for uart task to configure pins and run. + UART_SIGNAL.signal(1); + Timer::after(Duration::from_millis(500)).await; + println!("HSM: UART_SIGNAL sent"); let uart_buffer_ready_struct = UartBufferReady { tcp_socket: s.tcp_socket, ssh_server: s.ssh_server, chan_pipe: s.chan_pipe, - uart_buff: s.uart_buff, + uart_buf: s.uart_buf, connection_loop: s.connection_loop, - uart: uart, }; match uart_enabled(uart_buffer_ready_struct).await { Ok(_) => (), @@ -514,35 +547,34 @@ where Ok(()) // todo!() return relevant value } -pub struct UartEnabled<'a, 'b, CL, U, BR> +pub struct UartEnabled<'a, 'b, CL, BR> where CL: Future>, - U: Future>, BR: Future>, { pub ssh_server: &'b SSHServer<'a>, pub bridge: BR, pub connection_loop: CL, - pub uart: U, pub tcp_socket: TcpSocket<'a>, } -async fn uart_enabled<'a, 'b, CL, U>(s: UartBufferReady<'a, 'b, CL, U>) -> Result<(), sunset::Error> +async fn uart_enabled<'a, 'b, CL>(s: UartBufferReady<'a, 'b, CL>) -> Result<(), sunset::Error> where CL: Future>, - U: Future>, 'a: 'b, { println!("HSM: uart_enabled"); + Timer::after(Duration::from_millis(500)).await; + // // loop { - println!("Setting up serial bridge"); - let bridge = serve::bridge_wait_for_initialisation(s.uart_buff, s.ssh_server, s.chan_pipe); + println!("HSM: Setting up serial bridge"); + let bridge = serve::bridge_wait_for_initialisation(s.uart_buf, s.ssh_server, s.chan_pipe); let uart_enabled_struct = UartEnabled { ssh_server: s.ssh_server, bridge: bridge, connection_loop: s.connection_loop, - uart: s.uart, + // uart: s.uart, tcp_socket: s.tcp_socket, }; match bridge_connected(uart_enabled_struct).await { @@ -558,28 +590,26 @@ where Ok(()) // todo!() return relevant value } -async fn bridge_connected<'a, 'b, CL, U, BR>( - s: UartEnabled<'a, 'b, CL, U, BR>, +async fn bridge_connected<'a, 'b, CL, BR>( + s: UartEnabled<'a, 'b, CL, BR>, ) -> Result<(), sunset::Error> where CL: Future>, - U: Future>, BR: Future>, 'a: 'b, { println!("HSM: bridge_connected"); let mut tcp_socket = s.tcp_socket; let (mut rsock, mut wsock) = tcp_socket.split(); - println!("Running server from handle_ssh_client()"); + println!("HSM: Running server from handle_ssh_client()"); let server = s.ssh_server.run(&mut rsock, &mut wsock); let connection_loop = s.connection_loop; let bridge = s.bridge; - println!("Main select() in bridge_connected()"); - match select4(server, connection_loop, bridge, s.uart).await { - Either4::First(r) => r, - Either4::Second(r) => r, - Either4::Third(r) => r, - Either4::Fourth(r) => r, + println!("HSM: Main select() in bridge_connected()"); + match select3(server, connection_loop, bridge).await { + Either3::First(r) => r, + Either3::Second(r) => r, + Either3::Third(r) => r, }?; Result::Ok(()) } @@ -588,3 +618,121 @@ where fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } + +use esp_hal::uart::{RxConfig, Uart}; + +// cfg_if::cfg_if! { +// if #[cfg(feature = "esp32")] { +// use esp_hal::peripherals::{GPIO1, GPIO3}; +// } else { +// use esp_hal::peripherals::{GPIO10, GPIO11}; +// } +// } +cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + pub struct GPIOS<'a> { + pub gpio13: AnyPin<'a>, + pub gpio14: AnyPin<'a>, + } + } else { + pub struct GPIOS<'a> { + pub gpio10: AnyPin<'a>, + pub gpio11: AnyPin<'a>, + } + } +); + +pub static UART_BUF: StaticCell = StaticCell::new(); + +pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { + UART_BUF.init_with(BufferedUart::new) +} + +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::signal::Signal; +pub static UART_SIGNAL: Signal = Signal::new(); +// use embassy_time::{Duration, Timer}; +// use esp_hal::gpio::AnyPin; +use esp_println::dbg; +#[embassy_executor::task] +pub async fn uart_task( + uart_buf: &'static BufferedUart, + uart1: UART1<'static>, + _config: &'static SunsetMutex, + // gpios: GPIOS<'static>, + rx_pin: AnyPin<'static>, + tx_pin: AnyPin<'static>, +) -> () { + dbg!("Configuring UART"); + Timer::after(Duration::from_millis(100)).await; + // Wait until ssh complete + UART_SIGNAL.wait().await; + dbg!("UART signal recieved"); + Timer::after(Duration::from_millis(100)).await; + + // let config_lock = config.lock().await; + // let rx: u8 = config_lock.uart_pins.rx; + // let tx: u8 = config_lock.uart_pins.tx; + // if rx != tx { + // cfg_if::cfg_if!( + // if #[cfg(feature = "esp32")] { + // let tx_pin: AnyPin<'static> = gpio1.into(); + // let rx_pin: AnyPin<'static> = gpio3.into(); + // // let mut holder0 = Some(gpios.gpio1); + // // let mut holder1 = Some(gpios.gpio3); + // } else { + // let tx_pin = gpio10; + // let rx_pin = gpio11; + // // let mut holder0 = Some(gpios.gpio10); + // // let mut holder1 = Some(gpios.gpio11); + // } + // ); + + // let rx_pin = match rx { + // 1 => holder0.take().unwrap().degrade(), + // 3 => holder1.take().unwrap().degrade(), + // 10 => holder0.take().unwrap().degrade(), + // 11 => holder1.take().unwrap().degrade(), + // _ => holder0.take().unwrap().degrade(), + // }; + // let tx_pin = match tx { + // 1 => holder0.take().unwrap().degrade(), + // 3 => holder1.take().unwrap().degrade(), + // 10 => holder0.take().unwrap().degrade(), + // 11 => holder1.take().unwrap().degrade(), + // _ => holder1.take().unwrap().degrade(), + // }; + + dbg!("UART config"); + Timer::after(Duration::from_millis(100)).await; + + // Hardware UART setup + let uart_config = esp_hal::uart::Config::default().with_rx( + RxConfig::default() + .with_fifo_full_threshold(16) + .with_timeout(1), + ); + + // let uart_config = Config::default().with_rx( + // RxConfig::default() + // .with_fifo_full_threshold(16) + // .with_timeout(1), + // ); + + dbg!("UART setup pins"); + Timer::after(Duration::from_millis(100)).await; + + let uart = Uart::new(uart1, uart_config) + .unwrap() + .with_rx(rx_pin) + .with_tx(tx_pin) + .into_async(); + + // Run the main buffered TX/RX loop + dbg!("uart_task running UART"); + Timer::after(Duration::from_millis(100)).await; + uart_buf.run(uart).await; + // } + // TODO: Pin config error + // Ok(()) +} diff --git a/src/serial.rs b/src/serial.rs index 4f63730..6092525 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -30,9 +30,11 @@ async fn uart_to_ssh( // TODO: should this also go to the SSH client? println!("UART RX dropped {} bytes", dropped); } - + println!("UART RX waiting to read bytes"); let n = uart_buf.read(&mut ssh_tx_buf).await; + println!("UART RX read {} bytes", n); chanw.write_all(&ssh_tx_buf[..n]).await?; + println!("UART RX write all complete"); } } @@ -42,10 +44,14 @@ async fn ssh_to_uart( ) -> Result<(), sunset::Error> { let mut uart_tx_buf = [0u8; 64]; loop { + println!("UART TX waiting to read bytes"); let n = chanr.read(&mut uart_tx_buf).await?; + println!("UART TX read bytes"); if n == 0 { return Err(sunset::Error::ChannelEOF); } + println!("UART TX waiting to write byte"); uart_buf.write(&uart_tx_buf[..n]).await; + println!("UART TX all bytes written"); } } diff --git a/src/serve.rs b/src/serve.rs index 36861a2..8343fcc 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -41,8 +41,9 @@ pub async fn connection_loop<'a>( let mut config_changed: bool = false; loop { let mut ph = ProgressHolder::new(); + // dbg!("Waiting for ssh server event"); let ev = serv.progress(&mut ph).await?; - dbg!(&ev); + // dbg!(&ev); #[allow(unreachable_patterns)] match ev { // #[cfg(feature = "sftp-ota")] @@ -182,7 +183,7 @@ pub async fn connection_loop<'a>( error::BadUsage.fail()? } ServEvent::PollAgain => { - println!("ServEvent::PollAgain"); + // println!("ServEvent::PollAgain"); } _ => (), } @@ -217,17 +218,21 @@ pub async fn bridge_wait_for_initialisation<'a, 'b>( ssh_server: &'b SSHServer<'a>, chan_pipe: &'b Channel, ) -> Result<(), sunset::Error> { + dbg!("Preparing bridge"); let bridge = { let chan_pipe = chan_pipe; let session_type = chan_pipe.receive().await; - + dbg!("Checking bridge session type"); match session_type { SessionType::Bridge(ch) => { + dbg!("Handling bridge session"); let stdio: ChanInOut<'_> = ssh_server.stdio(ch).await?; let stdio2 = stdio.clone(); + dbg!("Starting bridge"); serial_bridge(stdio, stdio2, uart_buff).await? } SessionType::Sftp(_ch) => { + dbg!("Handling SFTP session"); // Handle SFTP session // todo!() } From 71897363ce40dfd590c30e1046bfaeba48d54abd Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Wed, 18 Feb 2026 22:42:00 +1100 Subject: [PATCH 69/86] Increase priority of UART interrupt to Priority3 as it was becoming blocked when reading the buffered uart. --- src/espressif/buffered_uart.rs | 21 +-------------------- src/main.rs | 3 ++- src/serial.rs | 7 ------- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index 629ba2d..dd36053 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -15,7 +15,6 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; use esp_hal::Async; use esp_hal::system::software_reset; use esp_hal::uart::Uart; -use esp_println::dbg; // Sizes of the software buffers. Inward is more // important as an overrun here drops bytes. A full outward @@ -56,26 +55,16 @@ impl BufferedUart { loop { let rd_from = async { loop { - dbg!("BufferedUart run rd_from"); let n = uart_rx.read_async(&mut uart_rx_buf).await.unwrap(); - dbg!("rd_to uart_rx read"); let mut rx_slice = &uart_rx_buf[..n]; - dbg!("rd_to rx_slice"); // Write rx_slice to 'inward' pipe, dropping bytes rather than blocking if // the pipe is full while !rx_slice.is_empty() { - dbg!("rd_to rx_slice not empty"); - dbg!(rx_slice); - rx_slice = match self.inward.try_write(rx_slice) { - Ok(w) => { - dbg!("rd_to rx_slice try write ok"); - &rx_slice[w..] - } + Ok(w) => &rx_slice[w..], Err(TryWriteError::Full) => { - dbg!("rd_to rx_slice TryWriteError Full"); // If the receive buffer is full (no SSH client, or network congestion) then // drop the oldest bytes from the pipe so we can still write the newest ones. let mut drop_buf = [0u8; UART_BUF_SZ]; @@ -91,34 +80,26 @@ impl BufferedUart { rx_slice } }; - dbg!("rd_to rx_slice updated"); - dbg!(rx_slice); } } }; let rd_to = async { loop { - dbg!("BufferedUart run rd_to read"); let n = self.outward.read(&mut uart_tx_buf).await; // TODO: handle write errors - dbg!("BufferedUart run rd_to write_async"); let _ = uart_tx.write_async(&uart_tx_buf[..n]).await; } }; - dbg!("BufferedUart run select"); select(rd_from, rd_to).await; } } pub async fn read(&self, buf: &mut [u8]) -> usize { - dbg!("BufferedUart read"); self.inward.read(buf).await } pub async fn write(&self, buf: &[u8]) { - dbg!("BufferedUart write start"); self.outward.write_all(buf).await; - dbg!("BufferedUart write complete"); } /// Return the number of dropped bytes (if any) since the last check, diff --git a/src/main.rs b/src/main.rs index a70fd72..67b2dcd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -219,12 +219,13 @@ async fn main(spawner: Spawner) -> ! { } // Set up software buffered UART to run in a higher priority InterruptExecutor + // Must be higher priority than esp_rtos (Priority1) let uart_buf = UART_BUF.init_with(BufferedUart::new); let interrupt_executor = INT_EXECUTOR.init_with(|| InterruptExecutor::new(software_interrupts.software_interrupt1)); cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { - let interrupt_spawner = interrupt_executor.start(Priority::Priority1); + let interrupt_spawner = interrupt_executor.start(Priority::Priority3); } else { let interrupt_spawner = interrupt_executor.start(Priority::Priority10); } diff --git a/src/serial.rs b/src/serial.rs index 6092525..d46565d 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -30,11 +30,8 @@ async fn uart_to_ssh( // TODO: should this also go to the SSH client? println!("UART RX dropped {} bytes", dropped); } - println!("UART RX waiting to read bytes"); let n = uart_buf.read(&mut ssh_tx_buf).await; - println!("UART RX read {} bytes", n); chanw.write_all(&ssh_tx_buf[..n]).await?; - println!("UART RX write all complete"); } } @@ -44,14 +41,10 @@ async fn ssh_to_uart( ) -> Result<(), sunset::Error> { let mut uart_tx_buf = [0u8; 64]; loop { - println!("UART TX waiting to read bytes"); let n = chanr.read(&mut uart_tx_buf).await?; - println!("UART TX read bytes"); if n == 0 { return Err(sunset::Error::ChannelEOF); } - println!("UART TX waiting to write byte"); uart_buf.write(&uart_tx_buf[..n]).await; - println!("UART TX all bytes written"); } } From 6c7adfb68e20f7f5f91cdf06a1e4a5fde35fe78a Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 19 Feb 2026 04:16:14 +1100 Subject: [PATCH 70/86] Cleanup main and update UART pins for all ESP32 targets --- partitions.csv | 9 +- src/config.rs | 5 + src/espressif/rng.rs | 2 +- src/main.rs | 433 ++++++++++++++++++------------------------- src/settings.rs | 20 +- src/store.rs | 2 +- 6 files changed, 209 insertions(+), 262 deletions(-) diff --git a/partitions.csv b/partitions.csv index 8eaf718..34340f6 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,8 +1,9 @@ -# ESP-IDF Partition Table +# ESP-IDF Partition Table (wont fit in 4MB targets) # Name, Type, SubType, Offset, Size, Flags app_config, data, nvs, 0x9000, 0x2000, +# between app_config and otadata there is0x2000 bytes of free space otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x200000, -ota_0, app, ota_0, 0x210000, 0x100000, -ota_1, app, ota_1, 0x310000, 0x100000, \ No newline at end of file +ota_0, app, ota_0, 0x010000, 0x1e0000, +ota_1, app, ota_1, 0x1f0000, 0x1e0000, +extra_data, data, nvs, 0x3d0000, 64K, diff --git a/src/config.rs b/src/config.rs index b4c270c..4b1071d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -63,6 +63,7 @@ impl Default for UartPins { } } } +use esp_println::println; impl SSHStampConfig { /// Bump this when the format changes @@ -80,6 +81,10 @@ impl SSHStampConfig { let wifi_pw = None; let uart_pins = UartPins::default(); + println!( + "SSH Stamp Config new() - RX Pin: {} TX Pin: {}", + uart_pins.rx, uart_pins.tx + ); Ok(SSHStampConfig { hostkey, diff --git a/src/espressif/rng.rs b/src/espressif/rng.rs index 735ef36..54a040f 100644 --- a/src/espressif/rng.rs +++ b/src/espressif/rng.rs @@ -1,7 +1,7 @@ use core::cell::RefCell; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use esp_hal::rng::Rng; use getrandom::register_custom_getrandom; use static_cell::StaticCell; diff --git a/src/main.rs b/src/main.rs index 67b2dcd..4a54329 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,31 +16,12 @@ use embassy_net::IpListenEndpoint; use embassy_net::{Stack, tcp::TcpSocket}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use esp_hal::gpio::AnyPin; +use esp_hal::interrupt::{Priority, software::SoftwareInterruptControl}; use esp_hal::system::software_reset; -use esp_rtos::embassy::InterruptExecutor; - -use esp_hal::{ - peripherals::UART1, - peripherals::{SW_INTERRUPT, WIFI}, - rng::Rng, -}; - -cfg_if::cfg_if! { - if #[cfg(feature = "esp32")] { - use esp_hal::timer::timg::TimerGroup; - use esp_hal::peripherals::{TIMG1}; - } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { - use esp_hal::peripherals::{SYSTIMER}; - } else if #[cfg(any(feature = "esp32c2"))] { - // use esp_hal::interrupt::software::SoftwareInterruptControl; - use esp_hal::peripherals::{SYSTIMER}; - } else { - // use esp_hal::interrupt::software::SoftwareInterruptControl; - use esp_hal::peripherals::{SYSTIMER}; - } -} +use esp_hal::{peripherals::UART1, peripherals::WIFI, rng::Rng}; use esp_println::println; use esp_radio::Controller; +use esp_rtos::embassy::InterruptExecutor; use ssh_stamp::{ config::SSHStampConfig, espressif::{ @@ -57,88 +38,10 @@ use static_cell::StaticCell; use storage::flash; use sunset_async::{SSHServer, SunsetMutex}; -pub async fn peripherals_wait_for_initialisation<'a>() -> SshStampPeripherals<'a> { - let peripherals = esp_hal::init(esp_hal::Config::default()); - - println!("Initialising rng "); - - let rng = Rng::new(); - rng::register_custom_rng(rng); - - cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - let timg1 = peripherals.TIMG1; - } else { - let systimer = peripherals.SYSTIMER; - } - ); - - println!("Initialising flash "); - // Read SSH configuration from Flash (if it exists) - flash::init(peripherals.FLASH); - - println!("Loading config "); - let config = { - let Some(flash_storage_guard) = flash::get_flash_n_buffer() else { - panic!("Could not acquire flash storage lock"); - }; - let mut flash_storage = flash_storage_guard.lock().await; - // TODO: Migrate this function/test to embedded-test. - // Quick roundtrip test for SSHStampConfig - // ssh_stamp::config::roundtrip_config(); - ssh_stamp::store::load_or_create(&mut flash_storage).await - } - .expect("Could not load or create SSHStampConfig"); - - println!("Initialising config "); - - static CONFIG: StaticCell> = StaticCell::new(); - let config = CONFIG.init(SunsetMutex::new(config)); - - let wifi = peripherals.WIFI; - let uart1 = peripherals.UART1; - let sw_interrupt = peripherals.SW_INTERRUPT; - - println!("Initialising gpio "); - cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - let gpios = GPIOS { - gpio13: peripherals.GPIO13.into(), - gpio14: peripherals.GPIO14.into(), - }; - } else { - let gpios = GPIOS { - gpio10: peripherals.GPIO10.into(), - gpio11: peripherals.GPIO11.into(), - }; - } - ); - - cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - let ssh_stamp_peripherals = SshStampPeripherals { - rng: rng, - wifi: wifi, - config: config, - gpios: gpios, - uart1: uart1, - timg1: timg1, - sw_interrupt: sw_interrupt, - }; - } else { - let ssh_stamp_peripherals = SshStampPeripherals { - rng: rng, - wifi: wifi, - config: config, - gpios: gpios, - uart1: uart1, - systimer: systimer, - sw_interrupt: sw_interrupt, - }; - } - ); - - ssh_stamp_peripherals +cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + use esp_hal::timer::timg::TimerGroup; + } } pub async fn peripherals_disable() -> () { @@ -146,31 +49,6 @@ pub async fn peripherals_disable() -> () { software_reset(); } -cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - pub struct SshStampPeripherals<'a> { - pub rng: Rng, - pub wifi: WIFI<'a>, - pub config: &'a SunsetMutex, - pub gpios: GPIOS<'a>, - pub uart1: UART1<'a>, - pub timg1: TIMG1<'a>, - pub sw_interrupt: SW_INTERRUPT<'a>, - } - } else { - pub struct SshStampPeripherals<'a> { - pub rng: Rng, - pub wifi: WIFI<'a>, - pub config: &'a SunsetMutex, - pub gpios: GPIOS<'a>, - pub uart1: UART1<'a>, - pub systimer: SYSTIMER<'a>, - pub sw_interrupt: SW_INTERRUPT<'a>, - } - } -); -// use esp_hal::interrupt::software::SoftwareInterrupt; -use esp_hal::interrupt::{Priority, software::SoftwareInterruptControl}; pub struct SshStampInit<'a> { pub rng: Rng, pub wifi: WIFI<'a>, @@ -195,26 +73,70 @@ async fn main(spawner: Spawner) -> ! { esp_bootloader_esp_idf::esp_app_desc!(); esp_println::logger::init_logger_from_env(); println!("HSM: Initialising peripherals "); - let peripherals = peripherals_wait_for_initialisation().await; - println!("HSM: Initialising timers "); - let software_interrupts = SoftwareInterruptControl::new(peripherals.sw_interrupt); + // System init + let peripherals = esp_hal::init(esp_hal::Config::default()); + let rng = Rng::new(); + rng::register_custom_rng(rng); + + println!("Initialising flash "); + // Read SSH configuration from Flash (if it exists) + flash::init(peripherals.FLASH); + + println!("Loading config "); + let flash_config = { + let Some(flash_storage_guard) = flash::get_flash_n_buffer() else { + panic!("Could not acquire flash storage lock"); + }; + let mut flash_storage = flash_storage_guard.lock().await; + // TODO: Migrate this function/test to embedded-test. + // Quick roundtrip test for SSHStampConfig + // ssh_stamp::config::roundtrip_config(); + ssh_stamp::store::load_or_create(&mut flash_storage).await + } + .expect("Could not load or create SSHStampConfig"); + + println!("Initialising config "); + static CONFIG: StaticCell> = StaticCell::new(); + let config: &SunsetMutex = CONFIG.init(SunsetMutex::new(flash_config)); + + println!("Initialising gpio "); + // Only certain GPIO are available for each target. + // TODO: Confirm working pins on every target. + let mut gpios: GPIOS = Default::default(); + cfg_if::cfg_if!( + if #[cfg(any(feature = "esp32"))]{ + gpios.gpio13 = Some(peripherals.GPIO13.into()); + gpios.gpio14 = Some(peripherals.GPIO14.into()); + } else if #[cfg(feature = "esp32c2")] { + gpios.gpio9 = Some(peripherals.GPIO9.into()); + gpios.gpio10 = Some(peripherals.GPIO10.into()); + } else if #[cfg(feature = "esp32c3")] { + gpios.gpio20 = Some(peripherals.GPIO20.into()); + gpios.gpio21 = Some(peripherals.GPIO21.into()); + } else { + gpios.gpio10 = Some(peripherals.GPIO10.into()); + gpios.gpio11 = Some(peripherals.GPIO11.into()); + } + ); + + println!("Initialising timers "); + let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { // TODO: Test this feature configuration - let timg1 = TimerGroup::new(peripherals.timg1); + let timg1 = TimerGroup::new(peripherals.TIMG1); esp_rtos::start(timg1.timer0); } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { // TODO: Test this feature configuration use esp_hal::timer::systimer::SystemTimer; - let systimer = SystemTimer::new(peripherals.systimer); + let systimer = SystemTimer::new(peripherals.SYSTIMER); esp_rtos::start(systimer.alarm0); } else { - // let sw_int = SoftwareInterruptControl::new(peripherals.sw_interrupt); use esp_hal::timer::systimer::SystemTimer; - let systimer = SystemTimer::new(peripherals.systimer); - esp_rtos::start(systimer.alarm0, software_interrupts.software_interrupt0); + let systimer = SystemTimer::new(peripherals.SYSTIMER); + esp_rtos::start(systimer.alarm0, sw_int.software_interrupt0); } } @@ -222,7 +144,7 @@ async fn main(spawner: Spawner) -> ! { // Must be higher priority than esp_rtos (Priority1) let uart_buf = UART_BUF.init_with(BufferedUart::new); let interrupt_executor = - INT_EXECUTOR.init_with(|| InterruptExecutor::new(software_interrupts.software_interrupt1)); + INT_EXECUTOR.init_with(|| InterruptExecutor::new(sw_int.software_interrupt1)); cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { let interrupt_spawner = interrupt_executor.start(Priority::Priority3); @@ -232,40 +154,15 @@ async fn main(spawner: Spawner) -> ! { } // Use the same config reference for UART task. - // Pass pin_channel_ref into the UART task so it can acquire/release pins. - cfg_if::cfg_if! { - if #[cfg(feature = "esp32")] { - interrupt_spawner - .spawn(uart_task( - uart_buf, - peripherals.uart1, - &peripherals.config, - // peripherals.gpios, - peripherals.gpios.gpio13.into(), - peripherals.gpios.gpio14.into(), - )) - .unwrap(); - } else { - interrupt_spawner - .spawn(uart_task( - uart_buf, - peripherals.uart1, - &peripherals.config, - // peripherals.gpios, - peripherals.gpios.gpio10.into(), - peripherals.gpios.gpio11.into(), - )) - .unwrap(); - } - } - // TODO: Migrate this function/test to embedded-test. - // Quick roundtrip test for SSHStampConfig - // ssh_stamp::config::roundtrip_config(); + // Pass GPIO peripherals which can then be selected from config values + interrupt_spawner + .spawn(uart_task(uart_buf, peripherals.UART1, &config, gpios)) + .unwrap(); let peripherals_enabled_struct = SshStampInit { - rng: peripherals.rng, - wifi: peripherals.wifi, - config: peripherals.config, + rng: rng, + wifi: peripherals.WIFI, + config: config, spawner: spawner, uart_buf: uart_buf, }; @@ -575,7 +472,6 @@ where ssh_server: s.ssh_server, bridge: bridge, connection_loop: s.connection_loop, - // uart: s.uart, tcp_socket: s.tcp_socket, }; match bridge_connected(uart_enabled_struct).await { @@ -622,26 +518,17 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { use esp_hal::uart::{RxConfig, Uart}; -// cfg_if::cfg_if! { -// if #[cfg(feature = "esp32")] { -// use esp_hal::peripherals::{GPIO1, GPIO3}; -// } else { -// use esp_hal::peripherals::{GPIO10, GPIO11}; -// } -// } -cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - pub struct GPIOS<'a> { - pub gpio13: AnyPin<'a>, - pub gpio14: AnyPin<'a>, - } - } else { - pub struct GPIOS<'a> { - pub gpio10: AnyPin<'a>, - pub gpio11: AnyPin<'a>, - } - } -); +#[derive(Default)] +pub struct GPIOS<'a> { + pub gpio9: Option>, + pub gpio10: Option>, + pub gpio11: Option>, + pub gpio12: Option>, + pub gpio13: Option>, + pub gpio14: Option>, + pub gpio20: Option>, + pub gpio21: Option>, +} pub static UART_BUF: StaticCell = StaticCell::new(); @@ -654,15 +541,14 @@ use embassy_sync::signal::Signal; pub static UART_SIGNAL: Signal = Signal::new(); // use embassy_time::{Duration, Timer}; // use esp_hal::gpio::AnyPin; +use esp_hal::uart::Config; use esp_println::dbg; #[embassy_executor::task] pub async fn uart_task( uart_buf: &'static BufferedUart, uart1: UART1<'static>, _config: &'static SunsetMutex, - // gpios: GPIOS<'static>, - rx_pin: AnyPin<'static>, - tx_pin: AnyPin<'static>, + gpios: GPIOS<'static>, ) -> () { dbg!("Configuring UART"); Timer::after(Duration::from_millis(100)).await; @@ -671,69 +557,110 @@ pub async fn uart_task( dbg!("UART signal recieved"); Timer::after(Duration::from_millis(100)).await; + // Temporarily hardcoded pin numbers. Restore once ServEvent::SessionEnv properly updates config // let config_lock = config.lock().await; // let rx: u8 = config_lock.uart_pins.rx; // let tx: u8 = config_lock.uart_pins.tx; - // if rx != tx { - // cfg_if::cfg_if!( - // if #[cfg(feature = "esp32")] { - // let tx_pin: AnyPin<'static> = gpio1.into(); - // let rx_pin: AnyPin<'static> = gpio3.into(); - // // let mut holder0 = Some(gpios.gpio1); - // // let mut holder1 = Some(gpios.gpio3); - // } else { - // let tx_pin = gpio10; - // let rx_pin = gpio11; - // // let mut holder0 = Some(gpios.gpio10); - // // let mut holder1 = Some(gpios.gpio11); - // } - // ); - - // let rx_pin = match rx { - // 1 => holder0.take().unwrap().degrade(), - // 3 => holder1.take().unwrap().degrade(), - // 10 => holder0.take().unwrap().degrade(), - // 11 => holder1.take().unwrap().degrade(), - // _ => holder0.take().unwrap().degrade(), - // }; - // let tx_pin = match tx { - // 1 => holder0.take().unwrap().degrade(), - // 3 => holder1.take().unwrap().degrade(), - // 10 => holder0.take().unwrap().degrade(), - // 11 => holder1.take().unwrap().degrade(), - // _ => holder1.take().unwrap().degrade(), - // }; - - dbg!("UART config"); - Timer::after(Duration::from_millis(100)).await; - - // Hardware UART setup - let uart_config = esp_hal::uart::Config::default().with_rx( - RxConfig::default() - .with_fifo_full_threshold(16) - .with_timeout(1), + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let rx: u8 = 13; + let tx: u8 = 14; + } else if #[cfg(feature = "esp32c2")] { + let rx: u8 = 9; + let tx: u8 = 10; + } else if #[cfg(feature = "esp32c3")] { + let rx: u8 = 20; + let tx: u8 = 21; + } else { + let rx: u8 = 10; + let tx: u8 = 11; + } ); - // let uart_config = Config::default().with_rx( - // RxConfig::default() - // .with_fifo_full_threshold(16) - // .with_timeout(1), - // ); - - dbg!("UART setup pins"); - Timer::after(Duration::from_millis(100)).await; - - let uart = Uart::new(uart1, uart_config) - .unwrap() - .with_rx(rx_pin) - .with_tx(tx_pin) - .into_async(); - - // Run the main buffered TX/RX loop - dbg!("uart_task running UART"); - Timer::after(Duration::from_millis(100)).await; - uart_buf.run(uart).await; - // } + println!("Config Read - RX Pin: {} TX Pin: {}", rx, tx); + if rx != tx { + let mut _holder9 = Some(gpios.gpio9); + let mut _holder10 = Some(gpios.gpio10); + let mut _holder11 = Some(gpios.gpio11); + let mut _holder13 = Some(gpios.gpio13); + let mut _holder14 = Some(gpios.gpio14); + let mut _holder20 = Some(gpios.gpio20); + let mut _holder21 = Some(gpios.gpio21); + // Not every GPIO is available for every target. + // TODO: Merge all targets to only match on available GPIO + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let rx_pin = match rx { + 13 => _holder13.take().unwrap().unwrap(), + 14 => _holder14.take().unwrap().unwrap(), + _ => _holder13.take().unwrap().unwrap(), + }; + let tx_pin = match tx { + 13 => _holder13.take().unwrap().unwrap(), + 14 => _holder14.take().unwrap().unwrap(), + _ => _holder13.take().unwrap().unwrap(), + }; + } else if #[cfg(feature = "esp32c2")] { + let rx_pin = match rx { + 9 => _holder9.take().unwrap().unwrap(), + 10 => _holder10.take().unwrap().unwrap(), + _ => _holder9.take().unwrap().unwrap(), + }; + let tx_pin = match tx { + 9 => _holder9.take().unwrap().unwrap(), + 10 => _holder10.take().unwrap().unwrap(), + _ => _holder10.take().unwrap().unwrap(), + }; + } else if #[cfg(feature = "esp32c3")] { + let rx_pin = match rx { + 20 => _holder20.take().unwrap().unwrap(), + 21 => _holder21.take().unwrap().unwrap(), + _ => _holder20.take().unwrap().unwrap(), + }; + let tx_pin = match tx { + 20 => _holder20.take().unwrap().unwrap(), + 21 => _holder21.take().unwrap().unwrap(), + _ => _holder21.take().unwrap().unwrap(), + }; + } else { + let rx_pin = match rx { + 10 => _holder10.take().unwrap().unwrap(), + 11 => _holder11.take().unwrap().unwrap(), + _ => _holder10.take().unwrap().unwrap(), + }; + let tx_pin = match tx { + 10 => _holder10.take().unwrap().unwrap(), + 11 => _holder11.take().unwrap().unwrap(), + _ => _holder11.take().unwrap().unwrap(), + }; + } + ); + + dbg!("UART config"); + Timer::after(Duration::from_millis(100)).await; + + // Hardware UART setup + let uart_config = Config::default().with_rx( + RxConfig::default() + .with_fifo_full_threshold(16) + .with_timeout(1), + ); + + dbg!("UART setup pins"); + Timer::after(Duration::from_millis(100)).await; + + let uart = Uart::new(uart1, uart_config) + .unwrap() + .with_rx(rx_pin) + .with_tx(tx_pin) + .into_async(); + + // Run the main buffered TX/RX loop + dbg!("uart_task running UART"); + Timer::after(Duration::from_millis(100)).await; + uart_buf.run(uart).await; + } // TODO: Pin config error - // Ok(()) + dbg!("uart_task Pin config error! Using the same pin number for RX and TX!"); + () } diff --git a/src/settings.rs b/src/settings.rs index 0f3097b..9a80af5 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -8,11 +8,25 @@ use core::net::Ipv4Addr; pub(crate) const DEFAULT_SSID: &str = "ssh-stamp"; //pub(crate) const SSH_SERVER_ID: &str = "SSH-2.0-ssh-stamp-0.1"; pub(crate) const KEY_SLOTS: usize = 1; // TODO: Document whether this a "reasonable default"? Justify why? - //pub(crate) const PASSWORD_AUTHENTICATION: bool = true; +//pub(crate) const PASSWORD_AUTHENTICATION: bool = true; pub(crate) const DEFAULT_IP: &Ipv4Addr = &Ipv4Addr::new(192, 168, 4, 1); // UART settings //pub(crate) const BAUD_RATE: u32 = 115200; //pub(crate) const UART_SETTINGS: &str = "8N1"; -pub(crate) const DEFAULT_UART_TX_PIN: u8 = 10; -pub(crate) const DEFAULT_UART_RX_PIN: u8 = 11; +cfg_if::cfg_if!( + // if #[cfg(feature = "esp32")] { + if #[cfg(feature = "esp32")] { + pub(crate) const DEFAULT_UART_TX_PIN: u8 = 14; + pub(crate) const DEFAULT_UART_RX_PIN: u8 = 13; + } else if #[cfg(feature = "esp32c2")] { + pub(crate) const DEFAULT_UART_TX_PIN: u8 = 10; + pub(crate) const DEFAULT_UART_RX_PIN: u8 = 9; + } else if #[cfg(feature = "esp32c3")] { + pub(crate) const DEFAULT_UART_TX_PIN: u8 = 21; + pub(crate) const DEFAULT_UART_RX_PIN: u8 = 20; + } else { + pub(crate) const DEFAULT_UART_TX_PIN: u8 = 10; + pub(crate) const DEFAULT_UART_RX_PIN: u8 = 11; + } +); diff --git a/src/store.rs b/src/store.rs index e2cbedc..836e007 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,5 +1,5 @@ -use embedded_storage::nor_flash::NorFlash; use embedded_storage::ReadStorage; +use embedded_storage::nor_flash::NorFlash; use esp_bootloader_esp_idf::partitions; use esp_println::{dbg, println}; From 0d9491136d0076468642dd768d704d074169f091 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 19 Feb 2026 22:55:30 +1100 Subject: [PATCH 71/86] Move uart task to buffered_uart.rs. Move UART_SIGNAL trigger from HSM function to connection_loop. Now properly triggers after storing pin changes. --- src/espressif/buffered_uart.rs | 180 +++++++++++++++++++++++++++------ src/espressif/net.rs | 9 +- src/main.rs | 167 +----------------------------- src/serve.rs | 4 + 4 files changed, 160 insertions(+), 200 deletions(-) diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index dd36053..1e421e5 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -2,19 +2,27 @@ // // SPDX-License-Identifier: GPL-3.0-or-later -use portable_atomic::{AtomicUsize, Ordering}; - -use embassy_futures::select::select; -use embassy_sync::pipe::TryWriteError; /// Wrapper around bidirectional embassy-sync Pipes, in order to handle UART /// RX/RX happening in an InterruptExecutor at higher priority. /// /// Doesn't implement the InterruptExecutor, in the task in the app should await /// the 'run' async function. +/// +use crate::config::SSHStampConfig; +use embassy_futures::select::select; +use embassy_sync::pipe::TryWriteError; +use embassy_sync::signal::Signal; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; use esp_hal::Async; +use esp_hal::gpio::AnyPin; +use esp_hal::peripherals::UART1; use esp_hal::system::software_reset; -use esp_hal::uart::Uart; +use esp_hal::uart::{Config, RxConfig, Uart}; +use esp_println::dbg; +use esp_println::println; +use portable_atomic::{AtomicUsize, Ordering}; +use static_cell::StaticCell; +use sunset_async::SunsetMutex; // Sizes of the software buffers. Inward is more // important as an overrun here drops bytes. A full outward @@ -32,7 +40,7 @@ pub struct BufferedUart { dropped_rx_bytes: AtomicUsize, } -pub struct Config {} +pub struct UartConfig {} impl BufferedUart { pub fn new() -> Self { @@ -108,7 +116,7 @@ impl BufferedUart { self.dropped_rx_bytes.swap(0, Ordering::Relaxed) } - pub fn reconfigure(&self, _config: Config) { + pub fn reconfigure(&self, _config: &'static SunsetMutex) { todo!(); } } @@ -132,27 +140,137 @@ pub async fn uart_disable() -> () { software_reset(); } -// use esp_hal::uart::{Config, RxConfig, Uart}; - -// async fn uart_task( -// buffer: &'static BufferedUart, -// uart_periph: UART1<'static>, -// rx_pin: AnyPin<'static>, -// tx_pin: AnyPin<'static>, -// ) { -// // Hardware UART setup -// let uart_config = Config::default().with_rx( -// RxConfig::default() -// .with_fifo_full_threshold(16) -// .with_timeout(1), -// ); - -// let uart = Uart::new(uart_periph, uart_config) -// .unwrap() -// .with_rx(rx_pin) -// .with_tx(tx_pin) -// .into_async(); - -// // Run the main buffered TX/RX loop -// buffer.run(uart).await; -// } +#[derive(Default)] +pub struct GPIOS<'a> { + pub gpio9: Option>, + pub gpio10: Option>, + pub gpio11: Option>, + pub gpio12: Option>, + pub gpio13: Option>, + pub gpio14: Option>, + pub gpio20: Option>, + pub gpio21: Option>, +} + +pub static UART_BUF: StaticCell = StaticCell::new(); +pub static UART_SIGNAL: Signal = Signal::new(); + +pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { + UART_BUF.init_with(BufferedUart::new) +} + +#[embassy_executor::task] +pub async fn uart_task( + uart_buf: &'static BufferedUart, + uart1: UART1<'static>, + _config: &'static SunsetMutex, + gpios: GPIOS<'static>, +) -> () { + dbg!("UART task started"); + + // Wait until ssh shell complete + UART_SIGNAL.wait().await; + dbg!("UART signal recieved"); + + // Temporarily hardcoded pin numbers. Restore once ServEvent::SessionEnv properly updates config + // let config_lock = config.lock().await; + // let rx: u8 = config_lock.uart_pins.rx; + // let tx: u8 = config_lock.uart_pins.tx; + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let rx: u8 = 13; + let tx: u8 = 14; + } else if #[cfg(feature = "esp32c2")] { + let rx: u8 = 9; + let tx: u8 = 10; + } else if #[cfg(feature = "esp32c3")] { + let rx: u8 = 20; + let tx: u8 = 21; + } else { + let rx: u8 = 10; + let tx: u8 = 11; + } + ); + + println!("Config Read - RX Pin: {} TX Pin: {}", rx, tx); + if rx != tx { + let mut _holder9 = Some(gpios.gpio9); + let mut _holder10 = Some(gpios.gpio10); + let mut _holder11 = Some(gpios.gpio11); + let mut _holder13 = Some(gpios.gpio13); + let mut _holder14 = Some(gpios.gpio14); + let mut _holder20 = Some(gpios.gpio20); + let mut _holder21 = Some(gpios.gpio21); + // Not every GPIO is available for every target. + // TODO: Merge all targets to only match on available GPIO + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let rx_pin = match rx { + 13 => _holder13.take().unwrap().unwrap(), + 14 => _holder14.take().unwrap().unwrap(), + _ => _holder13.take().unwrap().unwrap(), + }; + let tx_pin = match tx { + 13 => _holder13.take().unwrap().unwrap(), + 14 => _holder14.take().unwrap().unwrap(), + _ => _holder13.take().unwrap().unwrap(), + }; + } else if #[cfg(feature = "esp32c2")] { + let rx_pin = match rx { + 9 => _holder9.take().unwrap().unwrap(), + 10 => _holder10.take().unwrap().unwrap(), + _ => _holder9.take().unwrap().unwrap(), + }; + let tx_pin = match tx { + 9 => _holder9.take().unwrap().unwrap(), + 10 => _holder10.take().unwrap().unwrap(), + _ => _holder10.take().unwrap().unwrap(), + }; + } else if #[cfg(feature = "esp32c3")] { + let rx_pin = match rx { + 20 => _holder20.take().unwrap().unwrap(), + 21 => _holder21.take().unwrap().unwrap(), + _ => _holder20.take().unwrap().unwrap(), + }; + let tx_pin = match tx { + 20 => _holder20.take().unwrap().unwrap(), + 21 => _holder21.take().unwrap().unwrap(), + _ => _holder21.take().unwrap().unwrap(), + }; + } else { + let rx_pin = match rx { + 10 => _holder10.take().unwrap().unwrap(), + 11 => _holder11.take().unwrap().unwrap(), + _ => _holder10.take().unwrap().unwrap(), + }; + let tx_pin = match tx { + 10 => _holder10.take().unwrap().unwrap(), + 11 => _holder11.take().unwrap().unwrap(), + _ => _holder11.take().unwrap().unwrap(), + }; + } + ); + + // Hardware UART setup + dbg!("UART config"); + let uart_config = Config::default().with_rx( + RxConfig::default() + .with_fifo_full_threshold(16) + .with_timeout(1), + ); + + dbg!("UART setup pins"); + let uart = Uart::new(uart1, uart_config) + .unwrap() + .with_rx(rx_pin) + .with_tx(tx_pin) + .into_async(); + + // Run the main buffered TX/RX loop + dbg!("uart_task running UART"); + uart_buf.run(uart).await; + } + // TODO: Pin config error + dbg!("uart_task Pin config error! Using the same pin number for RX and TX!"); + () +} diff --git a/src/espressif/net.rs b/src/espressif/net.rs index d186120..c40c441 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -2,6 +2,9 @@ // // SPDX-License-Identifier: GPL-3.0-or-later +use crate::config::SSHStampConfig; +use crate::espressif::buffered_uart::BufferedUart; +use crate::settings::{DEFAULT_IP, DEFAULT_SSID}; use core::net::Ipv4Addr; use core::net::SocketAddrV4; use core::str::FromStr; @@ -25,12 +28,6 @@ use esp_radio::wifi::WifiEvent; use esp_radio::wifi::{AccessPointConfig, ModeConfig, WifiApState, WifiController}; use heapless::String; use sunset_async::SunsetMutex; - -use crate::config::SSHStampConfig; -use crate::settings::{DEFAULT_IP, DEFAULT_SSID}; - -use super::buffered_uart::BufferedUart; - // When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html macro_rules! mk_static { ($t:ty,$val:expr) => {{ diff --git a/src/main.rs b/src/main.rs index 4a54329..4c83fc9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,25 +12,19 @@ use core::result::Result::Ok; use core::future::Future; use embassy_executor::Spawner; use embassy_futures::select::{Either3, select3}; -use embassy_net::IpListenEndpoint; use embassy_net::{Stack, tcp::TcpSocket}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; -use esp_hal::gpio::AnyPin; use esp_hal::interrupt::{Priority, software::SoftwareInterruptControl}; use esp_hal::system::software_reset; -use esp_hal::{peripherals::UART1, peripherals::WIFI, rng::Rng}; +use esp_hal::{peripherals::WIFI, rng::Rng}; use esp_println::println; use esp_radio::Controller; use esp_rtos::embassy::InterruptExecutor; use ssh_stamp::{ config::SSHStampConfig, espressif::{ - buffered_uart, - buffered_uart::BufferedUart, - // buffered_uart::GPIOS, - // net::accept_requests, - net, - rng, + buffered_uart::{self, BufferedUart, GPIOS, UART_BUF, UART_BUFFER_SIZE, uart_task}, + net, rng, }, serve, }; @@ -53,7 +47,6 @@ pub struct SshStampInit<'a> { pub rng: Rng, pub wifi: WIFI<'a>, pub config: &'a SunsetMutex, - // pub gpios: GPIOS<'a>, pub uart_buf: &'a BufferedUart, pub spawner: Spawner, } @@ -187,6 +180,7 @@ pub struct PeripheralsEnabled<'a> { pub uart_buf: &'a BufferedUart, pub spawner: Spawner, } + async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Error> { println!("HSM: peripherals_enabled"); let controller = esp_radio::init().unwrap(); @@ -286,7 +280,6 @@ pub struct SocketEnabled<'a> { pub uart_buf: &'a BufferedUart, } -use buffered_uart::UART_BUFFER_SIZE; async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { println!("HSM: socket_enabled"); // loop { @@ -421,10 +414,7 @@ where // loop { // let uart = buffered_uart::uart_task(s.uart_buff, s.uart1, &s.config, s.gpios); - // Signal for uart task to configure pins and run. - UART_SIGNAL.signal(1); Timer::after(Duration::from_millis(500)).await; - println!("HSM: UART_SIGNAL sent"); let uart_buffer_ready_struct = UartBufferReady { tcp_socket: s.tcp_socket, @@ -515,152 +505,3 @@ where fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } - -use esp_hal::uart::{RxConfig, Uart}; - -#[derive(Default)] -pub struct GPIOS<'a> { - pub gpio9: Option>, - pub gpio10: Option>, - pub gpio11: Option>, - pub gpio12: Option>, - pub gpio13: Option>, - pub gpio14: Option>, - pub gpio20: Option>, - pub gpio21: Option>, -} - -pub static UART_BUF: StaticCell = StaticCell::new(); - -pub async fn uart_buffer_wait_for_initialisation() -> &'static BufferedUart { - UART_BUF.init_with(BufferedUart::new) -} - -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::signal::Signal; -pub static UART_SIGNAL: Signal = Signal::new(); -// use embassy_time::{Duration, Timer}; -// use esp_hal::gpio::AnyPin; -use esp_hal::uart::Config; -use esp_println::dbg; -#[embassy_executor::task] -pub async fn uart_task( - uart_buf: &'static BufferedUart, - uart1: UART1<'static>, - _config: &'static SunsetMutex, - gpios: GPIOS<'static>, -) -> () { - dbg!("Configuring UART"); - Timer::after(Duration::from_millis(100)).await; - // Wait until ssh complete - UART_SIGNAL.wait().await; - dbg!("UART signal recieved"); - Timer::after(Duration::from_millis(100)).await; - - // Temporarily hardcoded pin numbers. Restore once ServEvent::SessionEnv properly updates config - // let config_lock = config.lock().await; - // let rx: u8 = config_lock.uart_pins.rx; - // let tx: u8 = config_lock.uart_pins.tx; - cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - let rx: u8 = 13; - let tx: u8 = 14; - } else if #[cfg(feature = "esp32c2")] { - let rx: u8 = 9; - let tx: u8 = 10; - } else if #[cfg(feature = "esp32c3")] { - let rx: u8 = 20; - let tx: u8 = 21; - } else { - let rx: u8 = 10; - let tx: u8 = 11; - } - ); - - println!("Config Read - RX Pin: {} TX Pin: {}", rx, tx); - if rx != tx { - let mut _holder9 = Some(gpios.gpio9); - let mut _holder10 = Some(gpios.gpio10); - let mut _holder11 = Some(gpios.gpio11); - let mut _holder13 = Some(gpios.gpio13); - let mut _holder14 = Some(gpios.gpio14); - let mut _holder20 = Some(gpios.gpio20); - let mut _holder21 = Some(gpios.gpio21); - // Not every GPIO is available for every target. - // TODO: Merge all targets to only match on available GPIO - cfg_if::cfg_if!( - if #[cfg(feature = "esp32")] { - let rx_pin = match rx { - 13 => _holder13.take().unwrap().unwrap(), - 14 => _holder14.take().unwrap().unwrap(), - _ => _holder13.take().unwrap().unwrap(), - }; - let tx_pin = match tx { - 13 => _holder13.take().unwrap().unwrap(), - 14 => _holder14.take().unwrap().unwrap(), - _ => _holder13.take().unwrap().unwrap(), - }; - } else if #[cfg(feature = "esp32c2")] { - let rx_pin = match rx { - 9 => _holder9.take().unwrap().unwrap(), - 10 => _holder10.take().unwrap().unwrap(), - _ => _holder9.take().unwrap().unwrap(), - }; - let tx_pin = match tx { - 9 => _holder9.take().unwrap().unwrap(), - 10 => _holder10.take().unwrap().unwrap(), - _ => _holder10.take().unwrap().unwrap(), - }; - } else if #[cfg(feature = "esp32c3")] { - let rx_pin = match rx { - 20 => _holder20.take().unwrap().unwrap(), - 21 => _holder21.take().unwrap().unwrap(), - _ => _holder20.take().unwrap().unwrap(), - }; - let tx_pin = match tx { - 20 => _holder20.take().unwrap().unwrap(), - 21 => _holder21.take().unwrap().unwrap(), - _ => _holder21.take().unwrap().unwrap(), - }; - } else { - let rx_pin = match rx { - 10 => _holder10.take().unwrap().unwrap(), - 11 => _holder11.take().unwrap().unwrap(), - _ => _holder10.take().unwrap().unwrap(), - }; - let tx_pin = match tx { - 10 => _holder10.take().unwrap().unwrap(), - 11 => _holder11.take().unwrap().unwrap(), - _ => _holder11.take().unwrap().unwrap(), - }; - } - ); - - dbg!("UART config"); - Timer::after(Duration::from_millis(100)).await; - - // Hardware UART setup - let uart_config = Config::default().with_rx( - RxConfig::default() - .with_fifo_full_threshold(16) - .with_timeout(1), - ); - - dbg!("UART setup pins"); - Timer::after(Duration::from_millis(100)).await; - - let uart = Uart::new(uart1, uart_config) - .unwrap() - .with_rx(rx_pin) - .with_tx(tx_pin) - .into_async(); - - // Run the main buffered TX/RX loop - dbg!("uart_task running UART"); - Timer::after(Duration::from_millis(100)).await; - uart_buf.run(uart).await; - } - // TODO: Pin config error - dbg!("uart_task Pin config error! Using the same pin number for RX and TX!"); - () -} diff --git a/src/serve.rs b/src/serve.rs index 8343fcc..e61252c 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -19,6 +19,7 @@ use esp_hal::system::software_reset; use heapless::String; use sunset_async::SunsetMutex; // use sunset::sshwire::SSHEncode; +use crate::espressif::buffered_uart::UART_SIGNAL; use sunset::{ChanHandle, ServEvent, SignKey, error}; use sunset_async::{ProgressHolder, SSHServer}; @@ -81,6 +82,9 @@ pub async fn connection_loop<'a>( debug_assert!(ch.num() == a.channel()); a.succeed()?; dbg!("We got shell"); + // Signal for uart task to configure pins and run. Value is irrelevant. + UART_SIGNAL.signal(1); + println!("Connection loop: UART_SIGNAL sent"); let _ = chan_pipe.try_send(SessionType::Bridge(ch)); } else { a.fail()?; From b0ef820f8bd8440f01beb13988ca8b6e73d341ed Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 19 Feb 2026 22:58:22 +1100 Subject: [PATCH 72/86] Restore tcp_socket creation from main back into net::accept_requests() --- src/espressif/net.rs | 47 +++++++++----------------------------------- src/main.rs | 18 +++-------------- 2 files changed, 12 insertions(+), 53 deletions(-) diff --git a/src/espressif/net.rs b/src/espressif/net.rs index c40c441..6d7d568 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -98,7 +98,12 @@ pub async fn ap_stack_disable() -> () { software_reset(); } -pub async fn create_tcp_socket<'a>( +pub async fn tcp_socket_disable() -> () { + // drop tcp stack + software_reset(); +} + +pub async fn accept_requests<'a>( tcp_stack: Stack<'a>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8], @@ -114,46 +119,12 @@ pub async fn create_tcp_socket<'a>( .await { println!("connect error: {:?}", e); + // continue; tcp_socket_disable().await; } - tcp_socket -} + println!("Connected, port 22"); -pub async fn tcp_socket_disable() -> () { - // drop tcp stack - software_reset(); -} - -pub async fn accept_requests<'a>(stack: Stack<'a>, _uart: &BufferedUart) -> ! { - // let rx_buffer = mk_static!([u8; 1536], [0; 1536]); - // let tx_buffer = mk_static!([u8; 1536], [0; 1536]); - let mut rx_buffer = [0u8; 1536]; - let mut tx_buffer = [0u8; 1536]; - loop { - // let mut socket = TcpSocket::new(stack, rx_buffer, tx_buffer); - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - - println!("Waiting for SSH client..."); - - if let Err(e) = socket - .accept(IpListenEndpoint { - addr: None, - port: 22, - }) - .await - { - println!("connect error: {:?}", e); - continue; - } - - println!("Connected, port 22"); - // match crate::serve::handle_ssh_client(&mut socket, uart, pin_channel_ref).await { - // Ok(_) => (), - // Err(e) => { - // println!("SSH client fatal error: {}", e); - // } - // }; - } + tcp_socket } #[embassy_executor::task] diff --git a/src/main.rs b/src/main.rs index 4c83fc9..b05f6b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -241,23 +241,11 @@ pub struct TCPEnabled<'a> { async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Error> { println!("HSM: tcp_enabled"); + let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; - let mut tcp_socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); - - println!("Waiting for SSH client..."); - if let Err(e) = tcp_socket - .accept(IpListenEndpoint { - addr: None, - port: 22, - }) - .await - { - println!("connect error: {:?}", e); - net::tcp_socket_disable().await; - } - // let tcp_socket = net::create_tcp_socket(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); - println!("Connected, port 22"); + let tcp_socket = net::accept_requests(s.tcp_stack, &mut rx_buffer, &mut tx_buffer).await; + let tcp_enabled_struct = TCPEnabled { config: s.config, tcp_socket: tcp_socket, From da4e46a1b6abbb0cfa150b9d5d733295f3e1c3ae Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 19 Feb 2026 23:04:04 +1100 Subject: [PATCH 73/86] Cleanup unneeded HSM state functions due to uart being in task. --- src/espressif/net.rs | 1 - src/main.rs | 110 +++++-------------------------------------- src/serve.rs | 5 +- 3 files changed, 13 insertions(+), 103 deletions(-) diff --git a/src/espressif/net.rs b/src/espressif/net.rs index 6d7d568..f6271ac 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later use crate::config::SSHStampConfig; -use crate::espressif::buffered_uart::BufferedUart; use crate::settings::{DEFAULT_IP, DEFAULT_SSID}; use core::net::Ipv4Addr; use core::net::SocketAddrV4; diff --git a/src/main.rs b/src/main.rs index b05f6b0..429189d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ use esp_rtos::embassy::InterruptExecutor; use ssh_stamp::{ config::SSHStampConfig, espressif::{ - buffered_uart::{self, BufferedUart, GPIOS, UART_BUF, UART_BUFFER_SIZE, uart_task}, + buffered_uart::{BufferedUart, GPIOS, UART_BUF, UART_BUFFER_SIZE, uart_task}, net, rng, }, serve, @@ -96,7 +96,6 @@ async fn main(spawner: Spawner) -> ! { println!("Initialising gpio "); // Only certain GPIO are available for each target. // TODO: Confirm working pins on every target. - let mut gpios: GPIOS = Default::default(); cfg_if::cfg_if!( if #[cfg(any(feature = "esp32"))]{ @@ -307,11 +306,8 @@ where pub chan_pipe: &'b Channel, pub connection_loop: CL, } -use embassy_time::{Duration, Timer}; -async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> -// where - // 'b: 'a, -{ + +async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> { println!("HSM: ssh_enabled"); // loop { println!("HSM: Starting channel pipe"); @@ -319,7 +315,7 @@ async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> println!("HSM: Started channel pipe. Calling connection_loop from ssh_enabled"); let connection = serve::connection_loop(&s.ssh_server, &chan_pipe, &s.config); println!("HSM: Started connection loop"); - Timer::after(Duration::from_millis(500)).await; + let ssh_enabled_struct = SshEnabled { tcp_socket: s.tcp_socket, ssh_server: &s.ssh_server, @@ -340,90 +336,7 @@ async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> Ok(()) // todo!() return relevant value } -pub struct ClientConnected<'a, 'b, CL> -where - CL: Future>, -{ - pub tcp_socket: TcpSocket<'a>, - pub ssh_server: &'b SSHServer<'a>, - pub chan_pipe: &'b Channel, - pub connection_loop: CL, - pub uart_buf: &'a BufferedUart, - pub config: &'a SunsetMutex, -} -async fn client_connected<'a, 'b, CL>(s: SshEnabled<'a, 'b, CL>) -> Result<(), sunset::Error> -// where 'b: 'a { -where - CL: Future>, -{ - println!("HSM: client_connected"); - // loop { - // let uart_buff = buffered_uart::uart_buffer_wait_for_initialisation().await; - Timer::after(Duration::from_millis(500)).await; - - let client_connected_struct = ClientConnected { - tcp_socket: s.tcp_socket, - ssh_server: s.ssh_server, - chan_pipe: s.chan_pipe, - connection_loop: s.connection_loop, - uart_buf: s.uart_buf, - config: s.config, - }; - match uart_buffer_ready(client_connected_struct).await { - Ok(_) => (), - Err(e) => { - println!("UART buffer error: {}", e); - } - } - - buffered_uart::uart_buffer_disable().await; - // } - Ok(()) // todo!() return relevant value -} - -pub struct UartBufferReady<'a, 'b, CL> -where - CL: Future>, -{ - pub tcp_socket: TcpSocket<'a>, - pub ssh_server: &'b SSHServer<'a>, - pub uart_buf: &'a BufferedUart, - pub chan_pipe: &'b Channel, - pub connection_loop: CL, -} - -async fn uart_buffer_ready<'a, 'b, CL>(s: ClientConnected<'a, 'b, CL>) -> Result<(), sunset::Error> -where - // 'b: 'a, - CL: Future>, -{ - println!("HSM: uart_buffer_ready"); - Timer::after(Duration::from_millis(500)).await; - - // loop { - // let uart = buffered_uart::uart_task(s.uart_buff, s.uart1, &s.config, s.gpios); - Timer::after(Duration::from_millis(500)).await; - - let uart_buffer_ready_struct = UartBufferReady { - tcp_socket: s.tcp_socket, - ssh_server: s.ssh_server, - chan_pipe: s.chan_pipe, - uart_buf: s.uart_buf, - connection_loop: s.connection_loop, - }; - match uart_enabled(uart_buffer_ready_struct).await { - Ok(_) => (), - Err(e) => { - println!("UART error: {}", e); - } - } - - buffered_uart::uart_disable().await; - // } - Ok(()) // todo!() return relevant value -} - -pub struct UartEnabled<'a, 'b, CL, BR> +pub struct ClientConnected<'a, 'b, CL, BR> where CL: Future>, BR: Future>, @@ -434,19 +347,18 @@ where pub tcp_socket: TcpSocket<'a>, } -async fn uart_enabled<'a, 'b, CL>(s: UartBufferReady<'a, 'b, CL>) -> Result<(), sunset::Error> +async fn client_connected<'a, 'b, CL>(s: SshEnabled<'a, 'b, CL>) -> Result<(), sunset::Error> where CL: Future>, 'a: 'b, { - println!("HSM: uart_enabled"); - Timer::after(Duration::from_millis(500)).await; + println!("HSM: client_connected"); // // loop { println!("HSM: Setting up serial bridge"); - let bridge = serve::bridge_wait_for_initialisation(s.uart_buf, s.ssh_server, s.chan_pipe); + let bridge = serve::handle_ssh_client(s.uart_buf, s.ssh_server, s.chan_pipe); - let uart_enabled_struct = UartEnabled { + let uart_enabled_struct = ClientConnected { ssh_server: s.ssh_server, bridge: bridge, connection_loop: s.connection_loop, @@ -466,7 +378,7 @@ where } async fn bridge_connected<'a, 'b, CL, BR>( - s: UartEnabled<'a, 'b, CL, BR>, + s: ClientConnected<'a, 'b, CL, BR>, ) -> Result<(), sunset::Error> where CL: Future>, @@ -476,7 +388,7 @@ where println!("HSM: bridge_connected"); let mut tcp_socket = s.tcp_socket; let (mut rsock, mut wsock) = tcp_socket.split(); - println!("HSM: Running server from handle_ssh_client()"); + println!("HSM: Running server from bridge_connected()"); let server = s.ssh_server.run(&mut rsock, &mut wsock); let connection_loop = s.connection_loop; let bridge = s.bridge; diff --git a/src/serve.rs b/src/serve.rs index e61252c..f8afd9f 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -20,11 +20,10 @@ use heapless::String; use sunset_async::SunsetMutex; // use sunset::sshwire::SSHEncode; use crate::espressif::buffered_uart::UART_SIGNAL; +use esp_println::{dbg, println}; use sunset::{ChanHandle, ServEvent, SignKey, error}; use sunset_async::{ProgressHolder, SSHServer}; -use esp_println::{dbg, println}; - pub enum SessionType { Bridge(ChanHandle), Sftp(ChanHandle), @@ -217,7 +216,7 @@ use crate::espressif::buffered_uart::BufferedUart; use crate::serial::serial_bridge; use sunset_async::ChanInOut; -pub async fn bridge_wait_for_initialisation<'a, 'b>( +pub async fn handle_ssh_client<'a, 'b>( uart_buff: &'a BufferedUart, ssh_server: &'b SSHServer<'a>, chan_pipe: &'b Channel, From 0c34e676bb5cfe2d29230c2055cc8829718b0fda Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Thu, 19 Feb 2026 23:09:37 +1100 Subject: [PATCH 74/86] Remove unused pins.rs --- src/pins.rs | 292 ---------------------------------------------------- 1 file changed, 292 deletions(-) delete mode 100644 src/pins.rs diff --git a/src/pins.rs b/src/pins.rs deleted file mode 100644 index 3c7cb74..0000000 --- a/src/pins.rs +++ /dev/null @@ -1,292 +0,0 @@ -use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel}; -use esp_hal::gpio::AnyPin; -use esp_hal::peripherals; -use sunset::sshwire::{SSHDecode, SSHEncode, SSHSink, SSHSource, WireResult}; -// use static_cell::StaticCell; -// use sunset_async::SunsetMutex; - -use crate::{ - config::{dec_option, enc_option}, - errors, -}; - -#[derive(Debug, Clone, PartialEq)] -pub struct SerdePinConfig { - pub tx: u8, - pub rx: u8, - pub rts: Option, - pub cts: Option, -} - -impl SerdePinConfig { - /// Create a new SerdePinConfig with default values. - pub fn new() -> Self { - Self::default() - } - - /// Set the TX pin, returning an updated SerdePinConfig (builder style). - pub fn with_tx(mut self, tx: u8) -> Self { - self.tx = tx; - self - } - - /// Set the RX pin, returning an updated SerdePinConfig (builder style). - pub fn with_rx(mut self, rx: u8) -> Self { - self.rx = rx; - self - } - - /// Set the RTS pin (optional), returning an updated SerdePinConfig. - pub fn with_rts(mut self, rts: Option) -> Self { - self.rts = rts; - self - } - - /// Set the CTS pin (optional), returning an updated SerdePinConfig. - pub fn with_cts(mut self, cts: Option) -> Self { - self.cts = cts; - self - } -} - -impl Default for SerdePinConfig { - fn default() -> Self { - Self { - tx: 10, - rx: 11, - rts: None, - cts: None, - } - } -} - -impl SSHEncode for SerdePinConfig { - fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> { - self.tx.enc(s)?; - self.rx.enc(s)?; - enc_option(&self.rts, s)?; - enc_option(&self.cts, s) - } -} - -impl<'de> SSHDecode<'de> for SerdePinConfig { - fn dec(s: &mut S) -> WireResult - where - S: SSHSource<'de>, - { - // Decoding Options is problematic since encode only writes them if they exist. - let mut pin_config = SerdePinConfig::default(); - pin_config.tx = u8::dec(s)?; - pin_config.rx = u8::dec(s)?; - - pin_config.rts = dec_option(s)?; - pin_config.cts = dec_option(s)?; - - Ok(pin_config) - } -} - -#[derive(Default)] -pub struct GPIOConfig<'a> { - pub gpio10: Option>, - pub gpio11: Option>, -} - -pub struct PinChannel<'a> { - pub config: SerdePinConfig, - pub gpios: GPIOConfig<'a>, - pub tx: Channel, - pub rx: Channel, - // TODO: cts/rts pins -} - -impl<'a> PinChannel<'a> { - pub fn new(config: SerdePinConfig, gpios: GPIOConfig<'a>) -> Self { - Self { - config, - gpios, - tx: Channel::::new(), - rx: Channel::::new(), - } - } - - pub async fn recv_tx(&mut self) -> errors::Result> { - // tx needs to lock here. - //self.tx.receive().await; - - Ok(match self.config.tx { - 10 => self - .gpios - .gpio10 - .take() - .ok_or_else(|| errors::Error::InvalidPin)?, - 11 => self - .gpios - .gpio11 - .take() - .ok_or_else(|| errors::Error::InvalidPin)?, - _ => return Err(errors::Error::InvalidPin), - }) - } - - pub async fn send_tx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { - match self.config.tx { - 10 => self.gpios.gpio10 = Some(pin), - 11 => self.gpios.gpio11 = Some(pin), - _ => return Err(errors::Error::InvalidPin), - }; - - // tx lock needs to be released. - self.tx.send(()).await; - Ok(()) - } - - pub async fn recv_rx(&mut self) -> errors::Result> { - let res = Ok(match self.config.rx { - 10 => self - .gpios - .gpio10 - .take() - .ok_or_else(|| errors::Error::InvalidPin)?, - 11 => self - .gpios - .gpio11 - .take() - .ok_or_else(|| errors::Error::InvalidPin)?, - _ => return Err(errors::Error::InvalidPin), - }); - // rx needs to lock here. - // dbg!("recv_rx: before rx.receive.await"); - // self.rx.receive().await; - // dbg!("recv_rx: after rx.receive.await"); - - res - } - - pub async fn send_rx(&mut self, pin: AnyPin<'a>) -> errors::Result<()> { - match self.config.rx { - 10 => self.gpios.gpio10 = Some(pin), - 11 => self.gpios.gpio11 = Some(pin), - _ => return Err(errors::Error::InvalidPin), - }; - - // rx lock needs to be released. - self.rx.send(()).await; - Ok(()) - } - - pub async fn with_channel(&mut self, f: F) -> errors::Result<()> - where - F: for<'b> AsyncFnOnce(AnyPin<'b>, AnyPin<'b>), - { - let mut rx = self.recv_rx().await?; - let mut tx = self.recv_tx().await?; - - f(rx.reborrow(), tx.reborrow()).await; - - self.send_rx(rx).await.unwrap(); - self.send_tx(tx).await.unwrap(); - - Ok(()) - } - - // Update the runtime config's TX pin number. This only changes the - // u8 config; actual AnyPin movement happens when the uart task next - // reacquires pins via recv_tx/recv_rx. - pub fn set_tx_pin(&mut self, tx: u8) -> errors::Result<()> { - if tx == self.config.rx { - return Err(errors::Error::InvalidPin); - } - match tx { - 10 | 11 => { - self.config.tx = tx; - Ok(()) - } - _ => Err(errors::Error::InvalidPin), - } - } - - pub fn set_rx_pin(&mut self, rx: u8) -> errors::Result<()> { - if rx == self.config.tx { - return Err(errors::Error::InvalidPin); - } - match rx { - 10 | 11 => { - self.config.rx = rx; - Ok(()) - } - _ => Err(errors::Error::InvalidPin), - } - } -} - -// Global PinChannel holder: initialize from main() and access from other modules. -// We keep a StaticCell but avoid any unsafe global pointer; callers receive -// the &'static SunsetMutex returned by init_global_channel and must retain it. -// static GLOBAL_PIN_CHANNEL: StaticCell> = StaticCell::new(); - -// pub fn init_global_channel(ch: PinChannel) -> &SunsetMutex { -// // Initialize the StaticCell and return the &'static reference. -// GLOBAL_PIN_CHANNEL.init(SunsetMutex::new(ch)) -// } - -pub struct PinConfig<'a> { - pub tx: AnyPin<'a>, - pub rx: AnyPin<'a>, -} - -pub struct PinConfigAlt { - pub peripherals: peripherals::Peripherals, -} - -impl PinConfigAlt { - pub fn new(peripherals: peripherals::Peripherals) -> Self { - Self { peripherals } - } - - pub fn take_pin<'a>(&'a mut self, pin: u8) -> AnyPin<'a> { - match pin { - 0 => self.peripherals.GPIO0.reborrow().into(), - 1 => self.peripherals.GPIO1.reborrow().into(), - _ => panic!(), - } - } -} - -impl<'a> PinConfig<'a> { - pub fn new( - mut gpio_config: GPIOConfig<'a>, - config_inner: SerdePinConfig, - ) -> errors::Result { - if config_inner.rx == config_inner.tx { - return Err(errors::Error::InvalidPin); - } - - // SAFETY: Safe because moved in peripherals. - Ok(Self { - rx: match config_inner.rx { - 10 => gpio_config.gpio10.take().unwrap().into(), - 11 => gpio_config.gpio11.take().unwrap().into(), - _ => return Err(errors::Error::InvalidPin), - }, - tx: match config_inner.tx { - 10 => gpio_config.gpio10.take().unwrap().into(), - 11 => gpio_config.gpio11.take().unwrap().into(), - _ => return Err(errors::Error::InvalidPin), - }, - }) - } - - /// Resolves a u8 pin number into an AnyPin GPIO type. - /// Returns None if the pin number is invalid or unsupported. - pub fn initialize_pin( - peripherals: peripherals::Peripherals, - pin_number: u8, - ) -> errors::Result> { - match pin_number { - 0 => Ok(peripherals.GPIO0.into()), - - _ => Err(errors::Error::InvalidPin), - } - } -} From c81afe4a0e6fdc277c3a32f3f417dcc165e3f346 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Fri, 20 Feb 2026 01:23:33 +1100 Subject: [PATCH 75/86] Start TCP socket in main to resolve ESP32 block in bridge connection. --- src/main.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 429189d..80871b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -238,13 +238,33 @@ pub struct TCPEnabled<'a> { pub uart_buf: &'a BufferedUart, } +cfg_if::cfg_if!(if #[cfg(feature = "esp32")] {use embassy_net::IpListenEndpoint;}); async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Error> { println!("HSM: tcp_enabled"); let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; - let tcp_socket = net::accept_requests(s.tcp_stack, &mut rx_buffer, &mut tx_buffer).await; + // TO FIX: ESP32 target needs tcp_socket.accept in main.rs - Gets blocked at bridge connection? + cfg_if::cfg_if!( + if #[cfg(feature = "esp32")] { + let mut tcp_socket = TcpSocket::new(s.tcp_stack, &mut rx_buffer, &mut tx_buffer); + println!("Waiting for SSH client..."); + if let Err(e) = tcp_socket + .accept(IpListenEndpoint { + addr: None, + port: 22, + }) + .await + { + println!("connect error: {:?}", e); + net::tcp_socket_disable().await; + } + println!("Connected, port 22"); + } else { + let tcp_socket = net::accept_requests(s.tcp_stack, &mut rx_buffer, &mut tx_buffer).await; + } + ); let tcp_enabled_struct = TCPEnabled { config: s.config, tcp_socket: tcp_socket, From 398e7f5344c90bda06e9cdf82df326772d0222bb Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:26:07 +1100 Subject: [PATCH 76/86] Remove unused storage/src/esp.rs --- storage/src/esp.rs | 12 ------------ storage/src/lib.rs | 2 -- 2 files changed, 14 deletions(-) delete mode 100644 storage/src/esp.rs diff --git a/storage/src/esp.rs b/storage/src/esp.rs deleted file mode 100644 index e9813af..0000000 --- a/storage/src/esp.rs +++ /dev/null @@ -1,12 +0,0 @@ -// #[allow(unused_imports)] -// use log::{debug, error, info, warn}; - -// use crate::flash; -// use embedded_storage::Storage; -// use esp_bootloader_esp_idf; -// use esp_hal::system; - -// TODO: This file contained many OTA-related -// functionality, it should be named as such -// i.e: esp_ota.rs or similar, not just -// esp.rs diff --git a/storage/src/lib.rs b/storage/src/lib.rs index cd29fce..16fc0b1 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -11,8 +11,6 @@ feature = "esp32c3", feature = "esp32c6" ))] -pub mod esp; - // TODO: When the time comes, generalise the flash so it can be used with all supported targets /// [[flash]] is a packet to provide safe access to the Flash storage used by SSH-Stamp /// From 57fc6a0ff09e9d05c3793f8d789957f2292dfd72 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:40:59 +1100 Subject: [PATCH 77/86] Move UART_BUFFER_SIZE setting to settings.rs --- src/espressif/buffered_uart.rs | 1 - src/main.rs | 3 ++- src/serve.rs | 10 +++++----- src/settings.rs | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index 1e421e5..89bcf19 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -127,7 +127,6 @@ impl Default for BufferedUart { } } -pub const UART_BUFFER_SIZE: usize = 4096; pub async fn uart_buffer_disable() -> () { // disable uart buffer diff --git a/src/main.rs b/src/main.rs index 80871b1..ba44dff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,10 +23,11 @@ use esp_rtos::embassy::InterruptExecutor; use ssh_stamp::{ config::SSHStampConfig, espressif::{ - buffered_uart::{BufferedUart, GPIOS, UART_BUF, UART_BUFFER_SIZE, uart_task}, + buffered_uart::{BufferedUart, GPIOS, UART_BUF, uart_task}, net, rng, }, serve, + settings::UART_BUFFER_SIZE, }; use static_cell::StaticCell; use storage::flash; diff --git a/src/serve.rs b/src/serve.rs index f8afd9f..77dcbbb 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -2,12 +2,12 @@ // // SPDX-License-Identifier: GPL-3.0-or-later -use core::option::Option::{self, None, Some}; -use core::result::Result; - use crate::config::SSHStampConfig; use crate::keys; +use crate::settings::UART_BUFFER_SIZE; use crate::store; +use core::option::Option::{self, None, Some}; +use core::result::Result; use storage::flash; // Embassy @@ -199,8 +199,8 @@ pub async fn connection_disable() -> () { } pub async fn ssh_wait_for_initialisation<'server>( - inbuf: &'server mut [u8; crate::espressif::buffered_uart::UART_BUFFER_SIZE], - outbuf: &'server mut [u8; crate::espressif::buffered_uart::UART_BUFFER_SIZE], + inbuf: &'server mut [u8; UART_BUFFER_SIZE], + outbuf: &'server mut [u8; UART_BUFFER_SIZE], ) -> SSHServer<'server> { let ssh_server = SSHServer::new(inbuf, outbuf); ssh_server diff --git a/src/settings.rs b/src/settings.rs index 9a80af5..0b66f87 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -10,6 +10,7 @@ pub(crate) const DEFAULT_SSID: &str = "ssh-stamp"; pub(crate) const KEY_SLOTS: usize = 1; // TODO: Document whether this a "reasonable default"? Justify why? //pub(crate) const PASSWORD_AUTHENTICATION: bool = true; pub(crate) const DEFAULT_IP: &Ipv4Addr = &Ipv4Addr::new(192, 168, 4, 1); +pub const UART_BUFFER_SIZE: usize = 4096; // UART settings //pub(crate) const BAUD_RATE: u32 = 115200; From 0e92030fe70c2bb54d11a35412e0411c1fe47b06 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:41:43 +1100 Subject: [PATCH 78/86] Fix ESP32-S2 target compilation. Target will still not run. --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index ba44dff..2b5dafc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,9 +57,9 @@ async fn main(spawner: Spawner) -> ! { println!("HSM: main"); cfg_if::cfg_if!( if #[cfg(feature = "esp32s2")] { - // TODO: This heap size will crash at runtime, we need to fix this + // TODO: This heap size will crash at runtime (only for the ESP32S2), we need to fix this // applying ideas from https://github.com/brainstorm/ssh-stamp/pull/41#issuecomment-2964775170 - esp_alloc::heap_allocator!(size: 69 * 1024); + esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 72 * 1024); } else { esp_alloc::heap_allocator!(size: 72 * 1024); } From f093b7f245b94f3d3c926311f95bb4147f41a041 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:43:00 +1100 Subject: [PATCH 79/86] Restore OTA Packer build to config.toml --- .cargo/config.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 66d0a6b..a4139ed 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -29,12 +29,13 @@ run-esp32c6 = "run --release --target riscv32imac-unknown-none-elf --no-default- run-esp32s2 = "run --profile esp32s2 --target xtensa-esp32s2-none-elf --no-default-features --features esp32s2 " run-esp32s3 = "run --release --target xtensa-esp32s3-none-elf --no-default-features --features esp32s3 " -# Build ota-packer alias: I might need to modify it so when run it can run a build for target architecture, extract the binary from the elf image and then pack the ota file. -ota-packer = "run --package ota --bin ota-packer --target x86_64-unknown-linux-gnu" - # Test alias test-ota = "test --package ota --target x86_64-unknown-linux-gnu" +# ota-packer aliases +build-ota-packer = "build --package ota --bin ota-packer --target x86_64-unknown-linux-gnu" +ota-packer = "run --package ota --bin ota-packer --target x86_64-unknown-linux-gnu" + [target.xtensa-esp32-none-elf] # ESP32 runner = "espflash flash --baud=921600 --monitor --chip esp32" rustflags = ["-C", "link-arg=-nostartfiles", '--cfg=feature="esp32"'] From a0d17898c96566c0095b55d570d79205fe3d83c1 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:55:15 +1100 Subject: [PATCH 80/86] Fix clippy lint errors for ESP32-C6. --- src/config.rs | 8 +++--- src/espressif/buffered_uart.rs | 1 - src/espressif/net.rs | 4 +-- src/main.rs | 51 +++++++++++++++++++++------------- src/serve.rs | 44 +++++++++++++---------------- 5 files changed, 56 insertions(+), 52 deletions(-) diff --git a/src/config.rs b/src/config.rs index 4b1071d..bcd1521 100644 --- a/src/config.rs +++ b/src/config.rs @@ -102,7 +102,7 @@ impl SSHStampConfig { } pub fn set_admin_pw(&mut self, pw: Option<&str>) -> Result<()> { - self.admin_pw = pw.map(|p| PwHash::new(p)).transpose()?; + self.admin_pw = pw.map(PwHash::new).transpose()?; Ok(()) } @@ -215,7 +215,7 @@ where return Err(WireError::PacketWrong); } let gw: Option = dec_option(s)?; - let gateway = gw.map(|gw| Ipv4Addr::from_bits(gw)); + let gateway = gw.map(Ipv4Addr::from_bits); Ok(StaticConfigV4 { address: Ipv4Cidr::new(ad, prefix), gateway, @@ -366,14 +366,14 @@ impl PwHash { return false; } let prehash = Self::prehash(pw, &self.salt); - let check_hash = bcrypt::bcrypt(self.cost as u32, self.salt.clone(), &prehash); + let check_hash = bcrypt::bcrypt(self.cost as u32, self.salt, &prehash); check_hash.ct_eq(&self.hash).into() } fn prehash(pw: &str, salt: &[u8]) -> [u8; 32] { // OK unwrap: can't fail, accepts any length // TODO: Generalise, not only Espressif esp_hal - let mut prehash = Hmac::::new_from_slice(&salt).unwrap(); + let mut prehash = Hmac::::new_from_slice(salt).unwrap(); prehash.update(pw.as_bytes()); prehash.finalize().into_bytes().into() } diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index 89bcf19..660e662 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -271,5 +271,4 @@ pub async fn uart_task( } // TODO: Pin config error dbg!("uart_task Pin config error! Using the same pin number for RX and TX!"); - () } diff --git a/src/espressif/net.rs b/src/espressif/net.rs index f6271ac..f06bd03 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -32,7 +32,7 @@ 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)); + let x = STATIC_CELL.uninit().write($val); x }}; } @@ -53,7 +53,7 @@ pub async fn if_up( let res = wifi_controller.set_config(&ap_config); println!("wifi_set_configuration returned {:?}", res); - let gw_ip_addr_ipv4 = DEFAULT_IP.clone(); + let gw_ip_addr_ipv4 = *DEFAULT_IP; let net_config = embassy_net::Config::ipv4_static(StaticConfigV4 { address: Ipv4Cidr::new(gw_ip_addr_ipv4, 24), diff --git a/src/main.rs b/src/main.rs index 2b5dafc..1ad9cbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,20 +97,31 @@ async fn main(spawner: Spawner) -> ! { println!("Initialising gpio "); // Only certain GPIO are available for each target. // TODO: Confirm working pins on every target. - let mut gpios: GPIOS = Default::default(); cfg_if::cfg_if!( if #[cfg(any(feature = "esp32"))]{ - gpios.gpio13 = Some(peripherals.GPIO13.into()); - gpios.gpio14 = Some(peripherals.GPIO14.into()); + let gpios = GPIOS { + gpio13: Some(peripherals.GPIO13.into()), + gpio14: Some(peripherals.GPIO14.into()), + .. Default::default() + }; } else if #[cfg(feature = "esp32c2")] { - gpios.gpio9 = Some(peripherals.GPIO9.into()); - gpios.gpio10 = Some(peripherals.GPIO10.into()); + let gpios = GPIOS { + gpio9: Some(peripherals.GPIO9.into()), + gpio10: Some(peripherals.GPIO10.into()), + .. Default::default() + }; } else if #[cfg(feature = "esp32c3")] { - gpios.gpio20 = Some(peripherals.GPIO20.into()); - gpios.gpio21 = Some(peripherals.GPIO21.into()); + let gpios = GPIOS { + gpio20: Some(peripherals.GPIO20.into()), + gpio21: Some(peripherals.GPIO21.into()), + .. Default::default() + }; } else { - gpios.gpio10 = Some(peripherals.GPIO10.into()); - gpios.gpio11 = Some(peripherals.GPIO11.into()); + let gpios = GPIOS { + gpio10: Some(peripherals.GPIO10.into()), + gpio11: Some(peripherals.GPIO11.into()), + .. Default::default() + }; } ); @@ -149,15 +160,15 @@ async fn main(spawner: Spawner) -> ! { // Use the same config reference for UART task. // Pass GPIO peripherals which can then be selected from config values interrupt_spawner - .spawn(uart_task(uart_buf, peripherals.UART1, &config, gpios)) + .spawn(uart_task(uart_buf, peripherals.UART1, config, gpios)) .unwrap(); let peripherals_enabled_struct = SshStampInit { - rng: rng, + rng, wifi: peripherals.WIFI, - config: config, - spawner: spawner, - uart_buf: uart_buf, + config, + spawner, + uart_buf, }; match peripherals_enabled(peripherals_enabled_struct).await { @@ -189,7 +200,7 @@ async fn peripherals_enabled(s: SshStampInit<'static>) -> Result<(), sunset::Err rng: s.rng, wifi: s.wifi, config: s.config, - controller: controller, + controller, uart_buf: s.uart_buf, spawner: s.spawner, }; @@ -221,7 +232,7 @@ pub async fn wifi_controller_enabled(s: PeripheralsEnabled<'static>) -> Result<( config: s.config, rng: s.rng, uart_buf: s.uart_buf, - tcp_stack: tcp_stack, + tcp_stack, }; match tcp_enabled(wifi_controller_enabled_stack).await { Ok(_) => (), @@ -268,7 +279,7 @@ async fn tcp_enabled<'a>(s: WifiControllerEnabled<'a>) -> Result<(), sunset::Err ); let tcp_enabled_struct = TCPEnabled { config: s.config, - tcp_socket: tcp_socket, + tcp_socket, uart_buf: s.uart_buf, }; match socket_enabled(tcp_enabled_struct).await { @@ -301,7 +312,7 @@ async fn socket_enabled<'a>(s: TCPEnabled<'a>) -> Result<(), sunset::Error> { let socket_enabled_struct = SocketEnabled { config: s.config, tcp_socket: s.tcp_socket, - ssh_server: ssh_server, + ssh_server, uart_buf: s.uart_buf, }; match ssh_enabled(socket_enabled_struct).await { @@ -334,7 +345,7 @@ async fn ssh_enabled<'a>(s: SocketEnabled<'a>) -> Result<(), sunset::Error> { println!("HSM: Starting channel pipe"); let chan_pipe = Channel::::new(); println!("HSM: Started channel pipe. Calling connection_loop from ssh_enabled"); - let connection = serve::connection_loop(&s.ssh_server, &chan_pipe, &s.config); + let connection = serve::connection_loop(&s.ssh_server, &chan_pipe, s.config); println!("HSM: Started connection loop"); let ssh_enabled_struct = SshEnabled { @@ -381,7 +392,7 @@ where let uart_enabled_struct = ClientConnected { ssh_server: s.ssh_server, - bridge: bridge, + bridge, connection_loop: s.connection_loop, tcp_socket: s.tcp_socket, }; diff --git a/src/serve.rs b/src/serve.rs index 77dcbbb..3d4ee70 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -9,7 +9,6 @@ use crate::store; use core::option::Option::{self, None, Some}; use core::result::Result; use storage::flash; - // Embassy use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; @@ -29,10 +28,10 @@ pub enum SessionType { Sftp(ChanHandle), } -pub async fn connection_loop<'a>( +pub async fn connection_loop( serv: &SSHServer<'_>, chan_pipe: &Channel, - config: &'a SunsetMutex, + config: &SunsetMutex, ) -> Result<(), sunset::Error> { let username = Mutex::::new(String::<20>::new()); let mut session: Option = None; @@ -202,8 +201,7 @@ pub async fn ssh_wait_for_initialisation<'server>( inbuf: &'server mut [u8; UART_BUFFER_SIZE], outbuf: &'server mut [u8; UART_BUFFER_SIZE], ) -> SSHServer<'server> { - let ssh_server = SSHServer::new(inbuf, outbuf); - ssh_server + SSHServer::new(inbuf, outbuf) } pub async fn ssh_disable() -> () { @@ -222,27 +220,23 @@ pub async fn handle_ssh_client<'a, 'b>( chan_pipe: &'b Channel, ) -> Result<(), sunset::Error> { dbg!("Preparing bridge"); - let bridge = { - let chan_pipe = chan_pipe; - let session_type = chan_pipe.receive().await; - dbg!("Checking bridge session type"); - match session_type { - SessionType::Bridge(ch) => { - dbg!("Handling bridge session"); - let stdio: ChanInOut<'_> = ssh_server.stdio(ch).await?; - let stdio2 = stdio.clone(); - dbg!("Starting bridge"); - serial_bridge(stdio, stdio2, uart_buff).await? - } - SessionType::Sftp(_ch) => { - dbg!("Handling SFTP session"); - // Handle SFTP session - // todo!() - } - }; - Ok(()) + let session_type = chan_pipe.receive().await; + dbg!("Checking bridge session type"); + match session_type { + SessionType::Bridge(ch) => { + dbg!("Handling bridge session"); + let stdio: ChanInOut<'_> = ssh_server.stdio(ch).await?; + let stdio2 = stdio.clone(); + dbg!("Starting bridge"); + serial_bridge(stdio, stdio2, uart_buff).await? + } + SessionType::Sftp(_ch) => { + dbg!("Handling SFTP session"); + // Handle SFTP session + // todo!() + } }; - bridge + Ok(()) } pub async fn bridge_disable() -> () { From 557b6498479e3af5a70fe37b6289fcfc69d36379 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:56:15 +1100 Subject: [PATCH 81/86] Update README with default UART pins for all targets. --- README.md | 89 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 2e8a073..aff59ca 100644 --- a/README.md +++ b/README.md @@ -39,59 +39,94 @@ A "low level to SSH Swiss army knife". Rust versions are controlled via `rust-toolchain.toml` and the equivalent defined on the CI workflow. -Required for all targets: +On a fresh system the following should be enough to build and run on the relevant ESP32 dev boards. + +## Required for all targets: ``` rustup toolchain install stable --component rust-src cargo install espflash ``` -On a fresh system the following should be enough to build and run on an ESP32-C6 dev board. +## ESP32-C6 + + ``` rustup target add riscv32imac-unknown-none-elf cargo build-esp32c6 cargo run-esp32c6 ``` -Build and run for ESP32-C2 / ESP32-C3: +## ESP32-C2 / ESP32-C3 +``` +rustup target add riscv32imc-unknown-none-elf` +``` +### ESP32-C2 ``` -rustup target add riscv32imc-unknown-none-elf cargo build-esp32c2 -cargo build-esp32c3 cargo run-esp32c2 +``` +### ESP32-C3 +``` +cargo build-esp32c2 cargo run-esp32c3 ``` -Build for ESP32 / ESP32-S2 / ESP32-S3 (Xtensa Cores) - + +## ESP32 / ESP32-S2 / ESP32-S3 (Xtensa Cores) Install esp toolchain first: https://github.com/esp-rs/espup ``` -cargo install espup -espup install -$HOME/export-esp.sh -rustup override set esp +cargo install espup +espup install +source $HOME/export-esp.sh +``` + +### ESP32 +``` +cargo +esp build-esp32 +cargo +esp run-esp32 +``` +### ESP32-S2 +``` +cargo +esp build-esp32s2 +cargo +esp run-esp32s2 +``` +### ESP32-S3 +``` +cargo +esp build-esp32s3 +cargo +esp run-esp32s3 +``` + +### Using rustup toolchain override (Doesn't require `+esp`) +To set rustup override: +``` +rustup override set esp +``` +To remove rustup override: +``` +cargo override unset +``` +Build: +``` cargo build-esp32 cargo build-esp32s2 cargo build-esp32s3 ``` -Running on the target: +Run: ``` cargo run-esp32 cargo run-esp32s2 cargo run-esp32s3 ``` -Alternatively to not use rustup override: -``` -cargo +esp build-esp32 -cargo +esp build-esp32s2 -cargo +esp build-esp32s3 -``` -Running on the target: -``` -cargo +esp run-esp32 -cargo +esp run-esp32s2 -cargo +esp run-esp32s3 -``` +# Default UART Pins +| Target | RX | TX | +| ---- | -- | -- | +| ESP32 | 13 | 14 | +| ESP32S2 | 11 | 10 | +| ESP32C2 | 9 | 10 | +| ESP32C3 | 20 | 21 | +| ESP32C6 | 11 | 10 | # Example usecases @@ -117,3 +152,11 @@ cargo cyclonedx -f json --manifest-path ./docs/ [nlnet-grant]: https://nlnet.nl/project/SSH-Stamp/ [openwrt_mediatek_no_monitor]: https://github.com/openwrt/openwrt/issues/16279 [nlnet_zero_commons]: ./docs/nlnet/zero_commons_logo.svg + +/dev/ttyUSB0 is ESP32 + +USB to UART +sudo minicom --device /dev/ttyUSB1 + + +https://docs.espressif.com/projects/esp-matter/en/latest/esp32/optimizations.html From f38b8cf81ba70ed3a783e4d20a34923839c50869 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Sat, 21 Feb 2026 13:30:47 +1100 Subject: [PATCH 82/86] Change to stable branch for building OTA packer. --- .github/workflows/build.yml | 2 +- Cargo.lock | 113 +++++++++++++++++++++++++++++++++ Cargo.toml | 4 +- ota/Cargo.toml | 2 +- rust-toolchain.toml | 4 +- src/espressif/buffered_uart.rs | 1 - 6 files changed, 120 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e4afb33..2613369 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -68,4 +68,4 @@ jobs: target: riscv32imac-unknown-none-elf toolchain: stable - name: Build utility - run: cargo build-ota-packer \ No newline at end of file + run: cargo build-ota-packer diff --git a/Cargo.lock b/Cargo.lock index 552960e..80f1827 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,56 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d" +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + [[package]] name = "anyhow" version = "1.0.101" @@ -158,6 +208,39 @@ dependencies = [ "zeroize", ] +[[package]] +name = "clap" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "const-default" version = "1.0.0" @@ -1327,6 +1410,12 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itoa" version = "1.0.17" @@ -1468,12 +1557,29 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "opaque-debug" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "ota" +version = "0.1.0" +dependencies = [ + "clap", + "log", + "sha2", + "sunset", + "sunset-async", +] + [[package]] name = "paste" version = "1.0.15" @@ -1903,6 +2009,7 @@ dependencies = [ "hex", "hmac", "log", + "ota", "paste", "portable-atomic", "pretty-hex", @@ -2173,6 +2280,12 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "vcell" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 8c36b99..92339ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ edition = "2024" license = "MIT OR Apache-2.0" [workspace] -members = ["storage"] +members = ["storage", "ota"] [workspace.dependencies] sunset = { git = "https://github.com/jubeormk1/sunset", branch = "dev/sftp-start", default-features = false, features = [ @@ -42,6 +42,8 @@ once_cell = { version = "1", features = [ [dependencies] storage = { path = "storage" } +ota = { path = "ota" } + sha2 = { workspace = true } rustc-hash = { workspace = true } diff --git a/ota/Cargo.toml b/ota/Cargo.toml index 70d4edd..131e4f7 100644 --- a/ota/Cargo.toml +++ b/ota/Cargo.toml @@ -20,4 +20,4 @@ clap = "4.5" [[bin]] name = "ota-packer" -path = "src/bin/ota-packer.rs" +path = "src/bin/ota-packer.rs" \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5f7ec45..6a9dba9 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later [toolchain] -channel = "nightly" -#channel = "stable" +#channel = "nightly" +channel = "stable" components = ["rust-src"] targets = ["riscv32imac-unknown-none-elf"] diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index 660e662..4cf11a0 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -127,7 +127,6 @@ impl Default for BufferedUart { } } - pub async fn uart_buffer_disable() -> () { // disable uart buffer software_reset(); From d60ed9e832f9cbe937046191057e8b49f43c3f23 Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Sat, 21 Feb 2026 13:35:18 +1100 Subject: [PATCH 83/86] Update github build to fix clippy warning: WARNING: use --release We *strongly* recommend using release profile when building esp-hal. The dev profile can potentially be one or more orders of magnitude slower than release, and may cause issues with timing-senstive peripherals and/or devices. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2613369..f8d8654 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: - name: Check lints and format if: ${{ contains(fromJson('["esp32c6"]'), matrix.device.soc) }} run: | - cargo +${{ matrix.device.toolchain }} clippy --features ${{ matrix.device.soc }} --target riscv32imac-unknown-none-elf -- -D warnings + cargo +${{ matrix.device.toolchain }} clippy --release --features ${{ matrix.device.soc }} --target riscv32imac-unknown-none-elf -- -D warnings cargo +${{ matrix.device.toolchain }} fmt -- --check ota-packer: name: OTA Packer From d9dfdd14a439b8698ac15176ac3f4a36582f19ec Mon Sep 17 00:00:00 2001 From: brainstorm Date: Sat, 21 Feb 2026 13:47:11 +0100 Subject: [PATCH 84/86] Leverage semver to pin HAL versions to only major API changes, CI will warn us if and when something breaks. Helps with not having to bump minor versions continuously, see https://words.filippo.io/dependabot/ for a gist of the idea --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 92339ac..5a9c308 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ sha2 = { version = "0.10", default-features = false } rustc-hash = { version = "2.1", default-features = false } # For hash in the OTA SFTP server TODO: For the optimisation task: Remove this dependency and use sha2 instead? log = "0.4" -esp-hal = { version = "1.0.0", features = ["unstable", "log-04"] } +esp-hal = { version = "1", features = ["unstable", "log-04"] } embedded-storage = "0.3.1" esp-storage = { version = "0.8.0" } esp-bootloader-esp-idf = { version = "0.4.0" } From 8fcdafd5cf9bd09be5fac759f9eb4f3658d73dde Mon Sep 17 00:00:00 2001 From: brainstorm Date: Sat, 21 Feb 2026 13:47:53 +0100 Subject: [PATCH 85/86] Get rid of hardcoded hostkey, fixes issue https://github.com/brainstorm/ssh-stamp/issues/21 --- src/keys.rs | 14 -------------- src/lib.rs | 1 - src/serve.rs | 7 +++---- 3 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 src/keys.rs diff --git a/src/keys.rs b/src/keys.rs deleted file mode 100644 index 6189d1d..0000000 --- a/src/keys.rs +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Roman Valls, 2025 -// -// SPDX-License-Identifier: GPL-3.0-or-later - -// FIXME: For demo purposes, there should be a key handler/generator on first connection. -pub(crate) const HOST_SECRET_KEY: &[u8; 400] = b" ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW -QyNTUxOQAAACD/HyNyMDvZkVWgMRzpbK6VgVk+/b627AamAjoO8T4uSAAAAJCzAcYdswHG -HQAAAAtzc2gtZWQyNTUxOQAAACD/HyNyMDvZkVWgMRzpbK6VgVk+/b627AamAjoO8T4uSA -AAAEAZYxnkyw7+ehro8oDJ2PBAO8OpJrBAezD3PLOw9CdLCP8fI3IwO9mRVaAxHOlsrpWB -WT79vrbsBqYCOg7xPi5IAAAAC2d1c0B0aGVzZXVzAQI= ------END OPENSSH PRIVATE KEY----- -"; diff --git a/src/lib.rs b/src/lib.rs index f4dfe7f..9fed909 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,6 @@ pub mod config; pub mod errors; pub mod espressif; -pub mod keys; pub mod serial; pub mod serve; pub mod settings; diff --git a/src/serve.rs b/src/serve.rs index 3d4ee70..fa75f25 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later use crate::config::SSHStampConfig; -use crate::keys; use crate::settings::UART_BUFFER_SIZE; use crate::store; use core::option::Option::{self, None, Some}; @@ -20,7 +19,7 @@ use sunset_async::SunsetMutex; // use sunset::sshwire::SSHEncode; use crate::espressif::buffered_uart::UART_SIGNAL; use esp_println::{dbg, println}; -use sunset::{ChanHandle, ServEvent, SignKey, error}; +use sunset::{ChanHandle, ServEvent, error}; use sunset_async::{ProgressHolder, SSHServer}; pub enum SessionType { @@ -97,8 +96,8 @@ pub async fn connection_loop( } ServEvent::Hostkeys(h) => { println!("ServEvent::Hostkeys"); - let signkey: SignKey = SignKey::from_openssh(keys::HOST_SECRET_KEY)?; - h.hostkeys(&[&signkey])?; + let config_guard = config.lock().await; + h.hostkeys(&[&config_guard.hostkey])?; } ServEvent::PasswordAuth(a) => { println!("ServEvent::PasswordAuth"); From afb8200934f727396feb81ead218779abceee11b Mon Sep 17 00:00:00 2001 From: Autofix <209348056+Autofix@users.noreply.github.com> Date: Sun, 22 Feb 2026 17:13:57 +1100 Subject: [PATCH 86/86] Cleanup/clarification based on @jubeormk1 feedback for @brainstorm/ssh-stamp PR #61 --- .vscode/settings.json | 5 +---- Cargo.toml | 1 - rust-toolchain.toml | 1 - src/espressif/buffered_uart.rs | 4 ++++ src/espressif/net.rs | 4 ++++ src/serve.rs | 6 ++++++ 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f9a1f42..c4db1c1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,3 @@ { - "rust-analyzer.check.allTargets": false, - // "rust-analyzer.cargo.features": [ - // "esp32c6" - // ] + "rust-analyzer.check.allTargets": false, } diff --git a/Cargo.toml b/Cargo.toml index 92339ac..658b0af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,7 +130,6 @@ opt-level = "s" [features] ipv6 = [] -default = ["esp32c6", "sftp-ota"] # Enables the SFTP OTA Subsystem. Use ota-packer to pack a binary and PUT it over sftp sftp-ota = [] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 6a9dba9..23bc28c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -3,7 +3,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later [toolchain] -#channel = "nightly" channel = "stable" components = ["rust-src"] targets = ["riscv32imac-unknown-none-elf"] diff --git a/src/espressif/buffered_uart.rs b/src/espressif/buffered_uart.rs index 4cf11a0..413c899 100644 --- a/src/espressif/buffered_uart.rs +++ b/src/espressif/buffered_uart.rs @@ -129,12 +129,16 @@ impl Default for BufferedUart { pub async fn uart_buffer_disable() -> () { // disable uart buffer + println!("UART buffer disabled"); + // TODO: Correctly disable/restart UART buffer and/or send messsage to user over SSH software_reset(); } // use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; pub async fn uart_disable() -> () { // disable uart + println!("UART disabled"); + // TODO: Correctly disable/restart UART and/or send messsage to user over SSH software_reset(); } diff --git a/src/espressif/net.rs b/src/espressif/net.rs index f06bd03..aba202f 100644 --- a/src/espressif/net.rs +++ b/src/espressif/net.rs @@ -94,11 +94,15 @@ pub async fn if_up( pub async fn ap_stack_disable() -> () { // drop ap_stack + println!("AP Stack disabled"); + // TODO: Correctly disable/restart AP Stack and/or send messsage to user over SSH software_reset(); } pub async fn tcp_socket_disable() -> () { // drop tcp stack + println!("TCP socket disabled"); + // TODO: Correctly disable/restart tcp socket and/or send messsage to user over SSH software_reset(); } diff --git a/src/serve.rs b/src/serve.rs index 3d4ee70..b06d4d2 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -194,6 +194,8 @@ pub async fn connection_loop( pub async fn connection_disable() -> () { // disable connection loop + println!("Connection loop disabled"); + // TODO: Correctly disable/restart Conection loop and/or send messsage to user over SSH software_reset(); } @@ -206,6 +208,8 @@ pub async fn ssh_wait_for_initialisation<'server>( pub async fn ssh_disable() -> () { // drop ssh server + println!("SSH Server disabled"); + // TODO: Correctly disable/restart SSH Server and/or send messsage to user over SSH software_reset(); } @@ -241,5 +245,7 @@ pub async fn handle_ssh_client<'a, 'b>( pub async fn bridge_disable() -> () { // disable bridge + println!("Bridge disabled"); + // TODO: Correctly disable/restart bridge and/or send messsage to user over SSH software_reset(); }