use core::sync::atomic::{AtomicU64, Ordering};
use esp_idf_sys::*;
pub(crate) static CS: IsrCriticalSection = IsrCriticalSection::new();
#[inline(always)]
#[link_section = ".iram1.interrupt_active"]
pub fn active() -> bool {
unsafe { xPortInIsrContext() != 0 }
}
pub fn with_isr_yield_signal(cb: impl FnOnce()) -> bool {
if !active() {
panic!("with_isr_yield_signal() can only be called from an ISR context");
}
let mut signaled = false;
let prev_yielder =
unsafe { set_isr_yielder(Some((do_yield_signal, &mut signaled as *mut _ as _))) };
cb();
unsafe { set_isr_yielder(prev_yielder) };
signaled
}
unsafe fn do_yield_signal(arg: *mut ()) {
let signaled = arg.cast::<bool>().as_mut().unwrap();
*signaled = true
}
static ISR_YIELDER: AtomicU64 = AtomicU64::new(0);
#[allow(clippy::type_complexity)]
#[inline(always)]
#[link_section = ".iram1.interrupt_get_isr_yielder"]
pub(crate) unsafe fn get_isr_yielder() -> Option<(unsafe fn(*mut ()), *mut ())> {
if active() {
let value = ISR_YIELDER.load(Ordering::SeqCst);
if value == 0 {
None
} else {
let func: fn(*mut ()) = core::mem::transmute((value >> 32) as usize);
let arg = (value & 0xffffffff) as usize as *mut ();
Some((func, arg))
}
} else {
None
}
}
#[allow(clippy::type_complexity)]
#[inline(always)]
#[link_section = ".iram1.interrupt_set_isr_yielder"]
pub unsafe fn set_isr_yielder(
yielder: Option<(unsafe fn(*mut ()), *mut ())>,
) -> Option<(unsafe fn(*mut ()), *mut ())> {
if active() {
let value = if let Some((func, arg)) = yielder {
((func as usize as u64) << 32) | (arg as usize as u64)
} else {
0
};
let value = ISR_YIELDER.swap(value, Ordering::SeqCst);
if value == 0 {
None
} else {
let func: fn(*mut ()) = core::mem::transmute((value >> 32) as usize);
let arg = (value & 0xffffffff) as usize as *mut ();
Some((func, arg))
}
} else {
None
}
}
#[cfg(not(esp32c3))]
pub struct IsrCriticalSection(core::cell::UnsafeCell<portMUX_TYPE>);
#[cfg(esp32c3)]
pub struct IsrCriticalSection(core::marker::PhantomData<*const ()>);
#[cfg(esp32c3)]
#[inline(always)]
#[link_section = ".iram1.interrupt_enter"]
fn enter(_cs: &IsrCriticalSection) {
unsafe {
vPortEnterCritical();
}
}
#[cfg(not(esp32c3))]
#[inline(always)]
#[link_section = ".iram1.interrupt_enter"]
fn enter(cs: &IsrCriticalSection) {
#[cfg(esp_idf_version = "4.3")]
unsafe {
vPortEnterCritical(cs.0.get());
}
#[cfg(not(esp_idf_version = "4.3"))]
unsafe {
xPortEnterCriticalTimeout(cs.0.get(), portMUX_NO_TIMEOUT);
}
}
#[cfg(esp32c3)]
#[inline(always)]
#[link_section = ".iram1.interrupt_exit"]
fn exit(_cs: &IsrCriticalSection) {
unsafe {
vPortExitCritical();
}
}
#[cfg(not(esp32c3))]
#[inline(always)]
#[link_section = ".iram1.interrupt_exit"]
fn exit(cs: &IsrCriticalSection) {
unsafe {
vPortExitCritical(cs.0.get());
}
}
impl IsrCriticalSection {
#[inline(always)]
#[link_section = ".iram1.interrupt_cs_new"]
pub const fn new() -> Self {
#[cfg(not(esp32c3))]
let mux = core::cell::UnsafeCell::new(portMUX_TYPE {
owner: portMUX_FREE_VAL,
count: 0,
#[cfg(esp_idf_freertos_portmux_debug)]
lastLockedFn: b"(never locked)",
#[cfg(esp_idf_freertos_portmux_debug)]
lastLockedLine: -1,
});
#[cfg(esp32c3)]
let mux = core::marker::PhantomData;
Self(mux)
}
#[inline(always)]
#[link_section = ".iram1.interrupt_cs_enter"]
pub fn enter(&self) -> IsrCriticalSectionGuard {
enter(self);
IsrCriticalSectionGuard(self)
}
}
impl Default for IsrCriticalSection {
#[inline(always)]
#[link_section = ".iram1.interrupt_cs_default"]
fn default() -> Self {
Self::new()
}
}
unsafe impl Send for IsrCriticalSection {}
unsafe impl Sync for IsrCriticalSection {}
pub struct IsrCriticalSectionGuard<'a>(&'a IsrCriticalSection);
impl<'a> Drop for IsrCriticalSectionGuard<'a> {
#[inline(always)]
#[link_section = ".iram1.interrupt_csg_drop"]
fn drop(&mut self) {
exit(self.0);
}
}
#[inline(always)]
#[link_section = ".iram1.interrupt_free"]
pub fn free<R>(f: impl FnOnce() -> R) -> R {
let _guard = CS.enter();
f()
}
#[cfg(feature = "embassy-sync")]
pub mod embassy_sync {
use core::marker::PhantomData;
use embassy_sync::blocking_mutex::raw::RawMutex;
pub struct IsrRawMutex {
_phantom: PhantomData<()>,
}
unsafe impl Send for IsrRawMutex {}
unsafe impl Sync for IsrRawMutex {}
impl IsrRawMutex {
pub const fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
unsafe impl RawMutex for IsrRawMutex {
const INIT: Self = Self::new();
fn lock<R>(&self, f: impl FnOnce() -> R) -> R {
super::free(f)
}
}
}