Skip to content

Commit 1f3fc62

Browse files
committed
zephyr: Create Timer
Add a set of wrappers for Zephyr's timer. Timers start as `StoppedTimer`, which can be either dynamic or static (through object). This can then be started either with `start_simple` or `start_callback` which will return a `SimpleTimer` or a `CallbackTimer`. This can then be turned back into stopped timers with a `stop` method. Signed-off-by: David Brown <[email protected]>
1 parent f958ce6 commit 1f3fc62

File tree

2 files changed

+363
-0
lines changed

2 files changed

+363
-0
lines changed

zephyr/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod object;
1717
pub mod sync;
1818
pub mod sys;
1919
pub mod time;
20+
pub mod timer;
2021

2122
pub use error::{Error, Result};
2223

zephyr/src/timer.rs

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

0 commit comments

Comments
 (0)