capsules_core/led.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 access to LEDs on a board.
6//!
7//! This allows for much more cross platform controlling of LEDs without having
8//! to know which of the GPIO pins exposed across the syscall interface are
9//! LEDs.
10//!
11//! This capsule takes an array of pins and the polarity of the LED (active high
12//! or active low). This allows the board to configure how the underlying GPIO
13//! must be controlled to turn on and off LEDs, such that the syscall driver
14//! interface can be agnostic to the LED polarity.
15//!
16//! Usage
17//! -----
18//!
19//! ```rust,ignore
20//! # use kernel::static_init;
21//!
22//! let led_pins = static_init!(
23//! [(&'static sam4l::gpio::GPIOPin, kernel::hil::gpio::ActivationMode); 3],
24//! [(&sam4l::gpio::PA[13], kernel::hil::gpio::ActivationMode::ActiveLow), // Red
25//! (&sam4l::gpio::PA[15], kernel::hil::gpio::ActivationMode::ActiveLow), // Green
26//! (&sam4l::gpio::PA[14], kernel::hil::gpio::ActivationMode::ActiveLow)]); // Blue
27//! let led = static_init!(
28//! capsules_core::led::LED<'static, sam4l::gpio::GPIOPin>,
29//! capsules_core::led::LED::new(led_pins));
30//! ```
31//!
32//! Syscall Interface
33//! -----------------
34//!
35//! - Stability: 2 - Stable
36//!
37//! ### Command
38//!
39//! All LED operations are synchronous, so this capsule only uses the `command`
40//! syscall.
41//!
42//! #### `command_num`
43//!
44//! - `0`: Return the number of LEDs on this platform.
45//! - `data`: Unused.
46//! - Return: Number of LEDs.
47//! - `1`: Turn the LED on.
48//! - `data`: The index of the LED. Starts at 0.
49//! - Return: `Ok(())` if the LED index was valid, `INVAL` otherwise.
50//! - `2`: Turn the LED off.
51//! - `data`: The index of the LED. Starts at 0.
52//! - Return: `Ok(())` if the LED index was valid, `INVAL` otherwise.
53//! - `3`: Toggle the on/off state of the LED.
54//! - `data`: The index of the LED. Starts at 0.
55//! - Return: `Ok(())` if the LED index was valid, `INVAL` otherwise.
56//! - `4`: Read the current state of the LED.
57//! - `data`: The index of the LED. Starts at 0.
58//! - Return: `INVAL` if the LED index was invalid, `Ok(0u32)` if the LED is
59//! off, `Ok(1u32)` if the LED is on.
60
61use kernel::hil::led;
62use kernel::syscall::{CommandReturn, SyscallDriver};
63use kernel::{ErrorCode, ProcessId};
64
65/// Syscall driver number.
66use crate::driver;
67pub const DRIVER_NUM: usize = driver::NUM::Led as usize;
68
69/// Holds the array of LEDs and implements a `Driver` interface to
70/// control them.
71pub struct LedDriver<'a, L: led::Led, const NUM_LEDS: usize> {
72 leds: &'a [&'a L; NUM_LEDS],
73}
74
75impl<'a, L: led::Led, const NUM_LEDS: usize> LedDriver<'a, L, NUM_LEDS> {
76 pub fn new(leds: &'a [&'a L; NUM_LEDS]) -> Self {
77 // Initialize all LEDs and turn them off
78 for led in leds.iter() {
79 led.init();
80 led.off();
81 }
82
83 Self { leds }
84 }
85}
86
87impl<L: led::Led, const NUM_LEDS: usize> SyscallDriver for LedDriver<'_, L, NUM_LEDS> {
88 /// Control the LEDs.
89 ///
90 /// ### `command_num`
91 ///
92 /// - `0`: Returns the number of LEDs on the board. This will always be 0 or
93 /// greater, and therefore also allows for checking for this driver.
94 /// - `1`: Turn the LED at index specified by `data` on. Returns `INVAL` if
95 /// the LED index is not valid.
96 /// - `2`: Turn the LED at index specified by `data` off. Returns `INVAL` if
97 /// the LED index is not valid.
98 /// - `3`: Toggle the LED at index specified by `data` on or off. Returns
99 /// `INVAL` if the LED index is not valid.
100 /// - `4`: Read the LED state at index specified by `data`. Returns `INVAL`
101 /// if the LED index is not valid.
102 fn command(&self, command_num: usize, data: usize, _: usize, _: ProcessId) -> CommandReturn {
103 match command_num {
104 // get number of LEDs
105 // TODO(Tock 3.0): TRD104 specifies that Command 0 should return Success, not SuccessU32,
106 // but this driver is unchanged since it has been stabilized. It will be brought into
107 // compliance as part of the next major release of Tock. See #3375.
108 0 => CommandReturn::success_u32(NUM_LEDS as u32),
109
110 // on
111 1 => {
112 if data >= NUM_LEDS {
113 CommandReturn::failure(ErrorCode::INVAL) /* led out of range */
114 } else {
115 self.leds[data].on();
116 CommandReturn::success()
117 }
118 }
119
120 // off
121 2 => {
122 if data >= NUM_LEDS {
123 CommandReturn::failure(ErrorCode::INVAL) /* led out of range */
124 } else {
125 self.leds[data].off();
126 CommandReturn::success()
127 }
128 }
129
130 // toggle
131 3 => {
132 if data >= NUM_LEDS {
133 CommandReturn::failure(ErrorCode::INVAL) /* led out of range */
134 } else {
135 self.leds[data].toggle();
136 CommandReturn::success()
137 }
138 }
139
140 // read
141 4 => {
142 if data >= NUM_LEDS {
143 CommandReturn::failure(ErrorCode::INVAL) /* led out of range */
144 } else {
145 let value = self.leds[data].read();
146 CommandReturn::success_u32(value as u32)
147 }
148 }
149
150 // default
151 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
152 }
153 }
154
155 fn allocate_grant(&self, _processid: ProcessId) -> Result<(), kernel::process::Error> {
156 Ok(())
157 }
158}