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}