zephyr/
timer.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
// Copyright (c) 2024 EOVE SAS
// Copyright (c) 2024 Linaro LTD
// SPDX-License-Identifier: Apache-2.0

//! Zephyr timers
//!
//! This provides a relatively high-level and almost safe interface to Zephyr's underlying
//! `k_timer`.
//!
//! Every timer starts as a [`StoppedTimer`], which has been allocated, but is not tracking any
//! time.  These can either be created through [`StoppedTimer::new`], or by calling `.init_once(())` on a
//! StaticStoppedTimer declared within the `kobject_define!` macro, from [`object`].
//!
//! The `StoppedTimer` has two methods of interest here:
//!
//! - [`start_simple`]: which starts the timer.  This timer has methods for robustly getting counts
//!   of the number of times it has fired, as well as blocking the current thread for the timer to
//!   expire.
//! - [`start_callback`]: which starts the timer, registering a callback handler.  This timer will
//!   (unsafely) call the callback function, from IRQ context, every time the timer expires.
//!
//! Both of these returned timer types [`SimpleTimer`] and [`CallbackTimer`] have a [`stop`] method
//! that will stop the timer, and give back the original `StoppedTimer`.
//!
//! All of the types implement `Drop` and can dynamic timers can be safely dropped.  It is safe to
//! drop a timer allocated through the static object system, but it will then not be possible to
//! re-use that timer.
//!
//! [`object`]: crate::object
//! [`start_simple`]: StoppedTimer::start_simple
//! [`start_callback`]: StoppedTimer::start_callback
//! [`stop`]: SimpleTimer::stop

extern crate alloc;

#[cfg(CONFIG_RUST_ALLOC)]
use alloc::boxed::Box;

use core::ffi::c_void;
use core::marker::PhantomPinned;
use core::pin::Pin;
use core::{fmt, mem};

use crate::object::{Fixed, StaticKernelObject, Wrapped};
use crate::raw::{
    k_timer, k_timer_init, k_timer_start, k_timer_status_get, k_timer_status_sync, k_timer_stop,
    k_timer_user_data_get, k_timer_user_data_set,
};
use crate::time::Timeout;

/// A Zephyr timer that is not running.
///
/// A basic timer, allocated, but not running.
pub struct StoppedTimer {
    /// The underlying Zephyr timer.
    item: Fixed<k_timer>,
}

impl StoppedTimer {
    /// Construct a new timer.
    ///
    /// Allocates a dynamically allocate timer.  The time will not be running.
    #[cfg(CONFIG_RUST_ALLOC)]
    pub fn new() -> Self {
        let item: Fixed<k_timer> = Fixed::new(unsafe { mem::zeroed() });
        unsafe {
            // SAFETY: The `Fixed` type takes care of ensuring the timer is allocate at a fixed or
            // pinned address.
            k_timer_init(item.get(), None, None);
        }
        StoppedTimer { item }
    }

    /// Start the timer, in "simple" mode.
    ///
    /// Returns the [`SimpleTimer`] representing the running timer.  The `delay` specifies the
    /// amount of time before the first expiration happens.  `period` gives the time of subsequent
    /// timer expirations.  If `period` is [`NoWait`] or [`Forever`], then the timer will be
    /// one-shot
    ///
    /// [`NoWait`]: crate::time::NoWait
    /// [`Forever`]: crate::time::Forever
    pub fn start_simple(
        self,
        delay: impl Into<Timeout>,
        period: impl Into<Timeout>,
    ) -> SimpleTimer {
        unsafe {
            // SAFETY: The timer will be registered with Zephyr, using fields within the struct.
            // The `Fixed` type takes care of ensuring that the memory is not used.  Drop will call
            // `stop` to ensure that the timer is unregistered before the memory is returned.
            k_timer_start(self.item.get(), delay.into().0, period.into().0);
        }

        SimpleTimer {
            item: Some(self.item),
        }
    }

    /// Start the timer in "callback" mode.
    ///
    /// Returns the [`CallbackTimer`] representing the running timer.  The `delay` specifies the
    /// amount of time before the first expiration happens.  `period` gives the time of subsequent
    /// timer expirations.  If `period` is [`NoWait`] or [`Forever`], then the timer will be one
    /// shot.
    ///
    /// Each time the timer expires, The callback function given by the `Callback` will be called
    /// from IRQ context.  Much of Zephyr's API is unavailable from within IRQ context.  Some useful
    /// things to use are data that is wrapped in a [`SpinMutex`], a channel [`Sender`] from a
    /// bounded channel, or a [`Semaphore`], which can has it's `give` method available from IRQ
    /// context.
    ///
    /// Because the callback is registered with Zephyr, the resulting CallbackTimer must be pinned.
    ///
    /// [`NoWait`]: crate::time::NoWait
    /// [`Forever`]: crate::time::Forever
    /// [`SpinMutex`]: crate::sync::SpinMutex
    /// [`Semaphore`]: crate::sys::sync::Semaphore
    /// [`Sender`]: crate::sync::channel::Sender
    pub fn start_callback<T>(
        self,
        callback: Callback<T>,
        delay: impl Into<Timeout>,
        period: impl Into<Timeout>,
    ) -> Pin<Box<CallbackTimer<T>>>
    where
        T: Send + Sync,
    {
        CallbackTimer::new(self, callback, delay, period)
    }
}

