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}