|
| 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