impl fmt::Debug for StoppedTimer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "StoppedTimer {:?}", self.item.get())
    }
}

impl Default for StoppedTimer {
    fn default() -> Self {
        Self::new()
    }
}

/// A statically allocated `k_timer` (StoppedTimer).
///
/// This is intended to be used from within the `kobj_define!` macro.  It declares a static
/// `k_timer` that will be properly registered with the Zephyr object system (and can be used from
/// userspace).  Call `[init_once`] to get the `StoppedTimer` that it represents.
///
/// [`init_once`]: StaticStoppedTimer::init_once
pub type StaticStoppedTimer = StaticKernelObject<k_timer>;

// SAFETY: The timer itself is not associated with any particular thread, but it is unclear if they
// are safe to use from multiple threads.  As such, we'll declare this as Send, !Sync.
unsafe impl Send for StoppedTimer {}

impl Wrapped for StaticKernelObject<k_timer> {
    type T = StoppedTimer;

    /// No initializers.
    type I = ();

    fn get_wrapped(&self, _arg: Self::I) -> StoppedTimer {
        let ptr = self.value.get();
        unsafe {
            // SAFETY: The ptr is static, so it is safe to have Zephyr initialize.  The callback is
            // safe as it checks if the user data has been set.  The callback is needed for the
            // callback version of the timer.
            k_timer_init(ptr, None, None);
        }
        StoppedTimer {
            item: Fixed::Static(ptr),
        }
    }
}

/// A simple timer.
///
/// A SimpleTimer represents a running Zephyr `k_timer` that does not have a callback registered.
/// It can only be created by calling [`StoppedTimer::start_simple`].
pub struct SimpleTimer {
    /// The underlying Zephyr timer.  Option is needed to coordinate 'stop' and 'drop'.
    item: Option<Fixed<k_timer>>,
}

impl SimpleTimer {
    /// Read the count from the timer.
    ///
    /// Returns the number of times the timer has fired since the last time either this method or
    /// [`read_count_wait`] was called.
    ///
    /// This works via an internal counter, that is atomically reset to zero when the current value
    /// of the counter is read.
    ///
    /// [`read_count_wait`]: Self::read_count_wait
    pub fn read_count(&mut self) -> u32 {
        unsafe {
            // SAFETY: As long as the timer's data is allocated, this call is safe in Zephyr.
            k_timer_status_get(self.item_ptr())
        }
    }

    /// Read the count from the timer, waiting for it to become non-zero.
    ///
    /// Blocks the current thread until the timer has fired at least once since the last call to
    /// this method or [`read_count`].  Once it has fired, will return the count.  This will return
    /// immediately if the timer has already fired once since the last time.
    ///
    /// [`read_count`]: Self::read_count
    pub fn read_count_wait(&mut self) -> u32 {
        unsafe {
            // SAFETY: As long as the timer's data is allocated, this call is safe in Zephyr.
            k_timer_status_sync(self.item_ptr())
        }
    }

    /// Restart the current timer.
    ///
    /// This resets the fired counter back to zero, and sets a new `delay` and `period` for the
    /// timer.  It is mostly equivalent to `self.stop().start_simple(delay, period)`, but saves the
    /// step of having to stop the timer.
    pub fn restart(&mut self, delay: impl Into<Timeout>, period: impl Into<Timeout>) {
        unsafe {
            // SAFETY: According to zephyr docs, it is safe to `start` a running timer, and the
            // behavior is as described here.
            k_timer_start(self.item_ptr(), delay.into().0, period.into().0);
        }
    }

    /// Get the item pointer, assuming it is still present.
    fn item_ptr(&self) -> *mut k_timer {
        self.item
            .as_ref()
            .expect("Use of SimpleTimer after stop")
            .get()
    }

    /// Stop the timer.
    ///
    /// Stops the timer, so that it will not fire any more, converting the timer back into a
    /// StoppedTimer.
    pub fn stop(mut self) -> StoppedTimer {
        // Actually do the stop.
        let item = self.raw_stop();

        let item = item.expect("Error in stop/drop interaction");

        StoppedTimer { item }
    }

    /// Attempt to stop the timer, if it is still present.  Returns the possible inner item.
    fn raw_stop(&mut self) -> Option<Fixed<k_timer>> {
        let item = self.item.take();
        if let Some(ref item) = item {
            unsafe {
                // SAFETY: This call, in Zephyr, removes the timer from any queues.  There must also
                // not be any threads blocked on `read_count_wait`, which will be the case because
                // this is `self` and there can be no other references to the timer in Rust.
                k_timer_stop(item.get())
            }
        }
        item
    }
}

