1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
//! Chip-Generic Interrupt Utilities
//!
//! For the most part, [crate::interrupt::free] is what you want:
//!
//! ```
//! avr_device::interrupt::free(|cs| {
//!     // Interrupts are disabled here
//! });
//! ```
//!
//! To access shared state, Mutex can be used:
//!
//! ```
//! use avr_device::interrupt::Mutex;
//! use core::cell::Cell;
//!
//! // Use Cell, if the wrapped type is Copy.
//! // Use RefCell, if the wrapped type is not Copy or if you need a reference to it for other reasons.
//! static MYGLOBAL: Mutex<Cell<u16>> = Mutex::new(Cell::new(0));
//!
//! fn my_fun() {
//!     avr_device::interrupt::free(|cs| {
//!         // Interrupts are disabled here
//!
//!         // Acquire mutex to global variable.
//!         let myglobal_ref = MYGLOBAL.borrow(cs);
//!         // Write to the global variable.
//!         myglobal_ref.set(42);
//!     });
//! }
//! ```

pub use bare_metal::{CriticalSection, Mutex};

#[cfg(target_arch = "avr")]
use core::arch::asm;

/// Opaque structure for storing the global interrupt flag status.
///
/// This structure does not implement `Copy` and `Clone`,
/// because the user shall not duplicate and pass it twice to [crate::interrupt::restore].
#[derive(Debug)]
#[cfg_attr(feature = "ufmt", derive(ufmt::derive::uDebug))]
pub struct IrqFlag {
    // The saved SREG.
    sreg: u8,
}

impl IrqFlag {
    #[inline(always)]
    fn new(sreg: u8) -> IrqFlag {
        IrqFlag { sreg }
    }

    /// Check the status of the saved global interrupt flag.
    ///
    /// Returns true, if the saved global interrupt flag is set (IRQs enabled).
    /// Otherwise returns false.
    ///
    /// This method can be used to check whether interrupts were enabled
    /// before the [crate::interrupt::disable_save] call.
    /// You probably shouldn't make your program behavior dependent on this state.
    /// Consider using a different design.
    #[inline(always)]
    pub fn enabled(&self) -> bool {
        self.sreg & 0x80 != 0
    }
}

/// Disable the global interrupt flag.
///
/// *Hint*: Most of the time you probably don't want to use this function directly.
///         Consider creating a critical section with [crate::interrupt::free] instead.
///
/// This function is an optimization fence.
/// That means memory accesses will not be re-ordered by the compiler across this function call.
#[inline(always)]
pub fn disable() {
    cfg_if::cfg_if! {
        if #[cfg(target_arch = "avr")] {
            // Disable interrupts
            unsafe { asm!("cli") };
        } else {
            unimplemented!()
        }
    }
}

/// Disable the global interrupt flag and return an opaque representation of the previous flag status.
///
/// *Hint*: Most of the time you probably don't want to use this function directly.
///         Consider creating a critical section with [crate::interrupt::free] instead.
///
/// This function is an optimization fence.
/// That means memory accesses will not be re-ordered by the compiler across this function call.
///
/// Returns an object that contains the status of the global interrupt flag from *before* the `disable_save()` call.
/// This object shall later be passed to the [crate::interrupt::restore] function.
#[inline(always)]
#[allow(unreachable_code)]
pub fn disable_save() -> IrqFlag {
    let sreg;
    cfg_if::cfg_if! {
        if #[cfg(target_arch = "avr")] {
            // Store current state
            unsafe {
                asm!(
                    "in {sreg}, 0x3F",
                    sreg = out(reg) sreg,
                )
            };
        } else {
            let _ = sreg;
            unimplemented!()
        }
    }
    // Disable interrupts
    disable();

    IrqFlag::new(sreg)
}

