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}