Skip to main content

frclib_core/hal/
mod.rs

1//! This module contains what is needed to write and use a HAL(Hardware Abstraction Layer).
2
3pub mod comm;
4pub mod gpio;
5pub mod rt;
6
7#[cfg(not(test))]
8use std::sync::OnceLock;
9#[allow(unused_imports)]
10use std::{cell::RefCell, mem::size_of};
11
12use self::{
13    gpio::SimGPIODriver,
14    rt::{
15        station_interface::{StationInterfaceDriver, StationInterfaceVTable},
16        watchdog::SimWatchdogDriver,
17    },
18};
19#[allow(unused_imports)]
20use self::{
21    gpio::{GPIODriver, GPIOVTable},
22    rt::{
23        notifier::{NotifierDriver, NotifierVTable},
24        time::{initialize_time_callbacks, ClockDriver},
25        watchdog::{WatchdogDriver, WatchdogVTable},
26    },
27};
28
29#[cfg(not(test))]
30static HAL_INSTANCE: OnceLock<HAL> = OnceLock::new();
31
32#[cfg(test)]
33thread_local! {
34    static HAL_INSTANCE_LOCAL: RefCell<Option<HAL>> = const { RefCell::new(None) };
35}
36
37/// A trait that defines a HAL(Hardware Abstraction Layer) Driver.
38/// Should be implemented by a platform specific HAL Driver Zero Sized Type.
39pub trait HALDriver:
40    GPIODriver
41    + ClockDriver
42    + NotifierDriver
43    + WatchdogDriver
44    + StationInterfaceDriver
45    /*
46    + CanDriver
47    + SpiDriver
48    + I2cDriver
49    + UartDriver
50    */
51    + 'static {
52    /// The name of the driver, used for logging/debugging.
53    const NAME: &'static str;
54
55    /// Will only be called once per program execution.
56    /// Can be used to setup global state for the driver.
57    fn init();
58
59    /// Will only be called once per program execution but is not guaranteed to be called.
60    /// Will always be called after [`HALDriver::init`].
61    /// Can be used to cleanup global state for the driver.
62    fn cleanup();
63}
64
65/// A trait that defines a HAL(Hardware Abstraction Layer) Driver with sim support.
66/// Should be implemented by a platform specific HAL Driver Zero Sized Type.
67///
68/// A HAL with sim support should implement this trait instead of [`HALDriver`],
69/// because of this a HAL with sim support can be used in as a [`HALDriver`] too.
70pub trait SimHALDriver: HALDriver + SimGPIODriver + SimWatchdogDriver {}
71
72/// A struct that defines a platform specific HAL(Hardware Abstraction Layer) using a Driver.
73///
74/// Using a HAL allows for significant platform changes without having to rewrite much code.
75/// HALs are also useful for testing/sim as they can be mocked.
76///
77/// HALs also allow for easier community support for exotic platforms.
78#[allow(clippy::upper_case_acronyms)]
79#[derive(Debug, Clone, Copy)]
80pub struct HAL {
81    driver_name: &'static str,
82    cleanup: fn(),
83    gpio: GPIOVTable,
84    notifier: NotifierVTable,
85    watchdog: WatchdogVTable,
86    station_interface: StationInterfaceVTable,
87}
88
89impl HAL {
90    /// Initializes the HAL, can only be called once across every HAL.
91    ///
92    /// # Panics
93    /// If HAL has already been initialized this will panic
94    /// If the used driver is not zero sized this will panic
95    pub fn init<Driver: HALDriver>() {
96        assert!(size_of::<Driver>() == 0, "Driver must be zero sized");
97        Driver::init();
98        initialize_time_callbacks::<Driver>(Driver::NAME);
99        Self {
100            driver_name: Driver::NAME,
101            cleanup: Driver::cleanup,
102            gpio: GPIOVTable::from_driver::<Driver>(),
103            notifier: NotifierVTable::from_driver::<Driver>(),
104            watchdog: WatchdogVTable::from_driver::<Driver>(),
105            station_interface: StationInterfaceVTable::from_driver::<Driver>(),
106        }
107        .set_hal();
108    }
109
110    /// Initializes the HAL with support for sim, can only be called once across every HAL.
111    ///
112    /// # Panics
113    /// If HAL has already been initialized this will panic.
114    /// If the used driver is not zero sized this will panic.
115    pub fn init_sim<Driver: SimHALDriver>() {
116        assert!(size_of::<Driver>() == 0, "Driver must be zero sized");
117        Driver::init();
118        #[cfg(not(test))]
119        initialize_time_callbacks::<Driver>(Driver::NAME);
120        Self {
121            driver_name: Driver::NAME,
122            cleanup: Driver::cleanup,
123            gpio: GPIOVTable::from_sim_driver::<Driver>(),
124            notifier: NotifierVTable::from_driver::<Driver>(),
125            watchdog: WatchdogVTable::from_sim_driver::<Driver>(),
126            station_interface: StationInterfaceVTable::from_driver::<Driver>(),
127        }
128        .set_hal();
129    }
130
131    fn set_hal(self) {
132        #[cfg(not(test))]
133        {
134            assert!(
135                HAL_INSTANCE.get().is_none(),
136                "HAL has already been initialized"
137            );
138            let _ = HAL_INSTANCE.set(self);
139        }
140        #[cfg(test)]
141        {
142            assert!(
143                HAL_INSTANCE_LOCAL.with(|inst| inst.borrow().is_none()),
144                "HAL has already been initialized"
145            );
146            HAL_INSTANCE_LOCAL.with(|local_hal_instance| {
147                *local_hal_instance.borrow_mut() = Some(self);
148            });
149        }
150    }
151
152    /// Cleans up the HAL
153    pub fn cleanup(&self) {
154        (self.cleanup)();
155    }
156
157    /// Returns the name of the current [`HALDriver`].
158    #[must_use]
159    pub const fn driver_name(&self) -> &'static str {
160        self.driver_name
161    }
162
163    /// Returns the [`NotifierVTable`] for the current [`HALDriver`].
164    #[must_use]
165    pub const fn notifier_api(&self) -> NotifierVTable {
166        self.notifier
167    }
168
169    /// Returns the [`WatchdogVTable`] for the current [`HALDriver`].
170    #[must_use]
171    pub const fn watchdog_api(&self) -> WatchdogVTable {
172        self.watchdog
173    }
174
175    /// Returns the [`GPIOVTable`] for the current [`HALDriver`].
176    #[must_use]
177    pub const fn gpio_api(&self) -> GPIOVTable {
178        self.gpio
179    }
180
181    /// Returns the [`StationInterfaceVTable`] for the current [`HALDriver`].
182    #[must_use]
183    pub const fn station_interface_api(&self) -> StationInterfaceVTable {
184        self.station_interface
185    }
186}
187
188/// Returns the current [`HAL`] instance.
189///
190/// # Errors
191///  - [`HALNotInitializedError`] if the [`HAL`] has not been initialized
192pub fn get_hal() -> Result<HAL, HALNotInitializedError> {
193    #[cfg(not(test))]
194    {
195        HAL_INSTANCE.get().copied().ok_or(HALNotInitializedError)
196    }
197    #[cfg(test)]
198    {
199        HAL_INSTANCE_LOCAL
200            .with(|local_hal_instance| *local_hal_instance.borrow())
201            .ok_or(HALNotInitializedError)
202    }
203}
204
205/// An error that occurs when a function is called that is only available when the [`HAL`] is initialized with sim support
206/// but the [`HAL`] is not initialized with sim support.
207#[derive(Debug, Clone, Copy, PartialEq, Eq)]
208pub struct NotSimError;
209impl std::fmt::Display for NotSimError {
210    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211        write!(
212            f,
213            "This function is only available when the HAL is initialized with sim support"
214        )
215    }
216}
217impl std::error::Error for NotSimError {}
218
219/// An error that occurs when the [`HAL`] has not been initialized but is attempted to be used.
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221pub struct HALNotInitializedError;
222impl std::fmt::Display for HALNotInitializedError {
223    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224        write!(f, "`HAL` has not been initialized")
225    }
226}
227impl std::error::Error for HALNotInitializedError {}
228
229// #[doc(hidden)]
230// pub mod __private {
231//     pub trait ZST {}
232// }
233//
234// /// Validates that a driver is zero size
235// #[macro_export]
236// macro_rules! assert_driver_zst {
237//     ($driver:ty) => {
238//         const _: fn() = || {
239//             let _ = core::mem::transmute::<$driver, ()>;
240//         };
241//         impl $crate::hal::__private::ZST for $driver {}
242//     };
243// }