/// Enable the global interrupt flag.
///
/// *Warning*: This function enables interrupts, no matter what the enable-state was before [crate::interrupt::disable].
///            Especially in library code, where the previous interrupt state may be unknown,
///            this function call shall be avoided.
///            Most of the time you probably don't want to use this function directly.
///            Consider creating a critical section with [crate::interrupt::free] instead.
///
/// This function is an optimization fence.
/// That means memory accesses will not be re-ordered by the compiler across this function call.
///
/// # Safety
///
/// - Do not call this function inside an [crate::interrupt::free] critical section
#[inline(always)]
pub unsafe fn enable() {
    cfg_if::cfg_if! {
        if #[cfg(target_arch = "avr")] {
            asm!("sei");
        } else {
            unimplemented!()
        }
    }
}

/// Restore the global interrupt flag to its previous state before [crate::interrupt::disable_save].
///
/// *Hint*: Most of the time you probably don't want to use this function directly.
///         Consider creating a critical section with [crate::interrupt::free] instead.
///
/// This function is an optimization fence.
/// That means memory accesses will not be re-ordered by the compiler across this function call.
///
/// # Safety
///
/// - If you call this function inside of a [crate::interrupt::free] critical section, the
///   corresponding [crate::interrupt::disable_save] must also be in the same critical section.
/// - If you nest multiple [crate::interrupt::disable_save] + [crate::interrupt::restore]
///   sequences, the [crate::interrupt::restore] must be called in the reverse order of the
///   [crate::interrupt::disable_save] call order.
///   That means the first saved IrqFlag must be restored last.
#[inline(always)]
pub unsafe fn restore(irq_flag: IrqFlag) {
    cfg_if::cfg_if! {
        if #[cfg(target_arch = "avr")] {
            // Restore global interrupt flag in SREG.
            // This also clobbers all other bits in SREG.
            asm!(
                "out 0x3F, {sreg}",
                sreg = in(reg) irq_flag.sreg,
            );
        } else {
            let _ = irq_flag;
            unimplemented!()
        }
    }
}

/// Check whether the global interrupt flag is currently enabled (in SREG).
///
/// *Warning*: You shouldn't use this to hand craft your own memory/interrupt safety mechanisms.
///            This function may be used for things such as deciding whether to do
///            expensive calculations in library code, or similar things.
///
/// This function is **not** an optimization fence.
/// That means memory accesses *can* be re-ordered by the compiler across this function call.
#[inline(always)]
#[allow(unreachable_code)]
pub fn is_enabled() -> bool {
    let sreg;
    cfg_if::cfg_if! {
        if #[cfg(target_arch = "avr")] {
            // Store current state
            unsafe {
                asm!(
                    "in {sreg}, 0x3F",
                    sreg = out(reg) sreg,
                    options(readonly, preserves_flags, nostack),
                )
            };
        } else {
            let _ = sreg;
            unimplemented!()
        }
    }

    IrqFlag::new(sreg).enabled()
}

/// Execute closure `f` in an interrupt-free context.
///
/// This is also known as a "critical section".
#[inline(always)]
pub fn free<F, R>(f: F) -> R
where
    F: FnOnce(CriticalSection) -> R,
{
    cfg_if::cfg_if! {
        if #[cfg(target_arch = "avr")] {
            // Disable interrupts. This is an optimization fence.
            let irq_flag = disable_save();

            let r = f(unsafe { CriticalSection::new() });

            // Restore interrupt state. This is an optimization fence.
            unsafe { restore(irq_flag); }

            r
        } else {
            let _ = f;
            unimplemented!()
        }
    }
}

#[cfg(feature = "critical-section-impl")]
mod cs {
    use critical_section::RawRestoreState;

    struct AvrCriticalSection;
    critical_section::set_impl!(AvrCriticalSection);

    unsafe impl critical_section::Impl for AvrCriticalSection {
        unsafe fn acquire() -> RawRestoreState {
            crate::interrupt::disable_save().sreg
        }

        unsafe fn release(restore_state: RawRestoreState) {
            crate::interrupt::restore(crate::interrupt::IrqFlag::new(restore_state))
        }
    }
}