Skip to main content

leodos_libcfs/cfe/es/
system.rs

1//! System-level queries, reset control, and startup synchronization.
2
3use crate::error::Result;
4use crate::ffi;
5use crate::status::check;
6use core::mem::MaybeUninit;
7use core::time::Duration;
8
9/// The type of reset the processor most recently underwent.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[repr(u32)]
12pub enum ResetType {
13    /// A processor reset, where volatile memory areas may have been preserved.
14    Processor = ffi::CFE_PSP_RST_TYPE_PROCESSOR,
15    /// A power-on reset, where all memory has been cleared.
16    PowerOn = ffi::CFE_PSP_RST_TYPE_POWERON,
17    /// An unknown or unhandled reset type.
18    Unknown(u32),
19}
20
21impl From<u32> for ResetType {
22    fn from(val: u32) -> Self {
23        match val {
24            ffi::CFE_PSP_RST_TYPE_PROCESSOR => Self::Processor,
25            ffi::CFE_PSP_RST_TYPE_POWERON => Self::PowerOn,
26            other => Self::Unknown(other),
27        }
28    }
29}
30
31impl From<ResetType> for u32 {
32    fn from(val: ResetType) -> Self {
33        match val {
34            ResetType::Processor => ffi::CFE_PSP_RST_TYPE_PROCESSOR,
35            ResetType::PowerOn => ffi::CFE_PSP_RST_TYPE_POWERON,
36            ResetType::Unknown(other) => other,
37        }
38    }
39}
40
41/// The specific cause of the most recent reset.
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43#[repr(u32)]
44pub enum ResetSubtype {
45    /// Reset caused by a power cycle.
46    PowerCycle = ffi::CFE_PSP_RST_SUBTYPE_POWER_CYCLE,
47    /// Reset caused by a push button.
48    PushButton = ffi::CFE_PSP_RST_SUBTYPE_PUSH_BUTTON,
49    /// Reset caused by a hardware special command.
50    HwSpecialCommand = ffi::CFE_PSP_RST_SUBTYPE_HW_SPECIAL_COMMAND,
51    /// Reset caused by a hardware watchdog timer expiring.
52    HwWatchdog = ffi::CFE_PSP_RST_SUBTYPE_HW_WATCHDOG,
53    /// Reset caused by a cFE ES Reset command.
54    ResetCommand = ffi::CFE_PSP_RST_SUBTYPE_RESET_COMMAND,
55    /// Reset caused by a processor exception.
56    Exception = ffi::CFE_PSP_RST_SUBTYPE_EXCEPTION,
57    /// Reset cause is undefined.
58    Undefined = ffi::CFE_PSP_RST_SUBTYPE_UNDEFINED_RESET,
59    /// Reset caused by a hardware debugger.
60    HwDebug = ffi::CFE_PSP_RST_SUBTYPE_HWDEBUG_RESET,
61    /// Reset reverted to a POWERON due to a boot bank switch.
62    BankSwitch = ffi::CFE_PSP_RST_SUBTYPE_BANKSWITCH_RESET,
63    /// An unknown or unhandled reset subtype.
64    Unknown(u32),
65}
66
67impl From<u32> for ResetSubtype {
68    fn from(val: u32) -> Self {
69        match val {
70            ffi::CFE_PSP_RST_SUBTYPE_POWER_CYCLE => Self::PowerCycle,
71            ffi::CFE_PSP_RST_SUBTYPE_PUSH_BUTTON => Self::PushButton,
72            ffi::CFE_PSP_RST_SUBTYPE_HW_SPECIAL_COMMAND => Self::HwSpecialCommand,
73            ffi::CFE_PSP_RST_SUBTYPE_HW_WATCHDOG => Self::HwWatchdog,
74            ffi::CFE_PSP_RST_SUBTYPE_RESET_COMMAND => Self::ResetCommand,
75            ffi::CFE_PSP_RST_SUBTYPE_EXCEPTION => Self::Exception,
76            ffi::CFE_PSP_RST_SUBTYPE_UNDEFINED_RESET => Self::Undefined,
77            ffi::CFE_PSP_RST_SUBTYPE_HWDEBUG_RESET => Self::HwDebug,
78            ffi::CFE_PSP_RST_SUBTYPE_BANKSWITCH_RESET => Self::BankSwitch,
79            other => Self::Unknown(other),
80        }
81    }
82}
83
84/// The overall state of the cFE system, used for startup synchronization.
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86#[repr(u32)]
87pub enum SystemState {
88    /// Single-threaded mode during early cFE setup.
89    EarlyInit = ffi::CFE_ES_SystemState_CFE_ES_SystemState_EARLY_INIT,
90    /// Core cFE apps are starting.
91    CoreStartup = ffi::CFE_ES_SystemState_CFE_ES_SystemState_CORE_STARTUP,
92    /// Core apps are ready, external apps/libraries are starting.
93    CoreReady = ffi::CFE_ES_SystemState_CFE_ES_SystemState_CORE_READY,
94    /// External apps have completed early initialization.
95    AppsInit = ffi::CFE_ES_SystemState_CFE_ES_SystemState_APPS_INIT,
96    /// Normal operation; all apps are running.
97    Operational = ffi::CFE_ES_SystemState_CFE_ES_SystemState_OPERATIONAL,
98    /// Shutdown state.
99    Shutdown = ffi::CFE_ES_SystemState_CFE_ES_SystemState_SHUTDOWN,
100}
101
102/// Returns the type and subtype of the most recent processor reset.
103pub fn get_reset_type() -> (ResetType, ResetSubtype) {
104    let mut subtype = MaybeUninit::uninit();
105    let reset_type = unsafe { ffi::CFE_ES_GetResetType(subtype.as_mut_ptr()) };
106    (
107        (reset_type as u32).into(),
108        unsafe { subtype.assume_init() }.into(),
109    )
110}
111
112/// Allows an application to wait until the cFE system reaches a minimum state.
113///
114/// This is useful for synchronizing application startup phases. For example, an
115/// application can wait until `SystemState::CoreReady` before attempting to
116/// subscribe to messages from core cFE services.
117///
118/// # Arguments
119/// * `state`: The minimum system state to wait for.
120/// * `timeout`: The maximum duration to wait.
121pub fn wait_for_system_state(state: SystemState, timeout: Duration) -> Result<()> {
122    let millis = timeout.as_millis();
123    let millis_u32 = millis.try_into().unwrap_or(u32::MAX);
124    check(unsafe { ffi::CFE_ES_WaitForSystemState(state as u32, millis_u32) })?;
125    Ok(())
126}
127
128/// Returns the cFE-defined processor ID for the current CPU.
129pub fn get_processor_id() -> u32 {
130    unsafe { ffi::CFE_PSP_GetProcessorId() }
131}
132
133/// Returns the cFE-defined spacecraft ID.
134pub fn get_spacecraft_id() -> u32 {
135    unsafe { ffi::CFE_PSP_GetSpacecraftId() }
136}
137
138/// Reset the cFE Core and all cFE Applications.
139///
140/// On success this function does not return. On error (e.g. invalid
141/// reset type) it returns `CFE_ES_BAD_ARGUMENT` or
142/// `CFE_ES_NOT_IMPLEMENTED`, but this wrapper loops forever to
143/// satisfy the `!` return type.
144///
145/// # Arguments
146/// * `reset_type`: The type of reset to perform (`PowerOn` or `Processor`).
147pub fn reset_cfe(reset_type: ResetType) -> ! {
148    let _status = unsafe { ffi::CFE_ES_ResetCFE(reset_type.into()) };
149
150    // CFE_ES_ResetCFE does not return on success. If it returns, it's an error.
151    loop {}
152}
153
154/// Allows an application to wait until all cFE apps have reached
155/// the `OPERATIONAL` state.
156///
157/// This is a convenience wrapper for
158/// `wait_for_system_state(SystemState::Operational, ...)`.
159///
160/// Timeout values below 1000 ms are rounded up to 1000 ms.
161/// Should only be called as the last step of application init,
162/// and only by apps started from the ES startup file.
163///
164/// # Arguments
165/// * `timeout`: The maximum duration to wait.
166pub fn wait_for_startup_sync(timeout: Duration) {
167    let millis = timeout.as_millis();
168    let millis_u32 = millis.try_into().unwrap_or(u32::MAX);
169    unsafe {
170        ffi::CFE_ES_WaitForStartupSync(millis_u32);
171    }
172}
173
174/// Wakes up the ES background task to process pending jobs.
175///
176/// Normally the ES background task wakes up at a periodic interval.
177/// Whenever new background work is added, this can be used to wake
178/// the task early, which may reduce the delay before the job is
179/// processed.
180///
181/// Work is pro-rated based on elapsed time since the last wakeup.
182/// Waking the task early does not cause extra work to be done.
183pub fn background_wakeup() {
184    unsafe { ffi::CFE_ES_BackgroundWakeup() };
185}
186
187/// Notifies ES that an asynchronous event was detected by the
188/// underlying OS/PSP.
189///
190/// This hook routine is called from the PSP when an exception or
191/// other asynchronous system event occurs.
192///
193/// Must not be invoked directly from ISR or signal context. The
194/// PSP must guarantee it runs from a context that can use OSAL
195/// primitives.
196pub fn process_async_event() {
197    unsafe { ffi::CFE_ES_ProcessAsyncEvent() };
198}