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                upcalls
150                    .schedule_upcall(UPCALL_NUM, (pin_num as usize, pin_state as usize, 0))
151                    .ok();
152            });
153        }
154    }
155}
156
157impl<'a, IP: gpio::InterruptPin<'a>> SyscallDriver for GPIO<'a, IP> {
158    /// Query and control pin values and states.
159    ///
160    /// Each byte of the `data` argument is treated as its own field.
161    /// For all commands, the lowest order halfword is the pin number (`pin`).
162    /// A few commands use higher order bytes for purposes documented below.
163    /// If the higher order bytes are not used, they must be set to `0`.
164    ///
165    /// Other data bytes:
166    ///
167    /// - `pin_config`: An internal resistor setting.
168    ///   - Set to `0` for a pull-up resistor.
169    ///   - Set to `1` for a pull-down resistor.
170    ///   - Set to `2` for none.
171    /// - `irq_config`: Interrupt configuration setting.
172    ///   - Set to `0` to interrupt on either edge.
173    ///   - Set to `1` for rising edge.
174    ///   - Set to `2` for falling edge.
175    ///
176    /// ### `command_num`
177    ///
178    /// - `0`: Driver existence check.
179    /// - `1`: Enable output on `pin`.
180    /// - `2`: Set `pin`.
181    /// - `3`: Clear `pin`.
182    /// - `4`: Toggle `pin`.
183    /// - `5`: Enable input on `pin` with `pin_config` in 0x00XX00000
184    /// - `6`: Read `pin` value.
185    /// - `7`: Configure interrupt on `pin` with `irq_config` in 0x00XX00000
186    /// - `8`: Disable interrupt on `pin`.
187    /// - `9`: Disable `pin`.
188    /// - `10`: Get number of GPIO ports supported.
189    fn command(
190        &self,
191        command_num: usize,
192        data1: usize,
193        data2: usize,
194        _: ProcessId,
195    ) -> CommandReturn {
196        let pins = self.pins;
197        let pin_index = data1;
198        match command_num {
199            // Check existence.
200            0 => CommandReturn::success(),
201
202            // enable output
203            1 => {
204                if pin_index >= pins.len() {
205                    /* impossible pin */
206                    CommandReturn::failure(ErrorCode::INVAL)
207                } else {
208                    if let Some(pin) = pins[pin_index] {
209                        pin.make_output();
210                        CommandReturn::success()
211                    } else {
212                        CommandReturn::failure(ErrorCode::NODEVICE)
213                    }
214                }
215            }
216
217            // set pin
218            2 => {
219                if pin_index >= pins.len() {
220                    /* impossible pin */
221                    CommandReturn::failure(ErrorCode::INVAL)
222                } else {
223                    if let Some(pin) = pins[pin_index] {
224                        pin.set();
225                        CommandReturn::success()
226                    } else {
227                        CommandReturn::failure(ErrorCode::NODEVICE)
228                    }
229                }
230            }
231
232            // clear pin
233            3 => {
234                if pin_index >= pins.len() {
235                    /* impossible pin */
236                    CommandReturn::failure(ErrorCode::INVAL)
237                } else {
238                    if let Some(pin) = pins[pin_index] {
239                        pin.clear();
240                        CommandReturn::success()
241                    } else {
242                        CommandReturn::failure(ErrorCode::NODEVICE)
243                    }
244                }
245            }
246
247            // toggle pin
248            4 => {
249                if pin_index >= pins.len() {
250                    /* impossible pin */
251                    CommandReturn::failure(ErrorCode::INVAL)
252                } else {
253                    if let Some(pin) = pins[pin_index] {
254                        pin.toggle();
255                        CommandReturn::success()
256                    } else {
257                        CommandReturn::failure(ErrorCode::NODEVICE)
258                    }
259                }
260            }
261
262            // enable and configure input
263            5 => {
264                let pin_config = data2;
265                if pin_index >= pins.len() {
266                    /* impossible pin */
267                    CommandReturn::failure(ErrorCode::INVAL)
268                } else {
269                    self.configure_input_pin(pin_index as u32, pin_config)
270                }
271            }
272
273            // read input
274            6 => {
275                if pin_index >= pins.len() {
276                    /* impossible pin */
277                    CommandReturn::failure(ErrorCode::INVAL)
278                } else {
279                    if let Some(pin) = pins[pin_index] {
280                        let pin_state = pin.read();
281                        CommandReturn::success_u32(pin_state as u32)
282                    } else {
283                        CommandReturn::failure(ErrorCode::NODEVICE)
284                    }
285                }
286            }
287
288            // configure interrupts on pin
289            // (no affect or reliance on registered callback)
290            7 => {
291                let irq_config = data2;
292                if pin_index >= pins.len() {
293                    /* impossible pin */
294                    CommandReturn::failure(ErrorCode::INVAL)
295                } else {
296                    self.configure_interrupt(pin_index as u32, irq_config)
297                }
298            }
299
300            // disable interrupts on pin, also disables pin
301            // (no affect or reliance on registered callback)
302            8 => {
303                if pin_index >= pins.len() {
304                    /* impossible pin */
305                    CommandReturn::failure(ErrorCode::INVAL)
306                } else {
307                    if let Some(pin) = pins[pin_index] {
308                        pin.disable_interrupts();
309                        pin.deactivate_to_low_power();
310                        CommandReturn::success()
311                    } else {
312                        CommandReturn::failure(ErrorCode::NODEVICE)
313                    }
314                }
315            }
316
317            // disable pin
318            9 => {
319                if pin_index >= pins.len() {
320                    /* impossible pin */
321                    CommandReturn::failure(ErrorCode::INVAL)
322                } else {
323                    if let Some(pin) = pins[pin_index] {
324                        pin.deactivate_to_low_power();
325                        CommandReturn::success()
326                    } else {
327                        CommandReturn::failure(ErrorCode::NODEVICE)
328                    }
329                }
330            }
331
332            // number of pins
333            10 => CommandReturn::success_u32(pins.len() as u32),
334
335            // default
336            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
337        }
338    }
339
340    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
341        self.apps.enter(processid, |_, _| {})
342    }
343}