radio_esp32::events
The driver’s events: a bounded queue and a task that drains it.
Why a queue, when a direct call is shorter
Section titled “Why a queue, when a direct call is shorter”_event_post is called from inside the blob, on wifiT, part-way through
whatever the driver was doing. The first implementation called the
application’s handler right there. That is one function call instead of a
copy, an allocation-free ring push and a context switch — and it is wrong,
for a reason both references state rather than imply.
Zephyr’s event thread carries the comment:
Dispatch library-posted events on this dedicated task so a handler runs on its own stack and cannot stall or overrun the Wi-Fi library task. A single queue drained by one task preserves event order.
esp-idf does the same through esp_event’s loop task. Both then do the
thing a synchronous handler cannot: they call back into esp_wifi_* from
the handler. Zephyr drains esp_wifi_scan_get_ap_record inside its
WIFI_EVENT_SCAN_DONE case. A handler invoked on wifiT cannot do that —
it would re-enter the driver from the driver’s own task, behind the API
lock that task already holds.
That is the whole reason this file exists, and it was found by comparing
against the references rather than by debugging: esp_wifi_scan_get_ap_num
did not return, and the shape of the call was a pattern neither reference
uses.
What is copied, and why it has to be
Section titled “What is copied, and why it has to be”event_data points at the driver’s own storage and is valid only for the
duration of the call. A queue that stored the pointer would hand the
handler freed memory some microseconds later — which is exactly the class
of bug the OSI table pointer already produced once (see
[crate::wifi::init]). So the payload is copied into the entry.
event_base is not copied: it is a const char * to a string literal in
the blob’s .rodata, or to [crate::wifi::WIFI_EVENT], both of which
outlive everything here. It is compared by pointer, which is what makes it
an identity rather than a string.
Functions
Section titled “Functions”dropped
Section titled “dropped”fn dropped() -> u32How many events have been refused because the queue was full.
Reported rather than silent: a dropped event is a scan result nobody collects or a disconnect nobody notices, and the only symptom is absence.
unsafe fn post(base: *const core::ffi::c_char, id: i32, data: *mut core::ffi::c_void, len: usize, _ticks: u32) -> i32_event_post(base, id, data, len, ticks), and wifi_init_config_t’s
event_handler.
Both routes land here. esp-idf’s esp_event_send_internal — the one the
config field points at — calls esp_event_post, the OSI entry, and then
forwards to the legacy loop; with no legacy loop to forward to, the two
converge on one function.
Always returns ESP_OK, including when the queue is full. The driver
treats a failed post as a fault worth retrying, and a full queue is a
consumer that is behind, not an error the driver can do anything about.
The drop is counted instead — see [dropped].
ticks is ignored. It is how long esp-idf would block waiting for room on
the event queue, and blocking here would be blocking wifiT, which is the
thing this file exists to stop.
Safety
Section titled “Safety”base is a C string the blob owns. data is readable for len bytes.
Called by the blob.
set_handler
Section titled “set_handler”fn set_handler(f: Option<EventHandler>) -> Option<EventHandler>Install what the dispatch task calls. Returns the previous handler.
The handler runs on the dispatch task, on its own stack, with nothing of
the driver’s held. It may call back into esp_wifi_*, which is the
point — see the module docs.
References: EventHandler
fn start()Start the dispatch task. Idempotent.
Called from [crate::wifi::init], for the reason ets_timer::start is: a
service the adapter depends on should not be each application’s job to
remember, and init is the only place that can guarantee “before the blob
posts anything”.
Constants
Section titled “Constants”MAX_PAYLOAD
Section titled “MAX_PAYLOAD”const MAX_PAYLOAD: usize;The largest event payload kept.
The biggest a station build posts is wifi_event_sta_disconnected_t: a
32-byte SSID, its length, a 6-byte BSSID, a reason and an RSSI — 45 bytes.
64 leaves room for the ones that grow without making the queue expensive:
this is [QUEUE_LEN] times this many bytes of .bss, and nothing else.
A payload larger than this is truncated rather than dropped, and says so. The alternative — dropping — loses the event and the fact that it happened, and every event carries its id in the entry regardless.
QUEUE_LEN
Section titled “QUEUE_LEN”const QUEUE_LEN: usize;How many events may be waiting.
Eight. The driver posts one per state change, and the dispatch task runs above the application, so the queue is normally empty or holds one. Depth is here for the burst when a scan finishes and something else changes at the same time.
Type Aliases
Section titled “Type Aliases”EventHandler
Section titled “EventHandler”type EventHandler = fn(..);What a handler is given.
base is an esp_event_base_t — compare it against
[crate::wifi::WIFI_EVENT] by pointer. data points into the dispatch
task’s own copy and is valid for the duration of the call only.