From 397e3d2db1d6080f790fb0aa925c6a7f924f8df5 Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Thu, 27 Jan 2022 22:34:44 +0200 Subject: [PATCH 1/9] first implementation --- Cargo.toml | 1 + apis/gpio/Cargo.toml | 14 +++ apis/gpio/src/lib.rs | 209 +++++++++++++++++++++++++++++++++++++++++ apis/gpio/src/tests.rs | 85 +++++++++++++++++ 4 files changed, 309 insertions(+) create mode 100644 apis/gpio/Cargo.toml create mode 100644 apis/gpio/src/lib.rs create mode 100644 apis/gpio/src/tests.rs diff --git a/Cargo.toml b/Cargo.toml index 1b230c76..f1de2c8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,7 @@ debug = true [workspace] exclude = [ "tock" ] members = [ + "apis/gpio", "apis/leds", "apis/low_level_debug", "codegen", diff --git a/apis/gpio/Cargo.toml b/apis/gpio/Cargo.toml new file mode 100644 index 00000000..df4f68c8 --- /dev/null +++ b/apis/gpio/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "libtock_gpio" +version = "0.1.0" +authors = ["Tock Project Developers "] +license = "MIT/Apache-2.0" +edition = "2018" +repository = "https://www.github.com/tock/libtock-rs" +description = "libtock gpio driver" + +[dependencies] +libtock_platform = { path = "../../platform" } + +[dev-dependencies] +libtock_unittest = { path = "../../unittest" } diff --git a/apis/gpio/src/lib.rs b/apis/gpio/src/lib.rs new file mode 100644 index 00000000..756d221c --- /dev/null +++ b/apis/gpio/src/lib.rs @@ -0,0 +1,209 @@ +#![no_std] + +use core::{convert::TryFrom, marker::PhantomData}; + +use libtock_platform::{ErrorCode, Syscalls}; + +/// The LEDs driver +/// +/// # Example +/// ```ignore +/// use libtock2::Leds; +/// +/// // Turn on led 0 +/// Leds::on(0); +/// ``` +pub struct Gpio(S); + +impl Gpio { + /// Run a check against the gpio capsule to ensure it is present. + /// + /// Returns true` 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 driver_check() -> bool { + S::command(DRIVER_ID, GPIO_COUNT, 0, 0).is_success_u32() + } + + pub fn gpios() -> Gpios<'static, S> { + let num_gpios = S::command(DRIVER_ID, GPIO_COUNT, 0, 0) + .get_success_u32() + .unwrap_or_default() as usize; + Gpios { + num_gpios, + current_gpio: 0, + _syscalls: &PhantomData, + } + } +} + +pub enum GpioState { + Low = 0, + High = 1, +} + +impl From for GpioState { + fn from(original: u32) -> GpioState { + match original { + 0 => GpioState::Low, + _ => GpioState::High, + } + } +} + +pub enum Error { + Invalid, + Failed, +} + +pub trait Pull { + const MODE: u32; +} + +pub struct PullUp; +impl Pull for PullUp { + const MODE: u32 = 1; +} + +pub struct PullDown; +impl Pull for PullDown { + const MODE: u32 = 2; +} + +pub struct PullNone; +impl Pull for PullNone { + const MODE: u32 = 0; +} + +pub struct Pin<'a, S: Syscalls> { + pin_number: u32, + _gpio: &'a PhantomData, +} + +impl<'a, S: Syscalls> Pin<'a, S> { + pub(crate) fn make_output(&'a self) -> Result, ErrorCode> { + if let Some(error) = + S::command(DRIVER_ID, GPIO_ENABLE_OUTPUT, self.pin_number, 0).get_failure() + { + return Err(error); + } + Ok(OutputPin { pin: self }) + } + + pub(crate) fn make_input(&'a self) -> Result, ErrorCode> { + if let Some(error) = + S::command(DRIVER_ID, GPIO_ENABLE_INPUT, self.pin_number, P::MODE).get_failure() + { + return Err(error); + } + Ok(InputPin { + pin: self, + _pull: PhantomData, + }) + } +} + +pub struct OutputPin<'a, S: Syscalls> { + pin: &'a Pin<'a, S>, +} + +impl<'a, S: Syscalls> OutputPin<'a, S> { + pub fn toggle(&mut self) -> Result<(), ErrorCode> { + if let Some(error) = + S::command(DRIVER_ID, GPIO_TOGGLE, self.pin.pin_number, 0).get_failure() + { + Err(error) + } else { + Ok(()) + } + } + pub fn set(&mut self) -> Result<(), ErrorCode> { + if let Some(error) = S::command(DRIVER_ID, GPIO_SET, self.pin.pin_number, 0).get_failure() { + Err(error) + } else { + Ok(()) + } + } + pub fn clear(&mut self) -> Result<(), ErrorCode> { + if let Some(error) = S::command(DRIVER_ID, GPIO_CLEAR, self.pin.pin_number, 0).get_failure() + { + Err(error) + } else { + Ok(()) + } + } +} + +pub struct InputPin<'a, S: Syscalls, P: Pull> { + pin: &'a Pin<'a, S>, + _pull: PhantomData

, +} + +impl<'a, S: Syscalls, P: Pull> InputPin<'a, S, P> { + pub fn read(&self) -> Option { + S::command(DRIVER_ID, GPIO_READ_INPUT, self.pin.pin_number, 0) + .get_success_u32() + .map(|v| v.into()) + } +} + +impl<'a, S: Syscalls> TryFrom<&'a Pin<'a, S>> for OutputPin<'a, S> { + type Error = ErrorCode; + + fn try_from(pin: &'a Pin<'a, S>) -> Result, ErrorCode> { + pin.make_output() + } +} + +impl<'a, S: Syscalls, P: Pull> TryFrom<&'a Pin<'a, S>> for InputPin<'a, S, P> { + type Error = ErrorCode; + + fn try_from(pin: &'a Pin<'a, S>) -> Result, ErrorCode> { + pin.make_input() + } +} + +pub struct Gpios<'a, S: Syscalls> { + num_gpios: usize, + current_gpio: usize, + _syscalls: &'a PhantomData, +} + +impl<'a, S: Syscalls> Iterator for Gpios<'a, S> { + type Item = Pin<'a, S>; + fn next(&mut self) -> Option { + if self.current_gpio < self.num_gpios { + let pin_number = self.current_gpio as u32; + self.current_gpio += 1; + Some(Pin { + pin_number, + _gpio: &PhantomData, + }) + } else { + None + } + } +} + +impl<'a, S: Syscalls> Drop for Pin<'a, S> { + fn drop(&mut self) { + S::command(DRIVER_ID, GPIO_DISABLE, self.pin_number, 0); + } +} + +const DRIVER_ID: u32 = 2; + +// Command IDs +const GPIO_COUNT: u32 = 0; + +const GPIO_ENABLE_OUTPUT: u32 = 1; +const GPIO_SET: u32 = 2; +const GPIO_CLEAR: u32 = 3; +const GPIO_TOGGLE: u32 = 4; + +const GPIO_ENABLE_INPUT: u32 = 5; +const GPIO_READ_INPUT: u32 = 6; +const GPIO_DISABLE: u32 = 9; + +#[cfg(test)] +mod tests; diff --git a/apis/gpio/src/tests.rs b/apis/gpio/src/tests.rs new file mode 100644 index 00000000..ad9d2299 --- /dev/null +++ b/apis/gpio/src/tests.rs @@ -0,0 +1,85 @@ +use libtock_unittest::fake; + +type Gpio = super::Gpio; + +#[test] +fn no_driver() { + let _kernel = fake::Kernel::new(); + assert!(Gpio::driver_check()); +} + +// #[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)); +// } +// } From 06eda2105d4ed57f71ffca202b018dfc6854b470 Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Tue, 1 Feb 2022 03:33:59 +0200 Subject: [PATCH 2/9] no iterator --- apis/gpio/src/lib.rs | 169 ++++++++++++++++++++--------------------- apis/gpio/src/tests.rs | 6 +- 2 files changed, 87 insertions(+), 88 deletions(-) diff --git a/apis/gpio/src/lib.rs b/apis/gpio/src/lib.rs index 756d221c..6455c368 100644 --- a/apis/gpio/src/lib.rs +++ b/apis/gpio/src/lib.rs @@ -1,17 +1,18 @@ #![no_std] -use core::{convert::TryFrom, marker::PhantomData}; +use core::marker::PhantomData; use libtock_platform::{ErrorCode, Syscalls}; -/// The LEDs driver +/// The Gpios driver /// /// # Example /// ```ignore -/// use libtock2::Leds; +/// use libtock2::Gpios; /// /// // Turn on led 0 -/// Leds::on(0); +/// let pin = Gpios::get_pin(0)?; +/// /// ``` pub struct Gpio(S); @@ -25,16 +26,69 @@ impl Gpio { S::command(DRIVER_ID, GPIO_COUNT, 0, 0).is_success_u32() } - pub fn gpios() -> Gpios<'static, S> { - let num_gpios = S::command(DRIVER_ID, GPIO_COUNT, 0, 0) - .get_success_u32() - .unwrap_or_default() as usize; - Gpios { - num_gpios, - current_gpio: 0, - _syscalls: &PhantomData, + fn enable_gpio_output(pin: u32) -> Result<(), ErrorCode> { + if let Some(error) = S::command(DRIVER_ID, GPIO_ENABLE_OUTPUT, pin, 0).get_failure() { + Err(error) + } else { + Ok(()) + } + } + + fn enable_gpio_input(pin: u32, mode: u32) -> Result<(), ErrorCode> { + if let Some(error) = S::command(DRIVER_ID, GPIO_ENABLE_INPUT, pin, mode).get_failure() { + Err(error) + } else { + Ok(()) + } + } + + fn write(pin: u32, state: GpioState) -> Result<(), ErrorCode> { + let action = match state { + GpioState::Low => GPIO_CLEAR, + _ => GPIO_SET, + }; + if let Some(error) = S::command(DRIVER_ID, action, pin, 0).get_failure() { + Err(error) + } else { + Ok(()) + } + } + + fn read(pin: u32) -> Result { + let command_return = S::command(DRIVER_ID, GPIO_READ_INPUT, pin, 0); + if let Some(error) = command_return.get_failure() { + Err(error) + } else { + command_return + .get_success_u32() + .map(|v| Ok(v.into())) + .unwrap_or(Err(ErrorCode::Fail)) // TODO replace with BadRValue + } + } + + fn toggle(pin: u32) -> Result<(), ErrorCode> { + if let Some(error) = S::command(DRIVER_ID, GPIO_TOGGLE, pin, 0).get_failure() { + Err(error) + } else { + Ok(()) } } + + fn disable(pin: u32) -> Result<(), ErrorCode> { + if let Some(error) = S::command(DRIVER_ID, GPIO_DISABLE, pin, 0).get_failure() { + Err(error) + } else { + Ok(()) + } + } + + pub fn get_pin(pin: u32) -> Result, ErrorCode> { + Self::disable(pin)?; + Ok(Pin { + pin_number: pin, + _syscalls: PhantomData, + }) + } } pub enum GpioState { @@ -75,27 +129,19 @@ impl Pull for PullNone { const MODE: u32 = 0; } -pub struct Pin<'a, S: Syscalls> { +pub struct Pin { pin_number: u32, - _gpio: &'a PhantomData, + _syscalls: PhantomData, } -impl<'a, S: Syscalls> Pin<'a, S> { - pub(crate) fn make_output(&'a self) -> Result, ErrorCode> { - if let Some(error) = - S::command(DRIVER_ID, GPIO_ENABLE_OUTPUT, self.pin_number, 0).get_failure() - { - return Err(error); - } +impl Pin { + pub fn make_output(&mut self) -> Result, ErrorCode> { + Gpio::::enable_gpio_output(self.pin_number)?; Ok(OutputPin { pin: self }) } - pub(crate) fn make_input(&'a self) -> Result, ErrorCode> { - if let Some(error) = - S::command(DRIVER_ID, GPIO_ENABLE_INPUT, self.pin_number, P::MODE).get_failure() - { - return Err(error); - } + pub fn make_input(&self) -> Result, ErrorCode> { + Gpio::::enable_gpio_input(self.pin_number, P::MODE)?; Ok(InputPin { pin: self, _pull: PhantomData, @@ -104,90 +150,39 @@ impl<'a, S: Syscalls> Pin<'a, S> { } pub struct OutputPin<'a, S: Syscalls> { - pin: &'a Pin<'a, S>, + pin: &'a Pin, } impl<'a, S: Syscalls> OutputPin<'a, S> { pub fn toggle(&mut self) -> Result<(), ErrorCode> { - if let Some(error) = - S::command(DRIVER_ID, GPIO_TOGGLE, self.pin.pin_number, 0).get_failure() - { - Err(error) - } else { - Ok(()) - } + Gpio::::toggle(self.pin.pin_number) } pub fn set(&mut self) -> Result<(), ErrorCode> { - if let Some(error) = S::command(DRIVER_ID, GPIO_SET, self.pin.pin_number, 0).get_failure() { - Err(error) - } else { - Ok(()) - } + Gpio::::write(self.pin.pin_number, GpioState::High) } pub fn clear(&mut self) -> Result<(), ErrorCode> { - if let Some(error) = S::command(DRIVER_ID, GPIO_CLEAR, self.pin.pin_number, 0).get_failure() - { - Err(error) - } else { - Ok(()) - } + Gpio::::write(self.pin.pin_number, GpioState::Low) } } pub struct InputPin<'a, S: Syscalls, P: Pull> { - pin: &'a Pin<'a, S>, + pin: &'a Pin, _pull: PhantomData

, } impl<'a, S: Syscalls, P: Pull> InputPin<'a, S, P> { pub fn read(&self) -> Option { - S::command(DRIVER_ID, GPIO_READ_INPUT, self.pin.pin_number, 0) - .get_success_u32() - .map(|v| v.into()) - } -} - -impl<'a, S: Syscalls> TryFrom<&'a Pin<'a, S>> for OutputPin<'a, S> { - type Error = ErrorCode; - - fn try_from(pin: &'a Pin<'a, S>) -> Result, ErrorCode> { - pin.make_output() - } -} - -impl<'a, S: Syscalls, P: Pull> TryFrom<&'a Pin<'a, S>> for InputPin<'a, S, P> { - type Error = ErrorCode; - - fn try_from(pin: &'a Pin<'a, S>) -> Result, ErrorCode> { - pin.make_input() - } -} - -pub struct Gpios<'a, S: Syscalls> { - num_gpios: usize, - current_gpio: usize, - _syscalls: &'a PhantomData, -} - -impl<'a, S: Syscalls> Iterator for Gpios<'a, S> { - type Item = Pin<'a, S>; - fn next(&mut self) -> Option { - if self.current_gpio < self.num_gpios { - let pin_number = self.current_gpio as u32; - self.current_gpio += 1; - Some(Pin { - pin_number, - _gpio: &PhantomData, - }) + if let Ok(state) = Gpio::::read(self.pin.pin_number) { + Some(state) } else { None } } } -impl<'a, S: Syscalls> Drop for Pin<'a, S> { +impl Drop for Pin { fn drop(&mut self) { - S::command(DRIVER_ID, GPIO_DISABLE, self.pin_number, 0); + let _ = Gpio::::disable(self.pin_number); } } diff --git a/apis/gpio/src/tests.rs b/apis/gpio/src/tests.rs index ad9d2299..7d733f01 100644 --- a/apis/gpio/src/tests.rs +++ b/apis/gpio/src/tests.rs @@ -1,5 +1,9 @@ +use core::convert::TryInto; + use libtock_unittest::fake; +use crate::{InputPin, OutputPin, PullNone}; + type Gpio = super::Gpio; #[test] @@ -8,7 +12,7 @@ fn no_driver() { assert!(Gpio::driver_check()); } -// #[test] +// // #[test] // fn driver_check() { // let kernel = fake::Kernel::new(); // let driver = fake::Leds::<10>::new(); From 4dd2b7051714c547f1c0889c49f8275deb99a83e Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Thu, 3 Feb 2022 22:28:58 +0200 Subject: [PATCH 3/9] works --- apis/gpio/src/lib.rs | 79 ++++++++++---------- apis/gpio/src/tests.rs | 5 +- unittest/src/fake/gpio/mod.rs | 127 ++++++++++++++++++++++++++++++++ unittest/src/fake/gpio/tests.rs | 112 ++++++++++++++++++++++++++++ unittest/src/fake/mod.rs | 2 + 5 files changed, 284 insertions(+), 41 deletions(-) create mode 100644 unittest/src/fake/gpio/mod.rs create mode 100644 unittest/src/fake/gpio/tests.rs diff --git a/apis/gpio/src/lib.rs b/apis/gpio/src/lib.rs index 6455c368..34364d8c 100644 --- a/apis/gpio/src/lib.rs +++ b/apis/gpio/src/lib.rs @@ -2,7 +2,9 @@ use core::marker::PhantomData; -use libtock_platform::{ErrorCode, Syscalls}; +use libtock_platform::{ + share::Handle, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, +}; /// The Gpios driver /// @@ -22,24 +24,16 @@ impl Gpio { /// Returns true` 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 driver_check() -> bool { - S::command(DRIVER_ID, GPIO_COUNT, 0, 0).is_success_u32() + pub fn count() -> Result { + S::command(DRIVER_ID, GPIO_COUNT, 0, 0).to_result() } fn enable_gpio_output(pin: u32) -> Result<(), ErrorCode> { - if let Some(error) = S::command(DRIVER_ID, GPIO_ENABLE_OUTPUT, pin, 0).get_failure() { - Err(error) - } else { - Ok(()) - } + S::command(DRIVER_ID, GPIO_ENABLE_OUTPUT, pin, 0).to_result() } fn enable_gpio_input(pin: u32, mode: u32) -> Result<(), ErrorCode> { - if let Some(error) = S::command(DRIVER_ID, GPIO_ENABLE_INPUT, pin, mode).get_failure() { - Err(error) - } else { - Ok(()) - } + S::command(DRIVER_ID, GPIO_ENABLE_INPUT, pin, mode).to_result() } fn write(pin: u32, state: GpioState) -> Result<(), ErrorCode> { @@ -47,39 +41,20 @@ impl Gpio { GpioState::Low => GPIO_CLEAR, _ => GPIO_SET, }; - if let Some(error) = S::command(DRIVER_ID, action, pin, 0).get_failure() { - Err(error) - } else { - Ok(()) - } + S::command(DRIVER_ID, action, pin, 0).to_result() } fn read(pin: u32) -> Result { - let command_return = S::command(DRIVER_ID, GPIO_READ_INPUT, pin, 0); - if let Some(error) = command_return.get_failure() { - Err(error) - } else { - command_return - .get_success_u32() - .map(|v| Ok(v.into())) - .unwrap_or(Err(ErrorCode::Fail)) // TODO replace with BadRValue - } + let pin_state: u32 = S::command(DRIVER_ID, GPIO_READ_INPUT, pin, 0).to_result()?; + Ok(pin_state.into()) } fn toggle(pin: u32) -> Result<(), ErrorCode> { - if let Some(error) = S::command(DRIVER_ID, GPIO_TOGGLE, pin, 0).get_failure() { - Err(error) - } else { - Ok(()) - } + S::command(DRIVER_ID, GPIO_TOGGLE, pin, 0).to_result() } fn disable(pin: u32) -> Result<(), ErrorCode> { - if let Some(error) = S::command(DRIVER_ID, GPIO_DISABLE, pin, 0).get_failure() { - Err(error) - } else { - Ok(()) - } + S::command(DRIVER_ID, GPIO_DISABLE, pin, 0).to_result() } pub fn get_pin(pin: u32) -> Result, ErrorCode> { @@ -89,6 +64,26 @@ impl Gpio { _syscalls: PhantomData, }) } + + /// Register an interrupt listener + /// + /// There can be only one single listener registered at a time. + /// Each time this function is used, it will replace the + /// previously registered listener. + pub fn register_listener<'share, F: Fn(u32, GpioState)>( + listener: &'share InterruptListener, + subscribe: Handle>, + ) -> Result<(), ErrorCode> { + S::subscribe::<_, _, DefaultConfig, DRIVER_ID, 0>(subscribe, listener) + } + + /// Unregister the interrupt listener + /// + /// This function may be used even if there was no + /// previously registered listener. + pub fn unregister_listener() { + S::unsubscribe(DRIVER_ID, 0) + } } pub enum GpioState { @@ -186,7 +181,15 @@ impl Drop for Pin { } } -const DRIVER_ID: u32 = 2; +pub struct InterruptListener(pub F); + +impl Upcall> for InterruptListener { + fn upcall(&self, gpio_index: u32, state: u32, _arg2: u32) { + self.0(gpio_index, state.into()) + } +} + +const DRIVER_ID: u32 = 4; // Command IDs const GPIO_COUNT: u32 = 0; diff --git a/apis/gpio/src/tests.rs b/apis/gpio/src/tests.rs index 7d733f01..961b8656 100644 --- a/apis/gpio/src/tests.rs +++ b/apis/gpio/src/tests.rs @@ -1,5 +1,4 @@ -use core::convert::TryInto; - +use libtock_platform::ErrorCode; use libtock_unittest::fake; use crate::{InputPin, OutputPin, PullNone}; @@ -9,7 +8,7 @@ type Gpio = super::Gpio; #[test] fn no_driver() { let _kernel = fake::Kernel::new(); - assert!(Gpio::driver_check()); + assert_eq!(Gpio::count(), Err(ErrorCode::NoDevice)); } // // #[test] diff --git a/unittest/src/fake/gpio/mod.rs b/unittest/src/fake/gpio/mod.rs new file mode 100644 index 00000000..832e9796 --- /dev/null +++ b/unittest/src/fake/gpio/mod.rs @@ -0,0 +1,127 @@ +//! Fake implementation of the GPIO API, documented here: +//! https://github.com/tock/tock/blob/master/doc/syscalls/00004_gpio.md +//! +//! Like the real API, `Gpio` controls a set of fake gpios. It provides +//! a function `get_button_state` used to retrieve the state and interrupt +//! status of a button. +//! +//! It also provides the function `set_pressed` that set the button's state. + +use core::cell::Cell; +use libtock_platform::{CommandReturn, ErrorCode}; + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum GpioMode { + Output, + Input(PullMode), +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum PullMode { + PullNone = 0, + PullUp = 1, + PullDown = 2, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub struct GpioState { + pub value: bool, + pub mode: GpioMode, + pub interrupt_enabled: bool, +} + +pub struct Gpio { + gpios: [Cell; NUM_GPIOS], +} + +impl Gpio { + pub fn new() -> std::rc::Rc> { + #[allow(clippy::declare_interior_mutable_const)] + const OFF: Cell = Cell::new(GpioState { + value: false, + mode: GpioMode::Input(PullMode::PullNone), + interrupt_enabled: false, + }); + std::rc::Rc::new(Gpio { + gpios: [OFF; NUM_GPIOS], + }) + } +} + +impl crate::fake::SyscallDriver for Gpio { + fn id(&self) -> u32 { + DRIVER_ID + } + fn num_upcalls(&self) -> u32 { + 1 + } + + fn command(&self, command_number: u32, argument0: u32, _argument1: u32) -> CommandReturn { + if command_number == GPIO_COUNT { + crate::command_return::success_u32(NUM_GPIOS as u32) + } else { + if argument0 < NUM_GPIOS as u32 { + match command_number { + GPIO_ENABLE_OUTPUT => { + let gpio = self.gpios[argument0 as usize].get(); + self.gpios[argument0 as usize].set(GpioState { + mode: GpioMode::Output, + ..gpio + }); + crate::command_return::success() + } + GPIO_SET => { + let gpio = self.gpios[argument0 as usize].get(); + self.gpios[argument0 as usize].set(GpioState { + mode: GpioMode::Output, + ..gpio + }); + crate::command_return::success() + } + _ => crate::command_return::failure(ErrorCode::NoSupport), + } + } else { + crate::command_return::failure(ErrorCode::Invalid) + } + } + } +} + +// ----------------------------------------------------------------------------- +// Implementation details below +// ----------------------------------------------------------------------------- + +#[cfg(test)] +mod tests; + +const DRIVER_ID: u32 = 4; + +// Command IDs +const GPIO_COUNT: u32 = 0; + +const GPIO_ENABLE_OUTPUT: u32 = 1; +const GPIO_SET: u32 = 2; +const GPIO_CLEAR: u32 = 3; +const GPIO_TOGGLE: u32 = 4; + +const GPIO_ENABLE_INPUT: u32 = 5; +const GPIO_READ_INPUT: u32 = 6; +const GPIO_DISABLE: u32 = 9; + +impl Gpio { + pub fn set_pressed(&self, button: u32, pressed: bool) -> Result<(), ErrorCode> { + self.buttons + .get(button as usize) + .map(|button| { + button.set(GpioState { + pressed, + ..button.get() + }) + }) + .ok_or(ErrorCode::Invalid) + } + + pub fn get_button_state(&self, button: u32) -> Option { + self.buttons.get(button as usize).map(|button| button.get()) + } +} diff --git a/unittest/src/fake/gpio/tests.rs b/unittest/src/fake/gpio/tests.rs new file mode 100644 index 00000000..d62b8d20 --- /dev/null +++ b/unittest/src/fake/gpio/tests.rs @@ -0,0 +1,112 @@ +// use crate::fake; +// use fake::buttons::*; +// use libtock_platform::ErrorCode; + +// // Tests the command implementation. +// #[test] +// fn command() { +// use fake::SyscallDriver; +// let buttons = Buttons::<10>::new(); + +// assert_eq!( +// buttons.command(BUTTONS_COUNT, 1, 2).get_success_u32(), +// Some(10) +// ); + +// assert_eq!( +// buttons.command(BUTTONS_READ, 11, 0).get_failure(), +// Some(ErrorCode::Invalid) +// ); +// assert_eq!( +// buttons +// .command(BUTTONS_ENABLE_INTERRUPTS, 11, 0) +// .get_failure(), +// Some(ErrorCode::Invalid) +// ); +// assert_eq!( +// buttons +// .command(BUTTONS_DISABLE_INTERRUPTS, 11, 0) +// .get_failure(), +// Some(ErrorCode::Invalid) +// ); + +// for button_index in 0..10 { +// assert_eq!( +// buttons.get_button_state(button_index).unwrap(), +// ButtonState { +// pressed: false, +// interrupt_enabled: false +// } +// ); + +// assert!(buttons +// .command(BUTTONS_ENABLE_INTERRUPTS, button_index, 0) +// .is_success()); +// assert_eq!( +// buttons.get_button_state(button_index).unwrap(), +// ButtonState { +// pressed: false, +// interrupt_enabled: true +// } +// ); + +// assert!(buttons +// .command(BUTTONS_DISABLE_INTERRUPTS, button_index, 0) +// .is_success()); +// assert_eq!( +// buttons.get_button_state(button_index).unwrap(), +// ButtonState { +// pressed: false, +// interrupt_enabled: false +// } +// ); + +// assert_eq!(buttons.set_pressed(button_index, true), Ok(())); +// assert_eq!( +// buttons.get_button_state(button_index).unwrap(), +// ButtonState { +// pressed: true, +// interrupt_enabled: false +// } +// ); + +// assert_eq!(buttons.set_pressed(button_index, false), Ok(())); +// assert_eq!( +// buttons.get_button_state(button_index).unwrap(), +// ButtonState { +// pressed: false, +// interrupt_enabled: false +// } +// ); +// } +// } + +// // Integration test that verifies Buttons works with fake::Kernel and +// // libtock_platform::Syscalls. +// #[test] +// fn kernel_integration() { +// use libtock_platform::Syscalls; +// let kernel = fake::Kernel::new(); +// let buttons = Buttons::<10>::new(); +// kernel.add_driver(&buttons); +// let value = fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_COUNT, 1, 2); +// assert_eq!(value.get_success_u32(), Some(10)); + +// assert_eq!( +// fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_READ, 11, 0).get_failure(), +// Some(ErrorCode::Invalid) +// ); +// assert!(fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_READ, 0, 0).is_success_u32()); + +// assert_eq!( +// fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_ENABLE_INTERRUPTS, 11, 0).get_failure(), +// Some(ErrorCode::Invalid) +// ); +// assert!(fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_ENABLE_INTERRUPTS, 0, 0).is_success()); + +// assert_eq!( +// fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_DISABLE_INTERRUPTS, 11, 0).get_failure(), +// Some(ErrorCode::Invalid) +// ); +// assert!(fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_DISABLE_INTERRUPTS, 0, 0).is_success()); +// } diff --git a/unittest/src/fake/mod.rs b/unittest/src/fake/mod.rs index 978fe87f..7e9d045f 100644 --- a/unittest/src/fake/mod.rs +++ b/unittest/src/fake/mod.rs @@ -9,12 +9,14 @@ //! `use libtock_unittest::fake` and refer to the type with the `fake::` prefix //! (e.g. `fake::Console`). +mod gpio; mod kernel; mod leds; mod low_level_debug; mod syscall_driver; mod syscalls; +pub use gpio::Gpio; pub use kernel::Kernel; pub use leds::Leds; pub use low_level_debug::{LowLevelDebug, Message}; From 8ac0179e1518bcbb83e03a35629ac332b8c36191 Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Mon, 7 Feb 2022 02:07:05 +0200 Subject: [PATCH 4/9] working driver --- apis/gpio/src/lib.rs | 171 +++++++---- apis/gpio/src/tests.rs | 331 +++++++++++++++----- unittest/src/fake/gpio/mod.rs | 194 ++++++++++-- unittest/src/fake/gpio/tests.rs | 519 +++++++++++++++++++++++++------- unittest/src/fake/mod.rs | 2 +- 5 files changed, 932 insertions(+), 285 deletions(-) diff --git a/apis/gpio/src/lib.rs b/apis/gpio/src/lib.rs index 34364d8c..aa66fdbe 100644 --- a/apis/gpio/src/lib.rs +++ b/apis/gpio/src/lib.rs @@ -6,7 +6,7 @@ use libtock_platform::{ share::Handle, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, }; -/// The Gpios driver +/// The Gpio driver /// /// # Example /// ```ignore @@ -16,6 +16,62 @@ use libtock_platform::{ /// let pin = Gpios::get_pin(0)?; /// /// ``` + +const DRIVER_NUMBER: u32 = 4; + +// Command IDs +const GPIO_COUNT: u32 = 0; + +const GPIO_ENABLE_OUTPUT: u32 = 1; +const GPIO_SET: u32 = 2; +const GPIO_CLEAR: u32 = 3; +const GPIO_TOGGLE: u32 = 4; + +const GPIO_ENABLE_INPUT: u32 = 5; +const GPIO_READ_INPUT: u32 = 6; + +const GPIO_ENABLE_INTERRUPTS: u32 = 7; +const GPIO_DISABLE_INTERRUPTS: u32 = 8; + +const GPIO_DISABLE: u32 = 9; + +#[derive(Copy, Clone, PartialEq)] +#[cfg_attr(test, derive(Debug))] +pub enum GpioState { + Low = 0, + High = 1, +} + +pub enum PinInterruptEdge { + Either = 0, + Rising = 1, + Falling = 2, +} + +pub enum Error { + Invalid, + Failed, +} + +pub trait Pull { + const MODE: u32; +} + +pub struct PullUp; +impl Pull for PullUp { + const MODE: u32 = 1; +} + +pub struct PullDown; +impl Pull for PullDown { + const MODE: u32 = 2; +} + +pub struct PullNone; +impl Pull for PullNone { + const MODE: u32 = 0; +} + pub struct Gpio(S); impl Gpio { @@ -25,15 +81,15 @@ impl Gpio { /// that the driver is working, as it may still fail to allocate grant /// memory. pub fn count() -> Result { - S::command(DRIVER_ID, GPIO_COUNT, 0, 0).to_result() + S::command(DRIVER_NUMBER, GPIO_COUNT, 0, 0).to_result() } fn enable_gpio_output(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_ID, GPIO_ENABLE_OUTPUT, pin, 0).to_result() + S::command(DRIVER_NUMBER, GPIO_ENABLE_OUTPUT, pin, 0).to_result() } fn enable_gpio_input(pin: u32, mode: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_ID, GPIO_ENABLE_INPUT, pin, mode).to_result() + S::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, pin, mode).to_result() } fn write(pin: u32, state: GpioState) -> Result<(), ErrorCode> { @@ -41,20 +97,28 @@ impl Gpio { GpioState::Low => GPIO_CLEAR, _ => GPIO_SET, }; - S::command(DRIVER_ID, action, pin, 0).to_result() + S::command(DRIVER_NUMBER, action, pin, 0).to_result() } fn read(pin: u32) -> Result { - let pin_state: u32 = S::command(DRIVER_ID, GPIO_READ_INPUT, pin, 0).to_result()?; + let pin_state: u32 = S::command(DRIVER_NUMBER, GPIO_READ_INPUT, pin, 0).to_result()?; Ok(pin_state.into()) } fn toggle(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_ID, GPIO_TOGGLE, pin, 0).to_result() + S::command(DRIVER_NUMBER, GPIO_TOGGLE, pin, 0).to_result() } fn disable(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_ID, GPIO_DISABLE, pin, 0).to_result() + S::command(DRIVER_NUMBER, GPIO_DISABLE, pin, 0).to_result() + } + + fn enable_interrupts(pin: u32, edge: PinInterruptEdge) -> Result<(), ErrorCode> { + S::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, pin, edge as u32).to_result() + } + + fn disable_interrupts(pin: u32) -> Result<(), ErrorCode> { + S::command(DRIVER_NUMBER, GPIO_DISABLE_INTERRUPTS, pin, 0).to_result() } pub fn get_pin(pin: u32) -> Result, ErrorCode> { @@ -71,10 +135,10 @@ impl Gpio { /// Each time this function is used, it will replace the /// previously registered listener. pub fn register_listener<'share, F: Fn(u32, GpioState)>( - listener: &'share InterruptListener, - subscribe: Handle>, + listener: &'share GpioInterruptListener, + subscribe: Handle>, ) -> Result<(), ErrorCode> { - S::subscribe::<_, _, DefaultConfig, DRIVER_ID, 0>(subscribe, listener) + S::subscribe::<_, _, DefaultConfig, DRIVER_NUMBER, 0>(subscribe, listener) } /// Unregister the interrupt listener @@ -82,13 +146,24 @@ impl Gpio { /// This function may be used even if there was no /// previously registered listener. pub fn unregister_listener() { - S::unsubscribe(DRIVER_ID, 0) + S::unsubscribe(DRIVER_NUMBER, 0) } } -pub enum GpioState { - Low = 0, - High = 1, +/// A wrapper around a closure to be registered and called when +/// a gpio interrupt occurs. +/// +/// ```ignore +/// let listener = GpioInterruptListener(|gpio, interrupt_edge| { +/// // make use of the button's state +/// }); +/// ``` +pub struct GpioInterruptListener(pub F); + +impl Upcall> for GpioInterruptListener { + fn upcall(&self, gpio_index: u32, value: u32, _arg2: u32) { + self.0(gpio_index, value.into()) + } } impl From for GpioState { @@ -100,30 +175,6 @@ impl From for GpioState { } } -pub enum Error { - Invalid, - Failed, -} - -pub trait Pull { - const MODE: u32; -} - -pub struct PullUp; -impl Pull for PullUp { - const MODE: u32 = 1; -} - -pub struct PullDown; -impl Pull for PullDown { - const MODE: u32 = 2; -} - -pub struct PullNone; -impl Pull for PullNone { - const MODE: u32 = 0; -} - pub struct Pin { pin_number: u32, _syscalls: PhantomData, @@ -166,42 +217,30 @@ pub struct InputPin<'a, S: Syscalls, P: Pull> { } impl<'a, S: Syscalls, P: Pull> InputPin<'a, S, P> { - pub fn read(&self) -> Option { - if let Ok(state) = Gpio::::read(self.pin.pin_number) { - Some(state) - } else { - None - } + pub fn read(&self) -> Result { + Gpio::::read(self.pin.pin_number) + } + + pub fn enable_interrupts(&self, edge: PinInterruptEdge) -> Result<(), ErrorCode> { + Gpio::::enable_interrupts(self.pin.pin_number, edge) + } + + pub fn disable_interrupts(&self) -> Result<(), ErrorCode> { + Gpio::::disable_interrupts(self.pin.pin_number) } } -impl Drop for Pin { +impl Drop for OutputPin<'_, S> { fn drop(&mut self) { - let _ = Gpio::::disable(self.pin_number); + let _ = Gpio::::disable(self.pin.pin_number); } } -pub struct InterruptListener(pub F); - -impl Upcall> for InterruptListener { - fn upcall(&self, gpio_index: u32, state: u32, _arg2: u32) { - self.0(gpio_index, state.into()) +impl Drop for InputPin<'_, S, P> { + fn drop(&mut self) { + let _ = Gpio::::disable(self.pin.pin_number); } } -const DRIVER_ID: u32 = 4; - -// Command IDs -const GPIO_COUNT: u32 = 0; - -const GPIO_ENABLE_OUTPUT: u32 = 1; -const GPIO_SET: u32 = 2; -const GPIO_CLEAR: u32 = 3; -const GPIO_TOGGLE: u32 = 4; - -const GPIO_ENABLE_INPUT: u32 = 5; -const GPIO_READ_INPUT: u32 = 6; -const GPIO_DISABLE: u32 = 9; - #[cfg(test)] mod tests; diff --git a/apis/gpio/src/tests.rs b/apis/gpio/src/tests.rs index 961b8656..1c67b59c 100644 --- a/apis/gpio/src/tests.rs +++ b/apis/gpio/src/tests.rs @@ -1,7 +1,9 @@ -use libtock_platform::ErrorCode; -use libtock_unittest::fake; +use core::cell::Cell; -use crate::{InputPin, OutputPin, PullNone}; +use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; +use libtock_unittest::fake::{self, GpioMode, InterruptEdge, PullMode}; + +use crate::{GpioInterruptListener, GpioState, PullDown, PullNone, PullUp}; type Gpio = super::Gpio; @@ -11,78 +13,251 @@ fn no_driver() { assert_eq!(Gpio::count(), Err(ErrorCode::NoDevice)); } -// // #[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)); -// } -// } +#[test] +fn num_gpio() { + let kernel = fake::Kernel::new(); + let driver = fake::Gpio::<10>::new(); + kernel.add_driver(&driver); + assert_eq!(Gpio::count(), Ok(10)); +} + +// Tests the command implementation. +#[test] +fn output() { + let kernel = fake::Kernel::new(); + let driver = fake::Gpio::<10>::new(); + driver.set_missing_gpio(1); + kernel.add_driver(&driver); + + assert_eq!(Gpio::count(), Ok(10)); + + let pin_11 = Gpio::get_pin(11); + assert!(pin_11.is_err()); + let _ = pin_11.map_err(|error| assert_eq!(error, ErrorCode::Invalid)); + let pin_1 = Gpio::get_pin(1); + assert!(pin_1.is_err()); + let _ = pin_1.map_err(|error| assert_eq!(error, ErrorCode::NoDevice)); + + let pin_0 = Gpio::get_pin(0); + assert!(pin_0.is_ok()); + + let _ = pin_0.map(|mut pin| { + let output_pin = pin.make_output(); + assert!(output_pin.is_ok()); + assert_eq!(driver.get_gpio_state(0).unwrap().mode, GpioMode::Output); + let _ = output_pin.map(|mut pin| { + assert_eq!(pin.set(), Ok(())); + assert!(driver.get_gpio_state(0).unwrap().value); + assert_eq!(pin.clear(), Ok(())); + assert!(!driver.get_gpio_state(0).unwrap().value); + assert_eq!(pin.toggle(), Ok(())); + assert!(driver.get_gpio_state(0).unwrap().value); + assert_eq!(pin.toggle(), Ok(())); + assert!(!driver.get_gpio_state(0).unwrap().value); + }); + assert_eq!(driver.get_gpio_state(0).unwrap().mode, GpioMode::Disable); + }); +} + +// Tests the command implementation. +#[test] +fn input() { + let kernel = fake::Kernel::new(); + let driver = fake::Gpio::<10>::new(); + driver.set_missing_gpio(1); + kernel.add_driver(&driver); + + assert_eq!(Gpio::count(), Ok(10)); + + let pin_11 = Gpio::get_pin(11); + assert!(pin_11.is_err()); + let _ = pin_11.map_err(|error| assert_eq!(error, ErrorCode::Invalid)); + let pin_1 = Gpio::get_pin(1); + assert!(pin_1.is_err()); + let _ = pin_1.map_err(|error| assert_eq!(error, ErrorCode::NoDevice)); + + let pin_0 = Gpio::get_pin(0); + assert!(pin_0.is_ok()); + + let _ = pin_0.map(|pin| { + let input_pin = pin.make_input::(); + assert!(input_pin.is_ok()); + assert_eq!( + driver.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullNone) + ); + + let input_pin = pin.make_input::(); + assert!(input_pin.is_ok()); + assert_eq!( + driver.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullUp) + ); + + let input_pin = pin.make_input::(); + assert!(input_pin.is_ok()); + assert_eq!( + driver.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullDown) + ); + + let _ = input_pin.map(|pin| { + assert_eq!(driver.set_value(0, true), Ok(())); + assert_eq!(pin.read(), Ok(GpioState::High)); + assert_eq!(driver.set_value(0, false), Ok(())); + assert_eq!(pin.read(), Ok(GpioState::Low)); + }); + assert_eq!(driver.get_gpio_state(0).unwrap().mode, GpioMode::Disable); + }); +} + +// Tests the command implementation. +#[test] +fn interrupts() { + let kernel = fake::Kernel::new(); + let driver = fake::Gpio::<10>::new(); + driver.set_missing_gpio(1); + kernel.add_driver(&driver); + + assert_eq!(Gpio::count(), Ok(10)); + + let pin_11 = Gpio::get_pin(11); + assert!(pin_11.is_err()); + let _ = pin_11.map_err(|error| assert_eq!(error, ErrorCode::Invalid)); + let pin_1 = Gpio::get_pin(1); + assert!(pin_1.is_err()); + let _ = pin_1.map_err(|error| assert_eq!(error, ErrorCode::NoDevice)); + + let pin_0 = Gpio::get_pin(0); + assert!(pin_0.is_ok()); + + let _ = pin_0.map(|pin| { + // Either + let input_pin = pin.make_input::(); + assert!(input_pin.is_ok()); + assert_eq!( + driver.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullNone) + ); + + let _ = input_pin.map(|pin| { + assert_eq!( + pin.enable_interrupts(crate::PinInterruptEdge::Either), + Ok(()) + ); + assert_eq!( + driver.get_gpio_state(0).unwrap().interrupt_enabled, + Some(InterruptEdge::Either) + ); + + assert_eq!(driver.set_value(0, false), Ok(())); + + let gpio_state = Cell::>::new(None); + let listener = GpioInterruptListener(|gpio, state| { + assert_eq!(gpio, 0); + gpio_state.set(Some(state)); + }); + + share::scope(|subscribe| { + assert_eq!(Gpio::register_listener(&listener, subscribe), Ok(())); + assert_eq!(driver.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(gpio_state.get(), Some(GpioState::High)); + gpio_state.set(None); + assert_eq!(driver.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(gpio_state.get(), Some(GpioState::Low)); + + assert_eq!(pin.disable_interrupts(), Ok(())); + assert_eq!(driver.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!(driver.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + }); + }); + + // Rising + let input_pin = pin.make_input::(); + assert!(input_pin.is_ok()); + assert_eq!( + driver.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullNone) + ); + + let _ = input_pin.map(|pin| { + assert_eq!( + pin.enable_interrupts(crate::PinInterruptEdge::Rising), + Ok(()) + ); + assert_eq!( + driver.get_gpio_state(0).unwrap().interrupt_enabled, + Some(InterruptEdge::Rising) + ); + + assert_eq!(driver.set_value(0, false), Ok(())); + + let gpio_state = Cell::>::new(None); + let listener = GpioInterruptListener(|gpio, state| { + assert_eq!(gpio, 0); + gpio_state.set(Some(state)); + }); + + share::scope(|subscribe| { + assert_eq!(Gpio::register_listener(&listener, subscribe), Ok(())); + assert_eq!(driver.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(gpio_state.get(), Some(GpioState::High)); + assert_eq!(driver.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert_eq!(pin.disable_interrupts(), Ok(())); + assert_eq!(driver.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!(driver.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + }); + }); + + // Falling + let input_pin = pin.make_input::(); + assert!(input_pin.is_ok()); + assert_eq!( + driver.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullNone) + ); + + let _ = input_pin.map(|pin| { + assert_eq!( + pin.enable_interrupts(crate::PinInterruptEdge::Falling), + Ok(()) + ); + assert_eq!( + driver.get_gpio_state(0).unwrap().interrupt_enabled, + Some(InterruptEdge::Falling) + ); + + assert_eq!(driver.set_value(0, false), Ok(())); + + let gpio_state = Cell::>::new(None); + let listener = GpioInterruptListener(|gpio, state| { + assert_eq!(gpio, 0); + gpio_state.set(Some(state)); + }); + + share::scope(|subscribe| { + assert_eq!(Gpio::register_listener(&listener, subscribe), Ok(())); + assert_eq!(driver.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!(driver.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(gpio_state.get(), Some(GpioState::Low)); + + assert_eq!(pin.disable_interrupts(), Ok(())); + assert_eq!(driver.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!(driver.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + }); + }); + }); +} diff --git a/unittest/src/fake/gpio/mod.rs b/unittest/src/fake/gpio/mod.rs index 832e9796..08181b0c 100644 --- a/unittest/src/fake/gpio/mod.rs +++ b/unittest/src/fake/gpio/mod.rs @@ -9,11 +9,15 @@ use core::cell::Cell; use libtock_platform::{CommandReturn, ErrorCode}; +use std::convert::TryFrom; + +use crate::upcall; #[derive(Copy, Clone, PartialEq, Debug)] pub enum GpioMode { Output, Input(PullMode), + Disable, } #[derive(Copy, Clone, PartialEq, Debug)] @@ -23,25 +27,58 @@ pub enum PullMode { PullDown = 2, } +impl TryFrom for PullMode { + type Error = ErrorCode; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(PullMode::PullNone), + 1 => Ok(PullMode::PullUp), + 2 => Ok(PullMode::PullDown), + _ => Err(ErrorCode::Invalid), + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum InterruptEdge { + Either, + Rising, + Falling, +} + +impl TryFrom for InterruptEdge { + type Error = ErrorCode; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(InterruptEdge::Either), + 1 => Ok(InterruptEdge::Rising), + 2 => Ok(InterruptEdge::Falling), + _ => Err(ErrorCode::Invalid), + } + } +} + #[derive(Copy, Clone, PartialEq, Debug)] pub struct GpioState { pub value: bool, pub mode: GpioMode, - pub interrupt_enabled: bool, + pub interrupt_enabled: Option, } pub struct Gpio { - gpios: [Cell; NUM_GPIOS], + gpios: [Cell>; NUM_GPIOS], } impl Gpio { pub fn new() -> std::rc::Rc> { #[allow(clippy::declare_interior_mutable_const)] - const OFF: Cell = Cell::new(GpioState { + const OFF: Cell> = Cell::new(Some(GpioState { value: false, mode: GpioMode::Input(PullMode::PullNone), - interrupt_enabled: false, - }); + interrupt_enabled: None, + })); std::rc::Rc::new(Gpio { gpios: [OFF; NUM_GPIOS], }) @@ -50,39 +87,107 @@ impl Gpio { impl crate::fake::SyscallDriver for Gpio { fn id(&self) -> u32 { - DRIVER_ID + DRIVER_NUMBER } fn num_upcalls(&self) -> u32 { 1 } - fn command(&self, command_number: u32, argument0: u32, _argument1: u32) -> CommandReturn { + fn command(&self, command_number: u32, argument0: u32, argument1: u32) -> CommandReturn { if command_number == GPIO_COUNT { crate::command_return::success_u32(NUM_GPIOS as u32) - } else { - if argument0 < NUM_GPIOS as u32 { + } else if argument0 < NUM_GPIOS as u32 { + if self.gpios[argument0 as usize].get().is_some() { + let gpio = self.gpios[argument0 as usize].get().unwrap(); match command_number { GPIO_ENABLE_OUTPUT => { - let gpio = self.gpios[argument0 as usize].get(); - self.gpios[argument0 as usize].set(GpioState { + self.gpios[argument0 as usize].set(Some(GpioState { mode: GpioMode::Output, ..gpio - }); + })); crate::command_return::success() } GPIO_SET => { - let gpio = self.gpios[argument0 as usize].get(); - self.gpios[argument0 as usize].set(GpioState { - mode: GpioMode::Output, + if let GpioMode::Output = gpio.mode { + self.gpios[argument0 as usize].set(Some(GpioState { + value: true, + ..gpio + })); + } + crate::command_return::success() + } + GPIO_CLEAR => { + if let GpioMode::Output = gpio.mode { + self.gpios[argument0 as usize].set(Some(GpioState { + value: false, + ..gpio + })); + } + crate::command_return::success() + } + GPIO_TOGGLE => { + if let GpioMode::Output = gpio.mode { + self.gpios[argument0 as usize].set(Some(GpioState { + value: !gpio.value, + ..gpio + })); + } + crate::command_return::success() + } + GPIO_ENABLE_INPUT => { + let pull_mode = PullMode::try_from(argument1); + match pull_mode { + Ok(mode) => { + self.gpios[argument0 as usize].set(Some(GpioState { + mode: GpioMode::Input(mode), + ..gpio + })); + crate::command_return::success() + } + Err(error) => crate::command_return::failure(error), + } + } + GPIO_READ_INPUT => { + if let GpioMode::Input(_) = gpio.mode { + crate::command_return::success_u32(gpio.value as u32) + } else { + crate::command_return::success_u32(0) + } + } + GPIO_ENABLE_INTERRUPTS => { + let edge = InterruptEdge::try_from(argument1); + match edge { + Ok(interrupt_edge) => { + self.gpios[argument0 as usize].set(Some(GpioState { + interrupt_enabled: Some(interrupt_edge), + ..gpio + })); + crate::command_return::success() + } + Err(error) => crate::command_return::failure(error), + } + } + GPIO_DISABLE_INTERRUPTS => { + self.gpios[argument0 as usize].set(Some(GpioState { + interrupt_enabled: None, ..gpio - }); + })); + crate::command_return::success() + } + GPIO_DISABLE => { + self.gpios[argument0 as usize].set(Some(GpioState { + mode: GpioMode::Disable, + ..gpio + })); crate::command_return::success() } _ => crate::command_return::failure(ErrorCode::NoSupport), } } else { - crate::command_return::failure(ErrorCode::Invalid) + crate::command_return::failure(ErrorCode::NoDevice) } + } else { + crate::command_return::failure(ErrorCode::Invalid) } } } @@ -94,7 +199,7 @@ impl crate::fake::SyscallDriver for Gpio { #[cfg(test)] mod tests; -const DRIVER_ID: u32 = 4; +const DRIVER_NUMBER: u32 = 4; // Command IDs const GPIO_COUNT: u32 = 0; @@ -106,22 +211,55 @@ const GPIO_TOGGLE: u32 = 4; const GPIO_ENABLE_INPUT: u32 = 5; const GPIO_READ_INPUT: u32 = 6; + +const GPIO_ENABLE_INTERRUPTS: u32 = 7; +const GPIO_DISABLE_INTERRUPTS: u32 = 8; + const GPIO_DISABLE: u32 = 9; impl Gpio { - pub fn set_pressed(&self, button: u32, pressed: bool) -> Result<(), ErrorCode> { - self.buttons - .get(button as usize) - .map(|button| { - button.set(GpioState { - pressed, - ..button.get() - }) + pub fn set_missing_gpio(&self, gpio: usize) { + if (gpio as usize) < self.gpios.len() { + self.gpios[gpio as usize].set(None); + } + } + + pub fn set_value(&self, pin: u32, value: bool) -> Result<(), ErrorCode> { + self.gpios + .get(pin as usize) + .map(|gpio| { + if let Some(gpio_state) = gpio.get() { + let original_value = gpio_state.value; + gpio.set(Some(GpioState { + value, + ..gpio_state + })); + if original_value != value { + if value { + if gpio_state.interrupt_enabled == Some(InterruptEdge::Either) + || gpio_state.interrupt_enabled == Some(InterruptEdge::Rising) + { + let _ = upcall::schedule(DRIVER_NUMBER, 0, (pin, value as u32, 0)); + } + } else if gpio_state.interrupt_enabled == Some(InterruptEdge::Falling) + || gpio_state.interrupt_enabled == Some(InterruptEdge::Either) + { + let _ = upcall::schedule(DRIVER_NUMBER, 0, (pin, value as u32, 0)); + } + } + Ok(()) + } else { + Err(ErrorCode::NoDevice) + } }) .ok_or(ErrorCode::Invalid) + .and_then(|value| value) } - pub fn get_button_state(&self, button: u32) -> Option { - self.buttons.get(button as usize).map(|button| button.get()) + pub fn get_gpio_state(&self, button: u32) -> Option { + self.gpios + .get(button as usize) + .map(|button| button.get()) + .and_then(|value| value) } } diff --git a/unittest/src/fake/gpio/tests.rs b/unittest/src/fake/gpio/tests.rs index d62b8d20..1f5f658e 100644 --- a/unittest/src/fake/gpio/tests.rs +++ b/unittest/src/fake/gpio/tests.rs @@ -1,112 +1,407 @@ -// use crate::fake; -// use fake::buttons::*; -// use libtock_platform::ErrorCode; - -// // Tests the command implementation. -// #[test] -// fn command() { -// use fake::SyscallDriver; -// let buttons = Buttons::<10>::new(); - -// assert_eq!( -// buttons.command(BUTTONS_COUNT, 1, 2).get_success_u32(), -// Some(10) -// ); - -// assert_eq!( -// buttons.command(BUTTONS_READ, 11, 0).get_failure(), -// Some(ErrorCode::Invalid) -// ); -// assert_eq!( -// buttons -// .command(BUTTONS_ENABLE_INTERRUPTS, 11, 0) -// .get_failure(), -// Some(ErrorCode::Invalid) -// ); -// assert_eq!( -// buttons -// .command(BUTTONS_DISABLE_INTERRUPTS, 11, 0) -// .get_failure(), -// Some(ErrorCode::Invalid) -// ); - -// for button_index in 0..10 { -// assert_eq!( -// buttons.get_button_state(button_index).unwrap(), -// ButtonState { -// pressed: false, -// interrupt_enabled: false -// } -// ); - -// assert!(buttons -// .command(BUTTONS_ENABLE_INTERRUPTS, button_index, 0) -// .is_success()); -// assert_eq!( -// buttons.get_button_state(button_index).unwrap(), -// ButtonState { -// pressed: false, -// interrupt_enabled: true -// } -// ); - -// assert!(buttons -// .command(BUTTONS_DISABLE_INTERRUPTS, button_index, 0) -// .is_success()); -// assert_eq!( -// buttons.get_button_state(button_index).unwrap(), -// ButtonState { -// pressed: false, -// interrupt_enabled: false -// } -// ); - -// assert_eq!(buttons.set_pressed(button_index, true), Ok(())); -// assert_eq!( -// buttons.get_button_state(button_index).unwrap(), -// ButtonState { -// pressed: true, -// interrupt_enabled: false -// } -// ); - -// assert_eq!(buttons.set_pressed(button_index, false), Ok(())); -// assert_eq!( -// buttons.get_button_state(button_index).unwrap(), -// ButtonState { -// pressed: false, -// interrupt_enabled: false -// } -// ); -// } -// } - -// // Integration test that verifies Buttons works with fake::Kernel and -// // libtock_platform::Syscalls. -// #[test] -// fn kernel_integration() { -// use libtock_platform::Syscalls; -// let kernel = fake::Kernel::new(); -// let buttons = Buttons::<10>::new(); -// kernel.add_driver(&buttons); -// let value = fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_COUNT, 1, 2); -// assert_eq!(value.get_success_u32(), Some(10)); - -// assert_eq!( -// fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_READ, 11, 0).get_failure(), -// Some(ErrorCode::Invalid) -// ); -// assert!(fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_READ, 0, 0).is_success_u32()); - -// assert_eq!( -// fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_ENABLE_INTERRUPTS, 11, 0).get_failure(), -// Some(ErrorCode::Invalid) -// ); -// assert!(fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_ENABLE_INTERRUPTS, 0, 0).is_success()); - -// assert_eq!( -// fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_DISABLE_INTERRUPTS, 11, 0).get_failure(), -// Some(ErrorCode::Invalid) -// ); -// assert!(fake::Syscalls::command(DRIVER_NUMBER, BUTTONS_DISABLE_INTERRUPTS, 0, 0).is_success()); -// } +use crate::fake; +use fake::gpio::*; +use libtock_platform::{share, DefaultConfig, ErrorCode, YieldNoWaitReturn}; + +// Tests the command implementation. +#[test] +fn command() { + use fake::SyscallDriver; + let gpio = Gpio::<10>::new(); + + gpio.set_missing_gpio(1); + + assert_eq!(gpio.command(GPIO_COUNT, 1, 2).get_success_u32(), Some(10)); + + // Enable Output + assert_eq!( + gpio.command(GPIO_ENABLE_OUTPUT, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + gpio.command(GPIO_ENABLE_OUTPUT, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert!(gpio.command(GPIO_ENABLE_OUTPUT, 0, 0).is_success()); + + // Gpio Set + assert_eq!( + gpio.command(GPIO_SET, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + gpio.command(GPIO_SET, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert!(gpio.command(GPIO_SET, 0, 0).is_success(),); + assert!(gpio.get_gpio_state(0).unwrap().value); + + // Gpio Clear + assert_eq!( + gpio.command(GPIO_CLEAR, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + gpio.command(GPIO_CLEAR, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert!(gpio.command(GPIO_CLEAR, 0, 0).is_success(),); + assert!(!gpio.get_gpio_state(0).unwrap().value); + + // Gpio Toggle + assert_eq!( + gpio.command(GPIO_TOGGLE, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + gpio.command(GPIO_TOGGLE, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert!(gpio.command(GPIO_TOGGLE, 0, 0).is_success(),); + assert!(gpio.get_gpio_state(0).unwrap().value); + assert!(gpio.command(GPIO_TOGGLE, 0, 0).is_success(),); + assert!(!gpio.get_gpio_state(0).unwrap().value); + + // Enable Input + assert_eq!( + gpio.command(GPIO_ENABLE_INPUT, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + gpio.command(GPIO_ENABLE_INPUT, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + + assert_eq!( + gpio.command(GPIO_ENABLE_INPUT, 0, 3).get_failure(), + Some(ErrorCode::Invalid) + ); + + assert!(gpio.command(GPIO_ENABLE_INPUT, 0, 0).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullNone) + ); + + assert!(gpio.command(GPIO_ENABLE_INPUT, 0, 1).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullUp) + ); + + assert!(gpio.command(GPIO_ENABLE_INPUT, 0, 2).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullDown) + ); + + // Gpio Read + assert_eq!( + gpio.command(GPIO_READ_INPUT, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + gpio.command(GPIO_READ_INPUT, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + + assert_eq!(gpio.set_value(0, true), Ok(())); + assert_eq!( + gpio.command(GPIO_READ_INPUT, 0, 0).get_success_u32(), + Some(1) + ); + assert_eq!(gpio.set_value(0, false), Ok(())); + assert_eq!( + gpio.command(GPIO_READ_INPUT, 0, 0).get_success_u32(), + Some(0) + ); + + // Enable Interrupts + assert_eq!( + gpio.command(GPIO_ENABLE_INTERRUPTS, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + gpio.command(GPIO_ENABLE_INTERRUPTS, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert_eq!( + gpio.command(GPIO_ENABLE_INTERRUPTS, 0, 3).get_failure(), + Some(ErrorCode::Invalid) + ); + + assert!(gpio.command(GPIO_ENABLE_INTERRUPTS, 0, 0).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().interrupt_enabled, + Some(InterruptEdge::Either) + ); + + assert!(gpio.command(GPIO_ENABLE_INTERRUPTS, 0, 1).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().interrupt_enabled, + Some(InterruptEdge::Rising) + ); + + assert!(gpio.command(GPIO_ENABLE_INTERRUPTS, 0, 2).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().interrupt_enabled, + Some(InterruptEdge::Falling) + ); + + // Disable Interrupts + assert_eq!( + gpio.command(GPIO_DISABLE_INTERRUPTS, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + gpio.command(GPIO_DISABLE_INTERRUPTS, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + + assert!(gpio.command(GPIO_DISABLE_INTERRUPTS, 0, 0).is_success()); + assert_eq!(gpio.get_gpio_state(0).unwrap().interrupt_enabled, None); + + // Disable Pin + assert_eq!( + gpio.command(GPIO_DISABLE, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + gpio.command(GPIO_DISABLE, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + + assert!(gpio.command(GPIO_DISABLE, 0, 0).is_success()); + assert_eq!(gpio.get_gpio_state(0).unwrap().mode, GpioMode::Disable); +} + +// Integration test that verifies Gpio works with fake::Kernel and +// libtock_platform::Syscalls. +#[test] +fn kernel_integration() { + use libtock_platform::Syscalls; + let kernel = fake::Kernel::new(); + let gpio = Gpio::<10>::new(); + gpio.set_missing_gpio(1); + kernel.add_driver(&gpio); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_COUNT, 1, 2).get_success_u32(), + Some(10) + ); + + // Enable Output + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_OUTPUT, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_OUTPUT, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_OUTPUT, 0, 0).is_success()); + + // Gpio Set + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_SET, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_SET, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_SET, 0, 0).is_success(),); + assert!(gpio.get_gpio_state(0).unwrap().value); + + // Gpio Clear + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_CLEAR, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_CLEAR, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_CLEAR, 0, 0).is_success(),); + assert!(!gpio.get_gpio_state(0).unwrap().value); + + // Gpio Toggle + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_TOGGLE, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_TOGGLE, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_TOGGLE, 0, 0).is_success(),); + assert!(gpio.get_gpio_state(0).unwrap().value); + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_TOGGLE, 0, 0).is_success(),); + assert!(!gpio.get_gpio_state(0).unwrap().value); + + // Enable Input + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 0, 3).get_failure(), + Some(ErrorCode::Invalid) + ); + + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 0, 0).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullNone) + ); + + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 0, 1).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullUp) + ); + + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 0, 2).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().mode, + GpioMode::Input(PullMode::PullDown) + ); + + // Gpio Read + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_READ_INPUT, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_READ_INPUT, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + + assert_eq!(gpio.set_value(0, true), Ok(())); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_READ_INPUT, 0, 0).get_success_u32(), + Some(1) + ); + assert_eq!(gpio.set_value(0, false), Ok(())); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_READ_INPUT, 0, 0).get_success_u32(), + Some(0) + ); + + // Enable Interrupts + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 0, 3).get_failure(), + Some(ErrorCode::Invalid) + ); + + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 0, 0).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().interrupt_enabled, + Some(InterruptEdge::Either) + ); + + let listener = Cell::>::new(None); + share::scope(|subscribe| { + assert_eq!( + fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUMBER, 0>( + subscribe, &listener + ), + Ok(()) + ); + + assert_eq!(gpio.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + + assert_eq!(gpio.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert_eq!(gpio.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + + assert_eq!(gpio.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 0, 1).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().interrupt_enabled, + Some(InterruptEdge::Rising) + ); + + assert_eq!(gpio.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + + assert_eq!(gpio.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert_eq!(gpio.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 0, 2).is_success()); + assert_eq!( + gpio.get_gpio_state(0).unwrap().interrupt_enabled, + Some(InterruptEdge::Falling) + ); + + assert_eq!(gpio.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert_eq!(gpio.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + + assert_eq!(gpio.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + }); + + // Disable Interrupts + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE_INTERRUPTS, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE_INTERRUPTS, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE_INTERRUPTS, 0, 0).is_success()); + assert_eq!(gpio.get_gpio_state(0).unwrap().interrupt_enabled, None); + + let listener = Cell::>::new(None); + share::scope(|subscribe| { + assert_eq!( + fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUMBER, 0>( + subscribe, &listener + ), + Ok(()) + ); + + assert_eq!(gpio.set_value(0, true), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert_eq!(gpio.set_value(0, false), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + }); + + // Disable Pin + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE, 11, 0).get_failure(), + Some(ErrorCode::Invalid) + ); + assert_eq!( + fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE, 1, 0).get_failure(), + Some(ErrorCode::NoDevice) + ); + + assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE, 0, 0).is_success()); + assert_eq!(gpio.get_gpio_state(0).unwrap().mode, GpioMode::Disable); +} diff --git a/unittest/src/fake/mod.rs b/unittest/src/fake/mod.rs index 7e9d045f..e8f9030e 100644 --- a/unittest/src/fake/mod.rs +++ b/unittest/src/fake/mod.rs @@ -16,7 +16,7 @@ mod low_level_debug; mod syscall_driver; mod syscalls; -pub use gpio::Gpio; +pub use gpio::{Gpio, GpioMode, InterruptEdge, PullMode}; pub use kernel::Kernel; pub use leds::Leds; pub use low_level_debug::{LowLevelDebug, Message}; From 00a10fbf2ff23394ef2f69c0e4e8ebc68eb8d323 Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Mon, 7 Feb 2022 02:25:50 +0200 Subject: [PATCH 5/9] ready for review --- apis/gpio/src/tests.rs | 56 ++++++++++++++++++++++++++++--- unittest/src/fake/gpio/mod.rs | 63 ++++++++++++++++++++--------------- 2 files changed, 87 insertions(+), 32 deletions(-) diff --git a/apis/gpio/src/tests.rs b/apis/gpio/src/tests.rs index 1c67b59c..84328b06 100644 --- a/apis/gpio/src/tests.rs +++ b/apis/gpio/src/tests.rs @@ -1,9 +1,12 @@ use core::cell::Cell; use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; -use libtock_unittest::fake::{self, GpioMode, InterruptEdge, PullMode}; +use libtock_unittest::{ + fake::{self, GpioMode, InterruptEdge, PullMode}, + upcall, +}; -use crate::{GpioInterruptListener, GpioState, PullDown, PullNone, PullUp}; +use crate::{GpioInterruptListener, GpioState, PullDown, PullNone, PullUp, DRIVER_NUMBER}; type Gpio = super::Gpio; @@ -21,7 +24,7 @@ fn num_gpio() { assert_eq!(Gpio::count(), Ok(10)); } -// Tests the command implementation. +// Tests the OutputPin implementation. #[test] fn output() { let kernel = fake::Kernel::new(); @@ -59,7 +62,7 @@ fn output() { }); } -// Tests the command implementation. +// Tests the InputPin implementation #[test] fn input() { let kernel = fake::Kernel::new(); @@ -111,7 +114,7 @@ fn input() { }); } -// Tests the command implementation. +// Tests the pin interrupts implementation #[test] fn interrupts() { let kernel = fake::Kernel::new(); @@ -121,6 +124,22 @@ fn interrupts() { assert_eq!(Gpio::count(), Ok(10)); + let gpio_state = Cell::>::new(None); + let listener = GpioInterruptListener(|gpio, state| { + assert_eq!(gpio, 0); + gpio_state.set(Some(state)); + }); + + share::scope(|subscribe| { + assert_eq!(Gpio::register_listener(&listener, subscribe), Ok(())); + assert_eq!(upcall::schedule(DRIVER_NUMBER, 0, (0, 0, 0)), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(gpio_state.get(), Some(GpioState::Low)); + }); + + assert_eq!(upcall::schedule(DRIVER_NUMBER, 0, (0, 0, 0)), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + let pin_11 = Gpio::get_pin(11); assert!(pin_11.is_err()); let _ = pin_11.map_err(|error| assert_eq!(error, ErrorCode::Invalid)); @@ -261,3 +280,30 @@ fn interrupts() { }); }); } + +// Tests the pin event subcribe implementation +#[test] +fn subscribe() { + let kernel = fake::Kernel::new(); + let driver = fake::Gpio::<10>::new(); + driver.set_missing_gpio(1); + kernel.add_driver(&driver); + + assert_eq!(Gpio::count(), Ok(10)); + + let gpio_state = Cell::>::new(None); + let listener = GpioInterruptListener(|gpio, state| { + assert_eq!(gpio, 0); + gpio_state.set(Some(state)); + }); + + share::scope(|subscribe| { + assert_eq!(Gpio::register_listener(&listener, subscribe), Ok(())); + assert_eq!(upcall::schedule(DRIVER_NUMBER, 0, (0, 0, 0)), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(gpio_state.get(), Some(GpioState::Low)); + }); + + assert_eq!(upcall::schedule(DRIVER_NUMBER, 0, (0, 0, 0)), Ok(())); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); +} diff --git a/unittest/src/fake/gpio/mod.rs b/unittest/src/fake/gpio/mod.rs index 08181b0c..7b2ce70b 100644 --- a/unittest/src/fake/gpio/mod.rs +++ b/unittest/src/fake/gpio/mod.rs @@ -13,6 +13,32 @@ use std::convert::TryFrom; use crate::upcall; +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUMBER: u32 = 4; + +// Command IDs +const GPIO_COUNT: u32 = 0; + +const GPIO_ENABLE_OUTPUT: u32 = 1; +const GPIO_SET: u32 = 2; +const GPIO_CLEAR: u32 = 3; +const GPIO_TOGGLE: u32 = 4; + +const GPIO_ENABLE_INPUT: u32 = 5; +const GPIO_READ_INPUT: u32 = 6; + +const GPIO_ENABLE_INTERRUPTS: u32 = 7; +const GPIO_DISABLE_INTERRUPTS: u32 = 8; + +const GPIO_DISABLE: u32 = 9; + +// ----------------------------------------------------------------------------- +// Definitions +// ----------------------------------------------------------------------------- + #[derive(Copy, Clone, PartialEq, Debug)] pub enum GpioMode { Output, @@ -71,6 +97,10 @@ pub struct Gpio { gpios: [Cell>; NUM_GPIOS], } +// ----------------------------------------------------------------------------- +// Implementation details below +// ----------------------------------------------------------------------------- + impl Gpio { pub fn new() -> std::rc::Rc> { #[allow(clippy::declare_interior_mutable_const)] @@ -192,31 +222,6 @@ impl crate::fake::SyscallDriver for Gpio { } } -// ----------------------------------------------------------------------------- -// Implementation details below -// ----------------------------------------------------------------------------- - -#[cfg(test)] -mod tests; - -const DRIVER_NUMBER: u32 = 4; - -// Command IDs -const GPIO_COUNT: u32 = 0; - -const GPIO_ENABLE_OUTPUT: u32 = 1; -const GPIO_SET: u32 = 2; -const GPIO_CLEAR: u32 = 3; -const GPIO_TOGGLE: u32 = 4; - -const GPIO_ENABLE_INPUT: u32 = 5; -const GPIO_READ_INPUT: u32 = 6; - -const GPIO_ENABLE_INTERRUPTS: u32 = 7; -const GPIO_DISABLE_INTERRUPTS: u32 = 8; - -const GPIO_DISABLE: u32 = 9; - impl Gpio { pub fn set_missing_gpio(&self, gpio: usize) { if (gpio as usize) < self.gpios.len() { @@ -239,12 +244,14 @@ impl Gpio { if gpio_state.interrupt_enabled == Some(InterruptEdge::Either) || gpio_state.interrupt_enabled == Some(InterruptEdge::Rising) { - let _ = upcall::schedule(DRIVER_NUMBER, 0, (pin, value as u32, 0)); + upcall::schedule(DRIVER_NUMBER, 0, (pin, value as u32, 0)) + .expect("Unable to schedule upcall"); } } else if gpio_state.interrupt_enabled == Some(InterruptEdge::Falling) || gpio_state.interrupt_enabled == Some(InterruptEdge::Either) { - let _ = upcall::schedule(DRIVER_NUMBER, 0, (pin, value as u32, 0)); + upcall::schedule(DRIVER_NUMBER, 0, (pin, value as u32, 0)) + .expect("Unable to schedule upcall"); } } Ok(()) @@ -263,3 +270,5 @@ impl Gpio { .and_then(|value| value) } } +#[cfg(test)] +mod tests; From 257bed97357b2742ec95e6cc7c315787d6a2b21b Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Mon, 7 Feb 2022 09:22:14 +0200 Subject: [PATCH 6/9] rename --- apis/gpio/src/lib.rs | 28 ++++++------ apis/gpio/src/tests.rs | 10 ++--- unittest/src/fake/gpio/mod.rs | 8 ++-- unittest/src/fake/gpio/tests.rs | 80 ++++++++++++++++----------------- 4 files changed, 61 insertions(+), 65 deletions(-) diff --git a/apis/gpio/src/lib.rs b/apis/gpio/src/lib.rs index aa66fdbe..db712d0d 100644 --- a/apis/gpio/src/lib.rs +++ b/apis/gpio/src/lib.rs @@ -17,7 +17,7 @@ use libtock_platform::{ /// /// ``` -const DRIVER_NUMBER: u32 = 4; +const DRIVER_NUM: u32 = 4; // Command IDs const GPIO_COUNT: u32 = 0; @@ -81,15 +81,15 @@ impl Gpio { /// that the driver is working, as it may still fail to allocate grant /// memory. pub fn count() -> Result { - S::command(DRIVER_NUMBER, GPIO_COUNT, 0, 0).to_result() + S::command(DRIVER_NUM, GPIO_COUNT, 0, 0).to_result() } fn enable_gpio_output(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUMBER, GPIO_ENABLE_OUTPUT, pin, 0).to_result() + S::command(DRIVER_NUM, GPIO_ENABLE_OUTPUT, pin, 0).to_result() } fn enable_gpio_input(pin: u32, mode: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, pin, mode).to_result() + S::command(DRIVER_NUM, GPIO_ENABLE_INPUT, pin, mode).to_result() } fn write(pin: u32, state: GpioState) -> Result<(), ErrorCode> { @@ -97,28 +97,28 @@ impl Gpio { GpioState::Low => GPIO_CLEAR, _ => GPIO_SET, }; - S::command(DRIVER_NUMBER, action, pin, 0).to_result() + S::command(DRIVER_NUM, action, pin, 0).to_result() } fn read(pin: u32) -> Result { - let pin_state: u32 = S::command(DRIVER_NUMBER, GPIO_READ_INPUT, pin, 0).to_result()?; + let pin_state: u32 = S::command(DRIVER_NUM, GPIO_READ_INPUT, pin, 0).to_result()?; Ok(pin_state.into()) } fn toggle(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUMBER, GPIO_TOGGLE, pin, 0).to_result() + S::command(DRIVER_NUM, GPIO_TOGGLE, pin, 0).to_result() } fn disable(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUMBER, GPIO_DISABLE, pin, 0).to_result() + S::command(DRIVER_NUM, GPIO_DISABLE, pin, 0).to_result() } fn enable_interrupts(pin: u32, edge: PinInterruptEdge) -> Result<(), ErrorCode> { - S::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, pin, edge as u32).to_result() + S::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, pin, edge as u32).to_result() } fn disable_interrupts(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUMBER, GPIO_DISABLE_INTERRUPTS, pin, 0).to_result() + S::command(DRIVER_NUM, GPIO_DISABLE_INTERRUPTS, pin, 0).to_result() } pub fn get_pin(pin: u32) -> Result, ErrorCode> { @@ -136,9 +136,9 @@ impl Gpio { /// previously registered listener. pub fn register_listener<'share, F: Fn(u32, GpioState)>( listener: &'share GpioInterruptListener, - subscribe: Handle>, + subscribe: Handle>, ) -> Result<(), ErrorCode> { - S::subscribe::<_, _, DefaultConfig, DRIVER_NUMBER, 0>(subscribe, listener) + S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) } /// Unregister the interrupt listener @@ -146,7 +146,7 @@ impl Gpio { /// This function may be used even if there was no /// previously registered listener. pub fn unregister_listener() { - S::unsubscribe(DRIVER_NUMBER, 0) + S::unsubscribe(DRIVER_NUM, 0) } } @@ -160,7 +160,7 @@ impl Gpio { /// ``` pub struct GpioInterruptListener(pub F); -impl Upcall> for GpioInterruptListener { +impl Upcall> for GpioInterruptListener { fn upcall(&self, gpio_index: u32, value: u32, _arg2: u32) { self.0(gpio_index, value.into()) } diff --git a/apis/gpio/src/tests.rs b/apis/gpio/src/tests.rs index 84328b06..f6ffff5f 100644 --- a/apis/gpio/src/tests.rs +++ b/apis/gpio/src/tests.rs @@ -6,7 +6,7 @@ use libtock_unittest::{ upcall, }; -use crate::{GpioInterruptListener, GpioState, PullDown, PullNone, PullUp, DRIVER_NUMBER}; +use crate::{GpioInterruptListener, GpioState, PullDown, PullNone, PullUp, DRIVER_NUM}; type Gpio = super::Gpio; @@ -132,12 +132,12 @@ fn interrupts() { share::scope(|subscribe| { assert_eq!(Gpio::register_listener(&listener, subscribe), Ok(())); - assert_eq!(upcall::schedule(DRIVER_NUMBER, 0, (0, 0, 0)), Ok(())); + assert_eq!(upcall::schedule(DRIVER_NUM, 0, (0, 0, 0)), Ok(())); assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); assert_eq!(gpio_state.get(), Some(GpioState::Low)); }); - assert_eq!(upcall::schedule(DRIVER_NUMBER, 0, (0, 0, 0)), Ok(())); + assert_eq!(upcall::schedule(DRIVER_NUM, 0, (0, 0, 0)), Ok(())); assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); let pin_11 = Gpio::get_pin(11); @@ -299,11 +299,11 @@ fn subscribe() { share::scope(|subscribe| { assert_eq!(Gpio::register_listener(&listener, subscribe), Ok(())); - assert_eq!(upcall::schedule(DRIVER_NUMBER, 0, (0, 0, 0)), Ok(())); + assert_eq!(upcall::schedule(DRIVER_NUM, 0, (0, 0, 0)), Ok(())); assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); assert_eq!(gpio_state.get(), Some(GpioState::Low)); }); - assert_eq!(upcall::schedule(DRIVER_NUMBER, 0, (0, 0, 0)), Ok(())); + assert_eq!(upcall::schedule(DRIVER_NUM, 0, (0, 0, 0)), Ok(())); assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); } diff --git a/unittest/src/fake/gpio/mod.rs b/unittest/src/fake/gpio/mod.rs index 7b2ce70b..2b3b4e50 100644 --- a/unittest/src/fake/gpio/mod.rs +++ b/unittest/src/fake/gpio/mod.rs @@ -17,7 +17,7 @@ use crate::upcall; // Driver number and command IDs // ----------------------------------------------------------------------------- -const DRIVER_NUMBER: u32 = 4; +const DRIVER_NUM: u32 = 4; // Command IDs const GPIO_COUNT: u32 = 0; @@ -117,7 +117,7 @@ impl Gpio { impl crate::fake::SyscallDriver for Gpio { fn id(&self) -> u32 { - DRIVER_NUMBER + DRIVER_NUM } fn num_upcalls(&self) -> u32 { 1 @@ -244,13 +244,13 @@ impl Gpio { if gpio_state.interrupt_enabled == Some(InterruptEdge::Either) || gpio_state.interrupt_enabled == Some(InterruptEdge::Rising) { - upcall::schedule(DRIVER_NUMBER, 0, (pin, value as u32, 0)) + upcall::schedule(DRIVER_NUM, 0, (pin, value as u32, 0)) .expect("Unable to schedule upcall"); } } else if gpio_state.interrupt_enabled == Some(InterruptEdge::Falling) || gpio_state.interrupt_enabled == Some(InterruptEdge::Either) { - upcall::schedule(DRIVER_NUMBER, 0, (pin, value as u32, 0)) + upcall::schedule(DRIVER_NUM, 0, (pin, value as u32, 0)) .expect("Unable to schedule upcall"); } } diff --git a/unittest/src/fake/gpio/tests.rs b/unittest/src/fake/gpio/tests.rs index 1f5f658e..daa77c1a 100644 --- a/unittest/src/fake/gpio/tests.rs +++ b/unittest/src/fake/gpio/tests.rs @@ -184,87 +184,87 @@ fn kernel_integration() { gpio.set_missing_gpio(1); kernel.add_driver(&gpio); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_COUNT, 1, 2).get_success_u32(), + fake::Syscalls::command(DRIVER_NUM, GPIO_COUNT, 1, 2).get_success_u32(), Some(10) ); // Enable Output assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_OUTPUT, 11, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_OUTPUT, 11, 0).get_failure(), Some(ErrorCode::Invalid) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_OUTPUT, 1, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_OUTPUT, 1, 0).get_failure(), Some(ErrorCode::NoDevice) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_OUTPUT, 0, 0).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_OUTPUT, 0, 0).is_success()); // Gpio Set assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_SET, 11, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_SET, 11, 0).get_failure(), Some(ErrorCode::Invalid) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_SET, 1, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_SET, 1, 0).get_failure(), Some(ErrorCode::NoDevice) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_SET, 0, 0).is_success(),); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_SET, 0, 0).is_success(),); assert!(gpio.get_gpio_state(0).unwrap().value); // Gpio Clear assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_CLEAR, 11, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_CLEAR, 11, 0).get_failure(), Some(ErrorCode::Invalid) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_CLEAR, 1, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_CLEAR, 1, 0).get_failure(), Some(ErrorCode::NoDevice) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_CLEAR, 0, 0).is_success(),); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_CLEAR, 0, 0).is_success(),); assert!(!gpio.get_gpio_state(0).unwrap().value); // Gpio Toggle assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_TOGGLE, 11, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_TOGGLE, 11, 0).get_failure(), Some(ErrorCode::Invalid) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_TOGGLE, 1, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_TOGGLE, 1, 0).get_failure(), Some(ErrorCode::NoDevice) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_TOGGLE, 0, 0).is_success(),); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_TOGGLE, 0, 0).is_success(),); assert!(gpio.get_gpio_state(0).unwrap().value); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_TOGGLE, 0, 0).is_success(),); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_TOGGLE, 0, 0).is_success(),); assert!(!gpio.get_gpio_state(0).unwrap().value); // Enable Input assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 11, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INPUT, 11, 0).get_failure(), Some(ErrorCode::Invalid) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 1, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INPUT, 1, 0).get_failure(), Some(ErrorCode::NoDevice) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 0, 3).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INPUT, 0, 3).get_failure(), Some(ErrorCode::Invalid) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 0, 0).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INPUT, 0, 0).is_success()); assert_eq!( gpio.get_gpio_state(0).unwrap().mode, GpioMode::Input(PullMode::PullNone) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 0, 1).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INPUT, 0, 1).is_success()); assert_eq!( gpio.get_gpio_state(0).unwrap().mode, GpioMode::Input(PullMode::PullUp) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INPUT, 0, 2).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INPUT, 0, 2).is_success()); assert_eq!( gpio.get_gpio_state(0).unwrap().mode, GpioMode::Input(PullMode::PullDown) @@ -272,40 +272,40 @@ fn kernel_integration() { // Gpio Read assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_READ_INPUT, 11, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_READ_INPUT, 11, 0).get_failure(), Some(ErrorCode::Invalid) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_READ_INPUT, 1, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_READ_INPUT, 1, 0).get_failure(), Some(ErrorCode::NoDevice) ); assert_eq!(gpio.set_value(0, true), Ok(())); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_READ_INPUT, 0, 0).get_success_u32(), + fake::Syscalls::command(DRIVER_NUM, GPIO_READ_INPUT, 0, 0).get_success_u32(), Some(1) ); assert_eq!(gpio.set_value(0, false), Ok(())); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_READ_INPUT, 0, 0).get_success_u32(), + fake::Syscalls::command(DRIVER_NUM, GPIO_READ_INPUT, 0, 0).get_success_u32(), Some(0) ); // Enable Interrupts assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 11, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, 11, 0).get_failure(), Some(ErrorCode::Invalid) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 1, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, 1, 0).get_failure(), Some(ErrorCode::NoDevice) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 0, 3).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, 0, 3).get_failure(), Some(ErrorCode::Invalid) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 0, 0).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, 0, 0).is_success()); assert_eq!( gpio.get_gpio_state(0).unwrap().interrupt_enabled, Some(InterruptEdge::Either) @@ -314,9 +314,7 @@ fn kernel_integration() { let listener = Cell::>::new(None); share::scope(|subscribe| { assert_eq!( - fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUMBER, 0>( - subscribe, &listener - ), + fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), Ok(()) ); @@ -332,7 +330,7 @@ fn kernel_integration() { assert_eq!(gpio.set_value(0, false), Ok(())); assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 0, 1).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, 0, 1).is_success()); assert_eq!( gpio.get_gpio_state(0).unwrap().interrupt_enabled, Some(InterruptEdge::Rising) @@ -347,7 +345,7 @@ fn kernel_integration() { assert_eq!(gpio.set_value(0, false), Ok(())); assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_ENABLE_INTERRUPTS, 0, 2).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, 0, 2).is_success()); assert_eq!( gpio.get_gpio_state(0).unwrap().interrupt_enabled, Some(InterruptEdge::Falling) @@ -365,23 +363,21 @@ fn kernel_integration() { // Disable Interrupts assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE_INTERRUPTS, 11, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_DISABLE_INTERRUPTS, 11, 0).get_failure(), Some(ErrorCode::Invalid) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE_INTERRUPTS, 1, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_DISABLE_INTERRUPTS, 1, 0).get_failure(), Some(ErrorCode::NoDevice) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE_INTERRUPTS, 0, 0).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_DISABLE_INTERRUPTS, 0, 0).is_success()); assert_eq!(gpio.get_gpio_state(0).unwrap().interrupt_enabled, None); let listener = Cell::>::new(None); share::scope(|subscribe| { assert_eq!( - fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUMBER, 0>( - subscribe, &listener - ), + fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), Ok(()) ); @@ -394,14 +390,14 @@ fn kernel_integration() { // Disable Pin assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE, 11, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_DISABLE, 11, 0).get_failure(), Some(ErrorCode::Invalid) ); assert_eq!( - fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE, 1, 0).get_failure(), + fake::Syscalls::command(DRIVER_NUM, GPIO_DISABLE, 1, 0).get_failure(), Some(ErrorCode::NoDevice) ); - assert!(fake::Syscalls::command(DRIVER_NUMBER, GPIO_DISABLE, 0, 0).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, GPIO_DISABLE, 0, 0).is_success()); assert_eq!(gpio.get_gpio_state(0).unwrap().mode, GpioMode::Disable); } From e6243bfc7dab1a0f36e737cb8b1c32f15793e2d6 Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Wed, 9 Feb 2022 17:33:17 +0200 Subject: [PATCH 7/9] update --- apis/gpio/src/lib.rs | 123 +++++++++++++++------------- unittest/src/fake/gpio/mod.rs | 147 ++++++++++++++++------------------ 2 files changed, 135 insertions(+), 135 deletions(-) diff --git a/apis/gpio/src/lib.rs b/apis/gpio/src/lib.rs index db712d0d..bc30fd1f 100644 --- a/apis/gpio/src/lib.rs +++ b/apis/gpio/src/lib.rs @@ -17,26 +17,7 @@ use libtock_platform::{ /// /// ``` -const DRIVER_NUM: u32 = 4; - -// Command IDs -const GPIO_COUNT: u32 = 0; - -const GPIO_ENABLE_OUTPUT: u32 = 1; -const GPIO_SET: u32 = 2; -const GPIO_CLEAR: u32 = 3; -const GPIO_TOGGLE: u32 = 4; - -const GPIO_ENABLE_INPUT: u32 = 5; -const GPIO_READ_INPUT: u32 = 6; - -const GPIO_ENABLE_INTERRUPTS: u32 = 7; -const GPIO_DISABLE_INTERRUPTS: u32 = 8; - -const GPIO_DISABLE: u32 = 9; - -#[derive(Copy, Clone, PartialEq)] -#[cfg_attr(test, derive(Debug))] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum GpioState { Low = 0, High = 1, @@ -84,43 +65,6 @@ impl Gpio { S::command(DRIVER_NUM, GPIO_COUNT, 0, 0).to_result() } - fn enable_gpio_output(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUM, GPIO_ENABLE_OUTPUT, pin, 0).to_result() - } - - fn enable_gpio_input(pin: u32, mode: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUM, GPIO_ENABLE_INPUT, pin, mode).to_result() - } - - fn write(pin: u32, state: GpioState) -> Result<(), ErrorCode> { - let action = match state { - GpioState::Low => GPIO_CLEAR, - _ => GPIO_SET, - }; - S::command(DRIVER_NUM, action, pin, 0).to_result() - } - - fn read(pin: u32) -> Result { - let pin_state: u32 = S::command(DRIVER_NUM, GPIO_READ_INPUT, pin, 0).to_result()?; - Ok(pin_state.into()) - } - - fn toggle(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUM, GPIO_TOGGLE, pin, 0).to_result() - } - - fn disable(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUM, GPIO_DISABLE, pin, 0).to_result() - } - - fn enable_interrupts(pin: u32, edge: PinInterruptEdge) -> Result<(), ErrorCode> { - S::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, pin, edge as u32).to_result() - } - - fn disable_interrupts(pin: u32) -> Result<(), ErrorCode> { - S::command(DRIVER_NUM, GPIO_DISABLE_INTERRUPTS, pin, 0).to_result() - } - pub fn get_pin(pin: u32) -> Result, ErrorCode> { Self::disable(pin)?; Ok(Pin { @@ -242,5 +186,70 @@ impl Drop for InputPin<'_, S, P> { } } +// ----------------------------------------------------------------------------- +// Implementation details below +// ----------------------------------------------------------------------------- + +impl Gpio { + fn enable_gpio_output(pin: u32) -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, GPIO_ENABLE_OUTPUT, pin, 0).to_result() + } + + fn enable_gpio_input(pin: u32, mode: u32) -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, GPIO_ENABLE_INPUT, pin, mode).to_result() + } + + fn write(pin: u32, state: GpioState) -> Result<(), ErrorCode> { + let action = match state { + GpioState::Low => GPIO_CLEAR, + _ => GPIO_SET, + }; + S::command(DRIVER_NUM, action, pin, 0).to_result() + } + + fn read(pin: u32) -> Result { + let pin_state: u32 = S::command(DRIVER_NUM, GPIO_READ_INPUT, pin, 0).to_result()?; + Ok(pin_state.into()) + } + + fn toggle(pin: u32) -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, GPIO_TOGGLE, pin, 0).to_result() + } + + fn disable(pin: u32) -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, GPIO_DISABLE, pin, 0).to_result() + } + + fn enable_interrupts(pin: u32, edge: PinInterruptEdge) -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, GPIO_ENABLE_INTERRUPTS, pin, edge as u32).to_result() + } + + fn disable_interrupts(pin: u32) -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, GPIO_DISABLE_INTERRUPTS, pin, 0).to_result() + } +} + #[cfg(test)] mod tests; + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 4; + +// Command IDs +const GPIO_COUNT: u32 = 0; + +const GPIO_ENABLE_OUTPUT: u32 = 1; +const GPIO_SET: u32 = 2; +const GPIO_CLEAR: u32 = 3; +const GPIO_TOGGLE: u32 = 4; + +const GPIO_ENABLE_INPUT: u32 = 5; +const GPIO_READ_INPUT: u32 = 6; + +const GPIO_ENABLE_INTERRUPTS: u32 = 7; +const GPIO_DISABLE_INTERRUPTS: u32 = 8; + +const GPIO_DISABLE: u32 = 9; diff --git a/unittest/src/fake/gpio/mod.rs b/unittest/src/fake/gpio/mod.rs index 2b3b4e50..ffc733c7 100644 --- a/unittest/src/fake/gpio/mod.rs +++ b/unittest/src/fake/gpio/mod.rs @@ -13,32 +13,6 @@ use std::convert::TryFrom; use crate::upcall; -// ----------------------------------------------------------------------------- -// Driver number and command IDs -// ----------------------------------------------------------------------------- - -const DRIVER_NUM: u32 = 4; - -// Command IDs -const GPIO_COUNT: u32 = 0; - -const GPIO_ENABLE_OUTPUT: u32 = 1; -const GPIO_SET: u32 = 2; -const GPIO_CLEAR: u32 = 3; -const GPIO_TOGGLE: u32 = 4; - -const GPIO_ENABLE_INPUT: u32 = 5; -const GPIO_READ_INPUT: u32 = 6; - -const GPIO_ENABLE_INTERRUPTS: u32 = 7; -const GPIO_DISABLE_INTERRUPTS: u32 = 8; - -const GPIO_DISABLE: u32 = 9; - -// ----------------------------------------------------------------------------- -// Definitions -// ----------------------------------------------------------------------------- - #[derive(Copy, Clone, PartialEq, Debug)] pub enum GpioMode { Output, @@ -97,10 +71,6 @@ pub struct Gpio { gpios: [Cell>; NUM_GPIOS], } -// ----------------------------------------------------------------------------- -// Implementation details below -// ----------------------------------------------------------------------------- - impl Gpio { pub fn new() -> std::rc::Rc> { #[allow(clippy::declare_interior_mutable_const)] @@ -113,6 +83,53 @@ impl Gpio { gpios: [OFF; NUM_GPIOS], }) } + + pub fn set_missing_gpio(&self, gpio: usize) { + if (gpio as usize) < self.gpios.len() { + self.gpios[gpio as usize].set(None); + } + } + + pub fn set_value(&self, pin: u32, value: bool) -> Result<(), ErrorCode> { + self.gpios + .get(pin as usize) + .map(|gpio| { + if let Some(gpio_state) = gpio.get() { + let original_value = gpio_state.value; + gpio.set(Some(GpioState { + value, + ..gpio_state + })); + if original_value != value { + if value { + if gpio_state.interrupt_enabled == Some(InterruptEdge::Either) + || gpio_state.interrupt_enabled == Some(InterruptEdge::Rising) + { + upcall::schedule(DRIVER_NUM, 0, (pin, value as u32, 0)) + .expect("Unable to schedule upcall"); + } + } else if gpio_state.interrupt_enabled == Some(InterruptEdge::Falling) + || gpio_state.interrupt_enabled == Some(InterruptEdge::Either) + { + upcall::schedule(DRIVER_NUM, 0, (pin, value as u32, 0)) + .expect("Unable to schedule upcall"); + } + } + Ok(()) + } else { + Err(ErrorCode::NoDevice) + } + }) + .ok_or(ErrorCode::Invalid) + .and_then(|value| value) + } + + pub fn get_gpio_state(&self, button: u32) -> Option { + self.gpios + .get(button as usize) + .map(|button| button.get()) + .and_then(|value| value) + } } impl crate::fake::SyscallDriver for Gpio { @@ -222,53 +239,27 @@ impl crate::fake::SyscallDriver for Gpio { } } -impl Gpio { - pub fn set_missing_gpio(&self, gpio: usize) { - if (gpio as usize) < self.gpios.len() { - self.gpios[gpio as usize].set(None); - } - } - - pub fn set_value(&self, pin: u32, value: bool) -> Result<(), ErrorCode> { - self.gpios - .get(pin as usize) - .map(|gpio| { - if let Some(gpio_state) = gpio.get() { - let original_value = gpio_state.value; - gpio.set(Some(GpioState { - value, - ..gpio_state - })); - if original_value != value { - if value { - if gpio_state.interrupt_enabled == Some(InterruptEdge::Either) - || gpio_state.interrupt_enabled == Some(InterruptEdge::Rising) - { - upcall::schedule(DRIVER_NUM, 0, (pin, value as u32, 0)) - .expect("Unable to schedule upcall"); - } - } else if gpio_state.interrupt_enabled == Some(InterruptEdge::Falling) - || gpio_state.interrupt_enabled == Some(InterruptEdge::Either) - { - upcall::schedule(DRIVER_NUM, 0, (pin, value as u32, 0)) - .expect("Unable to schedule upcall"); - } - } - Ok(()) - } else { - Err(ErrorCode::NoDevice) - } - }) - .ok_or(ErrorCode::Invalid) - .and_then(|value| value) - } - - pub fn get_gpio_state(&self, button: u32) -> Option { - self.gpios - .get(button as usize) - .map(|button| button.get()) - .and_then(|value| value) - } -} #[cfg(test)] mod tests; + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 4; + +// Command IDs +const GPIO_COUNT: u32 = 0; + +const GPIO_ENABLE_OUTPUT: u32 = 1; +const GPIO_SET: u32 = 2; +const GPIO_CLEAR: u32 = 3; +const GPIO_TOGGLE: u32 = 4; + +const GPIO_ENABLE_INPUT: u32 = 5; +const GPIO_READ_INPUT: u32 = 6; + +const GPIO_ENABLE_INTERRUPTS: u32 = 7; +const GPIO_DISABLE_INTERRUPTS: u32 = 8; + +const GPIO_DISABLE: u32 = 9; From ad708c506b9537de811efe34e9007a52df59077b Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Sun, 13 Feb 2022 15:49:52 +0200 Subject: [PATCH 8/9] use core::matches --- apis/gpio/src/tests.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/apis/gpio/src/tests.rs b/apis/gpio/src/tests.rs index f6ffff5f..8d54906a 100644 --- a/apis/gpio/src/tests.rs +++ b/apis/gpio/src/tests.rs @@ -34,12 +34,8 @@ fn output() { assert_eq!(Gpio::count(), Ok(10)); - let pin_11 = Gpio::get_pin(11); - assert!(pin_11.is_err()); - let _ = pin_11.map_err(|error| assert_eq!(error, ErrorCode::Invalid)); - let pin_1 = Gpio::get_pin(1); - assert!(pin_1.is_err()); - let _ = pin_1.map_err(|error| assert_eq!(error, ErrorCode::NoDevice)); + assert!(core::matches!(Gpio::get_pin(11), Err(ErrorCode::Invalid))); + assert!(core::matches!(Gpio::get_pin(1), Err(ErrorCode::NoDevice))); let pin_0 = Gpio::get_pin(0); assert!(pin_0.is_ok()); @@ -72,12 +68,8 @@ fn input() { assert_eq!(Gpio::count(), Ok(10)); - let pin_11 = Gpio::get_pin(11); - assert!(pin_11.is_err()); - let _ = pin_11.map_err(|error| assert_eq!(error, ErrorCode::Invalid)); - let pin_1 = Gpio::get_pin(1); - assert!(pin_1.is_err()); - let _ = pin_1.map_err(|error| assert_eq!(error, ErrorCode::NoDevice)); + assert!(core::matches!(Gpio::get_pin(11), Err(ErrorCode::Invalid))); + assert!(core::matches!(Gpio::get_pin(1), Err(ErrorCode::NoDevice))); let pin_0 = Gpio::get_pin(0); assert!(pin_0.is_ok()); @@ -140,12 +132,8 @@ fn interrupts() { assert_eq!(upcall::schedule(DRIVER_NUM, 0, (0, 0, 0)), Ok(())); assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); - let pin_11 = Gpio::get_pin(11); - assert!(pin_11.is_err()); - let _ = pin_11.map_err(|error| assert_eq!(error, ErrorCode::Invalid)); - let pin_1 = Gpio::get_pin(1); - assert!(pin_1.is_err()); - let _ = pin_1.map_err(|error| assert_eq!(error, ErrorCode::NoDevice)); + assert!(core::matches!(Gpio::get_pin(11), Err(ErrorCode::Invalid))); + assert!(core::matches!(Gpio::get_pin(1), Err(ErrorCode::NoDevice))); let pin_0 = Gpio::get_pin(0); assert!(pin_0.is_ok()); From 3fb46cd09a4b848c380703d2b9e4301c63c876cc Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Fri, 11 Mar 2022 19:25:05 +0200 Subject: [PATCH 9/9] delete empty line --- unittest/src/fake/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/unittest/src/fake/mod.rs b/unittest/src/fake/mod.rs index f1832a6f..dc7d13bd 100644 --- a/unittest/src/fake/mod.rs +++ b/unittest/src/fake/mod.rs @@ -17,7 +17,6 @@ mod low_level_debug; mod syscall_driver; mod syscalls; - pub use buttons::Buttons; pub use gpio::{Gpio, GpioMode, InterruptEdge, PullMode}; pub use kernel::Kernel;