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