impl Drop for SimpleTimer {
    fn drop(&mut self) {
        // Stop the timer, discarding the inner item.
        let _ = self.raw_stop();
    }
}

/// A timer callback.  The function will be called in IRQ context being passed the given data.
/// Note that this handler owns the data, but passes a reference to the handler.  This will
/// typically be a `SpinMutex` to allow for proper sharing with IRQ context.
pub struct Callback<T: Send + Sync> {
    /// The callback function.
    pub call: fn(data: &T),
    /// The data passed into the callback.
    pub data: T,
}

/// A zephyr timer that calls a callback each time the timer expires.
///
/// Each time the timer fires, the callback will be called.  It is important to note the data
/// associated with the timer must be both `Send` and `Sync`.  As the callback will be called from
/// interrupt context, a normal `Mutex` cannot be used.  For this purpose, there is a [`SpinMutex`]
/// type that protects the data with a spin lock.  Other useful things a pass as data to the
/// callback are [`Sender`] from a bounded channel, and a [`Semaphore`].
///
/// [`SpinMutex`]: crate::sync::SpinMutex
/// [`Sender`]: crate::sync::channel::Sender
/// [`Semaphore`]: crate::sys::sync::Semaphore
pub struct CallbackTimer<T: Send + Sync> {
    /// The underlying Zephyr timer.
    item: Option<Fixed<k_timer>>,

    /// The callback used for expiry.
    expiry: Callback<T>,

    /// Marker to prevent unpinning.
    _marker: PhantomPinned,
}

impl<T: Send + Sync> CallbackTimer<T> {
    fn new(
        item: StoppedTimer,
        callback: Callback<T>,
        delay: impl Into<Timeout>,
        period: impl Into<Timeout>,
    ) -> Pin<Box<CallbackTimer<T>>> {
        let this = Box::pin(CallbackTimer {
            item: Some(item.item),
            expiry: callback,
            _marker: PhantomPinned,
        });

        // Set the timer's expiry function.
        unsafe {
            // SAFETY: The timer is not running as this came from a stopped timer.  Therefore there
            // are no races with timers potentially using the callback function.
            //
            // After we set the expiry function, the timer will be started with `k_timer_start`,
            // which includes the necessary memory barrier to that the timer irq will see the updated
            // callback function and user data.
            let item_ptr = this.item_ptr();
            (*item_ptr).expiry_fn = Some(Self::timer_expiry);
            let raw = &this.expiry as *const _ as *const c_void;
            k_timer_user_data_set(item_ptr, raw as *mut c_void);

            k_timer_start(item_ptr, delay.into().0, period.into().0);
        }

        this
    }

    /// The timer callback.  Called in IRQ context, by Zephyr.
    unsafe extern "C" fn timer_expiry(ktimer: *mut k_timer) {
        // The user data comes back from Zephyr as a `* mut`, even though that is not sound.
        let data = unsafe {
            // SAFETY: The user data pointer was set above to the pinned expiry.  It will be
            // unregistered, as set as null when drop is called.  Although the timer will also be
            // stopped, the callback should be safe as this function checks.
            k_timer_user_data_get(ktimer)
        };
        if data.is_null() {
            return;
        }
        let cb: &Callback<T> = &*(data as *const Callback<T>);
        (cb.call)(&cb.data);
    }

    /// Get the item pointer, assuming it is still present.
    fn item_ptr(&self) -> *mut k_timer {
        self.item
            .as_ref()
            .expect("Use of SimpleTimer after stop")
            .get()
    }

    /// Stop the timer.
    ///
    /// Stops the timer, so that it will not fire any more, converting the timer back into a
    /// StoppedTimer.
    pub fn stop(mut self) -> StoppedTimer {
        // Actually do the stop.
        let item = self.raw_stop();

        let item = item.expect("Error in stop/drop interaction");

        StoppedTimer { item }
    }

    /// Stop the timer.  Returns the inner item.
    fn raw_stop(&mut self) -> Option<Fixed<k_timer>> {
        let item = self.item.take();
        if let Some(ref item) = item {
            unsafe {
                // SAFETY: Stopping the timer removes it from any queues.  There must not be threads
                // blocked, which is enforced by this only being called from either `stop` or
                // `drop`.  Once this has been stopped, it is then safe to remove the callback.  As
                // there will be no more timer operations until the timer is restarted, which will
                // have a barrier, this is also safe.
                let raw_item = item.get();
                k_timer_stop(raw_item);
                (*raw_item).expiry_fn = None;
            }
        }
        item
    }
}

impl<T: Send + Sync> Drop for CallbackTimer<T> {
    fn drop(&mut self) {
        // Stop the timer, discarding the inner item.
        let _ = self.raw_stop();
    }
}