capsules_core/
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//! Provides userspace applications with access to GPIO pins.
6//!
7//! GPIOs are presented through a driver interface with synchronous commands
8//! and a callback for interrupts.
9//!
10//! This capsule takes an array of pins to expose as generic GPIOs.
11//! Note that this capsule is used for general purpose GPIOs. Pins that are
12//! attached to LEDs or buttons are generally wired directly to those capsules,
13//! not through this capsule as an intermediary.
14//!
15//! Usage
16//! -----
17//!
18//! ```rust,ignore
19//! # use kernel::static_init;
20//!
21//! let gpio_pins = static_init!(
22//!     [Option<&'static sam4l::gpio::GPIOPin>; 4],
23//!     [Option<&sam4l::gpio::PB[14]>,
24//!      Option<&sam4l::gpio::PB[15]>,
25//!      Option<&sam4l::gpio::PB[11]>,
26//!      Option<&sam4l::gpio::PB[12]>]);
27//! let gpio = static_init!(
28//!     capsules_core::gpio::GPIO<'static, sam4l::gpio::GPIOPin>,
29//!     capsules_core::gpio::GPIO::new(gpio_pins));
30//! for maybe_pin in gpio_pins.iter() {
31//!     if let Some(pin) = maybe_pin {
32//!         pin.set_client(gpio);
33//!     }
34//! }
35//! ```
36//!
37//! Syscall Interface
38//! -----------------
39//!
40//! - Stability: 2 - Stable
41//!
42//! ### Commands
43//!
44//! All GPIO operations are synchronous.
45//!
46//! Commands control and query GPIO information, namely how many GPIOs are
47//! present, the GPIO direction and state, and whether they should interrupt.
48//!
49//! ### Subscribes
50//!
51//! The GPIO interface provides only one callback, which is used for pins that
52//! have had interrupts enabled.
53
54/// Syscall driver number.
55use crate::driver;
56pub const DRIVER_NUM: usize = driver::NUM::Gpio as usize;
57
58use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
59use kernel::hil::gpio;
60use kernel::hil::gpio::{Configure, Input, InterruptWithValue, Output};
61use kernel::syscall::{CommandReturn, SyscallDriver};
62use kernel::{ErrorCode, ProcessId};
63
64/// ### `subscribe_num`
65///
66/// - `0`: Subscribe to interrupts from all pins with interrupts enabled. The
67///   callback signature is `fn(pin_num: usize, pin_state: bool)`
68const UPCALL_NUM: usize = 0;
69
70pub struct GPIO<'a, IP: gpio::InterruptPin<'a>> {
71    pins: &'a [Option<&'a gpio::InterruptValueWrapper<'a, IP>>],
72    apps: Grant<(), UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
73}
74
75impl<'a, IP: gpio::InterruptPin<'a>> GPIO<'a, IP> {
76    pub fn new(
77        pins: &'a [Option<&'a gpio::InterruptValueWrapper<'a, IP>>],
78        grant: Grant<(), UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
79    ) -> Self {
80        for (i, maybe_pin) in pins.iter().enumerate() {
81            if let Some(pin) = maybe_pin {
82                pin.set_value(i as u32);
83            }
84        }
85        Self { pins, apps: grant }
86    }
87
88    fn configure_input_pin(&self, pin_num: u32, config: usize) -> CommandReturn {
89        let maybe_pin = self.pins[pin_num as usize];
90        if let Some(pin) = maybe_pin {
91            pin.make_input();
92            match config {
93                0 => {
94                    pin.set_floating_state(gpio::FloatingState::PullNone);
95                    CommandReturn::success()
96                }
97                1 => {
98                    pin.set_floating_state(gpio::FloatingState::PullUp);
99                    CommandReturn::success()
100                }
101                2 => {
102                    pin.set_floating_state(gpio::FloatingState::PullDown);
103                    CommandReturn::success()
104                }
105                _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
106            }
107        } else {
108            CommandReturn::failure(ErrorCode::NODEVICE)
109        }
110    }
111
112    fn configure_interrupt(&self, pin_num: u32, config: usize) -> CommandReturn {
113        let pins = self.pins;
114        let index = pin_num as usize;
115        if let Some(pin) = pins[index] {
116            match config {
117                0 => {
118                    let _ = pin.enable_interrupts(gpio::InterruptEdge::EitherEdge);
119                    CommandReturn::success()
120                }
121
122                1 => {
123                    let _ = pin.enable_interrupts(gpio::InterruptEdge::RisingEdge);
124                    CommandReturn::success()
125                }
126
127                2 => {
128                    let _ = pin.enable_interrupts(gpio::InterruptEdge::FallingEdge);
129                    CommandReturn::success()
130                }
131
132                _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
133            }
134        } else {
135            CommandReturn::failure(ErrorCode::NODEVICE)
136        }
137    }
138}
139
140impl<'a, IP: gpio::InterruptPin<'a>> gpio::ClientWithValue for GPIO<'a, IP> {
141    fn fired(&self, pin_num: u32) {
142        // read the value of the pin
143        let pins = self.pins;
144        if let Some(pin) = pins[pin_num as usize] {
145            let pin_state = pin.read();
146
147            // schedule callback with the pin number and value
148            self.apps.each(|_, _, upcalls| {
149                let _ =
150                    upcalls.schedule_upcall(UPCALL_NUM, (pin_num as usize, pin_state as usize, 0));
151            });
152        }
153    }
154}
155
156impl<'a, IP: gpio::InterruptPin<'a>> SyscallDriver for GPIO<'a, IP> {
157    /// Query and control pin values and states.
158    ///
159    /// Each byte of the `data` argument is treated as its own field.
160    /// For all commands, the lowest order halfword is the pin number (`pin`).
161    /// A few commands use higher order bytes for purposes documented below.
162    /// If the higher order bytes are not used, they must be set to `0`.
163    ///
164    /// Other data bytes:
165    ///
166    /// - `pin_config`: An internal resistor setting.
167    ///   - Set to `0` for a pull-up resistor.
168    ///   - Set to `1` for a pull-down resistor.
169    ///   - Set to `2` for none.
170    /// - `irq_config`: Interrupt configuration setting.
171    ///   - Set to `0` to interrupt on either edge.
172    ///   - Set to `1` for rising edge.
173    ///   - Set to `2` for falling edge.
174    ///
175    /// ### `command_num`
176    ///
177    /// - `0`: Driver existence check.
178    /// - `1`: Enable output on `pin`.
179    /// - `2`: Set `pin`.
180    /// - `3`: Clear `pin`.
181    /// - `4`: Toggle `pin`.
182    /// - `5`: Enable input on `pin` with `pin_config` in 0x00XX00000
183    /// - `6`: Read `pin` value.
184    /// - `7`: Configure interrupt on `pin` with `irq_config` in 0x00XX00000
185    /// - `8`: Disable interrupt on `pin`.
186    /// - `9`: Disable `pin`.
187    /// - `10`: Get number of GPIO ports supported.
188    fn command(
189        &self,
190        command_num: usize,
191        data1: usize,
192        data2: usize,
193        _: ProcessId,
194    ) -> CommandReturn {
195        let pins = self.pins;
196        let pin_index = data1;
197        match command_num {
198            // Check existence.
199            0 => CommandReturn::success(),
200
201            // enable output
202            1 => {
203                if pin_index >= pins.len() {
204                    /* impossible pin */
205                    CommandReturn::failure(ErrorCode::INVAL)
206                } else {
207                    if let Some(pin) = pins[pin_index] {
208                        pin.make_output();
209                        CommandReturn::success()
210                    } else {
211                        CommandReturn::failure(ErrorCode::NODEVICE)
212                    }
213                }
214            }
215
216            // set pin
217            2 => {
218                if pin_index >= pins.len() {
219                    /* impossible pin */
220                    CommandReturn::failure(ErrorCode::INVAL)
221                } else {
222                    if let Some(pin) = pins[pin_index] {
223                        pin.set();
224                        CommandReturn::success()
225                    } else {
226                        CommandReturn::failure(ErrorCode::NODEVICE)
227                    }
228                }
229            }
230
231            // clear pin
232            3 => {
233                if pin_index >= pins.len() {
234                    /* impossible pin */
235                    CommandReturn::failure(ErrorCode::INVAL)
236                } else {
237                    if let Some(pin) = pins[pin_index] {
238                        pin.clear();
239                        CommandReturn::success()
240                    } else {
241                        CommandReturn::failure(ErrorCode::NODEVICE)
242                    }
243                }
244            }
245
246            // toggle pin
247            4 => {
248                if pin_index >= pins.len() {
249                    /* impossible pin */
250                    CommandReturn::failure(ErrorCode::INVAL)
251                } else {
252                    if let Some(pin) = pins[pin_index] {
253                        pin.toggle();
254                        CommandReturn::success()
255                    } else {
256                        CommandReturn::failure(ErrorCode::NODEVICE)
257                    }
258                }
259            }
260
261            // enable and configure input
262            5 => {
263                let pin_config = data2;
264                if pin_index >= pins.len() {
265                    /* impossible pin */
266                    CommandReturn::failure(ErrorCode::INVAL)
267                } else {
268                    self.configure_input_pin(pin_index as u32, pin_config)
269                }
270            }
271
272            // read input
273            6 => {
274                if pin_index >= pins.len() {
275                    /* impossible pin */
276                    CommandReturn::failure(ErrorCode::INVAL)
277                } else {
278                    if let Some(pin) = pins[pin_index] {
279                        let pin_state = pin.read();
280                        CommandReturn::success_u32(pin_state as u32)
281                    } else {
282                        CommandReturn::failure(ErrorCode::NODEVICE)
283                    }
284                }
285            }
286
287            // configure interrupts on pin
288            // (no affect or reliance on registered callback)
289            7 => {
290                let irq_config = data2;
291                if pin_index >= pins.len() {
292                    /* impossible pin */
293                    CommandReturn::failure(ErrorCode::INVAL)
294                } else {
295                    self.configure_interrupt(pin_index as u32, irq_config)
296                }
297            }
298
299            // disable interrupts on pin, also disables pin
300            // (no affect or reliance on registered callback)
301            8 => {
302                if pin_index >= pins.len() {
303                    /* impossible pin */
304                    CommandReturn::failure(ErrorCode::INVAL)
305                } else {
306                    if let Some(pin) = pins[pin_index] {
307                        pin.disable_interrupts();
308                        pin.deactivate_to_low_power();
309                        CommandReturn::success()
310                    } else {
311                        CommandReturn::failure(ErrorCode::NODEVICE)
312                    }
313                }
314            }
315
316            // disable pin
317            9 => {
318                if pin_index >= pins.len() {
319                    /* impossible pin */
320                    CommandReturn::failure(ErrorCode::INVAL)
321                } else {
322                    if let Some(pin) = pins[pin_index] {
323                        pin.deactivate_to_low_power();
324                        CommandReturn::success()
325                    } else {
326                        CommandReturn::failure(ErrorCode::NODEVICE)
327                    }
328                }
329            }
330
331            // number of pins
332            10 => CommandReturn::success_u32(pins.len() as u32),
333
334            // default
335            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
336        }
337    }
338
339    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
340        self.apps.enter(processid, |_, _| {})
341    }
342}