From 1e508e2e1e4e97922f79408d7a329bf510abe7a8 Mon Sep 17 00:00:00 2001 From: Juraj Sadel Date: Thu, 20 Nov 2025 14:24:10 +0100 Subject: [PATCH] Refactor esp_radio::init() (#4482) * Refactor esp_radio::init() * Introduce BleInitError and WifiInitError enums and call RadioRefGuard new and drop in critical section * reviews (wifi::new returns WifiError) * Changelog and MG * Redo tests * Add coex qa-test that drops ble and test if wifi still works --- esp-radio/CHANGELOG.md | 5 + esp-radio/Cargo.toml | 8 + esp-radio/MIGRATING-0.17.0.md | 18 ++ esp-radio/src/ble/controller/mod.rs | 30 ++- esp-radio/src/lib.rs | 137 ++++++----- esp-radio/src/wifi/mod.rs | 48 +++- examples/ble/bas_peripheral/src/main.rs | 6 +- examples/ble/scanner/src/main.rs | 6 +- examples/esp-now/embassy_esp_now/src/main.rs | 20 +- .../embassy_esp_now_duplex/src/main.rs | 14 +- examples/esp-now/esp_now/src/main.rs | 5 +- examples/wifi/80211_tx/src/main.rs | 4 +- examples/wifi/access_point/src/main.rs | 4 +- .../wifi/access_point_with_sta/src/main.rs | 4 +- examples/wifi/coex/src/main.rs | 6 +- examples/wifi/dhcp/src/main.rs | 4 +- .../wifi/embassy_access_point/src/main.rs | 14 +- .../embassy_access_point_with_sta/src/main.rs | 23 +- examples/wifi/embassy_dhcp/src/main.rs | 23 +- examples/wifi/embassy_sntp/src/main.rs | 23 +- examples/wifi/sniffer/src/main.rs | 4 +- examples/wifi/static_ip/src/main.rs | 4 +- hil-test/src/bin/esp_radio/ble_controller.rs | 12 +- hil-test/src/bin/esp_radio/init_tests.rs | 105 ++++++-- qa-test/Cargo.toml | 4 + qa-test/src/bin/embassy_wifi_bench.rs | 14 +- qa-test/src/bin/embassy_wifi_i2s.rs | 22 +- qa-test/src/bin/embassy_wifi_stress.rs | 11 +- qa-test/src/bin/wifi_bench.rs | 4 +- qa-test/src/bin/wifi_csi.rs | 4 +- qa-test/src/bin/wifi_survives_ble_drop.rs | 231 ++++++++++++++++++ 31 files changed, 567 insertions(+), 250 deletions(-) create mode 100644 qa-test/src/bin/wifi_survives_ble_drop.rs diff --git a/esp-radio/CHANGELOG.md b/esp-radio/CHANGELOG.md index b6cd22edb..34c5a29bc 100644 --- a/esp-radio/CHANGELOG.md +++ b/esp-radio/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `ble::mac` to get the MAC address of the device (#4485) - `last_calibration_result` to get the result of the last calibration (#4479) +- `BleInitError` for BLE init failures and `Internal`, `WrongClockConfig`, `SchedulerNotInitialized` and `Adc2IsUsed` variants to `WifiError (#4482) ### Changed @@ -19,6 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `InitializationError::General` is replaced by `InitializationError::Internal` (#4467) - The `WifiError::InternalError` is removed and `InternalWifiError` was folded into `WifiError`, some variants renamed for clarity (#4467) - Refactor: extract submodules from `esp_radio::wifi` module (#4460) +- `BleConnector::new` returns `BleInitError` instead of `InvalidConfigError` (#4482) +- `esp_radio::init()` is not public anymore (#4482) +- `esp_radio::wifi::new` and `BleConnector::new` no longer take a `Controller` (#4482) ### Fixed @@ -29,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - The `serde` feature has been removed (#4435) +- `Controller` struct and `InitializationError::InterruptsDisabled` enum variant have been removed (#4482) ## [v0.17.0] - 2025-10-30 diff --git a/esp-radio/Cargo.toml b/esp-radio/Cargo.toml index 912760862..7b32bb3f9 100644 --- a/esp-radio/Cargo.toml +++ b/esp-radio/Cargo.toml @@ -44,6 +44,7 @@ cfg-if = "1" esp-hal = { version = "1.0.0", path = "../esp-hal", default-features = false, features = ["requires-unstable"] } portable-atomic = { version = "1.11", default-features = false } enumset = { version = "1.1", default-features = false, optional = true } +critical-section = { version = "1", default-features = false, optional = true } # ⚠️ Unstable dependencies esp-radio-rtos-driver = { version = "0.2.0", path = "../esp-radio-rtos-driver" } @@ -137,6 +138,7 @@ esp32 = [ "dep:esp-wifi-sys-esp32", "esp-metadata-generated/esp32", "xtensa-lx-rt/float-save-restore", + "critical-section", ] ## esp32c2 = [ @@ -145,6 +147,7 @@ esp32c2 = [ "esp-phy/esp32c2", "dep:esp-wifi-sys-esp32c2", "esp-metadata-generated/esp32c2", + "critical-section", ] ## esp32c3 = [ @@ -153,6 +156,7 @@ esp32c3 = [ "esp-phy/esp32c3", "dep:esp-wifi-sys-esp32c3", "esp-metadata-generated/esp32c3", + "critical-section", ] ## esp32c6 = [ @@ -161,6 +165,7 @@ esp32c6 = [ "esp-phy/esp32c6", "dep:esp-wifi-sys-esp32c6", "esp-metadata-generated/esp32c6", + "critical-section", ] ## esp32h2 = [ @@ -169,6 +174,7 @@ esp32h2 = [ "esp-phy/esp32h2", "dep:esp-wifi-sys-esp32h2", "esp-metadata-generated/esp32h2", + "critical-section", ] ## esp32s2 = [ @@ -178,6 +184,7 @@ esp32s2 = [ "dep:esp-wifi-sys-esp32s2", "esp-metadata-generated/esp32s2", "xtensa-lx-rt/float-save-restore", + "critical-section", ] ## esp32s3 = [ @@ -187,6 +194,7 @@ esp32s3 = [ "dep:esp-wifi-sys-esp32s3", "esp-metadata-generated/esp32s3", "xtensa-lx-rt/float-save-restore", + "critical-section", ] #! ### Wireless Feature Flags diff --git a/esp-radio/MIGRATING-0.17.0.md b/esp-radio/MIGRATING-0.17.0.md index d80ef36ef..d2c12df89 100644 --- a/esp-radio/MIGRATING-0.17.0.md +++ b/esp-radio/MIGRATING-0.17.0.md @@ -36,3 +36,21 @@ impl From for esp_radio::wifi::ScanMethod { - `EapFastConfig`, `TlsPhase2Method` and `EapClientConfig` are now located in `wifi::ap::eap`. You will need to update any imports in your project accordingly. + +## `esp_radio::init()` and `Controller` is no longer available + +WiFi initialization: +```diff +- let esp_radio_ctrl = esp_radio::init().unwrap(); +- let (mut controller, interfaces) = +- esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap(); ++ let (mut controller, interfaces) = esp_radio::wifi::new(wifi, Default::default()).unwrap(); +``` + +BLE initialization: +```diff +- static RADIO: StaticCell> = StaticCell::new(); +- let radio = RADIO.init(esp_radio::init().unwrap()); +- let connector = BleConnector::new(radio, bluetooth, Default::default()).unwrap(); ++ let connector = BleConnector::new(bluetooth, Default::default()).unwrap(); +``` diff --git a/esp-radio/src/ble/controller/mod.rs b/esp-radio/src/ble/controller/mod.rs index 7206928a3..b9a6257bb 100644 --- a/esp-radio/src/ble/controller/mod.rs +++ b/esp-radio/src/ble/controller/mod.rs @@ -12,16 +12,37 @@ use esp_hal::asynch::AtomicWaker; use esp_phy::PhyInitGuard; use crate::{ - Controller, + InitializationError, + RadioRefGuard, ble::{Config, InvalidConfigError, have_hci_read_data, read_hci, read_next, send_hci}, }; +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Error enum for BLE initialization failures. +pub enum BleInitError { + /// Failure during initial validation of the provided configuration. + Config(InvalidConfigError), + + /// Failure during the acquisition or initialization of the global radio hardware. + RadioInit(InitializationError), +} + +// Implement the From trait for cleaner error mapping +impl From for BleInitError { + fn from(err: InvalidConfigError) -> Self { + BleInitError::Config(err) + } +} + /// A blocking HCI connector #[instability::unstable] pub struct BleConnector<'d> { _phy_init_guard: PhyInitGuard<'d>, _device: crate::hal::peripherals::BT<'d>, + _guard: RadioRefGuard, } + impl Drop for BleConnector<'_> { fn drop(&mut self) { crate::ble::ble_deinit(); @@ -31,14 +52,17 @@ impl<'d> BleConnector<'d> { /// Create and init a new BLE connector. #[instability::unstable] pub fn new( - _init: &'d Controller<'d>, device: crate::hal::peripherals::BT<'d>, config: Config, - ) -> Result, InvalidConfigError> { + ) -> Result, BleInitError> { + let _guard = RadioRefGuard::new().map_err(BleInitError::RadioInit)?; + config.validate()?; + Ok(Self { _phy_init_guard: crate::ble::ble_init(&config), _device: device, + _guard, }) } diff --git a/esp-radio/src/lib.rs b/esp-radio/src/lib.rs index 9fc88b6c2..50fa44a47 100644 --- a/esp-radio/src/lib.rs +++ b/esp-radio/src/lib.rs @@ -136,8 +136,6 @@ extern crate alloc; // MUST be the first module mod fmt; -use core::marker::PhantomData; - use esp_hal::{ self as hal, @@ -226,6 +224,9 @@ pub(crate) mod memory_fence; pub(crate) static ESP_RADIO_LOCK: RawMutex = RawMutex::new(); +static RADIO_REFCOUNT: critical_section::Mutex> = + critical_section::Mutex::new(core::cell::Cell::new(0)); + // this is just to verify that we use the correct defaults in `build.rs` #[allow(clippy::assertions_on_constants)] // TODO: try assert_eq once it's usable in const context const _: () = { @@ -243,30 +244,6 @@ const _: () = { }; }; -#[derive(Debug, PartialEq, PartialOrd)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Controller for the ESP Radio driver. -pub struct Controller<'d> { - _inner: PhantomData<&'d ()>, -} - -impl Drop for Controller<'_> { - fn drop(&mut self) { - // Disable coexistence - #[cfg(coex)] - { - unsafe { crate::wifi::os_adapter::coex_disable() }; - unsafe { crate::wifi::os_adapter::coex_deinit() }; - } - - shutdown_radio_isr(); - - #[cfg(esp32)] - // Allow using `ADC2` again - release_adc2(unsafe { esp_hal::Internal::conjure() }); - } -} - #[procmacros::doc_replace] /// Initialize for using Wi-Fi and or BLE. /// @@ -289,35 +266,12 @@ impl Drop for Controller<'_> { )] /// - The function may return an error if interrupts are disabled. /// - The function may return an error if initializing the underlying driver fails. -/// -/// ## Example -/// -/// For examples of the necessary setup, see your RTOS's documentation. If you are -/// using the `esp-rtos` crate, you will need to initialize the scheduler before calling this -/// function: -/// -/// ```rust, no_run -/// # {before_snippet} -/// use esp_hal::{interrupt::software::SoftwareInterruptControl, timer::timg::TimerGroup}; -/// -/// let timg0 = TimerGroup::new(peripherals.TIMG0); -/// let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); -/// esp_rtos::start(timg0.timer0, software_interrupt.software_interrupt0); -/// -/// // You can now start esp-radio: -/// let esp_radio_controller = esp_radio::init().unwrap(); -/// # {after_snippet} -/// ``` -pub fn init<'d>() -> Result, InitializationError> { +pub(crate) fn init() -> Result<(), InitializationError> { #[cfg(esp32)] if try_claim_adc2(unsafe { hal::Internal::conjure() }).is_err() { return Err(InitializationError::Adc2IsUsed); } - if crate::is_interrupts_disabled() { - return Err(InitializationError::InterruptsDisabled); - } - if !preempt::initialized() { return Err(InitializationError::SchedulerNotInitialized); } @@ -339,12 +293,75 @@ pub fn init<'d>() -> Result, InitializationError> { #[cfg(coex)] match crate::wifi::coex_initialize() { 0 => {} - error => return Err(InitializationError::Internal), + error => panic!("Failed to initialize coexistence, error code: {}", error), } - Ok(Controller { - _inner: PhantomData, - }) + debug!("Radio initialized"); + + Ok(()) +} + +pub(crate) fn deinit() { + // Disable coexistence + #[cfg(coex)] + { + unsafe { crate::wifi::os_adapter::coex_disable() }; + unsafe { crate::wifi::os_adapter::coex_deinit() }; + } + + shutdown_radio_isr(); + + #[cfg(esp32)] + // Allow using `ADC2` again + release_adc2(unsafe { esp_hal::Internal::conjure() }); + + debug!("Radio deinitialized"); +} + +/// Management of the global reference count +/// and conditional hardware initialization/deinitialization. +#[derive(Debug)] +pub(crate) struct RadioRefGuard; + +impl RadioRefGuard { + /// Increments the refcount. If the old count was 0, it performs hardware init. + /// If hardware init fails, it rolls back the refcount only once. + fn new() -> Result { + critical_section::with(|cs| { + debug!("Creating RadioRefGuard"); + let rc = RADIO_REFCOUNT.borrow(cs); + + let prev = rc.get(); + rc.set(prev + 1); + + if prev == 0 + && let Err(e) = init() + { + rc.set(prev); + return Err(e); + } + + Ok(RadioRefGuard) + }) + } +} + +impl Drop for RadioRefGuard { + /// Decrements the refcount. If the count drops to 0, it performs hardware de-init. + fn drop(&mut self) { + critical_section::with(|cs| { + debug!("Dropping RadioRefGuard"); + let rc = RADIO_REFCOUNT.borrow(cs); + + let prev = rc.get(); + rc.set(prev - 1); + + if prev == 1 { + // Last user dropped, run de-initialization + deinit(); + } + }); + } } /// Returns true if at least some interrupt levels are disabled. @@ -360,19 +377,14 @@ fn is_interrupts_disabled() -> bool { #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Error which can be returned during [`init`]. +/// Error which can be returned during radio initialization. #[non_exhaustive] pub enum InitializationError { - /// An internal error occurred. - Internal, /// An error from the Wi-Fi driver. #[cfg(feature = "wifi")] WifiError(WifiError), /// The current CPU clock frequency is too low. WrongClockConfig, - /// Tried to initialize while interrupts are disabled. - /// This is not supported. - InterruptsDisabled, /// The scheduler is not initialized. SchedulerNotInitialized, #[cfg(esp32)] @@ -385,7 +397,6 @@ impl core::error::Error for InitializationError {} impl core::fmt::Display for InitializationError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - InitializationError::Internal => write!(f, "An internal error occurred"), #[cfg(feature = "wifi")] InitializationError::WifiError(e) => { write!(f, "Wi-Fi driver related error occurred: {e}") @@ -393,10 +404,6 @@ impl core::fmt::Display for InitializationError { InitializationError::WrongClockConfig => { write!(f, "The current CPU clock frequency is too low") } - InitializationError::InterruptsDisabled => write!( - f, - "Attempted to initialize while interrupts are disabled (Unsupported)" - ), InitializationError::SchedulerNotInitialized => { write!(f, "The scheduler is not initialized") } diff --git a/esp-radio/src/wifi/mod.rs b/esp-radio/src/wifi/mod.rs index 5f1a9d0fd..59ba7c562 100644 --- a/esp-radio/src/wifi/mod.rs +++ b/esp-radio/src/wifi/mod.rs @@ -40,7 +40,8 @@ use self::{ #[instability::unstable] pub use crate::sys::include::wifi_csi_info_t; // FIXME use crate::{ - Controller, + InitializationError, + RadioRefGuard, common_adapter::*, esp_wifi_result, hal::ram, @@ -730,6 +731,16 @@ pub enum WifiError { // definitive and exhausting list of errors we should expect and panicking because of an // unmapped error. Unknown(i32), + + /// The current CPU clock frequency is too low. + WrongClockConfig, + + /// The scheduler is not initialized. + SchedulerNotInitialized, + + #[cfg(esp32)] + /// ADC2 is required by esp-radio, but it is in use by esp-hal. + Adc2IsUsed, } impl WifiError { @@ -809,12 +820,35 @@ impl core::fmt::Display for WifiError { WifiError::Unknown(_) => { write!(f, "An unknown error was reported by the Wi-Fi driver.") } + WifiError::WrongClockConfig => { + write!(f, "The current CPU clock frequency is too low") + } + WifiError::SchedulerNotInitialized => { + write!(f, "The scheduler is not initialized") + } + #[cfg(esp32)] + WifiError::Adc2IsUsed => write!( + f, + "ADC2 cannot be used with `radio` functionality on `esp32`" + ), } } } impl core::error::Error for WifiError {} +impl From for WifiError { + fn from(err: InitializationError) -> Self { + match err { + InitializationError::WifiError(e) => e, + InitializationError::SchedulerNotInitialized => WifiError::SchedulerNotInitialized, + InitializationError::WrongClockConfig => WifiError::WrongClockConfig, + #[cfg(esp32)] + InitializationError::Adc2IsUsed => WifiError::Adc2IsUsed, + } + } +} + /// Events generated by the Wi-Fi driver. #[derive(Debug, FromPrimitive, EnumSetType)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1014,7 +1048,7 @@ pub(crate) unsafe extern "C" fn coex_init() -> i32 { 0 } -fn wifi_deinit() -> Result<(), crate::InitializationError> { +fn wifi_deinit() -> Result<(), crate::WifiError> { esp_wifi_result!(unsafe { esp_wifi_stop() })?; esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?; esp_wifi_result!(unsafe { esp_supplicant_deinit() })?; @@ -2142,10 +2176,12 @@ impl Config { /// Make sure to **not** call this function while interrupts are disabled, or IEEE 802.15.4 is /// currently in use. pub fn new<'d>( - _inited: &'d Controller<'d>, device: crate::hal::peripherals::WIFI<'d>, config: Config, ) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> { + let _guard = RadioRefGuard::new()?; + + // TODO: Re-check, if not having interrupts disabled pre-condition is still true if crate::is_interrupts_disabled() { return Err(WifiError::Unsupported); } @@ -2203,6 +2239,7 @@ pub fn new<'d>( // Only create WifiController after we've enabled TRNG - otherwise returning an error from this // function will cause panic because WifiController::drop tries to disable the TRNG. let mut controller = WifiController { + _guard, _phantom: Default::default(), beacon_timeout: 6, ap_beacon_timeout: 100, @@ -2232,6 +2269,7 @@ pub fn new<'d>( /// Wi-Fi controller. #[non_exhaustive] pub struct WifiController<'d> { + _guard: RadioRefGuard, _phantom: PhantomData<&'d ()>, // Things we have to remember due to how esp-wifi works: beacon_timeout: u16, @@ -2283,10 +2321,8 @@ impl WifiController<'_> { /// # use esp_radio::wifi::{ap::AccessPointConfig, ModeConfig}; /// use esp_radio::wifi::Protocol; /// - /// let esp_radio_controller = esp_radio::init().unwrap(); - /// /// let (mut wifi_controller, _interfaces) = - /// esp_radio::wifi::new(&esp_radio_controller, peripherals.WIFI, Default::default())?; + /// esp_radio::wifi::new(peripherals.WIFI, Default::default())?; /// /// wifi_controller.set_config(&ModeConfig::AccessPoint( /// AccessPointConfig::default().with_ssid("esp-radio".into()), diff --git a/examples/ble/bas_peripheral/src/main.rs b/examples/ble/bas_peripheral/src/main.rs index 718f1dd16..c46058c85 100644 --- a/examples/ble/bas_peripheral/src/main.rs +++ b/examples/ble/bas_peripheral/src/main.rs @@ -15,7 +15,6 @@ use esp_hal::{ }; use esp_radio::ble::controller::BleConnector; use log::{info, warn}; -use static_cell::StaticCell; use trouble_host::prelude::*; esp_bootloader_esp_idf::esp_app_desc!(); @@ -29,11 +28,8 @@ async fn main(_s: Spawner) { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - static RADIO: StaticCell> = StaticCell::new(); - let radio = RADIO.init(esp_radio::init().unwrap()); - let bluetooth = peripherals.BT; - let connector = BleConnector::new(radio, bluetooth, Default::default()).unwrap(); + let connector = BleConnector::new(bluetooth, Default::default()).unwrap(); let controller: ExternalController<_, 1> = ExternalController::new(connector); ble_bas_peripheral_run(controller).await; diff --git a/examples/ble/scanner/src/main.rs b/examples/ble/scanner/src/main.rs index 8706a4308..0aabf6747 100644 --- a/examples/ble/scanner/src/main.rs +++ b/examples/ble/scanner/src/main.rs @@ -19,7 +19,6 @@ use esp_hal::{ use esp_radio::ble::controller::BleConnector; use heapless::Deque; use log::info; -use static_cell::StaticCell; use trouble_host::prelude::*; esp_bootloader_esp_idf::esp_app_desc!(); @@ -35,11 +34,8 @@ async fn main(_s: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - static RADIO: StaticCell> = StaticCell::new(); - let radio = RADIO.init(esp_radio::init().unwrap()); - let bluetooth = peripherals.BT; - let connector = BleConnector::new(radio, bluetooth, Default::default()).unwrap(); + let connector = BleConnector::new(bluetooth, Default::default()).unwrap(); let controller: ExternalController<_, 1> = ExternalController::new(connector); ble_scanner_run(controller).await; diff --git a/examples/esp-now/embassy_esp_now/src/main.rs b/examples/esp-now/embassy_esp_now/src/main.rs index 5ca27dd68..2d6f3d89a 100644 --- a/examples/esp-now/embassy_esp_now/src/main.rs +++ b/examples/esp-now/embassy_esp_now/src/main.rs @@ -16,23 +16,10 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::println; -use esp_radio::{ - Controller, - esp_now::{BROADCAST_ADDRESS, PeerInfo}, -}; +use esp_radio::esp_now::{BROADCAST_ADDRESS, PeerInfo}; esp_bootloader_esp_idf::esp_app_desc!(); -// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - 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) -> ! { esp_println::logger::init_logger_from_env(); @@ -45,11 +32,8 @@ async fn main(_spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let wifi = peripherals.WIFI; - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap(); + let (mut controller, interfaces) = esp_radio::wifi::new(wifi, Default::default()).unwrap(); controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/esp-now/embassy_esp_now_duplex/src/main.rs b/examples/esp-now/embassy_esp_now_duplex/src/main.rs index 7dff6ed09..f92f4dd0c 100644 --- a/examples/esp-now/embassy_esp_now_duplex/src/main.rs +++ b/examples/esp-now/embassy_esp_now_duplex/src/main.rs @@ -17,9 +17,12 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::println; -use esp_radio::{ - Controller, - esp_now::{BROADCAST_ADDRESS, EspNowManager, EspNowReceiver, EspNowSender, PeerInfo}, +use esp_radio::esp_now::{ + BROADCAST_ADDRESS, + EspNowManager, + EspNowReceiver, + EspNowSender, + PeerInfo, }; esp_bootloader_esp_idf::esp_app_desc!(); @@ -46,11 +49,8 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let wifi = peripherals.WIFI; - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap(); + let (mut controller, interfaces) = esp_radio::wifi::new(wifi, Default::default()).unwrap(); controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/esp-now/esp_now/src/main.rs b/examples/esp-now/esp_now/src/main.rs index 5b19a4b9a..bdaf36629 100644 --- a/examples/esp-now/esp_now/src/main.rs +++ b/examples/esp-now/esp_now/src/main.rs @@ -31,11 +31,8 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = esp_radio::init().unwrap(); - let wifi = peripherals.WIFI; - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap(); + let (mut controller, interfaces) = esp_radio::wifi::new(wifi, Default::default()).unwrap(); controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/wifi/80211_tx/src/main.rs b/examples/wifi/80211_tx/src/main.rs index f8bfe7671..b1398667f 100644 --- a/examples/wifi/80211_tx/src/main.rs +++ b/examples/wifi/80211_tx/src/main.rs @@ -50,11 +50,9 @@ fn main() -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = esp_radio::init().unwrap(); - // We must initialize some kind of interface and start it. let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); controller.set_mode(wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/wifi/access_point/src/main.rs b/examples/wifi/access_point/src/main.rs index 7e6ff8558..b02c7b4eb 100644 --- a/examples/wifi/access_point/src/main.rs +++ b/examples/wifi/access_point/src/main.rs @@ -67,10 +67,8 @@ fn main() -> ! { ); }); - let esp_radio_ctrl = esp_radio::init().unwrap(); - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.ap; let iface = create_interface(&mut device); diff --git a/examples/wifi/access_point_with_sta/src/main.rs b/examples/wifi/access_point_with_sta/src/main.rs index 15e653cb1..e182185b7 100644 --- a/examples/wifi/access_point_with_sta/src/main.rs +++ b/examples/wifi/access_point_with_sta/src/main.rs @@ -53,10 +53,8 @@ fn main() -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = esp_radio::init().unwrap(); - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let mut ap_device = interfaces.ap; let ap_interface = create_interface(&mut ap_device); diff --git a/examples/wifi/coex/src/main.rs b/examples/wifi/coex/src/main.rs index 17a60aaf2..01efe4521 100644 --- a/examples/wifi/coex/src/main.rs +++ b/examples/wifi/coex/src/main.rs @@ -74,13 +74,11 @@ fn main() -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = esp_radio::init().unwrap(); - let now = || time::Instant::now().duration_since_epoch().as_millis(); // initializing Bluetooth first results in a more stable WiFi connection on // ESP32 - let connector = BleConnector::new(&esp_radio_ctrl, peripherals.BT, Default::default()).unwrap(); + let connector = BleConnector::new(peripherals.BT, Default::default()).unwrap(); let hci = HciConnector::new(connector, now); let mut ble = Ble::new(&hci); @@ -102,7 +100,7 @@ fn main() -> ! { println!("started advertising"); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device); diff --git a/examples/wifi/dhcp/src/main.rs b/examples/wifi/dhcp/src/main.rs index c30a0b9d8..266ec883d 100644 --- a/examples/wifi/dhcp/src/main.rs +++ b/examples/wifi/dhcp/src/main.rs @@ -50,10 +50,8 @@ fn main() -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = esp_radio::init().unwrap(); - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device); diff --git a/examples/wifi/embassy_access_point/src/main.rs b/examples/wifi/embassy_access_point/src/main.rs index 91d17f620..1a6c6097d 100644 --- a/examples/wifi/embassy_access_point/src/main.rs +++ b/examples/wifi/embassy_access_point/src/main.rs @@ -34,9 +34,13 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::{print, println}; -use esp_radio::{ - Controller, - wifi::{ModeConfig, WifiApState, WifiController, WifiDevice, WifiEvent, ap::AccessPointConfig}, +use esp_radio::wifi::{ + ModeConfig, + WifiApState, + WifiController, + WifiDevice, + WifiEvent, + ap::AccessPointConfig, }; esp_bootloader_esp_idf::esp_app_desc!(); @@ -66,10 +70,8 @@ async fn main(spawner: Spawner) -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let (controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let device = interfaces.ap; diff --git a/examples/wifi/embassy_access_point_with_sta/src/main.rs b/examples/wifi/embassy_access_point_with_sta/src/main.rs index ddda00439..e14e968c0 100644 --- a/examples/wifi/embassy_access_point_with_sta/src/main.rs +++ b/examples/wifi/embassy_access_point_with_sta/src/main.rs @@ -42,17 +42,14 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::{print, println}; -use esp_radio::{ - Controller, - wifi::{ - ModeConfig, - WifiApState, - WifiController, - WifiDevice, - WifiEvent, - ap::AccessPointConfig, - sta::ClientConfig, - }, +use esp_radio::wifi::{ + ModeConfig, + WifiApState, + WifiController, + WifiDevice, + WifiEvent, + ap::AccessPointConfig, + sta::ClientConfig, }; esp_bootloader_esp_idf::esp_app_desc!(); @@ -83,10 +80,8 @@ async fn main(spawner: Spawner) -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let wifi_ap_device = interfaces.ap; let wifi_sta_device = interfaces.sta; diff --git a/examples/wifi/embassy_dhcp/src/main.rs b/examples/wifi/embassy_dhcp/src/main.rs index 367c438e4..f22f9ac39 100644 --- a/examples/wifi/embassy_dhcp/src/main.rs +++ b/examples/wifi/embassy_dhcp/src/main.rs @@ -24,17 +24,14 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::println; -use esp_radio::{ - Controller, - wifi::{ - ModeConfig, - ScanConfig, - WifiController, - WifiDevice, - WifiEvent, - WifiStaState, - sta::ClientConfig, - }, +use esp_radio::wifi::{ + ModeConfig, + ScanConfig, + WifiController, + WifiDevice, + WifiEvent, + WifiStaState, + sta::ClientConfig, }; esp_bootloader_esp_idf::esp_app_desc!(); @@ -65,10 +62,8 @@ async fn main(spawner: Spawner) -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let (controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let wifi_interface = interfaces.sta; diff --git a/examples/wifi/embassy_sntp/src/main.rs b/examples/wifi/embassy_sntp/src/main.rs index 2e0228bfa..2b627a7e8 100644 --- a/examples/wifi/embassy_sntp/src/main.rs +++ b/examples/wifi/embassy_sntp/src/main.rs @@ -30,17 +30,14 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::println; -use esp_radio::{ - Controller, - wifi::{ - ModeConfig, - ScanConfig, - WifiController, - WifiDevice, - WifiEvent, - WifiStaState, - sta::ClientConfig, - }, +use esp_radio::wifi::{ + ModeConfig, + ScanConfig, + WifiController, + WifiDevice, + WifiEvent, + WifiStaState, + sta::ClientConfig, }; use log::{error, info}; use sntpc::{NtpContext, NtpTimestampGenerator, get_time}; @@ -98,10 +95,8 @@ async fn main(spawner: Spawner) -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let (controller, interfaces) = - esp_radio::wifi::new(esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let wifi_interface = interfaces.sta; diff --git a/examples/wifi/sniffer/src/main.rs b/examples/wifi/sniffer/src/main.rs index 985cf2808..d1efe29ae 100644 --- a/examples/wifi/sniffer/src/main.rs +++ b/examples/wifi/sniffer/src/main.rs @@ -41,11 +41,9 @@ fn main() -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = esp_radio::init().unwrap(); - // We must initialize some kind of interface and start it. let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); controller.set_mode(wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/wifi/static_ip/src/main.rs b/examples/wifi/static_ip/src/main.rs index c4221ce1a..8418902e5 100644 --- a/examples/wifi/static_ip/src/main.rs +++ b/examples/wifi/static_ip/src/main.rs @@ -45,10 +45,8 @@ fn main() -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = esp_radio::init().unwrap(); - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device); diff --git a/hil-test/src/bin/esp_radio/ble_controller.rs b/hil-test/src/bin/esp_radio/ble_controller.rs index 5d37f2af7..60955c903 100644 --- a/hil-test/src/bin/esp_radio/ble_controller.rs +++ b/hil-test/src/bin/esp_radio/ble_controller.rs @@ -28,18 +28,18 @@ mod tests { // Compile-time test to check that esp-radio can be reinitialized. fn _esp_radio_can_be_reinited() { - let p = esp_hal::init(esp_hal::Config::default()); + let mut p = esp_hal::init(esp_hal::Config::default()); let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0); let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0); { - let _init = esp_radio::init().unwrap(); + let _connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap(); } { - let _init = esp_radio::init().unwrap(); + let _connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap(); } } const H4_TYPE_COMMAND: u8 = 1; @@ -60,9 +60,8 @@ mod tests { let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0); let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0); - let init = esp_radio::init().unwrap(); - let mut connector = BleConnector::new(&init, p.BT, Default::default()).unwrap(); + let mut connector = BleConnector::new(p.BT, Default::default()).unwrap(); // send reset cmd pub const fn opcode(ogf: u8, ocf: u16) -> [u8; 2] { @@ -111,9 +110,8 @@ mod tests { let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0); let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0); - let init = esp_radio::init().unwrap(); - let mut connector = BleConnector::new(&init, p.BT, Default::default()).unwrap(); + let mut connector = BleConnector::new(p.BT, Default::default()).unwrap(); // send reset cmd pub const fn opcode(ogf: u8, ocf: u16) -> [u8; 2] { diff --git a/hil-test/src/bin/esp_radio/init_tests.rs b/hil-test/src/bin/esp_radio/init_tests.rs index 247d33f11..e30ddd3cb 100644 --- a/hil-test/src/bin/esp_radio/init_tests.rs +++ b/hil-test/src/bin/esp_radio/init_tests.rs @@ -2,6 +2,8 @@ mod init_tests { use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal}; + #[cfg(soc_has_wifi)] + use esp_hal::peripherals::WIFI; #[cfg(riscv)] use esp_hal::riscv::interrupt::free as interrupt_free; #[cfg(xtensa)] @@ -15,21 +17,26 @@ mod init_tests { peripherals::{Peripherals, TIMG0}, timer::timg::TimerGroup, }; - use esp_radio::InitializationError; + #[cfg(soc_has_bt)] + use esp_radio::ble::controller::BleConnector; + #[cfg(soc_has_wifi)] + use esp_radio::wifi::WifiError; use esp_rtos::embassy::InterruptExecutor; use hil_test::mk_static; use static_cell::StaticCell; #[embassy_executor::task] + #[cfg(soc_has_wifi)] async fn try_init( - signal: &'static Signal>, + signal: &'static Signal>, + wifi_peripheral: WIFI<'static>, timer: TIMG0<'static>, sw_int0: SoftwareInterrupt<'static, 0>, ) { let timg0 = TimerGroup::new(timer); esp_rtos::start(timg0.timer0, sw_int0); - match esp_radio::init() { + match esp_radio::wifi::new(wifi_peripheral, Default::default()) { Ok(_) => signal.signal(None), Err(err) => signal.signal(Some(err)), } @@ -44,39 +51,45 @@ mod init_tests { } #[test] - fn test_init_fails_without_scheduler(_peripherals: Peripherals) { + #[cfg(soc_has_wifi)] + fn test_init_fails_without_scheduler(p: Peripherals) { // esp-rtos must be initialized before esp-radio. - let init = esp_radio::init(); + let init = esp_radio::wifi::new(p.WIFI, Default::default()); - assert!(matches!( - init, - Err(InitializationError::SchedulerNotInitialized), - )); + assert!(matches!(init, Err(WifiError::SchedulerNotInitialized))); } #[test] + #[cfg(soc_has_wifi)] fn test_init_fails_cs(p: Peripherals) { let timg0 = TimerGroup::new(p.TIMG0); let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0); - let init = critical_section::with(|_| esp_radio::init()); + let init = critical_section::with(|_| esp_radio::wifi::new(p.WIFI, Default::default())); - assert!(matches!(init, Err(InitializationError::InterruptsDisabled))); + match init { + Ok(_) => defmt::info!("Initialized wifi in critical section"), + Err(ref e) => defmt::info!("Failed to initialize wifi in critical section: {:?}", e), + } + + assert!(matches!(init, Err(WifiError::Unsupported))); } #[test] + #[cfg(soc_has_wifi)] fn test_init_fails_interrupt_free(p: Peripherals) { let timg0 = TimerGroup::new(p.TIMG0); let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0); - let init = interrupt_free(|| esp_radio::init()); + let init = interrupt_free(|| esp_radio::wifi::new(p.WIFI, Default::default())); - assert!(matches!(init, Err(InitializationError::InterruptsDisabled))); + assert!(matches!(init, Err(WifiError::Unsupported))); } #[test] + #[cfg(soc_has_wifi)] async fn test_init_fails_in_interrupt_executor_task(p: Peripherals) { let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT); @@ -86,17 +99,19 @@ mod init_tests { let spawner = executor_core0.start(Priority::Priority1); - let signal = - mk_static!(Signal>, Signal::new()); + let signal = mk_static!(Signal>, + Signal::new()); - spawner.must_spawn(try_init(signal, p.TIMG0, sw_ints.software_interrupt0)); + spawner.must_spawn(try_init( + signal, + p.WIFI, + p.TIMG0, + sw_ints.software_interrupt0, + )); let res = signal.wait().await; - assert!(matches!( - res, - Some(esp_radio::InitializationError::InterruptsDisabled), - )); + assert!(matches!(res, Some(WifiError::Unsupported))); } #[test] @@ -106,16 +121,52 @@ mod init_tests { let timg0 = TimerGroup::new(p.TIMG0); esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0); - let esp_radio_ctrl = - &*mk_static!(esp_radio::Controller<'static>, esp_radio::init().unwrap()); - // Initialize, then de-initialize wifi - let wifi = - esp_radio::wifi::new(&esp_radio_ctrl, p.WIFI.reborrow(), Default::default()).unwrap(); + let wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap(); drop(wifi); // Now, can we do it again? - let _wifi = - esp_radio::wifi::new(&esp_radio_ctrl, p.WIFI.reborrow(), Default::default()).unwrap(); + let _wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap(); + } + + #[test] + #[cfg(soc_has_wifi)] + #[cfg(soc_has_bt)] + fn test_init_and_drop(mut p: Peripherals) { + let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0); + let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT); + esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0); + + // Initialize BLE and WiFi then drop BLE + let connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap(); + let wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap(); + drop(connector); + + // Re-initialize BLE and drop WiFi and BLE + let connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap(); + drop(wifi); + drop(connector); + } + + #[test] + #[cfg(soc_has_wifi)] + #[cfg(soc_has_bt)] + fn test_create_ble_wifi_drop_ble_wifi_create_wifi_ble(mut p: Peripherals) { + let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0); + let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT); + esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0); + + // Initialize WiFi and BLE then drop BLE and WiFi + let wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap(); + let connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap(); + + drop(connector); + drop(wifi); + + // Re-initialize WiFi and BLE then drop WiFi + let wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap(); + let _connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap(); + + drop(wifi); } } diff --git a/qa-test/Cargo.toml b/qa-test/Cargo.toml index 18ffe4a6c..ff5d5db3a 100644 --- a/qa-test/Cargo.toml +++ b/qa-test/Cargo.toml @@ -6,6 +6,10 @@ license = "MIT OR Apache-2.0" publish = false [dependencies] +bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8", features = [ + "async", + "macros", +] } blocking-network-stack = { git = "https://github.com/bjoernQ/blocking-network-stack.git", rev = "b3ecefc", optional = true } cfg-if = "1" embassy-executor = "0.9.0" diff --git a/qa-test/src/bin/embassy_wifi_bench.rs b/qa-test/src/bin/embassy_wifi_bench.rs index f5317f534..91e5c43eb 100644 --- a/qa-test/src/bin/embassy_wifi_bench.rs +++ b/qa-test/src/bin/embassy_wifi_bench.rs @@ -33,9 +33,13 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::println; -use esp_radio::{ - Controller, - wifi::{ModeConfig, WifiController, WifiDevice, WifiEvent, WifiStaState, sta::ClientConfig}, +use esp_radio::wifi::{ + ModeConfig, + WifiController, + WifiDevice, + WifiEvent, + WifiStaState, + sta::ClientConfig, }; esp_bootloader_esp_idf::esp_app_desc!(); @@ -81,10 +85,8 @@ async fn main(spawner: Spawner) -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let wifi_interface = interfaces.sta; diff --git a/qa-test/src/bin/embassy_wifi_i2s.rs b/qa-test/src/bin/embassy_wifi_i2s.rs index 1c4eb9f77..bff328cef 100644 --- a/qa-test/src/bin/embassy_wifi_i2s.rs +++ b/qa-test/src/bin/embassy_wifi_i2s.rs @@ -22,17 +22,14 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::println; -use esp_radio::{ - Controller, - wifi::{ - ModeConfig, - WifiController, - WifiDevice, - WifiEvent, - WifiStaState, - sta::ClientConfig, - sta_state, - }, +use esp_radio::wifi::{ + ModeConfig, + WifiController, + WifiDevice, + WifiEvent, + WifiStaState, + sta::ClientConfig, + sta_state, }; use static_cell::StaticCell; @@ -245,9 +242,8 @@ async fn main(spawner: Spawner) { .build(rx_descriptors); // WiFi + network stack - let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); let (controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let wifi_interface = interfaces.sta; let config = embassy_net::Config::dhcpv4(Default::default()); diff --git a/qa-test/src/bin/embassy_wifi_stress.rs b/qa-test/src/bin/embassy_wifi_stress.rs index 093e64c2b..07c2c5a72 100644 --- a/qa-test/src/bin/embassy_wifi_stress.rs +++ b/qa-test/src/bin/embassy_wifi_stress.rs @@ -9,7 +9,7 @@ extern crate alloc; -use alloc::{boxed::Box, string::ToString}; +use alloc::string::ToString; use embassy_executor::Spawner; use embassy_time::{Duration, Timer, WithTimeout}; @@ -42,13 +42,8 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_wifi_ctrl = Box::leak(Box::new(esp_radio::init().unwrap())); - let (mut controller, _interfaces) = esp_radio::wifi::new( - esp_wifi_ctrl, - peripherals.WIFI.reborrow(), - Config::default(), - ) - .unwrap(); + let (mut controller, _interfaces) = + esp_radio::wifi::new(peripherals.WIFI.reborrow(), Config::default()).unwrap(); let mut i = 0; loop { diff --git a/qa-test/src/bin/wifi_bench.rs b/qa-test/src/bin/wifi_bench.rs index 3fc530cb6..8208a0dde 100644 --- a/qa-test/src/bin/wifi_bench.rs +++ b/qa-test/src/bin/wifi_bench.rs @@ -69,10 +69,8 @@ fn main() -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = esp_radio::init().unwrap(); - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device); diff --git a/qa-test/src/bin/wifi_csi.rs b/qa-test/src/bin/wifi_csi.rs index e2caed08c..cae247715 100644 --- a/qa-test/src/bin/wifi_csi.rs +++ b/qa-test/src/bin/wifi_csi.rs @@ -46,10 +46,8 @@ fn main() -> ! { let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); - let esp_radio_ctrl = esp_radio::init().unwrap(); - let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device); diff --git a/qa-test/src/bin/wifi_survives_ble_drop.rs b/qa-test/src/bin/wifi_survives_ble_drop.rs new file mode 100644 index 000000000..1ca030caf --- /dev/null +++ b/qa-test/src/bin/wifi_survives_ble_drop.rs @@ -0,0 +1,231 @@ +//! This example creates BLE and WiFi and while waiting for WiFi to connect, BLE gets dropped - it +//! shows that WiFi still works afterwards. +//! +//! - set SSID and PASSWORD env variable +//! - gets an ip address via DHCP +//! - performs an HTTP get request to some "random" server +//! +//! Note: On ESP32-C2 and ESP32-C3 you need a wifi-heap size of 70000, on +//! ESP32-C6 you need 80000 and a tx_queue_size of 10 + +//% FEATURES: esp-radio esp-radio/wifi esp-radio/ble esp-radio/smoltcp +//% FEATURES: esp-radio/unstable esp-hal/unstable +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32s3 + +#![no_std] +#![no_main] + +use core::net::Ipv4Addr; + +use bleps::{ + Ble, + HciConnector, + ad_structure::{ + AdStructure, + BR_EDR_NOT_SUPPORTED, + LE_GENERAL_DISCOVERABLE, + create_advertising_data, + }, + att::Uuid, +}; +use blocking_network_stack::Stack; +use embedded_io::*; +use esp_alloc as _; +use esp_backtrace as _; +use esp_hal::{ + clock::CpuClock, + interrupt::software::SoftwareInterruptControl, + main, + ram, + rng::Rng, + time::{self, Duration}, + timer::timg::TimerGroup, +}; +use esp_println::{print, println}; +use esp_radio::{ + ble::controller::BleConnector, + wifi::{ModeConfig, sta::ClientConfig}, +}; +use smoltcp::{ + iface::{SocketSet, SocketStorage}, + wire::{DhcpOption, IpAddress}, +}; + +esp_bootloader_esp_idf::esp_app_desc!(); + +const SSID: &str = env!("SSID"); +const PASSWORD: &str = env!("PASSWORD"); + +#[main] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); + + // COEX needs more RAM - add some more + #[cfg(feature = "esp32")] + { + esp_alloc::heap_allocator!(#[ram(reclaimed)] size: 96 * 1024); + esp_alloc::heap_allocator!(size: 24 * 1024); + } + #[cfg(not(feature = "esp32"))] + { + esp_alloc::heap_allocator!(#[ram(reclaimed)] size: 64 * 1024); + esp_alloc::heap_allocator!(size: 64 * 1024); + } + + let timg0 = TimerGroup::new(peripherals.TIMG0); + let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + esp_rtos::start(timg0.timer0, sw_int.software_interrupt0); + + let now = || time::Instant::now().duration_since_epoch().as_millis(); + + // initializing Bluetooth first results in a more stable WiFi connection on + // ESP32 + let connector = BleConnector::new(peripherals.BT, Default::default()).unwrap(); + let hci = HciConnector::new(connector, now); + let mut ble = Ble::new(&hci); + + println!("{:?}", ble.init()); + println!("{:?}", ble.cmd_set_le_advertising_parameters()); + println!( + "{:?}", + ble.cmd_set_le_advertising_data( + create_advertising_data(&[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ServiceUuids16(&[Uuid::Uuid16(0x1809)]), + AdStructure::CompleteLocalName(esp_hal::chip!()), + ]) + .unwrap() + ) + ); + println!("{:?}", ble.cmd_set_le_advertise_enable(true)); + + println!("started advertising"); + + let (mut controller, interfaces) = + esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap(); + + let mut device = interfaces.sta; + let iface = create_interface(&mut device); + + controller + .set_power_saving(esp_radio::wifi::PowerSaveMode::None) + .unwrap(); + + let mut socket_set_entries: [SocketStorage; 3] = Default::default(); + let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); + let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); + // we can set a hostname here (or add other DHCP options) + dhcp_socket.set_outgoing_options(&[DhcpOption { + kind: 12, + data: b"esp-radio", + }]); + socket_set.add(dhcp_socket); + + let rng = Rng::new(); + let stack = Stack::new(iface, device, socket_set, now, rng.random()); + + let client_config = ModeConfig::Client( + ClientConfig::default() + .with_ssid(SSID.into()) + .with_password(PASSWORD.into()), + ); + + let res = controller.set_config(&client_config); + println!("wifi_set_configuration returned {:?}", res); + + controller.start().unwrap(); + println!("is wifi started: {:?}", controller.is_started()); + println!("{:?}", controller.capabilities()); + println!("wifi_connect {:?}", controller.connect()); + + drop(hci); + println!("Dropped BLE HCI"); + + // wait to get connected + println!("Wait to get connected"); + loop { + match controller.is_connected() { + Ok(true) => break, + Ok(false) => {} + Err(err) => { + println!("{:?}", err); + loop {} + } + } + } + println!("{:?}", controller.is_connected()); + + // wait for getting an ip address + println!("Wait to get an ip address"); + loop { + stack.work(); + + if stack.is_iface_up() { + println!("got ip {:?}", stack.get_ip_info()); + break; + } + } + + println!("Start busy loop on main"); + + let mut rx_buffer = [0u8; 128]; + let mut tx_buffer = [0u8; 128]; + let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer); + + loop { + println!("Making HTTP request"); + socket.work(); + + socket + .open(IpAddress::Ipv4(Ipv4Addr::new(142, 250, 185, 115)), 80) + .unwrap(); + + socket + .write(b"GET / HTTP/1.0\r\nHost: www.mobile-j.de\r\n\r\n") + .unwrap(); + socket.flush().unwrap(); + + let deadline = time::Instant::now() + Duration::from_secs(20); + let mut buffer = [0u8; 128]; + while let Ok(len) = socket.read(&mut buffer) { + let to_print = unsafe { core::str::from_utf8_unchecked(&buffer[..len]) }; + print!("{}", to_print); + + if time::Instant::now() > deadline { + println!("Timeout"); + break; + } + } + println!(); + + socket.disconnect(); + + let deadline = time::Instant::now() + Duration::from_secs(5); + while time::Instant::now() < deadline { + socket.work(); + } + } +} + +// some smoltcp boilerplate +fn timestamp() -> smoltcp::time::Instant { + smoltcp::time::Instant::from_micros( + esp_hal::time::Instant::now() + .duration_since_epoch() + .as_micros() as i64, + ) +} + +pub fn create_interface(device: &mut esp_radio::wifi::WifiDevice) -> smoltcp::iface::Interface { + // users could create multiple instances but since they only have one WifiDevice + // they probably can't do anything bad with that + smoltcp::iface::Interface::new( + smoltcp::iface::Config::new(smoltcp::wire::HardwareAddress::Ethernet( + smoltcp::wire::EthernetAddress::from_bytes(&device.mac_address()), + )), + device, + timestamp(), + ) +}