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}