Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leds driver (v1) #359

Merged
merged 7 commits into from
Jan 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ debug = true
[workspace]
exclude = [ "tock" ]
members = [
"apis/leds",
"apis/low_level_debug",
"codegen",
"core",
Expand Down
14 changes: 14 additions & 0 deletions apis/leds/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "libtock_leds"
version = "0.1.0"
authors = ["Tock Project Developers <[email protected]>"]
license = "MIT/Apache-2.0"
edition = "2018"
repository = "https://www.github.com/tock/libtock-rs"
description = "libtock leds driver"

[dependencies]
libtock_platform = { path = "../../platform" }

[dev-dependencies]
libtock_unittest = { path = "../../unittest" }
48 changes: 48 additions & 0 deletions apis/leds/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#![no_std]

use libtock_platform::Syscalls;

/// The LEDs driver
///
/// # Example
/// ```ignore
/// use libtock2::Leds;
///
/// // Turn on led 0
/// Leds::on(0);
/// ```
pub struct Leds<S: Syscalls>(S);

impl<S: Syscalls> Leds<S> {
/// Run a check against the leds capsule to ensure it is present.
///
/// Returns `Some(number_of_leds)` if the driver was present. This does not necessarily mean
/// that the driver is working, as it may still fail to allocate grant
/// memory.
pub fn count() -> Option<u32> {
S::command(DRIVER_ID, LEDS_COUNT, 0, 0).get_success_u32()
}

pub fn on(led: u32) {
S::command(DRIVER_ID, LED_ON, led, 0);
}
alexandruradovici marked this conversation as resolved.
Show resolved Hide resolved

pub fn off(led: u32) {
S::command(DRIVER_ID, LED_OFF, led, 0);
}

pub fn toggle(led: u32) {
S::command(DRIVER_ID, LED_TOGGLE, led, 0);
}
}

const DRIVER_ID: u32 = 2;

// Command IDs
const LEDS_COUNT: u32 = 0;
const LED_ON: u32 = 1;
const LED_OFF: u32 = 2;
const LED_TOGGLE: u32 = 3;

#[cfg(test)]
mod tests;
85 changes: 85 additions & 0 deletions apis/leds/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use libtock_unittest::fake;

type Leds = super::Leds<fake::Syscalls>;

#[test]
fn no_driver() {
let _kernel = fake::Kernel::new();
assert_eq!(Leds::count(), None);
}

#[test]
fn driver_check() {
let kernel = fake::Kernel::new();
let driver = fake::Leds::<10>::new();
kernel.add_driver(&driver);

assert!(Leds::count().is_some());
for led in 0..10 {
assert_eq!(driver.get_led(led), Some(false));
}
}

#[test]
fn num_leds() {
let kernel = fake::Kernel::new();
let driver = fake::Leds::<10>::new();
kernel.add_driver(&driver);
assert_eq!(Leds::count().unwrap_or_default(), 10);
}

#[test]
fn on() {
let kernel = fake::Kernel::new();
let driver = fake::Leds::<10>::new();
kernel.add_driver(&driver);

Leds::on(0);
assert_eq!(driver.get_led(0), Some(true));
}

#[test]
fn off() {
let kernel = fake::Kernel::new();
let driver = fake::Leds::<10>::new();
kernel.add_driver(&driver);

Leds::off(0);
assert_eq!(driver.get_led(0), Some(false));
}

#[test]
fn toggle() {
let kernel = fake::Kernel::new();
let driver = fake::Leds::<10>::new();
kernel.add_driver(&driver);

Leds::toggle(0);
assert_eq!(driver.get_led(0), Some(true));
Leds::toggle(0);
assert_eq!(driver.get_led(0), Some(false));
}

#[test]
fn on_off() {
let kernel = fake::Kernel::new();
let driver = fake::Leds::<10>::new();
kernel.add_driver(&driver);

Leds::on(0);
assert_eq!(driver.get_led(0), Some(true));
Leds::off(0);
assert_eq!(driver.get_led(0), Some(false));
}

#[test]
fn no_led() {
let kernel = fake::Kernel::new();
let driver = fake::Leds::<10>::new();
kernel.add_driver(&driver);

Leds::on(11);
for led in 0..Leds::count().unwrap_or_default() {
assert_eq!(driver.get_led(led), Some(false));
}
}
1 change: 1 addition & 0 deletions libtock2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ version = "0.1.0"
[dependencies]
libtock_platform = { path = "../platform" }
libtock_runtime = { path = "../runtime" }
libtock_leds = { path = "../apis/leds" }
libtock_low_level_debug = { path = "../apis/low_level_debug" }

# TODO: Implement a panic handler with more debugging functionality, then
Expand Down
18 changes: 18 additions & 0 deletions libtock2/examples/leds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! An extremely simple libtock-rs example. Just turns on all the LEDs.

#![no_main]
#![no_std]

use libtock2::leds::Leds;
use libtock2::runtime::{set_main, stack_size};

set_main! {main}
stack_size! {0x100}

fn main() {
if let Some(leds_count) = Leds::count() {
for led_index in 0..leds_count {
Leds::on(led_index as u32);
}
}
}
4 changes: 4 additions & 0 deletions libtock2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ extern crate libtock_small_panic;
pub use libtock_platform as platform;
pub use libtock_runtime as runtime;

pub mod leds {
use libtock_leds as leds;
pub type Leds = leds::Leds<super::runtime::TockSyscalls>;
}
pub mod low_level_debug {
use libtock_low_level_debug as lldb;
pub type LowLevelDebug = lldb::LowLevelDebug<super::runtime::TockSyscalls>;
Expand Down
83 changes: 83 additions & 0 deletions unittest/src/fake/leds/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! Fake implementation of the LEDs API, documented here:
//! https://github.com/tock/tock/blob/master/doc/syscalls/00002_leds.md
//!
//! Like the real API, `Leds` controls a set of fake LEDs. It provides
//! a function `get_led` used to retrieve the state of an LED.

use core::cell::Cell;
use libtock_platform::{CommandReturn, ErrorCode};

pub struct Leds<const LEDS_COUNT: usize> {
leds: [Cell<bool>; LEDS_COUNT],
}

impl<const LEDS_COUNT: usize> Leds<LEDS_COUNT> {
pub fn new() -> std::rc::Rc<Leds<LEDS_COUNT>> {
#[allow(clippy::declare_interior_mutable_const)]
const OFF: Cell<bool> = Cell::new(false);
std::rc::Rc::new(Leds {
leds: [OFF; LEDS_COUNT],
alexandruradovici marked this conversation as resolved.
Show resolved Hide resolved
})
}
}

impl<const LEDS_COUNT: usize> crate::fake::SyscallDriver for Leds<LEDS_COUNT> {
fn id(&self) -> u32 {
DRIVER_NUMBER
}
fn num_upcalls(&self) -> u32 {
0
}

fn command(&self, command_number: u32, argument0: u32, _argument1: u32) -> CommandReturn {
match command_number {
DRIVER_CHECK => crate::command_return::success_u32(LEDS_COUNT as u32),
LED_ON => {
if argument0 < LEDS_COUNT as u32 {
self.leds[argument0 as usize].set(true);
crate::command_return::success()
} else {
crate::command_return::failure(ErrorCode::Invalid)
}
}
LED_OFF => {
if argument0 < LEDS_COUNT as u32 {
self.leds[argument0 as usize].set(false);
crate::command_return::success()
} else {
crate::command_return::failure(ErrorCode::Invalid)
}
}
LED_TOGGLE => {
if argument0 < LEDS_COUNT as u32 {
self.leds[argument0 as usize].set(!self.leds[argument0 as usize].get());
crate::command_return::success()
} else {
crate::command_return::failure(ErrorCode::Invalid)
}
}
_ => crate::command_return::failure(ErrorCode::NoSupport),
}
}
}

// -----------------------------------------------------------------------------
// Implementation details below
// -----------------------------------------------------------------------------

#[cfg(test)]
mod tests;

const DRIVER_NUMBER: u32 = 2;

// Command numbers
const DRIVER_CHECK: u32 = 0;
const LED_ON: u32 = 1;
const LED_OFF: u32 = 2;
const LED_TOGGLE: u32 = 3;

impl<const NUM_LEDS: usize> Leds<NUM_LEDS> {
pub fn get_led(&self, led: u32) -> Option<bool> {
self.leds.get(led as usize).map(|led| led.get())
}
}
51 changes: 51 additions & 0 deletions unittest/src/fake/leds/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::fake;
use fake::leds::*;
use libtock_platform::ErrorCode;

// Tests the command implementation.
#[test]
fn command() {
use fake::SyscallDriver;
let leds = Leds::<10>::new();
let value = leds.command(DRIVER_CHECK, 1, 2);
assert_eq!(value.get_success_u32(), Some(10));
assert_eq!(
leds.command(LED_ON, 11, 0).get_failure(),
Some(ErrorCode::Invalid)
);
assert_eq!(leds.get_led(0), Some(false));
assert!(leds.command(LED_ON, 0, 0).is_success());
assert_eq!(leds.get_led(0), Some(true));
assert!(leds.command(LED_OFF, 0, 0).is_success());
assert_eq!(leds.get_led(0), Some(false));
assert!(leds.command(LED_TOGGLE, 0, 0).is_success());
assert_eq!(leds.get_led(0), Some(true));
assert!(leds.command(LED_TOGGLE, 0, 0).is_success());
assert_eq!(leds.get_led(0), Some(false));
}

// Integration test that verifies Leds works with fake::Kernel and
// libtock_platform::Syscalls.
#[test]
fn kernel_integration() {
use libtock_platform::Syscalls;
let kernel = fake::Kernel::new();
let leds = Leds::<10>::new();
kernel.add_driver(&leds);
let value = fake::Syscalls::command(DRIVER_NUMBER, DRIVER_CHECK, 1, 2);
assert!(value.is_success_u32());
assert_eq!(value.get_success_u32(), Some(10));
assert_eq!(
fake::Syscalls::command(DRIVER_NUMBER, LED_ON, 11, 0).get_failure(),
Some(ErrorCode::Invalid)
);
assert_eq!(leds.get_led(0), Some(false));
assert!(fake::Syscalls::command(DRIVER_NUMBER, LED_ON, 0, 0).is_success());
assert_eq!(leds.get_led(0), Some(true));
assert!(fake::Syscalls::command(DRIVER_NUMBER, LED_OFF, 0, 0).is_success());
assert_eq!(leds.get_led(0), Some(false));
assert!(fake::Syscalls::command(DRIVER_NUMBER, LED_TOGGLE, 0, 0).is_success());
assert_eq!(leds.get_led(0), Some(true));
assert!(fake::Syscalls::command(DRIVER_NUMBER, LED_TOGGLE, 0, 0).is_success());
assert_eq!(leds.get_led(0), Some(false));
}
2 changes: 2 additions & 0 deletions unittest/src/fake/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
//! (e.g. `fake::Console`).

mod kernel;
mod leds;
mod low_level_debug;
mod syscall_driver;
mod syscalls;

pub use kernel::Kernel;
pub use leds::Leds;
pub use low_level_debug::{LowLevelDebug, Message};
pub use syscall_driver::SyscallDriver;
pub use syscalls::Syscalls;
Expand Down