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}