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}