blob: 6133c0d27c2724061537aaa78963f940497901ce [file] [log] [blame]
//! 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
//! # 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::led::LED<'static, sam4l::gpio::GPIOPin>,
//! capsules::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: `SUCCESS` if the LED index was valid, `EINVAL` otherwise.
//! - `2`: Turn the LED off.
//! - `data`: The index of the LED. Starts at 0.
//! - Return: `SUCCESS` if the LED index was valid, `EINVAL` otherwise.
//! - `3`: Toggle the on/off state of the LED.
//! - `data`: The index of the LED. Starts at 0.
//! - Return: `SUCCESS` if the LED index was valid, `EINVAL` otherwise.
use kernel::hil::gpio;
use kernel::{AppId, Driver, ReturnCode};
/// Syscall driver number.
use crate::driver;
pub const DRIVER_NUM: usize = driver::NUM::Led as usize;
/// Holds the array of GPIO pins attached to the LEDs and implements a `Driver`
/// interface to control them.
pub struct LED<'a, P: gpio::Pin> {
pins_init: &'a [(&'a P, gpio::ActivationMode)],
}
impl<'a, P: gpio::Pin> LED<'a, P> {
pub fn new(pins_init: &'a [(&'a P, gpio::ActivationMode)]) -> Self {
// Make all pins output and off
for &(pin, mode) in pins_init.as_ref().iter() {
pin.make_output();
pin.write_activation(gpio::ActivationState::Inactive, mode);
}
Self {
pins_init: pins_init,
}
}
}
impl<P: gpio::Pin> Driver for LED<'_, P> {
/// 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 `EINVAL` if
/// the LED index is not valid.
/// - `2`: Turn the LED at index specified by `data` off. Returns `EINVAL`
/// if the LED index is not valid.
/// - `3`: Toggle the LED at index specified by `data` on or off. Returns
/// `EINVAL` if the LED index is not valid.
fn command(&self, command_num: usize, data: usize, _: usize, _: AppId) -> ReturnCode {
let pins_init = self.pins_init.as_ref();
match command_num {
// get number of LEDs
0 => ReturnCode::SuccessWithValue {
value: pins_init.len() as usize,
},
// on
1 => {
if data >= pins_init.len() {
ReturnCode::EINVAL /* impossible pin */
} else {
let (pin, mode) = pins_init[data];
pin.write_activation(gpio::ActivationState::Active, mode);
ReturnCode::SUCCESS
}
}
// off
2 => {
if data >= pins_init.len() {
ReturnCode::EINVAL /* impossible pin */
} else {
let (pin, mode) = pins_init[data];
pin.write_activation(gpio::ActivationState::Inactive, mode);
ReturnCode::SUCCESS
}
}
// toggle
3 => {
if data >= pins_init.len() {
ReturnCode::EINVAL /* impossible pin */
} else {
let (pin, _) = pins_init[data];
pin.toggle();
ReturnCode::SUCCESS
}
}
// default
_ => ReturnCode::ENOSUPPORT,
}
}
}