Skip to main content

leodos_libcfs/os/
timer.rs

1//! Safe, idiomatic wrappers for OSAL Timer APIs.
2//!
3//! This module provides a `Timer` struct for creating, configuring, and managing
4//! OSAL timers that execute a callback function at a specified interval. The `Timer`
5//! struct uses RAII to ensure the underlying OSAL resource is deleted when it
6//! is dropped.
7
8use crate::error::Result;
9use crate::ffi;
10use crate::os::id::OsalId;
11use crate::os::timebase::TimeBaseId;
12use crate::os::util::c_name_from_str;
13use crate::string_from_c_buf;
14use crate::status::check;
15use core::ffi::c_void;
16use core::mem::MaybeUninit;
17use core::ops::Drop;
18use heapless::String;
19
20/// A type alias for the callback function used by an OSAL timer.
21///
22/// The function receives the ID of the timer that triggered it.
23pub type TimerCallback = unsafe extern "C" fn(timer_id: ffi::osal_id_t);
24
25/// A type alias for a timer callback that accepts a user-defined argument.
26pub type TimerArgCallback = unsafe extern "C" fn(timer_id: ffi::osal_id_t, arg: *mut c_void);
27
28/// Properties of an OSAL timer.
29#[derive(Debug, Clone)]
30pub struct TimerProp {
31    /// The registered name of the timer.
32    pub name: String<{ ffi::OS_MAX_API_NAME as usize }>,
33    /// The OSAL ID of the task that created the timer.
34    pub creator: OsalId,
35    /// The configured start time in microseconds.
36    pub start_time: u32,
37    /// The configured interval time in microseconds.
38    pub interval_time: u32,
39    /// The accuracy of the timer in microseconds.
40    pub accuracy: u32,
41}
42
43/// A handle to an OSAL timer.
44///
45/// This is a wrapper around an `osal_id_t` that will automatically call
46/// `OS_TimerDelete` when it goes out of scope, preventing resource leaks.
47#[derive(Debug)]
48pub struct Timer {
49    id: ffi::osal_id_t,
50}
51
52impl Timer {
53    /// Creates a new OSAL timer and associates it with a callback
54    /// function.
55    ///
56    /// This also creates a dedicated hidden time base object
57    /// (consuming a resource slot) that is deleted when the timer
58    /// is dropped.
59    ///
60    /// On success, returns the `Timer` instance and the clock
61    /// accuracy in microseconds. The timer does not start until
62    /// [`set`](Self::set) is called.
63    ///
64    /// Must not be called from the context of a timer callback.
65    ///
66    /// # Arguments
67    /// * `name`: A unique string to identify the timer.
68    /// * `callback`: The function to be executed when the timer expires.
69    pub fn new(name: &str, callback: TimerCallback) -> Result<(Self, u32)> {
70        let c_name = c_name_from_str(name)?;
71        let mut timer_id = MaybeUninit::uninit();
72        let mut clock_accuracy = MaybeUninit::uninit();
73
74        let status = unsafe {
75            ffi::OS_TimerCreate(
76                timer_id.as_mut_ptr(),
77                c_name.as_ptr(),
78                clock_accuracy.as_mut_ptr(),
79                Some(callback),
80            )
81        };
82
83        check(status)?;
84
85        Ok((
86            Self {
87                id: unsafe { timer_id.assume_init() },
88            },
89            unsafe { clock_accuracy.assume_init() },
90        ))
91    }
92
93    /// Programs the timer for a one-shot or periodic execution.
94    ///
95    /// Both `start_time_usecs` and `interval_time_usecs` being zero
96    /// is an error. Values below the clock accuracy are rounded up
97    /// to the timer's resolution.
98    ///
99    /// Must not be called from the context of a timer callback.
100    ///
101    /// # Arguments
102    /// * `start_time_usecs`: Time in microseconds until the first expiration.
103    /// * `interval_time_usecs`: Time in microseconds between subsequent expirations.
104    ///   If set to 0, the timer is a one-shot timer and will only fire once.
105    pub fn set(&self, start_time_usecs: u32, interval_time_usecs: u32) -> Result<()> {
106        let status = unsafe { ffi::OS_TimerSet(self.id, start_time_usecs, interval_time_usecs) };
107        check(status)?;
108        Ok(())
109    }
110
111    /// Finds an existing timer ID by its name.
112    ///
113    /// Must not be called from the context of a timer callback.
114    pub fn get_id_by_name(name: &str) -> Result<ffi::osal_id_t> {
115        let c_name = c_name_from_str(name)?;
116        let mut timer_id = MaybeUninit::uninit();
117        let status = unsafe { ffi::OS_TimerGetIdByName(timer_id.as_mut_ptr(), c_name.as_ptr()) };
118        check(status)?;
119        Ok(unsafe { timer_id.assume_init() })
120    }
121
122    /// Creates a new OSAL timer and attaches it to an existing time base.
123    ///
124    /// This allows multiple timers to share a single underlying timing source.
125    pub fn add(
126        name: &str,
127        timebase_id: TimeBaseId,
128        callback: TimerArgCallback,
129        callback_arg: *mut c_void,
130    ) -> Result<Self> {
131        let c_name = c_name_from_str(name)?;
132        let mut timer_id = MaybeUninit::uninit();
133        check(unsafe {
134            ffi::OS_TimerAdd(
135                timer_id.as_mut_ptr(),
136                c_name.as_ptr(),
137                timebase_id.0,
138                Some(callback),
139                callback_arg,
140            )
141        })?;
142        Ok(Self {
143            id: unsafe { timer_id.assume_init() },
144        })
145    }
146
147    /// Retrieves information about this timer.
148    ///
149    /// Must not be called from the context of a timer callback.
150    pub fn info(&self) -> Result<TimerProp> {
151        let mut prop = MaybeUninit::<ffi::OS_timer_prop_t>::uninit();
152        check(unsafe { ffi::OS_TimerGetInfo(self.id, prop.as_mut_ptr()) })?;
153        let prop = unsafe { prop.assume_init() };
154
155        Ok(TimerProp {
156            name: string_from_c_buf(&prop.name)?,
157            creator: OsalId(prop.creator),
158            start_time: prop.start_time,
159            interval_time: prop.interval_time,
160            accuracy: prop.accuracy,
161        })
162    }
163}
164
165impl Drop for Timer {
166    fn drop(&mut self) {
167        let _ = unsafe { ffi::OS_TimerDelete(self.id) };
168    }
169}