components/
gpio.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Components for GPIO pins.
6//!
7//! Usage
8//! -----
9//!
10//! The `gpio_component_helper!` macro takes 'static references to GPIO pins.
11//! When GPIO instances are owned values, the `gpio_component_helper_owned!` can
12//! be used, indicating that the passed values are owned values. This macro will
13//! perform static allocation of the passed in GPIO pins internally.
14//!
15//! ```rust
16//! let gpio = components::gpio::GpioComponent::new(
17//!     board_kernel,
18//!     components::gpio_component_helper!(
19//!         nrf52840::gpio::GPIOPin,
20//!         // left side of the USB plug
21//!         0 => &nrf52840::gpio::PORT[Pin::P0_13],
22//!         1 => &nrf52840::gpio::PORT[Pin::P0_15],
23//!         2 => &nrf52840::gpio::PORT[Pin::P0_17],
24//!         3 => &nrf52840::gpio::PORT[Pin::P0_20],
25//!         4 => &nrf52840::gpio::PORT[Pin::P0_22],
26//!         5 => &nrf52840::gpio::PORT[Pin::P0_24],
27//!         6 => &nrf52840::gpio::PORT[Pin::P1_00],
28//!         7 => &nrf52840::gpio::PORT[Pin::P0_09],
29//!         8 => &nrf52840::gpio::PORT[Pin::P0_10],
30//!         // right side of the USB plug
31//!         9 => &nrf52840::gpio::PORT[Pin::P0_31],
32//!         10 => &nrf52840::gpio::PORT[Pin::P0_29],
33//!         11 => &nrf52840::gpio::PORT[Pin::P0_02],
34//!         12 => &nrf52840::gpio::PORT[Pin::P1_15],
35//!         13 => &nrf52840::gpio::PORT[Pin::P1_13],
36//!         14 => &nrf52840::gpio::PORT[Pin::P1_10],
37//!         // Below the PCB
38//!         15 => &nrf52840::gpio::PORT[Pin::P0_26],
39//!         16 => &nrf52840::gpio::PORT[Pin::P0_04],
40//!         17 => &nrf52840::gpio::PORT[Pin::P0_11],
41//!         18 => &nrf52840::gpio::PORT[Pin::P0_14],
42//!         19 => &nrf52840::gpio::PORT[Pin::P1_11],
43//!         20 => &nrf52840::gpio::PORT[Pin::P1_07],
44//!         21 => &nrf52840::gpio::PORT[Pin::P1_01],
45//!         22 => &nrf52840::gpio::PORT[Pin::P1_04],
46//!         23 => &nrf52840::gpio::PORT[Pin::P1_02]
47//!     ),
48//! ).finalize(components::gpio_component_static!(nrf52840::gpio::GPIOPin));
49//! ```
50
51use capsules_core::gpio::GPIO;
52use core::mem::MaybeUninit;
53use kernel::capabilities;
54use kernel::component::Component;
55use kernel::create_capability;
56use kernel::hil::gpio;
57use kernel::hil::gpio::InterruptWithValue;
58
59#[macro_export]
60macro_rules! gpio_component_helper_max_pin {
61    () => { 0usize };
62    ($a:expr, $b:expr, $($tail:expr),* $(,)?) => { $crate::gpio_component_helper_max_pin! (max ($a, $b), $($tail,)*) };
63    ($a:expr $(,)?) => { $a };
64}
65
66#[macro_export]
67macro_rules! gpio_component_helper_owned {
68    (
69        $Pin:ty,
70        $($nr:literal => $pin:expr),* $(,)?
71    ) => {
72        $crate::gpio_component_helper!(
73            $Pin,
74            $(
75                $nr => static_init!($Pin, $pin),
76            )*
77        )
78    };
79}
80
81#[macro_export]
82/// Pins are declared using the following format:
83///     number => pin
84///
85/// Any pin numbers that are skipped will be declared as None
86/// and using them from user space will return NODEVICE
87macro_rules! gpio_component_helper {
88    (
89        $Pin:ty,
90        $($nr:literal => $pin:expr),* $(,)?
91    ) => {{
92        use kernel::count_expressions;
93        use kernel::hil::gpio::InterruptValueWrapper;
94        use kernel::static_init;
95
96        const fn max (a: usize, b: usize) -> usize {
97            [a, b][(a < b) as usize]
98        }
99
100        const NUM_PINS: usize = $crate::gpio_component_helper_max_pin! ($($nr,)*) + 1;
101
102        let mut pins = static_init!(
103            [Option<&'static InterruptValueWrapper<'static, $Pin>>; NUM_PINS],
104            [None; NUM_PINS]
105        );
106
107        $(
108            pins[$nr] = Some(static_init!(InterruptValueWrapper<$Pin>, InterruptValueWrapper::new($pin)).finalize());
109        )*
110
111        pins
112    };};
113}
114
115#[macro_export]
116macro_rules! gpio_component_static {
117    ($Pin:ty $(,)?) => {{
118        kernel::static_buf!(capsules_core::gpio::GPIO<'static, $Pin>)
119    };};
120}
121
122pub type GpioComponentType<IP> = GPIO<'static, IP>;
123
124pub struct GpioComponent<IP: 'static + gpio::InterruptPin<'static>> {
125    board_kernel: &'static kernel::Kernel,
126    driver_num: usize,
127    gpio_pins: &'static [Option<&'static gpio::InterruptValueWrapper<'static, IP>>],
128}
129
130impl<IP: 'static + gpio::InterruptPin<'static>> GpioComponent<IP> {
131    pub fn new(
132        board_kernel: &'static kernel::Kernel,
133        driver_num: usize,
134        gpio_pins: &'static [Option<&'static gpio::InterruptValueWrapper<'static, IP>>],
135    ) -> Self {
136        Self {
137            board_kernel,
138            driver_num,
139            gpio_pins,
140        }
141    }
142}
143
144impl<IP: 'static + gpio::InterruptPin<'static>> Component for GpioComponent<IP> {
145    type StaticInput = &'static mut MaybeUninit<GPIO<'static, IP>>;
146    type Output = &'static GPIO<'static, IP>;
147
148    fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
149        let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
150        let gpio = static_buffer.write(GPIO::new(
151            self.gpio_pins,
152            self.board_kernel.create_grant(self.driver_num, &grant_cap),
153        ));
154        for maybe_pin in self.gpio_pins.iter() {
155            if let Some(pin) = maybe_pin {
156                pin.set_client(gpio);
157            }
158        }
159
160        gpio
161    }
162}