radio_esp32::scan
Scanning for access points. Step 5.2 of doc/plan-radio.md, issue #67.
The three structs, and why their offsets are asserted
Section titled “The three structs, and why their offsets are asserted”wifi_scan_config_t goes into the blob and wifi_ap_record_t comes back
out of it, so both are read by code nobody here can inspect. A field at the
wrong offset does not fail to compile and does not fault — it reads the
neighbouring field, and the symptom is an SSID that is one byte short or an
RSSI that is really a channel number.
The layouts are transcribed from esp_wifi_types.h at the pinned v4.4 and
pinned by const _: () = assert!(...) on the target, where the compiler
knows the real offsets. The three that actually move are called out below:
the C enums are 4 bytes and force alignment, which is what puts second at
40 rather than 39 and authmode at 48 rather than 45.
Who frees the results
Section titled “Who frees the results”The driver allocates the result list — out of our heap, through the OSI
table — and holds it until [ap_records] is called, which copies it out and
frees it. Calling [ap_count] and then never calling [ap_records] leaks
it until the next scan. That is esp-idf’s contract, not an artefact here,
and [ap_records] is written so the leak needs an early return to happen.
Structs
Section titled “Structs”ActiveScanTime
Section titled “ActiveScanTime”struct ActiveScanTime { pub min: u32, pub max: u32,}wifi_scan_time_t’s active half.
Implements: Clone, Copy
ApRecord
Section titled “ApRecord”struct ApRecord { pub bssid: [u8; 6], pub ssid: [u8; 33], pub primary: u8, pub second: u32, pub rssi: i8, pub authmode: u32, pub pairwise_cipher: u32, pub group_cipher: u32, pub ant: u32, pub flags: u32, pub country: Country,}wifi_ap_record_t. One access point, as the driver saw it.
Implements: Clone, Copy
References: Country
Country
Section titled “Country”struct Country { pub cc: [u8; 3], pub schan: u8, pub nchan: u8, pub max_tx_power: i8, pub policy: u32,}wifi_country_t. Sits at the tail of every [ApRecord].
Implements: Clone, Copy
ScanConfig
Section titled “ScanConfig”struct ScanConfig { pub ssid: *mut u8, pub bssid: *mut u8, pub channel: u8, pub show_hidden: bool, pub scan_type: u32, pub scan_time: ScanTime,}wifi_scan_config_t.
Implements: Default
References: ScanTime
ScanTime
Section titled “ScanTime”struct ScanTime { pub active: ActiveScanTime, pub passive: u32,}wifi_scan_time_t.
Implements: Clone, Copy
References: ActiveScanTime
Functions
Section titled “Functions”ap_count
Section titled “ap_count”unsafe fn ap_count() -> Result<u16, i32>How many access points the last scan found.
Safety
Section titled “Safety”Calls into the blob.
ap_records
Section titled “ap_records”unsafe fn ap_records(out: &mut [ApRecord]) -> Result<u16, i32>Copy the last scan’s results into out, and release the driver’s copy.
Returns how many records were written, which is min(found, out.len()).
The driver frees its list here whether or not out was big enough, so
a short buffer loses the overflow rather than leaving it for a second call
— that is esp-idf’s behaviour and the reason [ap_count] exists.
Safety
Section titled “Safety”Calls into the blob, which writes out.
References: ApRecord
auth_name
Section titled “auth_name”fn auth_name(mode: u32) -> &'static strA short name for an auth mode, for logging.
unsafe fn start(config: &ScanConfig, block: bool) -> i32esp_wifi_scan_start.
With block set this does not return until the scan finishes, which for an
all-channel active scan is well over a second. The blocking form is what
makes a scan readable without an event loop; the non-blocking form needs
[crate::wifi::event::SCAN_DONE] through
[crate::events::set_handler].
Blocking here blocks the calling task, not the Wi-Fi task. The driver
waits on its own event group, which reaches _event_group_wait_bits in the
OSI table and blocks whoever called in — so an application task calling
this yields the CPU rather than spinning.
Safety
Section titled “Safety”Calls into the blob. [crate::wifi::start] must have succeeded.
References: ScanConfig
unsafe fn stop() -> i32esp_wifi_scan_stop.
Safety
Section titled “Safety”Calls into the blob.
Constants
Section titled “Constants”AUTH_OPEN
Section titled “AUTH_OPEN”const AUTH_OPEN: u32;wifi_auth_mode_t values worth naming.
AUTH_WAPI_PSK
Section titled “AUTH_WAPI_PSK”const AUTH_WAPI_PSK: u32;AUTH_WEP
Section titled “AUTH_WEP”const AUTH_WEP: u32;AUTH_WPA2_ENTERPRISE
Section titled “AUTH_WPA2_ENTERPRISE”const AUTH_WPA2_ENTERPRISE: u32;AUTH_WPA2_PSK
Section titled “AUTH_WPA2_PSK”const AUTH_WPA2_PSK: u32;AUTH_WPA2_WPA3_PSK
Section titled “AUTH_WPA2_WPA3_PSK”const AUTH_WPA2_WPA3_PSK: u32;AUTH_WPA3_PSK
Section titled “AUTH_WPA3_PSK”const AUTH_WPA3_PSK: u32;AUTH_WPA_PSK
Section titled “AUTH_WPA_PSK”const AUTH_WPA_PSK: u32;AUTH_WPA_WPA2_PSK
Section titled “AUTH_WPA_WPA2_PSK”const AUTH_WPA_WPA2_PSK: u32;