#![cfg_attr(
docsrs,
doc = "<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.esp-rs.org/esp-wifi/'>browse the <code>esp-wifi</code> documentation on the esp-rs website</a> instead.</p><p>The documentation here on <a href='https://docs.rs'>docs.rs</a> is built for a single chip only (ESP32-C3, in particular), while on the esp-rs website you can select your exact chip from the list of supported devices. Available peripherals and their APIs might change depending on the chip.</p></div>\n\n<br/>\n\n"
)]
#![cfg_attr(esp32, doc = "**ESP32**")]
#![cfg_attr(esp32s2, doc = "**ESP32-S2**")]
#![cfg_attr(esp32s3, doc = "**ESP32-S3**")]
#![cfg_attr(esp32c2, doc = "**ESP32-C2**")]
#![cfg_attr(esp32c3, doc = "**ESP32-C3**")]
#![cfg_attr(esp32c6, doc = "**ESP32-C6**")]
#![cfg_attr(esp32h2, doc = "**ESP32-H2**")]
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
#![doc = ""]
#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_wifi_config_table.md"))]
#![doc = ""]
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
#![no_std]
#![cfg_attr(xtensa, feature(asm_experimental_arch))]
#![cfg_attr(feature = "sys-logs", feature(c_variadic))]
#![allow(rustdoc::bare_urls)]
#![allow(unknown_lints)]
#![allow(non_local_definitions)]
extern crate alloc;
mod fmt;
use core::marker::PhantomData;
use common_adapter::chip_specific::phy_mem_init;
use esp_config::*;
use esp_hal as hal;
use esp_hal::peripheral::Peripheral;
#[cfg(not(feature = "esp32"))]
use esp_hal::timer::systimer::Alarm;
use fugit::MegahertzU32;
use hal::{
clock::Clocks,
rng::{Rng, Trng},
system::RadioClockController,
timer::{timg::Timer as TimgTimer, AnyTimer, PeriodicTimer},
};
use portable_atomic::Ordering;
#[cfg(feature = "wifi")]
use crate::wifi::WifiError;
use crate::{
tasks::init_tasks,
timer::{setup_timer_isr, shutdown_timer_isr},
};
mod binary {
pub use esp_wifi_sys::*;
}
mod compat;
mod preempt;
mod timer;
#[cfg(feature = "wifi")]
pub mod wifi;
#[cfg(feature = "ble")]
pub mod ble;
#[cfg(feature = "esp-now")]
pub mod esp_now;
pub mod config;
pub(crate) mod common_adapter;
#[doc(hidden)]
pub mod tasks;
pub(crate) mod memory_fence;
#[allow(clippy::assertions_on_constants)] const _: () = {
cfg_if::cfg_if! {
if #[cfg(not(esp32h2))] {
core::assert!(binary::include::CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM == 10);
core::assert!(binary::include::CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM == 32);
core::assert!(binary::include::WIFI_STATIC_TX_BUFFER_NUM == 0);
core::assert!(binary::include::CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM == 32);
core::assert!(binary::include::CONFIG_ESP_WIFI_AMPDU_RX_ENABLED == 1);
core::assert!(binary::include::CONFIG_ESP_WIFI_AMPDU_TX_ENABLED == 1);
core::assert!(binary::include::WIFI_AMSDU_TX_ENABLED == 0);
core::assert!(binary::include::CONFIG_ESP32_WIFI_RX_BA_WIN == 6);
}
};
};
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(unused)] struct Config {
rx_queue_size: usize,
tx_queue_size: usize,
static_rx_buf_num: usize,
dynamic_rx_buf_num: usize,
static_tx_buf_num: usize,
dynamic_tx_buf_num: usize,
csi_enable: bool,
ampdu_rx_enable: bool,
ampdu_tx_enable: bool,
amsdu_tx_enable: bool,
rx_ba_win: usize,
max_burst_size: usize,
country_code: &'static str,
country_code_operating_class: u8,
mtu: usize,
tick_rate_hz: u32,
listen_interval: u16,
beacon_timeout: u16,
ap_beacon_timeout: u16,
failure_retry_cnt: u8,
scan_method: u32,
}
pub(crate) const CONFIG: config::EspWifiConfig = config::EspWifiConfig {
rx_queue_size: esp_config_int!(usize, "ESP_WIFI_RX_QUEUE_SIZE"),
tx_queue_size: esp_config_int!(usize, "ESP_WIFI_TX_QUEUE_SIZE"),
static_rx_buf_num: esp_config_int!(usize, "ESP_WIFI_STATIC_RX_BUF_NUM"),
dynamic_rx_buf_num: esp_config_int!(usize, "ESP_WIFI_DYNAMIC_RX_BUF_NUM"),
static_tx_buf_num: esp_config_int!(usize, "ESP_WIFI_STATIC_TX_BUF_NUM"),
dynamic_tx_buf_num: esp_config_int!(usize, "ESP_WIFI_DYNAMIC_TX_BUF_NUM"),
csi_enable: esp_config_bool!("ESP_WIFI_CSI_ENABLE"),
ampdu_rx_enable: esp_config_bool!("ESP_WIFI_AMPDU_RX_ENABLE"),
ampdu_tx_enable: esp_config_bool!("ESP_WIFI_AMPDU_TX_ENABLE"),
amsdu_tx_enable: esp_config_bool!("ESP_WIFI_AMSDU_TX_ENABLE"),
rx_ba_win: esp_config_int!(usize, "ESP_WIFI_RX_BA_WIN"),
max_burst_size: esp_config_int!(usize, "ESP_WIFI_MAX_BURST_SIZE"),
country_code: esp_config_str!("ESP_WIFI_COUNTRY_CODE"),
country_code_operating_class: esp_config_int!(u8, "ESP_WIFI_COUNTRY_CODE_OPERATING_CLASS"),
mtu: esp_config_int!(usize, "ESP_WIFI_MTU"),
tick_rate_hz: esp_config_int!(u32, "ESP_WIFI_TICK_RATE_HZ"),
listen_interval: esp_config_int!(u16, "ESP_WIFI_LISTEN_INTERVAL"),
beacon_timeout: esp_config_int!(u16, "ESP_WIFI_BEACON_TIMEOUT"),
ap_beacon_timeout: esp_config_int!(u16, "ESP_WIFI_AP_BEACON_TIMEOUT"),
failure_retry_cnt: esp_config_int!(u8, "ESP_WIFI_FAILURE_RETRY_CNT"),
scan_method: esp_config_int!(u32, "ESP_WIFI_SCAN_METHOD"),
};
#[allow(clippy::assertions_on_constants)]
const _: () = {
core::assert!(
CONFIG.rx_ba_win < CONFIG.dynamic_rx_buf_num,
"WiFi configuration check: rx_ba_win should not be larger than dynamic_rx_buf_num!"
);
core::assert!(CONFIG.rx_ba_win < (CONFIG.static_rx_buf_num * 2), "WiFi configuration check: rx_ba_win should not be larger than double of the static_rx_buf_num!");
};
type TimeBase = PeriodicTimer<'static, AnyTimer>;
pub(crate) mod flags {
use portable_atomic::{AtomicBool, AtomicUsize};
pub(crate) static ESP_WIFI_INITIALIZED: AtomicBool = AtomicBool::new(false);
pub(crate) static WIFI: AtomicUsize = AtomicUsize::new(0);
pub(crate) static BLE: AtomicBool = AtomicBool::new(false);
}
#[derive(Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EspWifiController<'d> {
_inner: PhantomData<&'d ()>,
}
impl EspWifiController<'_> {
pub fn wifi(&self) -> bool {
crate::flags::WIFI.load(Ordering::Acquire) > 0
}
pub fn ble(&self) -> bool {
crate::flags::BLE.load(Ordering::Acquire)
}
pub fn deinit(self) -> Result<(), InitializationError> {
if crate::flags::ESP_WIFI_INITIALIZED.load(Ordering::Acquire) {
unsafe { deinit_unchecked() }
} else {
Ok(())
}
}
pub(crate) unsafe fn conjure() -> Self {
Self {
_inner: PhantomData,
}
}
}
impl Drop for EspWifiController<'_> {
fn drop(&mut self) {
if crate::flags::ESP_WIFI_INITIALIZED.load(Ordering::Acquire) {
unsafe { deinit_unchecked().ok() };
}
}
}
pub trait EspWifiTimerSource: private::Sealed {
fn timer(self) -> TimeBase;
}
trait IntoAnyTimer: Into<AnyTimer> {}
impl<T, DM> IntoAnyTimer for TimgTimer<T, DM>
where
DM: esp_hal::Mode,
Self: Into<AnyTimer>,
{
}
#[cfg(not(feature = "esp32"))]
impl<T, DM, COMP, UNIT> IntoAnyTimer for Alarm<'_, T, DM, COMP, UNIT>
where
DM: esp_hal::Mode,
Self: Into<AnyTimer>,
{
}
impl private::Sealed for AnyTimer {}
impl IntoAnyTimer for AnyTimer {}
impl<T> EspWifiTimerSource for T
where
T: IntoAnyTimer + private::Sealed,
{
fn timer(self) -> TimeBase {
TimeBase::new(self.into()).timer()
}
}
impl EspWifiTimerSource for TimeBase {
fn timer(self) -> TimeBase {
self
}
}
impl private::Sealed for TimeBase {}
impl<T, DM> private::Sealed for TimgTimer<T, DM>
where
DM: esp_hal::Mode,
Self: Into<AnyTimer>,
{
}
#[cfg(not(feature = "esp32"))]
impl<T, DM, COMP, UNIT> private::Sealed for Alarm<'_, T, DM, COMP, UNIT>
where
DM: esp_hal::Mode,
Self: Into<AnyTimer>,
{
}
pub trait EspWifiRngSource: rand_core::RngCore + private::Sealed {}
impl EspWifiRngSource for Rng {}
impl private::Sealed for Rng {}
impl EspWifiRngSource for Trng<'_> {}
impl private::Sealed for Trng<'_> {}
#[doc = esp_hal::before_snippet!()]
pub fn init<'d, T: EspWifiTimerSource>(
timer: impl Peripheral<P = T> + 'd,
_rng: impl EspWifiRngSource,
_radio_clocks: impl Peripheral<P = hal::peripherals::RADIO_CLK> + 'd,
) -> Result<EspWifiController<'d>, InitializationError> {
const MIN_CLOCK: u32 = 80;
let clocks = Clocks::get();
if clocks.cpu_clock < MegahertzU32::MHz(MIN_CLOCK) {
return Err(InitializationError::WrongClockConfig);
}
info!("esp-wifi configuration {:?}", crate::CONFIG);
crate::common_adapter::chip_specific::enable_wifi_power_domain();
phy_mem_init();
init_tasks();
setup_timer_isr(unsafe { timer.clone_unchecked() }.timer());
wifi_set_log_verbose();
init_clocks();
#[cfg(coex)]
match crate::wifi::coex_initialize() {
0 => {}
error => return Err(InitializationError::General(error)),
}
crate::flags::ESP_WIFI_INITIALIZED.store(true, Ordering::Release);
Ok(EspWifiController {
_inner: PhantomData,
})
}
pub unsafe fn deinit_unchecked() -> Result<(), InitializationError> {
#[cfg(coex)]
{
unsafe { crate::wifi::os_adapter::coex_disable() };
unsafe { crate::wifi::os_adapter::coex_deinit() };
}
let controller = unsafe { EspWifiController::conjure() };
if controller.wifi() {
#[cfg(feature = "wifi")]
crate::wifi::wifi_deinit()?;
crate::flags::WIFI.store(0, Ordering::Release);
}
if controller.ble() {
#[cfg(feature = "ble")]
crate::ble::ble_deinit();
crate::flags::BLE.store(false, Ordering::Release);
}
shutdown_timer_isr();
crate::preempt::delete_all_tasks();
critical_section::with(|cs| crate::timer::TIMER.borrow_ref_mut(cs).take());
crate::flags::ESP_WIFI_INITIALIZED.store(false, Ordering::Release);
Ok(())
}
pub(crate) mod private {
pub trait Sealed {}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InitializationError {
General(i32),
#[cfg(feature = "wifi")]
WifiError(WifiError),
WrongClockConfig,
}
#[cfg(feature = "wifi")]
impl From<WifiError> for InitializationError {
fn from(value: WifiError) -> Self {
InitializationError::WifiError(value)
}
}
pub fn wifi_set_log_verbose() {
#[cfg(all(feature = "sys-logs", not(esp32h2)))]
unsafe {
use crate::binary::include::{
esp_wifi_internal_set_log_level,
wifi_log_level_t_WIFI_LOG_VERBOSE,
};
esp_wifi_internal_set_log_level(wifi_log_level_t_WIFI_LOG_VERBOSE);
}
}
fn init_clocks() {
let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() };
radio_clocks.init_clocks();
}