radio_esp32::wifi
esp_wifi_init_internal — handing the Wi-Fi driver its configuration.
Step 5.1. The first call into the Wi-Fi half of the blobs, as
[crate::phy] was the first into the RF half.
This struct is the dangerous one
Section titled “This struct is the dangerous one”doc/plan-radio.md warns at step 3.4 that a wrong IDF version shows up
“as a magic-number mismatch if you are lucky, and as a working radio that
corrupts memory if you are not”. [WifiInitConfig] is where that happens:
the blob reads 200 bytes at whatever layout it was compiled against, and
nothing checks the middle of it. magic is last precisely so that a
struct which is the wrong size fails the magic test — but a struct that
is the right size with a field in the wrong place passes.
So the layout is asserted rather than described. The const blocks below
pin the size of both structs and the offset of every field whose position
is load-bearing, against the numbers in esp_wifi.h and
esp_wifi_crypto_types.h at v4.4. A change to either fails the build here
instead of on the air.
What is deliberately null
Section titled “What is deliberately null”[WpaCryptoFuncs] is all-null except its self-describing size and
version. esp-idf fills it from libwpa_supplicant, which is C source and
not one of the blobs, so every entry is something FlintOS would have to
provide: AES, SHA-1, SHA-256, HMAC, PBKDF2, RC4, CCMP, GMAC.
That is WPA2’s problem, not init’s and not an open scan’s — which is why step 5.2 (scan) comes before 5.3 (associate) here rather than in issue order. A null table is the honest encoding of “no crypto yet”: the blob calls through it only when it needs to authenticate, and a null pointer there is a fault at a known address rather than a silently wrong key.
Modules
Section titled “Modules”event—wifi_event_t, fromesp_wifi_types.hat v4.4. Only the ones a stationmode—wifi_mode_t. The driver refuses most calls until a mode is set.
Structs
Section titled “Structs”EventBase
Section titled “EventBase”struct EventBase(*const core::ffi::c_char);An esp_event_base_t, which is a const char * and therefore not Sync on
its own. It is written once, at link time, and never again.
Implements: Sync
ScanDone
Section titled “ScanDone”struct ScanDone { pub status: u32, pub number: u8, pub scan_id: u8,}wifi_event_sta_scan_done_t, the payload of [event::SCAN_DONE].
Implements: Clone, Copy, Debug
WifiInitConfig
Section titled “WifiInitConfig”struct WifiInitConfig { pub event_handler: Option<SystemEventHandler>, pub osi_funcs: *const crate::osi::WifiOsiFuncs, pub wpa_crypto_funcs: WpaCryptoFuncs, pub static_rx_buf_num: i32, pub dynamic_rx_buf_num: i32, pub tx_buf_type: i32, pub static_tx_buf_num: i32, pub dynamic_tx_buf_num: i32, pub cache_tx_buf_num: i32, pub csi_enable: i32, pub ampdu_rx_enable: i32, pub ampdu_tx_enable: i32, pub amsdu_tx_enable: i32, pub nvs_enable: i32, pub nano_enable: i32, pub rx_ba_win: i32, pub wifi_task_core_id: i32, pub beacon_max_len: i32, pub mgmt_sbuf_num: i32, pub feature_caps: u64, pub sta_disconnected_pm: bool, pub magic: i32,}wifi_init_config_t, field for field and in order.
The order is the header’s and may not be tidied. magic is last because
the header says so — “it should be the last field” — and that is the only
thing standing between a version mismatch and a radio that runs on
misaligned configuration.
Implements: Clone, Copy, Debug
References: SystemEventHandler, WifiOsiFuncs, WpaCryptoFuncs
WpaCryptoFuncs
Section titled “WpaCryptoFuncs”struct WpaCryptoFuncs { pub size: u32, pub version: u32, pub aes_wrap: Option<fn(..)>, pub aes_unwrap: Option<fn(..)>, pub hmac_sha256_vector: Option<fn(..)>, pub sha256_prf: Option<fn(..)>, pub hmac_md5: Option<fn(..)>, pub hmac_md5_vector: Option<fn(..)>, pub hmac_sha1: Option<fn(..)>, pub hmac_sha1_vector: Option<fn(..)>, pub sha1_prf: Option<fn(..)>, pub sha1_vector: Option<fn(..)>, pub pbkdf2_sha1: Option<fn(..)>, pub rc4_skip: Option<fn(..)>, pub md5_vector: Option<fn(..)>, pub aes_encrypt: Option<fn(..)>, pub aes_encrypt_init: Option<fn(..)>, pub aes_encrypt_deinit: Option<fn(..)>, pub aes_decrypt: Option<fn(..)>, pub aes_decrypt_init: Option<fn(..)>, pub aes_decrypt_deinit: Option<fn(..)>, pub aes_128_encrypt: Option<fn(..)>, pub aes_128_decrypt: Option<fn(..)>, pub omac1_aes_128: Option<fn(..)>, pub ccmp_decrypt: Option<fn(..)>, pub ccmp_encrypt: Option<fn(..)>, pub aes_gmac: Option<fn(..)>,}wpa_crypto_funcs_t. Twenty-five function pointers behind two words.
Embedded by value in [WifiInitConfig], not behind a pointer, so its
size is part of that struct’s layout and every field after it moves if this
one is wrong.
Implements: Clone, Copy, Debug
Functions
Section titled “Functions”unsafe fn init() -> i32Bring the Wi-Fi driver up.
Allocates the driver’s control structures and buffers out of
[kernel::heap] through the OSI table, so that must be initialised first
— and [crate::nvs] too, since nvs_enable is 1.
Returns the blob’s esp_err_t unchanged. Zero is ESP_OK; everything else
is Espressif’s, and is more useful reported than translated.
Safety
Section titled “Safety”Calls into the blob, which will call back out through the OSI table on the
same thread. Call once, and not from two cores: it writes OSI_TABLE,
whose soundness rests on there being exactly one writer before any reader.
set_mode
Section titled “set_mode”unsafe fn set_mode(m: u32) -> i32esp_wifi_set_mode. One of [mode].
Safety
Section titled “Safety”Calls into the blob. [init] must have succeeded.
unsafe fn start() -> i32esp_wifi_start. Starts the driver in whatever mode was set.
Does not return with the radio ready. The driver posts
[event::STA_START] when station mode is actually up, and a scan requested
before that returns ESP_ERR_WIFI_NOT_STARTED. In esp-idf the start is
synchronous enough in practice that most examples ignore this; it is called
out because the failure is intermittent and reads like a scan bug.
Safety
Section titled “Safety”Calls into the blob. A mode must have been set.
unsafe fn stop() -> i32esp_wifi_stop.
Safety
Section titled “Safety”Calls into the blob.
Constants
Section titled “Constants”INIT_CONFIG_LEN
Section titled “INIT_CONFIG_LEN”const INIT_CONFIG_LEN: usize;sizeof(wifi_init_config_t).
Two pointers, the crypto table, sixteen ints, then a uint64_t which
forces four bytes of padding before it, a bool, three more bytes of
padding, and the magic.
INIT_CONFIG_MAGIC
Section titled “INIT_CONFIG_MAGIC”const INIT_CONFIG_MAGIC: i32;WIFI_INIT_CONFIG_MAGIC from esp_wifi.h. Last field of the struct, so a
size mismatch lands somewhere else and fails this check.
WPA_CRYPTO_FUNCS_LEN
Section titled “WPA_CRYPTO_FUNCS_LEN”const WPA_CRYPTO_FUNCS_LEN: usize;The size esp_wifi_crypto_types.h gives it: two uint32_t and
twenty-five pointers.
Type Aliases
Section titled “Type Aliases”SystemEventHandler
Section titled “SystemEventHandler”type SystemEventHandler = fn(..);system_event_handler_t, v4.4’s, which is not what the name suggests.
The obvious reading of “system_event_handler_t” is a handler taking a
system_event_t * — the legacy tagged union — and that reading was written
into this file and was wrong. The header settles it:
typedef esp_err_t (*system_event_handler_t)(esp_event_base_t event_base, int32_t event_id, void* event_data, size_t event_data_size, TickType_t ticks_to_wait);The same five arguments as _event_post. A one-argument stub sat here and
linked, ran, and returned ESP_OK for every event the driver raised —
because on the windowed ABI a callee may read fewer arguments than it was
passed, so the wrong signature is invisible until you ask what happened to
the events.