1use crate::{Mcu, pac::Interrupt};
2use alloc::boxed::Box;
3use core::cell::{Cell, OnceCell};
4
5pub struct Callback {
6    callback: OnceCell<Cell<Box<dyn FnMut()>>>,
7    it_line: Interrupt,
8}
9
10unsafe impl Sync for Callback {}
11
12impl Callback {
16    pub const fn new(it_line: Interrupt) -> Self {
17        Self {
18            callback: OnceCell::new(),
19            it_line,
20        }
21    }
22
23    pub fn set(&self, mcu: &mut Mcu, callback: impl FnMut() + 'static) {
26        let cb = Cell::new(Box::new(callback));
27        critical_section::with(|_| {
28            assert!(self.callback.set(cb).is_ok());
29        });
30        mcu.nvic.enable(self.it_line, true);
31    }
32
33    pub unsafe fn call(&self) {
37        if let Some(cb) = self.callback.get() {
38            unsafe { (*cb.as_ptr())() }
39        }
40    }
41}
42
43#[macro_export]
44macro_rules! interrupt_handler {
45    ($(
46        ($LINE:ident, $CALLBACK:ident),
47    )+) => {$(
48        pub static $CALLBACK: $crate::interrupt::Callback =
49            $crate::interrupt::Callback::new($crate::pac::Interrupt::$LINE);
50
51        #[allow(non_snake_case)]
52        #[interrupt]
53        fn $LINE() {
54            unsafe { $CALLBACK.call() }
55        }
56    )+};
57}