radio_esp32::interrupts
_set_intr, _clear_intr and _set_isr — the blob’s route to an
interrupt.
Three entries in wifi_osi_funcs_t, and between them the reason the radio
cannot deliver a packet without this file. esp-idf implements them as
intr_matrix_set and xt_set_interrupt_handler; FlintOS already has both
halves — soc_esp32::intr_map for the crossbar and kernel::interrupt for
the CPU-side mask and dispatch — so what is left is the shape mismatch
between them.
The mismatch, and the trampolines
Section titled “The mismatch, and the trampolines”The blob hands over void (*f)(void *arg) and expects its arg back.
kernel::interrupt::register takes a plain fn(), which carries no data,
and that is deliberate: a top-half runs in trap context where it cannot
take a lock, so the kernel gives it nothing that would need one.
A closure cannot bridge that — fn() has no environment. What can is a
distinct function per CPU interrupt, each knowing its own number at
compile time. trampoline::<N> is a separate function item for every N,
so TRAMPOLINES is 32 different fn() values, each of which looks up
slot N and calls what the blob installed there.
That costs one static array and no allocation, and it keeps the kernel’s
contract intact rather than widening register to take a *mut c_void
that only this crate would ever use.
Structs
Section titled “Structs”struct Route { pub source: u8, pub num: u8, pub core: u8, pub connected: bool,}A route the blob asked for, kept so it can be reported later.
Implements: Clone, Copy, Eq, PartialEq, StructuralPartialEq
Functions
Section titled “Functions”clear_intr
Section titled “clear_intr”unsafe fn clear_intr(_source: u32, _num: u32)_clear_intr(source, num).
esp-idf’s own table leaves this pointing at a function that does nothing on
the ESP32 — the crossbar entry is overwritten by the next intr_matrix_set
rather than torn down, and the Wi-Fi driver never unroutes. Implemented as
the same no-op, and named as one, because a version that did unroute
would be a behaviour esp-idf does not have and the blob has never been
tested against.
Safety
Section titled “Safety”Does nothing. Called by the blob.
fn fires(n: usize) -> u32How many times CPU interrupt n’s trampoline has run.
for_each_route
Section titled “for_each_route”fn for_each_route<impl FnMut(Route)>(f: impl FnMut)Call f with every interrupt route the blob has asked for.
The answer to “did the radio’s interrupt get connected, and to what?”, which is otherwise invisible — a scan that times out looks identical whether the MAC interrupt is misrouted, refused, or never requested. On a v4.4 station bring-up the expected answer is exactly one route: source 0 to a level-1 CPU input on the core the Wi-Fi task is pinned to.
installed_handler
Section titled “installed_handler”fn installed_handler(n: usize) -> usize_set_isr(n, f, arg) — install f as the handler for CPU interrupt n.
The handler the driver installed for CPU interrupt n, as an address.
Zero if none. The point of comparison: the driver’s own MAC handler is
wDev_ProcessFiq, which links at a known address, and it is the only
function in the blobs that reads or clears the receiver’s event word. If
what we recorded is not that function, we wired up something else and
every observation downstream of the interrupt is explained.
set_intr
Section titled “set_intr”unsafe fn set_intr(cpu: i32, source: u32, num: u32, _prio: i32)_set_intr(cpu, source, num, prio) — route peripheral source to CPU
interrupt num and unmask it.
esp-idf’s intr_matrix_set. prio is accepted and ignored: on this chip
the priority is a property of the CPU interrupt number, not something set
alongside it, so the blob’s choice of num has already decided it. Saying
so here beats silently taking an argument that cannot be honoured.
cpu is checked rather than ignored. The blob asks for the core it is
running on, and routing a radio interrupt to the other core would deliver
it somewhere the blob does not expect.
Safety
Section titled “Safety”Writes the interrupt crossbar and unmasks a CPU input. Called by the blob.
set_isr
Section titled “set_isr”unsafe fn set_isr(n: i32, f: *mut core::ffi::c_void, arg: *mut core::ffi::c_void)esp-idf’s xt_set_interrupt_handler. Installing is separate from routing:
the blob calls this first and _set_intr after, or the other way round,
and neither order may lose the other’s work. So this only records the
handler, and [set_intr] does the routing and unmasking.
Safety
Section titled “Safety”f is called in trap context with arg. Called by the blob.