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}