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}