1#![cfg_attr(
2    all(docsrs, not(not_really_docsrs)),
3    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-hal/'>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"
4)]
5#![cfg_attr(esp32, doc = "**ESP32**")]
7#![cfg_attr(esp32s2, doc = "**ESP32-S2**")]
8#![cfg_attr(esp32s3, doc = "**ESP32-S3**")]
9#![cfg_attr(esp32c2, doc = "**ESP32-C2**")]
10#![cfg_attr(esp32c3, doc = "**ESP32-C3**")]
11#![cfg_attr(esp32c6, doc = "**ESP32-C6**")]
12#![cfg_attr(esp32h2, doc = "**ESP32-H2**")]
13#![doc = concat!(r#"features = [""#, esp_hal::chip!(), r#"", "wifi", "esp-now"]"#)]
29#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
74#![doc = ""]
81#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_wifi_config_table.md"))]
82#![doc = ""]
83#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
88#![no_std]
89#![cfg_attr(xtensa, feature(asm_experimental_arch))]
90#![cfg_attr(feature = "sys-logs", feature(c_variadic))]
91#![deny(rust_2018_idioms, rustdoc::all)]
92#![allow(rustdoc::bare_urls)]
93#![allow(unknown_lints)]
96#![allow(non_local_definitions)]
97
98extern crate alloc;
99
100mod fmt;
102
103use core::marker::PhantomData;
104
105use common_adapter::chip_specific::phy_mem_init;
106use esp_config::*;
107#[cfg(not(feature = "esp32"))]
108use esp_hal::timer::systimer::Alarm;
109use esp_hal::{
110    self as hal,
111    clock::RadioClockController,
112    peripheral::Peripheral,
113    peripherals::RADIO_CLK,
114};
115use hal::{
116    clock::Clocks,
117    rng::{Rng, Trng},
118    time::Rate,
119    timer::{timg::Timer as TimgTimer, AnyTimer, PeriodicTimer},
120    Blocking,
121};
122use portable_atomic::Ordering;
123
124#[cfg(feature = "wifi")]
125use crate::wifi::WifiError;
126use crate::{
127    preempt::yield_task,
128    radio::{setup_radio_isr, shutdown_radio_isr},
129    tasks::init_tasks,
130};
131
132mod binary {
133    pub use esp_wifi_sys::*;
134}
135mod compat;
136
137#[cfg(feature = "builtin-scheduler")]
138mod preempt_builtin;
139
140pub mod preempt;
141
142mod radio;
143mod time;
144
145#[cfg(feature = "wifi")]
146pub mod wifi;
147
148#[cfg(feature = "ble")]
149pub mod ble;
150
151#[cfg(feature = "esp-now")]
152pub mod esp_now;
153
154pub mod config;
155
156pub(crate) mod common_adapter;
157
158#[doc(hidden)]
159pub mod tasks;
160
161pub(crate) mod memory_fence;
162
163#[allow(clippy::assertions_on_constants)] const _: () = {
166    cfg_if::cfg_if! {
167        if #[cfg(not(esp32h2))] {
168            core::assert!(binary::include::CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM == 10);
169            core::assert!(binary::include::CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM == 32);
170            core::assert!(binary::include::WIFI_STATIC_TX_BUFFER_NUM == 0);
171            core::assert!(binary::include::CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM == 32);
172            core::assert!(binary::include::CONFIG_ESP_WIFI_AMPDU_RX_ENABLED == 1);
173            core::assert!(binary::include::CONFIG_ESP_WIFI_AMPDU_TX_ENABLED == 1);
174            core::assert!(binary::include::WIFI_AMSDU_TX_ENABLED == 0);
175            core::assert!(binary::include::CONFIG_ESP32_WIFI_RX_BA_WIN == 6);
176        }
177    };
178};
179
180#[derive(Debug)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182#[allow(unused)] struct Config {
185    rx_queue_size: usize,
186    tx_queue_size: usize,
187    static_rx_buf_num: usize,
188    dynamic_rx_buf_num: usize,
189    static_tx_buf_num: usize,
190    dynamic_tx_buf_num: usize,
191    ampdu_rx_enable: bool,
192    ampdu_tx_enable: bool,
193    amsdu_tx_enable: bool,
194    rx_ba_win: usize,
195    max_burst_size: usize,
196    country_code: &'static str,
197    country_code_operating_class: u8,
198    mtu: usize,
199    tick_rate_hz: u32,
200    listen_interval: u16,
201    beacon_timeout: u16,
202    ap_beacon_timeout: u16,
203    failure_retry_cnt: u8,
204    scan_method: u32,
205}
206
207pub(crate) const CONFIG: config::EspWifiConfig = config::EspWifiConfig {
208    rx_queue_size: esp_config_int!(usize, "ESP_WIFI_CONFIG_RX_QUEUE_SIZE"),
209    tx_queue_size: esp_config_int!(usize, "ESP_WIFI_CONFIG_TX_QUEUE_SIZE"),
210    static_rx_buf_num: esp_config_int!(usize, "ESP_WIFI_CONFIG_STATIC_RX_BUF_NUM"),
211    dynamic_rx_buf_num: esp_config_int!(usize, "ESP_WIFI_CONFIG_DYNAMIC_RX_BUF_NUM"),
212    static_tx_buf_num: esp_config_int!(usize, "ESP_WIFI_CONFIG_STATIC_TX_BUF_NUM"),
213    dynamic_tx_buf_num: esp_config_int!(usize, "ESP_WIFI_CONFIG_DYNAMIC_TX_BUF_NUM"),
214    ampdu_rx_enable: esp_config_bool!("ESP_WIFI_CONFIG_AMPDU_RX_ENABLE"),
215    ampdu_tx_enable: esp_config_bool!("ESP_WIFI_CONFIG_AMPDU_TX_ENABLE"),
216    amsdu_tx_enable: esp_config_bool!("ESP_WIFI_CONFIG_AMSDU_TX_ENABLE"),
217    rx_ba_win: esp_config_int!(usize, "ESP_WIFI_CONFIG_RX_BA_WIN"),
218    max_burst_size: esp_config_int!(usize, "ESP_WIFI_CONFIG_MAX_BURST_SIZE"),
219    country_code: esp_config_str!("ESP_WIFI_CONFIG_COUNTRY_CODE"),
220    country_code_operating_class: esp_config_int!(
221        u8,
222        "ESP_WIFI_CONFIG_COUNTRY_CODE_OPERATING_CLASS"
223    ),
224    mtu: esp_config_int!(usize, "ESP_WIFI_CONFIG_MTU"),
225    tick_rate_hz: esp_config_int!(u32, "ESP_WIFI_CONFIG_TICK_RATE_HZ"),
226    listen_interval: esp_config_int!(u16, "ESP_WIFI_CONFIG_LISTEN_INTERVAL"),
227    beacon_timeout: esp_config_int!(u16, "ESP_WIFI_CONFIG_BEACON_TIMEOUT"),
228    ap_beacon_timeout: esp_config_int!(u16, "ESP_WIFI_CONFIG_AP_BEACON_TIMEOUT"),
229    failure_retry_cnt: esp_config_int!(u8, "ESP_WIFI_CONFIG_FAILURE_RETRY_CNT"),
230    scan_method: esp_config_int!(u32, "ESP_WIFI_CONFIG_SCAN_METHOD"),
231};
232
233#[allow(clippy::assertions_on_constants)]
235const _: () = {
236    core::assert!(
239        CONFIG.rx_ba_win < CONFIG.dynamic_rx_buf_num,
240        "WiFi configuration check: rx_ba_win should not be larger than dynamic_rx_buf_num!"
241    );
242    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!");
243};
244
245type TimeBase = PeriodicTimer<'static, Blocking>;
246
247pub(crate) mod flags {
248    use portable_atomic::{AtomicBool, AtomicUsize};
249
250    pub(crate) static ESP_WIFI_INITIALIZED: AtomicBool = AtomicBool::new(false);
251    pub(crate) static WIFI: AtomicUsize = AtomicUsize::new(0);
252    pub(crate) static BLE: AtomicBool = AtomicBool::new(false);
253}
254
255#[derive(Debug, PartialEq, PartialOrd)]
256#[cfg_attr(feature = "defmt", derive(defmt::Format))]
257pub struct EspWifiController<'d> {
258    _inner: PhantomData<&'d ()>,
259}
260
261impl EspWifiController<'_> {
262    pub fn wifi(&self) -> bool {
264        crate::flags::WIFI.load(Ordering::Acquire) > 0
265    }
266
267    pub fn ble(&self) -> bool {
269        crate::flags::BLE.load(Ordering::Acquire)
270    }
271
272    pub fn deinit(self) -> Result<(), InitializationError> {
274        if crate::flags::ESP_WIFI_INITIALIZED.load(Ordering::Acquire) {
275            unsafe { deinit_unchecked() }
277        } else {
278            Ok(())
279        }
280    }
281
282    pub(crate) unsafe fn conjure() -> Self {
283        Self {
284            _inner: PhantomData,
285        }
286    }
287}
288
289impl Drop for EspWifiController<'_> {
290    fn drop(&mut self) {
291        if crate::flags::ESP_WIFI_INITIALIZED.load(Ordering::Acquire) {
292            unsafe { deinit_unchecked().ok() };
294        }
295    }
296}
297
298pub trait EspWifiTimerSource: private::Sealed {
303    fn timer(self) -> TimeBase;
305}
306
307trait IntoAnyTimer: Into<AnyTimer> {}
312
313impl IntoAnyTimer for TimgTimer where Self: Into<AnyTimer> {}
314
315#[cfg(not(feature = "esp32"))]
316impl IntoAnyTimer for Alarm where Self: Into<AnyTimer> {}
317
318impl private::Sealed for AnyTimer {}
319impl IntoAnyTimer for AnyTimer {}
320
321impl<T> EspWifiTimerSource for T
322where
323    T: IntoAnyTimer + private::Sealed,
324{
325    fn timer(self) -> TimeBase {
326        TimeBase::new(self.into()).timer()
327    }
328}
329
330impl EspWifiTimerSource for TimeBase {
331    fn timer(self) -> TimeBase {
332        self
333    }
334}
335
336impl private::Sealed for TimeBase {}
337impl private::Sealed for TimgTimer where Self: Into<AnyTimer> {}
338#[cfg(not(feature = "esp32"))]
339impl private::Sealed for Alarm where Self: Into<AnyTimer> {}
340
341pub trait EspWifiRngSource: rand_core::RngCore + private::Sealed {}
343
344impl EspWifiRngSource for Rng {}
345impl private::Sealed for Rng {}
346impl EspWifiRngSource for Trng<'_> {}
347impl private::Sealed for Trng<'_> {}
348
349#[doc = esp_hal::before_snippet!()]
365pub fn init<'d, T: EspWifiTimerSource, R: EspWifiRngSource>(
377    timer: impl Peripheral<P = T> + 'd,
378    _rng: impl Peripheral<P = R> + 'd,
379    _radio_clocks: impl Peripheral<P = RADIO_CLK> + 'd,
380) -> Result<EspWifiController<'d>, InitializationError> {
381    const MIN_CLOCK: Rate = Rate::from_mhz(80);
383    let clocks = Clocks::get();
384    if clocks.cpu_clock < MIN_CLOCK {
385        return Err(InitializationError::WrongClockConfig);
386    }
387
388    info!("esp-wifi configuration {:?}", crate::CONFIG);
389    crate::common_adapter::chip_specific::enable_wifi_power_domain();
390    phy_mem_init();
391
392    setup_radio_isr();
393
394    #[cfg(feature = "builtin-scheduler")]
396    preempt_builtin::setup_timer(unsafe { timer.clone_unchecked() }.timer());
397
398    preempt::enable();
400
401    init_tasks();
402    yield_task();
403
404    wifi_set_log_verbose();
405    init_clocks();
406
407    #[cfg(coex)]
408    match crate::wifi::coex_initialize() {
409        0 => {}
410        error => return Err(InitializationError::General(error)),
411    }
412
413    crate::flags::ESP_WIFI_INITIALIZED.store(true, Ordering::Release);
414
415    #[cfg(not(feature = "builtin-scheduler"))]
416    let _ = timer; Ok(EspWifiController {
419        _inner: PhantomData,
420    })
421}
422
423pub unsafe fn deinit_unchecked() -> Result<(), InitializationError> {
433    #[cfg(coex)]
435    {
436        unsafe { crate::wifi::os_adapter::coex_disable() };
437        unsafe { crate::wifi::os_adapter::coex_deinit() };
438    }
439
440    let controller = unsafe { EspWifiController::conjure() };
441
442    if controller.wifi() {
446        #[cfg(feature = "wifi")]
447        crate::wifi::wifi_deinit()?;
448        crate::flags::WIFI.store(0, Ordering::Release);
449    }
450
451    if controller.ble() {
452        #[cfg(feature = "ble")]
453        crate::ble::ble_deinit();
454        crate::flags::BLE.store(false, Ordering::Release);
455    }
456
457    shutdown_radio_isr();
458
459    #[cfg(feature = "builtin-scheduler")]
460    preempt_builtin::disable_timer();
461
462    preempt::disable();
464
465    crate::flags::ESP_WIFI_INITIALIZED.store(false, Ordering::Release);
466
467    Ok(())
468}
469
470pub(crate) mod private {
471    pub trait Sealed {}
472}
473
474#[derive(Debug, Clone, Copy)]
475#[cfg_attr(feature = "defmt", derive(defmt::Format))]
476pub enum InitializationError {
478    General(i32),
479    #[cfg(feature = "wifi")]
480    WifiError(WifiError),
481    WrongClockConfig,
482}
483
484#[cfg(feature = "wifi")]
485impl From<WifiError> for InitializationError {
486    fn from(value: WifiError) -> Self {
487        InitializationError::WifiError(value)
488    }
489}
490
491pub fn wifi_set_log_verbose() {
494    #[cfg(all(feature = "sys-logs", not(esp32h2)))]
495    unsafe {
496        use crate::binary::include::{
497            esp_wifi_internal_set_log_level,
498            wifi_log_level_t_WIFI_LOG_VERBOSE,
499        };
500
501        esp_wifi_internal_set_log_level(wifi_log_level_t_WIFI_LOG_VERBOSE);
502    }
503}
504
505fn init_clocks() {
506    let radio_clocks = unsafe { RADIO_CLK::steal() };
507    RadioClockController::new(radio_clocks).init_clocks();
508}