Skip to main content

xpanse_api/
driver.rs

1//! Module driver traits.
2//!
3//! A driver identifies its hardware through [`DriverMeta::ID`], consumes the
4//! detected module's [`crate::gpio_bank::GpioBank`], initializes any buses it
5//! needs, and publishes app-facing capabilities in a [`crate::registry::Registry`].
6
7use crate::bus::allocator::BusAllocator;
8use crate::gpio_bank::{BankPins, GpioBank};
9use crate::metadata::{ModuleID, ModuleSlot};
10use crate::registry::Registry;
11use core::future::Future;
12
13/// Error returned when a module driver cannot be initialized.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
15pub enum DriverError {
16    /// Peripheral setup, device communication, or resource registration failed.
17    InitFailed,
18}
19
20/// Static metadata used to match a driver to a detected module.
21pub trait DriverMeta {
22    /// Resistor-coded module identifier handled by this driver.
23    const ID: ModuleID;
24}
25
26/// Initializes a module and publishes the capabilities it provides.
27///
28/// The platform transfers ownership of the complete [`GpioBank`] to the driver.
29/// Bus resources allocated through [`BusAllocator`] are startup allocations and
30/// remain owned by the resulting capability for the rest of the boot.
31///
32/// Drivers are run on core 1, so any task spawn will also run on core 1
33/// The Apps and resource method calls run on core 0, so you can use both cores
34///
35/// # Example
36///
37/// A two-button module can consume GPIO 0 and GPIO 1 and publish each button as
38/// an independent resource:
39///
40/// ```ignore
41/// use xpanse_api::{
42///     bus::allocator::BusAllocator,
43///     driver::{Driver, DriverError, DriverMeta},
44///     gpio_bank::{BankPins, GpioBank},
45///     interfaces::buttons::{A, B, pin_button},
46///     metadata::{ModuleDetectResistor, ModuleID, ModuleSlot},
47///     registry::Registry,
48/// };
49///
50/// struct TwoButtonDriver;
51///
52/// impl DriverMeta for TwoButtonDriver {
53///     const ID: ModuleID = ModuleID {
54///         md0: ModuleDetectResistor::R1K6,
55///         md1: ModuleDetectResistor::R1K5,
56///     };
57/// }
58///
59/// impl<G: BankPins> Driver<G> for TwoButtonDriver {
60///     async fn create(
61///         bank: GpioBank<G>,
62///         slot: ModuleSlot,
63///         registry: &mut Registry,
64///         buses: &mut BusAllocator,
65///     ) -> Result<(), DriverError> {
66///         registry.register(slot, Self::ID, pin_button::<A>(bank.gpio0.into()));
67///         registry.register(slot, Self::ID, pin_button::<B>(bank.gpio1.into()));
68///         let _ = buses;
69///         Ok(())
70///     }
71/// }
72/// ```
73pub trait Driver<G: BankPins>: DriverMeta {
74    /// Consumes a module's pins, initializes its hardware, and registers its
75    /// capabilities.
76    ///
77    /// Implementations should avoid publishing partially initialized resources
78    /// and return [`DriverError::InitFailed`] if setup cannot complete.
79    ///
80    /// Can create new tasks which run on core 1
81    fn create(
82        gpio_bank: GpioBank<G>,
83        slot: ModuleSlot,
84        registry: &mut Registry,
85        bus_allocator: &mut BusAllocator,
86    ) -> impl Future<Output = Result<(), DriverError>>;
87}
88
89pub trait DualSlotDriver<G1: BankPins, G2: BankPins>: DriverMeta {
90    /// Consumes a module's pins, initializes its hardware, and registers its
91    /// capabilities.
92    ///
93    /// Implementations should avoid publishing partially initialized resources
94    /// and return [`DriverError::InitFailed`] if setup cannot complete.
95    ///
96    /// Can create new tasks which run on core 1
97    ///
98    /// 0 is right, and 1 is left for the tuples
99    fn create(
100        gpio_banks: (GpioBank<G1>, GpioBank<G2>),
101        slots: (ModuleSlot, ModuleSlot),
102        registry: &mut Registry,
103        bus_allocator: &mut BusAllocator,
104    ) -> impl Future<Output = Result<(), DriverError>>;
105}