2021-02-19 23:27:24 +00:00
|
|
|
use core::cell::UnsafeCell;
|
|
|
|
use core::marker::{PhantomData, PhantomPinned};
|
2021-01-05 00:57:05 +00:00
|
|
|
use core::pin::Pin;
|
2021-07-05 07:42:43 +00:00
|
|
|
use core::ptr;
|
2021-01-03 00:40:40 +00:00
|
|
|
|
2021-03-07 23:15:40 +00:00
|
|
|
use embassy::interrupt::{Interrupt, InterruptExt};
|
2021-02-28 23:44:38 +00:00
|
|
|
|
2021-07-05 07:42:43 +00:00
|
|
|
/// # Safety
|
2021-07-05 08:18:05 +00:00
|
|
|
/// When types implementing this trait are used with `PeripheralMutex`,
|
|
|
|
/// no fields referenced by `on_interrupt`'s lifetimes must end without first calling `Drop` on the `PeripheralMutex`.
|
2021-07-05 07:42:43 +00:00
|
|
|
pub unsafe trait PeripheralStateUnchecked {
|
2021-02-26 00:55:27 +00:00
|
|
|
type Interrupt: Interrupt;
|
2021-01-03 00:40:40 +00:00
|
|
|
fn on_interrupt(&mut self);
|
|
|
|
}
|
|
|
|
|
2021-07-05 07:42:43 +00:00
|
|
|
// `PeripheralMutex` is safe because `Pin` guarantees that the memory it references will not be invalidated or reused
|
|
|
|
// without calling `Drop`. However, it provides no guarantees about references contained within the state still being valid,
|
|
|
|
// so this `'static` bound is necessary.
|
|
|
|
pub trait PeripheralState: 'static {
|
|
|
|
type Interrupt: Interrupt;
|
|
|
|
fn on_interrupt(&mut self);
|
|
|
|
}
|
|
|
|
|
|
|
|
// SAFETY: `T` has to live for `'static` to implement `PeripheralState`, thus its lifetime cannot end.
|
|
|
|
unsafe impl<T> PeripheralStateUnchecked for T
|
|
|
|
where
|
|
|
|
T: PeripheralState,
|
|
|
|
{
|
|
|
|
type Interrupt = T::Interrupt;
|
|
|
|
fn on_interrupt(&mut self) {
|
|
|
|
self.on_interrupt()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PeripheralMutex<S: PeripheralStateUnchecked> {
|
2021-03-18 01:01:29 +00:00
|
|
|
state: UnsafeCell<S>,
|
2021-02-28 21:03:45 +00:00
|
|
|
|
2021-03-18 01:01:29 +00:00
|
|
|
irq_setup_done: bool,
|
|
|
|
irq: S::Interrupt,
|
2021-02-28 21:03:45 +00:00
|
|
|
|
2021-02-19 23:27:24 +00:00
|
|
|
_not_send: PhantomData<*mut ()>,
|
|
|
|
_pinned: PhantomPinned,
|
2021-01-03 00:40:40 +00:00
|
|
|
}
|
|
|
|
|
2021-07-05 07:42:43 +00:00
|
|
|
impl<S: PeripheralStateUnchecked> PeripheralMutex<S> {
|
2021-01-06 21:48:54 +00:00
|
|
|
pub fn new(state: S, irq: S::Interrupt) -> Self {
|
2021-01-03 00:40:40 +00:00
|
|
|
Self {
|
2021-03-18 01:01:29 +00:00
|
|
|
irq,
|
|
|
|
irq_setup_done: false,
|
|
|
|
|
|
|
|
state: UnsafeCell::new(state),
|
2021-02-19 23:27:24 +00:00
|
|
|
_not_send: PhantomData,
|
|
|
|
_pinned: PhantomPinned,
|
2021-01-03 00:40:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-18 01:29:03 +00:00
|
|
|
pub fn register_interrupt(self: Pin<&mut Self>) {
|
|
|
|
let this = unsafe { self.get_unchecked_mut() };
|
|
|
|
if this.irq_setup_done {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.irq.disable();
|
|
|
|
this.irq.set_handler(|p| {
|
2021-07-05 07:42:43 +00:00
|
|
|
critical_section::with(|_| {
|
|
|
|
if p.is_null() {
|
|
|
|
// The state was dropped, so we can't operate on it.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Safety: it's OK to get a &mut to the state, since
|
|
|
|
// - We're in a critical section, no one can preempt us (and call with())
|
|
|
|
// - We can't have preempted a with() call because the irq is disabled during it.
|
|
|
|
let state = unsafe { &mut *(p as *mut S) };
|
|
|
|
state.on_interrupt();
|
|
|
|
})
|
2021-02-26 01:04:48 +00:00
|
|
|
});
|
2021-03-18 01:29:03 +00:00
|
|
|
this.irq
|
|
|
|
.set_handler_context((&mut this.state) as *mut _ as *mut ());
|
|
|
|
this.irq.enable();
|
2021-02-28 21:03:45 +00:00
|
|
|
|
2021-03-18 01:29:03 +00:00
|
|
|
this.irq_setup_done = true;
|
2021-02-28 21:03:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with<R>(self: Pin<&mut Self>, f: impl FnOnce(&mut S, &mut S::Interrupt) -> R) -> R {
|
|
|
|
let this = unsafe { self.get_unchecked_mut() };
|
|
|
|
|
2021-03-18 01:01:29 +00:00
|
|
|
this.irq.disable();
|
2021-01-05 00:57:05 +00:00
|
|
|
|
|
|
|
// Safety: it's OK to get a &mut to the state, since the irq is disabled.
|
2021-03-18 01:01:29 +00:00
|
|
|
let state = unsafe { &mut *this.state.get() };
|
|
|
|
let r = f(state, &mut this.irq);
|
2021-01-03 00:40:40 +00:00
|
|
|
|
2021-03-18 01:01:29 +00:00
|
|
|
this.irq.enable();
|
2021-01-03 00:40:40 +00:00
|
|
|
|
|
|
|
r
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-05 07:42:43 +00:00
|
|
|
impl<S: PeripheralStateUnchecked> Drop for PeripheralMutex<S> {
|
2021-01-03 00:40:40 +00:00
|
|
|
fn drop(&mut self) {
|
2021-03-18 01:01:29 +00:00
|
|
|
self.irq.disable();
|
|
|
|
self.irq.remove_handler();
|
2021-07-05 07:42:43 +00:00
|
|
|
// Set the context to null so that the interrupt will know we're dropped
|
|
|
|
// if we pre-empted it before it entered a critical section.
|
|
|
|
self.irq.set_handler_context(ptr::null_mut());
|
2021-01-03 00:40:40 +00:00
|
|
|
}
|
|
|
|
}
|