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
57use kernel::hil::led;
58use kernel::syscall::{CommandReturn, SyscallDriver};
59use kernel::{ErrorCode, ProcessId};
60
61/// Syscall driver number.
62use crate::driver;
63pub const DRIVER_NUM: usize = driver::NUM::Led as usize;
64
65/// Holds the array of LEDs and implements a `Driver` interface to
66/// control them.
67pub struct LedDriver<'a, L: led::Led, const NUM_LEDS: usize> {
68 leds: &'a [&'a L; NUM_LEDS],
69}
70
71impl<'a, L: led::Led, const NUM_LEDS: usize> LedDriver<'a, L, NUM_LEDS> {
72 pub fn new(leds: &'a [&'a L; NUM_LEDS]) -> Self {
73 // Initialize all LEDs and turn them off
74 for led in leds.iter() {
75 led.init();
76 led.off();
77 }
78
79 Self { leds }
80 }
81}
82
83impl<L: led::Led, const NUM_LEDS: usize> SyscallDriver for LedDriver<'_, L, NUM_LEDS> {
84 /// Control the LEDs.
85 ///
86 /// ### `command_num`
87 ///
88 /// - `0`: Returns the number of LEDs on the board. This will always be 0 or
89 /// greater, and therefore also allows for checking for this driver.
90 /// - `1`: Turn the LED at index specified by `data` on. Returns `INVAL` if
91 /// the LED index is not valid.
92 /// - `2`: Turn the LED at index specified by `data` off. Returns `INVAL` if
93 /// the LED index is not valid.
94 /// - `3`: Toggle the LED at index specified by `data` on or off. Returns
95 /// `INVAL` if the LED index is not valid.
96 fn command(&self, command_num: usize, data: usize, _: usize, _: ProcessId) -> CommandReturn {
97 match command_num {
98 // get number of LEDs
99 // TODO(Tock 3.0): TRD104 specifies that Command 0 should return Success, not SuccessU32,
100 // but this driver is unchanged since it has been stabilized. It will be brought into
101 // compliance as part of the next major release of Tock. See #3375.
102 0 => CommandReturn::success_u32(NUM_LEDS as u32),
103
104 // on
105 1 => {
106 if data >= NUM_LEDS {
107 CommandReturn::failure(ErrorCode::INVAL) /* led out of range */
108 } else {
109 self.leds[data].on();
110 CommandReturn::success()
111 }
112 }
113
114 // off
115 2 => {
116 if data >= NUM_LEDS {
117 CommandReturn::failure(ErrorCode::INVAL) /* led out of range */
118 } else {
119 self.leds[data].off();
120 CommandReturn::success()
121 }
122 }
123
124 // toggle
125 3 => {
126 if data >= NUM_LEDS {
127 CommandReturn::failure(ErrorCode::INVAL) /* led out of range */
128 } else {
129 self.leds[data].toggle();
130 CommandReturn::success()
131 }
132 }
133
134 // default
135 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
136 }
137 }
138
139 fn allocate_grant(&self, _processid: ProcessId) -> Result<(), kernel::process::Error> {
140 Ok(())
141 }
142}