From 10a9b48b674661eefccf8992b1324f4f5fe8f9ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 26 Oct 2023 18:56:31 +0200 Subject: [PATCH] Typestate devices --- esp-wifi/examples/access_point.rs | 4 +- esp-wifi/examples/bench.rs | 10 +- esp-wifi/examples/coex.rs | 4 +- esp-wifi/examples/dhcp.rs | 4 +- esp-wifi/examples/embassy_access_point.rs | 6 +- esp-wifi/examples/embassy_bench.rs | 6 +- esp-wifi/examples/embassy_dhcp.rs | 6 +- esp-wifi/examples/static_ip.rs | 4 +- esp-wifi/src/wifi/mod.rs | 434 +++++++++++++--------- esp-wifi/src/wifi/utils.rs | 89 +++-- esp-wifi/src/wifi_interface.rs | 50 ++- 11 files changed, 365 insertions(+), 252 deletions(-) diff --git a/esp-wifi/examples/access_point.rs b/esp-wifi/examples/access_point.rs index c555f5ae..0a25b2d4 100644 --- a/esp-wifi/examples/access_point.rs +++ b/esp-wifi/examples/access_point.rs @@ -13,7 +13,7 @@ use esp_backtrace as _; use esp_println::{print, println}; use esp_wifi::initialize; use esp_wifi::wifi::utils::create_network_interface; -use esp_wifi::wifi::WifiMode; +use esp_wifi::wifi::WifiApDevice; use esp_wifi::wifi_interface::WifiStack; use esp_wifi::{current_millis, EspWifiInitFor}; use hal::clock::ClockControl; @@ -48,7 +48,7 @@ fn main() -> ! { let wifi = peripherals.WIFI; let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let (iface, device, mut controller, sockets) = - create_network_interface(&init, wifi, WifiMode::Ap, &mut socket_set_entries).unwrap(); + create_network_interface(&init, wifi, WifiApDevice, &mut socket_set_entries).unwrap(); let mut wifi_stack = WifiStack::new(iface, device, sockets, current_millis); let client_config = Configuration::AccessPoint(AccessPointConfiguration { diff --git a/esp-wifi/examples/bench.rs b/esp-wifi/examples/bench.rs index ba867f84..b100bc38 100644 --- a/esp-wifi/examples/bench.rs +++ b/esp-wifi/examples/bench.rs @@ -12,7 +12,7 @@ use embedded_svc::wifi::{AccessPointInfo, ClientConfiguration, Configuration, Wi use esp_backtrace as _; use esp_println::println; use esp_wifi::wifi::utils::create_network_interface; -use esp_wifi::wifi::{WifiError, WifiMode}; +use esp_wifi::wifi::{WifiError, WifiStaDevice}; use esp_wifi::wifi_interface::WifiStack; use esp_wifi::{current_millis, initialize, EspWifiInitFor}; use hal::clock::ClockControl; @@ -62,7 +62,7 @@ fn main() -> ! { let wifi = peripherals.WIFI; let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let (iface, device, mut controller, sockets) = - create_network_interface(&init, wifi, WifiMode::Sta, &mut socket_set_entries).unwrap(); + create_network_interface(&init, wifi, WifiStaDevice, &mut socket_set_entries).unwrap(); let wifi_stack = WifiStack::new(iface, device, sockets, current_millis); let client_config = Configuration::Client(ClientConfiguration { @@ -137,7 +137,7 @@ fn main() -> ! { fn test_download<'a>( server_address: Ipv4Address, - socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a>, + socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a, WifiStaDevice>, ) { println!("Testing download..."); socket.work(); @@ -171,7 +171,7 @@ fn test_download<'a>( fn test_upload<'a>( server_address: Ipv4Address, - socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a>, + socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a, WifiStaDevice>, ) { println!("Testing upload..."); socket.work(); @@ -205,7 +205,7 @@ fn test_upload<'a>( fn test_upload_download<'a>( server_address: Ipv4Address, - socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a>, + socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a, WifiStaDevice>, ) { println!("Testing upload+download..."); socket.work(); diff --git a/esp-wifi/examples/coex.rs b/esp-wifi/examples/coex.rs index 43c344e4..023e3e6e 100644 --- a/esp-wifi/examples/coex.rs +++ b/esp-wifi/examples/coex.rs @@ -14,7 +14,7 @@ use bleps::{ }; use esp_wifi::{ - ble::controller::BleConnector, current_millis, wifi::WifiMode, wifi_interface::WifiStack, + ble::controller::BleConnector, current_millis, wifi::WifiStaDevice, wifi_interface::WifiStack, EspWifiInitFor, }; @@ -61,7 +61,7 @@ fn main() -> ! { let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let (iface, device, mut controller, sockets) = - create_network_interface(&init, wifi, WifiMode::Sta, &mut socket_set_entries).unwrap(); + create_network_interface(&init, wifi, WifiStaDevice, &mut socket_set_entries).unwrap(); let wifi_stack = WifiStack::new(iface, device, sockets, current_millis); let client_config = Configuration::Client(ClientConfiguration { diff --git a/esp-wifi/examples/dhcp.rs b/esp-wifi/examples/dhcp.rs index e4435ce9..cd67c266 100644 --- a/esp-wifi/examples/dhcp.rs +++ b/esp-wifi/examples/dhcp.rs @@ -12,7 +12,7 @@ use embedded_svc::wifi::{AccessPointInfo, ClientConfiguration, Configuration, Wi use esp_backtrace as _; use esp_println::{print, println}; use esp_wifi::wifi::utils::create_network_interface; -use esp_wifi::wifi::{WifiError, WifiMode}; +use esp_wifi::wifi::{WifiError, WifiStaDevice}; use esp_wifi::wifi_interface::WifiStack; use esp_wifi::{current_millis, initialize, EspWifiInitFor}; use hal::clock::ClockControl; @@ -51,7 +51,7 @@ fn main() -> ! { let wifi = peripherals.WIFI; let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let (iface, device, mut controller, sockets) = - create_network_interface(&init, wifi, WifiMode::Sta, &mut socket_set_entries).unwrap(); + create_network_interface(&init, wifi, WifiStaDevice, &mut socket_set_entries).unwrap(); let wifi_stack = WifiStack::new(iface, device, sockets, current_millis); let client_config = Configuration::Client(ClientConfiguration { diff --git a/esp-wifi/examples/embassy_access_point.rs b/esp-wifi/examples/embassy_access_point.rs index 22cf27dd..34aea3b5 100644 --- a/esp-wifi/examples/embassy_access_point.rs +++ b/esp-wifi/examples/embassy_access_point.rs @@ -15,7 +15,7 @@ use embassy_time::{Duration, Timer}; use embedded_svc::wifi::{AccessPointConfiguration, Configuration, Wifi}; use esp_backtrace as _; use esp_println::{print, println}; -use esp_wifi::wifi::{WifiController, WifiDevice, WifiEvent, WifiMode, WifiState}; +use esp_wifi::wifi::{WifiApDevice, WifiController, WifiDevice, WifiEvent, WifiState}; use esp_wifi::{initialize, EspWifiInitFor}; use hal::clock::ClockControl; use hal::Rng; @@ -47,7 +47,7 @@ async fn main(spawner: Spawner) -> ! { let wifi = peripherals.WIFI; let (wifi_interface, controller) = - esp_wifi::wifi::new_with_mode(&init, wifi, WifiMode::Ap).unwrap(); + esp_wifi::wifi::new_with_mode(&init, wifi, WifiApDevice).unwrap(); let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); embassy::init(&clocks, timer_group0.timer0); @@ -184,6 +184,6 @@ async fn connection(mut controller: WifiController<'static>) { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) { +async fn net_task(stack: &'static Stack>) { stack.run().await } diff --git a/esp-wifi/examples/embassy_bench.rs b/esp-wifi/examples/embassy_bench.rs index 4e5df0c1..771bf0dd 100644 --- a/esp-wifi/examples/embassy_bench.rs +++ b/esp-wifi/examples/embassy_bench.rs @@ -14,7 +14,7 @@ use embassy_time::{with_timeout, Duration, Timer}; use embedded_svc::wifi::{ClientConfiguration, Configuration, Wifi}; use esp_backtrace as _; use esp_println::println; -use esp_wifi::wifi::{WifiController, WifiDevice, WifiEvent, WifiMode, WifiState}; +use esp_wifi::wifi::{WifiApDevice, WifiController, WifiDevice, WifiEvent, WifiState}; use esp_wifi::{initialize, EspWifiInitFor}; use hal::clock::ClockControl; use hal::Rng; @@ -60,7 +60,7 @@ async fn main(spawner: Spawner) -> ! { let wifi = peripherals.WIFI; let (wifi_interface, controller) = - esp_wifi::wifi::new_with_mode(&init, wifi, WifiMode::Sta).unwrap(); + esp_wifi::wifi::new_with_mode(&init, wifi, WifiApDevice).unwrap(); let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); embassy::init(&clocks, timer_group0.timer0); @@ -146,7 +146,7 @@ async fn connection(mut controller: WifiController<'static>) { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) { +async fn net_task(stack: &'static Stack>) { stack.run().await } diff --git a/esp-wifi/examples/embassy_dhcp.rs b/esp-wifi/examples/embassy_dhcp.rs index 243b92f9..d652efb3 100644 --- a/esp-wifi/examples/embassy_dhcp.rs +++ b/esp-wifi/examples/embassy_dhcp.rs @@ -13,7 +13,7 @@ use embassy_time::{Duration, Timer}; use embedded_svc::wifi::{ClientConfiguration, Configuration, Wifi}; use esp_backtrace as _; use esp_println::println; -use esp_wifi::wifi::{WifiController, WifiDevice, WifiEvent, WifiMode, WifiState}; +use esp_wifi::wifi::{WifiController, WifiDevice, WifiEvent, WifiStaDevice, WifiState}; use esp_wifi::{initialize, EspWifiInitFor}; use hal::clock::ClockControl; use hal::Rng; @@ -48,7 +48,7 @@ async fn main(spawner: Spawner) -> ! { let wifi = peripherals.WIFI; let (wifi_interface, controller) = - esp_wifi::wifi::new_with_mode(&init, wifi, WifiMode::Sta).unwrap(); + esp_wifi::wifi::new_with_mode(&init, wifi, WifiStaDevice).unwrap(); let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); embassy::init(&clocks, timer_group0.timer0); @@ -166,6 +166,6 @@ async fn connection(mut controller: WifiController<'static>) { } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) { +async fn net_task(stack: &'static Stack>) { stack.run().await } diff --git a/esp-wifi/examples/static_ip.rs b/esp-wifi/examples/static_ip.rs index 4919944c..7b313502 100644 --- a/esp-wifi/examples/static_ip.rs +++ b/esp-wifi/examples/static_ip.rs @@ -12,7 +12,7 @@ use embedded_svc::wifi::{AccessPointInfo, ClientConfiguration, Configuration, Wi use esp_backtrace as _; use esp_println::{print, println}; use esp_wifi::initialize; -use esp_wifi::wifi::WifiMode; +use esp_wifi::wifi::WifiStaDevice; use esp_wifi::wifi::{utils::create_network_interface, WifiError}; use esp_wifi::wifi_interface::WifiStack; use esp_wifi::{current_millis, EspWifiInitFor}; @@ -53,7 +53,7 @@ fn main() -> ! { let wifi = peripherals.WIFI; let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let (iface, device, mut controller, sockets) = - create_network_interface(&init, wifi, WifiMode::Sta, &mut socket_set_entries).unwrap(); + create_network_interface(&init, wifi, WifiStaDevice, &mut socket_set_entries).unwrap(); let mut wifi_stack = WifiStack::new(iface, device, sockets, current_millis); let client_config = Configuration::Client(ClientConfiguration { diff --git a/esp-wifi/src/wifi/mod.rs b/esp-wifi/src/wifi/mod.rs index 4535a268..eda12e06 100644 --- a/esp-wifi/src/wifi/mod.rs +++ b/esp-wifi/src/wifi/mod.rs @@ -183,34 +183,6 @@ impl Into for WifiMode { } } -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Take care not to drop this while 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 a critical section. -pub(crate) struct EspWifiPacketBuffer { - pub(crate) buffer: *mut c_types::c_void, - pub(crate) len: u16, - pub(crate) eb: *mut c_types::c_void, -} - -unsafe impl Send for EspWifiPacketBuffer {} - -impl Drop for EspWifiPacketBuffer { - fn drop(&mut self) { - trace!("Dropping EspWifiPacketBuffer, freeing memory"); - unsafe { esp_wifi_internal_free_rx_buffer(self.eb) }; - } -} - -impl EspWifiPacketBuffer { - pub fn as_slice_mut(&mut self) -> &mut [u8] { - unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) } - } -} - const DATA_FRAME_SIZE: usize = MTU + ETHERNET_FRAME_HEADER_SIZE; const RX_QUEUE_SIZE: usize = crate::CONFIG.rx_queue_size; @@ -953,25 +925,16 @@ pub(crate) fn wifi_start_scan( /// [`Configuration::Client`] or [`Configuration::Station`]. /// /// If you want to use AP-STA mode, use `[new_ap_sta]`. -pub fn new_with_config<'d>( +pub fn new_with_config<'d, MODE: WifiDeviceMode>( inited: &EspWifiInitialization, device: impl Peripheral

+ 'd, - config: Configuration, -) -> Result<(WifiDevice<'d>, WifiController<'d>), WifiError> { + config: MODE::Config, +) -> Result<(WifiDevice<'d, MODE>, WifiController<'d>), WifiError> { crate::hal::into_ref!(device); - match config { - Configuration::None | Configuration::Mixed(_, _) => { - panic!("This constructor can not initialize the AP-STA mode") - } - _ => {} - } - - let mode = WifiMode::try_from(&config)?; - Ok(( - WifiDevice::new(unsafe { device.clone_unchecked() }, mode), - WifiController::new_with_config(inited, device, config)?, + WifiDevice::new(unsafe { device.clone_unchecked() }, MODE::new()), + WifiController::new_with_config(inited, device, MODE::wrap_config(config))?, )) } @@ -980,20 +943,12 @@ pub fn new_with_config<'d>( /// /// 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>( +pub fn new_with_mode<'d, MODE: WifiDeviceMode>( inited: &EspWifiInitialization, - device: impl Peripheral

+ 'd, - mode: WifiMode, -) -> Result<(WifiDevice<'d>, WifiController<'d>), WifiError> { - new_with_config( - inited, - device, - match mode { - WifiMode::Sta => Configuration::Client(Default::default()), - WifiMode::Ap => Configuration::AccessPoint(Default::default()), - WifiMode::ApSta => panic!("This constructor can not initialize the AP-STA mode"), - }, - ) + device: impl crate::hal::peripheral::Peripheral

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

+ 'd, -) -> Result<(WifiDevice<'d>, WifiDevice<'d>, WifiController<'d>), WifiError> { + 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()) } @@ -1011,15 +973,22 @@ pub fn new_ap_sta<'d>( /// Returns a tuple of `(AP device, STA device, controller)`. pub fn new_ap_sta_with_config<'d>( inited: &EspWifiInitialization, - device: impl Peripheral

+ 'd, + device: impl Peripheral

+ 'd, sta_config: embedded_svc::wifi::ClientConfiguration, ap_config: embedded_svc::wifi::AccessPointConfiguration, -) -> Result<(WifiDevice<'d>, WifiDevice<'d>, WifiController<'d>), WifiError> { +) -> Result< + ( + WifiDevice<'d, WifiApDevice>, + WifiDevice<'d, WifiStaDevice>, + WifiController<'d>, + ), + WifiError, +> { crate::hal::into_ref!(device); Ok(( - WifiDevice::new(unsafe { device.clone_unchecked() }, WifiMode::Ap), - WifiDevice::new(unsafe { device.clone_unchecked() }, WifiMode::Sta), + WifiDevice::new(unsafe { device.clone_unchecked() }, WifiApDevice), + WifiDevice::new(unsafe { device.clone_unchecked() }, WifiStaDevice), WifiController::new_with_config( inited, device, @@ -1028,84 +997,235 @@ pub fn new_ap_sta_with_config<'d>( )) } -#[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum WifiDeviceMode { - Ap, - Sta, -} +mod sealed { + use super::*; -impl WifiDeviceMode { - fn data_queue_rx( - self, - cs: CriticalSection, - ) -> RefMut<'_, SimpleQueue> { - match self { - Self::Ap => DATA_QUEUE_RX_AP.borrow_ref_mut(cs), - Self::Sta => DATA_QUEUE_RX_STA.borrow_ref_mut(cs), + #[derive(Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + /// Take care not to drop this while 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 a critical section. + pub struct EspWifiPacketBuffer { + pub(crate) buffer: *mut c_types::c_void, + pub(crate) len: u16, + pub(crate) eb: *mut c_types::c_void, + } + + unsafe impl Send for EspWifiPacketBuffer {} + + impl Drop for EspWifiPacketBuffer { + fn drop(&mut self) { + trace!("Dropping EspWifiPacketBuffer, freeing memory"); + unsafe { esp_wifi_internal_free_rx_buffer(self.eb) }; } } - fn can_send(self) -> bool { - WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE + impl EspWifiPacketBuffer { + pub fn as_slice_mut(&mut self) -> &mut [u8] { + unsafe { core::slice::from_raw_parts_mut(self.buffer as *mut u8, self.len as usize) } + } } - fn increase_in_flight_counter(self) { - WIFI_TX_INFLIGHT.fetch_add(1, Ordering::SeqCst); + pub trait Sealed: Copy + Sized { + type Config: Default; + + fn new() -> Self; + + fn wrap_config(config: Self::Config) -> Configuration; + + fn data_queue_rx( + self, + cs: CriticalSection, + ) -> RefMut<'_, SimpleQueue>; + + 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() { + Some(WifiTxToken { mode: self }) + } else { + warn!("no Tx token available"); + None + } + } + + fn rx_token(self) -> Option<(WifiRxToken, WifiTxToken)> { + let is_empty = critical_section::with(|cs| self.data_queue_rx(cs).is_empty()); + + if !is_empty { + self.tx_token().map(|tx| (WifiRxToken { mode: self }, tx)) + } else { + trace!("no Rx token available"); + None + } + } + + fn interface(self) -> wifi_interface_t; + + #[cfg(feature = "embassy-net")] + fn register_transmit_waker(self, cx: &mut core::task::Context) { + embassy::TRANSMIT_WAKER.register(cx.waker()) + } + + #[cfg(feature = "embassy-net")] + fn register_receive_waker(self, cx: &mut core::task::Context); + + #[cfg(feature = "embassy-net")] + fn register_link_state_waker(self, cx: &mut core::task::Context); + + #[cfg(feature = "embassy-net")] + fn link_state(self) -> embassy_net::driver::LinkState; } - fn tx_token(self) -> Option { - if self.can_send() { - Some(WifiTxToken { mode: self }) - } else { - warn!("no Tx token available"); - None + 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, + cs: CriticalSection, + ) -> RefMut<'_, SimpleQueue> { + DATA_QUEUE_RX_STA.borrow_ref_mut(cs) + } + + fn interface(self) -> wifi_interface_t { + wifi_interface_t_WIFI_IF_STA + } + + #[cfg(feature = "embassy-net")] + fn register_receive_waker(self, cx: &mut core::task::Context) { + embassy::STA_RECEIVE_WAKER.register(cx.waker()); + } + + #[cfg(feature = "embassy-net")] + fn register_link_state_waker(self, cx: &mut core::task::Context) { + embassy::STA_LINK_STATE_WAKER.register(cx.waker()); + } + + #[cfg(feature = "embassy-net")] + fn link_state(self) -> embassy_net::driver::LinkState { + if matches!(get_sta_state(), WifiState::StaConnected) { + embassy_net::driver::LinkState::Up + } else { + embassy_net::driver::LinkState::Down + } } } - fn rx_token(self) -> Option<(WifiRxToken, WifiTxToken)> { - let is_empty = critical_section::with(|cs| self.data_queue_rx(cs).is_empty()); + impl Sealed for WifiApDevice { + type Config = AccessPointConfiguration; - if !is_empty { - self.tx_token().map(|tx| (WifiRxToken { mode: self }, tx)) - } else { - trace!("no Rx token available"); - None + fn new() -> Self { + Self + } + + fn wrap_config(config: AccessPointConfiguration) -> Configuration { + Configuration::AccessPoint(config) + } + + fn data_queue_rx( + self, + cs: CriticalSection, + ) -> RefMut<'_, SimpleQueue> { + DATA_QUEUE_RX_AP.borrow_ref_mut(cs) + } + + fn interface(self) -> wifi_interface_t { + wifi_interface_t_WIFI_IF_AP + } + + #[cfg(feature = "embassy-net")] + fn register_receive_waker(self, cx: &mut core::task::Context) { + embassy::AP_RECEIVE_WAKER.register(cx.waker()); } + + #[cfg(feature = "embassy-net")] + fn register_link_state_waker(self, cx: &mut core::task::Context) { + embassy::AP_LINK_STATE_WAKER.register(cx.waker()); + } + + #[cfg(feature = "embassy-net")] + fn link_state(self) -> embassy_net::driver::LinkState { + if matches!(get_ap_state(), WifiState::ApStarted) { + embassy_net::driver::LinkState::Up + } else { + embassy_net::driver::LinkState::Down + } + } + } +} + +use sealed::*; + +pub trait WifiDeviceMode: Sealed { + fn mode(self) -> WifiMode; + + fn mac_address(self) -> [u8; 6]; +} + +#[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 } - pub fn mac_address(self) -> [u8; 6] { + fn mac_address(self) -> [u8; 6] { let mut mac = [0; 6]; - match self { - Self::Ap => get_ap_mac(&mut mac), - Self::Sta => get_sta_mac(&mut mac), - } + get_sta_mac(&mut mac); + mac + } +} + +#[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 mac_address(self) -> [u8; 6] { + let mut mac = [0; 6]; + get_ap_mac(&mut mac); mac } } /// A wifi device implementing smoltcp's Device trait. -pub struct WifiDevice<'d> { +pub struct WifiDevice<'d, MODE: WifiDeviceMode> { _device: PeripheralRef<'d, crate::hal::peripherals::WIFI>, - mode: WifiDeviceMode, + mode: MODE, } -impl<'d> WifiDevice<'d> { +impl<'d, MODE: WifiDeviceMode> WifiDevice<'d, MODE> { pub(crate) fn new( _device: PeripheralRef<'d, crate::hal::peripherals::WIFI>, - mode: WifiMode, - ) -> WifiDevice { - let mode = match mode { - WifiMode::Sta => WifiDeviceMode::Sta, - WifiMode::Ap => WifiDeviceMode::Ap, - _ => panic!("A WifiDevice instance supports a single mode only"), - }; - + mode: MODE, + ) -> Self { Self { _device, mode } } - pub(crate) fn get_wifi_mode(&self) -> WifiDeviceMode { - self.mode + pub fn mac_address(&self) -> [u8; 6] { + self.mode.mac_address() } } @@ -1245,9 +1365,9 @@ impl<'d> WifiController<'d> { } // see https://docs.rs/smoltcp/0.7.1/smoltcp/phy/index.html -impl Device for WifiDevice<'_> { - type RxToken<'a> = WifiRxToken where Self: 'a; - type TxToken<'a> = WifiTxToken where Self: 'a; +impl Device for WifiDevice<'_, MODE> { + type RxToken<'a> = WifiRxToken where Self: 'a; + type TxToken<'a> = WifiTxToken where Self: 'a; fn receive( &mut self, @@ -1274,11 +1394,11 @@ impl Device for WifiDevice<'_> { #[doc(hidden)] #[derive(Debug)] -pub struct WifiRxToken { - mode: WifiDeviceMode, +pub struct WifiRxToken { + mode: MODE, } -impl WifiRxToken { +impl WifiRxToken { fn consume_token(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -1304,7 +1424,7 @@ impl WifiRxToken { } } -impl RxToken for WifiRxToken { +impl RxToken for WifiRxToken { fn consume(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -1315,40 +1435,32 @@ impl RxToken for WifiRxToken { #[doc(hidden)] #[derive(Debug)] -pub struct WifiTxToken { - mode: WifiDeviceMode, +pub struct WifiTxToken { + mode: MODE, } -impl WifiTxToken { +impl WifiTxToken { fn consume_token(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, { self.mode.increase_in_flight_counter(); - // (safety): creation of multiple WiFi devices is impossible in safe Rust, therefore only smoltcp _or_ embassy-net can be used at one time - static mut AP_BUFFER: [u8; DATA_FRAME_SIZE] = [0u8; DATA_FRAME_SIZE]; - static mut STA_BUFFER: [u8; DATA_FRAME_SIZE] = [0u8; DATA_FRAME_SIZE]; + // (safety): creation of multiple WiFi devices with the same mode is impossible in safe Rust, + // therefore only smoltcp _or_ embassy-net can be used at one time + static mut BUFFER: [u8; DATA_FRAME_SIZE] = [0u8; DATA_FRAME_SIZE]; - let buffer = match self.mode { - WifiDeviceMode::Ap => unsafe { &mut AP_BUFFER[..len] }, - WifiDeviceMode::Sta => unsafe { &mut STA_BUFFER[..len] }, - }; + let buffer = unsafe { &mut BUFFER[..len] }; let res = f(buffer); - let interface = match self.mode { - WifiDeviceMode::Ap => wifi_interface_t_WIFI_IF_AP, - WifiDeviceMode::Sta => wifi_interface_t_WIFI_IF_STA, - }; - - esp_wifi_send_data(interface, buffer); + esp_wifi_send_data(self.mode.interface(), buffer); res } } -impl TxToken for WifiTxToken { +impl TxToken for WifiTxToken { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -1614,27 +1726,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 WifiDeviceMode { - fn register_transmit_waker(self, cx: &mut core::task::Context) { - TRANSMIT_WAKER.register(cx.waker()) - } - - fn register_receive_waker(self, cx: &mut core::task::Context) { - match self { - Self::Ap => AP_RECEIVE_WAKER.register(cx.waker()), - Self::Sta => STA_RECEIVE_WAKER.register(cx.waker()), - } - } - - fn register_link_state_waker(self, cx: &mut core::task::Context) { - match self { - Self::Ap => AP_LINK_STATE_WAKER.register(cx.waker()), - Self::Sta => STA_LINK_STATE_WAKER.register(cx.waker()), - } - } - } - - impl RxToken for WifiRxToken { + impl RxToken for WifiRxToken { fn consume(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -1643,7 +1735,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, @@ -1652,14 +1744,9 @@ pub(crate) mod embassy { } } - impl Driver for WifiDevice<'_> { - type RxToken<'a> = WifiRxToken - where - Self: 'a; - - type TxToken<'a> = WifiTxToken - where - Self: 'a; + impl Driver for WifiDevice<'_, MODE> { + type RxToken<'a> = WifiRxToken where Self: 'a; + type TxToken<'a> = WifiTxToken where Self: 'a; fn receive( &mut self, @@ -1677,22 +1764,7 @@ pub(crate) mod embassy { fn link_state(&mut self, cx: &mut core::task::Context) -> embassy_net::driver::LinkState { self.mode.register_link_state_waker(cx); - match self.mode { - WifiDeviceMode::Sta => { - if matches!(get_sta_state(), WifiState::StaConnected) { - embassy_net::driver::LinkState::Up - } else { - embassy_net::driver::LinkState::Down - } - } - WifiDeviceMode::Ap => { - if matches!(get_ap_state(), WifiState::ApStarted) { - embassy_net::driver::LinkState::Up - } else { - embassy_net::driver::LinkState::Down - } - } - } + self.mode.link_state() } fn capabilities(&self) -> Capabilities { @@ -1707,7 +1779,7 @@ pub(crate) mod embassy { } fn hardware_address(&self) -> HardwareAddress { - HardwareAddress::Ethernet(self.mode.mac_address()) + HardwareAddress::Ethernet(self.mac_address()) } } } diff --git a/esp-wifi/src/wifi/utils.rs b/esp-wifi/src/wifi/utils.rs index f783a287..7c65ae03 100644 --- a/esp-wifi/src/wifi/utils.rs +++ b/esp-wifi/src/wifi/utils.rs @@ -7,44 +7,89 @@ use smoltcp::{ wire::{EthernetAddress, HardwareAddress}, }; -use crate::{current_millis, wifi::get_sta_mac}; -use crate::{wifi::get_ap_mac, EspWifiInitialization}; +use crate::current_millis; +use crate::EspWifiInitialization; -use super::{WifiController, WifiDevice, WifiError, WifiMode}; +use super::{WifiApDevice, WifiController, WifiDevice, WifiDeviceMode, WifiError, WifiStaDevice}; -/// Convenient way to create an `smoltcp` ethernet interface -/// You can use the provided macros to create and pass a suitable backing storage. -pub fn create_network_interface<'a, 'd>( - inited: &EspWifiInitialization, - device: impl crate::hal::peripheral::Peripheral

+ 'd, - mode: WifiMode, +fn setup_iface<'a, MODE: WifiDeviceMode>( + device: &mut WifiDevice<'_, MODE>, + mode: MODE, storage: &'a mut [SocketStorage<'a>], -) -> Result<(Interface, WifiDevice<'d>, WifiController<'d>, SocketSet<'a>), WifiError> { - let socket_set_entries = storage; - - let mut mac = [0u8; 6]; - match mode.is_ap() { - true => get_ap_mac(&mut mac), - false => get_sta_mac(&mut mac), - } +) -> (Interface, SocketSet<'a>) { + let mac = mode.mac_address(); let hw_address = HardwareAddress::Ethernet(EthernetAddress::from_bytes(&mac)); - let (mut device, controller) = crate::wifi::new_with_mode(inited, device, mode)?; - let config = Config::new(hw_address); let iface = Interface::new( config, - &mut device, + device, Instant::from_millis(current_millis() as i64), ); - let mut socket_set = SocketSet::new(socket_set_entries); + let mut socket_set = SocketSet::new(storage); - if !mode.is_ap() { + if mode.mode().is_sta() { // only add DHCP client in STA mode let dhcp_socket = Dhcpv4Socket::new(); socket_set.add(dhcp_socket); } + (iface, socket_set) +} + +/// Convenient way to create an `smoltcp` ethernet interface +/// You can use the provided macros to create and pass a suitable backing storage. +pub fn create_network_interface<'a, 'd, MODE: WifiDeviceMode>( + inited: &EspWifiInitialization, + device: impl crate::hal::peripheral::Peripheral

+ 'd, + mode: MODE, + storage: &'a mut [SocketStorage<'a>], +) -> Result< + ( + Interface, + WifiDevice<'d, MODE>, + WifiController<'d>, + SocketSet<'a>, + ), + WifiError, +> { + let (mut device, controller) = crate::wifi::new_with_mode(inited, device, mode)?; + + let (iface, socket_set) = setup_iface(&mut device, mode, storage); + Ok((iface, device, controller, socket_set)) } + +pub struct ApStaInterface<'a, '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 ap_socket_set: SocketSet<'a>, + pub sta_socket_set: SocketSet<'a>, +} + +pub fn create_ap_sta_network_interface<'a, 'd, MODE: WifiDeviceMode>( + inited: &EspWifiInitialization, + device: impl crate::hal::peripheral::Peripheral

+ 'd, + _mode: MODE, + ap_storage: &'a mut [SocketStorage<'a>], + sta_storage: &'a mut [SocketStorage<'a>], +) -> Result, WifiError> { + let (mut ap_device, mut sta_device, controller) = crate::wifi::new_ap_sta(inited, device)?; + + let (ap_interface, ap_socket_set) = setup_iface(&mut ap_device, WifiApDevice, ap_storage); + let (sta_interface, sta_socket_set) = setup_iface(&mut sta_device, WifiStaDevice, sta_storage); + + Ok(ApStaInterface { + ap_interface, + sta_interface, + ap_device, + sta_device, + controller, + ap_socket_set, + sta_socket_set, + }) +} diff --git a/esp-wifi/src/wifi_interface.rs b/esp-wifi/src/wifi_interface.rs index da563b70..f1853206 100644 --- a/esp-wifi/src/wifi_interface.rs +++ b/esp-wifi/src/wifi_interface.rs @@ -12,15 +12,15 @@ use smoltcp::time::Instant; use smoltcp::wire::{DnsQueryType, IpAddress, IpCidr, IpEndpoint, Ipv4Address}; use crate::current_millis; -use crate::wifi::{get_ap_mac, get_sta_mac, WifiDevice, WifiDeviceMode}; +use crate::wifi::{WifiDevice, WifiDeviceMode}; use core::borrow::BorrowMut; /// Non-async TCP/IP network stack /// /// Mostly a convenience wrapper for `smoltcp` -pub struct WifiStack<'a> { - device: RefCell>, // TODO allow non static lifetime +pub struct WifiStack<'a, MODE: WifiDeviceMode> { + device: RefCell>, // TODO allow non static lifetime network_interface: RefCell, sockets: RefCell>, current_millis_fn: fn() -> u64, @@ -31,13 +31,13 @@ pub struct WifiStack<'a> { dns_socket_handle: RefCell>, } -impl<'a> WifiStack<'a> { +impl<'a, MODE: WifiDeviceMode> WifiStack<'a, MODE> { pub fn new( network_interface: Interface, - device: WifiDevice<'static>, // TODO relax this lifetime requirement + device: WifiDevice<'static, MODE>, // TODO relax this lifetime requirement mut sockets: SocketSet<'a>, current_millis_fn: fn() -> u64, - ) -> WifiStack<'a> { + ) -> WifiStack<'a, MODE> { let mut dhcp_socket_handle: Option = None; let mut dns_socket_handle: Option = None; @@ -76,11 +76,7 @@ impl<'a> WifiStack<'a> { &self, conf: &ipv4::Configuration, ) -> Result<(), WifiStackError> { - let mut mac = [0u8; 6]; - match self.device.borrow().get_wifi_mode() { - WifiDeviceMode::Sta => get_sta_mac(&mut mac), - WifiDeviceMode::Ap => get_ap_mac(&mut mac), - } + let mac = self.device.borrow().mac_address(); let hw_address = smoltcp::wire::HardwareAddress::Ethernet( smoltcp::wire::EthernetAddress::from_bytes(&mac), ); @@ -239,7 +235,7 @@ impl<'a> WifiStack<'a> { &'s self, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8], - ) -> Socket<'s, 'a> + ) -> Socket<'s, 'a, MODE> where 'a: 's, { @@ -264,7 +260,7 @@ impl<'a> WifiStack<'a> { rx_buffer: &'a mut [u8], tx_meta: &'a mut [smoltcp::socket::udp::PacketMetadata], tx_buffer: &'a mut [u8], - ) -> UdpSocket<'s, 'a> + ) -> UdpSocket<'s, 'a, MODE> where 'a: 's, { @@ -411,7 +407,7 @@ impl<'a> WifiStack<'a> { } #[allow(unused)] - fn with(&self, f: impl FnOnce(&Interface, &WifiDevice, &SocketSet<'a>) -> R) -> R { + fn with(&self, f: impl FnOnce(&Interface, &WifiDevice, &SocketSet<'a>) -> R) -> R { f( &self.network_interface.borrow(), &self.device.borrow(), @@ -421,7 +417,7 @@ impl<'a> WifiStack<'a> { fn with_mut( &self, - f: impl FnOnce(&mut Interface, &mut WifiDevice, &mut SocketSet<'a>) -> R, + f: impl FnOnce(&mut Interface, &mut WifiDevice, &mut SocketSet<'a>) -> R, ) -> R { f( &mut self.network_interface.borrow_mut(), @@ -455,7 +451,7 @@ pub fn timestamp() -> Instant { Instant::from_millis(current_millis() as i64) } -impl<'a> ipv4::Interface for WifiStack<'a> { +impl ipv4::Interface for WifiStack<'_, MODE> { type Error = WifiStackError; fn get_iface_configuration(&self) -> Result { @@ -476,12 +472,12 @@ impl<'a> ipv4::Interface for WifiStack<'a> { } /// A TCP socket -pub struct Socket<'s, 'n: 's> { +pub struct Socket<'s, 'n: 's, MODE: WifiDeviceMode> { socket_handle: SocketHandle, - network: &'s WifiStack<'n>, + network: &'s WifiStack<'n, MODE>, } -impl<'s, 'n: 's> Socket<'s, 'n> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> Socket<'s, 'n, MODE> { /// Connect the socket pub fn open<'i>(&'i mut self, addr: IpAddress, port: u16) -> Result<(), IoError> where @@ -611,7 +607,7 @@ impl<'s, 'n: 's> Socket<'s, 'n> { } } -impl<'s, 'n: 's> Drop for Socket<'s, 'n> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> Drop for Socket<'s, 'n, MODE> { fn drop(&mut self) { self.network .with_mut(|_interface, _device, sockets| sockets.remove(self.socket_handle)); @@ -639,11 +635,11 @@ impl embedded_io::Error for IoError { } } -impl<'s, 'n: 's> ErrorType for Socket<'s, 'n> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> ErrorType for Socket<'s, 'n, MODE> { type Error = IoError; } -impl<'s, 'n: 's> Read for Socket<'s, 'n> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> Read for Socket<'s, 'n, MODE> { fn read(&mut self, buf: &mut [u8]) -> Result { self.network.with_mut(|interface, device, sockets| { use smoltcp::socket::tcp::RecvError; @@ -663,7 +659,7 @@ impl<'s, 'n: 's> Read for Socket<'s, 'n> { } } -impl<'s, 'n: 's> Write for Socket<'s, 'n> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> Write for Socket<'s, 'n, MODE> { fn write(&mut self, buf: &[u8]) -> Result { loop { let (may_send, is_open, can_send) = @@ -728,12 +724,12 @@ impl<'s, 'n: 's> Write for Socket<'s, 'n> { } /// A UDP socket -pub struct UdpSocket<'s, 'n: 's> { +pub struct UdpSocket<'s, 'n: 's, MODE: WifiDeviceMode> { socket_handle: SocketHandle, - network: &'s WifiStack<'n>, + network: &'s WifiStack<'n, MODE>, } -impl<'s, 'n: 's> UdpSocket<'s, 'n> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> UdpSocket<'s, 'n, MODE> { /// Binds the socket to the given port pub fn bind<'i>(&'i mut self, port: u16) -> Result<(), IoError> where @@ -861,7 +857,7 @@ impl<'s, 'n: 's> UdpSocket<'s, 'n> { } } -impl<'s, 'n: 's> Drop for UdpSocket<'s, 'n> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> Drop for UdpSocket<'s, 'n, MODE> { fn drop(&mut self) { self.network .with_mut(|_, _, sockets| sockets.borrow_mut().remove(self.socket_handle));