leodos_libcfs/os/timebase.rs
1//! Safe, idiomatic wrappers for OSAL Time Base APIs.
2//!
3//! This module provides a `TimeBase` struct for creating and managing OSAL
4//! time bases, which act as sources for timer ticks. The `TimeBase` struct uses
5//! RAII to ensure the underlying OSAL resource is properly cleaned up.
6
7use crate::error::{CfsError, OsalError, Result};
8use crate::ffi;
9use crate::os::id::OsalId;
10use crate::cstring;
11use crate::os::util::c_name_from_str;
12use crate::string_from_c_buf;
13use crate::status::check;
14use core::mem::MaybeUninit;
15use core::time::Duration;
16use heapless::String;
17
18/// A type-safe, zero-cost wrapper for an OSAL Time Base ID.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[repr(transparent)]
21pub struct TimeBaseId(pub ffi::osal_id_t);
22
23impl TimeBaseId {
24 /// Reads the value of the timebase free-running counter.
25 ///
26 /// This is a lightweight way to poll a monotonically increasing timer. The absolute
27 /// value is not relevant, but differences between successive calls can be used for
28 /// high-resolution timing.
29 pub fn get_free_run(&self) -> Result<u32> {
30 let mut freerun_val = MaybeUninit::uninit();
31 check(unsafe { ffi::OS_TimeBaseGetFreeRun(self.0, freerun_val.as_mut_ptr()) })?;
32 Ok(unsafe { freerun_val.assume_init() })
33 }
34
35 /// Finds an existing time base ID by its name.
36 pub fn from_name(name: &str) -> Result<Self> {
37 let c_name = c_name_from_str(name)?;
38 let mut timebase_id = MaybeUninit::uninit();
39 check(unsafe { ffi::OS_TimeBaseGetIdByName(timebase_id.as_mut_ptr(), c_name.as_ptr()) })?;
40 Ok(Self(unsafe { timebase_id.assume_init() }))
41 }
42}
43
44/// Properties of a time base, returned by `TimeBase::info`.
45#[derive(Debug, Clone)]
46pub struct TimeBaseProp {
47 /// The registered name of the time base.
48 pub name: String<{ ffi::OS_MAX_API_NAME as usize }>,
49 /// The OSAL ID of the task that created the time base.
50 pub creator: OsalId,
51 /// The nominal interval time in microseconds.
52 pub nominal_interval_time: u32,
53 /// The free-running time in microseconds.
54 pub freerun_time: u32,
55 /// The accuracy of the time base in microseconds.
56 pub accuracy: u32,
57}
58
59/// A handle to an OSAL time base.
60///
61/// A time base is an abstraction of a "timer tick" that can be used for
62/// measuring elapsed time or scheduling timer callbacks. This wrapper manages a
63/// software-simulated time base provided by the OSAL.
64#[derive(Debug)]
65pub struct TimeBase {
66 id: TimeBaseId,
67}
68
69impl TimeBase {
70 /// Creates a new software-simulated OSAL time base.
71 ///
72 /// This time base will use the underlying OS kernel's timing
73 /// facilities. The timer does not start until `set()` is called.
74 ///
75 /// This creates a servicing task at elevated priority that may
76 /// interrupt user tasks. The kernel must be configured for
77 /// `OS_MAX_TASKS + OS_MAX_TIMEBASES` threads.
78 ///
79 /// Must not be called from the context of a timer callback.
80 ///
81 /// # Arguments
82 /// * `name`: A unique string to identify the time base.
83 pub fn new(name: &str) -> Result<Self> {
84 let c_name = cstring::<{ ffi::OS_MAX_API_NAME as usize }>(name)?;
85
86 let mut timebase_id = MaybeUninit::uninit();
87 check(unsafe {
88 ffi::OS_TimeBaseCreate(
89 timebase_id.as_mut_ptr(),
90 c_name.as_ptr(),
91 None, // Use software-simulated timer
92 )
93 })?;
94
95 Ok(Self {
96 id: TimeBaseId(unsafe { timebase_id.assume_init() }),
97 })
98 }
99
100 /// Programs the time base for a one-shot or periodic tick.
101 ///
102 /// Must not be called from the context of a timer callback.
103 ///
104 /// # Arguments
105 /// * `start`: `Duration` until the first tick.
106 /// * `interval`: `Duration` between subsequent ticks. If `Duration::ZERO`,
107 /// the time base will only tick once.
108 pub fn set(&self, start: Duration, interval: Duration) -> Result<()> {
109 let start_usecs = start
110 .as_micros()
111 .try_into()
112 .map_err(|_| CfsError::Osal(OsalError::InvalidArgument))?;
113 let interval_usecs = interval
114 .as_micros()
115 .try_into()
116 .map_err(|_| CfsError::Osal(OsalError::InvalidArgument))?;
117
118 check(unsafe { ffi::OS_TimeBaseSet(self.id.0, start_usecs, interval_usecs) })?;
119 Ok(())
120 }
121
122 /// Returns the underlying `TimeBaseId`.
123 pub fn id(&self) -> TimeBaseId {
124 self.id
125 }
126
127 /// Retrieves information about this time base.
128 pub fn info(&self) -> Result<TimeBaseProp> {
129 let mut prop = MaybeUninit::<ffi::OS_timebase_prop_t>::uninit();
130 check(unsafe { ffi::OS_TimeBaseGetInfo(self.id.0, prop.as_mut_ptr()) })?;
131 let prop = unsafe { prop.assume_init() };
132
133 Ok(TimeBaseProp {
134 name: string_from_c_buf(&prop.name)?,
135 creator: OsalId(prop.creator),
136 nominal_interval_time: prop.nominal_interval_time,
137 freerun_time: prop.freerun_time,
138 accuracy: prop.accuracy,
139 })
140 }
141}
142
143impl Drop for TimeBase {
144 /// Deletes the OSAL time base when the `TimeBase` object goes out of scope.
145 fn drop(&mut self) {
146 let _ = unsafe { ffi::OS_TimeBaseDelete(self.id.0) };
147 }
148}