From aa97da80c09ee28e7a8d43bd26793cb165842db0 Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Tue, 16 Sep 2025 09:26:19 +0200 Subject: [PATCH 1/8] Minimal ll driver --- esp-wifi-hal/.vscode/settings.json | 3 +- esp-wifi-hal/Cargo.lock | 52 +++++++- esp-wifi-hal/Cargo.toml | 2 +- esp-wifi-hal/src/dma_list.rs | 110 --------------- esp-wifi-hal/src/ll.rs | 206 ++++++++++++++++++++++++++++- examples/Cargo.lock | 4 +- 6 files changed, 256 insertions(+), 121 deletions(-) diff --git a/esp-wifi-hal/.vscode/settings.json b/esp-wifi-hal/.vscode/settings.json index e2f5899..0e63e14 100644 --- a/esp-wifi-hal/.vscode/settings.json +++ b/esp-wifi-hal/.vscode/settings.json @@ -2,7 +2,8 @@ "rust-analyzer.check.allTargets": false, "rust-analyzer.cargo.target": "xtensa-esp32-none-elf", "rust-analyzer.cargo.features": [ - "esp32" + "esp32", + "esp-hal/unstable" ], "editor.formatOnSave": true, "rust-analyzer.runnables.cargoExtraArgs": [ diff --git a/esp-wifi-hal/Cargo.lock b/esp-wifi-hal/Cargo.lock index 2585d4c..f77225f 100644 --- a/esp-wifi-hal/Cargo.lock +++ b/esp-wifi-hal/Cargo.lock @@ -8,6 +8,12 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "basic-toml" version = "0.1.10" @@ -229,10 +235,11 @@ dependencies = [ [[package]] name = "embassy-embedded-hal" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fea5ef5bed4d3468dfd44f5c9fa4cda8f54c86d4fb4ae683eacf9d39e2ea12" +checksum = "8c62a3bf127e03832fb97d8b01a058775e617653bc89e2a12c256485a7fb54c1" dependencies = [ + "embassy-embedded-hal 0.4.0", "embassy-futures", "embassy-sync 0.6.2", "embassy-time", @@ -244,12 +251,38 @@ dependencies = [ "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.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-futures" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" +[[package]] +name = "embassy-hal-internal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a" +dependencies = [ + "num-traits", +] + [[package]] name = "embassy-sync" version = "0.6.2" @@ -438,7 +471,7 @@ dependencies = [ "delegate", "digest", "document-features", - "embassy-embedded-hal", + "embassy-embedded-hal 0.3.2", "embassy-futures", "embassy-sync 0.6.2", "embassy-usb-driver", @@ -576,9 +609,9 @@ dependencies = [ [[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", ] @@ -838,6 +871,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.36.7" diff --git a/esp-wifi-hal/Cargo.toml b/esp-wifi-hal/Cargo.toml index 3ba77e6..57dfb37 100644 --- a/esp-wifi-hal/Cargo.toml +++ b/esp-wifi-hal/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://esp32-open-mac.be/" repository = "https://github.com/esp32-open-mac/esp-wifi-hal" [dependencies] -esp-wifi-sys = "=0.7.1" +esp-wifi-sys = "=0.8.1" esp-hal = { version = "=1.0.0-rc.0", features = ["unstable"] } esp32 = { version = "=0.38.0", optional = true, features = ["critical-section"] } esp32s2 = { version = "=0.29.0", optional = true, features = [ diff --git a/esp-wifi-hal/src/dma_list.rs b/esp-wifi-hal/src/dma_list.rs index f49176d..717c896 100644 --- a/esp-wifi-hal/src/dma_list.rs +++ b/esp-wifi-hal/src/dma_list.rs @@ -5,116 +5,6 @@ use embassy_sync::blocking_mutex; use esp_hal::dma::{DmaDescriptor, DmaDescriptorFlags, Owner}; use esp_wifi_sys::include::wifi_pkt_rx_ctrl_t; -// Required to fix compile error for bitfield. -/* -#[bitfield(u32, defmt = cfg(feature = "defmt"))] -pub struct DMAListHeader { - #[bits(12)] - pub buffer_size: u16, - #[bits(12)] - pub buffer_length: u16, - #[bits(6)] - pub __: u8, - pub has_data: bool, - pub dma_owned: bool, -} - -pub struct Rx; -pub struct Tx; - -#[repr(C)] -/// An entry into the [DMAList]. -/// -/// The type parameter allows differentiation between Rx and Tx [DMAListItem]s, to prevent the user from injecting [DMAListItem]s, which aren't statically allocated. -pub struct DMAListItem { - dma_list_header: DMAListHeader, - buffer: *mut u8, - next: *mut DMAListItem, - _phantom: PhantomData, - _phantom_pinned: PhantomPinned, -} -unsafe impl Send for DMAListItem {} -impl DMAListItem { - /// Initialize a new [DMAListItem] for RX. - /// - /// This is handled by the [DMAList]. - /// SAFETY: - /// This assumes, that the buffer is valid for the entire duration, that this DMA descriptor is - /// in use. - unsafe fn init_for_rx(&mut self, buffer: *mut [u8], next: Option>) { - let next = match next { - Some(next) => next.as_ptr(), - None => null_mut(), - }; - self.buffer = buffer as *mut u8; - self.next = next; - self.dma_list_header = DMAListHeader::new() - .with_buffer_size(buffer.len() as u16) - .with_buffer_length(buffer.len() as u16) - .with_has_data(false) - .with_dma_owned(true); - } -} -impl DMAListItem { - /// Initialize a new [DMAListItem] for TX. - /// - /// This is handled by [WiFi](crate::WiFi). - /// SAFETY: - /// You must ensure, that the DMAListItem doens't outlive the buffer. - pub unsafe fn new_for_tx(buffer: *const [u8]) -> DMAListItem { - let mut temp = Self::UNINIT; - temp.buffer = buffer as *const _ as *mut u8 as _; - temp.next = null_mut(); - temp.dma_list_header = DMAListHeader::new() - .with_buffer_size(buffer.len() as u16 + 4) // This is for the FCS. - .with_buffer_length(buffer.len() as u16 + 4) - .with_has_data(true) - .with_dma_owned(true); - temp - } -} -impl DMAListItem { - pub const UNINIT: Self = Self { - dma_list_header: DMAListHeader::new(), - buffer: null_mut(), - next: null_mut(), - _phantom: PhantomData, - _phantom_pinned: PhantomPinned, - }; - /// Returns a byte slice of the buffer, which is [DMAListHeader::buffer_length] long. - pub fn buffer(&self) -> &[u8] { - assert!(!self.buffer.is_null()); - unsafe { - slice::from_raw_parts( - self.buffer as _, - self.dma_list_header.buffer_length() as usize, - ) - } - } - /// Same as [Self::buffer], but mutable. - pub fn buffer_mut(&mut self) -> &mut [u8] { - assert!(!self.buffer.is_null()); - unsafe { - slice::from_raw_parts_mut( - self.buffer as _, - self.dma_list_header.buffer_length() as usize, - ) - } - } - fn next(&mut self) -> Option> { - NonNull::new(self.next) - } - fn set_next(&mut self, next: Option<*mut Self>) { - self.next = match next { - Some(next) => next, - None => null_mut(), - }; - } -} -pub type RxDMAListItem = DMAListItem; -pub type TxDMAListItem = DMAListItem; -*/ - trait DmaDescriptorExt { fn next(&mut self) -> Option<&mut DmaDescriptor>; fn set_next(&mut self, next: Option<&mut DmaDescriptor>); diff --git a/esp-wifi-hal/src/ll.rs b/esp-wifi-hal/src/ll.rs index ab7a4de..a8c4d2b 100644 --- a/esp-wifi-hal/src/ll.rs +++ b/esp-wifi-hal/src/ll.rs @@ -1,9 +1,211 @@ //! Low Level functions. //! //! All functions in this module are unsafe, since their effect may be very context dependent. -use core::ptr::NonNull; +use core::{iter::IntoIterator, ptr::NonNull}; -use esp_hal::{dma::DmaDescriptor, peripherals::WIFI}; +use esp_hal::{ + clock::{ModemClockController, PhyClockGuard}, + dma::DmaDescriptor, + peripherals::{LPWR, WIFI}, +}; +use esp_wifi_sys::include::*; + +use crate::phy_init_data::PHY_INIT_DATA_DEFAULT; + +#[inline] +/// Run a reversible sequence of functions either forward or in reverse. +/// +/// If `forward` is `true`, the functions will be executed in the order in which they were passed. +/// Otherwise they'll be run in reverse order. +fn run_reversible_function_sequence(functions: Iter, parameter: T, forward: bool) +where + T: Copy, + Iter: IntoIterator, + ::IntoIter: DoubleEndedIterator, +{ + let for_each_closure = |function: fn(T)| (function)(parameter); + + let function_iter = functions.into_iter(); + if forward { + function_iter.for_each(for_each_closure); + } else { + function_iter.rev().for_each(for_each_closure); + } +} + +/// Low level driver for the Wi-Fi peripheral. +/// +/// This is intended as an intermediary layer between the user facing API and the hardware, to make +/// driver maintenance easier. +/// +/// # Safety +/// Pretty much all of the functions on this must be used with extreme caution, as the intention is +/// not to provide a fool proof API for the user, but a thin abstraction over the hardware, to make +/// development of the user facing API easier. A lot of the private functions do not take `&self` +/// as a parameter, as they must also be callable during initialization and aren't intended for +/// direct use anyways. Always pay close attention to their respective docs. +pub struct LowLevelDriver<'d> { + /// The Wi-Fi peripheral. + wifi: WIFI<'d>, + /// Prevents the PHY clock from being disabled, while the driver is running. + phy_clock_guard: PhyClockGuard<'d>, +} +impl<'d> LowLevelDriver<'d> { + /// Create a new [LowLevelDriver]. + pub fn new(mut wifi: WIFI<'d>) -> Self { + let phy_clock_guard = unsafe { Self::init(&mut wifi) }; + Self { + wifi, + phy_clock_guard, + } + } + + // Initialization + + /// Set the power and reset status of the Wi-Fi peripheral module. + /// + /// # Safety + /// Enabling or disabling the Wi-Fi PD, outside of init or deinit, breaks a lot of assumptions + /// other code makes, so take great care. + unsafe fn set_module_status(enable_module: bool) { + // For newer chips, we also have to handle the reset of the RF modules. + run_reversible_function_sequence( + [ + // Enable or disable forced power down + |power_down| { + LPWR::regs() + .dig_pwc() + .modify(|_, w| w.wifi_force_pd().bit(power_down)); + }, + // Enable or disable isolation + |isolate| { + LPWR::regs() + .dig_iso() + .modify(|_, w| w.wifi_force_iso().bit(isolate)); + }, + ], + !enable_module, + enable_module, + ); + } + /// Initialize the PHY. + /// + /// NOTE: This is very temporary and will be replaced, as soon as `esp-phy` is released. + unsafe fn initialize_phy() -> PhyClockGuard<'d> { + let clock_guard = unsafe { WIFI::steal() }.enable_phy_clock(); + let mut cal_data = [0u8; size_of::()]; + let init_data = &PHY_INIT_DATA_DEFAULT; + trace!("Enabling PHY."); + unsafe { + register_chipv7_phy( + init_data, + &mut cal_data as *mut _ as *mut esp_phy_calibration_data_t, + esp_phy_calibration_mode_t_PHY_RF_CAL_FULL, + ); + } + clock_guard + } + /// Perform a full MAC reset. + /// + /// # Safety + /// This will basically reset all state of the MAC to its defaults. Therefore it may also break + /// assumptions made by some pieces of code. + unsafe fn reset_mac() { + // Perform a full reset of the Wi-Fi module. + unsafe { WIFI::steal() }.reset_wifi_mac(); + // NOTE: coex_bt_high_prio would usually be here + + // Disable RX + unsafe { + Self::set_rx_status(false); + } + } + /// Initialize or deinitialize the MAC. + /// + /// If `intialized` is `true`, the MAC will be initialized, and vice versa. + /// # Safety + /// We don't really know, what exactly this does, so we only call it, where the closed source + /// driver would. + unsafe fn set_mac_state(intialized: bool) { + // TODO: Figure out, what these registers do precisely and move them into the PAC. + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + const MAC_INIT_MASK: u32 = 0xffffe800; + const MAC_READY_MASK: u32 = 0x2000; + } else if #[cfg(feature = "esp32s2")] { + const MAC_INIT_MASK: u32 = 0xff00efff; + const MAC_READY_MASK: u32 = 0x6000; + } else { + compile_error!("The MAC init mask may have to be updated for different chips."); + } + } + // Spin until the MAC state is marked as ready. + // This is only required on the ESP32-S2. + while !intialized + && cfg!(feature = "esp32s2") + && WIFI::regs().ctrl().read().bits() & MAC_READY_MASK == 0 + {} + // If we are initializing the MAC, we need to clear some bits, by masking the reg, with the + // MAC_INIT_MASK. On deinit, we need to set the bits we previously cleared. + WIFI::regs().ctrl().modify(|r, w| unsafe { + w.bits(if intialized { + r.bits() & MAC_INIT_MASK + } else { + r.bits() | !MAC_INIT_MASK + }) + }); + + // Spin until the MAC is no longer ready. + while !intialized && WIFI::regs().ctrl().read().bits() & MAC_READY_MASK != 0 {} + } + /// Initialize the hardware. + /// + /// As modem sleep is currently unimplemented, this does a full initialization. + /// + /// This includes: + /// - Enabling the Wi-Fi power domain and modem clock + /// - Initializing the PHY + /// - Initializing the MAC + /// - Setting up basic hardware functionality, like the MAC RX filters and crypto. + /// + /// What it doesn't do: + /// - Enabling RX and setting up the DMA list + /// - Setting any addresses + /// - Setting an interrupt handler + /// + /// # Safety + /// At the moment, this is only intended to be called upon initializing the low level driver. + /// Reinitialization and partial PHY calibration are NOT handled at all. The effects of calling + /// this multiple times are unknown. + unsafe fn init(wifi: &mut WIFI<'d>) -> PhyClockGuard<'d> { + // Enable the power domain. + unsafe { + Self::set_module_status(true); + } + // Enable the modem clock. + wifi.enable_modem_clock(true); + // Initialize the PHY. + let clock_guard = unsafe { Self::initialize_phy() }; + unsafe { + Self::reset_mac(); + Self::set_mac_state(true); + } + + clock_guard + } + + // RX + + /// Enable or disable the receiving of frames. + /// + /// # Safety + /// Enabling RX, when the DMA list isn't setup correctly yet, may be incorrect. + unsafe fn set_rx_status(enable_rx: bool) { + WIFI::regs() + .rx_ctrl() + .modify(|_, w| w.rx_enable().bit(enable_rx)); + } +} /// Tell the hardware to reload the RX descriptors. /// diff --git a/examples/Cargo.lock b/examples/Cargo.lock index d84cec0..3bb313e 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -761,9 +761,9 @@ dependencies = [ [[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", ] From 0a3ab1d1df8d0557d3f60c642b37e11dec9e78f6 Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Sat, 4 Oct 2025 23:12:02 +0200 Subject: [PATCH 2/8] Added basic crypto stuff --- esp-wifi-hal/.cargo/config.toml | 3 - esp-wifi-hal/.vscode/settings.json | 7 +- esp-wifi-hal/Cargo.toml | 4 +- esp-wifi-hal/rust-toolchain.toml | 1 - esp-wifi-hal/src/lib.rs | 1 + esp-wifi-hal/src/ll.rs | 267 ++++++++++++++++++++++++++--- esp-wifi-hal/src/wmac.rs | 17 +- 7 files changed, 255 insertions(+), 45 deletions(-) diff --git a/esp-wifi-hal/.cargo/config.toml b/esp-wifi-hal/.cargo/config.toml index d0a8df1..e8d4130 100644 --- a/esp-wifi-hal/.cargo/config.toml +++ b/esp-wifi-hal/.cargo/config.toml @@ -2,9 +2,6 @@ target = [ "xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf", - "xtensa-esp32s3-none-elf", - "riscv32imc-unknown-none-elf", - "riscv32imac-unknown-none-elf" ] [unstable] diff --git a/esp-wifi-hal/.vscode/settings.json b/esp-wifi-hal/.vscode/settings.json index 0e63e14..2557eff 100644 --- a/esp-wifi-hal/.vscode/settings.json +++ b/esp-wifi-hal/.vscode/settings.json @@ -1,10 +1,7 @@ { "rust-analyzer.check.allTargets": false, - "rust-analyzer.cargo.target": "xtensa-esp32-none-elf", - "rust-analyzer.cargo.features": [ - "esp32", - "esp-hal/unstable" - ], + "rust-analyzer.cargo.target": "xtensa-esp32s2-none-elf", + "rust-analyzer.cargo.features": ["esp32s2"], "editor.formatOnSave": true, "rust-analyzer.runnables.cargoExtraArgs": [ "+esp" diff --git a/esp-wifi-hal/Cargo.toml b/esp-wifi-hal/Cargo.toml index 57dfb37..96348f1 100644 --- a/esp-wifi-hal/Cargo.toml +++ b/esp-wifi-hal/Cargo.toml @@ -12,8 +12,8 @@ repository = "https://github.com/esp32-open-mac/esp-wifi-hal" [dependencies] esp-wifi-sys = "=0.8.1" esp-hal = { version = "=1.0.0-rc.0", features = ["unstable"] } -esp32 = { version = "=0.38.0", optional = true, features = ["critical-section"] } -esp32s2 = { version = "=0.29.0", optional = true, features = [ +esp32 = { version = "0.38.0", optional = true, features = ["critical-section"] } +esp32s2 = { version = "0.29.0", optional = true, features = [ "critical-section", ] } diff --git a/esp-wifi-hal/rust-toolchain.toml b/esp-wifi-hal/rust-toolchain.toml index 90aabae..ed87098 100644 --- a/esp-wifi-hal/rust-toolchain.toml +++ b/esp-wifi-hal/rust-toolchain.toml @@ -1,4 +1,3 @@ [toolchain] channel = "esp" components = ["rustfmt", "rustc-dev"] -targets = ["xtensa-esp32-none-elf"] diff --git a/esp-wifi-hal/src/lib.rs b/esp-wifi-hal/src/lib.rs index c68ff1e..2f48aa9 100644 --- a/esp-wifi-hal/src/lib.rs +++ b/esp-wifi-hal/src/lib.rs @@ -74,6 +74,7 @@ use esp32s2 as esp_pac; pub use crypto::*; pub use dma_list::WiFiResources; +pub use ll::{INTERFACE_COUNT, KEY_SLOT_COUNT}; pub use rates::*; pub use wmac::*; diff --git a/esp-wifi-hal/src/ll.rs b/esp-wifi-hal/src/ll.rs index a8c4d2b..c2355ad 100644 --- a/esp-wifi-hal/src/ll.rs +++ b/esp-wifi-hal/src/ll.rs @@ -6,13 +6,12 @@ use core::{iter::IntoIterator, ptr::NonNull}; use esp_hal::{ clock::{ModemClockController, PhyClockGuard}, dma::DmaDescriptor, - peripherals::{LPWR, WIFI}, + peripherals::{LPWR, WIFI as HalWIFI}, }; use esp_wifi_sys::include::*; -use crate::phy_init_data::PHY_INIT_DATA_DEFAULT; +use crate::{esp_pac::WIFI, phy_init_data::PHY_INIT_DATA_DEFAULT}; -#[inline] /// Run a reversible sequence of functions either forward or in reverse. /// /// If `forward` is `true`, the functions will be executed in the order in which they were passed. @@ -33,6 +32,61 @@ where } } +#[inline(always)] +/// Split a MAC address into a low [u32] and high [u16]. +/// +/// Let's hope the compiler optimizes this to two load-store pairs. +fn split_mac(address: &[u8; 6]) -> (u32, u16) { + let (low, high) = address.split_at(4); + ( + u32::from_ne_bytes(low.try_into().unwrap()), + u16::from_ne_bytes(high.try_into().unwrap()), + ) +} + +/// Parameters for a crypto key slot. +/// +/// NOTE: These values can contradict each other, unlike in the user facing API, since this API is +/// intended to only be used with great care. +pub struct KeySlotParameters { + /// Address of the party, with which this key is shared. + pub address: [u8; 6], + /// The key ID. + /// + /// This can be in the range from 0-3. All other values will get truncated. + pub key_id: u8, + /// The interface using this key. + pub interface: u8, + + // A key can be for both pairwise and group traffic, if WEP is in use. + /// Is the key for pairwise traffic. + pub pairwise: bool, + /// Is the key for group traffic. + pub group: bool, + + /// The cryptographic algorithm. + /// + /// The mapping can be seen in the following table: + /// + /// Algorithm | Number + /// -- | -- + /// WEP | 1 + /// TKIP | 2 + /// CCMP | 3 + /// SMS4 | 4 + /// (GCMP) | 5 + pub algorithm: u8, + /// If the algorithm is WEP, is the key 104 bits long. + pub wep_104: bool, +} + +#[cfg(any(feature = "esp32", feature = "esp32s2"))] +/// The number of "interfaces" supported by the hardware. +pub const INTERFACE_COUNT: usize = 4; + +/// The number of key slots the hardware has. +pub const KEY_SLOT_COUNT: usize = 25; + /// Low level driver for the Wi-Fi peripheral. /// /// This is intended as an intermediary layer between the user facing API and the hardware, to make @@ -44,15 +98,26 @@ where /// development of the user facing API easier. A lot of the private functions do not take `&self` /// as a parameter, as they must also be callable during initialization and aren't intended for /// direct use anyways. Always pay close attention to their respective docs. +/// +/// # Panics +/// A lot of the functions, that can panic, will do so under similar conditions, so the docs are +/// shared. When an index (i.e. key slot or interface) is provided, it is expected, that the input +/// is within the valid range. If it is not, the function will panic. pub struct LowLevelDriver<'d> { /// The Wi-Fi peripheral. - wifi: WIFI<'d>, + wifi: HalWIFI<'d>, /// Prevents the PHY clock from being disabled, while the driver is running. phy_clock_guard: PhyClockGuard<'d>, } impl<'d> LowLevelDriver<'d> { + /// Get the PAC Wi-Fi peripheral. + /// + /// This stops RA from doing funky shit. + fn regs() -> WIFI { + unsafe { WIFI::steal() } + } /// Create a new [LowLevelDriver]. - pub fn new(mut wifi: WIFI<'d>) -> Self { + pub fn new(mut wifi: esp_hal::peripherals::WIFI<'d>) -> Self { let phy_clock_guard = unsafe { Self::init(&mut wifi) }; Self { wifi, @@ -92,7 +157,7 @@ impl<'d> LowLevelDriver<'d> { /// /// NOTE: This is very temporary and will be replaced, as soon as `esp-phy` is released. unsafe fn initialize_phy() -> PhyClockGuard<'d> { - let clock_guard = unsafe { WIFI::steal() }.enable_phy_clock(); + let clock_guard = unsafe { HalWIFI::steal() }.enable_phy_clock(); let mut cal_data = [0u8; size_of::()]; let init_data = &PHY_INIT_DATA_DEFAULT; trace!("Enabling PHY."); @@ -112,7 +177,7 @@ impl<'d> LowLevelDriver<'d> { /// assumptions made by some pieces of code. unsafe fn reset_mac() { // Perform a full reset of the Wi-Fi module. - unsafe { WIFI::steal() }.reset_wifi_mac(); + unsafe { HalWIFI::steal() }.reset_wifi_mac(); // NOTE: coex_bt_high_prio would usually be here // Disable RX @@ -143,11 +208,11 @@ impl<'d> LowLevelDriver<'d> { // This is only required on the ESP32-S2. while !intialized && cfg!(feature = "esp32s2") - && WIFI::regs().ctrl().read().bits() & MAC_READY_MASK == 0 + && Self::regs().ctrl().read().bits() & MAC_READY_MASK == 0 {} // If we are initializing the MAC, we need to clear some bits, by masking the reg, with the // MAC_INIT_MASK. On deinit, we need to set the bits we previously cleared. - WIFI::regs().ctrl().modify(|r, w| unsafe { + Self::regs().ctrl().modify(|r, w| unsafe { w.bits(if intialized { r.bits() & MAC_INIT_MASK } else { @@ -156,7 +221,26 @@ impl<'d> LowLevelDriver<'d> { }); // Spin until the MAC is no longer ready. - while !intialized && WIFI::regs().ctrl().read().bits() & MAC_READY_MASK != 0 {} + while !intialized && Self::regs().ctrl().read().bits() & MAC_READY_MASK != 0 {} + } + #[inline] + /// Setup relevant blocks of the MAC. + /// + /// # Safety + /// This should only be called during init. + unsafe fn setup_mac() { + unsafe extern "C" { + fn hal_init(); + } + // We call the proprietary blob here, to do some init for us. In the long term, this will + // be moved to open source code. In the meantime, we do the init, that we already understand a + // second time in open source code + unsafe { + hal_init(); + } + + // Open source setup code + Self::crypto_init(); } /// Initialize the hardware. /// @@ -177,7 +261,7 @@ impl<'d> LowLevelDriver<'d> { /// At the moment, this is only intended to be called upon initializing the low level driver. /// Reinitialization and partial PHY calibration are NOT handled at all. The effects of calling /// this multiple times are unknown. - unsafe fn init(wifi: &mut WIFI<'d>) -> PhyClockGuard<'d> { + unsafe fn init(wifi: &mut HalWIFI<'d>) -> PhyClockGuard<'d> { // Enable the power domain. unsafe { Self::set_module_status(true); @@ -196,22 +280,161 @@ impl<'d> LowLevelDriver<'d> { // RX + #[inline] /// Enable or disable the receiving of frames. /// /// # Safety /// Enabling RX, when the DMA list isn't setup correctly yet, may be incorrect. unsafe fn set_rx_status(enable_rx: bool) { - WIFI::regs() + Self::regs() .rx_ctrl() .modify(|_, w| w.rx_enable().bit(enable_rx)); } + + #[inline] + /// Check if RX is enabled. + /// + /// We don't know, if this influences the RF frontend in any way. + pub fn rx_enabled(&self) -> bool { + Self::regs().rx_ctrl().read().rx_enable().bit() + } + + // Crypto + + #[inline] + /// Enable hardware cryptography for the specified interface. + /// + /// NOTE: We don't yet know, how to revert this, but it's assumed, that the two bits could each + /// stand for TX and RX. + /// + /// # Panics + /// If `interface` is greater, than [INTERFACE_COUNT], this will panic. + fn enable_crypto_for_interface(interface: usize) { + // Writing 0x3_000 in the crypto control register of an interface seem to be used to enable + // crypto for that interface. + Self::regs() + .crypto_control() + .interface_crypto_control(interface) + .write(|w| unsafe { w.bits(0x0003_0000) }); + } + #[inline] + /// Initialize hardware cryptography. + /// + /// This is pretty much the same as `hal_crypto_init`, except we init crypto for all + /// interfaces, not just zero and one. + fn crypto_init() { + // Enable crypto for all interfaces. + (0..INTERFACE_COUNT).for_each(Self::enable_crypto_for_interface); + + // Resetting the general crypto control register effectively marks all slots as unused. + Self::regs() + .crypto_control() + .general_crypto_control() + .reset(); + } + #[inline] + /// Enable or disable a key slot. + /// + /// As soon, as the key slot is enabled, the hardware will treat the data in it as valid. + pub fn set_key_slot_status(&self, key_slot: usize, enabled: bool) { + Self::regs() + .crypto_control() + .crypto_key_slot_state() + .modify(|_, w| w.key_slot_enable(key_slot as u8).bit(enabled)); + } + #[inline] + /// Set the MAC address for the specified key slot. + /// + /// This is always the address of the party, with which this key is shared. + pub fn set_key_slot_address(&self, key_slot: usize, address: &[u8; 6]) { + let wifi = Self::regs(); + let key_slot = wifi.crypto_key_slot(key_slot); + let (low, high) = split_mac(address); + + key_slot.addr_low().write(|w| unsafe { w.bits(low) }); + key_slot + .addr_high() + .modify(|_, w| unsafe { w.addr().bits(high) }); + } + #[inline] + /// Set the key ID for the specified key slot. + /// + /// Since only key IDs in the range from 0-3 are valid, all other values will be be truncated. + pub fn set_key_id(&self, key_slot: usize, key_id: u8) { + Self::regs() + .crypto_key_slot(key_slot) + .addr_high() + .modify(|_, w| unsafe { w.key_id().bits(key_id) }); + } + /// Set the cryptographic key for the specified key slot. + /// + /// # Panics + /// If `key` is longer than 32 bytes, or `key_slot` larger than 24. + pub fn set_key(&self, key_slot: usize, key: &[u8]) { + assert!(key.len() <= 32, "Keys can't be longer than 32 bytes."); + let wifi = Self::regs(); + let key_slot = wifi.crypto_key_slot(key_slot); + + // Let's hope the compiler is smart enough to emit no panics this way. + let (full_key_words, remaining_key_bytes) = key.as_chunks::<4>(); + + // We first write the full key words... + for (word, key_value) in full_key_words + .iter() + .copied() + .zip(key_slot.key_value_iter()) + { + key_value.write(|w| unsafe { w.bits(u32::from_ne_bytes(word)) }); + } + // and then the remaining bytes. + if !remaining_key_bytes.is_empty() { + assert!(remaining_key_bytes.len() < 4); + + let mut temp = [0u8; 4]; + temp[..remaining_key_bytes.len()].copy_from_slice(remaining_key_bytes); + key_slot + .key_value(full_key_words.len()) + .write(|w| unsafe { w.bits(u32::from_ne_bytes(temp)) }); + } + } + /// Set the key slot parameters. + /// + /// This will also set the 22nd bit, of the [addr_high register](crate::esp_pac::wifi::crypto_key_slot::addr_high), + /// the purpose of which we don't really know. In the PACs, it can be found at + /// [unknown](crate::esp_pac::wifi::crypto_key_slot::addr_high::W::unknown). + pub fn set_key_slot_parameters(&self, key_slot: usize, key_slot_parameters: &KeySlotParameters) { + let wifi = Self::regs(); + let key_slot = wifi.crypto_key_slot(key_slot); + let (address_low, address_high) = split_mac(&key_slot_parameters.address); + + key_slot.addr_low().write(|w| unsafe { w.bits(address_low) }); + key_slot + .addr_high() + .modify(|_, w| unsafe { + w.key_id() + .bits(key_slot_parameters.key_id) + .pairwise_key() + .bit(key_slot_parameters.pairwise) + .group_key() + .bit(key_slot_parameters.group) + .wep_104() + .bit(key_slot_parameters.wep_104) + .algorithm() + .bits(key_slot_parameters.algorithm) + .interface_id() + .bits(key_slot_parameters.interface) + .unknown() + .set_bit() + .addr().bits(address_high) + }); + } } /// Tell the hardware to reload the RX descriptors. /// /// This will spin, until the bit is clear again. pub unsafe fn reload_hw_rx_descriptors() { - let reg = WIFI::regs().rx_ctrl(); + let reg = HalWIFI::regs().rx_ctrl(); // Start the reload. reg.modify(|_, w| w.rx_descr_reload().set_bit()); @@ -223,7 +446,7 @@ pub unsafe fn reload_hw_rx_descriptors() { /// /// If `base_descriptor` is [None], this will reset the register back to zero. pub unsafe fn set_base_rx_descriptor(base_descriptor: Option>) { - let reg = WIFI::regs().rx_dma_list().rx_descr_base(); + let reg = HalWIFI::regs().rx_dma_list().rx_descr_base(); if let Some(ptr) = base_descriptor { reg.write(|w| w.bits(ptr.as_ptr() as u32)); } else { @@ -237,7 +460,7 @@ pub unsafe fn set_base_rx_descriptor(base_descriptor: Option *mut DmaDescriptor { - WIFI::regs().rx_dma_list().rx_descr_next().read().bits() as *mut DmaDescriptor + HalWIFI::regs().rx_dma_list().rx_descr_next().read().bits() as *mut DmaDescriptor } /// Get the raw value of the last RX descriptor register. /// @@ -245,7 +468,7 @@ pub unsafe fn next_rx_descriptor_raw() -> *mut DmaDescriptor { /// This could be unsafe, since we don't know what happens if we read the register, while the /// peripheral is powered down. pub unsafe fn last_rx_descriptor_raw() -> *mut DmaDescriptor { - WIFI::regs().rx_dma_list().rx_descr_last().read().bits() as *mut DmaDescriptor + HalWIFI::regs().rx_dma_list().rx_descr_last().read().bits() as *mut DmaDescriptor } /// Get the last RX descriptor. @@ -258,7 +481,7 @@ pub unsafe fn last_rx_descriptor() -> Option> { } /// Enable or disable RX. pub unsafe fn set_rx_enabled(enabled: bool) { - WIFI::regs() + HalWIFI::regs() .rx_ctrl() .modify(|_, w| w.rx_enable().bit(enabled)); } @@ -287,14 +510,14 @@ pub fn hw_slot_index(slot: usize) -> usize { } /// Enable or disable the specified TX slot. pub unsafe fn set_tx_slot_enabled(slot: usize, enabled: bool) { - WIFI::regs() + HalWIFI::regs() .tx_slot_config(hw_slot_index(slot)) .plcp0() .modify(|_, w| w.slot_enabled().bit(enabled)); } /// Validate or invalidate the specified TX slot. pub unsafe fn set_tx_slot_validity(slot: usize, valid: bool) { - WIFI::regs() + HalWIFI::regs() .tx_slot_config(hw_slot_index(slot)) .plcp0() .modify(|_, w| w.slot_valid().bit(valid)); @@ -302,7 +525,7 @@ pub unsafe fn set_tx_slot_validity(slot: usize, valid: bool) { #[inline(always)] pub unsafe fn process_tx_completions(mut cb: impl FnMut(usize)) { - let wifi = WIFI::regs(); + let wifi = HalWIFI::regs(); wifi.txq_state() .tx_complete_status() .read() @@ -318,7 +541,7 @@ pub unsafe fn process_tx_completions(mut cb: impl FnMut(usize)) { } #[inline(always)] pub unsafe fn process_tx_timeouts(mut cb: impl FnMut(usize)) { - let wifi = WIFI::regs(); + let wifi = HalWIFI::regs(); wifi.txq_state() .tx_error_status() .read() @@ -334,7 +557,7 @@ pub unsafe fn process_tx_timeouts(mut cb: impl FnMut(usize)) { } #[inline(always)] pub unsafe fn process_tx_collisions(mut cb: impl FnMut(usize)) { - let wifi = WIFI::regs(); + let wifi = HalWIFI::regs(); wifi.txq_state() .tx_error_status() .read() diff --git a/esp-wifi-hal/src/wmac.rs b/esp-wifi-hal/src/wmac.rs index 56a9556..406248e 100644 --- a/esp-wifi-hal/src/wmac.rs +++ b/esp-wifi-hal/src/wmac.rs @@ -11,7 +11,7 @@ use crate::{ ll, rates::RATE_LUT, sync::{BorrowedTxSlot, TxSlotQueue, TxSlotStatus}, - CipherParameters, WiFiRate, + CipherParameters, WiFiRate, INTERFACE_COUNT, KEY_SLOT_COUNT, }; use embassy_sync::blocking_mutex::{self}; use embassy_time::Instant; @@ -240,7 +240,7 @@ impl BorrowedBuffer<'_> { /// Get an iterator over the interfaces, to which this frame is addressed. pub fn interface_iterator(&self) -> impl Iterator + '_ { let byte = self.header_buffer()[3]; - (0..WiFi::INTERFACE_COUNT).filter(move |interface| check_bit!(byte, bit!(interface + 4))) + (0..INTERFACE_COUNT).filter(move |interface| check_bit!(byte, bit!(interface + 4))) } /// Unknown RX state. /// @@ -360,13 +360,6 @@ pub struct WiFi<'res> { _phy_clock_guard: PhyClockGuard<'static>, } impl<'res> WiFi<'res> { - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - /// The number of "interfaces" supported by the hardware. - pub const INTERFACE_COUNT: usize = 4; - - /// The number of key slots the hardware has. - pub const KEY_SLOT_COUNT: usize = 25; - /// Enable the Wi-Fi power domain. fn enable_wifi_power_domain() { unsafe { @@ -818,7 +811,7 @@ impl<'res> WiFi<'res> { } /// Check if that interface is valid. pub const fn validate_interface(interface: usize) -> WiFiResult<()> { - if interface < Self::INTERFACE_COUNT { + if interface < INTERFACE_COUNT { Ok(()) } else { Err(WiFiError::InterfaceOutOfBounds) @@ -918,7 +911,7 @@ impl<'res> WiFi<'res> { } /// Check if they key slot is valid. pub const fn validate_key_slot(key_slot: usize) -> WiFiResult<()> { - if key_slot < Self::KEY_SLOT_COUNT { + if key_slot < KEY_SLOT_COUNT { Ok(()) } else { Err(WiFiError::KeySlotOutOfBounds) @@ -949,7 +942,7 @@ impl<'res> WiFi<'res> { .crypto_key_slot_state() .read() .bits(); - (0..Self::KEY_SLOT_COUNT).map(move |i| check_bit!(key_slot_state, bit!(i))) + (0..KEY_SLOT_COUNT).map(move |i| check_bit!(key_slot_state, bit!(i))) } /// Set a cryptographic key. /// From 69d29aff21448025c03e9b7567d499a9d887c87b Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Sun, 5 Oct 2025 18:08:11 +0200 Subject: [PATCH 3/8] completed key slot handling --- esp-wifi-hal/src/ll.rs | 89 ++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/esp-wifi-hal/src/ll.rs b/esp-wifi-hal/src/ll.rs index c2355ad..7d3eb26 100644 --- a/esp-wifi-hal/src/ll.rs +++ b/esp-wifi-hal/src/ll.rs @@ -336,36 +336,12 @@ impl<'d> LowLevelDriver<'d> { /// Enable or disable a key slot. /// /// As soon, as the key slot is enabled, the hardware will treat the data in it as valid. - pub fn set_key_slot_status(&self, key_slot: usize, enabled: bool) { + pub fn set_key_slot_enable(&self, key_slot: usize, enabled: bool) { Self::regs() .crypto_control() .crypto_key_slot_state() .modify(|_, w| w.key_slot_enable(key_slot as u8).bit(enabled)); } - #[inline] - /// Set the MAC address for the specified key slot. - /// - /// This is always the address of the party, with which this key is shared. - pub fn set_key_slot_address(&self, key_slot: usize, address: &[u8; 6]) { - let wifi = Self::regs(); - let key_slot = wifi.crypto_key_slot(key_slot); - let (low, high) = split_mac(address); - - key_slot.addr_low().write(|w| unsafe { w.bits(low) }); - key_slot - .addr_high() - .modify(|_, w| unsafe { w.addr().bits(high) }); - } - #[inline] - /// Set the key ID for the specified key slot. - /// - /// Since only key IDs in the range from 0-3 are valid, all other values will be be truncated. - pub fn set_key_id(&self, key_slot: usize, key_id: u8) { - Self::regs() - .crypto_key_slot(key_slot) - .addr_high() - .modify(|_, w| unsafe { w.key_id().bits(key_id) }); - } /// Set the cryptographic key for the specified key slot. /// /// # Panics @@ -402,31 +378,52 @@ impl<'d> LowLevelDriver<'d> { /// This will also set the 22nd bit, of the [addr_high register](crate::esp_pac::wifi::crypto_key_slot::addr_high), /// the purpose of which we don't really know. In the PACs, it can be found at /// [unknown](crate::esp_pac::wifi::crypto_key_slot::addr_high::W::unknown). - pub fn set_key_slot_parameters(&self, key_slot: usize, key_slot_parameters: &KeySlotParameters) { + /// + /// The rationale behind having a single function setting all the parameters, is that it's unlikely + /// one of these will change, while the key slot is in use. It is however possible, that the key + /// gets changed, so that's in a separate function. + pub fn set_key_slot_parameters( + &self, + key_slot: usize, + key_slot_parameters: &KeySlotParameters, + ) { let wifi = Self::regs(); let key_slot = wifi.crypto_key_slot(key_slot); - let (address_low, address_high) = split_mac(&key_slot_parameters.address); + let (address_low, address_high) = split_address(&key_slot_parameters.address); - key_slot.addr_low().write(|w| unsafe { w.bits(address_low) }); key_slot - .addr_high() - .modify(|_, w| unsafe { - w.key_id() - .bits(key_slot_parameters.key_id) - .pairwise_key() - .bit(key_slot_parameters.pairwise) - .group_key() - .bit(key_slot_parameters.group) - .wep_104() - .bit(key_slot_parameters.wep_104) - .algorithm() - .bits(key_slot_parameters.algorithm) - .interface_id() - .bits(key_slot_parameters.interface) - .unknown() - .set_bit() - .addr().bits(address_high) - }); + .addr_low() + .write(|w| unsafe { w.bits(address_low) }); + key_slot.addr_high().modify(|_, w| unsafe { + w.key_id() + .bits(key_slot_parameters.key_id) + .pairwise_key() + .bit(key_slot_parameters.pairwise) + .group_key() + .bit(key_slot_parameters.group) + .wep_104() + .bit(key_slot_parameters.wep_104) + .algorithm() + .bits(key_slot_parameters.algorithm) + .interface_id() + .bits(key_slot_parameters.interface) + .unknown() + .set_bit() + .addr() + .bits(address_high) + }); + } + /// Clear all data from a key slot. + /// + /// This will not disable the key slot, use [Self::set_key_slot_enable] for that. + pub fn clear_key_slot(&self, key_slot: usize) { + let wifi = Self::regs(); + let key_slot = wifi.crypto_key_slot(key_slot); + + // Effectively zeroes the entire slot. + key_slot.addr_low().reset(); + key_slot.addr_high().reset(); + key_slot.key_value_iter().for_each(KEY_VALUE::reset); } } From b4f460ce085f5a5c251e25fe2b73bf6aa81f8360 Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Sun, 5 Oct 2025 18:08:21 +0200 Subject: [PATCH 4/8] Adjusted RX filters --- esp-wifi-hal/src/ll.rs | 89 +++++++++++++++++++++++++++++++++++++++- esp-wifi-hal/src/wmac.rs | 19 +-------- 2 files changed, 88 insertions(+), 20 deletions(-) diff --git a/esp-wifi-hal/src/ll.rs b/esp-wifi-hal/src/ll.rs index 7d3eb26..7088ca3 100644 --- a/esp-wifi-hal/src/ll.rs +++ b/esp-wifi-hal/src/ll.rs @@ -10,7 +10,7 @@ use esp_hal::{ }; use esp_wifi_sys::include::*; -use crate::{esp_pac::WIFI, phy_init_data::PHY_INIT_DATA_DEFAULT}; +use crate::{esp_pac::{WIFI, wifi::crypto_key_slot::KEY_VALUE}, phy_init_data::PHY_INIT_DATA_DEFAULT}; /// Run a reversible sequence of functions either forward or in reverse. /// @@ -36,7 +36,7 @@ where /// Split a MAC address into a low [u32] and high [u16]. /// /// Let's hope the compiler optimizes this to two load-store pairs. -fn split_mac(address: &[u8; 6]) -> (u32, u16) { +fn split_address(address: &[u8; 6]) -> (u32, u16) { let (low, high) = address.split_at(4); ( u32::from_ne_bytes(low.try_into().unwrap()), @@ -79,6 +79,23 @@ pub struct KeySlotParameters { /// If the algorithm is WEP, is the key 104 bits long. pub wep_104: bool, } +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +/// The bank of the RX filter. +pub enum RxFilterBank { + /// Basic Service Set Identifier (BSSID) + BSSID, + /// Receiver Address + ReceiverAddress, +} +impl RxFilterBank { + pub(crate) fn into_bits(self) -> usize { + match self { + Self::BSSID => 0, + Self::ReceiverAddress => 1, + } + } +} #[cfg(any(feature = "esp32", feature = "esp32s2"))] /// The number of "interfaces" supported by the hardware. @@ -298,6 +315,74 @@ impl<'d> LowLevelDriver<'d> { pub fn rx_enabled(&self) -> bool { Self::regs().rx_ctrl().read().rx_enable().bit() } + /// Enable or disable the specified filter. + pub fn set_filter_enable(&self, interface: usize, filter_bank: RxFilterBank, enabled: bool) { + Self::regs() + .filter_bank(filter_bank.into_bits()) + .mask_high(interface) + .modify(|_, w| w.enabled().bit(enabled)); + } + pub fn filter_enabled(&self, interface: usize, filter_bank: RxFilterBank) -> bool { + Self::regs() + .filter_bank(filter_bank.into_bits()) + .mask_high(interface) + .read() + .enabled() + .bit() + } + /// Set the filter address for the specified interface and bank combination. + /// + /// This will neither enable the filter nor configure the mask. + pub fn set_filter_address( + &self, + interface: usize, + filter_bank: RxFilterBank, + address: &[u8; 6], + ) { + let wifi = Self::regs(); + let filter_bank = wifi.filter_bank(filter_bank.into_bits()); + + let (address_low, address_high) = split_address(address); + filter_bank + .addr_low(interface) + .write(|w| unsafe { w.bits(address_low) }); + filter_bank + .addr_high(interface) + .write(|w| unsafe { w.addr().bits(address_high) }); + } + /// Set the filter mask for the specified interface and bank combination. + /// + /// This will neither enable the filter nor configure the address. + pub fn set_filter_mask(&self, interface: usize, filter_bank: RxFilterBank, mask: &[u8; 6]) { + let wifi = Self::regs(); + let filter_bank = wifi.filter_bank(filter_bank.into_bits()); + + let (mask_low, mask_high) = split_address(mask); + filter_bank + .mask_low(interface) + .write(|w| unsafe { w.bits(mask_low) }); + // We need to modfiy here, since otherwise we would be clearing the enable bit, which is + // not part of this functions job. + filter_bank + .mask_high(interface) + .modify(|_, w| unsafe { w.mask().bits(mask_high) }); + } + /// Clear a filter bank. + /// + /// This zeroes out the address and mask, while also disabling the filter as a side effect. + /// Although the inverse is not true, it makes sense to do this, since that way we can just + /// reset the [mask_high](crate::esp_pac::wifi::filter_bank::FILTER_BANK::mask_high) field, + /// saving us one read. This also prevents keeping the slot enabled, while zeroing it, which + /// would be invalid regardless. + pub fn clear_filter_bank(&self, interface: usize, filter_bank: RxFilterBank) { + let wifi = Self::regs(); + let filter_bank = wifi.filter_bank(filter_bank.into_bits()); + + filter_bank.addr_low(interface).reset(); + filter_bank.addr_high(interface).reset(); + filter_bank.mask_low(interface).reset(); + filter_bank.mask_high(interface).reset(); + } // Crypto diff --git a/esp-wifi-hal/src/wmac.rs b/esp-wifi-hal/src/wmac.rs index 406248e..37d5b45 100644 --- a/esp-wifi-hal/src/wmac.rs +++ b/esp-wifi-hal/src/wmac.rs @@ -8,7 +8,7 @@ use portable_atomic::{AtomicU16, AtomicU64, AtomicU8, Ordering}; use crate::{ esp_pac::wifi::TX_SLOT_CONFIG, - ll, + ll::{self, RxFilterBank}, rates::RATE_LUT, sync::{BorrowedTxSlot, TxSlotQueue, TxSlotStatus}, CipherParameters, WiFiRate, INTERFACE_COUNT, KEY_SLOT_COUNT, @@ -259,23 +259,6 @@ impl Drop for BorrowedBuffer<'_> { } #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -/// The bank of the RX filter. -pub enum RxFilterBank { - /// Basic Service Set Identifier (BSSID) - BSSID, - /// Receiver Address - ReceiverAddress, -} -impl RxFilterBank { - const fn into_bits(self) -> usize { - match self { - Self::BSSID => 0, - Self::ReceiverAddress => 1, - } - } -} -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] /// Errors returned by the Wi-Fi driver. pub enum WiFiError { /// The provided channel was invalid. From 34c29608f62dbc225d4f0676e9094deef25676fb Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Sun, 12 Oct 2025 12:19:49 +0200 Subject: [PATCH 5/8] Transition RX and crypto fully to LL driver --- esp-wifi-hal/build.rs | 4 +- esp-wifi-hal/src/crypto.rs | 2 +- esp-wifi-hal/src/dma_list.rs | 99 +++++---- esp-wifi-hal/src/lib.rs | 3 +- esp-wifi-hal/src/ll.rs | 396 ++++++++++++++++++++++++++--------- esp-wifi-hal/src/sync.rs | 2 +- esp-wifi-hal/src/wmac.rs | 382 +++++++-------------------------- 7 files changed, 441 insertions(+), 447 deletions(-) diff --git a/esp-wifi-hal/build.rs b/esp-wifi-hal/build.rs index f41781a..4095049 100644 --- a/esp-wifi-hal/build.rs +++ b/esp-wifi-hal/build.rs @@ -5,8 +5,8 @@ const PWR_INTERRUPT_PRESENT: &str = "pwr_interrupt_present"; const OSI_FUNCS_REQUIRED: &str = "osi_funcs_required"; const NOMAC_CHANNEL_SET: &str = "nomac_channel_set"; -const ESP32_META: &[&str] = &[NOMAC_CHANNEL_SET]; -const ESP32S2_META: &[&str] = &[PWR_INTERRUPT_PRESENT, OSI_FUNCS_REQUIRED]; +const ESP32_META: &[&str] = &["esp32", NOMAC_CHANNEL_SET]; +const ESP32S2_META: &[&str] = &["esp32s2", PWR_INTERRUPT_PRESENT, OSI_FUNCS_REQUIRED]; fn main() { let meta = if cfg!(feature = "esp32") { diff --git a/esp-wifi-hal/src/crypto.rs b/esp-wifi-hal/src/crypto.rs index d0244b5..0c351ce 100644 --- a/esp-wifi-hal/src/crypto.rs +++ b/esp-wifi-hal/src/crypto.rs @@ -90,7 +90,7 @@ pub enum CipherParameters<'a> { } impl CipherParameters<'_> { /// Get the [AesCipherParameters] if any. - pub const fn aes_cipher_parameters(&self) -> Option<&AesCipherParameters> { + pub const fn aes_cipher_parameters(&self) -> Option<&AesCipherParameters<'_>> { match self { CipherParameters::Ccmp(parameters) => Some(parameters), // CipherParameters::Gcmp(parameters) => Some(parameters), diff --git a/esp-wifi-hal/src/dma_list.rs b/esp-wifi-hal/src/dma_list.rs index 717c896..aaae974 100644 --- a/esp-wifi-hal/src/dma_list.rs +++ b/esp-wifi-hal/src/dma_list.rs @@ -1,5 +1,8 @@ -use crate::{ll, DefaultRawMutex}; -use core::{cell::RefCell, marker::PhantomData, mem::MaybeUninit, ptr::NonNull}; +use crate::{ + ll::LowLevelDriver, + DefaultRawMutex, +}; +use core::{cell::RefCell, mem::MaybeUninit, ptr::NonNull}; use embassy_sync::blocking_mutex; use esp_hal::dma::{DmaDescriptor, DmaDescriptorFlags, Owner}; @@ -28,7 +31,8 @@ const RX_BUFFER_SIZE: usize = 1600; pub struct WiFiResources { buffers: [[u8; RX_BUFFER_SIZE]; BUFFER_COUNT], dma_descriptors: [MaybeUninit; BUFFER_COUNT], - dma_list: MaybeUninit>>>, + dma_list: MaybeUninit>>, + ll_driver: MaybeUninit, } impl WiFiResources { /// Create new DMA resources. @@ -38,6 +42,7 @@ impl WiFiResources { buffers: [[0u8; RX_BUFFER_SIZE]; BUFFER_COUNT], dma_descriptors: [MaybeUninit::uninit(); BUFFER_COUNT], dma_list: MaybeUninit::uninit(), + ll_driver: MaybeUninit::uninit(), } } /// Initialize the DMA resources. @@ -46,7 +51,11 @@ impl WiFiResources { /// You must ensure, that this is only used as long as the DMA list lives! pub(crate) unsafe fn init( &mut self, - ) -> &blocking_mutex::Mutex>> { + ll_driver: LowLevelDriver, + ) -> ( + &blocking_mutex::Mutex>, + &LowLevelDriver, + ) { // We already asserted this in `Self::new`, but maybe this helps LLVM. assert!(self.dma_descriptors.len() >= 2); @@ -79,10 +88,18 @@ impl WiFiResources { let last_ptr = NonNull::from(init_iter.next().unwrap()); let base_ptr = NonNull::from(init_iter.last().unwrap()); - self.dma_list + let ll_driver = self.ll_driver.write(ll_driver); + let dma_list = self + .dma_list .write(blocking_mutex::Mutex::new(RefCell::new(DMAList::new( - base_ptr, last_ptr, - )))) + base_ptr, + last_ptr, + unsafe { + core::mem::transmute::<&LowLevelDriver, &'static LowLevelDriver>(ll_driver) + }, + )))); + + (dma_list, ll_driver) } } impl Default for WiFiResources { @@ -91,45 +108,48 @@ impl Default for WiFiResources { } } -pub struct DMAList<'res> { +pub struct DMAList { rx_chain_ptrs: Option<(NonNull, NonNull)>, - _phantom: PhantomData<&'res ()>, + ll_driver: &'static LowLevelDriver, } -impl<'res> DMAList<'res> { +impl DMAList { /// Instantiate a new DMAList. - fn new(base_ptr: NonNull, last_ptr: NonNull) -> Self { - unsafe { ll::init_rx(base_ptr) }; + fn new( + base_ptr: NonNull, + last_ptr: NonNull, + ll_driver: &'static LowLevelDriver, + ) -> Self { + ll_driver.set_base_rx_descriptor(base_ptr); trace!("Initialized DMA list."); - Self::log_stats(); Self { rx_chain_ptrs: Some((base_ptr, last_ptr)), - _phantom: PhantomData, + ll_driver, } } /// Sets [Self::rx_chain_ptrs], with the `dma_list_descriptor` at the base. /// /// This will automatically reload the RX descriptors. fn set_rx_chain_base(&mut self, base_descriptor: Option>) { - match (&mut self.rx_chain_ptrs, base_descriptor) { - // If neither the DMA list nor the DMA list descriptor is empty, we simply set rx_chain_begin to dma_list_desciptor. - (Some(rx_chain_ptrs), Some(dma_list_descriptor)) => { - rx_chain_ptrs.0 = dma_list_descriptor; + if let Some(base_descriptor) = base_descriptor { + if let Some(mut rx_chain_ptrs) = self.rx_chain_ptrs { + // If neither the DMA list nor the DMA list descriptor is empty, we simply set rx_chain_begin to dma_list_desciptor. + rx_chain_ptrs.0 = base_descriptor; + } else { + // The DMA list is currently empty. Therefore the dma_list_descriptor is now first and last. + self.rx_chain_ptrs = Some((base_descriptor, base_descriptor)); } + + self.ll_driver.set_base_rx_descriptor(base_descriptor); + } else { // If the DMA list isn't empty, but we want to set it to empty. - (Some(_), None) => self.rx_chain_ptrs = None, - // The DMA list is currently empty. Therefore the dma_list_descriptor is now first and last. - (None, Some(dma_list_descriptor)) => { - self.rx_chain_ptrs = Some((dma_list_descriptor, dma_list_descriptor)) - } - _ => {} - } - unsafe { - ll::set_base_rx_descriptor(base_descriptor); + self.rx_chain_ptrs = None; + + self.ll_driver.clear_base_rx_descriptor(); } } /// Take the first [DMAListItem] out of the list. - pub fn take_first(&mut self) -> Option<&'res mut DmaDescriptor> { + pub fn take_first(&mut self) -> Option<&'static mut DmaDescriptor> { let first = unsafe { self.rx_chain_ptrs?.0.as_mut() }; trace!("Taking buffer: {:x} from DMA list.", first as *mut _ as u32); if first.flags.suc_eof() && first.len() >= size_of::() { @@ -178,10 +198,12 @@ impl<'res> DMAList<'res> { unsafe { last_ptr.as_mut().set_next(Some(dma_list_descriptor)); - ll::reload_hw_rx_descriptors(); + self.ll_driver.reload_hw_rx_descriptors(); - if ll::next_rx_descriptor_raw() as u32 != 0x3ff00000 - || ll::last_rx_descriptor().map(NonNull::as_ptr) == Some(dma_list_descriptor) + if self.ll_driver.next_rx_descriptor().map(NonNull::as_ptr) + != Some(0x3ff00000 as *mut _) + || self.ll_driver.last_rx_descriptor().map(NonNull::as_ptr) + == Some(dma_list_descriptor) { *last_ptr = NonNull::new(dma_list_descriptor).unwrap(); return; @@ -191,22 +213,21 @@ impl<'res> DMAList<'res> { // If the DMA list is empty, we make this descriptor the base. self.set_rx_chain_base(NonNull::new(dma_list_descriptor)); } - pub fn log_stats() { + /// Log the stats about the DMA list. + pub fn log_stats(&self) { #[allow(unused)] unsafe { let (rx_next, rx_last) = ( - ll::next_rx_descriptor_raw() as u32, - ll::last_rx_descriptor_raw() as u32, + self.ll_driver.next_rx_descriptor().map(|non_null| non_null.as_ptr()), + self.ll_driver.last_rx_descriptor().map(|non_null| non_null.as_ptr()), ); trace!("DMA list: Next: {:x} Last: {:x}", rx_next, rx_last); } } } -impl Drop for DMAList<'_> { +impl Drop for DMAList { fn drop(&mut self) { - unsafe { - ll::deinit_rx(); - } + self.ll_driver.stop_rx(); } } -unsafe impl Send for DMAList<'_> {} +unsafe impl Send for DMAList {} diff --git a/esp-wifi-hal/src/lib.rs b/esp-wifi-hal/src/lib.rs index 2f48aa9..9b079b2 100644 --- a/esp-wifi-hal/src/lib.rs +++ b/esp-wifi-hal/src/lib.rs @@ -61,7 +61,8 @@ extern crate core; mod crypto; mod dma_list; mod ffi; -mod ll; +#[doc(hidden)] +pub mod ll; mod phy_init_data; mod rates; mod sync; diff --git a/esp-wifi-hal/src/ll.rs b/esp-wifi-hal/src/ll.rs index 7088ca3..c886665 100644 --- a/esp-wifi-hal/src/ll.rs +++ b/esp-wifi-hal/src/ll.rs @@ -6,11 +6,16 @@ use core::{iter::IntoIterator, ptr::NonNull}; use esp_hal::{ clock::{ModemClockController, PhyClockGuard}, dma::DmaDescriptor, + interrupt::{self, CpuInterrupt, InterruptHandler}, peripherals::{LPWR, WIFI as HalWIFI}, }; use esp_wifi_sys::include::*; -use crate::{esp_pac::{WIFI, wifi::crypto_key_slot::KEY_VALUE}, phy_init_data::PHY_INIT_DATA_DEFAULT}; +use crate::{ + esp_pac::{wifi::crypto_key_slot::KEY_VALUE, Interrupt as PacInterrupt, WIFI}, + ffi::{disable_wifi_agc, enable_wifi_agc, hal_init}, + phy_init_data::PHY_INIT_DATA_DEFAULT, +}; /// Run a reversible sequence of functions either forward or in reverse. /// @@ -84,18 +89,132 @@ pub struct KeySlotParameters { /// The bank of the RX filter. pub enum RxFilterBank { /// Basic Service Set Identifier (BSSID) - BSSID, + Bssid, /// Receiver Address ReceiverAddress, } impl RxFilterBank { pub(crate) fn into_bits(self) -> usize { match self { - Self::BSSID => 0, + Self::Bssid => 0, Self::ReceiverAddress => 1, } } } +macro_rules! cause_bitmask { + ([$($(@chip($chips:tt))? $mask:expr),*]) => { + const { + let mut temp = 0; + $( + #[allow(unused)] + let mut condition = true; + $( + condition = cfg!(any($chips)); + )? + temp |= if condition { $mask } else { 0 }; + )* + temp + } + }; + ($mask:expr) => { + $mask + }; +} +/// Create a struct to access the cause of an interrupt. +/// +/// All declared accessor functions are marked as `inline`, since they are basically just a mask +/// and a compare. +macro_rules! interrupt_cause_struct { + ($(#[$struct_meta:meta])* $struct_name:ident => { + $( + $(#[$function_meta:meta])* + $function_name:ident => $mask:tt + ),* + }) => { + $(#[$struct_meta])* + #[cfg_attr(feature = "defmt", defmt::Format)] + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[repr(C)] + pub struct $struct_name(u32); + impl $struct_name { + #[inline] + /// Get the raw interrupt cause. + pub const fn raw_cause(&self) -> u32 { + self.0 + } + #[inline] + /// Is there no known cause for the interrupt. + pub fn is_emtpy(&self) -> bool { + self.0 == 0 + } + $( + #[inline] + $(#[$function_meta])* + pub fn $function_name(&self) -> bool { + (self.0 & cause_bitmask!($mask)) != 0 + } + )* + } + }; +} +interrupt_cause_struct! { + /// Cause for the MAC interrupt. + MacInterruptCause => { + /// A frame was received. + /// + /// We don't know, what the individual bits mean, but this works. + rx => [0x100020, @chip(esp32) 0x4], + /// A frame was transmitted successfully. + tx_success => 0x80, + /// A transmission timeout occured. + /// + /// We think this might have something to do with waiting for medium access. + tx_timeout => 0x80000, + /// A transmission collision occured. + /// + /// This might be caused by multiple transmissions with different ACs getting a TXOP at the + /// same time. + tx_collision => 0x100 + } +} +#[cfg(pwr_interrupt_present)] +interrupt_cause_struct! { + /// Cause for the power interrupt. + PwrInterruptCause => { + /// A TBTT was reached or is about to be reached. + /// + /// NOTE: This is an unconfirmed assumption. + tbtt => [@chip(esp32s2) 0x1e], + // TODO: No idea. + tsf_timer => [@chip(esp32s2) 0x1e0] + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +/// The different interrupts of the Wi-Fi peripheral. +pub enum WiFiInterrupt { + /// Medium Access Control interrupt + Mac, + /// Baseband interrupt + /// + /// NOTE: We know literally nothing about this interrupt, as it doesn't appear to be used at + /// all. This is only included for completeness. + Bb, + #[cfg(pwr_interrupt_present)] + /// Low power interrupt + Pwr, + // With chips supporting Wi-Fi 6, there will be a BSS Color interrupt here. +} +impl From for PacInterrupt { + fn from(value: WiFiInterrupt) -> Self { + match value { + WiFiInterrupt::Mac => PacInterrupt::WIFI_MAC, + WiFiInterrupt::Bb => PacInterrupt::WIFI_BB, + #[cfg(pwr_interrupt_present)] + WiFiInterrupt::Pwr => PacInterrupt::WIFI_PWR, + } + } +} #[cfg(any(feature = "esp32", feature = "esp32s2"))] /// The number of "interfaces" supported by the hardware. @@ -120,13 +239,11 @@ pub const KEY_SLOT_COUNT: usize = 25; /// A lot of the functions, that can panic, will do so under similar conditions, so the docs are /// shared. When an index (i.e. key slot or interface) is provided, it is expected, that the input /// is within the valid range. If it is not, the function will panic. -pub struct LowLevelDriver<'d> { - /// The Wi-Fi peripheral. - wifi: HalWIFI<'d>, +pub struct LowLevelDriver { /// Prevents the PHY clock from being disabled, while the driver is running. - phy_clock_guard: PhyClockGuard<'d>, + _phy_clock_guard: PhyClockGuard<'static>, } -impl<'d> LowLevelDriver<'d> { +impl LowLevelDriver { /// Get the PAC Wi-Fi peripheral. /// /// This stops RA from doing funky shit. @@ -134,12 +251,9 @@ impl<'d> LowLevelDriver<'d> { unsafe { WIFI::steal() } } /// Create a new [LowLevelDriver]. - pub fn new(mut wifi: esp_hal::peripherals::WIFI<'d>) -> Self { - let phy_clock_guard = unsafe { Self::init(&mut wifi) }; - Self { - wifi, - phy_clock_guard, - } + pub fn new(_wifi: esp_hal::peripherals::WIFI<'_>) -> Self { + let _phy_clock_guard = unsafe { Self::init() }; + Self { _phy_clock_guard } } // Initialization @@ -173,7 +287,7 @@ impl<'d> LowLevelDriver<'d> { /// Initialize the PHY. /// /// NOTE: This is very temporary and will be replaced, as soon as `esp-phy` is released. - unsafe fn initialize_phy() -> PhyClockGuard<'d> { + unsafe fn initialize_phy() -> PhyClockGuard<'static> { let clock_guard = unsafe { HalWIFI::steal() }.enable_phy_clock(); let mut cal_data = [0u8; size_of::()]; let init_data = &PHY_INIT_DATA_DEFAULT; @@ -199,7 +313,7 @@ impl<'d> LowLevelDriver<'d> { // Disable RX unsafe { - Self::set_rx_status(false); + Self::set_rx_enable(false); } } /// Initialize or deinitialize the MAC. @@ -246,9 +360,6 @@ impl<'d> LowLevelDriver<'d> { /// # Safety /// This should only be called during init. unsafe fn setup_mac() { - unsafe extern "C" { - fn hal_init(); - } // We call the proprietary blob here, to do some init for us. In the long term, this will // be moved to open source code. In the meantime, we do the init, that we already understand a // second time in open source code @@ -257,7 +368,7 @@ impl<'d> LowLevelDriver<'d> { } // Open source setup code - Self::crypto_init(); + Self::init_crypto(); } /// Initialize the hardware. /// @@ -278,23 +389,71 @@ impl<'d> LowLevelDriver<'d> { /// At the moment, this is only intended to be called upon initializing the low level driver. /// Reinitialization and partial PHY calibration are NOT handled at all. The effects of calling /// this multiple times are unknown. - unsafe fn init(wifi: &mut HalWIFI<'d>) -> PhyClockGuard<'d> { + unsafe fn init() -> PhyClockGuard<'static> { // Enable the power domain. unsafe { Self::set_module_status(true); } // Enable the modem clock. - wifi.enable_modem_clock(true); + unsafe { HalWIFI::steal() }.enable_modem_clock(true); // Initialize the PHY. let clock_guard = unsafe { Self::initialize_phy() }; unsafe { Self::reset_mac(); Self::set_mac_state(true); + Self::setup_mac(); } + Self::init_crypto(); clock_guard } + // Interrupt handling + + #[inline(always)] + /// Get and clear the MAC interrupt cause. + /// + /// # Safety + /// This is expected to be called ONLY from the MAC interrupt handler. + pub unsafe fn get_and_clear_mac_interrupt_cause() -> MacInterruptCause { + let cause = Self::regs().mac_interrupt().wifi_int_status().read().bits(); + Self::regs() + .mac_interrupt() + .wifi_int_clear() + .write(|w| unsafe { w.bits(cause) }); + MacInterruptCause(cause) + } + #[cfg(pwr_interrupt_present)] + #[inline(always)] + /// Get and clear the PWR interrupt cause. + /// + /// # Safety + /// This is expected to be called ONLY from the MAC interrupt handler. + pub unsafe fn get_and_clear_pwr_interrupt_cause() -> PwrInterruptCause { + let cause = Self::regs().pwr_interrupt().pwr_int_status().read().bits(); + Self::regs() + .pwr_interrupt() + .pwr_int_clear() + .write(|w| unsafe { w.bits(cause) }); + PwrInterruptCause(cause) + } + /// Configure the specified interrupt. + /// + /// Usually all relevant interrupts are bound to the same interrupt handler and CPU interrupt. + /// The default CPU interrupt for Wi-Fi is [CpuInterrupt::Interrupt0LevelPriority1]. + pub fn configure_interrupt( + &self, + interrupt: WiFiInterrupt, + cpu_interrupt: CpuInterrupt, + interrupt_handler: InterruptHandler, + ) { + unsafe { + let interrupt = interrupt.into(); + interrupt::bind_interrupt(interrupt, interrupt_handler.handler()); + let _ = interrupt::enable_direct(interrupt, cpu_interrupt); + } + } + // RX #[inline] @@ -302,12 +461,11 @@ impl<'d> LowLevelDriver<'d> { /// /// # Safety /// Enabling RX, when the DMA list isn't setup correctly yet, may be incorrect. - unsafe fn set_rx_status(enable_rx: bool) { + unsafe fn set_rx_enable(enable_rx: bool) { Self::regs() .rx_ctrl() .modify(|_, w| w.rx_enable().bit(enable_rx)); } - #[inline] /// Check if RX is enabled. /// @@ -315,6 +473,64 @@ impl<'d> LowLevelDriver<'d> { pub fn rx_enabled(&self) -> bool { Self::regs().rx_ctrl().read().rx_enable().bit() } + /// Tell the hardware to reload the RX descriptors. + /// + /// This will spin, until the bit is clear again. + pub fn reload_hw_rx_descriptors(&self) { + let wifi = Self::regs(); + let reg = wifi.rx_ctrl(); + + // Start the reload. + reg.modify(|_, w| w.rx_descr_reload().set_bit()); + + // Wait for the hardware descriptors to no longer be in reload. + while reg.read().rx_descr_reload().bit() {} + } + /// Set the base descriptor. + pub fn set_base_rx_descriptor(&self, base_descriptor: NonNull) { + Self::regs() + .rx_dma_list() + .rx_descr_base() + .write(|w| unsafe { w.bits(base_descriptor.as_ptr() as u32) }); + self.reload_hw_rx_descriptors(); + } + /// Reset the base descriptor. + pub fn clear_base_rx_descriptor(&self) { + Self::regs().rx_dma_list().rx_descr_base().reset(); + } + /// Start receiving frames. + /// + /// This will set the provided descriptor as the base of the RX DMA list and enable RX. + pub fn start_rx(&self, base_descriptor: NonNull) { + self.set_base_rx_descriptor(base_descriptor); + unsafe { + Self::set_rx_enable(true); + } + } + /// Stop receiving frames. + /// + /// This will also clear the RX DMA list. + pub fn stop_rx(&self) { + unsafe { + Self::set_rx_enable(false); + } + self.clear_base_rx_descriptor(); + } + /// Get the base RX descriptor. + pub fn base_rx_descriptor(&self) -> Option> { + NonNull::new(Self::regs().rx_dma_list().rx_descr_base().read().bits() as *mut DmaDescriptor) + } + /// Get the next RX descriptor. + pub fn next_rx_descriptor(&self) -> Option> { + NonNull::new(Self::regs().rx_dma_list().rx_descr_next().read().bits() as *mut DmaDescriptor) + } + /// Get the last RX descriptor. + pub fn last_rx_descriptor(&self) -> Option> { + NonNull::new(Self::regs().rx_dma_list().rx_descr_last().read().bits() as *mut DmaDescriptor) + } + + // RX filtering + /// Enable or disable the specified filter. pub fn set_filter_enable(&self, interface: usize, filter_bank: RxFilterBank, enabled: bool) { Self::regs() @@ -322,6 +538,7 @@ impl<'d> LowLevelDriver<'d> { .mask_high(interface) .modify(|_, w| w.enabled().bit(enabled)); } + /// Check if the filter is enabled. pub fn filter_enabled(&self, interface: usize, filter_bank: RxFilterBank) -> bool { Self::regs() .filter_bank(filter_bank.into_bits()) @@ -384,6 +601,8 @@ impl<'d> LowLevelDriver<'d> { filter_bank.mask_high(interface).reset(); } + // TX + // Crypto #[inline] @@ -407,7 +626,7 @@ impl<'d> LowLevelDriver<'d> { /// /// This is pretty much the same as `hal_crypto_init`, except we init crypto for all /// interfaces, not just zero and one. - fn crypto_init() { + fn init_crypto() { // Enable crypto for all interfaces. (0..INTERFACE_COUNT).for_each(Self::enable_crypto_for_interface); @@ -417,7 +636,6 @@ impl<'d> LowLevelDriver<'d> { .general_crypto_control() .reset(); } - #[inline] /// Enable or disable a key slot. /// /// As soon, as the key slot is enabled, the hardware will treat the data in it as valid. @@ -427,6 +645,15 @@ impl<'d> LowLevelDriver<'d> { .crypto_key_slot_state() .modify(|_, w| w.key_slot_enable(key_slot as u8).bit(enabled)); } + /// Check if the key slot is enabled. + pub fn key_slot_enabled(&self, key_slot: usize) -> bool { + Self::regs() + .crypto_control() + .crypto_key_slot_state() + .read() + .key_slot_enable(key_slot as u8) + .bit() + } /// Set the cryptographic key for the specified key slot. /// /// # Panics @@ -510,80 +737,55 @@ impl<'d> LowLevelDriver<'d> { key_slot.addr_high().reset(); key_slot.key_value_iter().for_each(KEY_VALUE::reset); } -} - -/// Tell the hardware to reload the RX descriptors. -/// -/// This will spin, until the bit is clear again. -pub unsafe fn reload_hw_rx_descriptors() { - let reg = HalWIFI::regs().rx_ctrl(); + /// Set the crypto parameters for an interface. + /// + /// Management Frame Protection (MFP) and Signaling and Payload Protection (SPP) are configured + /// globally for every interface, as well as the use of an Authenticated Encryption with + /// Associated Data (AEAD) system. It is not possible to use non-AEAD ciphers (i.e. WEP) in + /// parallel with AEAD ciphers (all others) on the same interface. + /// + /// NOTE: SMS4 (WAPI) is not currently supported, as information about it is scarce and it's + /// not really used in practice. + pub fn set_interface_crypto_parameters( + &self, + interface: usize, + protect_management_frames: bool, + protect_signaling_and_payload: bool, + is_cipher_aead: bool, + ) { + Self::regs() + .crypto_control() + .interface_crypto_control(interface) + .modify(|r, w| unsafe { + w.bits(r.bits() | 0x10103) + .spp_enable() + .bit(protect_signaling_and_payload) + .pmf_disable() + .bit(!protect_management_frames) + .aead_cipher() + .bit(is_cipher_aead) + .sms4() + .clear_bit() + }); + } - // Start the reload. - reg.modify(|_, w| w.rx_descr_reload().set_bit()); + // RF - // Wait for the hardware descriptors to no longer be in reload. - while reg.read().rx_descr_reload().bit() {} -} -/// Set the base descriptor. -/// -/// If `base_descriptor` is [None], this will reset the register back to zero. -pub unsafe fn set_base_rx_descriptor(base_descriptor: Option>) { - let reg = HalWIFI::regs().rx_dma_list().rx_descr_base(); - if let Some(ptr) = base_descriptor { - reg.write(|w| w.bits(ptr.as_ptr() as u32)); - } else { - reg.reset(); + /// Set the radio channel. + /// + /// The channel number is not validated. + pub fn set_channel(&self, channel_number: u8) { + unsafe { + Self::set_mac_state(false); + #[cfg(nomac_channel_set)] + crate::ffi::chip_v7_set_chan_nomac(channel_number, 0); + #[cfg(not(nomac_channel_set))] + crate::ffi::chip_v7_set_chan(channel_number, 0); + disable_wifi_agc(); + Self::set_mac_state(true); + enable_wifi_agc(); + } } - reload_hw_rx_descriptors(); -} -/// Get the raw value of the next RX descriptor register. -/// -/// # Safety -/// This could be unsafe, since we don't know what happens if we read the register, while the -/// peripheral is powered down. -pub unsafe fn next_rx_descriptor_raw() -> *mut DmaDescriptor { - HalWIFI::regs().rx_dma_list().rx_descr_next().read().bits() as *mut DmaDescriptor -} -/// Get the raw value of the last RX descriptor register. -/// -/// # Safety -/// This could be unsafe, since we don't know what happens if we read the register, while the -/// peripheral is powered down. -pub unsafe fn last_rx_descriptor_raw() -> *mut DmaDescriptor { - HalWIFI::regs().rx_dma_list().rx_descr_last().read().bits() as *mut DmaDescriptor -} - -/// Get the last RX descriptor. -/// -/// # Safety -/// This could be unsafe, since we don't know what happens if we read the register, while the -/// peripheral is powered down. -pub unsafe fn last_rx_descriptor() -> Option> { - NonNull::new(last_rx_descriptor_raw()) -} -/// Enable or disable RX. -pub unsafe fn set_rx_enabled(enabled: bool) { - HalWIFI::regs() - .rx_ctrl() - .modify(|_, w| w.rx_enable().bit(enabled)); -} -/// Initialize RX. -/// -/// This will set the base descriptor, reload the hardware descriptors and enable RX. -/// -/// # Safety -/// The specified pointer must point to a valid DMA descriptor, with itself and it's associated -/// buffer in internal RAM. -pub unsafe fn init_rx(base_descriptor: NonNull) { - set_base_rx_descriptor(Some(base_descriptor)); - set_rx_enabled(true); -} -/// Disable RX. -/// -/// This will disable RX, zero the DMA list base descriptor and reload the hardware DMA list descriptors. -pub unsafe fn deinit_rx() { - set_rx_enabled(false); - set_base_rx_descriptor(None); } /// Convert a software slot index to the hardware slot index. diff --git a/esp-wifi-hal/src/sync.rs b/esp-wifi-hal/src/sync.rs index ee1ae4e..754058d 100644 --- a/esp-wifi-hal/src/sync.rs +++ b/esp-wifi-hal/src/sync.rs @@ -155,7 +155,7 @@ impl TxSlotQueue { } } /// Asynchronously wait for a new slot to become available. - pub async fn wait_for_slot(&self) -> BorrowedTxSlot { + pub async fn wait_for_slot(&self) -> BorrowedTxSlot<'_> { let slot = poll_fn(|cx| { self.state.lock(|rc| { let mut state = rc.borrow_mut(); diff --git a/esp-wifi-hal/src/wmac.rs b/esp-wifi-hal/src/wmac.rs index 37d5b45..4f31dc8 100644 --- a/esp-wifi-hal/src/wmac.rs +++ b/esp-wifi-hal/src/wmac.rs @@ -8,31 +8,23 @@ use portable_atomic::{AtomicU16, AtomicU64, AtomicU8, Ordering}; use crate::{ esp_pac::wifi::TX_SLOT_CONFIG, - ll::{self, RxFilterBank}, + ll::{self, KeySlotParameters, LowLevelDriver, RxFilterBank, WiFiInterrupt}, rates::RATE_LUT, sync::{BorrowedTxSlot, TxSlotQueue, TxSlotStatus}, CipherParameters, WiFiRate, INTERFACE_COUNT, KEY_SLOT_COUNT, }; use embassy_sync::blocking_mutex::{self}; -use embassy_time::Instant; use esp_hal::{ - clock::{ModemClockController, PhyClockGuard}, - dma::{DmaDescriptor, Owner}, - interrupt::{bind_interrupt, enable, map, CpuInterrupt, Priority}, - peripherals::{Interrupt, ADC2, LPWR, WIFI}, - ram, - system::Cpu, + dma::{DmaDescriptor, Owner}, handler, interrupt::CpuInterrupt, peripherals::{ADC2, WIFI}, }; use esp_wifi_sys::include::{ - esp_phy_calibration_data_t, esp_phy_calibration_mode_t_PHY_RF_CAL_FULL, register_chipv7_phy, wifi_pkt_rx_ctrl_t, }; use macro_bits::{bit, check_bit}; use crate::{ dma_list::DMAList, - ffi::{disable_wifi_agc, enable_wifi_agc, hal_init, tx_pwctrl_background}, - phy_init_data::PHY_INIT_DATA_DEFAULT, + ffi::{tx_pwctrl_background}, sync::{SignalQueue, TxSlotStateSignal}, DefaultRawMutex, WiFiResources, }; @@ -47,37 +39,24 @@ static WIFI_TX_SLOTS: [TxSlotStateSignal; 5] = [EMPTY_SLOT; 5]; // We run tx_pwctrl_background every four transmissions. static FRAMES_SINCE_LAST_TXPWR_CTRL: AtomicU8 = AtomicU8::new(0); -#[ram] -extern "C" fn interrupt_handler() { - // We don't want to have to steal this all the time. - let wifi = WIFI::regs(); - - let cause = wifi.mac_interrupt().wifi_int_status().read().bits(); - if cause == 0 { +#[handler] +fn mac_handler() { + let cause = unsafe { LowLevelDriver::get_and_clear_mac_interrupt_cause() }; + if cause.is_emtpy() { return; } - #[cfg(pwr_interrupt_present)] - { - // We ignore the WIFI_PWR interrupt for now... - let cause = wifi.pwr_interrupt().pwr_int_status().read().bits(); - wifi.pwr_interrupt() - .pwr_int_clear() - .write(|w| unsafe { w.bits(cause) }); - } - wifi.mac_interrupt() - .wifi_int_clear() - .write(|w| unsafe { w.bits(cause) }); - if cause & 0x1000024 != 0 { + + if cause.rx() { WIFI_RX_SIGNAL_QUEUE.put(); } - if cause & 0x80 != 0 { + if cause.tx_success() { unsafe { ll::process_tx_completions(|slot| { WIFI_TX_SLOTS[slot].signal(TxSlotStatus::Done); }) }; } - if cause & 0x80000 != 0 { + if cause.tx_timeout() { unsafe { ll::process_tx_timeouts(|slot| { WIFI_TX_SLOTS[slot].signal(TxSlotStatus::Timeout); @@ -86,7 +65,7 @@ extern "C" fn interrupt_handler() { }) }; } - if cause & 0x100 != 0 { + if cause.tx_collision() { unsafe { ll::process_tx_collisions(|slot| { WIFI_TX_SLOTS[slot].signal(TxSlotStatus::Collision); @@ -96,6 +75,12 @@ extern "C" fn interrupt_handler() { }; } } +#[cfg(pwr_interrupt_present)] +#[handler] +fn pwr_handler() { + let _cause = unsafe { LowLevelDriver::get_and_clear_pwr_interrupt_cause() }; + // We don't do anything yet. +} #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -110,7 +95,7 @@ pub enum TxErrorBehaviour { /// A buffer borrowed from the DMA list. pub struct BorrowedBuffer<'res> { - dma_list: &'res blocking_mutex::Mutex>>, + dma_list: &'res blocking_mutex::Mutex>, dma_descriptor: &'res mut DmaDescriptor, } impl BorrowedBuffer<'_> { @@ -336,154 +321,37 @@ static MAC_SYSTEM_TIME_DELTA: AtomicU64 = AtomicU64::new(0); /// /// WARNING: Currently dropping the driver is not properly implemented. pub struct WiFi<'res> { - dma_list: &'res blocking_mutex::Mutex>>, + dma_list: &'res blocking_mutex::Mutex>, + ll_driver: &'res LowLevelDriver, current_channel: AtomicU8, sequence_number: AtomicU16, tx_slot_queue: TxSlotQueue, - _phy_clock_guard: PhyClockGuard<'static>, } impl<'res> WiFi<'res> { - /// Enable the Wi-Fi power domain. - fn enable_wifi_power_domain() { - unsafe { - let rtc_cntl = &*LPWR::ptr(); - trace!("Enabling wifi power domain."); - rtc_cntl - .dig_pwc() - .modify(|_, w| w.wifi_force_pd().clear_bit()); - - rtc_cntl - .dig_iso() - .modify(|_, w| w.wifi_force_iso().clear_bit()); - } - } - fn enable_wifi_modem_clock() { - unsafe { WIFI::steal() }.enable_modem_clock(true); - } - /// Enable the PHY. - fn phy_enable() -> PhyClockGuard<'static> { - let clock_guard = unsafe { WIFI::steal() }.enable_phy_clock(); - let mut cal_data = [0u8; size_of::()]; - let init_data = &PHY_INIT_DATA_DEFAULT; - trace!("Enabling PHY."); - unsafe { - register_chipv7_phy( - init_data, - &mut cal_data as *mut _ as *mut esp_phy_calibration_data_t, - esp_phy_calibration_mode_t_PHY_RF_CAL_FULL, - ); - } - clock_guard - } - /// Reset the MAC. - fn reset_mac() { - let mut wifi = unsafe { WIFI::steal() }; - trace!("Reseting MAC."); - wifi.reset_wifi_mac(); - wifi.register_block() - .ctrl() - .modify(|r, w| unsafe { w.bits(r.bits() & 0x7fffffff) }); - } - /// Initialize the MAC. - unsafe fn init_mac() { - trace!("Initializing MAC."); - WIFI::regs() - .ctrl() - .modify(|r, w| unsafe { w.bits(r.bits() & 0xffffe800) }); - } - /// Deinitialize the MAC. - unsafe fn deinit_mac() { - trace!("Deinitializing MAC."); - WIFI::regs().ctrl().modify(|r, w| unsafe { - w.bits(r.bits() | 0x17ff); - while r.bits() & 0x2000 != 0 {} - w - }); - } - /// Set the interrupt handler. - fn set_isr() { - trace!("Setting interrupt handler."); - #[cfg(target_arch = "xtensa")] - let cpu_interrupt = CpuInterrupt::Interrupt0LevelPriority1; - #[cfg(target_arch = "riscv32")] - let cpu_interrupt = CpuInterrupt::Interrupt1; - unsafe { - map(Cpu::current(), Interrupt::WIFI_MAC, cpu_interrupt); - bind_interrupt(Interrupt::WIFI_MAC, interrupt_handler); - #[cfg(pwr_interrupt_present)] - { - map(Cpu::current(), Interrupt::WIFI_PWR, cpu_interrupt); - bind_interrupt(Interrupt::WIFI_PWR, interrupt_handler); - } - }; - enable(Interrupt::WIFI_MAC, Priority::Priority1).unwrap(); - #[cfg(pwr_interrupt_present)] - enable(Interrupt::WIFI_PWR, Priority::Priority1).unwrap(); - } - fn ic_enable() { - trace!("ic_enable"); - unsafe { - hal_init(); - } - Self::set_isr(); - } - fn crypto_init() { - let wifi = unsafe { WIFI::steal() }; - // We enable hardware crypto for all interfaces. - wifi.register_block() - .crypto_control() - .interface_crypto_control_iter() - .for_each(|interface_crypto_control| { - interface_crypto_control.write(|w| unsafe { w.bits(0x0003_0000) }); - }); - wifi.register_block() - .crypto_control() - .general_crypto_control() - .reset(); - } /// Initialize the WiFi peripheral. pub fn new( - _wifi: WIFI, + wifi: WIFI<'res>, _adc2: ADC2, wifi_resources: &'res mut WiFiResources, ) -> Self { trace!("Initializing WiFi."); - Self::enable_wifi_power_domain(); - Self::enable_wifi_modem_clock(); - let phy_clock_guard = Self::phy_enable(); - let start_time = Instant::now(); - Self::reset_mac(); - unsafe { - Self::init_mac(); - } - Self::ic_enable(); + let ll_driver = LowLevelDriver::new(wifi); + + ll_driver.configure_interrupt(WiFiInterrupt::Mac, CpuInterrupt::Interrupt0LevelPriority1, mac_handler); + + #[cfg(pwr_interrupt_present)] + ll_driver.configure_interrupt(WiFiInterrupt::Pwr, CpuInterrupt::Interrupt2LevelPriority1, pwr_handler); - // This is technically already done in `hal_init`, but I want to replace that eventually, - // so I'm trying to gradually move more and more parts out. - Self::crypto_init(); - unsafe { ll::set_rx_enabled(true) }; + let (dma_list, ll_driver) = unsafe { wifi_resources.init(ll_driver) }; let temp = Self { current_channel: AtomicU8::new(1), - dma_list: unsafe { wifi_resources.init() }, + ll_driver, + dma_list, sequence_number: AtomicU16::new(0), tx_slot_queue: TxSlotQueue::new(0..5), - _phy_clock_guard: phy_clock_guard, }; - // System should always be ahead of MAC time, since the MAC timer gets started after the - // System timer. - MAC_SYSTEM_TIME_DELTA.store( - esp_hal::time::Instant::now() - .duration_since_epoch() - .as_micros() - - temp.mac_time() as u64, - Ordering::Relaxed, - ); temp.set_channel(1).unwrap(); - trace!( - "WiFi MAC init complete. Took {} µs", - start_time.elapsed().as_micros() - ); temp } /// Clear all currently pending frames in the RX queue. @@ -769,16 +637,7 @@ impl<'res> WiFi<'res> { return Err(WiFiError::InvalidChannel); } trace!("Changing channel to {}.", channel_number); - unsafe { - Self::deinit_mac(); - #[cfg(nomac_channel_set)] - crate::ffi::chip_v7_set_chan_nomac(channel_number, 0); - #[cfg(not(nomac_channel_set))] - crate::ffi::chip_v7_set_chan(channel_number, 0); - disable_wifi_agc(); - Self::init_mac(); - enable_wifi_agc(); - } + self.ll_driver.set_channel(channel_number); self.current_channel .store(channel_number, Ordering::Relaxed); Ok(()) @@ -800,20 +659,6 @@ impl<'res> WiFi<'res> { Err(WiFiError::InterfaceOutOfBounds) } } - /// Enable or disable the filter. - pub fn set_filter_status( - &self, - bank: RxFilterBank, - interface: usize, - enabled: bool, - ) -> WiFiResult<()> { - Self::validate_interface(interface)?; - WIFI::regs() - .filter_bank(bank.into_bits()) - .mask_high(interface) - .modify(|_, w| w.enabled().bit(enabled)); - Ok(()) - } /// Specifiy whether the BSSID is checked or not. /// /// This is used to control a special behaviour of the hardware. If the RA filter is enabled, @@ -845,29 +690,19 @@ impl<'res> WiFi<'res> { Self::validate_interface(interface)?; Ok(WIFI::regs().interface_rx_control(interface).read().bits()) } - /// Set the parameters for the filter. + /// Configure the filter. pub fn set_filter( &self, - bank: RxFilterBank, + filter_bank: RxFilterBank, interface: usize, address: [u8; 6], - mask: [u8; 6], ) -> WiFiResult<()> { Self::validate_interface(interface)?; - let wifi = WIFI::regs(); - let bank = wifi.filter_bank(bank.into_bits()); - bank.addr_low(interface) - .write(|w| unsafe { w.bits(u32::from_le_bytes(address[..4].try_into().unwrap())) }); - bank.addr_high(interface).write(|w| unsafe { - w.addr() - .bits(u16::from_le_bytes(address[4..].try_into().unwrap())) - }); - bank.mask_low(interface) - .write(|w| unsafe { w.bits(u32::from_le_bytes(mask[..4].try_into().unwrap())) }); - bank.mask_high(interface).write(|w| unsafe { - w.mask() - .bits(u16::from_le_bytes(mask[4..].try_into().unwrap())) - }); + + self.ll_driver.set_filter_address(interface, filter_bank, &address); + self.ll_driver.set_filter_mask(interface, filter_bank, &[0xff; 6]); + self.ll_driver.set_filter_enable(interface, filter_bank, true); + Ok(()) } /// Enable or disable scanning mode. @@ -905,28 +740,9 @@ impl<'res> WiFi<'res> { /// If `key_slot` is larger than or equal to [WiFi::KEY_SLOT_COUNT], an error will be returned. pub fn key_slot_in_use(&self, key_slot: usize) -> WiFiResult { Self::validate_key_slot(key_slot).map(|_| { - WIFI::regs() - .crypto_control() - .crypto_key_slot_state() - .read() - .key_slot_enable(key_slot as u8) - .bit() + self.ll_driver.key_slot_enabled(key_slot) }) } - /// Get an iterator over the current state of all key slots. - /// - /// If the value returned by [Iterator::next] is `true`, the key slot is in use. - /// NOTE: The returned iterator only reflects the state, when the function was called. If you - /// call either [WiFi::set_key] or [WiFi::delete_key], between calling this function and - /// [Iterator::next], the result may no longer be accurate. - pub fn key_slot_state_iter(&self) -> impl Iterator { - let key_slot_state = WIFI::regs() - .crypto_control() - .crypto_key_slot_state() - .read() - .bits(); - (0..KEY_SLOT_COUNT).map(move |i| check_bit!(key_slot_state, bit!(i))) - } /// Set a cryptographic key. /// /// This is equivalent to MLME-SETKEYS.request with one key descriptor. @@ -945,7 +761,6 @@ impl<'res> WiFi<'res> { // of this function. Sadly the compiler still fails to prove some other stuff. let key = cipher_parameters.key(); - assert!(key.len() <= 32); Self::validate_key_slot(key_slot)?; Self::validate_interface(interface)?; @@ -957,74 +772,36 @@ impl<'res> WiFi<'res> { return Err(WiFiError::MulticastBitSet); } - let wifi = WIFI::regs(); - - let crypto_key_slot = wifi.crypto_key_slot(key_slot); - - crypto_key_slot - .addr_low() - .write(|w| unsafe { w.bits(u32::from_le_bytes(address[..4].try_into().unwrap())) }); - crypto_key_slot.addr_high().write(|w| unsafe { - w.addr() - .bits(u16::from_le_bytes(address[4..].try_into().unwrap())) - .algorithm() - .bits(cipher_parameters.algorithm()) - .wep_104() - .bit(cipher_parameters.is_wep_104()) - .bits_256() - .bit(cipher_parameters.is_256_bit_key()) - .pairwise_key() - .bit(cipher_parameters.is_pairwise()) - .group_key() - .bit(cipher_parameters.is_group()) - .unknown() - .set_bit() - .interface_id() - .bits(interface as u8) - .key_id() - .bits(key_id) - }); - - wifi.crypto_control() - .crypto_key_slot_state() - .modify(|_, w| w.key_slot_enable(key_slot as u8).set_bit()); - - let is_key_len_word_aligned = (key.len() & 0b11) == 0; - - // Copy the key into the slot. - for (i, key_chunk) in key.chunks(4).enumerate().take(8) { - let chunk_len = key_chunk.len(); - assert!(chunk_len <= 4); - - let key_chunk = if !is_key_len_word_aligned && chunk_len != 4 { - let mut temp = [0u8; 4]; - temp[..chunk_len].copy_from_slice(key_chunk); - temp + self.ll_driver.set_key_slot_parameters( + key_slot, + &KeySlotParameters { + address, + key_id, + interface: interface as u8, + pairwise: cipher_parameters.is_pairwise(), + group: cipher_parameters.is_group(), + algorithm: cipher_parameters.algorithm(), + wep_104: cipher_parameters.is_wep_104(), + }, + ); + self.ll_driver.set_key(key_slot, key); + self.ll_driver.set_key_slot_enable(key_slot, true); + + let (protect_management_frames, protect_signaling_and_payload) = + if let Some(aes_cipher_parameters) = cipher_parameters.aes_cipher_parameters() { + ( + aes_cipher_parameters.mfp_enabled, + aes_cipher_parameters.spp_enabled, + ) } else { - key_chunk.try_into().unwrap() + (false, false) }; - crypto_key_slot - .key_value(i) - .write(|w| unsafe { w.bits(u32::from_le_bytes(key_chunk)) }); - } - wifi.crypto_control() - .general_crypto_control() - .modify(|r, w| unsafe { w.bits(r.bits() & 0xff0000ff) }); - wifi.crypto_control() - .interface_crypto_control(interface) - .modify(|_, w| { - w.spp_enable() - .bit(cipher_parameters.is_spp_enabled()) - .pmf_disable() - .bit(!cipher_parameters.is_mfp_enabled()) - .sms4() - .clear_bit() - .aead_cipher() - .bit(cipher_parameters.is_aead()) - }); - wifi.crypto_control() - .interface_crypto_control(interface) - .modify(|r, w| unsafe { w.bits(r.bits() | 0x10103 & 0x3fff_ffff) }); + self.ll_driver.set_interface_crypto_parameters( + interface, + protect_management_frames, + protect_signaling_and_payload, + cipher_parameters.is_aead(), + ); Ok(()) } @@ -1032,21 +809,10 @@ impl<'res> WiFi<'res> { /// /// This is equivalent to MLME-DELETEKEYS.request with one key descriptor. pub fn delete_key(&self, key_slot: usize) -> WiFiResult<()> { - WiFi::validate_key_slot(key_slot)?; - - let wifi = WIFI::regs(); - wifi.crypto_control() - .crypto_key_slot_state() - .modify(|_, w| w.key_slot_enable(key_slot as u8).clear_bit()); - - let crypto_key_slot = wifi.crypto_key_slot(key_slot); - crypto_key_slot.addr_low().reset(); - crypto_key_slot.addr_high().reset(); - crypto_key_slot - .key_value_iter() - .for_each(|key_value| key_value.reset()); - - Ok(()) + WiFi::validate_key_slot(key_slot).map(|_| { + self.ll_driver.set_key_slot_enable(key_slot, false); + self.ll_driver.clear_key_slot(key_slot); + }) } /// Dump the entire contents of a key slot. pub fn dump_key_slot(&self, key_slot: usize) -> WiFiResult<()> { @@ -1130,6 +896,10 @@ impl<'res> WiFi<'res> { &enabled_key_slots[..enabled_slot_count] ); } + /// Log stats about the DMA list. + pub fn log_dma_list_stats(&self) { + self.dma_list.lock(|dma_list| dma_list.borrow().log_stats()) + } } impl Drop for WiFi<'_> { fn drop(&mut self) { From 653833dfe2575c45e086bba308c85bf76234559d Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Wed, 15 Oct 2025 21:34:26 +0200 Subject: [PATCH 6/8] TX works and new HAL revision. --- esp-wifi-hal/Cargo.lock | 389 ++++++++++------- esp-wifi-hal/Cargo.toml | 23 +- esp-wifi-hal/src/dma_list.rs | 8 +- esp-wifi-hal/src/ffi.rs | 2 +- esp-wifi-hal/src/lib.rs | 3 +- esp-wifi-hal/src/ll.rs | 171 ++++---- esp-wifi-hal/src/phy_init_data.rs | 146 ------- esp-wifi-hal/src/wmac.rs | 62 +-- examples/Cargo.lock | 541 +++++++++++++++--------- examples/Cargo.toml | 26 +- examples/src/bin/ack_timeout_test_rx.rs | 2 +- examples/src/bin/ack_timeout_test_tx.rs | 2 +- examples/src/bin/beacon.rs | 2 +- examples/src/bin/beacon_tsf_test_rx.rs | 2 +- examples/src/bin/beacon_tsf_test_tx.rs | 2 +- examples/src/bin/crypto_rx.rs | 2 +- examples/src/bin/crypto_tx.rs | 2 +- examples/src/bin/dup_test.rs | 2 +- examples/src/bin/minimal.rs | 2 +- examples/src/bin/multi_beacon.rs | 2 +- examples/src/bin/scanner.rs | 2 +- examples/src/bin/test_cli.rs | 16 +- examples/src/lib.rs | 8 +- 23 files changed, 767 insertions(+), 650 deletions(-) delete mode 100644 esp-wifi-hal/src/phy_init_data.rs diff --git a/esp-wifi-hal/Cargo.lock b/esp-wifi-hal/Cargo.lock index f77225f..9fa5c6f 100644 --- a/esp-wifi-hal/Cargo.lock +++ b/esp-wifi-hal/Cargo.lock @@ -14,15 +14,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "basic-toml" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" -dependencies = [ - "serde", -] - [[package]] name = "bitfield" version = "0.19.1" @@ -66,11 +57,20 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -84,6 +84,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -221,6 +230,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ + "block-buffer", "crypto-common", ] @@ -235,31 +245,13 @@ dependencies = [ [[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", - "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" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1611b7a7ab5d1fbed84c338df26d56fd9bded58006ebb029075112ed2c5e039" +checksum = "554e3e840696f54b4c9afcf28a0f24da431c927f4151040020416e7393d6d0d8" dependencies = [ "embassy-futures", "embassy-hal-internal", - "embassy-sync 0.7.0", + "embassy-sync 0.7.2", "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", @@ -270,9 +262,9 @@ dependencies = [ [[package]] name = "embassy-futures" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" [[package]] name = "embassy-hal-internal" @@ -291,7 +283,7 @@ checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049" dependencies = [ "cfg-if", "critical-section", - "embedded-io-async", + "embedded-io-async 0.6.1", "futures-sink", "futures-util", "heapless", @@ -299,23 +291,23 @@ dependencies = [ [[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", ] [[package]] name = "embassy-time" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f820157f198ada183ad62e0a66f554c610cdcd1a9f27d4b316358103ced7a1f8" +checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" dependencies = [ "cfg-if", "critical-section", @@ -324,32 +316,35 @@ dependencies = [ "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", - "futures-util", + "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-usb-driver" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970" +checksum = "17119855ccc2d1f7470a39756b12068454ae27a3eabb037d940b5c03d9c77b7a" +dependencies = [ + "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", ] @@ -393,13 +388,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]] @@ -446,22 +456,22 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "esp-config" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd4a8db4b72794637a25944bc8d361c3cc271d4f03987ce8741312b6b61529c" +checksum = "289fde78fff1ff500e81efbdf958b1dae1614eacc61cc5560b0a3e03a25f266e" dependencies = [ "document-features", "esp-metadata-generated", - "evalexpr", "serde", "serde_yaml", + "somni-expr", ] [[package]] name = "esp-hal" -version = "1.0.0-rc.0" +version = "1.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3887eda2917deef3d99e7a5c324f9190714e99055361ad36890dffd0a995b49" +checksum = "f75242d788e67fc7ce51308019c0ff5d5103f989721577bb566b02710ef1ba79" dependencies = [ "bitfield", "bitflags 2.9.1", @@ -471,22 +481,25 @@ dependencies = [ "delegate", "digest", "document-features", - "embassy-embedded-hal 0.3.2", + "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-config", "esp-hal-procmacros", "esp-metadata-generated", "esp-riscv-rt", "esp-rom-sys", + "esp-sync", "esp-synopsys-usb-otg", "esp32", "esp32c2", @@ -503,7 +516,8 @@ dependencies = [ "rand_core 0.6.4", "rand_core 0.9.3", "riscv", - "serde", + "sha1", + "sha2", "strum", "ufmt-write", "xtensa-lx", @@ -512,12 +526,11 @@ dependencies = [ [[package]] name = "esp-hal-procmacros" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbece384edaf0d1eabfa45afa96d910634d4158638ef983b2d419a8dec832246" +checksum = "9fd82a6506fb08d53a1086d165d92f085717aa9c59e67ac87a9e6f8acdcf6897" dependencies = [ "document-features", - "litrs", "object", "proc-macro-crate", "proc-macro2", @@ -527,51 +540,65 @@ dependencies = [ ] [[package]] -name = "esp-metadata" -version = "0.8.0" +name = "esp-metadata-generated" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6fbc1d166be84c0750f121e95c8989ddebd7e7bdd86af3594a6cfb34f039650" -dependencies = [ - "anyhow", - "basic-toml", - "indexmap", - "proc-macro2", - "quote", - "serde", - "strum", -] +checksum = "b18b1787dd3adea642fb529dd83fe558a08ace365bbaede4643a8959992900f4" [[package]] -name = "esp-metadata-generated" +name = "esp-phy" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189d36b8c8a752bdebec67fd02a15ebb1432feea345553749bca7ce2393cc795" +checksum = "62199c3e50eefcf53b8f3c690946582a124f7137b45bd1b14eb4b90a3fec2dd7" dependencies = [ - "esp-metadata", + "cfg-if", + "defmt", + "document-features", + "esp-config", + "esp-hal", + "esp-metadata-generated", + "esp-sync", + "esp-wifi-sys", + "log", ] [[package]] name = "esp-riscv-rt" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a00370dfcb0ccc01c6b2540076379c6efd6890a27f584de217c38e3239e19d5" +checksum = "502744a5b1e7268d27fd2a4e56ad45efe42ead517d6c517a6961540de949b0ee" dependencies = [ "document-features", "riscv", - "riscv-rt-macros", + "riscv-rt", ] [[package]] name = "esp-rom-sys" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "646aca2b30503b6c6f34250255fbd5887fd0c4104ea90802c1fea34f3035e7d6" +checksum = "01bafc39f59d56610b38ed0f63b16abeb8b357b8f546507d0d6c50c1a494f530" dependencies = [ "cfg-if", "document-features", "esp-metadata-generated", ] +[[package]] +name = "esp-sync" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b977b028ae5959f0b2daf6602a1d751723b2d329c7251daf869ad382c8ed1543" +dependencies = [ + "cfg-if", + "document-features", + "embassy-sync 0.6.2", + "embassy-sync 0.7.2", + "esp-metadata-generated", + "riscv", + "xtensa-lx", +] + [[package]] name = "esp-synopsys-usb-otg" version = "0.4.2" @@ -595,9 +622,10 @@ dependencies = [ "defmt", "defmt-or-log", "embassy-futures", - "embassy-sync 0.7.0", + "embassy-sync 0.7.2", "embassy-time", "esp-hal", + "esp-phy", "esp-wifi-sys", "esp32", "esp32s2", @@ -618,9 +646,9 @@ dependencies = [ [[package]] name = "esp32" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7680f79e3a4770e59c2dc25b17dcd852921ee57ffae9a4c4806c9ca5001d54d" +checksum = "b76170a463d18f888a1ad258031901036fd827a9ef126733053ba5f8739fb0c8" dependencies = [ "critical-section", "vcell", @@ -628,9 +656,9 @@ dependencies = [ [[package]] name = "esp32c2" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da1bcf86fca83543e0e95561cba27bbcc6b6e7adc5428f49187f5868bc0c3ed2" +checksum = "e62cf8932966b8d445b6f1832977b468178f0a84effb2e9fda89f60c24d45aa3" dependencies = [ "critical-section", "vcell", @@ -638,9 +666,9 @@ dependencies = [ [[package]] name = "esp32c3" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2c5a33d4377f974cbe8cadf8307f04f2c39755704cb09e81852c63ee4ac7b8" +checksum = "356af3771d0d6536c735bf71136594f4d1cbb506abf6e0c51a6639e9bf4e7988" dependencies = [ "critical-section", "vcell", @@ -648,9 +676,9 @@ dependencies = [ [[package]] name = "esp32c6" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ca8fc81b7164df58b5e04aaac9e987459312e51903cca807317990293973a6e" +checksum = "8f5e511df672d79cd63365c92045135e01ba952b6bddd25b660baff5e1110f6b" dependencies = [ "critical-section", "vcell", @@ -658,9 +686,9 @@ dependencies = [ [[package]] name = "esp32h2" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80171d08c17d8c63b53334c60ca654786a7593481531d19b639c4e5c76d276de" +checksum = "ed4a50bbd1380931e095e0973b9b12f782a9c481f2edf1f7c42e7eb4ff736d6d" dependencies = [ "critical-section", "vcell", @@ -668,9 +696,9 @@ dependencies = [ [[package]] name = "esp32s2" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c90d347480fca91f4be3e94b576af9c6c7987795c58dc3c5a7c108b6b3966dc" +checksum = "98574d4c577fbe888fe3e6df7fc80d25a05624d9998f7d7de1500ae21fcca78f" dependencies = [ "critical-section", "vcell", @@ -678,20 +706,14 @@ dependencies = [ [[package]] name = "esp32s3" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3769c56222c4548833f236c7009f1f8b3f2387af26366f6bd1cea456666a49d" +checksum = "1810d8ee4845ef87542af981e38eb80ab531d0ef1061e1486014ab7af74c337a" dependencies = [ "critical-section", "vcell", ] -[[package]] -name = "evalexpr" -version = "12.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a3229bec56a977f174b32fe7b8d89e8c79ebb4493d10ad763b6676dc2dc0c9" - [[package]] name = "fnv" version = "1.0.7" @@ -792,13 +814,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", "hashbrown", - "serde", ] [[package]] @@ -809,9 +830,9 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" [[package]] name = "instability" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" +checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" dependencies = [ "darling", "indoc", @@ -826,20 +847,23 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + [[package]] name = "litrs" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" -dependencies = [ - "proc-macro2", -] [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "macro-bits" @@ -882,9 +906,9 @@ dependencies = [ [[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", ] @@ -915,9 +939,9 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[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", ] @@ -962,12 +986,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" @@ -988,9 +1006,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", @@ -1001,9 +1019,9 @@ 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", @@ -1016,17 +1034,35 @@ 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.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc71814687c45ba4cd1e47a54e03a2dbc62ca3667098fbae9cc6b423956758fa" +checksum = "15c3138fdd8d128b2d81829842a3e0ce771b3712f7b6318ed1476b0695e7d330" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "riscv-target-parser" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1376b15f3ff160e9b1e8ea564ce427f2f6fcf77528cc0a8bf405cb476f9cea7" + [[package]] name = "rustversion" version = "1.0.21" @@ -1041,18 +1077,28 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +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 = "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", @@ -1072,6 +1118,43 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ee90f60ebcade244355ed4ff4077e364b251508c3462af386a9ee96c5a5492" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1157,18 +1240,31 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ "indexmap", "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ "winnow", ] @@ -1308,39 +1404,38 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] name = "xtensa-lx" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a564fffeb3cd773a524e8d8a5c66ca5e9739ea7450e36a3e6a54dd31f1e652f" +checksum = "e012d667b0aa6d2592ace8ef145a98bff3e76cca7a644f4181ecd7a916ed289b" dependencies = [ "critical-section", ] [[package]] name = "xtensa-lx-rt" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520a8fb0121eb6868f4f5ff383e262dc863f9042496724e01673a98a9b7e6c2b" +checksum = "8709f037fb123fe7ff146d2bce86f9dc0dfc53045c016bfd9d703317b6502845" dependencies = [ "document-features", - "r0", "xtensa-lx", "xtensa-lx-rt-proc-macros", ] [[package]] name = "xtensa-lx-rt-proc-macros" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a56a616147f5947ceb673790dd618d77b30e26e677f4a896df049d73059438" +checksum = "96fb42cd29c42f8744c74276e9f5bee7b06685bbe5b88df891516d72cb320450" dependencies = [ "proc-macro2", "quote", diff --git a/esp-wifi-hal/Cargo.toml b/esp-wifi-hal/Cargo.toml index 96348f1..616f2b4 100644 --- a/esp-wifi-hal/Cargo.toml +++ b/esp-wifi-hal/Cargo.toml @@ -11,16 +11,16 @@ repository = "https://github.com/esp32-open-mac/esp-wifi-hal" [dependencies] esp-wifi-sys = "=0.8.1" -esp-hal = { version = "=1.0.0-rc.0", features = ["unstable"] } -esp32 = { version = "0.38.0", optional = true, features = ["critical-section"] } -esp32s2 = { version = "0.29.0", optional = true, features = [ +esp-hal = { version = "=1.0.0-rc.1", features = ["unstable"] } +esp32 = { version = "0.39.0", optional = true, features = ["critical-section"] } +esp32s2 = { version = "0.30.0", optional = true, features = [ "critical-section", ] } # Embassy dependencies -embassy-futures = "0.1.1" -embassy-sync = "0.7.0" -embassy-time = "0.4.0" +embassy-futures = "0.1.2" +embassy-sync = "0.7.2" +embassy-time = "0.5.0" static_cell = { version = "2.1.1" } macro-bits = "0.1.5" @@ -29,8 +29,9 @@ cfg-if = "1.0.1" defmt-or-log = { version = "0.2.2", default-features = false } defmt = { version = "1.0.1", optional = true } bitfield-struct = "0.11.0" -log = { version = "0.4.27", optional = true } +log = { version = "0.4.28", optional = true } critical-section = "1.2.0" +esp-phy = "0.1.0" [profile.dev] # Rust debug is too slow. @@ -47,9 +48,9 @@ opt-level = 's' overflow-checks = false [features] -esp32 = ["dep:esp32", "esp-hal/esp32", "esp-wifi-sys/esp32"] -esp32s2 = ["dep:esp32s2", "esp-hal/esp32s2", "esp-wifi-sys/esp32s2"] +esp32 = ["dep:esp32", "esp-hal/esp32", "esp-wifi-sys/esp32", "esp-phy/esp32"] +esp32s2 = ["dep:esp32s2", "esp-hal/esp32s2", "esp-wifi-sys/esp32s2", "esp-phy/esp32s2"] -defmt = ["dep:defmt", "defmt-or-log/defmt"] -log = ["dep:log", "defmt-or-log/log"] +defmt = ["dep:defmt", "defmt-or-log/defmt", "esp-phy/defmt"] +log = ["dep:log", "defmt-or-log/log", "esp-phy/log-04"] critical_section = [] diff --git a/esp-wifi-hal/src/dma_list.rs b/esp-wifi-hal/src/dma_list.rs index aaae974..b87f9dd 100644 --- a/esp-wifi-hal/src/dma_list.rs +++ b/esp-wifi-hal/src/dma_list.rs @@ -119,7 +119,7 @@ impl DMAList { last_ptr: NonNull, ll_driver: &'static LowLevelDriver, ) -> Self { - ll_driver.set_base_rx_descriptor(base_ptr); + ll_driver.start_rx(base_ptr); trace!("Initialized DMA list."); Self { @@ -218,10 +218,10 @@ impl DMAList { #[allow(unused)] unsafe { let (rx_next, rx_last) = ( - self.ll_driver.next_rx_descriptor().map(|non_null| non_null.as_ptr()), - self.ll_driver.last_rx_descriptor().map(|non_null| non_null.as_ptr()), + self.ll_driver.next_rx_descriptor().map(|non_null| non_null.as_ptr() as u32), + self.ll_driver.last_rx_descriptor().map(|non_null| non_null.as_ptr() as u32), ); - trace!("DMA list: Next: {:x} Last: {:x}", rx_next, rx_last); + trace!("DMA list: Next: {:x?} Last: {:x?}", rx_next, rx_last); } } } diff --git a/esp-wifi-hal/src/ffi.rs b/esp-wifi-hal/src/ffi.rs index ff8244f..4130683 100644 --- a/esp-wifi-hal/src/ffi.rs +++ b/esp-wifi-hal/src/ffi.rs @@ -1,6 +1,6 @@ // These implementations are taken from esp-wifi. -use esp_hal::{ram, rtc_cntl::RtcClock}; +use esp_hal::{clock::RtcClock, ram}; #[cfg(osi_funcs_required)] #[ram] diff --git a/esp-wifi-hal/src/lib.rs b/esp-wifi-hal/src/lib.rs index 9b079b2..3a9a339 100644 --- a/esp-wifi-hal/src/lib.rs +++ b/esp-wifi-hal/src/lib.rs @@ -63,7 +63,6 @@ mod dma_list; mod ffi; #[doc(hidden)] pub mod ll; -mod phy_init_data; mod rates; mod sync; mod wmac; @@ -75,7 +74,7 @@ use esp32s2 as esp_pac; pub use crypto::*; pub use dma_list::WiFiResources; -pub use ll::{INTERFACE_COUNT, KEY_SLOT_COUNT}; +pub use ll::{INTERFACE_COUNT, KEY_SLOT_COUNT, RxFilterBank}; pub use rates::*; pub use wmac::*; diff --git a/esp-wifi-hal/src/ll.rs b/esp-wifi-hal/src/ll.rs index c886665..090e027 100644 --- a/esp-wifi-hal/src/ll.rs +++ b/esp-wifi-hal/src/ll.rs @@ -4,17 +4,17 @@ use core::{iter::IntoIterator, ptr::NonNull}; use esp_hal::{ - clock::{ModemClockController, PhyClockGuard}, + clock::ModemClockController, dma::DmaDescriptor, interrupt::{self, CpuInterrupt, InterruptHandler}, peripherals::{LPWR, WIFI as HalWIFI}, }; -use esp_wifi_sys::include::*; +use esp_phy::{PhyController, PhyInitGuard}; +use macro_bits::{bit, check_bit}; use crate::{ esp_pac::{wifi::crypto_key_slot::KEY_VALUE, Interrupt as PacInterrupt, WIFI}, ffi::{disable_wifi_agc, enable_wifi_agc, hal_init}, - phy_init_data::PHY_INIT_DATA_DEFAULT, }; /// Run a reversible sequence of functions either forward or in reverse. @@ -215,6 +215,47 @@ impl From for PacInterrupt { } } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +/// Status of a transmission event. +pub enum TxStatus { + /// TX completed successfully. + Success, + /// A timeout occured. + /// + /// This may be related to the timeout parameter. + Timeout, + /// A collision occured. + /// + /// This might be caused by internal TX queue scheduling conflicts. + Collision, +} +impl TxStatus { + pub fn raw_status(&self) -> u8 { + let wifi = LowLevelDriver::regs(); + (match self { + TxStatus::Success => wifi.txq_state().tx_complete_status().read().bits(), + TxStatus::Timeout => wifi.txq_state().tx_error_status().read().bits() >> 0x10, + TxStatus::Collision => wifi.txq_state().tx_error_status().read().bits(), + }) as u8 + } + pub fn clear_slot_bit(&self, slot: usize) { + let wifi = LowLevelDriver::regs(); + match self { + TxStatus::Success => wifi + .txq_state() + .tx_complete_clear() + .modify(|_, w| w.slot(slot as u8).set_bit()), + TxStatus::Timeout => wifi + .txq_state() + .tx_error_clear() + .modify(|_, w| w.slot_timeout(slot as u8).set_bit()), + TxStatus::Collision => wifi + .txq_state() + .tx_error_clear() + .modify(|_, w| w.slot_collision(slot as u8).set_bit()), + }; + } +} #[cfg(any(feature = "esp32", feature = "esp32s2"))] /// The number of "interfaces" supported by the hardware. @@ -240,20 +281,22 @@ pub const KEY_SLOT_COUNT: usize = 25; /// shared. When an index (i.e. key slot or interface) is provided, it is expected, that the input /// is within the valid range. If it is not, the function will panic. pub struct LowLevelDriver { - /// Prevents the PHY clock from being disabled, while the driver is running. - _phy_clock_guard: PhyClockGuard<'static>, + /// Prevents the PHY from being disabled, while the driver is running. + _phy_init_guard: PhyInitGuard<'static>, } impl LowLevelDriver { + #[doc(hidden)] /// Get the PAC Wi-Fi peripheral. /// /// This stops RA from doing funky shit. - fn regs() -> WIFI { + pub fn regs() -> WIFI { unsafe { WIFI::steal() } } /// Create a new [LowLevelDriver]. pub fn new(_wifi: esp_hal::peripherals::WIFI<'_>) -> Self { - let _phy_clock_guard = unsafe { Self::init() }; - Self { _phy_clock_guard } + Self { + _phy_init_guard: unsafe { Self::init() }, + } } // Initialization @@ -284,23 +327,6 @@ impl LowLevelDriver { enable_module, ); } - /// Initialize the PHY. - /// - /// NOTE: This is very temporary and will be replaced, as soon as `esp-phy` is released. - unsafe fn initialize_phy() -> PhyClockGuard<'static> { - let clock_guard = unsafe { HalWIFI::steal() }.enable_phy_clock(); - let mut cal_data = [0u8; size_of::()]; - let init_data = &PHY_INIT_DATA_DEFAULT; - trace!("Enabling PHY."); - unsafe { - register_chipv7_phy( - init_data, - &mut cal_data as *mut _ as *mut esp_phy_calibration_data_t, - esp_phy_calibration_mode_t_PHY_RF_CAL_FULL, - ); - } - clock_guard - } /// Perform a full MAC reset. /// /// # Safety @@ -389,15 +415,16 @@ impl LowLevelDriver { /// At the moment, this is only intended to be called upon initializing the low level driver. /// Reinitialization and partial PHY calibration are NOT handled at all. The effects of calling /// this multiple times are unknown. - unsafe fn init() -> PhyClockGuard<'static> { + unsafe fn init() -> PhyInitGuard<'static> { // Enable the power domain. unsafe { Self::set_module_status(true); } + let mut hal_wifi = unsafe { HalWIFI::steal() }; // Enable the modem clock. - unsafe { HalWIFI::steal() }.enable_modem_clock(true); + hal_wifi.enable_modem_clock(true); // Initialize the PHY. - let clock_guard = unsafe { Self::initialize_phy() }; + let phy_init_guard = hal_wifi.enable_phy(); unsafe { Self::reset_mac(); Self::set_mac_state(true); @@ -405,7 +432,7 @@ impl LowLevelDriver { } Self::init_crypto(); - clock_guard + phy_init_guard } // Interrupt handling @@ -603,6 +630,43 @@ impl LowLevelDriver { // TX + #[inline] + /// Process a TX status. + /// + /// The provided closure will be called for each slot, with its real index as a parameter. + /// + /// # Safety + /// This has to be static, so that it can be called in an ISR, but it is also expected to only + /// be called from there, since the Wi-Fi peripheral will have to be initialized there. + pub unsafe fn process_tx_status(tx_status: TxStatus, f: impl Fn(usize)) { + let raw_status = tx_status.raw_status(); + (0..5) + .filter(|i| check_bit!(raw_status, bit!(i))) + .for_each(|slot| { + (f)(slot); + tx_status.clear_slot_bit(slot); + }); + /* + match tx_status { + TxStatus::Success => { + let r = txq_state.tx_complete_status().read(); + Self::run_slot_closure(r.slot_iter(), f); + txq_state.tx_complete_clear().write(|w| unsafe { w.bits(r.bits()) }); + } + TxStatus::Timeout => { + let r = txq_state.tx_error_status().read(); + Self::run_slot_closure(r.slot_timeout_iter(), f); + txq_state.tx_error_clear().write(|w| unsafe { w.bits(r.bits() & 0x1f000000) }); + } + TxStatus::Collision => { + let r = txq_state.tx_error_status().read(); + Self::run_slot_closure(r.slot_collision_iter(), f); + txq_state.tx_error_clear().write(|w| unsafe { w.bits(r.bits() & 0x0000001f) }); + } + } + */ + } + // Crypto #[inline] @@ -806,52 +870,3 @@ pub unsafe fn set_tx_slot_validity(slot: usize, valid: bool) { .plcp0() .modify(|_, w| w.slot_valid().bit(valid)); } - -#[inline(always)] -pub unsafe fn process_tx_completions(mut cb: impl FnMut(usize)) { - let wifi = HalWIFI::regs(); - wifi.txq_state() - .tx_complete_status() - .read() - .slot_iter() - .enumerate() - .filter_map(|(slot, r)| r.bit().then_some(slot)) - .for_each(|slot| { - wifi.txq_state() - .tx_complete_clear() - .modify(|_, w| w.slot(slot as u8).set_bit()); - (cb)(slot); - }); -} -#[inline(always)] -pub unsafe fn process_tx_timeouts(mut cb: impl FnMut(usize)) { - let wifi = HalWIFI::regs(); - wifi.txq_state() - .tx_error_status() - .read() - .slot_timeout_iter() - .enumerate() - .filter_map(|(slot, r)| r.bit().then_some(slot)) - .for_each(|slot| { - wifi.txq_state() - .tx_error_clear() - .modify(|_, w| w.slot_timeout(slot as u8).set_bit()); - (cb)(slot); - }); -} -#[inline(always)] -pub unsafe fn process_tx_collisions(mut cb: impl FnMut(usize)) { - let wifi = HalWIFI::regs(); - wifi.txq_state() - .tx_error_status() - .read() - .slot_collision_iter() - .enumerate() - .filter_map(|(slot, r)| r.bit().then_some(slot)) - .for_each(|slot| { - wifi.txq_state() - .tx_error_clear() - .modify(|_, w| w.slot_collision(slot as u8).set_bit()); - (cb)(slot); - }); -} diff --git a/esp-wifi-hal/src/phy_init_data.rs b/esp-wifi-hal/src/phy_init_data.rs deleted file mode 100644 index b28845b..0000000 --- a/esp-wifi-hal/src/phy_init_data.rs +++ /dev/null @@ -1,146 +0,0 @@ -use esp_wifi_sys::include::esp_phy_init_data_t; - -const CONFIG_ESP32_PHY_MAX_TX_POWER: u8 = 20; - -const fn limit(val: u8, low: u8, high: u8) -> u8 { - if val < low { - low - } else if val > high { - high - } else { - val - } -} - -pub(crate) static PHY_INIT_DATA_DEFAULT: esp_phy_init_data_t = esp_phy_init_data_t { - params: [ - 3, - 3, - 0x05, - 0x09, - 0x06, - 0x05, - 0x03, - 0x06, - 0x05, - 0x04, - 0x06, - 0x04, - 0x05, - 0x00, - 0x00, - 0x00, - 0x00, - 0x05, - 0x09, - 0x06, - 0x05, - 0x03, - 0x06, - 0x05, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0xfc, - 0xfc, - 0xfe, - 0xf0, - 0xf0, - 0xf0, - 0xe0, - 0xe0, - 0xe0, - 0x18, - 0x18, - 0x18, - limit(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 40, 84), - limit(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 40, 72), - limit(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 40, 66), - limit(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 40, 60), - limit(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 40, 56), - limit(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 40, 52), - 0, - 1, - 1, - 2, - 2, - 3, - 4, - 5, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], -}; diff --git a/esp-wifi-hal/src/wmac.rs b/esp-wifi-hal/src/wmac.rs index 4f31dc8..09a86d7 100644 --- a/esp-wifi-hal/src/wmac.rs +++ b/esp-wifi-hal/src/wmac.rs @@ -8,23 +8,24 @@ use portable_atomic::{AtomicU16, AtomicU64, AtomicU8, Ordering}; use crate::{ esp_pac::wifi::TX_SLOT_CONFIG, - ll::{self, KeySlotParameters, LowLevelDriver, RxFilterBank, WiFiInterrupt}, + ll::{self, KeySlotParameters, LowLevelDriver, RxFilterBank, TxStatus, WiFiInterrupt}, rates::RATE_LUT, sync::{BorrowedTxSlot, TxSlotQueue, TxSlotStatus}, CipherParameters, WiFiRate, INTERFACE_COUNT, KEY_SLOT_COUNT, }; use embassy_sync::blocking_mutex::{self}; use esp_hal::{ - dma::{DmaDescriptor, Owner}, handler, interrupt::CpuInterrupt, peripherals::{ADC2, WIFI}, -}; -use esp_wifi_sys::include::{ - wifi_pkt_rx_ctrl_t, + dma::{DmaDescriptor, Owner}, + handler, + interrupt::CpuInterrupt, + peripherals::{ADC2, WIFI}, }; +use esp_wifi_sys::include::wifi_pkt_rx_ctrl_t; use macro_bits::{bit, check_bit}; use crate::{ dma_list::DMAList, - ffi::{tx_pwctrl_background}, + ffi::tx_pwctrl_background, sync::{SignalQueue, TxSlotStateSignal}, DefaultRawMutex, WiFiResources, }; @@ -51,14 +52,14 @@ fn mac_handler() { } if cause.tx_success() { unsafe { - ll::process_tx_completions(|slot| { + LowLevelDriver::process_tx_status(TxStatus::Success, |slot| { WIFI_TX_SLOTS[slot].signal(TxSlotStatus::Done); }) }; } if cause.tx_timeout() { unsafe { - ll::process_tx_timeouts(|slot| { + LowLevelDriver::process_tx_status(TxStatus::Timeout, |slot| { WIFI_TX_SLOTS[slot].signal(TxSlotStatus::Timeout); ll::set_tx_slot_validity(slot, false); ll::set_tx_slot_enabled(slot, false); @@ -67,7 +68,7 @@ fn mac_handler() { } if cause.tx_collision() { unsafe { - ll::process_tx_collisions(|slot| { + LowLevelDriver::process_tx_status(TxStatus::Collision, |slot| { WIFI_TX_SLOTS[slot].signal(TxSlotStatus::Collision); ll::set_tx_slot_validity(slot, false); ll::set_tx_slot_enabled(slot, false); @@ -148,12 +149,14 @@ impl BorrowedBuffer<'_> { self.padded_buffer() .get(..self.unpadded_buffer_len()) .unwrap_or_else(|| { - defmt_or_log::panic!( - "Corrected len: {} Padded len: {} SIG len: {}", + defmt_or_log::warn!( + "Buffer was shorter then calculated length. Corrected len: {} Padded len: {} SIG len: {} Header: {:02x?}", self.unpadded_buffer_len(), self.dma_descriptor.len(), - self.raw_header().sig_len() + self.raw_header().sig_len(), + &self.padded_buffer()[..Self::RX_CONTROL_HEADER_LENGTH] ); + self.padded_buffer() }) } /// Same as [Self::raw_buffer], but mutable. @@ -188,7 +191,7 @@ impl BorrowedBuffer<'_> { } /// The time at which the packet was received in µs. pub fn timestamp(&self) -> u32 { - u32::from_le_bytes(self.header_buffer()[12..16].try_into().unwrap()) + self.raw_header().timestamp() } /// The time the packet was received, corrected for the difference between MAC and system /// clock. @@ -337,10 +340,18 @@ impl<'res> WiFi<'res> { trace!("Initializing WiFi."); let ll_driver = LowLevelDriver::new(wifi); - ll_driver.configure_interrupt(WiFiInterrupt::Mac, CpuInterrupt::Interrupt0LevelPriority1, mac_handler); + ll_driver.configure_interrupt( + WiFiInterrupt::Mac, + CpuInterrupt::Interrupt0LevelPriority1, + mac_handler, + ); #[cfg(pwr_interrupt_present)] - ll_driver.configure_interrupt(WiFiInterrupt::Pwr, CpuInterrupt::Interrupt2LevelPriority1, pwr_handler); + ll_driver.configure_interrupt( + WiFiInterrupt::Pwr, + CpuInterrupt::Interrupt2LevelPriority1, + pwr_handler, + ); let (dma_list, ll_driver) = unsafe { wifi_resources.init(ll_driver) }; @@ -667,7 +678,7 @@ impl<'res> WiFi<'res> { pub fn set_filter_bssid_check(&self, interface: usize, enabled: bool) -> WiFiResult<()> { Self::validate_interface(interface)?; WIFI::regs() - .interface_rx_control(interface) + .filter_control(interface) .modify(|_, w| w.bssid_check().bit(enabled)); Ok(()) } @@ -678,7 +689,7 @@ impl<'res> WiFi<'res> { pub fn write_rx_policy_raw(&self, interface: usize, val: u32) -> WiFiResult<()> { Self::validate_interface(interface)?; WIFI::regs() - .interface_rx_control(interface) + .filter_control(interface) .write(|w| unsafe { w.bits(val) }); Ok(()) } @@ -688,7 +699,7 @@ impl<'res> WiFi<'res> { /// something else than that, you probably want to use one of the other functions. pub fn read_rx_policy_raw(&self, interface: usize) -> WiFiResult { Self::validate_interface(interface)?; - Ok(WIFI::regs().interface_rx_control(interface).read().bits()) + Ok(WIFI::regs().filter_control(interface).read().bits()) } /// Configure the filter. pub fn set_filter( @@ -699,9 +710,12 @@ impl<'res> WiFi<'res> { ) -> WiFiResult<()> { Self::validate_interface(interface)?; - self.ll_driver.set_filter_address(interface, filter_bank, &address); - self.ll_driver.set_filter_mask(interface, filter_bank, &[0xff; 6]); - self.ll_driver.set_filter_enable(interface, filter_bank, true); + self.ll_driver + .set_filter_address(interface, filter_bank, &address); + self.ll_driver + .set_filter_mask(interface, filter_bank, &[0xff; 6]); + self.ll_driver + .set_filter_enable(interface, filter_bank, true); Ok(()) } @@ -713,7 +727,7 @@ impl<'res> WiFi<'res> { ) -> WiFiResult<()> { Self::validate_interface(interface)?; WIFI::regs() - .interface_rx_control(interface) + .filter_control(interface) .modify(|_, w| match scanning_mode { ScanningMode::Disabled => { w.scan_mode().clear_bit().data_and_mgmt_mode().clear_bit() @@ -739,9 +753,7 @@ impl<'res> WiFi<'res> { /// /// If `key_slot` is larger than or equal to [WiFi::KEY_SLOT_COUNT], an error will be returned. pub fn key_slot_in_use(&self, key_slot: usize) -> WiFiResult { - Self::validate_key_slot(key_slot).map(|_| { - self.ll_driver.key_slot_enabled(key_slot) - }) + Self::validate_key_slot(key_slot).map(|_| self.ll_driver.key_slot_enabled(key_slot)) } /// Set a cryptographic key. /// diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 3bb313e..d3b6a4d 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -41,13 +41,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] -name = "basic-toml" -version = "0.1.10" +name = "base64" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" -dependencies = [ - "serde", -] +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "bitfield" @@ -66,7 +63,7 @@ checksum = "3787a07661997bfc05dd3431e379c0188573f78857080cf682e1393ab8e4d64c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -77,7 +74,7 @@ checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -103,9 +100,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -129,6 +126,12 @@ dependencies = [ "inout", ] +[[package]] +name = "const-default" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" + [[package]] name = "const_soft_float" version = "0.1.4" @@ -190,7 +193,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.101", ] [[package]] @@ -201,7 +204,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -239,7 +242,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -260,7 +263,7 @@ checksum = "d675dd299edbb7c8e01d4e9f520a0d8f22a8fe4af812c211c3fad5e9dcf41763" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -280,7 +283,7 @@ checksum = "b9b6483c2bbed26f97861cf57651d4f2b731964a28cd2257f934a4b452480d21" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -305,13 +308,13 @@ dependencies = [ [[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", @@ -322,32 +325,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", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] +[[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 = "1f878075b9794c1e4ac788c95b728f26aa6366d32eeb10c7051389f898f7d067" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" + +[[package]] +name = "embassy-hal-internal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a" +dependencies = [ + "num-traits", +] [[package]] name = "embassy-sync" @@ -357,31 +376,31 @@ 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]] name = "embassy-time" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f820157f198ada183ad62e0a66f554c610cdcd1a9f27d4b316358103ced7a1f8" +checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" dependencies = [ "cfg-if", "critical-section", @@ -390,42 +409,45 @@ dependencies = [ "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", - "futures-util", + "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.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970" +checksum = "17119855ccc2d1f7470a39756b12068454ae27a3eabb037d940b5c03d9c77b7a" +dependencies = [ + "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", ] @@ -469,13 +491,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]] @@ -511,7 +548,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -522,36 +559,42 @@ 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.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f270a29a3c4e492399b13e157b10151a7616cba69c4d554076ea93ed1bd2916" +checksum = "fdd8a541e17aa485d82df547f03c77b89f78cb110f59dea67cc90733d67e3678" dependencies = [ "cfg-if", + "document-features", "esp-config", + "esp-metadata-generated", "esp-println", - "heapless", + "heapless 0.9.1", + "riscv", "semihosting", + "xtensa-lx", ] [[package]] name = "esp-bootloader-esp-idf" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a093dbdc64b0288baacc214c2e8c2f3f13ecbf979c36ee2f63797ecf22538f1" +checksum = "c319c4a24fb44ef4c5f9854ff3dc6010eb8f15a6613d024227aa2355e3f6a334" dependencies = [ "cfg-if", "document-features", @@ -564,22 +607,22 @@ dependencies = [ [[package]] name = "esp-config" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd4a8db4b72794637a25944bc8d361c3cc271d4f03987ce8741312b6b61529c" +checksum = "289fde78fff1ff500e81efbdf958b1dae1614eacc61cc5560b0a3e03a25f266e" dependencies = [ "document-features", "esp-metadata-generated", - "evalexpr", "serde", "serde_yaml", + "somni-expr", ] [[package]] name = "esp-hal" -version = "1.0.0-rc.0" +version = "1.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3887eda2917deef3d99e7a5c324f9190714e99055361ad36890dffd0a995b49" +checksum = "f75242d788e67fc7ce51308019c0ff5d5103f989721577bb566b02710ef1ba79" dependencies = [ "bitfield", "bitflags 2.9.1", @@ -591,20 +634,23 @@ 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-config", "esp-hal-procmacros", "esp-metadata-generated", "esp-riscv-rt", "esp-rom-sys", + "esp-sync", "esp-synopsys-usb-otg", "esp32", "esp32c2", @@ -621,110 +667,121 @@ dependencies = [ "rand_core 0.6.4", "rand_core 0.9.3", "riscv", - "serde", + "sha1", + "sha2", "strum", "ufmt-write", "xtensa-lx", "xtensa-lx-rt", ] -[[package]] -name = "esp-hal-embassy" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d000d94064c485f86adc6b02b541e2f072e03321b4f03d4303b7ff3062c7e692" -dependencies = [ - "cfg-if", - "critical-section", - "document-features", - "embassy-executor", - "embassy-sync 0.6.2", - "embassy-time", - "embassy-time-driver", - "embassy-time-queue-utils", - "esp-config", - "esp-hal", - "esp-hal-procmacros", - "esp-metadata-generated", - "portable-atomic", - "static_cell", -] - [[package]] name = "esp-hal-procmacros" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbece384edaf0d1eabfa45afa96d910634d4158638ef983b2d419a8dec832246" +checksum = "9fd82a6506fb08d53a1086d165d92f085717aa9c59e67ac87a9e6f8acdcf6897" dependencies = [ "document-features", - "litrs", "object", "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.101", "termcolor", ] [[package]] -name = "esp-metadata" -version = "0.8.0" +name = "esp-metadata-generated" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6fbc1d166be84c0750f121e95c8989ddebd7e7bdd86af3594a6cfb34f039650" -dependencies = [ - "anyhow", - "basic-toml", - "indexmap", - "proc-macro2", - "quote", - "serde", - "strum", -] +checksum = "b18b1787dd3adea642fb529dd83fe558a08ace365bbaede4643a8959992900f4" [[package]] -name = "esp-metadata-generated" +name = "esp-phy" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189d36b8c8a752bdebec67fd02a15ebb1432feea345553749bca7ce2393cc795" +checksum = "62199c3e50eefcf53b8f3c690946582a124f7137b45bd1b14eb4b90a3fec2dd7" dependencies = [ - "esp-metadata", + "cfg-if", + "document-features", + "esp-config", + "esp-hal", + "esp-metadata-generated", + "esp-sync", + "esp-wifi-sys", + "log", ] [[package]] name = "esp-println" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e7e3ab41e96093d7fd307e93bfc88bd646a8ff23036ebf809e116b18869f719" +checksum = "6dcd18cbb132db6eb30d7c96bd831c3b6916894210f4528321f69fa66178b331" dependencies = [ - "critical-section", "document-features", "esp-metadata-generated", + "esp-sync", "log", "portable-atomic", ] [[package]] name = "esp-riscv-rt" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a00370dfcb0ccc01c6b2540076379c6efd6890a27f584de217c38e3239e19d5" +checksum = "502744a5b1e7268d27fd2a4e56ad45efe42ead517d6c517a6961540de949b0ee" dependencies = [ "document-features", "riscv", - "riscv-rt-macros", + "riscv-rt", ] [[package]] name = "esp-rom-sys" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "646aca2b30503b6c6f34250255fbd5887fd0c4104ea90802c1fea34f3035e7d6" +checksum = "01bafc39f59d56610b38ed0f63b16abeb8b357b8f546507d0d6c50c1a494f530" dependencies = [ "cfg-if", "document-features", "esp-metadata-generated", ] +[[package]] +name = "esp-rtos" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7844233ccdf92eaf4134c08c56c1b4a874f7d59e6249c579e912ecc6c9312fda" +dependencies = [ + "cfg-if", + "document-features", + "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-sync", + "portable-atomic", +] + +[[package]] +name = "esp-sync" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b977b028ae5959f0b2daf6602a1d751723b2d329c7251daf869ad382c8ed1543" +dependencies = [ + "cfg-if", + "document-features", + "embassy-sync 0.6.2", + "embassy-sync 0.7.2", + "esp-metadata-generated", + "riscv", + "xtensa-lx", +] + [[package]] name = "esp-synopsys-usb-otg" version = "0.4.2" @@ -747,9 +804,10 @@ dependencies = [ "critical-section", "defmt-or-log", "embassy-futures", - "embassy-sync 0.7.0", + "embassy-sync 0.7.2", "embassy-time", "esp-hal", + "esp-phy", "esp-wifi-sys", "esp32", "esp32s2", @@ -770,9 +828,9 @@ dependencies = [ [[package]] name = "esp32" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7680f79e3a4770e59c2dc25b17dcd852921ee57ffae9a4c4806c9ca5001d54d" +checksum = "b76170a463d18f888a1ad258031901036fd827a9ef126733053ba5f8739fb0c8" dependencies = [ "critical-section", "vcell", @@ -780,9 +838,9 @@ dependencies = [ [[package]] name = "esp32c2" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da1bcf86fca83543e0e95561cba27bbcc6b6e7adc5428f49187f5868bc0c3ed2" +checksum = "e62cf8932966b8d445b6f1832977b468178f0a84effb2e9fda89f60c24d45aa3" dependencies = [ "critical-section", "vcell", @@ -790,9 +848,9 @@ dependencies = [ [[package]] name = "esp32c3" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2c5a33d4377f974cbe8cadf8307f04f2c39755704cb09e81852c63ee4ac7b8" +checksum = "356af3771d0d6536c735bf71136594f4d1cbb506abf6e0c51a6639e9bf4e7988" dependencies = [ "critical-section", "vcell", @@ -800,9 +858,9 @@ dependencies = [ [[package]] name = "esp32c6" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ca8fc81b7164df58b5e04aaac9e987459312e51903cca807317990293973a6e" +checksum = "8f5e511df672d79cd63365c92045135e01ba952b6bddd25b660baff5e1110f6b" dependencies = [ "critical-section", "vcell", @@ -810,9 +868,9 @@ dependencies = [ [[package]] name = "esp32h2" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80171d08c17d8c63b53334c60ca654786a7593481531d19b639c4e5c76d276de" +checksum = "ed4a50bbd1380931e095e0973b9b12f782a9c481f2edf1f7c42e7eb4ff736d6d" dependencies = [ "critical-section", "vcell", @@ -820,9 +878,9 @@ dependencies = [ [[package]] name = "esp32s2" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c90d347480fca91f4be3e94b576af9c6c7987795c58dc3c5a7c108b6b3966dc" +checksum = "98574d4c577fbe888fe3e6df7fc80d25a05624d9998f7d7de1500ae21fcca78f" dependencies = [ "critical-section", "vcell", @@ -830,9 +888,9 @@ dependencies = [ [[package]] name = "esp32s3" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3769c56222c4548833f236c7009f1f8b3f2387af26366f6bd1cea456666a49d" +checksum = "1810d8ee4845ef87542af981e38eb80ab531d0ef1061e1486014ab7af74c337a" dependencies = [ "critical-section", "vcell", @@ -847,28 +905,22 @@ dependencies = [ "macro-bits", ] -[[package]] -name = "evalexpr" -version = "12.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a3229bec56a977f174b32fe7b8d89e8c79ebb4493d10ad763b6676dc2dc0c9" - [[package]] name = "examples" version = "0.1.0" dependencies = [ "embassy-executor", "embassy-futures", - "embassy-sync 0.7.0", + "embassy-sync 0.7.2", "embassy-time", - "embedded-io", - "embedded-io-async", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", "esp-alloc", "esp-backtrace", "esp-bootloader-esp-idf", "esp-hal", - "esp-hal-embassy", "esp-println", + "esp-rtos", "esp-wifi-hal", "ieee80211", "log", @@ -962,6 +1014,16 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heapless" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -1007,13 +1069,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", "hashbrown", - "serde", ] [[package]] @@ -1033,15 +1094,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" +checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" dependencies = [ "darling", "indoc", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1071,7 +1132,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1091,9 +1152,6 @@ name = "litrs" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" -dependencies = [ - "proc-macro2", -] [[package]] name = "llc-rs" @@ -1108,9 +1166,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "mac-parser" @@ -1160,7 +1218,7 @@ checksum = "113d1abd5bb3dc25a75d9b3a973f40e31eb03e0bae23c172b32cca4bcb9cfad2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1226,9 +1284,9 @@ dependencies = [ [[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", ] @@ -1278,9 +1336,9 @@ dependencies = [ [[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", ] @@ -1304,7 +1362,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1325,12 +1383,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" @@ -1351,9 +1403,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", @@ -1364,13 +1416,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.101", ] [[package]] @@ -1379,15 +1431,45 @@ 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.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc71814687c45ba4cd1e47a54e03a2dbc62ca3667098fbae9cc6b423956758fa" +checksum = "15c3138fdd8d128b2d81829842a3e0ce771b3712f7b6318ed1476b0695e7d330" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222fb240c3286247ecdee6fa5341e7cdad0ffdf8e7e401d9937f2d58482a20bf" +dependencies = [ + "cfg-if", + "const-default", + "libc", + "svgbobdoc", ] [[package]] @@ -1419,7 +1501,7 @@ checksum = "22fc4f90c27b57691bbaf11d8ecc7cfbfe98a4da6dbe60226115d322aa80c06e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1430,22 +1512,32 @@ checksum = "c3e1c7d2b77d80283c750a39c52f1ab4d17234e8f30bca43550f5b2375f41d5f" [[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.101", ] [[package]] @@ -1483,6 +1575,21 @@ dependencies = [ "digest", ] +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ee90f60ebcade244355ed4ff4077e364b251508c3462af386a9ee96c5a5492" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1523,7 +1630,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.101", ] [[package]] @@ -1532,6 +1639,30 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "svgbobdoc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c04b93fc15d79b39c63218f15e3fdffaa4c227830686e3b7c5f41244eb3e50" +dependencies = [ + "base64", + "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.101" @@ -1569,7 +1700,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] [[package]] @@ -1578,25 +1709,38 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6997294e7285bc4160c9bb47b49e120dec31b53143a7dc247392baa1d9d244d" dependencies = [ - "heapless", + "heapless 0.8.0", "no-panic", "scroll", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ "indexmap", "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ "winnow", ] @@ -1618,6 +1762,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 = "unsafe-libyaml" version = "0.2.11" @@ -1630,7 +1780,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" dependencies = [ - "heapless", + "heapless 0.8.0", "portable-atomic", ] @@ -1736,41 +1886,40 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] name = "xtensa-lx" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a564fffeb3cd773a524e8d8a5c66ca5e9739ea7450e36a3e6a54dd31f1e652f" +checksum = "e012d667b0aa6d2592ace8ef145a98bff3e76cca7a644f4181ecd7a916ed289b" dependencies = [ "critical-section", ] [[package]] name = "xtensa-lx-rt" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520a8fb0121eb6868f4f5ff383e262dc863f9042496724e01673a98a9b7e6c2b" +checksum = "8709f037fb123fe7ff146d2bce86f9dc0dfc53045c016bfd9d703317b6502845" dependencies = [ "document-features", - "r0", "xtensa-lx", "xtensa-lx-rt-proc-macros", ] [[package]] name = "xtensa-lx-rt-proc-macros" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a56a616147f5947ceb673790dd618d77b30e26e677f4a896df049d73059438" +checksum = "96fb42cd29c42f8744c74276e9f5bee7b06685bbe5b88df891516d72cb320450" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.101", ] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 643960e..c4e456a 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -10,9 +10,9 @@ license = "MIT OR Apache-2.0" esp-wifi-hal = { path = "../esp-wifi-hal/", features = ["log"] } # ESP-HAL dependencies -esp-hal = { version = "1.0.0-rc.0", features = ["unstable"] } -esp-alloc = "0.8.0" -esp-backtrace = { version = "0.17.0", features = ["panic-handler", "println"] } +esp-hal = { version = "1.0.0-rc.1", features = ["unstable"] } +esp-alloc = "0.9.0" +esp-backtrace = { version = "0.18.0", features = ["panic-handler", "println"] } # Embassy dependencies @@ -20,29 +20,29 @@ esp-backtrace = { version = "0.17.0", features = ["panic-handler", "println"] } log = "0.4.21" static_cell = { version = "2.1.0" } ieee80211 = { path = "../../../rust/ieee80211/" } -embedded-io-async = "0.6.1" +embedded-io-async = "0.7.0" embassy-futures = "0.1.1" -embassy-executor = { version = "0.7.0", features = ["task-arena-size-32768"] } -embassy-time = "0.4.0" -embassy-sync = "0.7.0" +embassy-executor = "0.9.1" +embassy-time = "0.5.0" +embassy-sync = "0.7.2" rand_core = "0.9.3" -embedded-io = "0.6.1" -esp-hal-embassy = "0.9.0" -esp-println = { version = "0.15.0", features = ["log-04"] } -esp-bootloader-esp-idf = "0.2.0" +embedded-io = "0.7.1" +esp-rtos = { version = "0.1.0", features = ["embassy"] } +esp-println = { version = "0.16.0", features = ["log-04"] } +esp-bootloader-esp-idf = "0.3.0" [features] esp32 = [ "esp-wifi-hal/esp32", "esp-hal/esp32", - "esp-hal-embassy/esp32", + "esp-rtos/esp32", "esp-backtrace/esp32", "esp-bootloader-esp-idf/esp32" ] esp32s2 = [ "esp-wifi-hal/esp32s2", "esp-hal/esp32s2", - "esp-hal-embassy/esp32s2", + "esp-rtos/esp32s2", "esp-backtrace/esp32s2", "esp-bootloader-esp-idf/esp32s2" ] diff --git a/examples/src/bin/ack_timeout_test_rx.rs b/examples/src/bin/ack_timeout_test_rx.rs index 6dad881..63bcdfa 100644 --- a/examples/src/bin/ack_timeout_test_rx.rs +++ b/examples/src/bin/ack_timeout_test_rx.rs @@ -11,7 +11,7 @@ use log::info; const OTHER_MAC_ADDRESS: MACAddress = MACAddress::new([0x00, 0x80, 0x41, 0x13, 0x37, 0x42]); const OWN_MAC_ADDRESS: MACAddress = MACAddress::new([0x00, 0x80, 0x41, 0x13, 0x37, 0x41]); -#[esp_hal_embassy::main] +#[esp_rtos::main] async fn main(_spawner: Spawner) { let peripherals = common_init(); embassy_init(peripherals.TIMG0); diff --git a/examples/src/bin/ack_timeout_test_tx.rs b/examples/src/bin/ack_timeout_test_tx.rs index 9b6e070..7fff152 100644 --- a/examples/src/bin/ack_timeout_test_tx.rs +++ b/examples/src/bin/ack_timeout_test_tx.rs @@ -22,7 +22,7 @@ macro_rules! mk_static { x }}; } -#[esp_hal_embassy::main] +#[esp_rtos::main] async fn main(_spawner: Spawner) { let peripherals = common_init(); embassy_init(peripherals.TIMG0); diff --git a/examples/src/bin/beacon.rs b/examples/src/bin/beacon.rs index 40f8299..ebc3b29 100644 --- a/examples/src/bin/beacon.rs +++ b/examples/src/bin/beacon.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Ticker}; use esp_backtrace as _; use esp_hal::efuse::Efuse; -use esp_hal_embassy::main; +use esp_rtos::main; use esp_wifi_hal::{TxParameters, WiFiRate}; use examples::{common_init, embassy_init, wifi_init}; use ieee80211::{ diff --git a/examples/src/bin/beacon_tsf_test_rx.rs b/examples/src/bin/beacon_tsf_test_rx.rs index da08cd4..b18be6e 100644 --- a/examples/src/bin/beacon_tsf_test_rx.rs +++ b/examples/src/bin/beacon_tsf_test_rx.rs @@ -14,7 +14,7 @@ use ieee80211::{ }; const SSID: &str = "BEACON TSF HIL TEST"; -#[esp_hal_embassy::main] +#[esp_rtos::main] async fn main(_spawner: Spawner) { let peripherals = common_init(); embassy_init(peripherals.TIMG0); diff --git a/examples/src/bin/beacon_tsf_test_tx.rs b/examples/src/bin/beacon_tsf_test_tx.rs index 0f1bd80..f13dc68 100644 --- a/examples/src/bin/beacon_tsf_test_tx.rs +++ b/examples/src/bin/beacon_tsf_test_tx.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use embassy_executor::Spawner; use embassy_time::{Duration, Ticker}; use esp_backtrace as _; -use esp_hal_embassy::main; +use esp_rtos::main; use esp_wifi_hal::{RxFilterBank, TxParameters, WiFiRate}; use examples::{common_init, embassy_init, setup_filters, wifi_init, AP_ADDRESS}; use ieee80211::{ diff --git a/examples/src/bin/crypto_rx.rs b/examples/src/bin/crypto_rx.rs index 3a43591..10ef547 100644 --- a/examples/src/bin/crypto_rx.rs +++ b/examples/src/bin/crypto_rx.rs @@ -10,7 +10,7 @@ use examples::{ use ieee80211::{crypto::MicState, data_frame::DataFrame, match_frames}; use log::info; -#[esp_hal_embassy::main] +#[esp_rtos::main] async fn main(_spawner: Spawner) { let peripherals = common_init(); embassy_init(peripherals.TIMG0); diff --git a/examples/src/bin/crypto_tx.rs b/examples/src/bin/crypto_tx.rs index bd72084..8676885 100644 --- a/examples/src/bin/crypto_tx.rs +++ b/examples/src/bin/crypto_tx.rs @@ -38,7 +38,7 @@ const PAIRWISE_TEMPLATE: DataFrame<'static, &[u8]> = DataFrameBuilder::new() .bssid(MACAddress(AP_ADDRESS)) .build(); -#[esp_hal_embassy::main] +#[esp_rtos::main] async fn main(_spawner: Spawner) { let peripherals = common_init(); embassy_init(peripherals.TIMG0); diff --git a/examples/src/bin/dup_test.rs b/examples/src/bin/dup_test.rs index f85b425..8474ae9 100644 --- a/examples/src/bin/dup_test.rs +++ b/examples/src/bin/dup_test.rs @@ -30,7 +30,7 @@ macro_rules! mk_static { }}; } -#[esp_hal_embassy::main] +#[esp_rtos::main] async fn main(_spawner: Spawner) { let peripherals = common_init(); embassy_init(peripherals.TIMG0); diff --git a/examples/src/bin/minimal.rs b/examples/src/bin/minimal.rs index 726c980..5ecf000 100644 --- a/examples/src/bin/minimal.rs +++ b/examples/src/bin/minimal.rs @@ -4,7 +4,7 @@ use embassy_executor::Spawner; use esp_backtrace as _; use examples::{common_init, embassy_init, wifi_init}; -#[esp_hal_embassy::main] +#[esp_rtos::main] async fn main(_spawner: Spawner) { let peripherals = common_init(); embassy_init(peripherals.TIMG0); diff --git a/examples/src/bin/multi_beacon.rs b/examples/src/bin/multi_beacon.rs index fde6d0e..c785530 100644 --- a/examples/src/bin/multi_beacon.rs +++ b/examples/src/bin/multi_beacon.rs @@ -7,7 +7,7 @@ use core::marker::PhantomData; use embassy_executor::Spawner; use embassy_time::Timer; use esp_backtrace as _; -use esp_hal_embassy::main; +use esp_rtos::main; use esp_wifi_hal::{TxParameters, WiFiRate}; use examples::{common_init, embassy_init, wifi_init}; use ieee80211::{ diff --git a/examples/src/bin/scanner.rs b/examples/src/bin/scanner.rs index d90d0d0..3570f3f 100644 --- a/examples/src/bin/scanner.rs +++ b/examples/src/bin/scanner.rs @@ -42,7 +42,7 @@ async fn scan_on_channel(wifi: &mut WiFi<'_>, known_ssids: &mut BTreeSet } } -#[esp_hal_embassy::main] +#[esp_rtos::main] async fn main(_spawner: Spawner) { let peripherals = common_init(); embassy_init(peripherals.TIMG0); diff --git a/examples/src/bin/test_cli.rs b/examples/src/bin/test_cli.rs index 9bd2e98..7c557b3 100644 --- a/examples/src/bin/test_cli.rs +++ b/examples/src/bin/test_cli.rs @@ -23,7 +23,7 @@ use esp_hal::{ uart::{Config, RxConfig, Uart, UartRx}, Async, }; -use esp_wifi_hal::{BorrowedBuffer, RxFilterBank, ScanningMode, TxParameters, WiFi, WiFiRate}; +use esp_wifi_hal::{BorrowedBuffer, RxFilterBank, ScanningMode, TxParameters, WiFi, WiFiRate, INTERFACE_COUNT}; use examples::{common_init, embassy_init, wifi_init}; use ieee80211::{ common::{CapabilitiesInformation, FrameType, ManagementFrameSubtype, TU}, @@ -275,7 +275,7 @@ fn parse_interface( let _ = writeln!( uart0_tx, "Expected argument [interface], valid banks are 0-{}", - WiFi::INTERFACE_COUNT + INTERFACE_COUNT ); None } @@ -287,7 +287,7 @@ fn filter_command<'a>( mut args: impl Iterator + 'a, ) { let bank = match args.next() { - Some("BSSID") => RxFilterBank::BSSID, + Some("BSSID") => RxFilterBank::Bssid, Some("RA") => RxFilterBank::ReceiverAddress, _ => { let _ = writeln!( @@ -310,13 +310,7 @@ fn filter_command<'a>( let _ = writeln!(uart0_tx, "The provided MAC address was invalid."); return; }; - let _ = wifi.set_filter(bank, interface, *mac_address, *BROADCAST); - } - Some("enable") => { - let _ = wifi.set_filter_status(bank, interface, true); - } - Some("disable") => { - let _ = wifi.set_filter_status(bank, interface, false); + let _ = wifi.set_filter(bank, interface, *mac_address); } None => { let _ = writeln!( @@ -576,7 +570,7 @@ fn find_last_codepoint(bytes: &[u8]) -> usize { 0 } */ -#[esp_hal_embassy::main] +#[esp_rtos::main] async fn main(_spawner: Spawner) { heap_allocator!(size: 32 * 1024); diff --git a/examples/src/lib.rs b/examples/src/lib.rs index 19ba332..27833d0 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs @@ -41,10 +41,8 @@ pub fn print_key<'buf>(key: &[u8; 16], buf: &'buf mut [u8; 32]) -> &'buf str { } /// Utility to set and enable the filters. pub fn setup_filters(wifi: &WiFi, ra: [u8; 6], bssid: [u8; 6]) { - let _ = wifi.set_filter(RxFilterBank::ReceiverAddress, 0, ra, [0xff; 6]); - let _ = wifi.set_filter(RxFilterBank::BSSID, 0, bssid, [0xff; 6]); - let _ = wifi.set_filter_status(RxFilterBank::ReceiverAddress, 0, true); - let _ = wifi.set_filter_status(RxFilterBank::BSSID, 0, true); + let _ = wifi.set_filter(RxFilterBank::ReceiverAddress, 0, ra); + let _ = wifi.set_filter(RxFilterBank::Bssid, 0, bssid); } /// Utility to set a key, with some basic parameters. pub fn insert_key( @@ -83,7 +81,7 @@ pub fn common_init() -> Peripherals { } pub fn embassy_init(timg0: TIMG0<'static>) { let timg0 = TimerGroup::<'static>::new(timg0); - esp_hal_embassy::init(timg0.timer0); + esp_rtos::start(timg0.timer0); } pub fn wifi_init<'a>(wifi: WIFI<'a>, adc2: ADC2<'a>) -> WiFi<'a> { static WIFI_RESOURCES: StaticCell> = StaticCell::new(); From 0e71a172bece32bfd54d3a0e17cf6922c21f0015 Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Sat, 1 Nov 2025 20:34:01 +0100 Subject: [PATCH 7/8] Working TX again --- esp-wifi-hal/Cargo.lock | 44 ++- esp-wifi-hal/Cargo.toml | 8 +- esp-wifi-hal/src/dma_list.rs | 15 +- esp-wifi-hal/src/lib.rs | 1 - esp-wifi-hal/src/ll.rs | 576 +++++++++++++++++++++++++------ esp-wifi-hal/src/rates.rs | 15 +- esp-wifi-hal/src/sync.rs | 55 ++- esp-wifi-hal/src/wmac.rs | 190 ++++------ examples/Cargo.lock | 81 +++-- examples/Cargo.toml | 16 +- examples/example.txt | 1 + examples/src/bin/beacon.rs | 2 +- examples/src/bin/test_cli.rs | 3 +- examples/src/bin/timeout_test.rs | 59 ++++ 14 files changed, 750 insertions(+), 316 deletions(-) create mode 100644 examples/example.txt create mode 100644 examples/src/bin/timeout_test.rs diff --git a/esp-wifi-hal/Cargo.lock b/esp-wifi-hal/Cargo.lock index 9fa5c6f..12c6584 100644 --- a/esp-wifi-hal/Cargo.lock +++ b/esp-wifi-hal/Cargo.lock @@ -36,9 +36,9 @@ dependencies = [ [[package]] name = "bitfield-struct" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" +checksum = "8769c4854c5ada2852ddf6fd09d15cf43d4c2aaeccb4de6432f5402f08a6003b" dependencies = [ "proc-macro2", "quote", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "defmt-or-log" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4439fab1ae9faccf0f69cdf2671989ba40f246e178c7f87d6e4fd5270e53a979" +checksum = "be59789d306cc1a082f53bb7c24c270d7e6078c00b7550a718219a7090156d97" dependencies = [ "defmt", "defmt-or-log-macros", @@ -456,12 +456,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "esp-config" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "289fde78fff1ff500e81efbdf958b1dae1614eacc61cc5560b0a3e03a25f266e" +checksum = "102871054f8dd98202177b9890cb4b71d0c6fe1f1413b7a379a8e0841fc2473c" dependencies = [ "document-features", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "serde", "serde_yaml", "somni-expr", @@ -469,9 +469,9 @@ dependencies = [ [[package]] name = "esp-hal" -version = "1.0.0-rc.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75242d788e67fc7ce51308019c0ff5d5103f989721577bb566b02710ef1ba79" +checksum = "54786287c0a61ca0f78cb0c338a39427551d1be229103b4444591796c579e093" dependencies = [ "bitfield", "bitflags 2.9.1", @@ -496,7 +496,7 @@ dependencies = [ "enumset", "esp-config", "esp-hal-procmacros", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "esp-riscv-rt", "esp-rom-sys", "esp-sync", @@ -526,9 +526,9 @@ dependencies = [ [[package]] name = "esp-hal-procmacros" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd82a6506fb08d53a1086d165d92f085717aa9c59e67ac87a9e6f8acdcf6897" +checksum = "3e025a7a7a0affdb4ff913b5c4494aef96ee03d085bf83c27453ae3a71d50da6" dependencies = [ "document-features", "object", @@ -545,18 +545,24 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b18b1787dd3adea642fb529dd83fe558a08ace365bbaede4643a8959992900f4" +[[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.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62199c3e50eefcf53b8f3c690946582a124f7137b45bd1b14eb4b90a3fec2dd7" +checksum = "6b1facf348e1e251517278fc0f5dc134e95e518251f5796cfbb532ca226a29bf" dependencies = [ "cfg-if", "defmt", "document-features", "esp-config", "esp-hal", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "esp-sync", "esp-wifi-sys", "log", @@ -581,20 +587,20 @@ checksum = "01bafc39f59d56610b38ed0f63b16abeb8b357b8f546507d0d6c50c1a494f530" dependencies = [ "cfg-if", "document-features", - "esp-metadata-generated", + "esp-metadata-generated 0.2.0", ] [[package]] name = "esp-sync" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b977b028ae5959f0b2daf6602a1d751723b2d329c7251daf869ad382c8ed1543" +checksum = "d44974639b4e88914f83fe60d2832c00276657d7d857628fdfc966cc7302e8a8" dependencies = [ "cfg-if", "document-features", "embassy-sync 0.6.2", "embassy-sync 0.7.2", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "riscv", "xtensa-lx", ] diff --git a/esp-wifi-hal/Cargo.toml b/esp-wifi-hal/Cargo.toml index 616f2b4..5d2c61f 100644 --- a/esp-wifi-hal/Cargo.toml +++ b/esp-wifi-hal/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/esp32-open-mac/esp-wifi-hal" [dependencies] esp-wifi-sys = "=0.8.1" -esp-hal = { version = "=1.0.0-rc.1", features = ["unstable"] } +esp-hal = { version = "=1.0.0", features = ["unstable"] } esp32 = { version = "0.39.0", optional = true, features = ["critical-section"] } esp32s2 = { version = "0.30.0", optional = true, features = [ "critical-section", @@ -26,12 +26,12 @@ static_cell = { version = "2.1.1" } macro-bits = "0.1.5" portable-atomic = { version = "1.11.1", default-features = false } cfg-if = "1.0.1" -defmt-or-log = { version = "0.2.2", default-features = false } +defmt-or-log = { version = "0.2.3", default-features = false } defmt = { version = "1.0.1", optional = true } -bitfield-struct = "0.11.0" +bitfield-struct = "0.12.1" log = { version = "0.4.28", optional = true } critical-section = "1.2.0" -esp-phy = "0.1.0" +esp-phy = "0.1.1" [profile.dev] # Rust debug is too slow. diff --git a/esp-wifi-hal/src/dma_list.rs b/esp-wifi-hal/src/dma_list.rs index b87f9dd..82138ea 100644 --- a/esp-wifi-hal/src/dma_list.rs +++ b/esp-wifi-hal/src/dma_list.rs @@ -1,12 +1,10 @@ use crate::{ - ll::LowLevelDriver, - DefaultRawMutex, + ll::LowLevelDriver, BorrowedBuffer, DefaultRawMutex }; use core::{cell::RefCell, mem::MaybeUninit, ptr::NonNull}; use embassy_sync::blocking_mutex; use esp_hal::dma::{DmaDescriptor, DmaDescriptorFlags, Owner}; -use esp_wifi_sys::include::wifi_pkt_rx_ctrl_t; trait DmaDescriptorExt { fn next(&mut self) -> Option<&mut DmaDescriptor>; @@ -98,7 +96,6 @@ impl WiFiResources { core::mem::transmute::<&LowLevelDriver, &'static LowLevelDriver>(ll_driver) }, )))); - (dma_list, ll_driver) } } @@ -108,6 +105,7 @@ impl Default for WiFiResources { } } +/// The receive DMA list. pub struct DMAList { rx_chain_ptrs: Option<(NonNull, NonNull)>, ll_driver: &'static LowLevelDriver, @@ -132,7 +130,8 @@ impl DMAList { /// This will automatically reload the RX descriptors. fn set_rx_chain_base(&mut self, base_descriptor: Option>) { if let Some(base_descriptor) = base_descriptor { - if let Some(mut rx_chain_ptrs) = self.rx_chain_ptrs { + // This needs to use `as_mut`, since otherwise we modify a copy of the tuple. + if let Some(rx_chain_ptrs) = self.rx_chain_ptrs.as_mut() { // If neither the DMA list nor the DMA list descriptor is empty, we simply set rx_chain_begin to dma_list_desciptor. rx_chain_ptrs.0 = base_descriptor; } else { @@ -152,10 +151,10 @@ impl DMAList { pub fn take_first(&mut self) -> Option<&'static mut DmaDescriptor> { let first = unsafe { self.rx_chain_ptrs?.0.as_mut() }; trace!("Taking buffer: {:x} from DMA list.", first as *mut _ as u32); - if first.flags.suc_eof() && first.len() >= size_of::() { + if first.flags.suc_eof() && first.len() >= BorrowedBuffer::RX_CONTROL_HEADER_LENGTH { let next = first.next(); if next.is_none() { - trace!("Next was none."); + warn!("Next was none."); }; self.set_rx_chain_base(next.map(NonNull::from)); first.set_owner(Owner::Cpu); @@ -221,7 +220,7 @@ impl DMAList { self.ll_driver.next_rx_descriptor().map(|non_null| non_null.as_ptr() as u32), self.ll_driver.last_rx_descriptor().map(|non_null| non_null.as_ptr() as u32), ); - trace!("DMA list: Next: {:x?} Last: {:x?}", rx_next, rx_last); + info!("DMA list: Next: {:x?} Last: {:x?}", rx_next, rx_last); } } } diff --git a/esp-wifi-hal/src/lib.rs b/esp-wifi-hal/src/lib.rs index 3a9a339..f7f9e10 100644 --- a/esp-wifi-hal/src/lib.rs +++ b/esp-wifi-hal/src/lib.rs @@ -61,7 +61,6 @@ extern crate core; mod crypto; mod dma_list; mod ffi; -#[doc(hidden)] pub mod ll; mod rates; mod sync; diff --git a/esp-wifi-hal/src/ll.rs b/esp-wifi-hal/src/ll.rs index 090e027..8604901 100644 --- a/esp-wifi-hal/src/ll.rs +++ b/esp-wifi-hal/src/ll.rs @@ -1,7 +1,11 @@ //! Low Level functions. //! //! All functions in this module are unsafe, since their effect may be very context dependent. -use core::{iter::IntoIterator, ptr::NonNull}; +use core::{ + iter::IntoIterator, + pin::Pin, + ptr::{with_exposed_provenance_mut, NonNull}, +}; use esp_hal::{ clock::ModemClockController, @@ -14,7 +18,8 @@ use macro_bits::{bit, check_bit}; use crate::{ esp_pac::{wifi::crypto_key_slot::KEY_VALUE, Interrupt as PacInterrupt, WIFI}, - ffi::{disable_wifi_agc, enable_wifi_agc, hal_init}, + ffi::{disable_wifi_agc, enable_wifi_agc, hal_init, tx_pwctrl_background}, + WiFiRate, }; /// Run a reversible sequence of functions either forward or in reverse. @@ -185,7 +190,7 @@ interrupt_cause_struct! { /// /// NOTE: This is an unconfirmed assumption. tbtt => [@chip(esp32s2) 0x1e], - // TODO: No idea. + /// We don't know them meaning of this yet. tsf_timer => [@chip(esp32s2) 0x1e0] } } @@ -216,8 +221,10 @@ impl From for PacInterrupt { } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -/// Status of a transmission event. -pub enum TxStatus { +/// Result of the transmission. +/// +/// This is separate from the MAC TX result, which accounts for ACK timeouts etc. +pub enum HardwareTxResult { /// TX completed successfully. Success, /// A timeout occured. @@ -229,33 +236,219 @@ pub enum TxStatus { /// This might be caused by internal TX queue scheduling conflicts. Collision, } -impl TxStatus { - pub fn raw_status(&self) -> u8 { +impl HardwareTxResult { + /// Get raw transmission status bits. + fn raw_status(&self) -> u8 { let wifi = LowLevelDriver::regs(); (match self { - TxStatus::Success => wifi.txq_state().tx_complete_status().read().bits(), - TxStatus::Timeout => wifi.txq_state().tx_error_status().read().bits() >> 0x10, - TxStatus::Collision => wifi.txq_state().tx_error_status().read().bits(), + HardwareTxResult::Success => wifi.txq_state().tx_complete_status().read().bits(), + HardwareTxResult::Timeout => wifi.txq_state().tx_error_status().read().bits() >> 0x10, + HardwareTxResult::Collision => wifi.txq_state().tx_error_status().read().bits(), }) as u8 } - pub fn clear_slot_bit(&self, slot: usize) { + /// Clear slot transmission status bit. + fn clear_slot_bit(&self, slot: usize) { let wifi = LowLevelDriver::regs(); match self { - TxStatus::Success => wifi + HardwareTxResult::Success => wifi .txq_state() .tx_complete_clear() .modify(|_, w| w.slot(slot as u8).set_bit()), - TxStatus::Timeout => wifi + HardwareTxResult::Timeout => wifi .txq_state() .tx_error_clear() .modify(|_, w| w.slot_timeout(slot as u8).set_bit()), - TxStatus::Collision => wifi + HardwareTxResult::Collision => wifi .txq_state() .tx_error_clear() .modify(|_, w| w.slot_collision(slot as u8).set_bit()), }; } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +/// Status of the TX slot. +pub enum TxSlotStatus { + /// The slot is currently disabled and the data in it may be invalid. + Disabled, + /// The data in the slot is valid, but the slot is not marked as ready yet. + Valid, + /// The data in the slot is valid and it is ready for transmission. + Ready, +} +impl TxSlotStatus { + /// Is the data in the slot valid. + fn valid(&self) -> bool { + *self != Self::Disabled + } + /// Is the slot enabled for transmission. + fn enabled(&self) -> bool { + *self == Self::Ready + } +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +/// Controls which control frames pass the filter. +pub struct ControlFrameFilterConfig { + /// Control frame wrapper + pub control_wrapper: bool, + /// Block ACK request (BAR) + pub block_ack_request: bool, + /// Block ACK (BA) + pub block_ack: bool, + /// PS-Poll + pub ps_poll: bool, + /// Request-to-send (RTS) + pub rts: bool, + /// Clear-to-send (CTS) + pub cts: bool, + /// ACK + pub ack: bool, + /// Control frame end + pub cf_end: bool, + /// Control frame end and ACK + pub cf_end_cf_ack: bool, +} +impl ControlFrameFilterConfig { + /// Disable reception of all control frames. + pub fn none() -> Self { + Self::default() + } + /// Receive all control frames. + pub fn all() -> Self { + Self { + control_wrapper: true, + block_ack_request: true, + block_ack: true, + ps_poll: true, + rts: true, + cts: true, + ack: true, + cf_end: true, + cf_end_cf_ack: true, + } + } +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +/// The EDCA access category. +/// +/// It maps the EDCA access category to the hardware TX slot number. +pub enum EdcaAccessCategory { + /// BA-AC + Background = 1, + #[default] + /// BE-AC + BestEffort = 2, + /// VI-AC + Video = 3, + /// VO-AC + Voice = 4, +} +impl EdcaAccessCategory { + #[inline] + /// Get the IEEE 802.11 Access Category Index (ACI). + pub const fn access_category_index(&self) -> usize { + match *self { + Self::Background => 1, + Self::BestEffort => 0, + Self::Video => 2, + Self::Voice => 3, + } + } + #[inline] + /// Convert the Access Category Index (ACI) into our AC type. + pub const fn from_access_category_index(access_category_index: usize) -> Option { + Some(match access_category_index { + 1 => Self::Background, + 0 => Self::BestEffort, + 2 => Self::Video, + 3 => Self::Voice, + _ => return None, + }) + } + #[inline] + /// Get the hardware slot number. + /// + /// NOTE: This slot numbering does not line up, with the one used by Espressif, as we index the + /// slots in ascending address order (i.e. slot 0 registers have the lowest addresses), not + /// descending order (i.e. slot 0 register have the highest addresses), which is used in the + /// proprietary stack. This makes no difference in practice, other than being subjectively less + /// confusing. + pub const fn hardware_slot(&self) -> usize { + *self as usize + } + #[inline] + /// Get the EDCA queue from the slot number. + /// + /// NOTE: This slot numbering does not line up, with the one used by Espressif, as we index the + /// slots in ascending address order (i.e. slot 0 registers have the lowest addresses), not + /// descending order (i.e. slot 0 register have the highest addresses), which is used in the + /// proprietary stack. This makes no difference in practice, other than being subjectively less + /// confusing. + pub const fn from_hardware_slot(slot: usize) -> Option { + Some(match slot { + 1 => Self::Background, + 2 => Self::BestEffort, + 3 => Self::Video, + 4 => Self::Voice, + _ => return None, + }) + } +} +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +/// The hardware TX queues. +pub enum HardwareTxQueue { + #[default] + /// Distributed Channel Access Function + /// + /// This is the default channel access function. + Dcf, + /// Enhanced Distributed Channel Access Function + /// + /// This is the modified channel access function used for QoS traffic. + Edcaf(EdcaAccessCategory), +} +impl HardwareTxQueue { + #[inline] + /// Get the hardware slot number. + /// + /// NOTE: This slot numbering does not line up, with the one used by Espressif, as we index the + /// slots in ascending address order (i.e. slot 0 registers have the lowest addresses), not + /// descending order (i.e. slot 0 register have the highest addresses), which is used in the + /// proprietary stack. This makes no difference in practice, other than being subjectively less + /// confusing. + pub const fn hardware_slot(&self) -> usize { + match *self { + Self::Dcf => 4, + Self::Edcaf(access_category) => access_category.hardware_slot(), + } + } + #[inline] + /// Get the queue from the slot number. + /// + /// NOTE: This slot numbering does not line up, with the one used by Espressif, as we index the + /// slots in ascending address order (i.e. slot 0 registers have the lowest addresses), not + /// descending order (i.e. slot 0 register have the highest addresses), which is used in the + /// proprietary stack. This makes no difference in practice, other than being subjectively less + /// confusing. + pub const fn from_hardware_slot(slot: usize) -> Option { + if slot == 0 { + Some(Self::Dcf) + } else if let Some(edca_ac) = EdcaAccessCategory::from_hardware_slot(slot) { + Some(Self::Edcaf(edca_ac)) + } else { + None + } + } + #[inline] + /// Is the queue an EDCA queue. + pub const fn is_edca(&self) -> bool { + matches!(self, Self::Edcaf(_)) + } +} #[cfg(any(feature = "esp32", feature = "esp32s2"))] /// The number of "interfaces" supported by the hardware. @@ -269,6 +462,15 @@ pub const KEY_SLOT_COUNT: usize = 25; /// This is intended as an intermediary layer between the user facing API and the hardware, to make /// driver maintenance easier. /// +/// # Implementation specifics +/// ## TX +/// ### Slot Numbering +/// The hardware has 5 TX slots +/// The slot numbering used for TX is aligned with that of the proprietary stack, not with the +/// hardware. This means, that our slot numbering is reversed compared to the hardware slot +/// registers, as this reduces confusion when reverse engineering. Our slot 0 would therefore be +/// the highest slot in the hardware (i.e. slot 4). +/// /// # Safety /// Pretty much all of the functions on this must be used with extreme caution, as the intention is /// not to provide a fool proof API for the user, but a thin abstraction over the hardware, to make @@ -282,20 +484,25 @@ pub const KEY_SLOT_COUNT: usize = 25; /// is within the valid range. If it is not, the function will panic. pub struct LowLevelDriver { /// Prevents the PHY from being disabled, while the driver is running. - _phy_init_guard: PhyInitGuard<'static>, + _phy_init_guard: Option>, } impl LowLevelDriver { #[doc(hidden)] /// Get the PAC Wi-Fi peripheral. /// - /// This stops RA from doing funky shit. + /// This stops rust-analyzer from doing funky shit. pub fn regs() -> WIFI { unsafe { WIFI::steal() } } /// Create a new [LowLevelDriver]. pub fn new(_wifi: esp_hal::peripherals::WIFI<'_>) -> Self { - Self { - _phy_init_guard: unsafe { Self::init() }, + // The reason we first create the struct an then initialize it, is so that we can access + // methods during init. + unsafe { + Self { + _phy_init_guard: None, + } + .init() } } @@ -306,7 +513,7 @@ impl LowLevelDriver { /// # Safety /// Enabling or disabling the Wi-Fi PD, outside of init or deinit, breaks a lot of assumptions /// other code makes, so take great care. - unsafe fn set_module_status(enable_module: bool) { + unsafe fn set_module_status(&self, enable_module: bool) { // For newer chips, we also have to handle the reset of the RF modules. run_reversible_function_sequence( [ @@ -332,7 +539,7 @@ impl LowLevelDriver { /// # Safety /// This will basically reset all state of the MAC to its defaults. Therefore it may also break /// assumptions made by some pieces of code. - unsafe fn reset_mac() { + unsafe fn reset_mac(&self) { // Perform a full reset of the Wi-Fi module. unsafe { HalWIFI::steal() }.reset_wifi_mac(); // NOTE: coex_bt_high_prio would usually be here @@ -348,7 +555,7 @@ impl LowLevelDriver { /// # Safety /// We don't really know, what exactly this does, so we only call it, where the closed source /// driver would. - unsafe fn set_mac_state(intialized: bool) { + unsafe fn set_mac_state(&self, intialized: bool) { // TODO: Figure out, what these registers do precisely and move them into the PAC. cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { @@ -385,16 +592,17 @@ impl LowLevelDriver { /// /// # Safety /// This should only be called during init. - unsafe fn setup_mac() { + unsafe fn setup_mac(&self) { // We call the proprietary blob here, to do some init for us. In the long term, this will // be moved to open source code. In the meantime, we do the init, that we already understand a - // second time in open source code + // second time in open source code, which should have no bad effects. unsafe { hal_init(); } // Open source setup code - Self::init_crypto(); + self.setup_filtering(); + self.setup_crypto(); } /// Initialize the hardware. /// @@ -415,24 +623,22 @@ impl LowLevelDriver { /// At the moment, this is only intended to be called upon initializing the low level driver. /// Reinitialization and partial PHY calibration are NOT handled at all. The effects of calling /// this multiple times are unknown. - unsafe fn init() -> PhyInitGuard<'static> { + unsafe fn init(mut self) -> Self { // Enable the power domain. unsafe { - Self::set_module_status(true); + self.set_module_status(true); } let mut hal_wifi = unsafe { HalWIFI::steal() }; // Enable the modem clock. hal_wifi.enable_modem_clock(true); // Initialize the PHY. - let phy_init_guard = hal_wifi.enable_phy(); + self._phy_init_guard = Some(hal_wifi.enable_phy()); unsafe { - Self::reset_mac(); - Self::set_mac_state(true); - Self::setup_mac(); + self.reset_mac(); + self.set_mac_state(true); + self.setup_mac(); } - Self::init_crypto(); - - phy_init_guard + self } // Interrupt handling @@ -518,12 +724,13 @@ impl LowLevelDriver { Self::regs() .rx_dma_list() .rx_descr_base() - .write(|w| unsafe { w.bits(base_descriptor.as_ptr() as u32) }); + .write(|w| unsafe { w.bits(base_descriptor.expose_provenance().get() as u32) }); self.reload_hw_rx_descriptors(); } /// Reset the base descriptor. pub fn clear_base_rx_descriptor(&self) { Self::regs().rx_dma_list().rx_descr_base().reset(); + self.reload_hw_rx_descriptors(); } /// Start receiving frames. /// @@ -545,15 +752,21 @@ impl LowLevelDriver { } /// Get the base RX descriptor. pub fn base_rx_descriptor(&self) -> Option> { - NonNull::new(Self::regs().rx_dma_list().rx_descr_base().read().bits() as *mut DmaDescriptor) + NonNull::new(with_exposed_provenance_mut( + Self::regs().rx_dma_list().rx_descr_base().read().bits() as usize, + )) } /// Get the next RX descriptor. pub fn next_rx_descriptor(&self) -> Option> { - NonNull::new(Self::regs().rx_dma_list().rx_descr_next().read().bits() as *mut DmaDescriptor) + NonNull::new(with_exposed_provenance_mut( + Self::regs().rx_dma_list().rx_descr_next().read().bits() as usize, + )) } /// Get the last RX descriptor. pub fn last_rx_descriptor(&self) -> Option> { - NonNull::new(Self::regs().rx_dma_list().rx_descr_last().read().bits() as *mut DmaDescriptor) + NonNull::new(with_exposed_provenance_mut( + Self::regs().rx_dma_list().rx_descr_last().read().bits() as usize, + )) } // RX filtering @@ -618,6 +831,9 @@ impl LowLevelDriver { /// reset the [mask_high](crate::esp_pac::wifi::filter_bank::FILTER_BANK::mask_high) field, /// saving us one read. This also prevents keeping the slot enabled, while zeroing it, which /// would be invalid regardless. + /// + /// This will also enable the BSSID check, to bring the filter into a sort of ground state, + /// so that we can make assumptions about its behaviour when configuring other aspects. pub fn clear_filter_bank(&self, interface: usize, filter_bank: RxFilterBank) { let wifi = Self::regs(); let filter_bank = wifi.filter_bank(filter_bank.into_bits()); @@ -627,72 +843,247 @@ impl LowLevelDriver { filter_bank.mask_low(interface).reset(); filter_bank.mask_high(interface).reset(); } + /// Reset the filters for an interface. + /// + /// This will clear the RX and BSSID filters, as well as enabling the BSSID check and enabling + /// filtering for unicast and multicast frames. + pub fn reset_interface_filters(&self, interface: usize) { + self.clear_filter_bank(interface, RxFilterBank::ReceiverAddress); + self.clear_filter_bank(interface, RxFilterBank::Bssid); + + self.set_bssid_check_enable(interface, true); + self.set_filtered_address_types(interface, true, true); + } + /// Set the type of addresses that should be filtered. + /// + /// If an address type is unfiltered, frames with RAs matching that address type will pass the + /// filter unconditionally. Otherwise the frame will be filtered as usual. + pub fn set_filtered_address_types(&self, interface: usize, unicast: bool, multicast: bool) { + Self::regs().filter_control(interface).modify(|_, w| { + w.block_unicast() + .bit(unicast) + .block_multicast() + .bit(multicast) + }); + } + /// Configure if the BSSID should be checked for RX filtering. + /// + /// If the BSSID check is enabled, a frame will only pass the RX filter, if its BSSID matches + /// the one in the filter. By default this is enabled. + pub fn set_bssid_check_enable(&self, interface: usize, enabled: bool) { + Self::regs() + .filter_control(interface) + .modify(|_, w| w.bssid_check().bit(enabled)); + } + /// Configure which control frames pass the filter. + pub fn set_control_frame_filter(&self, interface: usize, config: &ControlFrameFilterConfig) { + Self::regs().rx_ctrl_filter(interface).modify(|_, w| { + w.control_wrapper() + .bit(config.control_wrapper) + .block_ack_request() + .bit(config.block_ack_request) + .block_ack() + .bit(config.block_ack) + .ps_poll() + .bit(config.ps_poll) + .rts() + .bit(config.rts) + .cts() + .bit(config.cts) + .ack() + .bit(config.ack) + .cf_end() + .bit(config.cf_end) + .cf_end_cf_ack() + .bit(config.cf_end_cf_ack) + }); + } + #[inline] + /// Setup RX filtering. + /// + /// This will bring the filters to a known-good state, by enabling filtering for unicast and + /// multicast frames, filtering all control frames and enabling the BSSID check. + fn setup_filtering(&self) { + (0..4).for_each(|interface| { + self.set_filtered_address_types(interface, true, true); + self.set_control_frame_filter(interface, &ControlFrameFilterConfig::none()); + self.set_bssid_check_enable(interface, true); + }); + } // TX #[inline] /// Process a TX status. /// - /// The provided closure will be called for each slot, with its real index as a parameter. + /// The provided closure will be called for each slot, with the [HardwareTxQueue] as a + /// parameter. /// /// # Safety /// This has to be static, so that it can be called in an ISR, but it is also expected to only /// be called from there, since the Wi-Fi peripheral will have to be initialized there. - pub unsafe fn process_tx_status(tx_status: TxStatus, f: impl Fn(usize)) { + pub unsafe fn process_tx_status(tx_status: HardwareTxResult, f: impl Fn(HardwareTxQueue)) { let raw_status = tx_status.raw_status(); (0..5) .filter(|i| check_bit!(raw_status, bit!(i))) - .for_each(|slot| { - (f)(slot); - tx_status.clear_slot_bit(slot); + .for_each(|queue| { + // For some reason the slot numbering needs to be reversed when working with the + // interrupt cause register. + (f)(HardwareTxQueue::from_hardware_slot(4 - queue).unwrap()); + tx_status.clear_slot_bit(queue); }); - /* - match tx_status { - TxStatus::Success => { - let r = txq_state.tx_complete_status().read(); - Self::run_slot_closure(r.slot_iter(), f); - txq_state.tx_complete_clear().write(|w| unsafe { w.bits(r.bits()) }); - } - TxStatus::Timeout => { - let r = txq_state.tx_error_status().read(); - Self::run_slot_closure(r.slot_timeout_iter(), f); - txq_state.tx_error_clear().write(|w| unsafe { w.bits(r.bits() & 0x1f000000) }); - } - TxStatus::Collision => { - let r = txq_state.tx_error_status().read(); - Self::run_slot_closure(r.slot_collision_iter(), f); - txq_state.tx_error_clear().write(|w| unsafe { w.bits(r.bits() & 0x0000001f) }); - } - } - */ } - - // Crypto - #[inline] - /// Enable hardware cryptography for the specified interface. + /// Configure the status of a TX slot. /// - /// NOTE: We don't yet know, how to revert this, but it's assumed, that the two bits could each - /// stand for TX and RX. + /// # Safety + /// This is only expected to be called in the MAC ISR handler. For other purposes use + /// [LowLevelDriver::start_tx_for_slot] + pub unsafe fn set_tx_queue_status(queue: HardwareTxQueue, tx_slot_status: TxSlotStatus) { + Self::regs() + .tx_slot_config(queue.hardware_slot()) + .plcp0() + .modify(|_, w| { + w.slot_valid() + .bit(tx_slot_status.valid()) + .slot_enabled() + .bit(tx_slot_status.enabled()) + }); + } + #[inline] + /// Start transmission for a queue. /// - /// # Panics - /// If `interface` is greater, than [INTERFACE_COUNT], this will panic. - fn enable_crypto_for_interface(interface: usize) { - // Writing 0x3_000 in the crypto control register of an interface seem to be used to enable - // crypto for that interface. + /// TODO: Investigate what happens, if the DMA descriptor pointer ist zero. + pub fn start_tx_queue(&self, queue: HardwareTxQueue) { + unsafe { + Self::set_tx_queue_status(queue, TxSlotStatus::Ready); + } + } + #[inline] + /// Mark the transmission on a queue as done. + pub fn tx_done(&self, queue: HardwareTxQueue) { + unsafe { + Self::set_tx_queue_status(queue, TxSlotStatus::Disabled); + } + Self::regs().tx_slot_config(queue.hardware_slot()).plcp0().reset(); + } + #[inline] + /// Set parameters for channel access. + /// + /// We aren't really sure about any of these. + pub fn set_channel_access_parameters(&self, queue: HardwareTxQueue, timeout: usize, backoff_slots: usize, aifsn: usize) { + Self::regs().tx_slot_config(queue.hardware_slot()).config().modify(|_, w| unsafe { + w.timeout().bits(timeout as _).backoff_time().bits(backoff_slots as _).aifsn().bits(aifsn as _) + }); + } + #[inline] + /// Configure the PLCP0 register for a TX queue. + pub fn set_plcp0( + &self, + queue: HardwareTxQueue, + dma_list_item: Pin<&DmaDescriptor>, + wait_for_ack: bool, + ) { Self::regs() - .crypto_control() - .interface_crypto_control(interface) - .write(|w| unsafe { w.bits(0x0003_0000) }); + .tx_slot_config(queue.hardware_slot()) + .plcp0() + .modify(|_, w| unsafe { + w.dma_addr() + .bits((dma_list_item.get_ref() as *const DmaDescriptor).expose_provenance() as u32) + .wait_for_ack() + .bit(wait_for_ack) + }); + } + #[inline] + /// Configure the PLCP1 register for a TX queue. + /// + /// We are not 100% sure, that our assumption about the bandwidth config is correct. + pub fn set_plcp1( + &self, + queue: HardwareTxQueue, + rate: WiFiRate, + frame_length: usize, + interface: usize, + key_slot: Option, + ) { + Self::regs().plcp1(queue.hardware_slot()).write(|w| unsafe { + w.len() + .bits(frame_length as _) + .rate() + .bits(rate as _) + .is_80211_n() + .bit(rate.is_ht()) + .interface_id() + .bits(interface as _) + // NOTE: This makes the default value zero, which is a valid key slot ID. However + // unless you were to TX a frame, that was perfectly crafted to match the key ID, + // address and interface of key slot zero, and have a CCMP header. I guess this + // isn't an issue then. + .key_slot_id() + .bits(key_slot.unwrap_or_default() as _) + // Maybe my guess about bandwidth was wrong in the end. + .bandwidth() + .set_bit() + }); + } + #[inline] + /// Configure the PLCP2 register for a TX queue. + /// + /// Currently this doesn't do much, except setting a bit with unknown meaning to one. + pub fn set_plcp2(&self, queue: HardwareTxQueue) { + Self::regs() + .plcp2(queue.hardware_slot()) + .write(|w| w.unknown().set_bit()); + } + #[inline] + /// Set the duration for a TX queue. + pub fn set_duration(&self, queue: HardwareTxQueue, duration: u16) { + let duration = duration as u32; + Self::regs() + .duration(queue.hardware_slot()) + .write(|w| unsafe { w.bits(duration | (duration << 16)) }); + } + #[inline] + /// Configure the HT related register for a TX queue. + pub fn set_ht_parameters( + &self, + queue: HardwareTxQueue, + mcs: u8, + is_short_gi: bool, + frame_length: usize, + ) { + Self::regs() + .ht_sig(queue.hardware_slot()) + .write(|w| unsafe { + w.bits( + (mcs as u32 & 0b111) + | ((frame_length as u32 & 0xffff) << 8) + | (0b111 << 24) + | ((is_short_gi as u32) << 31), + ) + }); + Self::regs() + .ht_unknown(queue.hardware_slot()) + .write(|w| unsafe { w.bits(frame_length as u32 | 0x50000) }); } + + // Crypto + #[inline] /// Initialize hardware cryptography. /// /// This is pretty much the same as `hal_crypto_init`, except we init crypto for all /// interfaces, not just zero and one. - fn init_crypto() { + fn setup_crypto(&self) { // Enable crypto for all interfaces. - (0..INTERFACE_COUNT).for_each(Self::enable_crypto_for_interface); + (0..INTERFACE_COUNT).for_each(|interface| { + // Writing 0x3_000 in the crypto control register of an interface seem to be used to enable + // crypto for that interface. + Self::regs() + .crypto_control() + .interface_crypto_control(interface) + .write(|w| unsafe { w.bits(0x0003_0000) }); + }); // Resetting the general crypto control register effectively marks all slots as unused. Self::regs() @@ -840,33 +1231,22 @@ impl LowLevelDriver { /// The channel number is not validated. pub fn set_channel(&self, channel_number: u8) { unsafe { - Self::set_mac_state(false); + self.set_mac_state(false); #[cfg(nomac_channel_set)] crate::ffi::chip_v7_set_chan_nomac(channel_number, 0); #[cfg(not(nomac_channel_set))] crate::ffi::chip_v7_set_chan(channel_number, 0); disable_wifi_agc(); - Self::set_mac_state(true); + self.set_mac_state(true); enable_wifi_agc(); } } -} - -/// Convert a software slot index to the hardware slot index. -pub fn hw_slot_index(slot: usize) -> usize { - 4 - slot -} -/// Enable or disable the specified TX slot. -pub unsafe fn set_tx_slot_enabled(slot: usize, enabled: bool) { - HalWIFI::regs() - .tx_slot_config(hw_slot_index(slot)) - .plcp0() - .modify(|_, w| w.slot_enabled().bit(enabled)); -} -/// Validate or invalidate the specified TX slot. -pub unsafe fn set_tx_slot_validity(slot: usize, valid: bool) { - HalWIFI::regs() - .tx_slot_config(hw_slot_index(slot)) - .plcp0() - .modify(|_, w| w.slot_valid().bit(valid)); + /// Run TX power control. + /// + /// We don't really know, what this does. + pub fn run_power_control(&self) { + unsafe { + tx_pwctrl_background(1, 0); + } + } } diff --git a/esp-wifi-hal/src/rates.rs b/esp-wifi-hal/src/rates.rs index e92a378..3be914c 100644 --- a/esp-wifi-hal/src/rates.rs +++ b/esp-wifi-hal/src/rates.rs @@ -37,8 +37,6 @@ pub enum WiFiRate { PhyRate2ML = 0x01, PhyRate5ML = 0x02, PhyRate11ML = 0x03, - /// This rate is currently unstable for whatever reason. - PhyRate1MS = 0x04, PhyRate2MS = 0x05, PhyRate5MS = 0x06, PhyRate11MS = 0x07, @@ -68,10 +66,12 @@ pub enum WiFiRate { PhyRateMCS7SGI = 0x1f, } impl WiFiRate { + #[inline] /// Check if the rate is using the HT PHY. pub const fn is_ht(&self) -> bool { *self as u8 >= 0x10 } + #[inline] /// Check if the rate uses a short guard interval. pub const fn is_short_gi(&self) -> bool { *self as u8 >= 0x18 @@ -86,6 +86,16 @@ impl WiFiRate { _ => PhyMode::Ht, } } + /// Get the parameters of an HT rate. + /// + /// The returned tuple is the MCS index and a [bool] indicating short GI. + pub const fn ht_paramters(&self) -> Option<(u8, bool)> { + if self.is_ht() { + Some((*self as u8, self.is_short_gi())) + } else { + None + } + } } // This is a lookup-table for matching index->WiFiRate. pub(crate) static RATE_LUT: &[WiFiRate] = &[ @@ -93,7 +103,6 @@ pub(crate) static RATE_LUT: &[WiFiRate] = &[ WiFiRate::PhyRate2ML, WiFiRate::PhyRate5ML, WiFiRate::PhyRate11ML, - WiFiRate::PhyRate1MS, WiFiRate::PhyRate2MS, WiFiRate::PhyRate5MS, WiFiRate::PhyRate11MS, diff --git a/esp-wifi-hal/src/sync.rs b/esp-wifi-hal/src/sync.rs index 754058d..140f13a 100644 --- a/esp-wifi-hal/src/sync.rs +++ b/esp-wifi-hal/src/sync.rs @@ -9,24 +9,18 @@ use embassy_sync::blocking_mutex; use esp_hal::asynch::AtomicWaker; use portable_atomic::{AtomicU8, AtomicUsize, Ordering}; -use crate::DefaultRawMutex; +use crate::{ll::HardwareTxResult, DefaultRawMutex}; -/// The status of a TX slot that is in use. -pub enum TxSlotStatus { - Done, - Timeout, - Collision, -} /// A primitive for signaling the status of a TX slot. -pub struct TxSlotStateSignal { +pub struct HardwareTxResultSignal { state: AtomicU8, waker: AtomicWaker, } -impl TxSlotStateSignal { +impl HardwareTxResultSignal { /// A transmission is currently pending or the slot is inactive. const PENDING_OR_INACTIVE: u8 = 0; /// The tranmission has completed successfully. - const DONE: u8 = 1; + const SUCCESS: u8 = 1; /// A timeout occured while transmitting. const TIMEOUT: u8 = 2; /// A collision occured while transmitting. @@ -44,27 +38,27 @@ impl TxSlotStateSignal { .store(Self::PENDING_OR_INACTIVE, Ordering::Relaxed); } /// Signal a slot state. - pub fn signal(&self, slot_status: TxSlotStatus) { + pub fn signal(&self, slot_status: HardwareTxResult) { self.state.store( match slot_status { - TxSlotStatus::Done => Self::DONE, - TxSlotStatus::Timeout => Self::TIMEOUT, - TxSlotStatus::Collision => Self::COLLISION, + HardwareTxResult::Success => Self::SUCCESS, + HardwareTxResult::Timeout => Self::TIMEOUT, + HardwareTxResult::Collision => Self::COLLISION, }, Ordering::Relaxed, ); self.waker.wake(); } /// Wait for a slot state change. - pub fn wait(&self) -> impl Future + use<'_> { + pub fn wait(&self) -> impl Future + use<'_> { poll_fn(|cx| { let state = self.state.load(Ordering::Acquire); if state != Self::PENDING_OR_INACTIVE { self.reset(); Poll::Ready(match state { - Self::DONE => TxSlotStatus::Done, - Self::TIMEOUT => TxSlotStatus::Timeout, - Self::COLLISION => TxSlotStatus::Collision, + Self::SUCCESS => HardwareTxResult::Success, + Self::TIMEOUT => HardwareTxResult::Timeout, + Self::COLLISION => HardwareTxResult::Collision, _ => unreachable!(), }) } else { @@ -176,3 +170,28 @@ impl TxSlotQueue { } } } +/// A drop guard, which executes the provided closure on drop. +pub struct DropGuard { + drop_closure: F +} +impl DropGuard { + #[inline] + /// Create a new drop guard. + pub const fn new(drop_closure: F) -> Self { + Self { + drop_closure + } + } + #[inline] + /// Defuse the drop guard. + /// + /// This will prevent the drop closure from being run. + pub const fn defuse(self) { + core::mem::forget(self); + } +} +impl Drop for DropGuard { + fn drop(&mut self) { + (self.drop_closure)(); + } +} diff --git a/esp-wifi-hal/src/wmac.rs b/esp-wifi-hal/src/wmac.rs index 09a86d7..2f6fcd6 100644 --- a/esp-wifi-hal/src/wmac.rs +++ b/esp-wifi-hal/src/wmac.rs @@ -1,16 +1,15 @@ use core::{ cell::RefCell, - mem::forget, - ops::Deref, pin::{pin, Pin}, }; use portable_atomic::{AtomicU16, AtomicU64, AtomicU8, Ordering}; use crate::{ - esp_pac::wifi::TX_SLOT_CONFIG, - ll::{self, KeySlotParameters, LowLevelDriver, RxFilterBank, TxStatus, WiFiInterrupt}, + ll::{ + EdcaAccessCategory, HardwareTxQueue, HardwareTxResult, KeySlotParameters, LowLevelDriver, RxFilterBank, TxSlotStatus, WiFiInterrupt + }, rates::RATE_LUT, - sync::{BorrowedTxSlot, TxSlotQueue, TxSlotStatus}, + sync::{DropGuard, TxSlotQueue}, CipherParameters, WiFiRate, INTERFACE_COUNT, KEY_SLOT_COUNT, }; use embassy_sync::blocking_mutex::{self}; @@ -25,17 +24,15 @@ use macro_bits::{bit, check_bit}; use crate::{ dma_list::DMAList, - ffi::tx_pwctrl_background, - sync::{SignalQueue, TxSlotStateSignal}, + sync::{HardwareTxResultSignal, SignalQueue}, DefaultRawMutex, WiFiResources, }; static WIFI_RX_SIGNAL_QUEUE: SignalQueue = SignalQueue::new(); -#[allow(clippy::declare_interior_mutable_const)] -const EMPTY_SLOT: TxSlotStateSignal = TxSlotStateSignal::new(); -/// These are for knowing, when transmission has finished. -static WIFI_TX_SLOTS: [TxSlotStateSignal; 5] = [EMPTY_SLOT; 5]; +/// These are for knowing, when transmission has finished, and with which result. +static HARDWARE_TX_RESULT_SIGNALS: [HardwareTxResultSignal; 5] = + [const { HardwareTxResultSignal::new() }; 5]; // We run tx_pwctrl_background every four transmissions. static FRAMES_SINCE_LAST_TXPWR_CTRL: AtomicU8 = AtomicU8::new(0); @@ -52,26 +49,25 @@ fn mac_handler() { } if cause.tx_success() { unsafe { - LowLevelDriver::process_tx_status(TxStatus::Success, |slot| { - WIFI_TX_SLOTS[slot].signal(TxSlotStatus::Done); + LowLevelDriver::process_tx_status(HardwareTxResult::Success, |queue| { + HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()].signal(HardwareTxResult::Success); }) }; } if cause.tx_timeout() { unsafe { - LowLevelDriver::process_tx_status(TxStatus::Timeout, |slot| { - WIFI_TX_SLOTS[slot].signal(TxSlotStatus::Timeout); - ll::set_tx_slot_validity(slot, false); - ll::set_tx_slot_enabled(slot, false); + LowLevelDriver::process_tx_status(HardwareTxResult::Timeout, |queue| { + HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()].signal(HardwareTxResult::Timeout); + LowLevelDriver::set_tx_queue_status(queue, TxSlotStatus::Disabled); }) }; } if cause.tx_collision() { unsafe { - LowLevelDriver::process_tx_status(TxStatus::Collision, |slot| { - WIFI_TX_SLOTS[slot].signal(TxSlotStatus::Collision); - ll::set_tx_slot_validity(slot, false); - ll::set_tx_slot_enabled(slot, false); + LowLevelDriver::process_tx_status(HardwareTxResult::Collision, |queue| { + HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()] + .signal(HardwareTxResult::Collision); + LowLevelDriver::set_tx_queue_status(queue, TxSlotStatus::Disabled); }) }; } @@ -156,6 +152,7 @@ impl BorrowedBuffer<'_> { self.raw_header().sig_len(), &self.padded_buffer()[..Self::RX_CONTROL_HEADER_LENGTH] ); + self.dma_list.lock(|dma_list| dma_list.borrow().log_stats()); self.padded_buffer() }) } @@ -288,7 +285,7 @@ pub struct TxParameters { /// The maximum amount of time an ACK can take to arrive. pub ack_timeout: usize, /// The key slot to be used for encryption. - pub key_slot: Option, + pub key_slot: Option, } impl Default for TxParameters { fn default() -> Self { @@ -396,122 +393,67 @@ impl<'res> WiFi<'res> { dma_descriptor: dma_list_item, } } + /// Wait for TX to complete on a queue. + async fn wait_tx_done(&self, queue: HardwareTxQueue) -> WiFiResult<()> { + match HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()] + .wait() + .await + { + HardwareTxResult::Success => Ok(()), + HardwareTxResult::Timeout => Err(WiFiError::TxTimeout), + HardwareTxResult::Collision => Err(WiFiError::TxCollision), + } + } /// Set the packet for transmission. async fn transmit_internal( &self, dma_list_item: Pin<&DmaDescriptor>, tx_parameters: &TxParameters, duration: u16, - slot: &BorrowedTxSlot<'_>, + queue: HardwareTxQueue, ack_for_interface: Option, ) -> WiFiResult<()> { - let length = dma_list_item.len(); - let reversed_slot = 4 - slot.deref(); - let wifi = WIFI::regs(); - let tx_slot_config = wifi.tx_slot_config(reversed_slot); - tx_slot_config - .config() - .write(|w| unsafe { w.timeout().bits(tx_parameters.ack_timeout as u16) }); - - tx_slot_config.plcp0().modify(|_, w| unsafe { - w.dma_addr() - .bits(dma_list_item.get_ref() as *const _ as u32) - .wait_for_ack() - .bit(ack_for_interface.is_some()) - }); - - let rate = tx_parameters.rate; + let hardware_tx_result_signal = &HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()]; - wifi.plcp1(reversed_slot).write(|w| unsafe { - let w = if let Some(interface) = ack_for_interface { - w.interface_id().bits(interface as u8) - } else { - w - } - .len() - .bits(length as u16) - .is_80211_n() - .bit(rate.is_ht()) - .rate() - .bits(rate as u8); - if let Some(key_slot) = tx_parameters.key_slot { - w.key_slot_id().bits(key_slot as u8) - } else { - w - } - }); - wifi.plcp2(reversed_slot).write(|w| w.unknown().bit(true)); - let duration = duration as u32; - wifi.duration(reversed_slot) - .write(|w| unsafe { w.bits(duration | (duration << 0x10)) }); - if rate.is_ht() { - wifi.ht_sig(reversed_slot).write(|w| unsafe { - w.bits( - (rate as u32 & 0b111) - | ((length as u32 & 0xffff) << 8) - | (0b111 << 24) - | ((rate.is_short_gi() as u32) << 31), - ) - }); - wifi.ht_unknown(reversed_slot) - .write(|w| unsafe { w.length().bits(length as u32 | 0x50000) }); - } - // This also compensates for other slots being marked as done, without it being used at - // all. - WIFI_TX_SLOTS[**slot].reset(); - unsafe { - ll::set_tx_slot_validity(**slot, true); - ll::set_tx_slot_enabled(**slot, true); - } + self.ll_driver.set_channel_access_parameters(queue, tx_parameters.ack_timeout, 0, 0); + self.ll_driver + .set_plcp0(queue, dma_list_item, ack_for_interface.is_some()); + self.ll_driver.set_plcp1( + queue, + tx_parameters.rate, + dma_list_item.len(), + ack_for_interface.unwrap_or_default(), + tx_parameters.key_slot, + ); + self.ll_driver.set_plcp2(queue); + self.ll_driver.set_duration(queue, duration); - // Since this is the first and only await point, all the transmit parameters will have been - // set on the first poll. If the future gets dropped after the first poll, this would leave - // the slot in an invalid state, so we use this construction to reset the slot in that - // case. - struct CancelOnDrop<'a, 'b> { - tx_slot_config: &'a TX_SLOT_CONFIG, - slot: &'a BorrowedTxSlot<'b>, + if let Some((mcs, is_short_gi)) = tx_parameters.rate.ht_paramters() { + self.ll_driver + .set_ht_parameters(queue, mcs, is_short_gi, dma_list_item.len()); } - impl CancelOnDrop<'_, '_> { - async fn wait_for_tx_complete(self) -> WiFiResult<()> { - // Wait for the hardware to confirm transmission. - let res = WIFI_TX_SLOTS[**self.slot].wait().await; - // NOTE: This isn't done in the proprietary stack, but seems to prevent retransmissions. - self.tx_slot_config.plcp0().reset(); - let res = match res { - TxSlotStatus::Done => { - if FRAMES_SINCE_LAST_TXPWR_CTRL.fetch_add(1, Ordering::Relaxed) == 4 { - unsafe { tx_pwctrl_background(1, 0) }; - FRAMES_SINCE_LAST_TXPWR_CTRL.store(0, Ordering::Relaxed); - } - Ok(()) - } - TxSlotStatus::Collision => Err(WiFiError::TxCollision), - TxSlotStatus::Timeout => Err(WiFiError::TxTimeout), - }; - WIFI_TX_SLOTS[**self.slot].reset(); - forget(self); - res - } - } - impl Drop for CancelOnDrop<'_, '_> { - fn drop(&mut self) { - unsafe { - ll::set_tx_slot_validity(**self.slot, false); - ll::set_tx_slot_enabled(**self.slot, false); - } - WIFI_TX_SLOTS[**self.slot].reset(); + // This compensates for other slots being marked as done, without it being used at all. + hardware_tx_result_signal.reset(); + self.ll_driver.start_tx_queue(queue); + + // This will reset the slot, once the drop guard goes out of scope, or the future is + // dropped. + let tx_done_wait_drop_guard = DropGuard::new(|| { + self.ll_driver.tx_done(queue); + hardware_tx_result_signal.reset(); + }); + // We wait for the transmission to complete here. + self.wait_tx_done(queue).await.inspect(|_| { + if FRAMES_SINCE_LAST_TXPWR_CTRL.fetch_add(1, Ordering::Relaxed) == 4 { + self.ll_driver.run_power_control(); + FRAMES_SINCE_LAST_TXPWR_CTRL.store(0, Ordering::Relaxed); } - } - let cancel_on_drop = CancelOnDrop { - tx_slot_config, - slot, - }; - cancel_on_drop.wait_for_tx_complete().await?; + })?; + tx_done_wait_drop_guard.defuse(); - match wifi.pmd(reversed_slot).read().bits() >> 0xc { + match wifi.pmd(queue.hardware_slot()).read().bits() >> 0xc { 1 => Err(WiFiError::RtsTimeout), 2 => Err(WiFiError::CtsTimeout), 5 => Err(WiFiError::AckTimeout), @@ -604,7 +546,7 @@ impl<'res> WiFi<'res> { dma_descriptor_ref, tx_parameters, duration, - &slot, + HardwareTxQueue::from_hardware_slot(4 - *slot).unwrap(), ack_for_interface, ) .await; diff --git a/examples/Cargo.lock b/examples/Cargo.lock index d3b6a4d..c32f24d 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -77,6 +77,17 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "bitfield-struct" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8769c4854c5ada2852ddf6fd09d15cf43d4c2aaeccb4de6432f5402f08a6003b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -247,9 +258,9 @@ dependencies = [ [[package]] name = "defmt-or-log" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4439fab1ae9faccf0f69cdf2671989ba40f246e178c7f87d6e4fd5270e53a979" +checksum = "be59789d306cc1a082f53bb7c24c270d7e6078c00b7550a718219a7090156d97" dependencies = [ "defmt-or-log-macros", "log", @@ -575,14 +586,14 @@ dependencies = [ [[package]] name = "esp-backtrace" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd8a541e17aa485d82df547f03c77b89f78cb110f59dea67cc90733d67e3678" +checksum = "3318413fb566c7227387f67736cf70cd74d80a11f2bb31c7b95a9eb48d079669" dependencies = [ "cfg-if", "document-features", "esp-config", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "esp-println", "heapless 0.9.1", "riscv", @@ -592,14 +603,16 @@ dependencies = [ [[package]] name = "esp-bootloader-esp-idf" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c319c4a24fb44ef4c5f9854ff3dc6010eb8f15a6613d024227aa2355e3f6a334" +checksum = "02a56964ab5479ac20c9cf76fa3b0d3f2233b20b5d8554e81ef5d65f63c20567" dependencies = [ "cfg-if", "document-features", "embedded-storage", "esp-config", + "esp-hal-procmacros", + "esp-metadata-generated 0.3.0", "esp-rom-sys", "jiff", "strum", @@ -607,12 +620,12 @@ dependencies = [ [[package]] name = "esp-config" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "289fde78fff1ff500e81efbdf958b1dae1614eacc61cc5560b0a3e03a25f266e" +checksum = "102871054f8dd98202177b9890cb4b71d0c6fe1f1413b7a379a8e0841fc2473c" dependencies = [ "document-features", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "serde", "serde_yaml", "somni-expr", @@ -620,9 +633,9 @@ dependencies = [ [[package]] name = "esp-hal" -version = "1.0.0-rc.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75242d788e67fc7ce51308019c0ff5d5103f989721577bb566b02710ef1ba79" +checksum = "54786287c0a61ca0f78cb0c338a39427551d1be229103b4444591796c579e093" dependencies = [ "bitfield", "bitflags 2.9.1", @@ -647,7 +660,7 @@ dependencies = [ "enumset", "esp-config", "esp-hal-procmacros", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "esp-riscv-rt", "esp-rom-sys", "esp-sync", @@ -677,9 +690,9 @@ dependencies = [ [[package]] name = "esp-hal-procmacros" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd82a6506fb08d53a1086d165d92f085717aa9c59e67ac87a9e6f8acdcf6897" +checksum = "3e025a7a7a0affdb4ff913b5c4494aef96ee03d085bf83c27453ae3a71d50da6" dependencies = [ "document-features", "object", @@ -696,17 +709,23 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b18b1787dd3adea642fb529dd83fe558a08ace365bbaede4643a8959992900f4" +[[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.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62199c3e50eefcf53b8f3c690946582a124f7137b45bd1b14eb4b90a3fec2dd7" +checksum = "6b1facf348e1e251517278fc0f5dc134e95e518251f5796cfbb532ca226a29bf" dependencies = [ "cfg-if", "document-features", "esp-config", "esp-hal", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "esp-sync", "esp-wifi-sys", "log", @@ -714,12 +733,12 @@ dependencies = [ [[package]] name = "esp-println" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dcd18cbb132db6eb30d7c96bd831c3b6916894210f4528321f69fa66178b331" +checksum = "5a30e6c9fbcc01c348d46706fef8131c7775ab84c254a3cd65d0cd3f6414d592" dependencies = [ "document-features", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "esp-sync", "log", "portable-atomic", @@ -744,14 +763,14 @@ checksum = "01bafc39f59d56610b38ed0f63b16abeb8b357b8f546507d0d6c50c1a494f530" dependencies = [ "cfg-if", "document-features", - "esp-metadata-generated", + "esp-metadata-generated 0.2.0", ] [[package]] name = "esp-rtos" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7844233ccdf92eaf4134c08c56c1b4a874f7d59e6249c579e912ecc6c9312fda" +checksum = "162ec711c8d06e79c67b75d01595539e86b0aac209643af98ca87a12250428b3" dependencies = [ "cfg-if", "document-features", @@ -762,22 +781,22 @@ dependencies = [ "esp-config", "esp-hal", "esp-hal-procmacros", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "esp-sync", "portable-atomic", ] [[package]] name = "esp-sync" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b977b028ae5959f0b2daf6602a1d751723b2d329c7251daf869ad382c8ed1543" +checksum = "d44974639b4e88914f83fe60d2832c00276657d7d857628fdfc966cc7302e8a8" dependencies = [ "cfg-if", "document-features", "embassy-sync 0.6.2", "embassy-sync 0.7.2", - "esp-metadata-generated", + "esp-metadata-generated 0.3.0", "riscv", "xtensa-lx", ] @@ -799,7 +818,7 @@ dependencies = [ name = "esp-wifi-hal" version = "0.1.5" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.12.1", "cfg-if", "critical-section", "defmt-or-log", @@ -1047,10 +1066,10 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "ieee80211" -version = "0.5.7" +version = "0.5.9" dependencies = [ "aes-kw", - "bitfield-struct", + "bitfield-struct 0.11.0", "const_soft_float", "crc32fast", "defmt", diff --git a/examples/Cargo.toml b/examples/Cargo.toml index c4e456a..d84d9f1 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -10,26 +10,26 @@ license = "MIT OR Apache-2.0" esp-wifi-hal = { path = "../esp-wifi-hal/", features = ["log"] } # ESP-HAL dependencies -esp-hal = { version = "1.0.0-rc.1", features = ["unstable"] } +esp-hal = { version = "1.0.0", features = ["unstable"] } esp-alloc = "0.9.0" -esp-backtrace = { version = "0.18.0", features = ["panic-handler", "println"] } +esp-backtrace = { version = "0.18.1", features = ["panic-handler", "println"] } # Embassy dependencies # Misc -log = "0.4.21" -static_cell = { version = "2.1.0" } +log = "0.4.28" +static_cell = { version = "2.1.1" } ieee80211 = { path = "../../../rust/ieee80211/" } embedded-io-async = "0.7.0" -embassy-futures = "0.1.1" +embassy-futures = "0.1.2" embassy-executor = "0.9.1" embassy-time = "0.5.0" embassy-sync = "0.7.2" rand_core = "0.9.3" embedded-io = "0.7.1" -esp-rtos = { version = "0.1.0", features = ["embassy"] } -esp-println = { version = "0.16.0", features = ["log-04"] } -esp-bootloader-esp-idf = "0.3.0" +esp-rtos = { version = "0.2.0", features = ["embassy"] } +esp-println = { version = "0.16.1", features = ["log-04"] } +esp-bootloader-esp-idf = "0.4.0" [features] esp32 = [ diff --git a/examples/example.txt b/examples/example.txt new file mode 100644 index 0000000..a369ea6 --- /dev/null +++ b/examples/example.txt @@ -0,0 +1 @@ +1337 diff --git a/examples/src/bin/beacon.rs b/examples/src/bin/beacon.rs index ebc3b29..58bbba0 100644 --- a/examples/src/bin/beacon.rs +++ b/examples/src/bin/beacon.rs @@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) { embassy_init(peripherals.TIMG0); let wifi = wifi_init(peripherals.WIFI, peripherals.ADC2); - let _ = wifi.set_channel(6); + let _ = wifi.set_channel(1); let module_mac_address = Efuse::read_base_mac_address(); let module_mac_address = MACAddress::new(module_mac_address); let mut beacon_ticker = Ticker::every(Duration::from_micros(TU.as_micros() as u64 * 100)); diff --git a/examples/src/bin/test_cli.rs b/examples/src/bin/test_cli.rs index 7c557b3..ff67599 100644 --- a/examples/src/bin/test_cli.rs +++ b/examples/src/bin/test_cli.rs @@ -23,7 +23,7 @@ use esp_hal::{ uart::{Config, RxConfig, Uart, UartRx}, Async, }; -use esp_wifi_hal::{BorrowedBuffer, RxFilterBank, ScanningMode, TxParameters, WiFi, WiFiRate, INTERFACE_COUNT}; +use esp_wifi_hal::{RxFilterBank, ScanningMode, TxParameters, WiFi, WiFiRate, INTERFACE_COUNT}; use examples::{common_init, embassy_init, wifi_init}; use ieee80211::{ common::{CapabilitiesInformation, FrameType, ManagementFrameSubtype, TU}, @@ -254,6 +254,7 @@ fn dump_command(wifi: &WiFi, uart0_tx: &mut impl embedded_io::Write) { let integer_nf = (nf_qdbm & !0b11) / 4; let _ = writeln!(uart0_tx, "Noise floor: {integer_nf}.{decimal_nf} dBm"); } + wifi.log_dma_list_stats(); } fn parse_mac(mac_str: &str) -> Option { let mut mac = [0x00u8; 6]; diff --git a/examples/src/bin/timeout_test.rs b/examples/src/bin/timeout_test.rs new file mode 100644 index 0000000..07b814d --- /dev/null +++ b/examples/src/bin/timeout_test.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] +use embassy_executor::Spawner; +use embassy_time::Timer; +use esp_backtrace as _; +use esp_wifi_hal::{TxErrorBehaviour, TxParameters, WiFiError}; +use examples::{common_init, embassy_init, wifi_init, AP_ADDRESS}; +use ieee80211::{data_frame::builder::DataFrameBuilder, mac_parser::BROADCAST, scroll::Pwrite}; +use log::info; + +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} +#[esp_rtos::main] +async fn main(_spawner: Spawner) { + let peripherals = common_init(); + embassy_init(peripherals.TIMG0); + let wifi = wifi_init(peripherals.WIFI, peripherals.ADC2); + let buf = mk_static!([u8; 300], [0x00u8; 300]); + let written = buf + .pwrite( + DataFrameBuilder::new() + .from_ds() + .category_data() + .payload([0x69u8; 5].as_slice()) + .destination_address(BROADCAST) + .source_address(AP_ADDRESS.into()) + .bssid(AP_ADDRESS.into()) + .build(), + 0, + ) + .unwrap(); + loop { + let res = wifi + .transmit( + &mut buf[..written], + &TxParameters { + ack_timeout: 10, + tx_error_behaviour: TxErrorBehaviour::Drop, + ..Default::default() + }, + Some(0), + ) + .await; + match res { + Ok(_) => info!("TX success"), + Err(WiFiError::TxTimeout) => info!("TX timeout"), + Err(WiFiError::TxCollision) => info!("TX collision"), + err => info!("{err:?}"), + } + Timer::after_millis(500).await; + wifi.clear_rx_queue(); + } +} From 2a8b49af43e17a89272aa3c1729b330766691759 Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Tue, 18 Nov 2025 20:57:48 +0100 Subject: [PATCH 8/8] Completed TX --- esp-wifi-hal/.vscode/settings.json | 4 +- esp-wifi-hal/src/ll.rs | 289 +++++++++++++++++++---------- esp-wifi-hal/src/wmac.rs | 110 +++++------ examples/src/bin/mac_time_test.rs | 31 ++++ examples/src/bin/test_cli.rs | 2 +- examples/src/lib.rs | 4 +- 6 files changed, 282 insertions(+), 158 deletions(-) create mode 100644 examples/src/bin/mac_time_test.rs diff --git a/esp-wifi-hal/.vscode/settings.json b/esp-wifi-hal/.vscode/settings.json index 2557eff..50656df 100644 --- a/esp-wifi-hal/.vscode/settings.json +++ b/esp-wifi-hal/.vscode/settings.json @@ -1,7 +1,7 @@ { "rust-analyzer.check.allTargets": false, - "rust-analyzer.cargo.target": "xtensa-esp32s2-none-elf", - "rust-analyzer.cargo.features": ["esp32s2"], + "rust-analyzer.cargo.target": "xtensa-esp32-none-elf", + "rust-analyzer.cargo.features": ["esp32"], "editor.formatOnSave": true, "rust-analyzer.runnables.cargoExtraArgs": [ "+esp" diff --git a/esp-wifi-hal/src/ll.rs b/esp-wifi-hal/src/ll.rs index 8604901..46e4b21 100644 --- a/esp-wifi-hal/src/ll.rs +++ b/esp-wifi-hal/src/ll.rs @@ -15,11 +15,11 @@ use esp_hal::{ }; use esp_phy::{PhyController, PhyInitGuard}; use macro_bits::{bit, check_bit}; +use portable_atomic::AtomicU64; use crate::{ esp_pac::{wifi::crypto_key_slot::KEY_VALUE, Interrupt as PacInterrupt, WIFI}, - ffi::{disable_wifi_agc, enable_wifi_agc, hal_init, tx_pwctrl_background}, - WiFiRate, + ffi::{disable_wifi_agc, enable_wifi_agc, hal_init, tx_pwctrl_background}, WiFiRate, }; /// Run a reversible sequence of functions either forward or in reverse. @@ -239,7 +239,7 @@ pub enum HardwareTxResult { impl HardwareTxResult { /// Get raw transmission status bits. fn raw_status(&self) -> u8 { - let wifi = LowLevelDriver::regs(); + let wifi = LowLevelDriver::regs_internal(); (match self { HardwareTxResult::Success => wifi.txq_state().tx_complete_status().read().bits(), HardwareTxResult::Timeout => wifi.txq_state().tx_error_status().read().bits() >> 0x10, @@ -248,7 +248,7 @@ impl HardwareTxResult { } /// Clear slot transmission status bit. fn clear_slot_bit(&self, slot: usize) { - let wifi = LowLevelDriver::regs(); + let wifi = LowLevelDriver::regs_internal(); match self { HardwareTxResult::Success => wifi .txq_state() @@ -457,6 +457,8 @@ pub const INTERFACE_COUNT: usize = 4; /// The number of key slots the hardware has. pub const KEY_SLOT_COUNT: usize = 25; +static MAC_TIME_OFFSET: AtomicU64 = AtomicU64::new(0); + /// Low level driver for the Wi-Fi peripheral. /// /// This is intended as an intermediary layer between the user facing API and the hardware, to make @@ -487,11 +489,19 @@ pub struct LowLevelDriver { _phy_init_guard: Option>, } impl LowLevelDriver { - #[doc(hidden)] + /// Get the PAC Wi-Fi peripheral. + /// + /// # Safety + /// This is only intended to be used for debugging and development purposes. The ways you can + /// shoot yourself in the foot, by accessing the peripheral directly, are way too many to list. + /// Using this should be a last resort and only be done, if you know what you're doing. + pub unsafe fn regs() -> WIFI { + Self::regs_internal() + } /// Get the PAC Wi-Fi peripheral. /// /// This stops rust-analyzer from doing funky shit. - pub fn regs() -> WIFI { + fn regs_internal() -> WIFI { unsafe { WIFI::steal() } } /// Create a new [LowLevelDriver]. @@ -572,11 +582,11 @@ impl LowLevelDriver { // This is only required on the ESP32-S2. while !intialized && cfg!(feature = "esp32s2") - && Self::regs().ctrl().read().bits() & MAC_READY_MASK == 0 + && Self::regs_internal().ctrl().read().bits() & MAC_READY_MASK == 0 {} // If we are initializing the MAC, we need to clear some bits, by masking the reg, with the // MAC_INIT_MASK. On deinit, we need to set the bits we previously cleared. - Self::regs().ctrl().modify(|r, w| unsafe { + Self::regs_internal().ctrl().modify(|r, w| unsafe { w.bits(if intialized { r.bits() & MAC_INIT_MASK } else { @@ -585,7 +595,7 @@ impl LowLevelDriver { }); // Spin until the MAC is no longer ready. - while !intialized && Self::regs().ctrl().read().bits() & MAC_READY_MASK != 0 {} + while !intialized && Self::regs_internal().ctrl().read().bits() & MAC_READY_MASK != 0 {} } #[inline] /// Setup relevant blocks of the MAC. @@ -629,6 +639,13 @@ impl LowLevelDriver { self.set_module_status(true); } let mut hal_wifi = unsafe { HalWIFI::steal() }; + #[cfg(esp32)] + { + as esp_phy::MacTimeExt>::set_mac_time_update_cb(&hal_wifi, |duration| { + let _ = MAC_TIME_OFFSET + .fetch_add(duration.as_micros(), core::sync::atomic::Ordering::Relaxed); + }); + } // Enable the modem clock. hal_wifi.enable_modem_clock(true); // Initialize the PHY. @@ -649,8 +666,12 @@ impl LowLevelDriver { /// # Safety /// This is expected to be called ONLY from the MAC interrupt handler. pub unsafe fn get_and_clear_mac_interrupt_cause() -> MacInterruptCause { - let cause = Self::regs().mac_interrupt().wifi_int_status().read().bits(); - Self::regs() + let cause = Self::regs_internal() + .mac_interrupt() + .wifi_int_status() + .read() + .bits(); + Self::regs_internal() .mac_interrupt() .wifi_int_clear() .write(|w| unsafe { w.bits(cause) }); @@ -695,7 +716,7 @@ impl LowLevelDriver { /// # Safety /// Enabling RX, when the DMA list isn't setup correctly yet, may be incorrect. unsafe fn set_rx_enable(enable_rx: bool) { - Self::regs() + Self::regs_internal() .rx_ctrl() .modify(|_, w| w.rx_enable().bit(enable_rx)); } @@ -704,13 +725,13 @@ impl LowLevelDriver { /// /// We don't know, if this influences the RF frontend in any way. pub fn rx_enabled(&self) -> bool { - Self::regs().rx_ctrl().read().rx_enable().bit() + Self::regs_internal().rx_ctrl().read().rx_enable().bit() } /// Tell the hardware to reload the RX descriptors. /// /// This will spin, until the bit is clear again. pub fn reload_hw_rx_descriptors(&self) { - let wifi = Self::regs(); + let wifi = Self::regs_internal(); let reg = wifi.rx_ctrl(); // Start the reload. @@ -721,7 +742,7 @@ impl LowLevelDriver { } /// Set the base descriptor. pub fn set_base_rx_descriptor(&self, base_descriptor: NonNull) { - Self::regs() + Self::regs_internal() .rx_dma_list() .rx_descr_base() .write(|w| unsafe { w.bits(base_descriptor.expose_provenance().get() as u32) }); @@ -729,7 +750,7 @@ impl LowLevelDriver { } /// Reset the base descriptor. pub fn clear_base_rx_descriptor(&self) { - Self::regs().rx_dma_list().rx_descr_base().reset(); + Self::regs_internal().rx_dma_list().rx_descr_base().reset(); self.reload_hw_rx_descriptors(); } /// Start receiving frames. @@ -753,19 +774,31 @@ impl LowLevelDriver { /// Get the base RX descriptor. pub fn base_rx_descriptor(&self) -> Option> { NonNull::new(with_exposed_provenance_mut( - Self::regs().rx_dma_list().rx_descr_base().read().bits() as usize, + Self::regs_internal() + .rx_dma_list() + .rx_descr_base() + .read() + .bits() as usize, )) } /// Get the next RX descriptor. pub fn next_rx_descriptor(&self) -> Option> { NonNull::new(with_exposed_provenance_mut( - Self::regs().rx_dma_list().rx_descr_next().read().bits() as usize, + Self::regs_internal() + .rx_dma_list() + .rx_descr_next() + .read() + .bits() as usize, )) } /// Get the last RX descriptor. pub fn last_rx_descriptor(&self) -> Option> { NonNull::new(with_exposed_provenance_mut( - Self::regs().rx_dma_list().rx_descr_last().read().bits() as usize, + Self::regs_internal() + .rx_dma_list() + .rx_descr_last() + .read() + .bits() as usize, )) } @@ -773,14 +806,14 @@ impl LowLevelDriver { /// Enable or disable the specified filter. pub fn set_filter_enable(&self, interface: usize, filter_bank: RxFilterBank, enabled: bool) { - Self::regs() + Self::regs_internal() .filter_bank(filter_bank.into_bits()) .mask_high(interface) .modify(|_, w| w.enabled().bit(enabled)); } /// Check if the filter is enabled. pub fn filter_enabled(&self, interface: usize, filter_bank: RxFilterBank) -> bool { - Self::regs() + Self::regs_internal() .filter_bank(filter_bank.into_bits()) .mask_high(interface) .read() @@ -796,7 +829,7 @@ impl LowLevelDriver { filter_bank: RxFilterBank, address: &[u8; 6], ) { - let wifi = Self::regs(); + let wifi = Self::regs_internal(); let filter_bank = wifi.filter_bank(filter_bank.into_bits()); let (address_low, address_high) = split_address(address); @@ -811,7 +844,7 @@ impl LowLevelDriver { /// /// This will neither enable the filter nor configure the address. pub fn set_filter_mask(&self, interface: usize, filter_bank: RxFilterBank, mask: &[u8; 6]) { - let wifi = Self::regs(); + let wifi = Self::regs_internal(); let filter_bank = wifi.filter_bank(filter_bank.into_bits()); let (mask_low, mask_high) = split_address(mask); @@ -834,13 +867,14 @@ impl LowLevelDriver { /// /// This will also enable the BSSID check, to bring the filter into a sort of ground state, /// so that we can make assumptions about its behaviour when configuring other aspects. - pub fn clear_filter_bank(&self, interface: usize, filter_bank: RxFilterBank) { - let wifi = Self::regs(); + pub fn clear_filter(&self, interface: usize, filter_bank: RxFilterBank) { + let wifi = Self::regs_internal(); let filter_bank = wifi.filter_bank(filter_bank.into_bits()); filter_bank.addr_low(interface).reset(); filter_bank.addr_high(interface).reset(); filter_bank.mask_low(interface).reset(); + // As a side effect, this also disables the filter. filter_bank.mask_high(interface).reset(); } /// Reset the filters for an interface. @@ -848,8 +882,8 @@ impl LowLevelDriver { /// This will clear the RX and BSSID filters, as well as enabling the BSSID check and enabling /// filtering for unicast and multicast frames. pub fn reset_interface_filters(&self, interface: usize) { - self.clear_filter_bank(interface, RxFilterBank::ReceiverAddress); - self.clear_filter_bank(interface, RxFilterBank::Bssid); + self.clear_filter(interface, RxFilterBank::ReceiverAddress); + self.clear_filter(interface, RxFilterBank::Bssid); self.set_bssid_check_enable(interface, true); self.set_filtered_address_types(interface, true, true); @@ -859,44 +893,54 @@ impl LowLevelDriver { /// If an address type is unfiltered, frames with RAs matching that address type will pass the /// filter unconditionally. Otherwise the frame will be filtered as usual. pub fn set_filtered_address_types(&self, interface: usize, unicast: bool, multicast: bool) { - Self::regs().filter_control(interface).modify(|_, w| { - w.block_unicast() - .bit(unicast) - .block_multicast() - .bit(multicast) - }); + Self::regs_internal() + .filter_control(interface) + .modify(|_, w| { + w.block_unicast() + .bit(unicast) + .block_multicast() + .bit(multicast) + }); } /// Configure if the BSSID should be checked for RX filtering. /// /// If the BSSID check is enabled, a frame will only pass the RX filter, if its BSSID matches /// the one in the filter. By default this is enabled. pub fn set_bssid_check_enable(&self, interface: usize, enabled: bool) { - Self::regs() + Self::regs_internal() .filter_control(interface) .modify(|_, w| w.bssid_check().bit(enabled)); } /// Configure which control frames pass the filter. pub fn set_control_frame_filter(&self, interface: usize, config: &ControlFrameFilterConfig) { - Self::regs().rx_ctrl_filter(interface).modify(|_, w| { - w.control_wrapper() - .bit(config.control_wrapper) - .block_ack_request() - .bit(config.block_ack_request) - .block_ack() - .bit(config.block_ack) - .ps_poll() - .bit(config.ps_poll) - .rts() - .bit(config.rts) - .cts() - .bit(config.cts) - .ack() - .bit(config.ack) - .cf_end() - .bit(config.cf_end) - .cf_end_cf_ack() - .bit(config.cf_end_cf_ack) - }); + Self::regs_internal() + .rx_ctrl_filter(interface) + .modify(|_, w| { + w.control_wrapper() + .bit(config.control_wrapper) + .block_ack_request() + .bit(config.block_ack_request) + .block_ack() + .bit(config.block_ack) + .ps_poll() + .bit(config.ps_poll) + .rts() + .bit(config.rts) + .cts() + .bit(config.cts) + .ack() + .bit(config.ack) + .cf_end() + .bit(config.cf_end) + .cf_end_cf_ack() + .bit(config.cf_end_cf_ack) + }); + } + /// Set the parameters for scanning mode. + /// + /// We don't entirely know what these parmeters mean. + pub fn set_scanning_mode_parameters(&self, interface: usize, beacons: bool, other_frames: bool) { + Self::regs_internal().filter_control(interface).modify(|_, w| w.scan_mode().bit(beacons).data_and_mgmt_mode().bit(other_frames)); } #[inline] /// Setup RX filtering. @@ -940,7 +984,7 @@ impl LowLevelDriver { /// This is only expected to be called in the MAC ISR handler. For other purposes use /// [LowLevelDriver::start_tx_for_slot] pub unsafe fn set_tx_queue_status(queue: HardwareTxQueue, tx_slot_status: TxSlotStatus) { - Self::regs() + Self::regs_internal() .tx_slot_config(queue.hardware_slot()) .plcp0() .modify(|_, w| { @@ -965,16 +1009,33 @@ impl LowLevelDriver { unsafe { Self::set_tx_queue_status(queue, TxSlotStatus::Disabled); } - Self::regs().tx_slot_config(queue.hardware_slot()).plcp0().reset(); + Self::regs_internal() + .tx_slot_config(queue.hardware_slot()) + .plcp0() + .reset(); } #[inline] /// Set parameters for channel access. - /// + /// /// We aren't really sure about any of these. - pub fn set_channel_access_parameters(&self, queue: HardwareTxQueue, timeout: usize, backoff_slots: usize, aifsn: usize) { - Self::regs().tx_slot_config(queue.hardware_slot()).config().modify(|_, w| unsafe { - w.timeout().bits(timeout as _).backoff_time().bits(backoff_slots as _).aifsn().bits(aifsn as _) - }); + pub fn set_channel_access_parameters( + &self, + queue: HardwareTxQueue, + timeout: usize, + backoff_slots: usize, + aifsn: usize, + ) { + Self::regs_internal() + .tx_slot_config(queue.hardware_slot()) + .config() + .modify(|_, w| unsafe { + w.timeout() + .bits(timeout as _) + .backoff_time() + .bits(backoff_slots as _) + .aifsn() + .bits(aifsn as _) + }); } #[inline] /// Configure the PLCP0 register for a TX queue. @@ -984,12 +1045,15 @@ impl LowLevelDriver { dma_list_item: Pin<&DmaDescriptor>, wait_for_ack: bool, ) { - Self::regs() + Self::regs_internal() .tx_slot_config(queue.hardware_slot()) .plcp0() .modify(|_, w| unsafe { w.dma_addr() - .bits((dma_list_item.get_ref() as *const DmaDescriptor).expose_provenance() as u32) + .bits( + (dma_list_item.get_ref() as *const DmaDescriptor).expose_provenance() + as u32, + ) .wait_for_ack() .bit(wait_for_ack) }); @@ -1006,32 +1070,34 @@ impl LowLevelDriver { interface: usize, key_slot: Option, ) { - Self::regs().plcp1(queue.hardware_slot()).write(|w| unsafe { - w.len() - .bits(frame_length as _) - .rate() - .bits(rate as _) - .is_80211_n() - .bit(rate.is_ht()) - .interface_id() - .bits(interface as _) - // NOTE: This makes the default value zero, which is a valid key slot ID. However - // unless you were to TX a frame, that was perfectly crafted to match the key ID, - // address and interface of key slot zero, and have a CCMP header. I guess this - // isn't an issue then. - .key_slot_id() - .bits(key_slot.unwrap_or_default() as _) - // Maybe my guess about bandwidth was wrong in the end. - .bandwidth() - .set_bit() - }); + Self::regs_internal() + .plcp1(queue.hardware_slot()) + .write(|w| unsafe { + w.len() + .bits(frame_length as _) + .rate() + .bits(rate as _) + .is_80211_n() + .bit(rate.is_ht()) + .interface_id() + .bits(interface as _) + // NOTE: This makes the default value zero, which is a valid key slot ID. However + // unless you were to TX a frame, that was perfectly crafted to match the key ID, + // address and interface of key slot zero, and have a CCMP header. I guess this + // isn't an issue then. + .key_slot_id() + .bits(key_slot.unwrap_or_default() as _) + // Maybe my guess about bandwidth was wrong in the end. + .bandwidth() + .set_bit() + }); } #[inline] /// Configure the PLCP2 register for a TX queue. /// /// Currently this doesn't do much, except setting a bit with unknown meaning to one. pub fn set_plcp2(&self, queue: HardwareTxQueue) { - Self::regs() + Self::regs_internal() .plcp2(queue.hardware_slot()) .write(|w| w.unknown().set_bit()); } @@ -1039,7 +1105,7 @@ impl LowLevelDriver { /// Set the duration for a TX queue. pub fn set_duration(&self, queue: HardwareTxQueue, duration: u16) { let duration = duration as u32; - Self::regs() + Self::regs_internal() .duration(queue.hardware_slot()) .write(|w| unsafe { w.bits(duration | (duration << 16)) }); } @@ -1052,7 +1118,7 @@ impl LowLevelDriver { is_short_gi: bool, frame_length: usize, ) { - Self::regs() + Self::regs_internal() .ht_sig(queue.hardware_slot()) .write(|w| unsafe { w.bits( @@ -1062,7 +1128,7 @@ impl LowLevelDriver { | ((is_short_gi as u32) << 31), ) }); - Self::regs() + Self::regs_internal() .ht_unknown(queue.hardware_slot()) .write(|w| unsafe { w.bits(frame_length as u32 | 0x50000) }); } @@ -1079,14 +1145,14 @@ impl LowLevelDriver { (0..INTERFACE_COUNT).for_each(|interface| { // Writing 0x3_000 in the crypto control register of an interface seem to be used to enable // crypto for that interface. - Self::regs() + Self::regs_internal() .crypto_control() .interface_crypto_control(interface) .write(|w| unsafe { w.bits(0x0003_0000) }); }); // Resetting the general crypto control register effectively marks all slots as unused. - Self::regs() + Self::regs_internal() .crypto_control() .general_crypto_control() .reset(); @@ -1095,14 +1161,14 @@ impl LowLevelDriver { /// /// As soon, as the key slot is enabled, the hardware will treat the data in it as valid. pub fn set_key_slot_enable(&self, key_slot: usize, enabled: bool) { - Self::regs() + Self::regs_internal() .crypto_control() .crypto_key_slot_state() .modify(|_, w| w.key_slot_enable(key_slot as u8).bit(enabled)); } /// Check if the key slot is enabled. pub fn key_slot_enabled(&self, key_slot: usize) -> bool { - Self::regs() + Self::regs_internal() .crypto_control() .crypto_key_slot_state() .read() @@ -1115,7 +1181,7 @@ impl LowLevelDriver { /// If `key` is longer than 32 bytes, or `key_slot` larger than 24. pub fn set_key(&self, key_slot: usize, key: &[u8]) { assert!(key.len() <= 32, "Keys can't be longer than 32 bytes."); - let wifi = Self::regs(); + let wifi = Self::regs_internal(); let key_slot = wifi.crypto_key_slot(key_slot); // Let's hope the compiler is smart enough to emit no panics this way. @@ -1154,7 +1220,7 @@ impl LowLevelDriver { key_slot: usize, key_slot_parameters: &KeySlotParameters, ) { - let wifi = Self::regs(); + let wifi = Self::regs_internal(); let key_slot = wifi.crypto_key_slot(key_slot); let (address_low, address_high) = split_address(&key_slot_parameters.address); @@ -1184,7 +1250,7 @@ impl LowLevelDriver { /// /// This will not disable the key slot, use [Self::set_key_slot_enable] for that. pub fn clear_key_slot(&self, key_slot: usize) { - let wifi = Self::regs(); + let wifi = Self::regs_internal(); let key_slot = wifi.crypto_key_slot(key_slot); // Effectively zeroes the entire slot. @@ -1208,7 +1274,7 @@ impl LowLevelDriver { protect_signaling_and_payload: bool, is_cipher_aead: bool, ) { - Self::regs() + Self::regs_internal() .crypto_control() .interface_crypto_control(interface) .modify(|r, w| unsafe { @@ -1223,6 +1289,41 @@ impl LowLevelDriver { .clear_bit() }); } + /// Set the status of SMS4 (WAPI) encapsulation. + /// + /// SMS4 needs to be enabled here, if you want to use it elsewhere. Our current understanding + /// is, that WAPI can't be used concurrently with other ciphers. + pub fn set_sms4_status(&self, enabled: bool) { + Self::regs_internal() + .crypto_control() + .general_crypto_control() + .modify(|r, w| unsafe { + w.bits(if enabled { + r.bits() | 0x00ffff00 + } else { + r.bits() & 0xff0000ff + }) + }); + } + + // Timing + + /// Get the offset between the system timers and the MAC timer. + pub fn mac_time_offset() -> esp_hal::time::Duration { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + let offset = MAC_TIME_OFFSET.load(core::sync::atomic::Ordering::Relaxed); + } else { + let offset = 0; + } + } + esp_hal::time::Duration::from_micros(offset) + } + /// Get the current value of the MAC timer. + pub fn mac_time(&self) -> esp_hal::time::Duration { + esp_hal::time::Duration::from_micros(Self::regs_internal().mac_time().read().bits() as u64) + + Self::mac_time_offset() + } // RF diff --git a/esp-wifi-hal/src/wmac.rs b/esp-wifi-hal/src/wmac.rs index 2f6fcd6..1503ae6 100644 --- a/esp-wifi-hal/src/wmac.rs +++ b/esp-wifi-hal/src/wmac.rs @@ -2,11 +2,12 @@ use core::{ cell::RefCell, pin::{pin, Pin}, }; -use portable_atomic::{AtomicU16, AtomicU64, AtomicU8, Ordering}; +use portable_atomic::{AtomicU16, AtomicU8, Ordering}; use crate::{ ll::{ - EdcaAccessCategory, HardwareTxQueue, HardwareTxResult, KeySlotParameters, LowLevelDriver, RxFilterBank, TxSlotStatus, WiFiInterrupt + HardwareTxQueue, HardwareTxResult, KeySlotParameters, LowLevelDriver, RxFilterBank, + TxSlotStatus, WiFiInterrupt, }, rates::RATE_LUT, sync::{DropGuard, TxSlotQueue}, @@ -194,7 +195,7 @@ impl BorrowedBuffer<'_> { /// clock. pub fn corrected_timestamp(&self) -> embassy_time::Instant { embassy_time::Instant::from_micros( - self.timestamp() as u64 + MAC_SYSTEM_TIME_DELTA.load(Ordering::Relaxed), + self.timestamp() as u64 + LowLevelDriver::mac_time_offset().as_micros(), ) } /// Check if the frame is an A-MPDU. @@ -314,9 +315,6 @@ pub enum ScanningMode { /// A [Result] returned by the Wi-Fi driver. pub type WiFiResult = Result; -/// The time the MAC clock was enabled in microseconds. -static MAC_SYSTEM_TIME_DELTA: AtomicU64 = AtomicU64::new(0); - /// Driver for the Wi-Fi peripheral. /// /// WARNING: Currently dropping the driver is not properly implemented. @@ -417,7 +415,8 @@ impl<'res> WiFi<'res> { let hardware_tx_result_signal = &HARDWARE_TX_RESULT_SIGNALS[queue.hardware_slot()]; - self.ll_driver.set_channel_access_parameters(queue, tx_parameters.ack_timeout, 0, 0); + self.ll_driver + .set_channel_access_parameters(queue, tx_parameters.ack_timeout, 0, 0); self.ll_driver .set_plcp0(queue, dma_list_item, ack_for_interface.is_some()); self.ll_driver.set_plcp1( @@ -600,9 +599,8 @@ impl<'res> WiFi<'res> { self.current_channel.load(Ordering::Relaxed) } /// Get the current MAC time in µs. - pub fn mac_time(&self) -> u32 { - // We hardcode the addresses here, until PAC support is merged. - WIFI::regs().mac_time().read().bits() + pub fn mac_time(&self) -> esp_hal::time::Duration { + self.ll_driver.mac_time() } /// Check if that interface is valid. pub const fn validate_interface(interface: usize) -> WiFiResult<()> { @@ -618,48 +616,48 @@ impl<'res> WiFi<'res> { /// but the BSSID is disabled, it will assume that `BSSID == RA`. However setting this to /// `false` will stop this behaviour. pub fn set_filter_bssid_check(&self, interface: usize, enabled: bool) -> WiFiResult<()> { - Self::validate_interface(interface)?; - WIFI::regs() - .filter_control(interface) - .modify(|_, w| w.bssid_check().bit(enabled)); - Ok(()) - } - /// Write raw value to the `INTERFACE_RX_CONTROL` register. - /// - /// NOTE: This exists mostly for debugging purposes, so if you find yourself using this for - /// something else than that, you probably want to use one of the other functions. - pub fn write_rx_policy_raw(&self, interface: usize, val: u32) -> WiFiResult<()> { - Self::validate_interface(interface)?; - WIFI::regs() - .filter_control(interface) - .write(|w| unsafe { w.bits(val) }); - Ok(()) + Self::validate_interface(interface) + .inspect(|_| self.ll_driver.set_bssid_check_enable(interface, enabled)) } - /// Read a raw value from the `INTERFACE_RX_CONTROL` register. + /// Configure a filter. /// - /// NOTE: This exists mostly for debugging purposes, so if you find yourself using this for - /// something else than that, you probably want to use one of the other functions. - pub fn read_rx_policy_raw(&self, interface: usize) -> WiFiResult { - Self::validate_interface(interface)?; - Ok(WIFI::regs().filter_control(interface).read().bits()) - } - /// Configure the filter. + /// This will default the mask to an all ones mask, and enable the filter. pub fn set_filter( &self, - filter_bank: RxFilterBank, interface: usize, + filter_bank: RxFilterBank, address: [u8; 6], ) -> WiFiResult<()> { - Self::validate_interface(interface)?; - - self.ll_driver - .set_filter_address(interface, filter_bank, &address); - self.ll_driver - .set_filter_mask(interface, filter_bank, &[0xff; 6]); - self.ll_driver - .set_filter_enable(interface, filter_bank, true); + Self::validate_interface(interface).inspect(|_| { + self.ll_driver + .set_filter_address(interface, filter_bank, &address); + self.ll_driver + .set_filter_mask(interface, filter_bank, &[0xff; 6]); + self.ll_driver + .set_filter_enable(interface, filter_bank, true); + }) + } + /// Override the mask of a filter. + /// + /// This doesn't check, if the filter is actually enabled. + pub fn override_filter_mask( + &self, + interface: usize, + filter_bank: RxFilterBank, + mask: [u8; 6], + ) -> WiFiResult<()> { + Self::validate_interface(interface).inspect(|_| { + self.ll_driver + .set_filter_mask(interface, filter_bank, &mask) + }) + } - Ok(()) + /// Clear and disable a filter for an interface and bank. + /// + /// This will return the filter into its default state. + pub fn clear_filter(&self, interface: usize, filter_bank: RxFilterBank) -> WiFiResult<()> { + Self::validate_interface(interface) + .inspect(|_| self.ll_driver.clear_filter(interface, filter_bank)) } /// Enable or disable scanning mode. pub fn set_scanning_mode( @@ -667,21 +665,13 @@ impl<'res> WiFi<'res> { interface: usize, scanning_mode: ScanningMode, ) -> WiFiResult<()> { - Self::validate_interface(interface)?; - WIFI::regs() - .filter_control(interface) - .modify(|_, w| match scanning_mode { - ScanningMode::Disabled => { - w.scan_mode().clear_bit().data_and_mgmt_mode().clear_bit() - } - ScanningMode::BeaconsOnly => { - w.scan_mode().set_bit().data_and_mgmt_mode().clear_bit() - } - ScanningMode::ManagementAndData => { - w.scan_mode().clear_bit().data_and_mgmt_mode().set_bit() - } - }); - Ok(()) + Self::validate_interface(interface).inspect(|_| { + self.ll_driver.set_scanning_mode_parameters( + interface, + scanning_mode == ScanningMode::BeaconsOnly, + scanning_mode == ScanningMode::ManagementAndData, + ); + }) } /// Check if they key slot is valid. pub const fn validate_key_slot(key_slot: usize) -> WiFiResult<()> { @@ -756,6 +746,8 @@ impl<'res> WiFi<'res> { protect_signaling_and_payload, cipher_parameters.is_aead(), ); + // Currently, we do not support WAPI. What a shame... + self.ll_driver.set_sms4_status(false); Ok(()) } diff --git a/examples/src/bin/mac_time_test.rs b/examples/src/bin/mac_time_test.rs new file mode 100644 index 0000000..997fe7f --- /dev/null +++ b/examples/src/bin/mac_time_test.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] +use embassy_executor::Spawner; +use esp_backtrace as _; +use esp_wifi_hal::ScanningMode; +use examples::{common_init, embassy_init, wifi_init}; +use ieee80211::GenericFrame; +use log::info; + +#[esp_rtos::main] +async fn main(_spawner: Spawner) { + let peripherals = common_init(); + embassy_init(peripherals.TIMG0); + let wifi = wifi_init(peripherals.WIFI, peripherals.ADC2); + wifi.set_scanning_mode(0, ScanningMode::BeaconsOnly); + loop { + let received = wifi.receive().await; + let Ok(generic_frame) = GenericFrame::new(received.mpdu_buffer(), false) else { + continue; + }; + info!("IN: {:05}", received.corrected_timestamp().as_micros()); + /* + info!( + "Duration: {:03}s Delta: {:08}µs Type: {:?}", + generic_frame.duration(), + received.corrected_timestamp().as_micros() as i32 - wifi.mac_time() as i32, + generic_frame.frame_control_field().frame_type() + ); +*/ + } +} diff --git a/examples/src/bin/test_cli.rs b/examples/src/bin/test_cli.rs index ff67599..4a2eb5b 100644 --- a/examples/src/bin/test_cli.rs +++ b/examples/src/bin/test_cli.rs @@ -311,7 +311,7 @@ fn filter_command<'a>( let _ = writeln!(uart0_tx, "The provided MAC address was invalid."); return; }; - let _ = wifi.set_filter(bank, interface, *mac_address); + let _ = wifi.set_filter(interface, bank, *mac_address); } None => { let _ = writeln!( diff --git a/examples/src/lib.rs b/examples/src/lib.rs index 27833d0..ec278da 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs @@ -41,8 +41,8 @@ pub fn print_key<'buf>(key: &[u8; 16], buf: &'buf mut [u8; 32]) -> &'buf str { } /// Utility to set and enable the filters. pub fn setup_filters(wifi: &WiFi, ra: [u8; 6], bssid: [u8; 6]) { - let _ = wifi.set_filter(RxFilterBank::ReceiverAddress, 0, ra); - let _ = wifi.set_filter(RxFilterBank::Bssid, 0, bssid); + let _ = wifi.set_filter(0, RxFilterBank::ReceiverAddress, ra); + let _ = wifi.set_filter(0, RxFilterBank::Bssid, bssid); } /// Utility to set a key, with some basic parameters. pub fn insert_key(