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-radio</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**")]
9#![cfg_attr(esp32s2, doc = "**ESP32-S2**")]
10#![cfg_attr(esp32s3, doc = "**ESP32-S3**")]
11#![cfg_attr(esp32c2, doc = "**ESP32-C2**")]
12#![cfg_attr(esp32c3, doc = "**ESP32-C3**")]
13#![cfg_attr(esp32c6, doc = "**ESP32-C6**")]
14#![cfg_attr(esp32h2, doc = "**ESP32-H2**")]
15#![doc = concat!(r#"features = [""#, chip!(), r#"", "wifi", "esp-now", "esp-alloc"]"#)]
36#![doc = concat!(r#"features = [""#, chip!(), r#"", "esp-radio", "esp-alloc"]"#)]
38#![doc = concat!(r#"features = [""#, chip!(), r#""]"#)]
40# and `ESP_PHY_CONFIG_PHY_ENABLE_USB` is enabled by default."
70)]
71#![cfg_attr(
76 multi_core,
77 doc = concat!(
78 "### Running on the Second Core",
79 "\n\n",
80 "BLE and Wi-Fi can also be run on the second core.",
81 "\n\n",
82 "`esp_radio::init` is recommended to be called on the first core. The tasks ",
83 "created by `esp-radio` are pinned to the first core.",
84 "\n\n",
85 "It's also important to allocate adequate stack for the second core; in many ",
86 "cases 8kB is not enough, and 16kB or more may be required depending on your ",
87 "use case. Failing to allocate adequate stack may result in strange behaviour, ",
88 "such as your application silently failing at some point during execution."
89 )
90)]
91#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
101#![doc = ""]
108#![doc = include_str!(concat!(env!("OUT_DIR"), "/esp_radio_config_table.md"))]
109#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
110#![no_std]
111#![cfg_attr(xtensa, feature(asm_experimental_arch))]
112#![cfg_attr(feature = "sys-logs", feature(c_variadic))]
113#![deny(rust_2018_idioms, rustdoc::all)]
114#![allow(rustdoc::bare_urls)]
115#![allow(unknown_lints)]
118#![allow(non_local_definitions)]
119#![cfg_attr(
120 not(any(feature = "wifi", feature = "ble")),
121 allow(
122 unused,
123 reason = "There are a number of places where code is needed for either wifi or ble,
124 and cfg-ing them out would make the code less readable just to avoid warnings in the
125 less common case. Truly unused code will be flagged by the check that enables either
126 ble or wifi."
127 )
128)]
129#![cfg_attr(docsrs, feature(doc_cfg, custom_inner_attributes, proc_macro_hygiene))]
130
131#[macro_use]
132extern crate esp_metadata_generated;
133
134extern crate alloc;
135
136mod fmt;
138
139use core::marker::PhantomData;
140
141pub use common_adapter::{phy_calibration_data, set_phy_calibration_data};
142use esp_hal::{self as hal};
143use esp_radio_rtos_driver as preempt;
144use esp_sync::RawMutex;
145#[cfg(esp32)]
146use hal::analog::adc::{release_adc2, try_claim_adc2};
147use hal::{
148 clock::{Clocks, init_radio_clocks},
149 time::Rate,
150};
151
152use crate::radio::{setup_radio_isr, shutdown_radio_isr};
153#[cfg(feature = "wifi")]
154use crate::wifi::WifiError;
155
156#[doc(hidden)]
158macro_rules! unstable_module {
159 ($(
160 $(#[$meta:meta])*
161 pub mod $module:ident;
162 )*) => {
163 $(
164 $(#[$meta])*
165 #[cfg(feature = "unstable")]
166 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
167 pub mod $module;
168
169 $(#[$meta])*
170 #[cfg(not(feature = "unstable"))]
171 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
172 #[allow(unused)]
173 pub(crate) mod $module;
174 )*
175 };
176}
177
178mod binary {
179 pub use esp_wifi_sys::*;
180}
181mod compat;
182
183mod radio;
184mod time;
185
186#[cfg(feature = "wifi")]
187pub mod wifi;
188
189unstable_module! {
190 #[cfg(feature = "esp-now")]
191 pub mod esp_now;
192 #[cfg(feature = "ble")]
193 pub mod ble;
194 #[cfg(feature = "ieee802154")]
195 pub mod ieee802154;
196}
197
198pub(crate) mod common_adapter;
199pub(crate) mod memory_fence;
200
201pub(crate) static ESP_RADIO_LOCK: RawMutex = RawMutex::new();
202
203#[allow(clippy::assertions_on_constants)] const _: () = {
206 cfg_if::cfg_if! {
207 if #[cfg(not(esp32h2))] {
208 core::assert!(binary::include::CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM == 10);
209 core::assert!(binary::include::CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM == 32);
210 core::assert!(binary::include::WIFI_STATIC_TX_BUFFER_NUM == 0);
211 core::assert!(binary::include::CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM == 32);
212 core::assert!(binary::include::CONFIG_ESP_WIFI_AMPDU_RX_ENABLED == 1);
213 core::assert!(binary::include::CONFIG_ESP_WIFI_AMPDU_TX_ENABLED == 1);
214 core::assert!(binary::include::WIFI_AMSDU_TX_ENABLED == 0);
215 core::assert!(binary::include::CONFIG_ESP32_WIFI_RX_BA_WIN == 6);
216 }
217 };
218};
219
220#[derive(Debug, PartialEq, PartialOrd)]
221#[cfg_attr(feature = "defmt", derive(defmt::Format))]
222pub struct Controller<'d> {
224 _inner: PhantomData<&'d ()>,
225}
226
227impl Drop for Controller<'_> {
228 fn drop(&mut self) {
229 #[cfg(coex)]
231 {
232 unsafe { crate::wifi::os_adapter::coex_disable() };
233 unsafe { crate::wifi::os_adapter::coex_deinit() };
234 }
235
236 shutdown_radio_isr();
237
238 #[cfg(esp32)]
239 release_adc2(unsafe { esp_hal::Internal::conjure() });
241 }
242}
243
244#[cfg_attr(
260 esp32,
261 doc = " - The function may return an error if ADC2 is already in use."
262)]
263#[doc = esp_hal::before_snippet!()]
274#[cfg_attr(
278 riscv,
279 doc = " let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);"
280)]
281#[cfg_attr(riscv, doc = " esp_rtos::start(timg0.timer0, software_interrupt);")]
282#[cfg_attr(xtensa, doc = " esp_rtos::start(timg0.timer0);")]
283pub fn init<'d>() -> Result<Controller<'d>, InitializationError> {
288 #[cfg(esp32)]
289 if try_claim_adc2(unsafe { hal::Internal::conjure() }).is_err() {
290 return Err(InitializationError::Adc2IsUsed);
291 }
292
293 if crate::is_interrupts_disabled() {
294 return Err(InitializationError::InterruptsDisabled);
295 }
296
297 if !preempt::initialized() {
298 return Err(InitializationError::SchedulerNotInitialized);
299 }
300
301 const MIN_CLOCK: Rate = Rate::from_mhz(80);
303 let clocks = Clocks::get();
304 if clocks.cpu_clock < MIN_CLOCK {
305 return Err(InitializationError::WrongClockConfig);
306 }
307
308 crate::common_adapter::enable_wifi_power_domain();
309
310 setup_radio_isr();
311
312 wifi_set_log_verbose();
313 init_radio_clocks();
314
315 #[cfg(coex)]
316 match crate::wifi::coex_initialize() {
317 0 => {}
318 error => return Err(InitializationError::General(error)),
319 }
320
321 Ok(Controller {
322 _inner: PhantomData,
323 })
324}
325
326fn is_interrupts_disabled() -> bool {
328 #[cfg(target_arch = "xtensa")]
329 return hal::xtensa_lx::interrupt::get_level() != 0
330 || hal::xtensa_lx::interrupt::get_mask() == 0;
331
332 #[cfg(target_arch = "riscv32")]
333 return !hal::riscv::register::mstatus::read().mie()
334 || hal::interrupt::current_runlevel() >= hal::interrupt::Priority::Priority1;
335}
336
337#[derive(Debug, Clone, Copy)]
338#[cfg_attr(feature = "defmt", derive(defmt::Format))]
339#[non_exhaustive]
341pub enum InitializationError {
342 General(i32),
345 #[cfg(feature = "wifi")]
347 WifiError(WifiError),
348 WrongClockConfig,
350 InterruptsDisabled,
353 SchedulerNotInitialized,
355 #[cfg(esp32)]
356 Adc2IsUsed,
358}
359
360impl core::error::Error for InitializationError {}
361
362impl core::fmt::Display for InitializationError {
363 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
364 match self {
365 InitializationError::General(e) => write!(f, "A general error {e} occurred"),
366 #[cfg(feature = "wifi")]
367 InitializationError::WifiError(e) => {
368 write!(f, "Wi-Fi driver related error occured: {e}")
369 }
370 InitializationError::WrongClockConfig => {
371 write!(f, "The current CPU clock frequency is too low")
372 }
373 InitializationError::InterruptsDisabled => write!(
374 f,
375 "Attempted to initialize while interrupts are disabled (Unsupported)"
376 ),
377 InitializationError::SchedulerNotInitialized => {
378 write!(f, "The scheduler is not initialized")
379 }
380 #[cfg(esp32)]
381 InitializationError::Adc2IsUsed => write!(
382 f,
383 "ADC2 cannot be used with `radio` functionality on `esp32`"
384 ),
385 }
386 }
387}
388
389#[cfg(feature = "wifi")]
390impl From<WifiError> for InitializationError {
391 fn from(value: WifiError) -> Self {
392 InitializationError::WifiError(value)
393 }
394}
395
396#[instability::unstable]
399pub fn wifi_set_log_verbose() {
400 #[cfg(all(feature = "sys-logs", not(esp32h2)))]
401 unsafe {
402 use crate::binary::include::{
403 esp_wifi_internal_set_log_level,
404 wifi_log_level_t_WIFI_LOG_VERBOSE,
405 };
406
407 esp_wifi_internal_set_log_level(wifi_log_level_t_WIFI_LOG_VERBOSE);
408 }
409}