Skip to main content

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}