diff --git a/embassy-extras/src/lib.rs b/embassy-extras/src/lib.rs index 5dafba702..be08ddf1e 100644 --- a/embassy-extras/src/lib.rs +++ b/embassy-extras/src/lib.rs @@ -5,6 +5,7 @@ pub(crate) mod fmt; mod macros; pub mod peripheral; +pub mod peripheral_shared; pub mod ring_buffer; pub mod usb; diff --git a/embassy-extras/src/peripheral_shared.rs b/embassy-extras/src/peripheral_shared.rs new file mode 100644 index 000000000..73906698e --- /dev/null +++ b/embassy-extras/src/peripheral_shared.rs @@ -0,0 +1,68 @@ +use core::cell::UnsafeCell; +use core::marker::{PhantomData, PhantomPinned}; +use core::pin::Pin; +use core::sync::atomic::{compiler_fence, Ordering}; + +use embassy::interrupt::{Interrupt, InterruptExt}; + +pub trait PeripheralState { + type Interrupt: Interrupt; + fn on_interrupt(&self); +} + +pub struct Peripheral { + state: UnsafeCell, + + irq_setup_done: bool, + irq: S::Interrupt, + + _not_send: PhantomData<*mut ()>, + _pinned: PhantomPinned, +} + +impl Peripheral { + pub fn new(irq: S::Interrupt, state: S) -> Self { + Self { + irq, + irq_setup_done: false, + + state: UnsafeCell::new(state), + _not_send: PhantomData, + _pinned: PhantomPinned, + } + } + + pub fn register_interrupt(self: Pin<&mut Self>) { + let this = unsafe { self.get_unchecked_mut() }; + if this.irq_setup_done { + return; + } + + this.irq.disable(); + compiler_fence(Ordering::SeqCst); + + this.irq.set_handler(|p| { + let state = unsafe { &*(p as *const S) }; + state.on_interrupt(); + }); + this.irq + .set_handler_context((&this.state) as *const _ as *mut ()); + + compiler_fence(Ordering::SeqCst); + this.irq.enable(); + + this.irq_setup_done = true; + } + + pub fn state(self: Pin<&mut Self>) -> &S { + let this = unsafe { self.get_unchecked_mut() }; + unsafe { &*this.state.get() } + } +} + +impl Drop for Peripheral { + fn drop(&mut self) { + self.irq.disable(); + self.irq.remove_handler(); + } +}