diff options
Diffstat (limited to 'embassy-time-driver/src/lib.rs')
| -rw-r--r-- | embassy-time-driver/src/lib.rs | 135 |
1 files changed, 3 insertions, 132 deletions
diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 12f40b9b9..ffb363cd7 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs | |||
| @@ -21,8 +21,8 @@ | |||
| 21 | //! | 21 | //! |
| 22 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | 22 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. |
| 23 | //! | 23 | //! |
| 24 | //! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. | 24 | //! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. |
| 25 | //! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | 25 | //! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the |
| 26 | //! calls from the `embassy` crate to call into the driver crate. | 26 | //! calls from the `embassy` crate to call into the driver crate. |
| 27 | //! | 27 | //! |
| 28 | //! If there is none or multiple drivers in the crate tree, linking will fail. | 28 | //! If there is none or multiple drivers in the crate tree, linking will fail. |
| @@ -38,7 +38,7 @@ | |||
| 38 | //! # Example | 38 | //! # Example |
| 39 | //! | 39 | //! |
| 40 | //! ``` | 40 | //! ``` |
| 41 | //! use embassy_time_driver::{Driver, AlarmHandle}; | 41 | //! use embassy_time_driver::Driver; |
| 42 | //! | 42 | //! |
| 43 | //! struct MyDriver{} // not public! | 43 | //! struct MyDriver{} // not public! |
| 44 | //! | 44 | //! |
| @@ -46,15 +46,6 @@ | |||
| 46 | //! fn now(&self) -> u64 { | 46 | //! fn now(&self) -> u64 { |
| 47 | //! todo!() | 47 | //! todo!() |
| 48 | //! } | 48 | //! } |
| 49 | //! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 50 | //! todo!() | ||
| 51 | //! } | ||
| 52 | //! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 53 | //! todo!() | ||
| 54 | //! } | ||
| 55 | //! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | ||
| 56 | //! todo!() | ||
| 57 | //! } | ||
| 58 | //! } | 49 | //! } |
| 59 | //! | 50 | //! |
| 60 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); | 51 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); |
| @@ -70,28 +61,6 @@ mod tick; | |||
| 70 | /// This value is specified by the [`tick-*` Cargo features](crate#tick-rate) | 61 | /// This value is specified by the [`tick-*` Cargo features](crate#tick-rate) |
| 71 | pub const TICK_HZ: u64 = tick::TICK_HZ; | 62 | pub const TICK_HZ: u64 = tick::TICK_HZ; |
| 72 | 63 | ||
| 73 | /// Alarm handle, assigned by the driver. | ||
| 74 | #[derive(Clone, Copy)] | ||
| 75 | pub struct AlarmHandle { | ||
| 76 | id: u8, | ||
| 77 | } | ||
| 78 | |||
| 79 | impl AlarmHandle { | ||
| 80 | /// Create an AlarmHandle | ||
| 81 | /// | ||
| 82 | /// Safety: May only be called by the current global Driver impl. | ||
| 83 | /// The impl is allowed to rely on the fact that all `AlarmHandle` instances | ||
| 84 | /// are created by itself in unsafe code (e.g. indexing operations) | ||
| 85 | pub unsafe fn new(id: u8) -> Self { | ||
| 86 | Self { id } | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Get the ID of the AlarmHandle. | ||
| 90 | pub fn id(&self) -> u8 { | ||
| 91 | self.id | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Time driver | 64 | /// Time driver |
| 96 | pub trait Driver: Send + Sync + 'static { | 65 | pub trait Driver: Send + Sync + 'static { |
| 97 | /// Return the current timestamp in ticks. | 66 | /// Return the current timestamp in ticks. |
| @@ -105,76 +74,10 @@ pub trait Driver: Send + Sync + 'static { | |||
| 105 | /// you MUST extend them to 64-bit, for example by counting overflows in software, | 74 | /// you MUST extend them to 64-bit, for example by counting overflows in software, |
| 106 | /// or chaining multiple timers together. | 75 | /// or chaining multiple timers together. |
| 107 | fn now(&self) -> u64; | 76 | fn now(&self) -> u64; |
| 108 | |||
| 109 | /// Try allocating an alarm handle. Returns None if no alarms left. | ||
| 110 | /// Initially the alarm has no callback set, and a null `ctx` pointer. | ||
| 111 | /// | ||
| 112 | /// The allocated alarm is a reusable resource and can be used multiple times. | ||
| 113 | /// Once the alarm has fired, it remains allocated and can be set again without needing | ||
| 114 | /// to be reallocated. | ||
| 115 | /// | ||
| 116 | /// # Safety | ||
| 117 | /// It is UB to make the alarm fire before setting a callback. | ||
| 118 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>; | ||
| 119 | |||
| 120 | /// Set the callback function to be called when the alarm triggers. | ||
| 121 | /// The callback may be called from any context (interrupt or thread mode). | ||
| 122 | /// | ||
| 123 | /// The callback is maintained after the alarm has fired. Callers do not need | ||
| 124 | /// to set a callback again before setting another alarm, unless they want to | ||
| 125 | /// change the callback function or context. | ||
| 126 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | ||
| 127 | |||
| 128 | /// Set an alarm at the given timestamp. | ||
| 129 | /// | ||
| 130 | /// ## Behavior | ||
| 131 | /// | ||
| 132 | /// If `timestamp` is in the future, `set_alarm` schedules calling the callback function | ||
| 133 | /// at that time, and returns `true`. | ||
| 134 | /// | ||
| 135 | /// If `timestamp` is in the past, `set_alarm` has two allowed behaviors. Implementations can pick whether to: | ||
| 136 | /// | ||
| 137 | /// - Schedule calling the callback function "immediately", as if the requested timestamp was "now+epsilon" and return `true`, or | ||
| 138 | /// - Not schedule the callback, and return `false`. | ||
| 139 | /// | ||
| 140 | /// Callers must ensure to behave correctly with either behavior. | ||
| 141 | /// | ||
| 142 | /// When callback is called, it is guaranteed that `now()` will return a value greater than or equal to `timestamp`. | ||
| 143 | /// | ||
| 144 | /// ## Reentrancy | ||
| 145 | /// | ||
| 146 | /// Calling the callback from `set_alarm` synchronously is not allowed. If the implementation chooses the first option above, | ||
| 147 | /// it must still call the callback from another context (i.e. an interrupt handler or background thread), it's not allowed | ||
| 148 | /// to call it synchronously in the context `set_alarm` is running. | ||
| 149 | /// | ||
| 150 | /// The reason for the above is callers are explicitly permitted to do both of: | ||
| 151 | /// - Lock a mutex in the alarm callback. | ||
| 152 | /// - Call `set_alarm` while having that mutex locked. | ||
| 153 | /// | ||
| 154 | /// If `set_alarm` called the callback synchronously, it'd cause a deadlock or panic because it'd cause the | ||
| 155 | /// mutex to be locked twice reentrantly in the same context. | ||
| 156 | /// | ||
| 157 | /// ## Overwriting alarms | ||
| 158 | /// | ||
| 159 | /// Only one alarm can be active at a time for each `AlarmHandle`. This overwrites any previously-set alarm if any. | ||
| 160 | /// | ||
| 161 | /// ## Unsetting the alarm | ||
| 162 | /// | ||
| 163 | /// There is no `unset_alarm` API. Instead, callers can call `set_alarm` with `timestamp` set to `u64::MAX`. | ||
| 164 | /// | ||
| 165 | /// This allows for more efficient implementations, since they don't need to distinguish between the "alarm set" and | ||
| 166 | /// "alarm not set" cases, thanks to the fact "Alarm set for u64::MAX" is effectively equivalent for "alarm not set". | ||
| 167 | /// | ||
| 168 | /// This means implementations need to be careful to avoid timestamp overflows. The recommendation is to make `timestamp` | ||
| 169 | /// be in the same units as hardware ticks to avoid any conversions, which makes avoiding overflow easier. | ||
| 170 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool; | ||
| 171 | } | 77 | } |
| 172 | 78 | ||
| 173 | extern "Rust" { | 79 | extern "Rust" { |
| 174 | fn _embassy_time_now() -> u64; | 80 | fn _embassy_time_now() -> u64; |
| 175 | fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>; | ||
| 176 | fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | ||
| 177 | fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool; | ||
| 178 | } | 81 | } |
| 179 | 82 | ||
| 180 | /// See [`Driver::now`] | 83 | /// See [`Driver::now`] |
| @@ -182,23 +85,6 @@ pub fn now() -> u64 { | |||
| 182 | unsafe { _embassy_time_now() } | 85 | unsafe { _embassy_time_now() } |
| 183 | } | 86 | } |
| 184 | 87 | ||
| 185 | /// See [`Driver::allocate_alarm`] | ||
| 186 | /// | ||
| 187 | /// Safety: it is UB to make the alarm fire before setting a callback. | ||
| 188 | pub unsafe fn allocate_alarm() -> Option<AlarmHandle> { | ||
| 189 | _embassy_time_allocate_alarm() | ||
| 190 | } | ||
| 191 | |||
| 192 | /// See [`Driver::set_alarm_callback`] | ||
| 193 | pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 194 | unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) } | ||
| 195 | } | ||
| 196 | |||
| 197 | /// See [`Driver::set_alarm`] | ||
| 198 | pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool { | ||
| 199 | unsafe { _embassy_time_set_alarm(alarm, timestamp) } | ||
| 200 | } | ||
| 201 | |||
| 202 | /// Set the time Driver implementation. | 88 | /// Set the time Driver implementation. |
| 203 | /// | 89 | /// |
| 204 | /// See the module documentation for an example. | 90 | /// See the module documentation for an example. |
| @@ -211,20 +97,5 @@ macro_rules! time_driver_impl { | |||
| 211 | fn _embassy_time_now() -> u64 { | 97 | fn _embassy_time_now() -> u64 { |
| 212 | <$t as $crate::Driver>::now(&$name) | 98 | <$t as $crate::Driver>::now(&$name) |
| 213 | } | 99 | } |
| 214 | |||
| 215 | #[no_mangle] | ||
| 216 | unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::AlarmHandle> { | ||
| 217 | <$t as $crate::Driver>::allocate_alarm(&$name) | ||
| 218 | } | ||
| 219 | |||
| 220 | #[no_mangle] | ||
| 221 | fn _embassy_time_set_alarm_callback(alarm: $crate::AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 222 | <$t as $crate::Driver>::set_alarm_callback(&$name, alarm, callback, ctx) | ||
| 223 | } | ||
| 224 | |||
| 225 | #[no_mangle] | ||
| 226 | fn _embassy_time_set_alarm(alarm: $crate::AlarmHandle, timestamp: u64) -> bool { | ||
| 227 | <$t as $crate::Driver>::set_alarm(&$name, alarm, timestamp) | ||
| 228 | } | ||
| 229 | }; | 100 | }; |
| 230 | } | 101 | } |
