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.espressif.com/projects/rust/'>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 = [""#, 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(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
83#![no_std]
84#![cfg_attr(xtensa, feature(asm_experimental_arch))]
85#![cfg_attr(feature = "sys-logs", feature(c_variadic))]
86#![deny(rust_2018_idioms, rustdoc::all)]
87#![allow(rustdoc::bare_urls)]
88#![allow(unknown_lints)]
91#![allow(non_local_definitions)]
92#![cfg_attr(
93 not(any(feature = "wifi", feature = "ble")),
94 allow(
95 unused,
96 reason = "There are a number of places where code is needed for either wifi or ble,
97 and cfg-ing them out would make the code less readable just to avoid warnings in the
98 less common case. Truly unused code will be flagged by the check that enables either
99 ble or wifi."
100 )
101)]
102
103#[macro_use]
104extern crate esp_metadata_generated;
105
106extern crate alloc;
107
108mod fmt;
110
111use core::marker::PhantomData;
112
113use common_adapter::chip_specific::phy_mem_init;
114use esp_config::*;
115use esp_hal as hal;
116use hal::{
117 Blocking,
118 clock::{Clocks, init_radio_clocks},
119 rng::{Rng, Trng},
120 time::Rate,
121 timer::{AnyTimer, PeriodicTimer, timg::Timer as TimgTimer},
122};
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
233type TimeBase = PeriodicTimer<'static, Blocking>;
234
235#[derive(Debug, PartialEq, PartialOrd)]
236#[cfg_attr(feature = "defmt", derive(defmt::Format))]
237pub struct EspWifiController<'d> {
238 _inner: PhantomData<&'d ()>,
239}
240
241impl Drop for EspWifiController<'_> {
242 fn drop(&mut self) {
243 #[cfg(coex)]
245 {
246 unsafe { crate::wifi::os_adapter::coex_disable() };
247 unsafe { crate::wifi::os_adapter::coex_deinit() };
248 }
249
250 shutdown_radio_isr();
251
252 preempt::disable();
254 }
255}
256
257pub trait EspWifiTimerSource: private::Sealed {
261 unsafe fn timer(self) -> TimeBase;
267}
268
269impl private::Sealed for TimeBase {}
270impl private::Sealed for AnyTimer<'_> {}
271impl private::Sealed for TimgTimer<'_> {}
272#[cfg(systimer)]
273impl private::Sealed for esp_hal::timer::systimer::Alarm<'_> {}
274
275impl<T> EspWifiTimerSource for T
276where
277 T: esp_hal::timer::any::Degrade + private::Sealed,
278{
279 unsafe fn timer(self) -> TimeBase {
280 let any_timer: AnyTimer<'_> = self.degrade();
281 let any_timer: AnyTimer<'static> = unsafe {
282 core::mem::transmute(any_timer)
288 };
289 TimeBase::new(any_timer)
290 }
291}
292
293pub trait EspWifiRngSource: rand_core::RngCore + private::Sealed {}
295
296impl EspWifiRngSource for Rng {}
297impl private::Sealed for Rng {}
298impl EspWifiRngSource for Trng<'_> {}
299impl private::Sealed for Trng<'_> {}
300
301#[doc = esp_hal::before_snippet!()]
318pub fn init<'d>(
330 timer: impl EspWifiTimerSource + 'd,
331 _rng: impl EspWifiRngSource + 'd,
332) -> Result<EspWifiController<'d>, InitializationError> {
333 if crate::is_interrupts_disabled() {
334 return Err(InitializationError::InterruptsDisabled);
335 }
336
337 const MIN_CLOCK: Rate = Rate::from_mhz(80);
339 let clocks = Clocks::get();
340 if clocks.cpu_clock < MIN_CLOCK {
341 return Err(InitializationError::WrongClockConfig);
342 }
343
344 info!("esp-wifi configuration {:?}", crate::CONFIG);
345 crate::common_adapter::chip_specific::enable_wifi_power_domain();
346 phy_mem_init();
347
348 setup_radio_isr();
349
350 #[cfg(feature = "builtin-scheduler")]
352 preempt_builtin::setup_timer(unsafe { timer.timer() });
353
354 #[cfg(not(feature = "builtin-scheduler"))]
355 let _ = timer; preempt::enable();
359
360 init_tasks();
361 yield_task();
362
363 wifi_set_log_verbose();
364 init_radio_clocks();
365
366 #[cfg(coex)]
367 match crate::wifi::coex_initialize() {
368 0 => {}
369 error => return Err(InitializationError::General(error)),
370 }
371
372 Ok(EspWifiController {
373 _inner: PhantomData,
374 })
375}
376
377fn is_interrupts_disabled() -> bool {
379 #[cfg(target_arch = "xtensa")]
380 return hal::xtensa_lx::interrupt::get_level() != 0
381 || hal::xtensa_lx::interrupt::get_mask() == 0;
382
383 #[cfg(target_arch = "riscv32")]
384 return !hal::riscv::register::mstatus::read().mie()
385 || hal::interrupt::current_runlevel() >= hal::interrupt::Priority::Priority1;
386}
387
388pub(crate) mod private {
389 pub trait Sealed {}
390}
391
392#[derive(Debug, Clone, Copy)]
393#[cfg_attr(feature = "defmt", derive(defmt::Format))]
394#[non_exhaustive]
396pub enum InitializationError {
397 General(i32),
400 #[cfg(feature = "wifi")]
402 WifiError(WifiError),
403 WrongClockConfig,
405 InterruptsDisabled,
408}
409
410#[cfg(feature = "wifi")]
411impl From<WifiError> for InitializationError {
412 fn from(value: WifiError) -> Self {
413 InitializationError::WifiError(value)
414 }
415}
416
417pub fn wifi_set_log_verbose() {
420 #[cfg(all(feature = "sys-logs", not(esp32h2)))]
421 unsafe {
422 use crate::binary::include::{
423 esp_wifi_internal_set_log_level,
424 wifi_log_level_t_WIFI_LOG_VERBOSE,
425 };
426
427 esp_wifi_internal_set_log_level(wifi_log_level_t_WIFI_LOG_VERBOSE);
428 }
429}