#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ServiceSet(u32);
impl ServiceSet {
pub const EMPTY: Self = Self(0);
pub const AUDIO: Self = Self(1 << 0);
pub const CODEC_RESET: Self = Self(1 << 1);
pub const SD: Self = Self(1 << 2);
pub const QSPI: Self = Self(1 << 3);
pub const MEMS_MIC: Self = Self(1 << 4);
pub const SCOPE_PROBES: Self = Self(1 << 5);
#[inline]
pub const fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
#[inline]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[inline]
pub const fn raw(self) -> u32 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(C)]
pub struct SafeStopReport {
pub timeouts: u32,
pub elapsed_us: u32,
}
pub struct SafeStop;
impl SafeStop {
#[inline]
pub unsafe fn run(services: ServiceSet) -> SafeStopReport {
let _ = services;
SafeStopReport {
timeouts: 0,
elapsed_us: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn service_set_empty_is_zero() {
assert_eq!(ServiceSet::EMPTY.raw(), 0);
}
#[test]
fn service_set_named_bits_distinct() {
let all = [
ServiceSet::AUDIO,
ServiceSet::CODEC_RESET,
ServiceSet::SD,
ServiceSet::QSPI,
ServiceSet::MEMS_MIC,
ServiceSet::SCOPE_PROBES,
];
for (i, a) in all.iter().enumerate() {
assert!(
a.raw().is_power_of_two(),
"service {i} is not a single bit: 0x{:08x}",
a.raw()
);
for (j, b) in all.iter().enumerate() {
if i == j {
continue;
}
assert_eq!(
a.raw() & b.raw(),
0,
"services {i} and {j} share bits: 0x{:08x} & 0x{:08x}",
a.raw(),
b.raw()
);
}
}
}
#[test]
fn service_set_union_and_contains() {
let audio_sd = ServiceSet::AUDIO.union(ServiceSet::SD);
assert!(audio_sd.contains(ServiceSet::AUDIO));
assert!(audio_sd.contains(ServiceSet::SD));
assert!(!audio_sd.contains(ServiceSet::QSPI));
assert!(audio_sd.contains(ServiceSet::EMPTY));
assert_eq!(
audio_sd.union(audio_sd).raw(),
audio_sd.raw(),
"union is idempotent"
);
}
#[test]
fn service_set_round_trip_raw() {
let s = ServiceSet::AUDIO
.union(ServiceSet::CODEC_RESET)
.union(ServiceSet::MEMS_MIC);
let raw = s.raw();
assert_ne!(raw & ServiceSet::AUDIO.raw(), 0);
assert_ne!(raw & ServiceSet::CODEC_RESET.raw(), 0);
assert_ne!(raw & ServiceSet::MEMS_MIC.raw(), 0);
assert_eq!(raw & ServiceSet::SD.raw(), 0);
assert_eq!(raw & ServiceSet::QSPI.raw(), 0);
assert_eq!(raw & ServiceSet::SCOPE_PROBES.raw(), 0);
}
#[test]
fn safe_stop_report_size_is_two_u32() {
assert_eq!(core::mem::size_of::<SafeStopReport>(), 8);
}
#[test]
fn safe_stop_run_scaffold_returns_zero_report() {
let report = unsafe { SafeStop::run(ServiceSet::EMPTY) };
assert_eq!(report.timeouts, 0);
assert_eq!(report.elapsed_us, 0);
let report = unsafe {
SafeStop::run(
ServiceSet::AUDIO
.union(ServiceSet::CODEC_RESET)
.union(ServiceSet::SD),
)
};
assert_eq!(report.timeouts, 0);
assert_eq!(report.elapsed_us, 0);
}
}