Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions esp32c3/isr.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@ void espradio_prewire_wifi_interrupts(void) {
intr_matrix_set(0, ETS_WIFI_PWR_INTR_SOURCE, ESPRADIO_WIFI_CPU_INT);
}

extern void espradio_mark_wifi_isr_slot(int32_t n);

/* No-op: the blob calls set_intr to route peripheral sources to CPU
* interrupts, but on RISC-V (ESP32-C3) the routing is already configured
* by espradio_prewire_wifi_interrupts(). Letting the blob call
* intr_matrix_set at arbitrary times interferes with TinyGo's interrupt
* controller state. The Rust esp-wifi does the same (no-op set_intr). */
* controller state. The Rust esp-wifi does the same (no-op set_intr).
* Record the blob's requested intr_num as a WiFi ISR slot. */
void espradio_set_intr(int32_t cpu_no, uint32_t intr_source, uint32_t intr_num, int32_t intr_prio) {
(void)cpu_no;
(void)intr_source;
(void)intr_num;
(void)intr_prio;
espradio_mark_wifi_isr_slot((int32_t)intr_num);
}

/* No-op: the Rust esp-wifi also no-ops clear_intr. */
Expand Down
34 changes: 24 additions & 10 deletions esp32s3/isr.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ void espradio_prewire_wifi_interrupts(void) {
intr_matrix_set(0, ETS_WIFI_BB_INTR_SOURCE, ESPRADIO_WIFI_CPU_INT); /* src 3 */
}

/* No-op: the blob calls set_intr to route peripheral sources to CPU
* interrupts, but routing is already configured by
* espradio_prewire_wifi_interrupts(). */
extern void espradio_mark_wifi_isr_slot(int32_t n);

/* Route the blob's requested peripheral source to our fixed WiFi CPU interrupt
* and record the blob's requested intr_num as a WiFi ISR slot so that
* espradio_call_wifi_isr() only calls the relevant handlers. */
void espradio_set_intr(int32_t cpu_no, uint32_t intr_source, uint32_t intr_num, int32_t intr_prio) {
intr_matrix_set(0, intr_source, ESPRADIO_WIFI_CPU_INT);
espradio_mark_wifi_isr_slot((int32_t)intr_num);
}

/* No-op: same as set_intr. */
Expand All @@ -40,22 +43,33 @@ void espradio_clear_intr(uint32_t intr_source, uint32_t intr_num) {
(void)intr_num;
}

/* Enable CPU interrupts using Xtensa INTENABLE special register.
* The blob calls ints_on with its own mask (1<<0 for WiFi MAC).
* We translate: if the blob's mask includes a WiFi-related bit,
* also set our actual WiFi CPU interrupt bit. */
/* Enable/disable CPU interrupts using Xtensa INTENABLE special register.
*
* IMPORTANT: We deliberately ignore the blob's mask and only operate on
* ESPRADIO_WIFI_CPU_INT (bit 12). The blob passes its own original CPU
* interrupt number (e.g. 0 or 1), which may coincide with CPU interrupts
* that TinyGo has allocated for other purposes (bit 10 = GPIO, bit 9 =
* timer alarm). If we forwarded the blob's mask, espradio_ints_off would
* clear those bits from INTENABLE, permanently disabling user interrupts
* such as GPIO PinFalling callbacks (issue #40).
*
* The blob calls these for its own critical-section protection. Since all
* blob ISR handlers run in goroutine context (never from real ISR context),
* the blob's critical sections are already serialised by TinyGo's
* cooperative scheduler; ignoring the mask is safe. */
void espradio_ints_on(uint32_t mask) {
uint32_t actual_mask = mask | (1u << ESPRADIO_WIFI_CPU_INT);
(void)mask;
uint32_t val;
__asm__ volatile ("rsr %0, intenable" : "=r"(val));
val |= actual_mask;
val |= (1u << ESPRADIO_WIFI_CPU_INT);
__asm__ volatile ("wsr %0, intenable; rsync" :: "r"(val));
}

void espradio_ints_off(uint32_t mask) {
(void)mask;
uint32_t val;
__asm__ volatile ("rsr %0, intenable" : "=r"(val));
val &= ~mask;
val &= ~(1u << ESPRADIO_WIFI_CPU_INT);
__asm__ volatile ("wsr %0, intenable; rsync" :: "r"(val));
}

Expand Down
1 change: 1 addition & 0 deletions espradio.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ void espradio_ensure_osi_ptr(void);
void espradio_coex_adapter_init(void);
void espradio_call_saved_isr(int32_t n);
void espradio_call_wifi_isr(void);
void espradio_mark_wifi_isr_slot(int32_t n);
uint32_t espradio_get_wifi_isr_count(void);
void espradio_prewire_wifi_interrupts(void);
void espradio_wifi_int_to_level(void);
Expand Down
19 changes: 17 additions & 2 deletions isr.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ void espradio_user_exception(uint32_t cause, uint32_t epc, uint32_t excvaddr, ui
static void (*s_isr_fn[32])(void *);
static void *s_isr_arg[32];

/* Bitmask of ISR slots registered via espradio_set_intr (WiFi sources only). */
static uint32_t s_wifi_isr_slots;

void espradio_mark_wifi_isr_slot(int32_t n) {
if (n >= 0 && n < 32) {
s_wifi_isr_slots |= (1u << n);
}
}

void espradio_set_isr(int32_t n, void *f, void *arg) {
if (n >= 0 && n < 32) {
s_isr_fn[n] = (void (*)(void *))f;
Expand All @@ -98,8 +107,14 @@ void espradio_call_wifi_isr(void) {
s_wifi_isr_count++;
s_in_isr = 1;
ESPRADIO_MEMORY_BARRIER();
// CALL ALL ISRs from 0 to 31 just in case, to see if they are set!
for (int i = 0; i < 32; i++) {
/* Only call ISR slots that were registered via espradio_set_intr for a
* WiFi peripheral source. Calling all 32 slots risks invoking blob
* handlers at slot numbers that coincide with TinyGo's GPIO or timer
* CPU interrupts, which can corrupt INTENABLE. */
uint32_t slots = s_wifi_isr_slots;
while (slots) {
int i = __builtin_ctz(slots);
slots &= slots - 1;
if (s_isr_fn[i]) {
s_isr_fn[i](s_isr_arg[i]);
}
Expand Down
Loading