diff --git a/esp-radio/CHANGELOG.md b/esp-radio/CHANGELOG.md index 2e3814609..a32ea68d9 100644 --- a/esp-radio/CHANGELOG.md +++ b/esp-radio/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `BuilderLite` pattern to `AccessPointConfig`, `ClientConfig`, and `EapClientConfig` (#4017, #4115) - lifetime to `Sniffer` (#4017) - `dtim_period` parameter for `PowerSaveMode` (#4040) +- `WifiConfig`, `CountryInfo` and `OperatingClass` (#4121) ### Changed @@ -43,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The memory allocation functions expected by `esp_radio` have been renamed and extended (#3890, #4043) - Updated radio related drivers to ESP-IDF 5.5.1 (#4113) - Event handlers are now passed the event by reference (#4113) +- Some build-time configuration options have been replaced by runtime options in `WifiConfig` (#4121) ### Fixed diff --git a/esp-radio/MIGRATING-0.15.0.md b/esp-radio/MIGRATING-0.15.0.md index 34f247fb0..984294e6e 100644 --- a/esp-radio/MIGRATING-0.15.0.md +++ b/esp-radio/MIGRATING-0.15.0.md @@ -2,12 +2,12 @@ ## Initialization -The `builtin-scheduler` feature has been removed. The functionality has been moved to `esp_preempt_baremetal`. -`esp_preempt_baremetal` needs to be initialized before calling `esp_radio::init`. Failure to do so will result in an error. +The `builtin-scheduler` feature has been removed. The functionality has been moved to `esp_preempt`. +`esp_preempt` needs to be initialized before calling `esp_radio::init`. Failure to do so will result in an error. Depending on your chosen OS, you may need to use other `esp_preempt` implementations. -Furthermore, `esp_wifi::init` no longer requires `RNG` or a timer. +Furthermore, `esp_radio::init` no longer requires `RNG` or a timer. ```diff -let esp_wifi_ctrl = esp_wifi::init(timg0.timer0, Rng::new()).unwrap(); @@ -57,6 +57,20 @@ The `scan_with_config_sync_max`, `scan_with_config_sync_max`, `scan_n`, and `sca ## Configuration +`esp_radio::wifi::new` now takes a `WifiConfig` struct. The default configuration matches the previous build-time configuration defaults. The corresponding build-time configuration options have been removed. + +```diff + # .cargo/config.toml: + [env] +-ESP_RADIO_CONFIG_RX_QUEUE_SIZE = 27 + + # main.rs +-let (controller, ifaces) = esp_wifi::wifi::new(&ctrl, p.WIFI).unwrap(); ++let config = esp_radio::wifi::WifiConfig::default() ++ .with_rx_queue_size(27); ++let (controller, ifaces) = esp_radio::wifi::new(&ctrl, p.WIFI, config).unwrap(); +``` + The `Configuration`, `ClientConfiguration`, `AccessPointConfiguration`, and `EapClientConfiguration` enums have been renamed to `Config`, `ClientConfig`, `AccessPointConfig`, and `EapClientConfig`: ```diff diff --git a/esp-radio/esp_config.yml b/esp-radio/esp_config.yml index 08a8e52e2..27c51994d 100644 --- a/esp-radio/esp_config.yml +++ b/esp-radio/esp_config.yml @@ -1,142 +1,13 @@ crate: esp-radio options: - - name: rx_queue_size - description: Size of the RX queue in frames - default: - - value: 5 - constraints: - - type: - validator: positive_integer - - - name: tx_queue_size - description: Size of the TX queue in frames - default: - - value: 3 - constraints: - - type: - validator: positive_integer - - - name: static_rx_buf_num - description: "Max number of WiFi static RX buffers.
- Each buffer takes approximately 1.6KB of RAM. - The static rx buffers are allocated when esp_wifi_init is called, they are not freed - until esp_wifi_deinit is called.
- WiFi hardware use these buffers to receive all 802.11 frames. - A higher number may allow higher throughput but increases memory use. If ESP_WIFI_AMPDU_RX_ENABLED - is enabled, this value is recommended to set equal or bigger than ESP_WIFI_RX_BA_WIN in order to - achieve better throughput and compatibility with both stations and APs." - default: - - value: 10 - constraints: - - type: - validator: integer_in_range - value: - start: 0 - end: 129 - - - name: dynamic_rx_buf_num - description: "Max number of WiFi dynamic RX buffers
- Set the number of WiFi dynamic RX buffers, 0 means unlimited RX buffers will be allocated - (provided sufficient free RAM). The size of each dynamic RX buffer depends on the size of - the received data frame.
- For each received data frame, the WiFi driver makes a copy to an RX buffer and then delivers - it to the high layer TCP/IP stack. The dynamic RX buffer is freed after the higher layer has - successfully received the data frame.
- For some applications, WiFi data frames may be received faster than the application can - process them. In these cases we may run out of memory if RX buffer number is unlimited (0).
- If a dynamic RX buffer limit is set, it should be at least the number of static RX buffers." - default: - - value: 32 - constraints: - - type: - validator: integer_in_range - value: - start: 0 - end: 1025 - - - name: static_tx_buf_num - description: "Set the number of WiFi static TX buffers. Each buffer takes approximately 1.6KB of RAM. - The static RX buffers are allocated when esp_wifi_init() is called, they are not released - until esp_wifi_deinit() is called.
- For each transmitted data frame from the higher layer TCP/IP stack, the WiFi driver makes a - copy of it in a TX buffer. For some applications especially UDP applications, the upper - layer can deliver frames faster than WiFi layer can transmit. In these cases, we may run out - of TX buffers." - default: - - value: 0 - constraints: - - type: - validator: integer_in_range - value: - start: 0 - end: 65 - - - name: dynamic_tx_buf_num - description: "Set the number of WiFi dynamic TX buffers. The size of each dynamic TX buffer is not fixed, - it depends on the size of each transmitted data frame.
- For each transmitted frame from the higher layer TCP/IP stack, the WiFi driver makes a copy - of it in a TX buffer. For some applications, especially UDP applications, the upper layer - can deliver frames faster than WiFi layer can transmit. In these cases, we may run out of TX - buffers." - default: - - value: 32 - constraints: - - type: - validator: integer_in_range - value: - start: 0 - end: 129 - - - name: ampdu_rx_enable - description: "Select this option to enable AMPDU RX feature" - default: - - value: true - - - name: ampdu_tx_enable - description: "Select this option to enable AMPDU TX feature" - default: - - value: true - - - name: amsdu_tx_enable - description: "Select this option to enable AMSDU TX feature. (If ESP_WIFI_CONFIG_CACHE_TX_BUFFER_NUM >= 2)" - default: - - value: false - - - name: rx_ba_win - description: "Set the size of WiFi Block Ack RX window. Generally a bigger value means higher throughput and better - compatibility but more memory. Most of time we should NOT change the default value unless special - reason, e.g. test the maximum UDP RX throughput with iperf etc. For iperf test in shieldbox, the - recommended value is 9~12. If PSRAM is used and WiFi memory is preferred to allocate in PSRAM first, - the default and minimum value should be 16 to achieve better throughput and compatibility with both - stations and APs." - default: - - value: 6 - constraints: - - type: - validator: integer_in_range - value: - start: 2 - end: 65 - - - name: max_burst_size + # TODO these could also be runtime configurable + - name: wifi_max_burst_size description: See [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_burst_size) default: - value: 1 - - name: country_code - description: "Country code. See - [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code)" - default: - - value: '"CN"' - - - name: country_code_operating_class - description: 'If not 0: Operating Class table number. See - [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code)' - default: - - value: 0 - - - name: mtu + - name: wifi_mtu description: "MTU, see [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_transmission_unit)" default: - value: 1492 @@ -144,6 +15,7 @@ options: - type: validator: positive_integer + # TODO Should be part of ClientConfig - name: listen_interval description: 'Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. @@ -152,6 +24,7 @@ options: default: - value: 3 + # TODO Should be part of ClientConfig - name: beacon_timeout description: 'For Station, If the station does not receive a beacon frame from the connected SoftAP during the inactive time, disconnect from SoftAP. @@ -165,12 +38,14 @@ options: start: 6 end: 31 + # TODO Should be part of ApConfig - name: ap_beacon_timeout description: "For SoftAP, If the SoftAP doesn't receive any data from the connected STA during inactive time, the SoftAP will force deauth the STA. Default is 300s" default: - value: 300 + # TODO Should be part of ClientConfig - name: failure_retry_cnt description: "Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config. @@ -182,6 +57,7 @@ options: - type: validator: positive_integer + # TODO: this is a scan API argument - name: scan_method description: "0 = WIFI_FAST_SCAN, 1 = WIFI_ALL_CHANNEL_SCAN, defaults to 0" default: @@ -198,6 +74,7 @@ options: default: - value: false + # TODO: Should these be esp_radio::init config options? - name: phy_enable_usb description: "Keeps USB running when using WiFi. This allows debugging and log messages via USB Serial JTAG. @@ -215,6 +92,7 @@ options: default: - value: true + # TODO: This could be a config option for Ieee802154::new - name: ieee802154_rx_queue_size description: Size of the RX queue in frames default: @@ -222,7 +100,3 @@ options: constraints: - type: validator: positive_integer - -checks: - - 'ESP_RADIO_CONFIG_RX_BA_WIN < ESP_RADIO_CONFIG_DYNAMIC_RX_BUF_NUM' - - 'ESP_RADIO_CONFIG_RX_BA_WIN < (ESP_RADIO_CONFIG_STATIC_RX_BUF_NUM * 2)' diff --git a/esp-radio/src/wifi/internal.rs b/esp-radio/src/wifi/internal.rs index 97fb61893..beba05f60 100644 --- a/esp-radio/src/wifi/internal.rs +++ b/esp-radio/src/wifi/internal.rs @@ -1,11 +1,8 @@ -use esp_config::{esp_config_bool, esp_config_int}; use esp_wifi_sys::include::{ ESP_WIFI_OS_ADAPTER_MAGIC, ESP_WIFI_OS_ADAPTER_VERSION, - WIFI_INIT_CONFIG_MAGIC, wifi_init_config_t, wifi_osi_funcs_t, - wpa_crypto_funcs_t, }; use super::os_adapter::{self, *}; @@ -88,7 +85,7 @@ unsafe extern "C" fn is_in_isr_wrapper() -> i32 { } #[unsafe(no_mangle)] -static __ESP_RADIO_G_WIFI_OSI_FUNCS: wifi_osi_funcs_t = wifi_osi_funcs_t { +pub(crate) static __ESP_RADIO_G_WIFI_OSI_FUNCS: wifi_osi_funcs_t = wifi_osi_funcs_t { _version: ESP_WIFI_OS_ADAPTER_VERSION as i32, _env_is_chip: Some(env_is_chip), _set_intr: Some(set_intr), @@ -246,46 +243,4 @@ const WIFI_FEATURE_CAPS: u64 = WIFI_ENABLE_WPA3_SAE | WIFI_ENABLE_ENTERPRISE; #[unsafe(no_mangle)] pub(super) static mut __ESP_RADIO_G_WIFI_FEATURE_CAPS: u64 = WIFI_FEATURE_CAPS; -pub(super) static mut G_CONFIG: wifi_init_config_t = wifi_init_config_t { - osi_funcs: core::ptr::addr_of!(__ESP_RADIO_G_WIFI_OSI_FUNCS).cast_mut(), - - // dummy for now - populated in init - wpa_crypto_funcs: wpa_crypto_funcs_t { - size: 0, - version: 1, - hmac_sha256_vector: None, - pbkdf2_sha1: None, - aes_128_encrypt: None, - aes_128_decrypt: None, - omac1_aes_128: None, - ccmp_decrypt: None, - ccmp_encrypt: None, - aes_gmac: None, - sha256_vector: None, - }, - static_rx_buf_num: esp_config_int!(i32, "ESP_RADIO_CONFIG_RX_QUEUE_SIZE"), - dynamic_rx_buf_num: esp_config_int!(i32, "ESP_RADIO_CONFIG_DYNAMIC_RX_BUF_NUM"), - tx_buf_type: esp_wifi_sys::include::CONFIG_ESP_WIFI_TX_BUFFER_TYPE as i32, - static_tx_buf_num: esp_config_int!(i32, "ESP_RADIO_CONFIG_STATIC_TX_BUF_NUM"), - dynamic_tx_buf_num: esp_config_int!(i32, "ESP_RADIO_CONFIG_DYNAMIC_TX_BUF_NUM"), - rx_mgmt_buf_type: esp_wifi_sys::include::CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF as i32, - rx_mgmt_buf_num: esp_wifi_sys::include::CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF as i32, - cache_tx_buf_num: esp_wifi_sys::include::WIFI_CACHE_TX_BUFFER_NUM as i32, - csi_enable: cfg!(feature = "csi") as i32, - ampdu_rx_enable: esp_config_bool!("ESP_RADIO_CONFIG_AMPDU_RX_ENABLE") as i32, - ampdu_tx_enable: esp_config_bool!("ESP_RADIO_CONFIG_AMPDU_TX_ENABLE") as i32, - amsdu_tx_enable: esp_config_bool!("ESP_RADIO_CONFIG_AMSDU_TX_ENABLE") as i32, - nvs_enable: 0, - nano_enable: 0, - rx_ba_win: esp_config_int!(i32, "ESP_RADIO_CONFIG_RX_BA_WIN"), - wifi_task_core_id: 0, - beacon_max_len: esp_wifi_sys::include::WIFI_SOFTAP_BEACON_MAX_LEN as i32, - mgmt_sbuf_num: esp_wifi_sys::include::WIFI_MGMT_SBUF_NUM as i32, - feature_caps: WIFI_FEATURE_CAPS, - sta_disconnected_pm: false, - espnow_max_encrypt_num: esp_wifi_sys::include::CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM as i32, - tx_hetb_queue_num: 3, - dump_hesigb_enable: false, - - magic: WIFI_INIT_CONFIG_MAGIC as i32, -}; +pub(super) static mut G_CONFIG: wifi_init_config_t = unsafe { core::mem::zeroed() }; diff --git a/esp-radio/src/wifi/mod.rs b/esp-radio/src/wifi/mod.rs index 67755b6b2..2498b7b2a 100644 --- a/esp-radio/src/wifi/mod.rs +++ b/esp-radio/src/wifi/mod.rs @@ -17,12 +17,13 @@ use core::{ }; use enumset::{EnumSet, EnumSetType}; -use esp_config::{esp_config_int, esp_config_str}; -use esp_hal::asynch::AtomicWaker; +use esp_config::esp_config_int; +use esp_hal::{asynch::AtomicWaker, system::Cpu}; use esp_sync::NonReentrantMutex; #[cfg(all(any(feature = "sniffer", feature = "esp-now"), feature = "unstable"))] use esp_wifi_sys::include::wifi_pkt_rx_ctrl_t; use esp_wifi_sys::include::{ + WIFI_INIT_CONFIG_MAGIC, WIFI_PROTOCOL_11AX, WIFI_PROTOCOL_11B, WIFI_PROTOCOL_11G, @@ -30,6 +31,7 @@ use esp_wifi_sys::include::{ WIFI_PROTOCOL_LR, esp_wifi_connect_internal, esp_wifi_disconnect_internal, + wifi_init_config_t, wifi_scan_channel_bitmap_t, }; #[cfg(feature = "wifi-eap")] @@ -82,7 +84,7 @@ use crate::{ wifi::private::PacketBuffer, }; -const MTU: usize = esp_config_int!(usize, "ESP_RADIO_CONFIG_MTU"); +const MTU: usize = esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MTU"); #[cfg(all(feature = "csi", esp32c6))] use crate::binary::include::wifi_csi_acquire_config_t; @@ -1119,8 +1121,8 @@ impl CsiConfig { } } -const RX_QUEUE_SIZE: usize = esp_config_int!(usize, "ESP_RADIO_CONFIG_RX_QUEUE_SIZE"); -const TX_QUEUE_SIZE: usize = esp_config_int!(usize, "ESP_RADIO_CONFIG_TX_QUEUE_SIZE"); +static RX_QUEUE_SIZE: AtomicUsize = AtomicUsize::new(0); +static TX_QUEUE_SIZE: AtomicUsize = AtomicUsize::new(0); pub(crate) static DATA_QUEUE_RX_AP: NonReentrantMutex> = NonReentrantMutex::new(VecDeque::new()); @@ -1370,9 +1372,6 @@ pub fn sta_mac() -> [u8; 6] { pub(crate) fn wifi_init() -> Result<(), WifiError> { unsafe { - internal::G_CONFIG.wpa_crypto_funcs = g_wifi_default_wpa_crypto_funcs; - internal::G_CONFIG.feature_caps = internal::__ESP_RADIO_G_WIFI_FEATURE_CAPS; - #[cfg(coex)] esp_wifi_result!(coex_init())?; @@ -1456,7 +1455,7 @@ unsafe extern "C" fn recv_cb_sta( // the function will try to trigger a context switch, which will fail if we // are in an interrupt-free context. match DATA_QUEUE_RX_STA.with(|queue| { - if queue.len() < RX_QUEUE_SIZE { + if queue.len() < RX_QUEUE_SIZE.load(Ordering::Relaxed) { queue.push_back(packet); Ok(()) } else { @@ -1487,7 +1486,7 @@ unsafe extern "C" fn recv_cb_ap( // the function will try to trigger a context switch, which will fail if we // are in an interrupt-free context. match DATA_QUEUE_RX_AP.with(|queue| { - if queue.len() < RX_QUEUE_SIZE { + if queue.len() < RX_QUEUE_SIZE.load(Ordering::Relaxed) { queue.push_back(packet); Ok(()) } else { @@ -1763,7 +1762,7 @@ impl WifiDeviceMode { } fn can_send(&self) -> bool { - WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE + WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE.load(Ordering::Relaxed) } fn increase_in_flight_counter(&self) { @@ -2197,10 +2196,14 @@ impl Device for WifiDevice<'_> { fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { let mut caps = DeviceCapabilities::default(); caps.max_transmission_unit = MTU; - caps.max_burst_size = if esp_config_int!(usize, "ESP_RADIO_CONFIG_MAX_BURST_SIZE") == 0 { + caps.max_burst_size = if esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE") == 0 + { None } else { - Some(esp_config_int!(usize, "ESP_RADIO_CONFIG_MAX_BURST_SIZE")) + Some(esp_config_int!( + usize, + "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE" + )) }; caps } @@ -2627,12 +2630,15 @@ pub(crate) mod embassy { fn capabilities(&self) -> Capabilities { let mut caps = Capabilities::default(); caps.max_transmission_unit = MTU; - caps.max_burst_size = if esp_config_int!(usize, "ESP_RADIO_CONFIG_MAX_BURST_SIZE") == 0 - { - None - } else { - Some(esp_config_int!(usize, "ESP_RADIO_CONFIG_MAX_BURST_SIZE")) - }; + caps.max_burst_size = + if esp_config_int!(usize, "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE") == 0 { + None + } else { + Some(esp_config_int!( + usize, + "ESP_RADIO_CONFIG_WIFI_MAX_BURST_SIZE" + )) + }; caps } @@ -2644,7 +2650,8 @@ pub(crate) mod embassy { /// Power saving mode settings for the modem. #[non_exhaustive] -#[derive(Default)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PowerSaveMode { /// No power saving. #[default] @@ -2700,6 +2707,242 @@ pub struct Interfaces<'d> { pub sniffer: Sniffer<'d>, } +/// Wi-Fi operating class. +/// +/// Refer to Annex E of IEEE Std 802.11-2020. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[instability::unstable] +pub enum OperatingClass { + /// The regulations under which the station/AP is operating encompass all environments for the + /// current frequency band in the country. + AllEnvironments, + + /// The regulations under which the station/AP is operating are for an outdoor environment only. + Outdoors, + + /// The regulations under which the station/AP is operating are for an indoor environment only. + Indoors, + + /// The station/AP is operating under a noncountry entity. The first two octets of the + /// noncountry entity is two ASCII ‘XX’ characters. + NonCountryEntity, + + /// Binary representation of the Operating Class table number currently in use. Refer to Annex E + /// of IEEE Std 802.11-2020. + Repr(u8), +} + +impl Default for OperatingClass { + fn default() -> Self { + OperatingClass::Repr(0) // TODO: is this valid? + } +} + +impl OperatingClass { + fn into_code(self) -> u8 { + match self { + OperatingClass::AllEnvironments => b' ', + OperatingClass::Outdoors => b'O', + OperatingClass::Indoors => b'I', + OperatingClass::NonCountryEntity => b'X', + OperatingClass::Repr(code) => code, + } + } +} + +/// Country information. +/// +/// Defaults to China (CN) with Operating Class "0". +/// +/// To create a [`CountryInfo`] instance, use the `from` method first, then set additional +/// properties using the builder methods. +/// +/// ## Example +/// +/// ```rust,no_run +/// use esp_radio::wifi::{CountryInfo, OperatingClass}; +/// +/// let country_info = CountryInfo::from(*b"CN").operating_class(OperatingClass::Indoors); +/// ``` +/// +/// For more information, see the [Wi-Fi Country Code in the ESP-IDF documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code). +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BuilderLite)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct CountryInfo { + /// Country code. + #[builder_lite(skip)] + country: [u8; 2], + + /// Operating class. + #[builder_lite(unstable)] + operating_class: OperatingClass, +} + +impl From<[u8; 2]> for CountryInfo { + fn from(country: [u8; 2]) -> Self { + Self { + country, + operating_class: OperatingClass::default(), + } + } +} + +impl CountryInfo { + fn into_blob(self) -> wifi_country_t { + wifi_country_t { + cc: [ + self.country[0], + self.country[1], + self.operating_class.into_code(), + ], + // TODO: these may be valid defaults, but they should be configurable. + schan: 1, + nchan: 13, + max_tx_power: 20, + policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL, + } + } +} + +/// Wi-Fi configuration. +#[derive(Clone, Copy, BuilderLite, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct WifiConfig { + /// Power save mode. + power_save_mode: PowerSaveMode, + + /// Country code. + #[builder_lite(into)] + #[builder_lite(unstable)] + country_code: CountryInfo, + + /// Size of the RX queue in frames. + #[builder_lite(unstable)] + rx_queue_size: usize, + /// Size of the TX queue in frames. + #[builder_lite(unstable)] + tx_queue_size: usize, + + /// Max number of WiFi static RX buffers. + /// + /// Each buffer takes approximately 1.6KB of RAM. The static rx buffers are allocated when + /// esp_wifi_init is called, they are not freed until esp_wifi_deinit is called. + /// + /// WiFi hardware use these buffers to receive all 802.11 frames. A higher number may allow + /// higher throughput but increases memory use. If [`Self::ampdu_rx_enable`] is enabled, + /// this value is recommended to set equal or bigger than [`Self::rx_ba_win`] in order to + /// achieve better throughput and compatibility with both stations and APs. + #[builder_lite(unstable)] + static_rx_buf_num: u8, + + /// Max number of WiFi dynamic RX buffers + /// + /// Set the number of WiFi dynamic RX buffers, 0 means unlimited RX buffers will be allocated + /// (provided sufficient free RAM). The size of each dynamic RX buffer depends on the size of + /// the received data frame. + /// + /// For each received data frame, the WiFi driver makes a copy to an RX buffer and then + /// delivers it to the high layer TCP/IP stack. The dynamic RX buffer is freed after the + /// higher layer has successfully received the data frame. + /// + /// For some applications, WiFi data frames may be received faster than the application can + /// process them. In these cases we may run out of memory if RX buffer number is unlimited + /// (0). + /// + /// If a dynamic RX buffer limit is set, it should be at least the number of + /// static RX buffers. + #[builder_lite(unstable)] + dynamic_rx_buf_num: u16, + + /// Set the number of WiFi static TX buffers. + /// + /// Each buffer takes approximately 1.6KB of RAM. + /// The static RX buffers are allocated when esp_wifi_init() is called, they are not released + /// until esp_wifi_deinit() is called. + /// + /// For each transmitted data frame from the higher layer TCP/IP stack, the WiFi driver makes a + /// copy of it in a TX buffer. + /// + /// For some applications especially UDP applications, the upper layer can deliver frames + /// faster than WiFi layer can transmit. In these cases, we may run out of TX buffers. + #[builder_lite(unstable)] + static_tx_buf_num: u8, + + /// Set the number of WiFi dynamic TX buffers. + /// + /// The size of each dynamic TX buffer is not fixed, + /// it depends on the size of each transmitted data frame. + /// + /// For each transmitted frame from the higher layer TCP/IP stack, the WiFi driver makes a copy + /// of it in a TX buffer. + /// + /// For some applications, especially UDP applications, the upper layer can deliver frames + /// faster than WiFi layer can transmit. In these cases, we may run out of TX buffers. + #[builder_lite(unstable)] + dynamic_tx_buf_num: u16, + + /// Select this option to enable AMPDU RX feature. + #[builder_lite(unstable)] + ampdu_rx_enable: bool, + + /// Select this option to enable AMPDU TX feature. + #[builder_lite(unstable)] + ampdu_tx_enable: bool, + + /// Select this option to enable AMSDU TX feature. + #[builder_lite(unstable)] + amsdu_tx_enable: bool, + + /// Set the size of WiFi Block Ack RX window. + /// + /// Generally a bigger value means higher throughput and better compatibility but more memory. + /// Most of time we should NOT change the default value unless special reason, e.g. test + /// the maximum UDP RX throughput with iperf etc. For iperf test in shieldbox, the + /// recommended value is 9~12. + /// + /// If PSRAM is used and WiFi memory is preferred to allocate in PSRAM first, the default and + /// minimum value should be 16 to achieve better throughput and compatibility with both + /// stations and APs. + #[builder_lite(unstable)] + rx_ba_win: u8, +} + +impl Default for WifiConfig { + fn default() -> Self { + Self { + power_save_mode: PowerSaveMode::default(), + country_code: CountryInfo::from(*b"CN"), + + rx_queue_size: 5, + tx_queue_size: 3, + + static_rx_buf_num: 10, + dynamic_rx_buf_num: 32, + + static_tx_buf_num: 0, + dynamic_tx_buf_num: 32, + + ampdu_rx_enable: true, + ampdu_tx_enable: true, + amsdu_tx_enable: false, + + rx_ba_win: 6, + } + } +} + +impl WifiConfig { + fn validate(&self) { + if self.rx_ba_win as u16 >= self.dynamic_rx_buf_num { + warn!("RX BA window size should be less than the number of dynamic RX buffers."); + } + if self.rx_ba_win as u16 >= 2 * (self.static_rx_buf_num as u16) { + warn!("RX BA window size should be less than twice the number of static RX buffers."); + } + } +} + /// Create a Wi-Fi controller and it's associated interfaces. /// /// Dropping the controller will deinitialize / stop Wi-Fi. @@ -2709,25 +2952,56 @@ pub struct Interfaces<'d> { pub fn new<'d>( _inited: &'d Controller<'d>, _device: crate::hal::peripherals::WIFI<'d>, + config: WifiConfig, ) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> { if crate::is_interrupts_disabled() { return Err(WifiError::Unsupported); } + config.validate(); + + unsafe { + internal::G_CONFIG = wifi_init_config_t { + osi_funcs: (&raw const internal::__ESP_RADIO_G_WIFI_OSI_FUNCS).cast_mut(), + + wpa_crypto_funcs: g_wifi_default_wpa_crypto_funcs, + static_rx_buf_num: config.static_rx_buf_num as _, + dynamic_rx_buf_num: config.dynamic_rx_buf_num as _, + tx_buf_type: esp_wifi_sys::include::CONFIG_ESP_WIFI_TX_BUFFER_TYPE as i32, + static_tx_buf_num: config.static_tx_buf_num as _, + dynamic_tx_buf_num: config.dynamic_tx_buf_num as _, + rx_mgmt_buf_type: esp_wifi_sys::include::CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF as i32, + rx_mgmt_buf_num: esp_wifi_sys::include::CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF as i32, + cache_tx_buf_num: esp_wifi_sys::include::WIFI_CACHE_TX_BUFFER_NUM as i32, + csi_enable: cfg!(feature = "csi") as i32, + ampdu_rx_enable: config.ampdu_rx_enable as _, + ampdu_tx_enable: config.ampdu_tx_enable as _, + amsdu_tx_enable: config.amsdu_tx_enable as _, + nvs_enable: 0, + nano_enable: 0, + rx_ba_win: config.rx_ba_win as _, + wifi_task_core_id: Cpu::current() as _, + beacon_max_len: esp_wifi_sys::include::WIFI_SOFTAP_BEACON_MAX_LEN as i32, + mgmt_sbuf_num: esp_wifi_sys::include::WIFI_MGMT_SBUF_NUM as i32, + feature_caps: internal::__ESP_RADIO_G_WIFI_FEATURE_CAPS, + sta_disconnected_pm: false, + espnow_max_encrypt_num: esp_wifi_sys::include::CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM + as i32, + + tx_hetb_queue_num: 3, + dump_hesigb_enable: false, + + magic: WIFI_INIT_CONFIG_MAGIC as i32, + }; + + RX_QUEUE_SIZE.store(config.rx_queue_size, Ordering::Relaxed); + TX_QUEUE_SIZE.store(config.tx_queue_size, Ordering::Relaxed); + }; + crate::wifi::wifi_init()?; - let mut cntry_code = [0u8; 3]; - cntry_code[..esp_config_str!("ESP_RADIO_CONFIG_COUNTRY_CODE").len()] - .copy_from_slice(esp_config_str!("ESP_RADIO_CONFIG_COUNTRY_CODE").as_bytes()); - cntry_code[2] = esp_config_int!(u8, "ESP_RADIO_CONFIG_COUNTRY_CODE_OPERATING_CLASS"); unsafe { - let country = wifi_country_t { - cc: cntry_code, - schan: 1, - nchan: 13, - max_tx_power: 20, - policy: wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL, - }; + let country = config.country_code.into_blob(); esp_wifi_result!(esp_wifi_set_country(&country))?; } @@ -2740,7 +3014,7 @@ pub fn new<'d>( _phantom: Default::default(), }; - controller.set_power_saving(PowerSaveMode::default())?; + controller.set_power_saving(config.power_save_mode)?; Ok(( controller, diff --git a/examples/esp-now/embassy_esp_now/src/main.rs b/examples/esp-now/embassy_esp_now/src/main.rs index 53c786baa..75860c1f0 100644 --- a/examples/esp-now/embassy_esp_now/src/main.rs +++ b/examples/esp-now/embassy_esp_now/src/main.rs @@ -45,7 +45,8 @@ async fn main(_spawner: Spawner) -> ! { let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); let wifi = peripherals.WIFI; - let (mut controller, interfaces) = esp_radio::wifi::new(&esp_radio_ctrl, wifi).unwrap(); + let (mut controller, interfaces) = + esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap(); controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/esp-now/embassy_esp_now_duplex/src/main.rs b/examples/esp-now/embassy_esp_now_duplex/src/main.rs index 55cbfafba..4ee856b97 100644 --- a/examples/esp-now/embassy_esp_now_duplex/src/main.rs +++ b/examples/esp-now/embassy_esp_now_duplex/src/main.rs @@ -46,7 +46,8 @@ async fn main(spawner: Spawner) -> ! { let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); let wifi = peripherals.WIFI; - let (mut controller, interfaces) = esp_radio::wifi::new(&esp_radio_ctrl, wifi).unwrap(); + let (mut controller, interfaces) = + esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap(); controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/esp-now/esp_now/src/main.rs b/examples/esp-now/esp_now/src/main.rs index 60b2ddc89..7e78b6b6b 100644 --- a/examples/esp-now/esp_now/src/main.rs +++ b/examples/esp-now/esp_now/src/main.rs @@ -32,7 +32,8 @@ fn main() -> ! { let esp_radio_ctrl = esp_radio::init().unwrap(); let wifi = peripherals.WIFI; - let (mut controller, interfaces) = esp_radio::wifi::new(&esp_radio_ctrl, wifi).unwrap(); + let (mut controller, interfaces) = + esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap(); controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/wifi/80211_tx/src/main.rs b/examples/wifi/80211_tx/src/main.rs index ddbf988b3..56118c4ce 100644 --- a/examples/wifi/80211_tx/src/main.rs +++ b/examples/wifi/80211_tx/src/main.rs @@ -44,7 +44,7 @@ fn main() -> ! { // We must initialize some kind of interface and start it. let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); controller.set_mode(wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/wifi/access_point/src/main.rs b/examples/wifi/access_point/src/main.rs index abf84271d..51e26ee9d 100644 --- a/examples/wifi/access_point/src/main.rs +++ b/examples/wifi/access_point/src/main.rs @@ -66,7 +66,7 @@ fn main() -> ! { let esp_radio_ctrl = esp_radio::init().unwrap(); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.ap; let iface = create_interface(&mut device); diff --git a/examples/wifi/access_point_with_sta/src/main.rs b/examples/wifi/access_point_with_sta/src/main.rs index ff2e92dab..ec0808d14 100644 --- a/examples/wifi/access_point_with_sta/src/main.rs +++ b/examples/wifi/access_point_with_sta/src/main.rs @@ -52,7 +52,7 @@ fn main() -> ! { let esp_radio_ctrl = esp_radio::init().unwrap(); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let mut ap_device = interfaces.ap; let ap_interface = create_interface(&mut ap_device); diff --git a/examples/wifi/coex/src/main.rs b/examples/wifi/coex/src/main.rs index 170c0444a..be4eed3c3 100644 --- a/examples/wifi/coex/src/main.rs +++ b/examples/wifi/coex/src/main.rs @@ -99,7 +99,7 @@ fn main() -> ! { println!("started advertising"); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device); diff --git a/examples/wifi/dhcp/src/main.rs b/examples/wifi/dhcp/src/main.rs index c705d5463..7cc6f0e7f 100644 --- a/examples/wifi/dhcp/src/main.rs +++ b/examples/wifi/dhcp/src/main.rs @@ -49,7 +49,7 @@ fn main() -> ! { let esp_radio_ctrl = esp_radio::init().unwrap(); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device); diff --git a/examples/wifi/embassy_access_point/src/main.rs b/examples/wifi/embassy_access_point/src/main.rs index 70b5c7d53..7820165f8 100644 --- a/examples/wifi/embassy_access_point/src/main.rs +++ b/examples/wifi/embassy_access_point/src/main.rs @@ -62,7 +62,8 @@ async fn main(spawner: Spawner) -> ! { let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let (controller, interfaces) = esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + let (controller, interfaces) = + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let device = interfaces.ap; diff --git a/examples/wifi/embassy_access_point_with_sta/src/main.rs b/examples/wifi/embassy_access_point_with_sta/src/main.rs index 08ec0f366..b552bbbeb 100644 --- a/examples/wifi/embassy_access_point_with_sta/src/main.rs +++ b/examples/wifi/embassy_access_point_with_sta/src/main.rs @@ -78,7 +78,7 @@ async fn main(spawner: Spawner) -> ! { let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let wifi_ap_device = interfaces.ap; let wifi_sta_device = interfaces.sta; diff --git a/examples/wifi/embassy_dhcp/src/main.rs b/examples/wifi/embassy_dhcp/src/main.rs index fa5326406..7fcd80718 100644 --- a/examples/wifi/embassy_dhcp/src/main.rs +++ b/examples/wifi/embassy_dhcp/src/main.rs @@ -53,7 +53,8 @@ async fn main(spawner: Spawner) -> ! { let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let (controller, interfaces) = esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + let (controller, interfaces) = + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let wifi_interface = interfaces.sta; diff --git a/examples/wifi/sniffer/src/main.rs b/examples/wifi/sniffer/src/main.rs index e10e78b7d..eda5ab3ac 100644 --- a/examples/wifi/sniffer/src/main.rs +++ b/examples/wifi/sniffer/src/main.rs @@ -39,7 +39,7 @@ fn main() -> ! { // We must initialize some kind of interface and start it. let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); controller.set_mode(wifi::WifiMode::Sta).unwrap(); controller.start().unwrap(); diff --git a/examples/wifi/sntp/src/main.rs b/examples/wifi/sntp/src/main.rs index 9c188c7ee..f598d6805 100644 --- a/examples/wifi/sntp/src/main.rs +++ b/examples/wifi/sntp/src/main.rs @@ -85,7 +85,8 @@ async fn main(spawner: Spawner) -> ! { let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); - let (controller, interfaces) = esp_radio::wifi::new(esp_radio_ctrl, peripherals.WIFI).unwrap(); + let (controller, interfaces) = + esp_radio::wifi::new(esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let wifi_interface = interfaces.sta; diff --git a/examples/wifi/static_ip/src/main.rs b/examples/wifi/static_ip/src/main.rs index da2028544..8b7eeb327 100644 --- a/examples/wifi/static_ip/src/main.rs +++ b/examples/wifi/static_ip/src/main.rs @@ -46,7 +46,7 @@ fn main() -> ! { let esp_radio_ctrl = esp_radio::init().unwrap(); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device); diff --git a/hil-test/tests/esp_radio_init.rs b/hil-test/tests/esp_radio_init.rs index b5c9605e2..3f7d1c9ee 100644 --- a/hil-test/tests/esp_radio_init.rs +++ b/hil-test/tests/esp_radio_init.rs @@ -121,11 +121,13 @@ mod tests { &*mk_static!(esp_radio::Controller<'static>, esp_radio::init().unwrap()); // Initialize, then de-initialize wifi - let wifi = esp_radio::wifi::new(&esp_radio_ctrl, p.WIFI.reborrow()).unwrap(); + let wifi = + esp_radio::wifi::new(&esp_radio_ctrl, p.WIFI.reborrow(), Default::default()).unwrap(); drop(wifi); // Now, can we do it again? - let _wifi = esp_radio::wifi::new(&esp_radio_ctrl, p.WIFI.reborrow()).unwrap(); + let _wifi = + esp_radio::wifi::new(&esp_radio_ctrl, p.WIFI.reborrow(), Default::default()).unwrap(); } #[test] diff --git a/qa-test/src/bin/embassy_wifi_bench.rs b/qa-test/src/bin/embassy_wifi_bench.rs index 3a32c9fd6..996cd1db3 100644 --- a/qa-test/src/bin/embassy_wifi_bench.rs +++ b/qa-test/src/bin/embassy_wifi_bench.rs @@ -81,7 +81,7 @@ async fn main(spawner: Spawner) -> ! { let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap()); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let wifi_interface = interfaces.sta; diff --git a/qa-test/src/bin/wifi_bench.rs b/qa-test/src/bin/wifi_bench.rs index 2d1663a02..746ac537c 100644 --- a/qa-test/src/bin/wifi_bench.rs +++ b/qa-test/src/bin/wifi_bench.rs @@ -67,7 +67,7 @@ fn main() -> ! { let esp_radio_ctrl = esp_radio::init().unwrap(); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device); diff --git a/qa-test/src/bin/wifi_csi.rs b/qa-test/src/bin/wifi_csi.rs index 7b5b71043..f7b23c4e4 100644 --- a/qa-test/src/bin/wifi_csi.rs +++ b/qa-test/src/bin/wifi_csi.rs @@ -41,7 +41,7 @@ fn main() -> ! { let esp_radio_ctrl = esp_radio::init().unwrap(); let (mut controller, interfaces) = - esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI).unwrap(); + esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap(); let mut device = interfaces.sta; let iface = create_interface(&mut device);