pub mod comm;
pub mod gpio;
pub mod rt;
#[cfg(not(test))]
use std::sync::OnceLock;
#[allow(unused_imports)]
use std::{cell::RefCell, mem::size_of};
use self::{
gpio::SimGPIODriver,
rt::{
station_interface::{StationInterfaceDriver, StationInterfaceVTable},
watchdog::SimWatchdogDriver,
},
};
#[allow(unused_imports)]
use self::{
gpio::{GPIODriver, GPIOVTable},
rt::{
notifier::{NotifierDriver, NotifierVTable},
time::{initialize_time_callbacks, ClockDriver},
watchdog::{WatchdogDriver, WatchdogVTable},
},
};
#[cfg(not(test))]
static HAL_INSTANCE: OnceLock<HAL> = OnceLock::new();
#[cfg(test)]
thread_local! {
static HAL_INSTANCE_LOCAL: RefCell<Option<HAL>> = const { RefCell::new(None) };
}
pub trait HALDriver:
GPIODriver
+ ClockDriver
+ NotifierDriver
+ WatchdogDriver
+ StationInterfaceDriver
+ 'static {
const NAME: &'static str;
fn init();
fn cleanup();
}
pub trait SimHALDriver: HALDriver + SimGPIODriver + SimWatchdogDriver {}
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy)]
pub struct HAL {
driver_name: &'static str,
cleanup: fn(),
gpio: GPIOVTable,
notifier: NotifierVTable,
watchdog: WatchdogVTable,
station_interface: StationInterfaceVTable,
}
impl HAL {
pub fn init<Driver: HALDriver>() {
assert!(size_of::<Driver>() == 0, "Driver must be zero sized");
Driver::init();
initialize_time_callbacks::<Driver>(Driver::NAME);
Self {
driver_name: Driver::NAME,
cleanup: Driver::cleanup,
gpio: GPIOVTable::from_driver::<Driver>(),
notifier: NotifierVTable::from_driver::<Driver>(),
watchdog: WatchdogVTable::from_driver::<Driver>(),
station_interface: StationInterfaceVTable::from_driver::<Driver>(),
}
.set_hal();
}
pub fn init_sim<Driver: SimHALDriver>() {
assert!(size_of::<Driver>() == 0, "Driver must be zero sized");
Driver::init();
#[cfg(not(test))]
initialize_time_callbacks::<Driver>(Driver::NAME);
Self {
driver_name: Driver::NAME,
cleanup: Driver::cleanup,
gpio: GPIOVTable::from_sim_driver::<Driver>(),
notifier: NotifierVTable::from_driver::<Driver>(),
watchdog: WatchdogVTable::from_sim_driver::<Driver>(),
station_interface: StationInterfaceVTable::from_driver::<Driver>(),
}
.set_hal();
}
fn set_hal(self) {
#[cfg(not(test))]
{
assert!(
HAL_INSTANCE.get().is_none(),
"HAL has already been initialized"
);
let _ = HAL_INSTANCE.set(self);
}
#[cfg(test)]
{
assert!(
HAL_INSTANCE_LOCAL.with(|inst| inst.borrow().is_none()),
"HAL has already been initialized"
);
HAL_INSTANCE_LOCAL.with(|local_hal_instance| {
*local_hal_instance.borrow_mut() = Some(self);
});
}
}
pub fn cleanup(&self) {
(self.cleanup)();
}
#[must_use]
pub const fn driver_name(&self) -> &'static str {
self.driver_name
}
#[must_use]
pub const fn notifier_api(&self) -> NotifierVTable {
self.notifier
}
#[must_use]
pub const fn watchdog_api(&self) -> WatchdogVTable {
self.watchdog
}
#[must_use]
pub const fn gpio_api(&self) -> GPIOVTable {
self.gpio
}
#[must_use]
pub const fn station_interface_api(&self) -> StationInterfaceVTable {
self.station_interface
}
}
pub fn get_hal() -> Result<HAL, HALNotInitializedError> {
#[cfg(not(test))]
{
HAL_INSTANCE.get().copied().ok_or(HALNotInitializedError)
}
#[cfg(test)]
{
HAL_INSTANCE_LOCAL
.with(|local_hal_instance| *local_hal_instance.borrow())
.ok_or(HALNotInitializedError)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NotSimError;
impl std::fmt::Display for NotSimError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"This function is only available when the HAL is initialized with sim support"
)
}
}
impl std::error::Error for NotSimError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HALNotInitializedError;
impl std::fmt::Display for HALNotInitializedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "`HAL` has not been initialized")
}
}
impl std::error::Error for HALNotInitializedError {}