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}