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
//! # `shared`
//!
//! A moderately low cost, easy to use, safe abstraction for sharing
//! data between application and interrupt context.
//!
//! ## Example
//!
//! ```rust
//! use nrf52832_pac::Interrupt;
//! use bare_metal;
//! use cortex_m;
//!
//! // Tuples are of the format:
//! //  (VARIABLE_NAME, VARIABLE_TYPE, CORRESPONDING_INTERRUPT),
//! shared!(
//!     (RADIO_PKTS, usize, Interrupt::RADIO),
//!     (WALL_CLOCK, usize, Interrupt::RTC0),
//! );
//!
//! #[entry]
//! fn main() {
//!     // Using a `shared` data item in non-interrupt context
//!     // requires a token. This is a singleton, sort of like
//!     // the peripherals from a peripheral access crate
//!     let mut token = RADIO_PKTS::set_initial(27).unwrap();
//!
//!     // You access the data from within a closure. The interrupt
//!     // this data is shared with is disabled for the duration of
//!     // the closure. Other interrupts may still occur.
//!     token.modify_app_context(|y| {
//!         *y -= 1;
//!         y
//!     }).unwrap();
//! }
//!
//! #[interrupt]
//! fn RADIO() {
//!     // Within an interrupt, access is only granted if it matches
//!     // the declared interrupt. Inside the `RADIO` interrupt here,
//!     // only `RADIO_PKTS` is accessible.
//!     //
//!     // Access from within an interrupt doesn't require a token.
//!     RADIO_PKTS::modify_int_context(|x| {
//!         *x += 1;
//!         x
//!     }).unwrap();
//! }
//!
//! #[interrupt]
//! fn RTC0() {
//!     // If `set_initial` was never called, then all attempts to
//!     // access will return an `Err`. This code would panic at
//!     // runtime!
//!     BAZ::modify_int_context(|x| {
//!         *x += 1;
//!         x
//!     }).unwrap();
//! }
//! ```

#![no_std]

#[macro_export]
macro_rules! shared {
    (
        $(($NAME:ident, $dat_ty:ty, $int:expr),)+
    ) => {
        /// Re-export all the structures at the top level, making them
        /// visible at the scope the macro was used (not necessarily global!)
        pub use shared_internals::structs::*;

        /// This module is basically just here to hide all of the stuff
        /// from being public
        #[doc(hidden)]
        pub mod shared_internals {

            /// These are the actual data structures that back the
            /// shared data
            mod singletons {
                $(
                    pub static mut $NAME: Option<$dat_ty> = None;
                )+
            }

            /// These flags are used to prevent re-entrant calls from within
            /// an interrupt
            mod flags {
                use ::core::sync::atomic::AtomicBool;
                $(
                    pub static $NAME: AtomicBool = AtomicBool::new(false);
                )+
            }

            /// This is the primary interface to the shared data. The struct itself
            /// is actually an opaque zero sized type, with methods that grab data
            /// from the `flags` and `singletons` modules
            pub mod structs {
                use ::core::sync::atomic::Ordering;
                use ::cortex_m::peripheral::NVIC;
                use ::bare_metal::Nr;

                // This is bad. I don't know how else to generically get
                // the interrupt enum provided by the -PAC though.
                // PRs welcome :)
                use super::super::Interrupt;

                $(
                    pub struct $NAME {
                        _private: ()
                    }

                    impl $NAME {
                        /// Set the initial value of the shared data. This must be done
                        /// from application context, not interrupt context.
                        ///
                        /// This function must be called before the `modify_*` methods
                        /// can be used, otherwise they will return errors.
                        pub fn set_initial(data: $dat_ty) -> Result<$NAME, $dat_ty> {
                            if int_is_enabled($int) || super::flags::$NAME.load(Ordering::SeqCst) {
                                return Err(data);
                            }

                            if unsafe { super::singletons::$NAME.is_none() } {
                                unsafe {
                                    super::singletons::$NAME = Some(data);
                                }
                                Ok($NAME { _private: () })
                            } else {
                                Err(data)
                            }
                        }

                        /// Access the shared data from the application (non-interrupt) context.
                        /// The interrupt must not be active when calling this function.
                        ///
                        /// During the scope of the closure, the corresponding interrupt will be
                        /// disabled to prevent concurrent access.
                        pub fn modify_app_context<F>(&mut self, f: F) -> Result<(), ()>
                        where
                            for<'w> F: FnOnce(&'w mut $dat_ty) -> &'w mut $dat_ty,
                        {
                            // theoretical race condition: if an interrupt enables this interrupt between
                            // the next line and the line after
                            let enabled = int_is_enabled($int);
                            if enabled {
                                disable_int($int);
                            }
                            if int_is_active($int) || unsafe { super::singletons::$NAME.is_none() } {
                                if enabled {
                                    enable_int($int);
                                }
                                return Err(());
                            }

                            unsafe {
                                f(super::singletons::$NAME.as_mut().unwrap());
                            }

                            if enabled {
                                enable_int($int);
                            }

                            Ok(())
                        }

                        /// Access the shared data from the interrupt context. This function will
                        /// only work if the corresponding interrupt is currently active. This
                        /// function is not re-entrant - you cannot grab the shared data more than
                        /// once.
                        pub fn modify_int_context<F>(f: F) -> Result<(), ()>
                        where
                            for<'w> F: FnOnce(&'w mut $dat_ty) -> &'w mut $dat_ty,
                        {
                            if !int_is_active($int) || unsafe { super::singletons::$NAME.is_none() } || super::flags::$NAME.swap(true, Ordering::SeqCst) {
                                return Err(());
                            }

                            unsafe {
                                f(super::singletons::$NAME.as_mut().unwrap());
                            }

                            assert!(super::flags::$NAME.swap(false, Ordering::SeqCst));
                            Ok(())

                        }
                    }
                )+

                /////////////////////////////////////////////////////////
                // This section comes from the cortex-m crate.
                //
                // Ideally, we wouldn't need to copy/paste code, but
                // I don't think it's possible to use these functions without
                // having a mutable reference to the NVIC, which would require
                // something taking ownership of it.
                //
                // PRs welcome if this could be done better!
                /////////////////////////////////////////////////////////

                /// This method comes from `cortex-m::NVIC`
                fn int_is_enabled<I>(interrupt: I) -> bool
                    where I: Nr,
                {
                    let nr = interrupt.nr();
                    let mask = 1 << (nr % 32);

                    // NOTE(unsafe) atomic read with no side effects
                    unsafe { ((*NVIC::ptr()).iser[usize::from(nr / 32)].read() & mask) == mask }
                }

                /// This method comes from `cortex-m::NVIC`
                fn int_is_active<I>(interrupt: I) -> bool
                    where I: Nr
                {
                    let nr = interrupt.nr();
                    let mask = 1 << (nr % 32);

                    // NOTE(unsafe) atomic read with no side effects
                    unsafe { ((*NVIC::ptr()).iabr[usize::from(nr / 32)].read() & mask) == mask }
                }

                /// This method comes from `cortex-m::NVIC`
                fn disable_int<I>(interrupt: I)
                    where I: Nr
                {
                    let nr = interrupt.nr();

                    unsafe { (*NVIC::ptr()).icer[usize::from(nr / 32)].write(1 << (nr % 32)) }
                }

                /// This method comes from `cortex-m::NVIC`
                fn enable_int<I>(interrupt: I)
                    where I: Nr
                {
                    let nr = interrupt.nr();

                    unsafe { (*NVIC::ptr()).iser[usize::from(nr / 32)].write(1 << (nr % 32)) }
                }
            }
        }
    }
}