GPIO: prevent woken task interrupting GPIO handler (#2838)

* Clear interrupt bit before allowing waker to wake a task

* Add test
This commit is contained in:
Dániel Buga 2024-12-19 08:53:48 +01:00 committed by GitHub
parent b8f15f0a8b
commit 97598968eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 37 additions and 3 deletions

View File

@ -930,8 +930,11 @@ fn handle_pin_interrupts(user_handler: fn()) {
intr_bits -= 1 << pin_pos; intr_bits -= 1 << pin_pos;
let pin_nr = pin_pos as u8 + bank.offset(); let pin_nr = pin_pos as u8 + bank.offset();
crate::interrupt::free(|| {
asynch::PIN_WAKERS[pin_nr as usize].wake(); asynch::PIN_WAKERS[pin_nr as usize].wake();
set_int_enable(pin_nr, Some(0), 0, false); set_int_enable(pin_nr, Some(0), 0, false);
});
} }
bank.write_interrupt_status_clear(intrs); bank.write_interrupt_status_clear(intrs);

View File

@ -12,6 +12,7 @@
//! interrupt15() => Priority::Priority15 //! interrupt15() => Priority::Priority15
//! ``` //! ```
pub(crate) use esp_riscv_rt::riscv::interrupt::free;
pub use esp_riscv_rt::TrapFrame; pub use esp_riscv_rt::TrapFrame;
use riscv::register::{mcause, mtvec}; use riscv::register::{mcause, mtvec};

View File

@ -1,6 +1,7 @@
//! Interrupt handling //! Interrupt handling
use xtensa_lx::interrupt; use xtensa_lx::interrupt;
pub(crate) use xtensa_lx::interrupt::free;
use xtensa_lx_rt::exception::Context; use xtensa_lx_rt::exception::Context;
pub use self::vectored::*; pub use self::vectored::*;

View File

@ -904,7 +904,7 @@ where
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(esp32)] { if #[cfg(esp32)] {
// https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html // https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html
xtensa_lx::interrupt::free(|| { crate::interrupt::free(|| {
*byte = fifo.read().rxfifo_rd_byte().bits(); *byte = fifo.read().rxfifo_rd_byte().bits();
}); });
} else { } else {

View File

@ -399,4 +399,33 @@ mod tests {
_ = Input::new(pin, Pull::Down); _ = Input::new(pin, Pull::Down);
} }
#[test]
fn interrupt_executor_is_not_frozen(ctx: Context) {
use esp_hal::interrupt::{software::SoftwareInterrupt, Priority};
use esp_hal_embassy::InterruptExecutor;
use static_cell::StaticCell;
static INTERRUPT_EXECUTOR: StaticCell<InterruptExecutor<1>> = StaticCell::new();
let interrupt_executor = INTERRUPT_EXECUTOR.init(InterruptExecutor::new(unsafe {
SoftwareInterrupt::<1>::steal()
}));
let spawner = interrupt_executor.start(Priority::max());
spawner.must_spawn(test_task(ctx.test_gpio1.degrade()));
#[embassy_executor::task]
async fn test_task(pin: AnyPin) {
let mut pin = Input::new(pin, Pull::Down);
// This line must return, even if the executor
// is running at a higher priority than the GPIO handler.
pin.wait_for_low().await;
embedded_test::export::check_outcome(());
}
loop {}
}
} }