aboutsummaryrefslogtreecommitdiff
path: root/embassy-time-driver/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-time-driver/src')
-rw-r--r--embassy-time-driver/src/lib.rs135
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)
71pub const TICK_HZ: u64 = tick::TICK_HZ; 62pub const TICK_HZ: u64 = tick::TICK_HZ;
72 63
73/// Alarm handle, assigned by the driver.
74#[derive(Clone, Copy)]
75pub struct AlarmHandle {
76 id: u8,
77}
78
79impl 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
96pub trait Driver: Send + Sync + 'static { 65pub 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
173extern "Rust" { 79extern "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.
188pub unsafe fn allocate_alarm() -> Option<AlarmHandle> {
189 _embassy_time_allocate_alarm()
190}
191
192/// See [`Driver::set_alarm_callback`]
193pub 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`]
198pub 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}