diff --git a/esp-wifi/CHANGELOG.md b/esp-wifi/CHANGELOG.md index 4338d273d0..d91c356937 100644 --- a/esp-wifi/CHANGELOG.md +++ b/esp-wifi/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `set_power_saving` is now also available when the `coex` feature is activated (#3081) +- Network interfaces and the controller are now more separated (#3027) + ### Fixed - Fixed a problem using BLE on ESP32-C6 when connected via Serial-JTAG (#2981) diff --git a/esp-wifi/Cargo.toml b/esp-wifi/Cargo.toml index 915ca6f056..9d9b74162d 100644 --- a/esp-wifi/Cargo.toml +++ b/esp-wifi/Cargo.toml @@ -128,9 +128,6 @@ csi = [] ## Provide implementations of smoltcp traits smoltcp = ["dep:smoltcp"] -## Provide utilities for smoltcp initialization. Adds smoltcp dependency -utils = ["smoltcp"] - ## Use builtin scheduler builtin-scheduler = [] diff --git a/esp-wifi/MIGRATING-0.11.md b/esp-wifi/MIGRATING-0.11.md index 04ec279247..fa9c37bfbd 100644 --- a/esp-wifi/MIGRATING-0.11.md +++ b/esp-wifi/MIGRATING-0.11.md @@ -27,3 +27,40 @@ As part of limiting public API changes due to config options, the `csi_enabled` -esp-wifi = { version = "0.12.0", features = ["wifi"] } +esp-wifi = { version = "0.12.0", features = ["wifi", "csi"] } ``` + +## Changed the way to get the WiFi controller and interfaces + +The network interfaces and the controller are now more separated. This way you can change between STA, AP and AP_STA mode easily without reconstructing the networking stacks. + +There is no convenience utility to create a `smoltcp` interface needed by blocking networking stacks anymore. You need your own implementation. + +Please note that networking stacks _might_ need to be reset when connecting to a different network interface (i.e. get a new IP address and routings) - `embassy-net` should manage to do that automatically. + +```diff +- let (iface, device, mut controller) = +- create_network_interface(&init, peripherals.WIFI, WifiStaDevice).unwrap(); ++ let (mut controller, interfaces) = ++ esp_wifi::wifi::new(&init, peripherals.WIFI).unwrap(); ++ let mut device = interfaces.sta; ++ let iface = create_interface(&mut device); + ... ++ 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_wifi::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(), ++ ) ++ } +``` diff --git a/esp-wifi/src/ble/btdm.rs b/esp-wifi/src/ble/btdm.rs index 6ac2d7bed7..b85c37048f 100644 --- a/esp-wifi/src/ble/btdm.rs +++ b/esp-wifi/src/ble/btdm.rs @@ -1,7 +1,7 @@ use alloc::boxed::Box; use core::ptr::{addr_of, addr_of_mut}; -use esp_wifi_sys::c_types::c_void; +use esp_wifi_sys::c_types::{c_char, c_void}; use portable_atomic::{AtomicBool, Ordering}; use super::ReceivedPacket; @@ -36,7 +36,7 @@ struct VhciHostCallbacks { extern "C" { fn btdm_osi_funcs_register(osi_funcs: *const osi_funcs_s) -> i32; - fn btdm_controller_get_compile_version() -> *const u8; + fn btdm_controller_get_compile_version() -> *const c_char; #[cfg(any(esp32c3, esp32s3))] fn btdm_controller_init(config_opts: *const esp_bt_controller_config_t) -> i32; @@ -208,7 +208,7 @@ unsafe extern "C" fn queue_recv_from_isr( unsafe extern "C" fn task_create( func: *mut crate::binary::c_types::c_void, - name: *const u8, + name: *const c_char, stack_depth: u32, param: *mut crate::binary::c_types::c_void, prio: u32, diff --git a/esp-wifi/src/ble/npl.rs b/esp-wifi/src/ble/npl.rs index d3820c424a..5077d7eac0 100644 --- a/esp-wifi/src/ble/npl.rs +++ b/esp-wifi/src/ble/npl.rs @@ -384,7 +384,7 @@ unsafe extern "C" fn task_create( task_handle: *const c_void, core_id: u32, ) -> i32 { - let name_str = str_from_c(name as *const _); + let name_str = str_from_c(name); trace!( "task_create {:?} {} {} {:?} {} {:?} {}", task_func, @@ -421,7 +421,7 @@ unsafe extern "C" fn task_delete(task: *const c_void) { } unsafe extern "C" fn osi_assert(ln: u32, fn_name: *const c_void, param1: u32, param2: u32) { - let name_str = str_from_c(fn_name as *const u8); + let name_str = str_from_c(fn_name as _); panic!("ASSERT {}:{} {} {}", name_str, ln, param1, param2); } diff --git a/esp-wifi/src/ble/os_adapter_esp32.rs b/esp-wifi/src/ble/os_adapter_esp32.rs index 2c79ad0bca..a8adc7e0b6 100644 --- a/esp-wifi/src/ble/os_adapter_esp32.rs +++ b/esp-wifi/src/ble/os_adapter_esp32.rs @@ -59,7 +59,7 @@ pub(super) struct osi_funcs_s { task_create: Option< unsafe extern "C" fn( *mut crate::binary::c_types::c_void, - *const u8, + *const crate::binary::c_types::c_char, u32, *mut crate::binary::c_types::c_void, u32, diff --git a/esp-wifi/src/ble/os_adapter_esp32c3.rs b/esp-wifi/src/ble/os_adapter_esp32c3.rs index 9b682017a4..bffb911a29 100644 --- a/esp-wifi/src/ble/os_adapter_esp32c3.rs +++ b/esp-wifi/src/ble/os_adapter_esp32c3.rs @@ -1,5 +1,8 @@ use super::*; -use crate::hal::{interrupt, peripherals::Interrupt}; +use crate::{ + binary::c_types::c_char, + hal::{interrupt, peripherals::Interrupt}, +}; pub(crate) static mut BT_INTERRUPT_FUNCTION5: ( *mut crate::binary::c_types::c_void, @@ -43,7 +46,7 @@ pub(super) struct osi_funcs_s { task_create: Option< unsafe extern "C" fn( *mut crate::binary::c_types::c_void, - *const u8, + *const c_char, u32, *mut crate::binary::c_types::c_void, u32, diff --git a/esp-wifi/src/ble/os_adapter_esp32s3.rs b/esp-wifi/src/ble/os_adapter_esp32s3.rs index 3a9c26dd87..880165bb3a 100644 --- a/esp-wifi/src/ble/os_adapter_esp32s3.rs +++ b/esp-wifi/src/ble/os_adapter_esp32s3.rs @@ -46,7 +46,7 @@ pub(super) struct osi_funcs_s { task_create: Option< unsafe extern "C" fn( *mut crate::binary::c_types::c_void, - *const u8, + *const crate::binary::c_types::c_char, u32, *mut crate::binary::c_types::c_void, u32, diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32c2.rs b/esp-wifi/src/common_adapter/common_adapter_esp32c2.rs index 911db6b3dd..548ee2d43b 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32c2.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32c2.rs @@ -33,7 +33,7 @@ pub(crate) unsafe fn phy_enable() { [0u8; core::mem::size_of::()]; let phy_version = get_phy_version_str(); - trace!("phy_version {}", str_from_c(phy_version as *const u8)); + trace!("phy_version {}", str_from_c(phy_version)); let init_data = &PHY_INIT_DATA_DEFAULT; diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs b/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs index f5e8dfc425..90d94ad3fb 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs @@ -68,7 +68,7 @@ pub(crate) unsafe fn phy_enable() { [0u8; core::mem::size_of::()]; let phy_version = get_phy_version_str(); - trace!("phy_version {}", str_from_c(phy_version as *const u8)); + trace!("phy_version {}", str_from_c(phy_version)); let init_data = &PHY_INIT_DATA_DEFAULT; diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32c6.rs b/esp-wifi/src/common_adapter/common_adapter_esp32c6.rs index 071dd9903d..28d8e7fcf2 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32c6.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32c6.rs @@ -33,7 +33,7 @@ pub(crate) unsafe fn phy_enable() { [0u8; core::mem::size_of::()]; let phy_version = get_phy_version_str(); - trace!("phy_version {}", str_from_c(phy_version as *const u8)); + trace!("phy_version {}", str_from_c(phy_version)); let init_data = &PHY_INIT_DATA_DEFAULT; diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32h2.rs b/esp-wifi/src/common_adapter/common_adapter_esp32h2.rs index fd6e953bea..51350ed729 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32h2.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32h2.rs @@ -34,7 +34,7 @@ pub(crate) unsafe fn phy_enable() { [0u8; core::mem::size_of::()]; let phy_version = get_phy_version_str(); - trace!("phy_version {}", str_from_c(phy_version as *const u8)); + trace!("phy_version {}", str_from_c(phy_version)); let init_data = &PHY_INIT_DATA_DEFAULT; diff --git a/esp-wifi/src/common_adapter/mod.rs b/esp-wifi/src/common_adapter/mod.rs index 49d0a273b5..d311ebf123 100644 --- a/esp-wifi/src/common_adapter/mod.rs +++ b/esp-wifi/src/common_adapter/mod.rs @@ -1,4 +1,4 @@ -use esp_wifi_sys::include::timeval; +use esp_wifi_sys::{c_types::c_char, include::timeval}; use portable_atomic::{AtomicU32, Ordering}; use crate::{ @@ -193,7 +193,7 @@ pub(crate) unsafe extern "C" fn semphr_give_from_isr(sem: *const (), hptw: *cons // other functions #[no_mangle] -pub unsafe extern "C" fn puts(s: *const u8) { +pub unsafe extern "C" fn puts(s: *const c_char) { let cstr = str_from_c(s); info!("{}", cstr); } @@ -205,10 +205,10 @@ static mut WIFI_EVENT: esp_event_base_t = c"WIFI_EVENT".as_ptr(); // stuff needed by wpa-supplicant #[no_mangle] pub unsafe extern "C" fn __assert_func( - file: *const u8, + file: *const c_char, line: u32, - func: *const u8, - failed_expr: *const u8, + func: *const c_char, + failed_expr: *const c_char, ) { let file = str_from_c(file); let (func_pre, func) = if func.is_null() { diff --git a/esp-wifi/src/compat/common.rs b/esp-wifi/src/compat/common.rs index 530867e557..4062ad864f 100644 --- a/esp-wifi/src/compat/common.rs +++ b/esp-wifi/src/compat/common.rs @@ -7,7 +7,7 @@ use core::{ ptr::{self, addr_of, addr_of_mut}, }; -use esp_wifi_sys::include::malloc; +use esp_wifi_sys::{c_types::c_char, include::malloc}; use super::malloc::free; use crate::{ @@ -164,13 +164,13 @@ impl RawQueue { } } -pub unsafe fn str_from_c<'a>(s: *const u8) -> &'a str { +pub unsafe fn str_from_c<'a>(s: *const c_char) -> &'a str { let c_str = core::ffi::CStr::from_ptr(s.cast()); core::str::from_utf8_unchecked(c_str.to_bytes()) } #[no_mangle] -unsafe extern "C" fn strnlen(chars: *const u8, maxlen: usize) -> usize { +unsafe extern "C" fn strnlen(chars: *const c_char, maxlen: usize) -> usize { let mut len = 0; loop { if chars.offset(len).read_volatile() == 0 { diff --git a/esp-wifi/src/wifi/mod.rs b/esp-wifi/src/wifi/mod.rs index 1addf599ed..d9260d97be 100644 --- a/esp-wifi/src/wifi/mod.rs +++ b/esp-wifi/src/wifi/mod.rs @@ -3,17 +3,18 @@ pub mod event; pub(crate) mod os_adapter; pub(crate) mod state; - use alloc::collections::vec_deque::VecDeque; use core::{ fmt::Debug, + marker::PhantomData, mem::{self, MaybeUninit}, ptr::addr_of, + task::Poll, time::Duration, }; use enumset::{EnumSet, EnumSetType}; -use esp_hal::sync::Locked; +use esp_hal::{asynch::AtomicWaker, sync::Locked}; use esp_wifi_sys::include::{ esp_eap_client_clear_ca_cert, esp_eap_client_clear_certificate_and_key, @@ -65,18 +66,13 @@ use crate::{ common_adapter::*, config::PowerSaveMode, esp_wifi_result, - hal::{ - peripheral::{Peripheral, PeripheralRef}, - ram, - }, + hal::ram, + wifi::private::EspWifiPacketBuffer, EspWifiController, }; const MTU: usize = crate::CONFIG.mtu; -#[cfg(feature = "utils")] -pub mod utils; - #[cfg(coex)] use include::{coex_adapter_funcs_t, coex_pre_init, esp_coex_adapter_register}; @@ -1626,6 +1622,8 @@ pub(crate) fn wifi_start() -> Result<(), WifiError> { #[allow(clippy::useless_transmute)] let country = wifi_country_t { + // FIXME once we bumped the MSRV accordingly (see https://github.com/esp-rs/esp-hal/pull/3027#discussion_r1944718266) + #[allow(clippy::useless_transmute)] cc: core::mem::transmute::<[u8; 3], [core::ffi::c_char; 3]>(cntry_code), schan: 1, nchan: 13, @@ -1780,87 +1778,7 @@ pub(crate) fn wifi_start_scan( unsafe { esp_wifi_scan_start(&scan_config, block) } } -/// Creates a new [WifiDevice] and [WifiController] in either AP or STA mode -/// with the given configuration. -/// -/// This function will panic if the configuration is not -/// [`Configuration::Client`] or [`Configuration::AccessPoint`]. -/// -/// If you want to use AP-STA mode, use `[new_ap_sta]`. -pub fn new_with_config<'d, Dm: WifiDeviceMode>( - inited: &'d EspWifiController<'d>, - device: impl Peripheral

+ 'd, - config: Dm::Config, -) -> Result<(WifiDevice<'d, Dm>, WifiController<'d>), WifiError> { - crate::hal::into_ref!(device); - - Ok(( - WifiDevice::new(unsafe { device.clone_unchecked() }, Dm::new()), - WifiController::new_with_config(inited, device, Dm::wrap_config(config))?, - )) -} - -/// Creates a new [WifiDevice] and [WifiController] in either AP or STA mode -/// with a default configuration. -/// -/// This function will panic if the mode is [`WifiMode::ApSta`]. -/// If you want to use AP-STA mode, use `[new_ap_sta]`. -pub fn new_with_mode<'d, Dm: WifiDeviceMode>( - inited: &'d EspWifiController<'d>, - device: impl Peripheral

+ 'd, - _mode: Dm, -) -> Result<(WifiDevice<'d, Dm>, WifiController<'d>), WifiError> { - new_with_config(inited, device, ::Config::default()) -} - -/// Creates a new [WifiDevice] and [WifiController] in AP-STA mode, with a -/// default configuration. -/// -/// Returns a tuple of `(AP device, STA device, controller)`. -pub fn new_ap_sta<'d>( - inited: &'d EspWifiController<'d>, - device: impl Peripheral

+ 'd, -) -> Result< - ( - WifiDevice<'d, WifiApDevice>, - WifiDevice<'d, WifiStaDevice>, - WifiController<'d>, - ), - WifiError, -> { - new_ap_sta_with_config(inited, device, Default::default(), Default::default()) -} - -/// Creates a new Wifi device and controller in AP-STA mode. -/// -/// Returns a tuple of `(AP device, STA device, controller)`. -pub fn new_ap_sta_with_config<'d>( - inited: &'d EspWifiController<'d>, - device: impl Peripheral

+ 'd, - sta_config: crate::wifi::ClientConfiguration, - ap_config: crate::wifi::AccessPointConfiguration, -) -> Result< - ( - WifiDevice<'d, WifiApDevice>, - WifiDevice<'d, WifiStaDevice>, - WifiController<'d>, - ), - WifiError, -> { - crate::hal::into_ref!(device); - - Ok(( - WifiDevice::new(unsafe { device.clone_unchecked() }, WifiApDevice), - WifiDevice::new(unsafe { device.clone_unchecked() }, WifiStaDevice), - WifiController::new_with_config( - inited, - device, - Configuration::Mixed(sta_config, ap_config), - )?, - )) -} - -mod sealed { +mod private { use super::*; #[derive(Debug)] @@ -1892,193 +1810,125 @@ mod sealed { unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) } } } +} - pub trait Sealed: Copy + Sized { - type Config: Default; - - fn new() -> Self; - - fn wrap_config(config: Self::Config) -> Configuration; - - fn data_queue_rx(self) -> &'static Locked>; - - fn can_send(self) -> bool { - WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE - } - - fn increase_in_flight_counter(self) { - WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst); - } - - fn tx_token(self) -> Option> { - if !self.can_send() { - crate::preempt::yield_task(); - } - - if self.can_send() { - Some(WifiTxToken { mode: self }) - } else { - None - } - } +/// Provides methods for retrieving the Wi-Fi mode and MAC address. +#[derive(Debug, Clone, Copy)] +pub enum WifiDeviceMode { + Sta, + Ap, +} - fn rx_token(self) -> Option<(WifiRxToken, WifiTxToken)> { - let is_empty = self.data_queue_rx().with(|q| q.is_empty()); - if is_empty || !self.can_send() { - crate::preempt::yield_task(); +impl WifiDeviceMode { + fn mac_address(&self) -> [u8; 6] { + match self { + WifiDeviceMode::Sta => { + let mut mac = [0; 6]; + sta_mac(&mut mac); + mac } - - let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty()); - - if !is_empty { - self.tx_token().map(|tx| (WifiRxToken { mode: self }, tx)) - } else { - None + WifiDeviceMode::Ap => { + let mut mac = [0; 6]; + ap_mac(&mut mac); + mac } } - - fn interface(self) -> wifi_interface_t; - - fn register_transmit_waker(self, cx: &mut core::task::Context<'_>) { - embassy::TRANSMIT_WAKER.register(cx.waker()) - } - - fn register_receive_waker(self, cx: &mut core::task::Context<'_>); - - fn register_link_state_waker(self, cx: &mut core::task::Context<'_>); - - fn link_state(self) -> embassy_net_driver::LinkState; } - impl Sealed for WifiStaDevice { - type Config = ClientConfiguration; - - fn new() -> Self { - Self - } - - fn wrap_config(config: ClientConfiguration) -> Configuration { - Configuration::Client(config) - } - - fn data_queue_rx(self) -> &'static Locked> { - &DATA_QUEUE_RX_STA - } - - fn interface(self) -> wifi_interface_t { - wifi_interface_t_WIFI_IF_STA - } - - fn register_receive_waker(self, cx: &mut core::task::Context<'_>) { - embassy::STA_RECEIVE_WAKER.register(cx.waker()); - } - - fn register_link_state_waker(self, cx: &mut core::task::Context<'_>) { - embassy::STA_LINK_STATE_WAKER.register(cx.waker()); - } - - fn link_state(self) -> embassy_net_driver::LinkState { - if matches!(sta_state(), WifiState::StaConnected) { - embassy_net_driver::LinkState::Up - } else { - embassy_net_driver::LinkState::Down - } + fn data_queue_rx(&self) -> &'static Locked> { + match self { + WifiDeviceMode::Sta => &DATA_QUEUE_RX_STA, + WifiDeviceMode::Ap => &DATA_QUEUE_RX_AP, } } - impl Sealed for WifiApDevice { - type Config = AccessPointConfiguration; + fn can_send(&self) -> bool { + WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE + } - fn new() -> Self { - Self - } + fn increase_in_flight_counter(&self) { + WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst); + } - fn wrap_config(config: AccessPointConfiguration) -> Configuration { - Configuration::AccessPoint(config) + fn tx_token(&self) -> Option { + if !self.can_send() { + crate::preempt::yield_task(); } - fn data_queue_rx(self) -> &'static Locked> { - &DATA_QUEUE_RX_AP + if self.can_send() { + Some(WifiTxToken { mode: *self }) + } else { + None } + } - fn interface(self) -> wifi_interface_t { - wifi_interface_t_WIFI_IF_AP + fn rx_token(&self) -> Option<(WifiRxToken, WifiTxToken)> { + let is_empty = self.data_queue_rx().with(|q| q.is_empty()); + if is_empty || !self.can_send() { + crate::preempt::yield_task(); } - fn register_receive_waker(self, cx: &mut core::task::Context<'_>) { - embassy::AP_RECEIVE_WAKER.register(cx.waker()); - } + let is_empty = is_empty && self.data_queue_rx().with(|q| q.is_empty()); - fn register_link_state_waker(self, cx: &mut core::task::Context<'_>) { - embassy::AP_LINK_STATE_WAKER.register(cx.waker()); + if !is_empty { + self.tx_token().map(|tx| (WifiRxToken { mode: *self }, tx)) + } else { + None } + } - fn link_state(self) -> embassy_net_driver::LinkState { - if matches!(ap_state(), WifiState::ApStarted) { - embassy_net_driver::LinkState::Up - } else { - embassy_net_driver::LinkState::Down - } + fn interface(&self) -> wifi_interface_t { + match self { + WifiDeviceMode::Sta => wifi_interface_t_WIFI_IF_STA, + WifiDeviceMode::Ap => wifi_interface_t_WIFI_IF_AP, } } -} - -use sealed::*; - -/// Provides methods for retrieving the Wi-Fi mode and MAC address. -pub trait WifiDeviceMode: Sealed { - /// Returns the currently active Wi-Fi mode. - fn mode(self) -> WifiMode; - - /// Returns the MAC address of the Wi-Fi device. - fn mac_address(self) -> [u8; 6]; -} - -/// Wi-Fi station device. -#[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct WifiStaDevice; -impl WifiDeviceMode for WifiStaDevice { - fn mode(self) -> WifiMode { - WifiMode::Sta + fn register_transmit_waker(&self, cx: &mut core::task::Context<'_>) { + embassy::TRANSMIT_WAKER.register(cx.waker()) } - fn mac_address(self) -> [u8; 6] { - let mut mac = [0; 6]; - sta_mac(&mut mac); - mac + fn register_receive_waker(&self, cx: &mut core::task::Context<'_>) { + match self { + WifiDeviceMode::Sta => embassy::STA_RECEIVE_WAKER.register(cx.waker()), + WifiDeviceMode::Ap => embassy::AP_RECEIVE_WAKER.register(cx.waker()), + } } -} -/// Wi-Fi Access Point (AP) device. -#[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct WifiApDevice; - -impl WifiDeviceMode for WifiApDevice { - fn mode(self) -> WifiMode { - WifiMode::Ap + fn register_link_state_waker(&self, cx: &mut core::task::Context<'_>) { + match self { + WifiDeviceMode::Sta => embassy::STA_LINK_STATE_WAKER.register(cx.waker()), + WifiDeviceMode::Ap => embassy::AP_LINK_STATE_WAKER.register(cx.waker()), + } } - fn mac_address(self) -> [u8; 6] { - let mut mac = [0; 6]; - ap_mac(&mut mac); - mac + fn link_state(&self) -> embassy_net_driver::LinkState { + match self { + WifiDeviceMode::Sta => { + if matches!(sta_state(), WifiState::StaConnected) { + embassy_net_driver::LinkState::Up + } else { + embassy_net_driver::LinkState::Down + } + } + WifiDeviceMode::Ap => { + if matches!(ap_state(), WifiState::ApStarted) { + embassy_net_driver::LinkState::Up + } else { + embassy_net_driver::LinkState::Down + } + } + } } } /// A wifi device implementing smoltcp's Device trait. -pub struct WifiDevice<'d, Dm: WifiDeviceMode> { - _device: PeripheralRef<'d, crate::hal::peripherals::WIFI>, - mode: Dm, +pub struct WifiDevice<'d> { + _phantom: PhantomData<&'d ()>, + mode: WifiDeviceMode, } -impl<'d, Dm: WifiDeviceMode> WifiDevice<'d, Dm> { - pub(crate) fn new(_device: PeripheralRef<'d, crate::hal::peripherals::WIFI>, mode: Dm) -> Self { - Self { _device, mode } - } - +impl WifiDevice<'_> { /// Retrieves the MAC address of the Wi-Fi device. pub fn mac_address(&self) -> [u8; 6] { self.mode.mac_address() @@ -2087,14 +1937,14 @@ impl<'d, Dm: WifiDeviceMode> WifiDevice<'d, Dm> { /// Receives data from the Wi-Fi device (only when `smoltcp` feature is /// disabled). #[cfg(not(feature = "smoltcp"))] - pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> { + pub fn receive(&mut self) -> Option<(WifiRxToken, WifiTxToken)> { self.mode.rx_token() } /// Transmits data through the Wi-Fi device (only when `smoltcp` feature is /// disabled). #[cfg(not(feature = "smoltcp"))] - pub fn transmit(&mut self) -> Option> { + pub fn transmit(&mut self) -> Option { self.mode.tx_token() } } @@ -2363,7 +2213,11 @@ impl Sniffer { ) -> Result<(), WifiError> { esp_wifi_result!(unsafe { esp_wifi_80211_tx( - if use_sta_interface { 0 } else { 1 } as wifi_interface_t, + if use_sta_interface { + wifi_interface_t_WIFI_IF_STA + } else { + wifi_interface_t_WIFI_IF_AP + } as wifi_interface_t, buffer.as_ptr() as *const _, buffer.len() as i32, use_internal_seq_num, @@ -2376,326 +2230,91 @@ impl Sniffer { } } -/// A wifi controller -pub struct WifiController<'d> { - _device: PeripheralRef<'d, crate::hal::peripherals::WIFI>, - config: Configuration, - #[cfg(feature = "sniffer")] - sniffer_taken: AtomicBool, -} +// see https://docs.rs/smoltcp/0.7.1/smoltcp/phy/index.html +#[cfg(feature = "smoltcp")] +impl Device for WifiDevice<'_> { + type RxToken<'a> + = WifiRxToken + where + Self: 'a; + type TxToken<'a> + = WifiTxToken + where + Self: 'a; -impl Drop for WifiController<'_> { - fn drop(&mut self) { - if unwrap!( - crate::flags::WIFI.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { - Some(x.saturating_sub(1)) - }) - ) == 0 - { - if let Err(e) = crate::wifi::wifi_deinit() { - warn!("Failed to cleanly deinit wifi: {:?}", e); - } - } + fn receive( + &mut self, + _instant: smoltcp::time::Instant, + ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + self.mode.rx_token() } -} - -impl<'d> WifiController<'d> { - pub(crate) fn new_with_config( - inited: &'d EspWifiController<'d>, - _device: PeripheralRef<'d, crate::hal::peripherals::WIFI>, - config: Configuration, - ) -> Result { - if !inited.wifi() { - crate::wifi::wifi_init()?; - } - - // We set up the controller with the default config because we need to call - // `set_configuration` to apply the actual configuration, and it will update the - // stored configuration anyway. - let mut this = Self { - _device, - config: Default::default(), - #[cfg(feature = "sniffer")] - sniffer_taken: AtomicBool::new(false), - }; - - let mode = WifiMode::try_from(&config)?; - esp_wifi_result!(unsafe { esp_wifi_set_mode(mode.into()) })?; - debug!("Wifi mode {:?} set", mode); - this.set_configuration(&config)?; - Ok(this) + fn transmit(&mut self, _instant: smoltcp::time::Instant) -> Option> { + self.mode.tx_token() } - /// Attempts to take the sniffer, returns `Some(Sniffer)` if successful, - /// otherwise `None`. - #[cfg(feature = "sniffer")] - pub fn take_sniffer(&self) -> Option { - if self - .sniffer_taken - .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - == Ok(false) - { - Some(Sniffer::new()) - } else { + fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = MTU; + caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 { None - } + } else { + Some(crate::CONFIG.max_burst_size) + }; + caps } +} - /// Set CSI configuration and register the receiving callback. - #[cfg(feature = "csi")] - pub fn set_csi( - &mut self, - mut csi: CsiConfig, - cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send, - ) -> Result<(), WifiError> { - csi.apply_config()?; - csi.set_receive_cb(cb)?; - csi.set_csi(true)?; - - Ok(()) - } +#[doc(hidden)] +#[derive(Debug)] +pub struct WifiRxToken { + mode: WifiDeviceMode, +} - /// Set the wifi protocol. - /// - /// This will set the wifi protocol to the desired protocol, the default for - /// this is: `WIFI_PROTOCOL_11B|WIFI_PROTOCOL_11G|WIFI_PROTOCOL_11N` - /// - /// # Arguments: - /// - /// * `protocols` - The desired protocols - /// - /// # Example: - /// - /// ``` - /// wifi_controller.set_protocol(Protocol::P802D11BGNLR.into()); - /// ``` - pub fn set_protocol(&mut self, protocols: EnumSet) -> Result<(), WifiError> { - let mut protocol = 0u8; - - protocols.into_iter().for_each(|v| match v { - Protocol::P802D11B => protocol |= WIFI_PROTOCOL_11B as u8, - Protocol::P802D11BG => protocol |= WIFI_PROTOCOL_11B as u8 | WIFI_PROTOCOL_11G as u8, - Protocol::P802D11BGN => { - protocol |= - WIFI_PROTOCOL_11B as u8 | WIFI_PROTOCOL_11G as u8 | WIFI_PROTOCOL_11N as u8 - } - Protocol::P802D11BGNLR => { - protocol |= WIFI_PROTOCOL_11B as u8 - | WIFI_PROTOCOL_11G as u8 - | WIFI_PROTOCOL_11N as u8 - | WIFI_PROTOCOL_LR as u8 - } - Protocol::P802D11LR => protocol |= WIFI_PROTOCOL_LR as u8, - Protocol::P802D11BGNAX => { - protocol |= WIFI_PROTOCOL_11B as u8 - | WIFI_PROTOCOL_11G as u8 - | WIFI_PROTOCOL_11N as u8 - | WIFI_PROTOCOL_11AX as u8 - } +impl WifiRxToken { + /// Consumes the RX token and applies the callback function to the received + /// data buffer. + pub fn consume_token(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut data = self.mode.data_queue_rx().with(|queue| { + unwrap!( + queue.pop_front(), + "unreachable: transmit()/receive() ensures there is a packet to process" + ) }); - let mut mode = wifi_mode_t_WIFI_MODE_NULL; - esp_wifi_result!(unsafe { esp_wifi_get_mode(&mut mode) })?; - - if mode == wifi_mode_t_WIFI_MODE_STA || mode == wifi_mode_t_WIFI_MODE_APSTA { - esp_wifi_result!(unsafe { - esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_STA, protocol) - })?; - } - if mode == wifi_mode_t_WIFI_MODE_AP || mode == wifi_mode_t_WIFI_MODE_APSTA { - esp_wifi_result!(unsafe { - esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_AP, protocol) - })?; - } - - Ok(()) - } + // We handle the received data outside of the lock because + // EspWifiPacketBuffer::drop must not be called in a critical section. + // Dropping an EspWifiPacketBuffer will call `esp_wifi_internal_free_rx_buffer` + // which will try to lock an internal mutex. If the mutex is already + // taken, the function will try to trigger a context switch, which will + // fail if we are in an interrupt-free context. + let buffer = data.as_slice_mut(); + dump_packet_info(buffer); - /// Configures modem power saving - pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> { - apply_power_saving(ps) + f(buffer) } +} - /// Checks if Wi-Fi is enabled as a station. - pub fn is_sta_enabled(&self) -> Result { - WifiMode::try_from(&self.config).map(|m| m.is_sta()) +#[cfg(feature = "smoltcp")] +impl RxToken for WifiRxToken { + fn consume(self, f: F) -> R + where + F: FnOnce(&[u8]) -> R, + { + self.consume_token(|t| f(t)) } +} - /// Checks if Wi-Fi is enabled as an access p. - pub fn is_ap_enabled(&self) -> Result { - WifiMode::try_from(&self.config).map(|m| m.is_ap()) - } +#[doc(hidden)] +#[derive(Debug)] +pub struct WifiTxToken { + mode: WifiDeviceMode, +} - /// A blocking wifi network scan with caller-provided scanning options. - pub fn scan_with_config_sync( - &mut self, - config: ScanConfig<'_>, - ) -> Result<(heapless::Vec, usize), WifiError> { - esp_wifi_result!(crate::wifi::wifi_start_scan(true, config))?; - - let count = self.scan_result_count()?; - let result = self.scan_results()?; - - Ok((result, count)) - } - - fn scan_result_count(&mut self) -> Result { - let mut bss_total: u16 = 0; - - // Prevents memory leak on error - let guard = FreeApListOnDrop; - - unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? }; - - guard.defuse(); - - Ok(bss_total as usize) - } - - fn scan_results( - &mut self, - ) -> Result, WifiError> { - let mut scanned = heapless::Vec::::new(); - let mut bss_total: u16 = N as u16; - - let mut records: [MaybeUninit; N] = [MaybeUninit::uninit(); N]; - - // Prevents memory leak on error - let guard = FreeApListOnDrop; - - unsafe { - esp_wifi_result!(include::esp_wifi_scan_get_ap_records( - &mut bss_total, - records[0].as_mut_ptr(), - ))? - }; - - guard.defuse(); - - for i in 0..bss_total { - let record = unsafe { MaybeUninit::assume_init_ref(&records[i as usize]) }; - let ap_info = convert_ap_info(record); - - scanned.push(ap_info).ok(); - } - - Ok(scanned) - } - - /// A blocking wifi network scan with default scanning options. - pub fn scan_n( - &mut self, - ) -> Result<(heapless::Vec, usize), WifiError> { - self.scan_with_config_sync(Default::default()) - } - - /// Starts the WiFi controller. - pub fn start(&mut self) -> Result<(), WifiError> { - crate::wifi::wifi_start() - } - - /// Stops the WiFi controller. - pub fn stop(&mut self) -> Result<(), WifiError> { - self.stop_impl() - } - - /// Connects the WiFi controller to a network. - pub fn connect(&mut self) -> Result<(), WifiError> { - self.connect_impl() - } - - /// Disconnects the WiFi controller from a network. - pub fn disconnect(&mut self) -> Result<(), WifiError> { - self.disconnect_impl() - } -} - -// see https://docs.rs/smoltcp/0.7.1/smoltcp/phy/index.html -#[cfg(feature = "smoltcp")] -impl Device for WifiDevice<'_, Dm> { - type RxToken<'a> - = WifiRxToken - where - Self: 'a; - type TxToken<'a> - = WifiTxToken - where - Self: 'a; - - fn receive( - &mut self, - _instant: smoltcp::time::Instant, - ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - self.mode.rx_token() - } - - fn transmit(&mut self, _instant: smoltcp::time::Instant) -> Option> { - self.mode.tx_token() - } - - fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = MTU; - caps.max_burst_size = if crate::CONFIG.max_burst_size == 0 { - None - } else { - Some(crate::CONFIG.max_burst_size) - }; - caps - } -} - -#[doc(hidden)] -#[derive(Debug)] -pub struct WifiRxToken { - mode: Dm, -} - -impl WifiRxToken { - /// Consumes the RX token and applies the callback function to the received - /// data buffer. - pub fn consume_token(self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - let mut data = self.mode.data_queue_rx().with(|queue| { - unwrap!( - queue.pop_front(), - "unreachable: transmit()/receive() ensures there is a packet to process" - ) - }); - - // We handle the received data outside of the lock because - // EspWifiPacketBuffer::drop must not be called in a critical section. - // Dropping an EspWifiPacketBuffer will call `esp_wifi_internal_free_rx_buffer` - // which will try to lock an internal mutex. If the mutex is already - // taken, the function will try to trigger a context switch, which will - // fail if we are in an interrupt-free context. - let buffer = data.as_slice_mut(); - dump_packet_info(buffer); - - f(buffer) - } -} - -#[cfg(feature = "smoltcp")] -impl RxToken for WifiRxToken { - fn consume(self, f: F) -> R - where - F: FnOnce(&[u8]) -> R, - { - self.consume_token(|t| f(t)) - } -} - -#[doc(hidden)] -#[derive(Debug)] -pub struct WifiTxToken { - mode: Dm, -} - -impl WifiTxToken { +impl WifiTxToken { /// Consumes the TX token and applies the callback function to the received /// data buffer. pub fn consume_token(self, len: usize, f: F) -> R @@ -2720,7 +2339,7 @@ impl WifiTxToken { } #[cfg(feature = "smoltcp")] -impl TxToken for WifiTxToken { +impl TxToken for WifiTxToken { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -2966,119 +2585,7 @@ fn apply_sta_eap_config(config: &EapClientConfiguration) -> Result<(), WifiError } } -impl WifiController<'_> { - /// Get the supported capabilities of the controller. - pub fn capabilities(&self) -> Result, WifiError> { - let caps = match self.config { - Configuration::None => unreachable!(), - Configuration::Client(_) => enumset::enum_set! { Capability::Client }, - Configuration::AccessPoint(_) => enumset::enum_set! { Capability::AccessPoint }, - Configuration::Mixed(_, _) => { - Capability::Client | Capability::AccessPoint | Capability::Mixed - } - Configuration::EapClient(_) => enumset::enum_set! { Capability::Client }, - }; - - Ok(caps) - } - - /// Get the currently used configuration. - pub fn configuration(&self) -> Result { - Ok(self.config.clone()) - } - - /// Set the configuration, you need to use Wifi::connect() for connecting to - /// an AP. - /// - /// This will replace any previously set configuration - pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), WifiError> { - let wifi_mode = WifiMode::current().unwrap_or(WifiMode::Sta); - let sta_enabled = wifi_mode.is_sta(); - let ap_enabled = wifi_mode.is_ap(); - - match conf { - Configuration::Client(_) if !sta_enabled => { - return Err(WifiError::InternalError( - InternalWifiError::EspErrInvalidArg, - )) - } - Configuration::AccessPoint(_) if !ap_enabled => { - return Err(WifiError::InternalError( - InternalWifiError::EspErrInvalidArg, - )) - } - Configuration::EapClient(_) if !sta_enabled => { - return Err(WifiError::InternalError( - InternalWifiError::EspErrInvalidArg, - )) - } - _ => (), - } - - self.config = conf.clone(); - - match conf { - Configuration::None => { - return Err(WifiError::InternalError( - InternalWifiError::EspErrInvalidArg, - )); - } - Configuration::Client(config) => apply_sta_config(config)?, - Configuration::AccessPoint(config) => apply_ap_config(config)?, - Configuration::Mixed(sta_config, ap_config) => { - apply_ap_config(ap_config)?; - apply_sta_config(sta_config)?; - } - Configuration::EapClient(config) => apply_sta_eap_config(config)?, - }; - - Ok(()) - } - - pub(crate) fn stop_impl(&mut self) -> Result<(), WifiError> { - esp_wifi_result!(unsafe { esp_wifi_stop() }) - } - - pub(crate) fn connect_impl(&mut self) -> Result<(), WifiError> { - esp_wifi_result!(unsafe { esp_wifi_connect() }) - } - - pub(crate) fn disconnect_impl(&mut self) -> Result<(), WifiError> { - esp_wifi_result!(unsafe { esp_wifi_disconnect() }) - } - - /// Checks if the WiFi controller has started. - /// - /// This function should be called after the `start` method to verify if the - /// WiFi has started successfully. - pub fn is_started(&self) -> Result { - if matches!( - crate::wifi::sta_state(), - WifiState::StaStarted | WifiState::StaConnected | WifiState::StaDisconnected - ) { - return Ok(true); - } - if matches!(crate::wifi::ap_state(), WifiState::ApStarted) { - return Ok(true); - } - Ok(false) - } - - /// Checks if the WiFi controller is connected to a configured network. - /// - /// This function should be called after the `connect` method to verify if - /// the connection was successful. - pub fn is_connected(&self) -> Result { - match crate::wifi::sta_state() { - crate::wifi::WifiState::StaConnected => Ok(true), - crate::wifi::WifiState::StaDisconnected => Err(WifiError::Disconnected), - // FIXME: Should any other enum value trigger an error instead of returning false? - _ => Ok(false), - } - } -} - -fn dump_packet_info(_buffer: &[u8]) { +fn dump_packet_info(_buffer: &mut [u8]) { #[cfg(dump_packets)] { info!("@WIFIFRAME {:?}", _buffer); @@ -3118,7 +2625,7 @@ pub(crate) mod embassy { pub(crate) static STA_RECEIVE_WAKER: AtomicWaker = AtomicWaker::new(); pub(crate) static STA_LINK_STATE_WAKER: AtomicWaker = AtomicWaker::new(); - impl RxToken for WifiRxToken { + impl RxToken for WifiRxToken { fn consume(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -3127,7 +2634,7 @@ pub(crate) mod embassy { } } - impl TxToken for WifiTxToken { + impl TxToken for WifiTxToken { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -3136,13 +2643,13 @@ pub(crate) mod embassy { } } - impl Driver for WifiDevice<'_, Dm> { + impl Driver for WifiDevice<'_> { type RxToken<'a> - = WifiRxToken + = WifiRxToken where Self: 'a; type TxToken<'a> - = WifiTxToken + = WifiTxToken where Self: 'a; @@ -3190,247 +2697,564 @@ pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> { Ok(()) } -mod asynch { - use core::task::Poll; - - use esp_hal::asynch::AtomicWaker; - - use super::*; +struct FreeApListOnDrop; +impl FreeApListOnDrop { + pub fn defuse(self) { + core::mem::forget(self); + } +} - // TODO assumes STA mode only - impl WifiController<'_> { - /// Async version of [`crate::wifi::WifiController`]'s `scan_n` method - pub async fn scan_n_async( - &mut self, - ) -> Result<(heapless::Vec, usize), WifiError> { - self.scan_with_config_async(Default::default()).await +impl Drop for FreeApListOnDrop { + fn drop(&mut self) { + unsafe { + include::esp_wifi_clear_ap_list(); } + } +} - /// An async wifi network scan with caller-provided scanning options. - pub async fn scan_with_config_async( - &mut self, - config: ScanConfig<'_>, - ) -> Result<(heapless::Vec, usize), WifiError> { - Self::clear_events(WifiEvent::ScanDone); - esp_wifi_result!(wifi_start_scan(false, config))?; - - // Prevents memory leak if `scan_n`'s future is dropped. - let guard = FreeApListOnDrop; - WifiEventFuture::new(WifiEvent::ScanDone).await; - - guard.defuse(); +#[non_exhaustive] +pub struct Interfaces<'d> { + pub sta: WifiDevice<'d>, + pub ap: WifiDevice<'d>, +} - let count = self.scan_result_count()?; - let result = self.scan_results()?; - - Ok((result, count)) - } +/// Create a WiFi controller and it's associated interfaces. +/// +/// Dropping the controller will deinitialize / stop WiFi. +pub fn new<'d>( + inited: &'d EspWifiController<'d>, + _device: impl crate::hal::peripheral::Peripheral

+ 'd, +) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> { + if !inited.wifi() { + crate::wifi::wifi_init()?; + } + Ok(( + WifiController { + _phantom: Default::default(), + #[cfg(feature = "sniffer")] + sniffer_taken: AtomicBool::new(false), + }, + Interfaces { + sta: WifiDevice { + _phantom: Default::default(), + mode: WifiDeviceMode::Sta, + }, + ap: WifiDevice { + _phantom: Default::default(), + mode: WifiDeviceMode::Ap, + }, + }, + )) +} - /// Async version of [`crate::wifi::WifiController`]'s `start` method - pub async fn start_async(&mut self) -> Result<(), WifiError> { - let mode = WifiMode::try_from(&self.config)?; +#[non_exhaustive] +pub struct WifiController<'d> { + _phantom: PhantomData<&'d ()>, + #[cfg(feature = "sniffer")] + sniffer_taken: AtomicBool, +} - let mut events = enumset::enum_set! {}; - if mode.is_ap() { - events |= WifiEvent::ApStart; - } - if mode.is_sta() { - events |= WifiEvent::StaStart; +impl Drop for WifiController<'_> { + fn drop(&mut self) { + if unwrap!( + crate::flags::WIFI.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + Some(x.saturating_sub(1)) + }) + ) == 0 + { + if let Err(e) = crate::wifi::wifi_deinit() { + warn!("Failed to cleanly deinit wifi: {:?}", e); } + } + } +} - Self::clear_events(events); +impl WifiController<'_> { + /// Attempts to take the sniffer, returns `Some(Sniffer)` if successful, + /// otherwise `None`. + #[cfg(feature = "sniffer")] + pub fn take_sniffer(&self) -> Option { + if !self.sniffer_taken.fetch_or(true, Ordering::Acquire) { + Some(Sniffer::new()) + } else { + None + } + } - wifi_start()?; + /// Set CSI configuration and register the receiving callback. + #[cfg(feature = "csi")] + pub fn set_csi( + &mut self, + mut csi: CsiConfig, + cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send, + ) -> Result<(), WifiError> { + csi.apply_config()?; + csi.set_receive_cb(cb)?; + csi.set_csi(true)?; - self.wait_for_all_events(events, false).await; + Ok(()) + } - Ok(()) + /// Set the wifi protocol. + /// + /// This will set the wifi protocol to the desired protocol, the default for + /// this is: `WIFI_PROTOCOL_11B|WIFI_PROTOCOL_11G|WIFI_PROTOCOL_11N` + /// + /// # Arguments: + /// + /// * `protocols` - The desired protocols + /// + /// # Example: + /// + /// ``` + /// wifi_controller.set_protocol(Protocol::P802D11BGNLR.into()); + /// ``` + pub fn set_protocol(&mut self, protocols: EnumSet) -> Result<(), WifiError> { + let protocol = protocols + .into_iter() + .map(|v| match v { + Protocol::P802D11B => WIFI_PROTOCOL_11B, + Protocol::P802D11BG => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G, + Protocol::P802D11BGN => WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N, + Protocol::P802D11BGNLR => { + WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR + } + Protocol::P802D11LR => WIFI_PROTOCOL_LR, + Protocol::P802D11BGNAX => { + WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX + } + }) + .fold(0, |combined, protocol| combined | protocol) as u8; + + let mode = self.mode()?; + if mode.is_sta() { + esp_wifi_result!(unsafe { + esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_STA, protocol) + })?; + } + if mode.is_ap() { + esp_wifi_result!(unsafe { + esp_wifi_set_protocol(wifi_interface_t_WIFI_IF_AP, protocol) + })?; } - /// Async version of [`crate::wifi::WifiController`]'s `stop` method - pub async fn stop_async(&mut self) -> Result<(), WifiError> { - let mode = WifiMode::try_from(&self.config)?; + Ok(()) + } - let mut events = enumset::enum_set! {}; - if mode.is_ap() { - events |= WifiEvent::ApStop; - } - if mode.is_sta() { - events |= WifiEvent::StaStop; - } + /// Configures modem power saving + pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> { + apply_power_saving(ps) + } - Self::clear_events(events); + /// A blocking wifi network scan with caller-provided scanning options. + pub fn scan_with_config_sync( + &mut self, + config: ScanConfig<'_>, + ) -> Result<(heapless::Vec, usize), WifiError> { + esp_wifi_result!(crate::wifi::wifi_start_scan(true, config))?; - crate::wifi::WifiController::stop_impl(self)?; + let count = self.scan_result_count()?; + let result = self.scan_results()?; - self.wait_for_all_events(events, false).await; + Ok((result, count)) + } - reset_ap_state(); - reset_sta_state(); + fn scan_result_count(&mut self) -> Result { + let mut bss_total: u16 = 0; - Ok(()) + // Prevents memory leak on error + let guard = FreeApListOnDrop; + + unsafe { esp_wifi_result!(include::esp_wifi_scan_get_ap_num(&mut bss_total))? }; + + guard.defuse(); + + Ok(bss_total as usize) + } + + fn scan_results( + &mut self, + ) -> Result, WifiError> { + let mut scanned = heapless::Vec::::new(); + let mut bss_total: u16 = N as u16; + + let mut records: [MaybeUninit; N] = [MaybeUninit::uninit(); N]; + + // Prevents memory leak on error + let guard = FreeApListOnDrop; + + unsafe { + esp_wifi_result!(include::esp_wifi_scan_get_ap_records( + &mut bss_total, + records[0].as_mut_ptr(), + ))? + }; + + guard.defuse(); + + for i in 0..bss_total { + let record = unsafe { MaybeUninit::assume_init_ref(&records[i as usize]) }; + let ap_info = convert_ap_info(record); + + scanned.push(ap_info).ok(); } - /// Async version of [`crate::wifi::WifiController`]'s `connect` method - pub async fn connect_async(&mut self) -> Result<(), WifiError> { - Self::clear_events(WifiEvent::StaConnected | WifiEvent::StaDisconnected); + Ok(scanned) + } - let err = crate::wifi::WifiController::connect_impl(self).err(); + /// A blocking wifi network scan with default scanning options. + pub fn scan_n( + &mut self, + ) -> Result<(heapless::Vec, usize), WifiError> { + self.scan_with_config_sync(Default::default()) + } - if MultiWifiEventFuture::new(WifiEvent::StaConnected | WifiEvent::StaDisconnected) - .await - .contains(WifiEvent::StaDisconnected) - { - Err(err.unwrap_or(WifiError::Disconnected)) - } else { - Ok(()) + /// Starts the WiFi controller. + pub fn start(&mut self) -> Result<(), WifiError> { + crate::wifi::wifi_start() + } + + /// Stops the WiFi controller. + pub fn stop(&mut self) -> Result<(), WifiError> { + self.stop_impl() + } + + /// Connect WiFi station to the AP. + /// + /// - If station is connected , call [Self::disconnect] to disconnect. + /// - Scanning will not be effective until connection between device and the + /// AP is established. + /// - If device is scanning and connecting at the same time, it will abort + /// scanning and return a warning message and error + pub fn connect(&mut self) -> Result<(), WifiError> { + self.connect_impl() + } + + /// Disconnect WiFi station from the AP. + pub fn disconnect(&mut self) -> Result<(), WifiError> { + self.disconnect_impl() + } + + /// Get the supported capabilities of the controller. + pub fn capabilities(&self) -> Result, WifiError> { + let caps = + enumset::enum_set! { Capability::Client | Capability::AccessPoint | Capability::Mixed }; + Ok(caps) + } + + /// Set the configuration. + /// + /// This will set the mode accordingly. + /// You need to use Wifi::connect() for connecting to an AP. + pub fn set_configuration(&mut self, conf: &Configuration) -> Result<(), WifiError> { + let mode = match conf { + Configuration::None => { + return Err(WifiError::InternalError( + InternalWifiError::EspErrInvalidArg, + )); } - } + Configuration::Client(_) => wifi_mode_t_WIFI_MODE_STA, + Configuration::AccessPoint(_) => wifi_mode_t_WIFI_MODE_AP, + Configuration::Mixed(_, _) => wifi_mode_t_WIFI_MODE_APSTA, + Configuration::EapClient(_) => wifi_mode_t_WIFI_MODE_STA, + }; + + esp_wifi_result!(unsafe { esp_wifi_set_mode(mode) })?; - /// Async version of [`crate::wifi::WifiController`]'s `Disconnect` - /// method - pub async fn disconnect_async(&mut self) -> Result<(), WifiError> { - // If not connected, this will do nothing. - // It will also wait forever for a `StaDisconnected` event that will never come. - // Return early instead of hanging. - if !matches!(self.is_connected(), Ok(true)) { - return Ok(()); + match conf { + Configuration::None => { + return Err(WifiError::InternalError( + InternalWifiError::EspErrInvalidArg, + )); + } + Configuration::Client(config) => { + apply_sta_config(config)?; + } + Configuration::AccessPoint(config) => { + apply_ap_config(config)?; + } + Configuration::Mixed(sta_config, ap_config) => { + apply_ap_config(ap_config)?; + apply_sta_config(sta_config)?; } + Configuration::EapClient(config) => { + apply_sta_eap_config(config)?; + } + }; - Self::clear_events(WifiEvent::StaDisconnected); - crate::wifi::WifiController::disconnect_impl(self)?; - WifiEventFuture::new(WifiEvent::StaDisconnected).await; + Ok(()) + } - Ok(()) + /// Set the WiFi mode. + /// + /// This will override the mode inferred by [Self::set_configuration]. + pub fn set_mode(&mut self, mode: WifiMode) -> Result<(), WifiError> { + esp_wifi_result!(unsafe { esp_wifi_set_mode(mode.into()) })?; + Ok(()) + } + + fn stop_impl(&mut self) -> Result<(), WifiError> { + esp_wifi_result!(unsafe { esp_wifi_stop() }) + } + + fn connect_impl(&mut self) -> Result<(), WifiError> { + esp_wifi_result!(unsafe { esp_wifi_connect() }) + } + + fn disconnect_impl(&mut self) -> Result<(), WifiError> { + esp_wifi_result!(unsafe { esp_wifi_disconnect() }) + } + + /// Checks if the WiFi controller has started. + /// + /// This function should be called after the `start` method to verify if the + /// WiFi has started successfully. + pub fn is_started(&self) -> Result { + if matches!( + crate::wifi::sta_state(), + WifiState::StaStarted | WifiState::StaConnected | WifiState::StaDisconnected + ) { + return Ok(true); } + if matches!(crate::wifi::ap_state(), WifiState::ApStarted) { + return Ok(true); + } + Ok(false) + } - fn clear_events(events: impl Into>) { - WIFI_EVENTS.with(|evts| evts.get_mut().remove_all(events.into())); + /// Checks if the WiFi controller is connected to an AP. + /// + /// This function should be called after the `connect` method to verify if + /// the connection was successful. + pub fn is_connected(&self) -> Result { + match crate::wifi::sta_state() { + crate::wifi::WifiState::StaConnected => Ok(true), + crate::wifi::WifiState::StaDisconnected => Err(WifiError::Disconnected), + // FIXME: Should any other enum value trigger an error instead of returning false? + _ => Ok(false), } + } + + fn mode(&self) -> Result { + WifiMode::current() + } + + /// Async version of [`crate::wifi::WifiController`]'s `scan_n` method + pub async fn scan_n_async( + &mut self, + ) -> Result<(heapless::Vec, usize), WifiError> { + self.scan_with_config_async(Default::default()).await + } + + /// An async wifi network scan with caller-provided scanning options. + pub async fn scan_with_config_async( + &mut self, + config: ScanConfig<'_>, + ) -> Result<(heapless::Vec, usize), WifiError> { + Self::clear_events(WifiEvent::ScanDone); + esp_wifi_result!(wifi_start_scan(false, config))?; + + // Prevents memory leak if `scan_n`'s future is dropped. + let guard = FreeApListOnDrop; + WifiEventFuture::new(WifiEvent::ScanDone).await; + + guard.defuse(); + + let count = self.scan_result_count()?; + let result = self.scan_results()?; + + Ok((result, count)) + } - /// Wait for one [`WifiEvent`]. - pub async fn wait_for_event(&mut self, event: WifiEvent) { - Self::clear_events(event); - WifiEventFuture::new(event).await + /// Async version of [`crate::wifi::WifiController`]'s `start` method + pub async fn start_async(&mut self) -> Result<(), WifiError> { + let mut events = enumset::enum_set! {}; + + let mode = self.mode()?; + if mode.is_ap() { + events |= WifiEvent::ApStart; + } + if mode.is_sta() { + events |= WifiEvent::StaStart; } - /// Wait for one of multiple [`WifiEvent`]s. Returns the events that - /// occurred while waiting. - pub async fn wait_for_events( - &mut self, - events: EnumSet, - clear_pending: bool, - ) -> EnumSet { - if clear_pending { - Self::clear_events(events); - } - MultiWifiEventFuture::new(events).await + Self::clear_events(events); + + wifi_start()?; + + self.wait_for_all_events(events, false).await; + + Ok(()) + } + + /// Async version of [`crate::wifi::WifiController`]'s `stop` method + pub async fn stop_async(&mut self) -> Result<(), WifiError> { + let mut events = enumset::enum_set! {}; + + let mode = self.mode()?; + if mode.is_ap() { + events |= WifiEvent::ApStop; + } + if mode.is_sta() { + events |= WifiEvent::StaStop; } - /// Wait for multiple [`WifiEvent`]s. - pub async fn wait_for_all_events( - &mut self, - mut events: EnumSet, - clear_pending: bool, - ) { - if clear_pending { - Self::clear_events(events); - } + Self::clear_events(events); - while !events.is_empty() { - let fired = MultiWifiEventFuture::new(events).await; - events -= fired; - } + crate::wifi::WifiController::stop_impl(self)?; + + self.wait_for_all_events(events, false).await; + + reset_ap_state(); + reset_sta_state(); + + Ok(()) + } + + /// Async version of [`crate::wifi::WifiController`]'s `connect` method + pub async fn connect_async(&mut self) -> Result<(), WifiError> { + Self::clear_events(WifiEvent::StaConnected | WifiEvent::StaDisconnected); + + let err = crate::wifi::WifiController::connect_impl(self).err(); + + if MultiWifiEventFuture::new(WifiEvent::StaConnected | WifiEvent::StaDisconnected) + .await + .contains(WifiEvent::StaDisconnected) + { + Err(err.unwrap_or(WifiError::Disconnected)) + } else { + Ok(()) } } - impl WifiEvent { - pub(crate) fn waker(&self) -> &'static AtomicWaker { - // for now use only one waker for all events - // if that ever becomes a problem we might want to pick some events to use their - // own - static WAKER: AtomicWaker = AtomicWaker::new(); - &WAKER + /// Async version of [`crate::wifi::WifiController`]'s `Disconnect` + /// method + pub async fn disconnect_async(&mut self) -> Result<(), WifiError> { + // If not connected, this will do nothing. + // It will also wait forever for a `StaDisconnected` event that will never come. + // Return early instead of hanging. + if !matches!(self.is_connected(), Ok(true)) { + return Ok(()); } + + Self::clear_events(WifiEvent::StaDisconnected); + crate::wifi::WifiController::disconnect_impl(self)?; + WifiEventFuture::new(WifiEvent::StaDisconnected).await; + + Ok(()) + } + + fn clear_events(events: impl Into>) { + WIFI_EVENTS.with(|evts| evts.get_mut().remove_all(events.into())); } - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct WifiEventFuture { - event: WifiEvent, + /// Wait for one [`WifiEvent`]. + pub async fn wait_for_event(&mut self, event: WifiEvent) { + Self::clear_events(event); + WifiEventFuture::new(event).await } - impl WifiEventFuture { - /// Creates a new `Future` for the specified WiFi event. - pub fn new(event: WifiEvent) -> Self { - Self { event } + /// Wait for one of multiple [`WifiEvent`]s. Returns the events that + /// occurred while waiting. + pub async fn wait_for_events( + &mut self, + events: EnumSet, + clear_pending: bool, + ) -> EnumSet { + if clear_pending { + Self::clear_events(events); } + MultiWifiEventFuture::new(events).await } - impl core::future::Future for WifiEventFuture { - type Output = (); + /// Wait for multiple [`WifiEvent`]s. + pub async fn wait_for_all_events( + &mut self, + mut events: EnumSet, + clear_pending: bool, + ) { + if clear_pending { + Self::clear_events(events); + } - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> Poll { - self.event.waker().register(cx.waker()); - if WIFI_EVENTS.with(|events| events.get_mut().remove(self.event)) { - Poll::Ready(()) - } else { - Poll::Pending - } + while !events.is_empty() { + let fired = MultiWifiEventFuture::new(events).await; + events -= fired; } } +} - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct MultiWifiEventFuture { - event: EnumSet, +impl WifiEvent { + pub(crate) fn waker(&self) -> &'static AtomicWaker { + // for now use only one waker for all events + // if that ever becomes a problem we might want to pick some events to use their + // own + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER } +} - impl MultiWifiEventFuture { - /// Creates a new `Future` for the specified set of WiFi events. - pub fn new(event: EnumSet) -> Self { - Self { event } - } +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub(crate) struct WifiEventFuture { + event: WifiEvent, +} + +impl WifiEventFuture { + /// Creates a new `Future` for the specified WiFi event. + pub fn new(event: WifiEvent) -> Self { + Self { event } } +} - impl core::future::Future for MultiWifiEventFuture { - type Output = EnumSet; +impl core::future::Future for WifiEventFuture { + type Output = (); - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> Poll { - let output = WIFI_EVENTS.with(|events| { - let events = events.get_mut(); - let active = events.intersection(self.event); - events.remove_all(active); - active - }); - if output.is_empty() { - for event in self.event.iter() { - event.waker().register(cx.waker()); - } - - Poll::Pending - } else { - Poll::Ready(output) - } + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll { + self.event.waker().register(cx.waker()); + if WIFI_EVENTS.with(|events| events.get_mut().remove(self.event)) { + Poll::Ready(()) + } else { + Poll::Pending } } } -struct FreeApListOnDrop; -impl FreeApListOnDrop { - pub fn defuse(self) { - core::mem::forget(self); +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub(crate) struct MultiWifiEventFuture { + event: EnumSet, +} + +impl MultiWifiEventFuture { + /// Creates a new `Future` for the specified set of WiFi events. + pub fn new(event: EnumSet) -> Self { + Self { event } } } -impl Drop for FreeApListOnDrop { - fn drop(&mut self) { - unsafe { - include::esp_wifi_clear_ap_list(); +impl core::future::Future for MultiWifiEventFuture { + type Output = EnumSet; + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll { + let output = WIFI_EVENTS.with(|events| { + let events = events.get_mut(); + let active = events.intersection(self.event); + events.remove_all(active); + active + }); + if output.is_empty() { + for event in self.event.iter() { + event.waker().register(cx.waker()); + } + + Poll::Pending + } else { + Poll::Ready(output) } } } diff --git a/esp-wifi/src/wifi/os_adapter.rs b/esp-wifi/src/wifi/os_adapter.rs index 3eeab64d31..7497229d00 100644 --- a/esp-wifi/src/wifi/os_adapter.rs +++ b/esp-wifi/src/wifi/os_adapter.rs @@ -652,7 +652,7 @@ pub unsafe extern "C" fn task_create_pinned_to_core( ) -> i32 { trace!("task_create_pinned_to_core task_func {:?} name {} stack_depth {} param {:?} prio {}, task_handle {:?} core_id {}", task_func, - str_from_c(name as *const _), + str_from_c(name as _), stack_depth, param, prio, @@ -1029,7 +1029,7 @@ pub unsafe extern "C" fn phy_update_country_info( country: *const crate::binary::c_types::c_char, ) -> crate::binary::c_types::c_int { // not implemented in original code - trace!("phy_update_country_info {}", *country as u8 as char); + trace!("phy_update_country_info {}", str_from_c(country.cast())); -1 } @@ -1456,7 +1456,7 @@ pub unsafe extern "C" fn log_write( format: *const crate::binary::c_types::c_char, args: ... ) { - crate::binary::log::syslog(level, format as *const _, args); + crate::binary::log::syslog(level, format as _, args); } /// ************************************************************************** @@ -1485,7 +1485,7 @@ pub unsafe extern "C" fn log_writev( ) { crate::binary::log::syslog( level, - format as *const _, + format as _, core::mem::transmute::>(args), ); } diff --git a/esp-wifi/src/wifi/utils.rs b/esp-wifi/src/wifi/utils.rs deleted file mode 100644 index eca819f997..0000000000 --- a/esp-wifi/src/wifi/utils.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Convenience utilities for non-async code - -use smoltcp::{ - iface::{Config, Interface}, - wire::{EthernetAddress, HardwareAddress}, -}; - -use super::{WifiApDevice, WifiController, WifiDevice, WifiDeviceMode, WifiError, WifiStaDevice}; -use crate::EspWifiController; - -/// [esp_hal::time::Instant] as a smoltcp [`Instant`] -#[cfg(feature = "smoltcp")] -fn timestamp() -> smoltcp::time::Instant { - smoltcp::time::Instant::from_micros( - esp_hal::time::Instant::now() - .duration_since_epoch() - .as_micros() as i64, - ) -} - -fn setup_iface(device: &mut WifiDevice<'_, Dm>, mode: Dm) -> Interface { - let mac = mode.mac_address(); - let hw_address = HardwareAddress::Ethernet(EthernetAddress::from_bytes(&mac)); - - let config = Config::new(hw_address); - let iface = Interface::new(config, device, timestamp()); - iface -} - -/// Convenient way to create an `smoltcp` ethernet interface -pub fn create_network_interface<'d, Dm: WifiDeviceMode>( - inited: &'d EspWifiController<'d>, - device: impl crate::hal::peripheral::Peripheral

+ 'd, - mode: Dm, -) -> Result<(Interface, WifiDevice<'d, Dm>, WifiController<'d>), WifiError> { - let (mut device, controller) = crate::wifi::new_with_mode(inited, device, mode)?; - - let iface = setup_iface(&mut device, mode); - - Ok((iface, device, controller)) -} - -pub struct ApStaInterface<'d> { - pub ap_interface: Interface, - pub sta_interface: Interface, - pub ap_device: WifiDevice<'d, WifiApDevice>, - pub sta_device: WifiDevice<'d, WifiStaDevice>, - pub controller: WifiController<'d>, -} - -pub fn create_ap_sta_network_interface<'d>( - inited: &'d EspWifiController<'d>, - device: impl crate::hal::peripheral::Peripheral

+ 'd, -) -> Result, WifiError> { - let (mut ap_device, mut sta_device, controller) = crate::wifi::new_ap_sta(inited, device)?; - - let ap_interface = setup_iface(&mut ap_device, WifiApDevice); - let sta_interface = setup_iface(&mut sta_device, WifiStaDevice); - - Ok(ApStaInterface { - ap_interface, - sta_interface, - ap_device, - sta_device, - controller, - }) -} diff --git a/examples/src/bin/wifi_80211_tx.rs b/examples/src/bin/wifi_80211_tx.rs index 098b832f8d..1269f97dd8 100644 --- a/examples/src/bin/wifi_80211_tx.rs +++ b/examples/src/bin/wifi_80211_tx.rs @@ -3,7 +3,7 @@ //! Periodically transmits a beacon frame. //! -//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/sniffer esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -21,6 +21,7 @@ use esp_hal::{ time::Duration, timer::timg::TimerGroup, }; +use esp_println::println; use esp_wifi::{init, wifi}; use ieee80211::{ common::{CapabilitiesInformation, FCFFlags}, @@ -47,17 +48,17 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( + let esp_wifi_ctrl = init( timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, ) .unwrap(); - let wifi = peripherals.WIFI; - // We must initialize some kind of interface and start it. - let (_, mut controller) = wifi::new_with_mode(&init, wifi, wifi::WifiApDevice).unwrap(); + let (mut controller, _) = esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + controller.set_mode(wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); let mut sniffer = controller.take_sniffer().unwrap(); @@ -112,8 +113,10 @@ fn main() -> ! { // Only use the actually written bytes. let beacon = &beacon[..length]; + println!("Scan for WiFi networks and find `esp-wifi 802.11 injection`"); + loop { - sniffer.send_raw_frame(false, beacon, false).unwrap(); + sniffer.send_raw_frame(true, beacon, false).unwrap(); delay.delay(Duration::from_millis(100)); } } diff --git a/examples/src/bin/wifi_access_point.rs b/examples/src/bin/wifi_access_point.rs index 301dd1422b..e590545a1a 100644 --- a/examples/src/bin/wifi_access_point.rs +++ b/examples/src/bin/wifi_access_point.rs @@ -8,7 +8,7 @@ //! On Android you might need to choose _Keep Accesspoint_ when it tells you the WiFi has no internet connection, Chrome might not want to load the URL - you can use a shell and try `curl` and `ping` //! -//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/smoltcp esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -30,10 +30,8 @@ use esp_wifi::{ init, wifi::{ event::{self, EventExt}, - utils::create_network_interface, AccessPointConfiguration, Configuration, - WifiApDevice, }, }; use smoltcp::iface::{SocketSet, SocketStorage}; @@ -68,11 +66,14 @@ fn main() -> ! { let mut rng = Rng::new(peripherals.RNG); - let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + let esp_wifi_ctrl = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + + let (mut controller, interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let mut device = interfaces.ap; + let iface = create_interface(&mut device); - let mut wifi = peripherals.WIFI; - let (iface, device, mut controller) = - create_network_interface(&init, &mut wifi, WifiApDevice).unwrap(); let now = || time::Instant::now().duration_since_epoch().as_millis(); let mut socket_set_entries: [SocketStorage; 3] = Default::default(); @@ -186,3 +187,24 @@ fn parse_ip(ip: &str) -> [u8; 4] { } result } + +// 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_wifi::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(), + ) +} diff --git a/examples/src/bin/wifi_access_point_with_sta.rs b/examples/src/bin/wifi_access_point_with_sta.rs index 9684392b38..0d01905e76 100644 --- a/examples/src/bin/wifi_access_point_with_sta.rs +++ b/examples/src/bin/wifi_access_point_with_sta.rs @@ -9,7 +9,7 @@ //! On Android you might need to choose _Keep Accesspoint_ when it tells you the WiFi has no internet connection, Chrome might not want to load the URL - you can use a shell and try `curl` and `ping` //! -//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/smoltcp esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -31,12 +31,7 @@ use esp_hal::{ use esp_println::{print, println}; use esp_wifi::{ init, - wifi::{ - utils::{create_ap_sta_network_interface, ApStaInterface}, - AccessPointConfiguration, - ClientConfiguration, - Configuration, - }, + wifi::{AccessPointConfiguration, ClientConfiguration, Configuration}, }; use smoltcp::{ iface::{SocketSet, SocketStorage}, @@ -58,17 +53,16 @@ fn main() -> ! { let mut rng = Rng::new(peripherals.RNG); - let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + let esp_wifi_ctrl = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); - let wifi = peripherals.WIFI; + let (mut controller, interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); - let ApStaInterface { - ap_interface, - sta_interface, - ap_device, - sta_device, - mut controller, - } = create_ap_sta_network_interface(&init, wifi).unwrap(); + let mut ap_device = interfaces.ap; + let ap_interface = create_interface(&mut ap_device); + + let mut sta_device = interfaces.sta; + let sta_interface = create_interface(&mut sta_device); let now = || time::Instant::now().duration_since_epoch().as_millis(); let mut ap_socket_set_entries: [SocketStorage; 3] = Default::default(); @@ -233,3 +227,24 @@ fn parse_ip(ip: &str) -> [u8; 4] { } result } + +// 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_wifi::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(), + ) +} diff --git a/examples/src/bin/wifi_bench.rs b/examples/src/bin/wifi_bench.rs index 9ba0cfbd58..06537da8d8 100644 --- a/examples/src/bin/wifi_bench.rs +++ b/examples/src/bin/wifi_bench.rs @@ -8,7 +8,7 @@ //! Ensure you have set the IP of your local machine in the `HOST_IP` env variable. E.g `HOST_IP="192.168.0.24"` and also set SSID and PASSWORD env variable before running this example. //! -//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/smoltcp esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -31,14 +31,7 @@ use esp_hal::{ use esp_println::println; use esp_wifi::{ init, - wifi::{ - utils::create_network_interface, - AccessPointInfo, - ClientConfiguration, - Configuration, - WifiError, - WifiStaDevice, - }, + wifi::{AccessPointInfo, ClientConfiguration, Configuration, WifiError}, }; use smoltcp::{ iface::{SocketSet, SocketStorage}, @@ -71,11 +64,17 @@ fn main() -> ! { let mut rng = Rng::new(peripherals.RNG); - let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + let esp_wifi_ctrl = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); - let mut wifi = peripherals.WIFI; - let (iface, device, mut controller) = - create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + let (mut controller, interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let mut device = interfaces.sta; + let iface = create_interface(&mut device); + + controller + .set_power_saving(esp_wifi::config::PowerSaveMode::None) + .unwrap(); let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); @@ -264,3 +263,24 @@ fn test_upload_download<'a, D: smoltcp::phy::Device>( socket.disconnect(); } + +// 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_wifi::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(), + ) +} diff --git a/examples/src/bin/wifi_ble.rs b/examples/src/bin/wifi_ble.rs index 85494d8df2..0a0991ef5a 100644 --- a/examples/src/bin/wifi_ble.rs +++ b/examples/src/bin/wifi_ble.rs @@ -45,7 +45,7 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( + let esp_wifi_ctrl = init( timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, @@ -67,7 +67,7 @@ fn main() -> ! { let now = || time::Instant::now().duration_since_epoch().as_millis(); loop { - let connector = BleConnector::new(&init, &mut bluetooth); + let connector = BleConnector::new(&esp_wifi_ctrl, &mut bluetooth); let hci = HciConnector::new(connector, now); let mut ble = Ble::new(&hci); diff --git a/examples/src/bin/wifi_coex.rs b/examples/src/bin/wifi_coex.rs index a0d8def41d..b33c35319e 100644 --- a/examples/src/bin/wifi_coex.rs +++ b/examples/src/bin/wifi_coex.rs @@ -8,7 +8,7 @@ //! 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-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/ble esp-wifi/coex esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/smoltcp esp-wifi/ble esp-wifi/coex esp-hal/unstable //% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -42,7 +42,7 @@ use esp_println::{print, println}; use esp_wifi::{ ble::controller::BleConnector, init, - wifi::{utils::create_network_interface, ClientConfiguration, Configuration, WifiStaDevice}, + wifi::{ClientConfiguration, Configuration}, }; use smoltcp::{ iface::{SocketSet, SocketStorage}, @@ -66,13 +66,16 @@ fn main() -> ! { let mut rng = Rng::new(peripherals.RNG); - let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + let esp_wifi_ctrl = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); - let mut wifi = peripherals.WIFI; let bluetooth = peripherals.BT; - let (iface, device, mut controller) = - create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + let (mut controller, interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let mut device = interfaces.sta; + let iface = create_interface(&mut device); + controller .set_power_saving(esp_wifi::config::PowerSaveMode::None) .unwrap(); @@ -128,7 +131,7 @@ fn main() -> ! { } } - let connector = BleConnector::new(&init, bluetooth); + let connector = BleConnector::new(&esp_wifi_ctrl, bluetooth); let hci = HciConnector::new(connector, now); let mut ble = Ble::new(&hci); @@ -189,3 +192,24 @@ fn main() -> ! { } } } + +// 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_wifi::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(), + ) +} diff --git a/examples/src/bin/wifi_csi.rs b/examples/src/bin/wifi_csi.rs index 410ffc8780..33125a1481 100644 --- a/examples/src/bin/wifi_csi.rs +++ b/examples/src/bin/wifi_csi.rs @@ -4,7 +4,7 @@ //! Set SSID and PASSWORD env variable before running this example. //! -//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/log esp-wifi/csi esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/smoltcp esp-wifi/log esp-wifi/csi esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -19,15 +19,7 @@ use esp_hal::{clock::CpuClock, main, rng::Rng, time, timer::timg::TimerGroup}; use esp_println::println; use esp_wifi::{ init, - wifi::{ - utils::create_network_interface, - AccessPointInfo, - ClientConfiguration, - Configuration, - CsiConfig, - WifiError, - WifiStaDevice, - }, + wifi::{AccessPointInfo, ClientConfiguration, Configuration, CsiConfig, WifiError}, }; use smoltcp::{ iface::{SocketSet, SocketStorage}, @@ -48,11 +40,13 @@ fn main() -> ! { let mut rng = Rng::new(peripherals.RNG); let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + let esp_wifi_ctrl = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); - let mut wifi = peripherals.WIFI; - let (iface, device, mut controller) = - create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + let (mut controller, interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let mut device = interfaces.sta; + let iface = create_interface(&mut device); let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); @@ -132,3 +126,24 @@ fn main() -> ! { println!("Start busy loop on main"); loop {} } + +// 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_wifi::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(), + ) +} diff --git a/examples/src/bin/wifi_dhcp.rs b/examples/src/bin/wifi_dhcp.rs index f47fc64867..4b6ef22da5 100644 --- a/examples/src/bin/wifi_dhcp.rs +++ b/examples/src/bin/wifi_dhcp.rs @@ -6,9 +6,11 @@ //! This gets an ip address via DHCP then performs an HTTP get request to some "random" server //! -//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/wifi esp-hal/unstable esp-wifi/smoltcp //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 +// esp-wifi/utils + #![no_std] #![no_main] @@ -29,14 +31,7 @@ use esp_hal::{ use esp_println::{print, println}; use esp_wifi::{ init, - wifi::{ - utils::create_network_interface, - AccessPointInfo, - ClientConfiguration, - Configuration, - WifiError, - WifiStaDevice, - }, + wifi::{AccessPointInfo, ClientConfiguration, Configuration, WifiError}, }; use smoltcp::{ iface::{SocketSet, SocketStorage}, @@ -58,14 +53,13 @@ fn main() -> ! { let mut rng = Rng::new(peripherals.RNG); - let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + let esp_wifi_ctrl = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); - let mut wifi = peripherals.WIFI; - let (iface, device, mut controller) = - create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); - controller - .set_power_saving(esp_wifi::config::PowerSaveMode::None) - .unwrap(); + let (mut controller, interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let mut device = interfaces.sta; + let iface = create_interface(&mut device); let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); @@ -80,6 +74,10 @@ fn main() -> ! { let now = || time::Instant::now().duration_since_epoch().as_millis(); let stack = Stack::new(iface, device, socket_set, now, rng.random()); + controller + .set_power_saving(esp_wifi::config::PowerSaveMode::None) + .unwrap(); + let client_config = Configuration::Client(ClientConfiguration { ssid: SSID.try_into().unwrap(), password: PASSWORD.try_into().unwrap(), @@ -167,3 +165,24 @@ fn main() -> ! { } } } + +// 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_wifi::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(), + ) +} diff --git a/examples/src/bin/wifi_embassy_access_point.rs b/examples/src/bin/wifi_embassy_access_point.rs index 0282b16c3f..fb2cf27abc 100644 --- a/examples/src/bin/wifi_embassy_access_point.rs +++ b/examples/src/bin/wifi_embassy_access_point.rs @@ -5,11 +5,8 @@ //! - open http://192.168.2.1:8080/ in your browser - the example will perform an HTTP get request to some "random" server //! //! On Android you might need to choose _Keep Accesspoint_ when it tells you the WiFi has no internet connection, Chrome might not want to load the URL - you can use a shell and try `curl` and `ping` -//! -//! Because of the huge task-arena size configured this won't work on ESP32-S2 -//! -//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer esp-hal/unstable +//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -37,7 +34,6 @@ use esp_wifi::{ wifi::{ AccessPointConfiguration, Configuration, - WifiApDevice, WifiController, WifiDevice, WifiEvent, @@ -69,14 +65,14 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); let mut rng = Rng::new(peripherals.RNG); - let init = &*mk_static!( + let esp_wifi_ctrl = &*mk_static!( EspWifiController<'static>, init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap() ); - let wifi = peripherals.WIFI; - let (wifi_interface, controller) = - esp_wifi::wifi::new_with_mode(&init, wifi, WifiApDevice).unwrap(); + let (controller, interfaces) = esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let device = interfaces.sta; cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { @@ -102,7 +98,7 @@ async fn main(spawner: Spawner) -> ! { // Init network stack let (stack, runner) = embassy_net::new( - wifi_interface, + device, config, mk_static!(StackResources<3>, StackResources::<3>::new()), seed, @@ -273,6 +269,6 @@ async fn connection(mut controller: WifiController<'static>) { } #[embassy_executor::task] -async fn net_task(mut runner: Runner<'static, WifiDevice<'static, WifiApDevice>>) { +async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) { runner.run().await } diff --git a/examples/src/bin/wifi_embassy_access_point_with_sta.rs b/examples/src/bin/wifi_embassy_access_point_with_sta.rs index aac08cf7ce..a848516770 100644 --- a/examples/src/bin/wifi_embassy_access_point_with_sta.rs +++ b/examples/src/bin/wifi_embassy_access_point_with_sta.rs @@ -12,7 +12,7 @@ //! Because of the huge task-arena size configured this won't work on ESP32-S2 //! -//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-wifi/utils esp-hal/unstable +//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -40,11 +40,9 @@ use esp_wifi::{ AccessPointConfiguration, ClientConfiguration, Configuration, - WifiApDevice, WifiController, WifiDevice, WifiEvent, - WifiStaDevice, WifiState, }, EspWifiController, @@ -74,14 +72,16 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); let mut rng = Rng::new(peripherals.RNG); - let init = &*mk_static!( + let esp_wifi_ctrl = &*mk_static!( EspWifiController<'static>, init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap() ); - let wifi = peripherals.WIFI; - let (wifi_ap_interface, wifi_sta_interface, mut controller) = - esp_wifi::wifi::new_ap_sta(&init, wifi).unwrap(); + let (mut controller, interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let wifi_ap_device = interfaces.ap; + let wifi_sta_device = interfaces.sta; cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { @@ -105,13 +105,13 @@ async fn main(spawner: Spawner) -> ! { // Init network stacks let (ap_stack, ap_runner) = embassy_net::new( - wifi_ap_interface, + wifi_ap_device, ap_config, mk_static!(StackResources<3>, StackResources::<3>::new()), seed, ); let (sta_stack, sta_runner) = embassy_net::new( - wifi_sta_interface, + wifi_sta_device, sta_config, mk_static!(StackResources<3>, StackResources::<3>::new()), seed, @@ -131,8 +131,8 @@ async fn main(spawner: Spawner) -> ! { controller.set_configuration(&client_config).unwrap(); spawner.spawn(connection(controller)).ok(); - spawner.spawn(ap_task(ap_runner)).ok(); - spawner.spawn(sta_task(sta_runner)).ok(); + spawner.spawn(net_task(ap_runner)).ok(); + spawner.spawn(net_task(sta_runner)).ok(); loop { if sta_stack.is_link_up() { @@ -328,12 +328,7 @@ async fn connection(mut controller: WifiController<'static>) { } } -#[embassy_executor::task] -async fn ap_task(mut runner: Runner<'static, WifiDevice<'static, WifiApDevice>>) { - runner.run().await -} - -#[embassy_executor::task] -async fn sta_task(mut runner: Runner<'static, WifiDevice<'static, WifiStaDevice>>) { +#[embassy_executor::task(pool_size = 2)] +async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) { runner.run().await } diff --git a/examples/src/bin/wifi_embassy_bench.rs b/examples/src/bin/wifi_embassy_bench.rs index b8acab766b..0113f29bd4 100644 --- a/examples/src/bin/wifi_embassy_bench.rs +++ b/examples/src/bin/wifi_embassy_bench.rs @@ -10,7 +10,7 @@ //! Because of the huge task-arena size configured this won't work on ESP32-S2 and ESP32-C2 //! -//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-wifi/utils esp-hal/unstable +//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c3 esp32c6 #![allow(static_mut_refs)] @@ -29,15 +29,7 @@ use esp_hal::{clock::CpuClock, rng::Rng, timer::timg::TimerGroup}; use esp_println::println; use esp_wifi::{ init, - wifi::{ - ClientConfiguration, - Configuration, - WifiController, - WifiDevice, - WifiEvent, - WifiStaDevice, - WifiState, - }, + wifi::{ClientConfiguration, Configuration, WifiController, WifiDevice, WifiEvent, WifiState}, EspWifiController, }; @@ -82,14 +74,19 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); let mut rng = Rng::new(peripherals.RNG); - let init = &*mk_static!( + let esp_wifi_ctrl = &*mk_static!( EspWifiController<'static>, init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap() ); - let wifi = peripherals.WIFI; - let (wifi_interface, controller) = - esp_wifi::wifi::new_with_mode(&init, wifi, WifiStaDevice).unwrap(); + let (mut controller, interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let wifi_interface = interfaces.sta; + + controller + .set_power_saving(esp_wifi::config::PowerSaveMode::None) + .unwrap(); cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { @@ -185,7 +182,7 @@ async fn connection(mut controller: WifiController<'static>) { } #[embassy_executor::task] -async fn net_task(mut runner: Runner<'static, WifiDevice<'static, WifiStaDevice>>) { +async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) { runner.run().await } diff --git a/examples/src/bin/wifi_embassy_ble.rs b/examples/src/bin/wifi_embassy_ble.rs index 32002fc8ce..122ddc38e0 100644 --- a/examples/src/bin/wifi_embassy_ble.rs +++ b/examples/src/bin/wifi_embassy_ble.rs @@ -57,7 +57,7 @@ async fn main(_spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = &*mk_static!( + let esp_wifi_ctrl = &*mk_static!( EspWifiController<'static>, init( timg0.timer0, @@ -89,7 +89,7 @@ async fn main(_spawner: Spawner) -> ! { let mut bluetooth = peripherals.BT; - let connector = BleConnector::new(&init, &mut bluetooth); + let connector = BleConnector::new(&esp_wifi_ctrl, &mut bluetooth); let now = || time::Instant::now().duration_since_epoch().as_millis(); let mut ble = Ble::new(connector, now); diff --git a/examples/src/bin/wifi_embassy_dhcp.rs b/examples/src/bin/wifi_embassy_dhcp.rs index ca657d7ad3..559eeed374 100644 --- a/examples/src/bin/wifi_embassy_dhcp.rs +++ b/examples/src/bin/wifi_embassy_dhcp.rs @@ -7,7 +7,7 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-wifi/utils esp-hal/unstable +//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -24,15 +24,7 @@ use esp_hal::{clock::CpuClock, rng::Rng, timer::timg::TimerGroup}; use esp_println::println; use esp_wifi::{ init, - wifi::{ - ClientConfiguration, - Configuration, - WifiController, - WifiDevice, - WifiEvent, - WifiStaDevice, - WifiState, - }, + wifi::{ClientConfiguration, Configuration, WifiController, WifiDevice, WifiEvent, WifiState}, EspWifiController, }; @@ -60,14 +52,14 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); let mut rng = Rng::new(peripherals.RNG); - let init = &*mk_static!( + let esp_wifi_ctrl = &*mk_static!( EspWifiController<'static>, init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap() ); - let wifi = peripherals.WIFI; - let (wifi_interface, controller) = - esp_wifi::wifi::new_with_mode(&init, wifi, WifiStaDevice).unwrap(); + let (controller, interfaces) = esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let wifi_interface = interfaces.sta; cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { @@ -193,6 +185,6 @@ async fn connection(mut controller: WifiController<'static>) { } #[embassy_executor::task] -async fn net_task(mut runner: Runner<'static, WifiDevice<'static, WifiStaDevice>>) { +async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) { runner.run().await } diff --git a/examples/src/bin/wifi_embassy_esp_now.rs b/examples/src/bin/wifi_embassy_esp_now.rs index c2e66b5f9e..f026128a8a 100644 --- a/examples/src/bin/wifi_embassy_esp_now.rs +++ b/examples/src/bin/wifi_embassy_esp_now.rs @@ -4,7 +4,7 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now esp-hal/unstable +//% FEATURES: embassy esp-wifi esp-wifi/esp-now esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -43,7 +43,7 @@ async fn main(_spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = &*mk_static!( + let esp_wifi_ctrl = &*mk_static!( EspWifiController<'static>, init( timg0.timer0, @@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) -> ! { ); let wifi = peripherals.WIFI; - let mut esp_now = esp_wifi::esp_now::EspNow::new(&init, wifi).unwrap(); + let mut esp_now = esp_wifi::esp_now::EspNow::new(&esp_wifi_ctrl, wifi).unwrap(); println!("esp-now version {}", esp_now.version().unwrap()); cfg_if::cfg_if! { diff --git a/examples/src/bin/wifi_embassy_esp_now_duplex.rs b/examples/src/bin/wifi_embassy_esp_now_duplex.rs index 22a1d5437b..e2ff4280a2 100644 --- a/examples/src/bin/wifi_embassy_esp_now_duplex.rs +++ b/examples/src/bin/wifi_embassy_esp_now_duplex.rs @@ -4,7 +4,7 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: embassy esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now esp-hal/unstable +//% FEATURES: embassy esp-wifi esp-wifi/esp-now esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -43,7 +43,7 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = &*mk_static!( + let esp_wifi_ctrl = &*mk_static!( EspWifiController<'static>, init( timg0.timer0, @@ -54,7 +54,7 @@ async fn main(spawner: Spawner) -> ! { ); let wifi = peripherals.WIFI; - let esp_now = esp_wifi::esp_now::EspNow::new(&init, wifi).unwrap(); + let esp_now = esp_wifi::esp_now::EspNow::new(&esp_wifi_ctrl, wifi).unwrap(); println!("esp-now version {}", esp_now.version().unwrap()); cfg_if::cfg_if! { diff --git a/examples/src/bin/wifi_esp_now.rs b/examples/src/bin/wifi_esp_now.rs index 2c88b65919..0eceaf2e36 100644 --- a/examples/src/bin/wifi_esp_now.rs +++ b/examples/src/bin/wifi_esp_now.rs @@ -2,7 +2,7 @@ //! //! Broadcasts, receives and sends messages via esp-now -//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/esp-now esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -33,7 +33,7 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( + let esp_wifi_ctrl = init( timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, @@ -41,7 +41,7 @@ fn main() -> ! { .unwrap(); let wifi = peripherals.WIFI; - let mut esp_now = esp_wifi::esp_now::EspNow::new(&init, wifi).unwrap(); + let mut esp_now = esp_wifi::esp_now::EspNow::new(&esp_wifi_ctrl, wifi).unwrap(); println!("esp-now version {}", esp_now.version().unwrap()); diff --git a/examples/src/bin/wifi_sniffer.rs b/examples/src/bin/wifi_sniffer.rs index c8f4d96e15..edaa243b20 100644 --- a/examples/src/bin/wifi_sniffer.rs +++ b/examples/src/bin/wifi_sniffer.rs @@ -3,7 +3,7 @@ //! Sniffs for beacon frames. //! -//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/sniffer esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -35,17 +35,18 @@ fn main() -> ! { esp_alloc::heap_allocator!(size: 72 * 1024); let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( + let esp_wifi_ctrl = init( timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, ) .unwrap(); - let wifi = peripherals.WIFI; - // We must initialize some kind of interface and start it. - let (_, mut controller) = wifi::new_with_mode(&init, wifi, wifi::WifiApDevice).unwrap(); + let (mut controller, _interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + controller.set_mode(wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); let mut sniffer = controller.take_sniffer().unwrap(); diff --git a/examples/src/bin/wifi_static_ip.rs b/examples/src/bin/wifi_static_ip.rs index 796f614ea4..66196f038e 100644 --- a/examples/src/bin/wifi_static_ip.rs +++ b/examples/src/bin/wifi_static_ip.rs @@ -7,7 +7,7 @@ //! - responds with some HTML content when connecting to port 8080 //! -//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-hal/unstable +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/smoltcp esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -27,14 +27,7 @@ use esp_hal::{ use esp_println::{print, println}; use esp_wifi::{ init, - wifi::{ - utils::create_network_interface, - AccessPointInfo, - ClientConfiguration, - Configuration, - WifiError, - WifiStaDevice, - }, + wifi::{AccessPointInfo, ClientConfiguration, Configuration, WifiError}, }; use smoltcp::iface::{SocketSet, SocketStorage}; @@ -55,11 +48,17 @@ fn main() -> ! { let mut rng = Rng::new(peripherals.RNG); - let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + let esp_wifi_ctrl = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); - let mut wifi = peripherals.WIFI; - let (iface, device, mut controller) = - create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + let (mut controller, interfaces) = + esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).unwrap(); + + let mut device = interfaces.sta; + let iface = create_interface(&mut device); + + controller + .set_power_saving(esp_wifi::config::PowerSaveMode::None) + .unwrap(); let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let socket_set = SocketSet::new(&mut socket_set_entries[..]); @@ -199,3 +198,24 @@ fn parse_ip(ip: &str) -> [u8; 4] { } result } + +// 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_wifi::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(), + ) +} diff --git a/xtask/src/documentation.rs b/xtask/src/documentation.rs index 4936251900..c1f61ce467 100644 --- a/xtask/src/documentation.rs +++ b/xtask/src/documentation.rs @@ -242,7 +242,6 @@ fn apply_feature_rules(package: &Package, config: &Config) -> Vec { features.push("wifi".to_owned()); features.push("esp-now".to_owned()); features.push("sniffer".to_owned()); - features.push("utils".to_owned()); features.push("smoltcp/proto-ipv4".to_owned()); features.push("smoltcp/proto-ipv6".to_owned()); }