zephyr/timer.rs
1// Copyright (c) 2024 EOVE SAS
2// Copyright (c) 2024 Linaro LTD
3// SPDX-License-Identifier: Apache-2.0
4
5//! Zephyr timers
6//!
7//! This provides a relatively high-level and almost safe interface to Zephyr's underlying
8//! `k_timer`.
9//!
10//! Every timer starts as a [`StoppedTimer`], which has been allocated, but is not tracking any
11//! time. These can either be created through [`StoppedTimer::new`], or by calling `.init_once(())` on a
12//! StaticStoppedTimer declared within the `kobject_define!` macro, from [`object`].
13//!
14//! The `StoppedTimer` has two methods of interest here:
15//!
16//! - [`start_simple`]: which starts the timer. This timer has methods for robustly getting counts
17//! of the number of times it has fired, as well as blocking the current thread for the timer to
18//! expire.
19//! - [`start_callback`]: which starts the timer, registering a callback handler. This timer will
20//! (unsafely) call the callback function, from IRQ context, every time the timer expires.
21//!
22//! Both of these returned timer types [`SimpleTimer`] and [`CallbackTimer`] have a [`stop`] method
23//! that will stop the timer, and give back the original `StoppedTimer`.
24//!
25//! All of the types implement `Drop` and can dynamic timers can be safely dropped. It is safe to
26//! drop a timer allocated through the static object system, but it will then not be possible to
27//! re-use that timer.
28//!
29//! [`object`]: crate::object
30//! [`start_simple`]: StoppedTimer::start_simple
31//! [`start_callback`]: StoppedTimer::start_callback
32//! [`stop`]: SimpleTimer::stop
33
34extern crate alloc;
35
36#[cfg(CONFIG_RUST_ALLOC)]
37use alloc::boxed::Box;
38
39use core::ffi::c_void;
40use core::marker::PhantomPinned;
41use core::pin::Pin;
42use core::{fmt, mem};
43
44use crate::object::{Fixed, StaticKernelObject, Wrapped};
45use crate::raw::{
46 k_timer, k_timer_init, k_timer_start, k_timer_status_get, k_timer_status_sync, k_timer_stop,
47 k_timer_user_data_get, k_timer_user_data_set,
48};
49use crate::time::Timeout;
50
51/// A Zephyr timer that is not running.
52///
53/// A basic timer, allocated, but not running.
54pub struct StoppedTimer {
55 /// The underlying Zephyr timer.
56 item: Fixed<k_timer>,
57}
58
59impl StoppedTimer {
60 /// Construct a new timer.
61 ///
62 /// Allocates a dynamically allocate timer. The time will not be running.
63 #[cfg(CONFIG_RUST_ALLOC)]
64 pub fn new() -> Self {
65 let item: Fixed<k_timer> = Fixed::new(unsafe { mem::zeroed() });
66 unsafe {
67 // SAFETY: The `Fixed` type takes care of ensuring the timer is allocate at a fixed or
68 // pinned address.
69 k_timer_init(item.get(), None, None);
70 }
71 StoppedTimer { item }
72 }
73
74 /// Start the timer, in "simple" mode.
75 ///
76 /// Returns the [`SimpleTimer`] representing the running timer. The `delay` specifies the
77 /// amount of time before the first expiration happens. `period` gives the time of subsequent
78 /// timer expirations. If `period` is [`NoWait`] or [`Forever`], then the timer will be
79 /// one-shot
80 ///
81 /// [`NoWait`]: crate::time::NoWait
82 /// [`Forever`]: crate::time::Forever
83 pub fn start_simple(
84 self,
85 delay: impl Into<Timeout>,
86 period: impl Into<Timeout>,
87 ) -> SimpleTimer {
88 unsafe {
89 // SAFETY: The timer will be registered with Zephyr, using fields within the struct.
90 // The `Fixed` type takes care of ensuring that the memory is not used. Drop will call
91 // `stop` to ensure that the timer is unregistered before the memory is returned.
92 k_timer_start(self.item.get(), delay.into().0, period.into().0);
93 }
94
95 SimpleTimer {
96 item: Some(self.item),
97 }
98 }
99
100 /// Start the timer in "callback" mode.
101 ///
102 /// Returns the [`CallbackTimer`] representing the running timer. The `delay` specifies the
103 /// amount of time before the first expiration happens. `period` gives the time of subsequent
104 /// timer expirations. If `period` is [`NoWait`] or [`Forever`], then the timer will be one
105 /// shot.
106 ///
107 /// Each time the timer expires, The callback function given by the `Callback` will be called
108 /// from IRQ context. Much of Zephyr's API is unavailable from within IRQ context. Some useful
109 /// things to use are data that is wrapped in a [`SpinMutex`], a channel [`Sender`] from a
110 /// bounded channel, or a [`Semaphore`], which can has it's `give` method available from IRQ
111 /// context.
112 ///
113 /// Because the callback is registered with Zephyr, the resulting CallbackTimer must be pinned.
114 ///
115 /// [`NoWait`]: crate::time::NoWait
116 /// [`Forever`]: crate::time::Forever
117 /// [`SpinMutex`]: crate::sync::SpinMutex
118 /// [`Semaphore`]: crate::sys::sync::Semaphore
119 /// [`Sender`]: crate::sync::channel::Sender
120 pub fn start_callback<T>(
121 self,
122 callback: Callback<T>,
123 delay: impl Into<Timeout>,
124 period: impl Into<Timeout>,
125 ) -> Pin<Box<CallbackTimer<T>>>
126 where
127 T: Send + Sync,
128 {
129 CallbackTimer::new(self, callback, delay, period)
130 }
131}
132
133impl fmt::Debug for StoppedTimer {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 write!(f, "StoppedTimer {:?}", self.item.get())
136 }
137}
138
139impl Default for StoppedTimer {
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145/// A statically allocated `k_timer` (StoppedTimer).
146///
147/// This is intended to be used from within the `kobj_define!` macro. It declares a static
148/// `k_timer` that will be properly registered with the Zephyr object system (and can be used from
149/// userspace). Call `[init_once`] to get the `StoppedTimer` that it represents.
150///
151/// [`init_once`]: StaticStoppedTimer::init_once
152pub type StaticStoppedTimer = StaticKernelObject<k_timer>;
153
154// SAFETY: The timer itself is not associated with any particular thread, but it is unclear if they
155// are safe to use from multiple threads. As such, we'll declare this as Send, !Sync.
156unsafe impl Send for StoppedTimer {}
157
158impl Wrapped for StaticKernelObject<k_timer> {
159 type T = StoppedTimer;
160
161 /// No initializers.
162 type I = ();
163
164 fn get_wrapped(&self, _arg: Self::I) -> StoppedTimer {
165 let ptr = self.value.get();
166 unsafe {
167 // SAFETY: The ptr is static, so it is safe to have Zephyr initialize. The callback is
168 // safe as it checks if the user data has been set. The callback is needed for the
169 // callback version of the timer.
170 k_timer_init(ptr, None, None);
171 }
172 StoppedTimer {
173 item: Fixed::Static(ptr),
174 }
175 }
176}
177
178/// A simple timer.
179///
180/// A SimpleTimer represents a running Zephyr `k_timer` that does not have a callback registered.
181/// It can only be created by calling [`StoppedTimer::start_simple`].
182pub struct SimpleTimer {
183 /// The underlying Zephyr timer. Option is needed to coordinate 'stop' and 'drop'.
184 item: Option<Fixed<k_timer>>,
185}
186
187impl SimpleTimer {
188 /// Read the count from the timer.
189 ///
190 /// Returns the number of times the timer has fired since the last time either this method or
191 /// [`read_count_wait`] was called.
192 ///
193 /// This works via an internal counter, that is atomically reset to zero when the current value
194 /// of the counter is read.
195 ///
196 /// [`read_count_wait`]: Self::read_count_wait
197 pub fn read_count(&mut self) -> u32 {
198 unsafe {
199 // SAFETY: As long as the timer's data is allocated, this call is safe in Zephyr.
200 k_timer_status_get(self.item_ptr())
201 }
202 }
203
204 /// Read the count from the timer, waiting for it to become non-zero.
205 ///
206 /// Blocks the current thread until the timer has fired at least once since the last call to
207 /// this method or [`read_count`]. Once it has fired, will return the count. This will return
208 /// immediately if the timer has already fired once since the last time.
209 ///
210 /// [`read_count`]: Self::read_count
211 pub fn read_count_wait(&mut self) -> u32 {
212 unsafe {
213 // SAFETY: As long as the timer's data is allocated, this call is safe in Zephyr.
214 k_timer_status_sync(self.item_ptr())
215 }
216 }
217
218 /// Restart the current timer.
219 ///
220 /// This resets the fired counter back to zero, and sets a new `delay` and `period` for the
221 /// timer. It is mostly equivalent to `self.stop().start_simple(delay, period)`, but saves the
222 /// step of having to stop the timer.
223 pub fn restart(&mut self, delay: impl Into<Timeout>, period: impl Into<Timeout>) {
224 unsafe {
225 // SAFETY: According to zephyr docs, it is safe to `start` a running timer, and the
226 // behavior is as described here.
227 k_timer_start(self.item_ptr(), delay.into().0, period.into().0);
228 }
229 }
230
231 /// Get the item pointer, assuming it is still present.
232 fn item_ptr(&self) -> *mut k_timer {
233 self.item
234 .as_ref()
235 .expect("Use of SimpleTimer after stop")
236 .get()
237 }
238
239 /// Stop the timer.
240 ///
241 /// Stops the timer, so that it will not fire any more, converting the timer back into a
242 /// StoppedTimer.
243 pub fn stop(mut self) -> StoppedTimer {
244 // Actually do the stop.
245 let item = self.raw_stop();
246
247 let item = item.expect("Error in stop/drop interaction");
248
249 StoppedTimer { item }
250 }
251
252 /// Attempt to stop the timer, if it is still present. Returns the possible inner item.
253 fn raw_stop(&mut self) -> Option<Fixed<k_timer>> {
254 let item = self.item.take();
255 if let Some(ref item) = item {
256 unsafe {
257 // SAFETY: This call, in Zephyr, removes the timer from any queues. There must also
258 // not be any threads blocked on `read_count_wait`, which will be the case because
259 // this is `self` and there can be no other references to the timer in Rust.
260 k_timer_stop(item.get())
261 }
262 }
263 item
264 }
265}
266
267impl Drop for SimpleTimer {
268 fn drop(&mut self) {
269 // Stop the timer, discarding the inner item.
270 let _ = self.raw_stop();
271 }
272}
273
274/// A timer callback. The function will be called in IRQ context being passed the given data.
275/// Note that this handler owns the data, but passes a reference to the handler. This will
276/// typically be a `SpinMutex` to allow for proper sharing with IRQ context.
277pub struct Callback<T: Send + Sync> {
278 /// The callback function.
279 pub call: fn(data: &T),
280 /// The data passed into the callback.
281 pub data: T,
282}
283
284/// A zephyr timer that calls a callback each time the timer expires.
285///
286/// Each time the timer fires, the callback will be called. It is important to note the data
287/// associated with the timer must be both `Send` and `Sync`. As the callback will be called from
288/// interrupt context, a normal `Mutex` cannot be used. For this purpose, there is a [`SpinMutex`]
289/// type that protects the data with a spin lock. Other useful things a pass as data to the
290/// callback are [`Sender`] from a bounded channel, and a [`Semaphore`].
291///
292/// [`SpinMutex`]: crate::sync::SpinMutex
293/// [`Sender`]: crate::sync::channel::Sender
294/// [`Semaphore`]: crate::sys::sync::Semaphore
295pub struct CallbackTimer<T: Send + Sync> {
296 /// The underlying Zephyr timer.
297 item: Option<Fixed<k_timer>>,
298
299 /// The callback used for expiry.
300 expiry: Callback<T>,
301
302 /// Marker to prevent unpinning.
303 _marker: PhantomPinned,
304}
305
306impl<T: Send + Sync> CallbackTimer<T> {
307 fn new(
308 item: StoppedTimer,
309 callback: Callback<T>,
310 delay: impl Into<Timeout>,
311 period: impl Into<Timeout>,
312 ) -> Pin<Box<CallbackTimer<T>>> {
313 let this = Box::pin(CallbackTimer {
314 item: Some(item.item),
315 expiry: callback,
316 _marker: PhantomPinned,
317 });
318
319 // Set the timer's expiry function.
320 unsafe {
321 // SAFETY: The timer is not running as this came from a stopped timer. Therefore there
322 // are no races with timers potentially using the callback function.
323 //
324 // After we set the expiry function, the timer will be started with `k_timer_start`,
325 // which includes the necessary memory barrier to that the timer irq will see the updated
326 // callback function and user data.
327 let item_ptr = this.item_ptr();
328 (*item_ptr).expiry_fn = Some(Self::timer_expiry);
329 let raw = &this.expiry as *const _ as *const c_void;
330 k_timer_user_data_set(item_ptr, raw as *mut c_void);
331
332 k_timer_start(item_ptr, delay.into().0, period.into().0);
333 }
334
335 this
336 }
337
338 /// The timer callback. Called in IRQ context, by Zephyr.
339 unsafe extern "C" fn timer_expiry(ktimer: *mut k_timer) {
340 // The user data comes back from Zephyr as a `* mut`, even though that is not sound.
341 let data = unsafe {
342 // SAFETY: The user data pointer was set above to the pinned expiry. It will be
343 // unregistered, as set as null when drop is called. Although the timer will also be
344 // stopped, the callback should be safe as this function checks.
345 k_timer_user_data_get(ktimer)
346 };
347 if data.is_null() {
348 return;
349 }
350 let cb: &Callback<T> = &*(data as *const Callback<T>);
351 (cb.call)(&cb.data);
352 }
353
354 /// Get the item pointer, assuming it is still present.
355 fn item_ptr(&self) -> *mut k_timer {
356 self.item
357 .as_ref()
358 .expect("Use of SimpleTimer after stop")
359 .get()
360 }
361
362 /// Stop the timer.
363 ///
364 /// Stops the timer, so that it will not fire any more, converting the timer back into a
365 /// StoppedTimer.
366 pub fn stop(mut self) -> StoppedTimer {
367 // Actually do the stop.
368 let item = self.raw_stop();
369
370 let item = item.expect("Error in stop/drop interaction");
371
372 StoppedTimer { item }
373 }
374
375 /// Stop the timer. Returns the inner item.
376 fn raw_stop(&mut self) -> Option<Fixed<k_timer>> {
377 let item = self.item.take();
378 if let Some(ref item) = item {
379 unsafe {
380 // SAFETY: Stopping the timer removes it from any queues. There must not be threads
381 // blocked, which is enforced by this only being called from either `stop` or
382 // `drop`. Once this has been stopped, it is then safe to remove the callback. As
383 // there will be no more timer operations until the timer is restarted, which will
384 // have a barrier, this is also safe.
385 let raw_item = item.get();
386 k_timer_stop(raw_item);
387 (*raw_item).expiry_fn = None;
388 }
389 }
390 item
391 }
392}
393
394impl<T: Send + Sync> Drop for CallbackTimer<T> {
395 fn drop(&mut self) {
396 // Stop the timer, discarding the inner item.
397 let _ = self.raw_stop();
398 }
399}