diff --git a/Cargo.toml b/Cargo.toml index 73df556a..f86ea661 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,17 +10,22 @@ repository = "https://www.github.com/tock/libtock-rs" version = "0.1.0" [dependencies] +libtock_adc = { path = "apis/adc"} libtock_air_quality = { path = "apis/air_quality" } libtock_alarm = { path = "apis/alarm" } +libtock_ambient_light = { path = "apis/ambient_light" } libtock_buttons = { path = "apis/buttons" } +libtock_buzzer = {path = "apis/buzzer"} libtock_console = { path = "apis/console" } libtock_debug_panic = { path = "panic_handlers/debug_panic" } libtock_gpio = { path = "apis/gpio" } libtock_leds = { path = "apis/leds" } libtock_low_level_debug = { path = "apis/low_level_debug" } +libtock_ninedof = { path = "apis/ninedof" } libtock_platform = { path = "platform" } libtock_proximity = { path = "apis/proximity" } libtock_runtime = { path = "runtime" } +libtock_sound_pressure = {path = "apis/sound_pressure"} libtock_temperature = { path = "apis/temperature" } [profile.dev] @@ -36,15 +41,19 @@ debug = true [workspace] exclude = ["tock"] members = [ + "apis/adc", "apis/air_quality", "apis/alarm", "apis/gpio", "apis/buttons", + "apis/buzzer", "apis/console", "apis/leds", "apis/low_level_debug", + "apis/ninedof", "apis/proximity", "apis/temperature", + "apis/ambient_light", "panic_handlers/debug_panic", "panic_handlers/small_panic", "platform", diff --git a/apis/adc/Cargo.toml b/apis/adc/Cargo.toml new file mode 100644 index 00000000..eb650054 --- /dev/null +++ b/apis/adc/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "libtock_adc" +version = "0.1.0" +authors = ["Tock Project Developers "] +license = "MIT/Apache-2.0" +edition = "2021" +repository = "https://www.github.com/tock/libtock-rs" +description = "libtock adc driver" + +[dependencies] +libtock_platform = { path = "../../platform" } + +[dev-dependencies] +libtock_unittest = { path = "../../unittest" } diff --git a/apis/adc/src/lib.rs b/apis/adc/src/lib.rs new file mode 100644 index 00000000..31c6f153 --- /dev/null +++ b/apis/adc/src/lib.rs @@ -0,0 +1,93 @@ +#![no_std] + +use core::cell::Cell; +use libtock_platform::{ + share, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, +}; + +pub struct Adc(S); + +impl Adc { + /// Returns Ok() if the driver was present.This does not necessarily mean + /// that the driver is working. + pub fn exists() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() + } + + // Initiate a sample reading + pub fn read_single_sample() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 0).to_result() + } + + // Register a listener to be called when the ADC conversion is finished + pub fn register_listener<'share, F: Fn(u16)>( + listener: &'share ADCListener, + subscribe: share::Handle>, + ) -> Result<(), ErrorCode> { + S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) + } + + /// Unregister the events listener + pub fn unregister_listener() { + S::unsubscribe(DRIVER_NUM, 0) + } + + /// Initiates a synchronous ADC conversion + /// Returns the converted ADC value or an error + pub fn read_single_sample_sync() -> Result { + let sample: Cell> = Cell::new(None); + let listener = ADCListener(|adc_val| { + sample.set(Some(adc_val)); + }); + share::scope(|subscribe| { + Self::register_listener(&listener, subscribe)?; + Self::read_single_sample()?; + while sample.get() == None { + S::yield_wait(); + } + + match sample.get() { + None => Err(ErrorCode::Busy), + Some(adc_val) => Ok(adc_val), + } + }) + } + + /// Returns the number of ADC resolution bits + pub fn get_resolution_bits() -> Result { + S::command(DRIVER_NUM, GET_RES_BITS, 0, 0).to_result() + } + + /// Returns the reference voltage in millivolts (mV) + pub fn get_reference_voltage_mv() -> Result { + S::command(DRIVER_NUM, GET_VOLTAGE_REF, 0, 0).to_result() + } +} + +pub struct ADCListener(pub F); + +impl Upcall> for ADCListener { + fn upcall(&self, adc_val: u32, _arg1: u32, _arg2: u32) { + self.0(adc_val as u16) + } +} + +#[cfg(test)] +mod tests; + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x5; + +// Command IDs + +const EXISTS: u32 = 0; +const SINGLE_SAMPLE: u32 = 1; +// const REPEAT_SINGLE_SAMPLE: u32 = 2; +// const MULTIPLE_SAMPLE: u32 = 3; +// const CONTINUOUS_BUFF_SAMPLE: u32 = 4; +// const STOP_SAMPLE: u32 = 5; +const GET_RES_BITS: u32 = 101; +const GET_VOLTAGE_REF: u32 = 102; diff --git a/apis/adc/src/tests.rs b/apis/adc/src/tests.rs new file mode 100644 index 00000000..2947568e --- /dev/null +++ b/apis/adc/src/tests.rs @@ -0,0 +1,71 @@ +use core::cell::Cell; +use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; +use libtock_unittest::fake; + +type Adc = super::Adc; + +#[test] +fn no_driver() { + let _kernel = fake::Kernel::new(); + assert_eq!(Adc::exists(), Err(ErrorCode::NoDevice)); +} + +#[test] +fn driver_check() { + let kernel = fake::Kernel::new(); + let driver = fake::Adc::new(); + kernel.add_driver(&driver); + + assert_eq!(Adc::exists(), Ok(())); +} + +#[test] +fn read_single_sample() { + let kernel = fake::Kernel::new(); + let driver = fake::Adc::new(); + kernel.add_driver(&driver); + + assert_eq!(Adc::read_single_sample(), Ok(())); + assert!(driver.is_busy()); + + assert_eq!(Adc::read_single_sample(), Err(ErrorCode::Busy)); + assert_eq!(Adc::read_single_sample_sync(), Err(ErrorCode::Busy)); +} + +#[test] +fn register_unregister_listener() { + let kernel = fake::Kernel::new(); + let driver = fake::Adc::new(); + kernel.add_driver(&driver); + + let sample: Cell> = Cell::new(None); + let listener = crate::ADCListener(|adc_val| { + sample.set(Some(adc_val)); + }); + share::scope(|subscribe| { + assert_eq!(Adc::read_single_sample(), Ok(())); + driver.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert_eq!(Adc::register_listener(&listener, subscribe), Ok(())); + assert_eq!(Adc::read_single_sample(), Ok(())); + driver.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(sample.get(), Some(100)); + + Adc::unregister_listener(); + assert_eq!(Adc::read_single_sample(), Ok(())); + driver.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + }); +} + +#[test] +fn read_single_sample_sync() { + let kernel = fake::Kernel::new(); + let driver = fake::Adc::new(); + kernel.add_driver(&driver); + + driver.set_value_sync(1000); + assert_eq!(Adc::read_single_sample_sync(), Ok(1000)); +} diff --git a/apis/ambient_light/Cargo.toml b/apis/ambient_light/Cargo.toml new file mode 100644 index 00000000..b38d750c --- /dev/null +++ b/apis/ambient_light/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "libtock_ambient_light" +version = "0.1.0" +authors = ["Tock Project Developers "] +license = "MIT/Apache-2.0" +edition = "2021" +repository = "https://www.github.com/tock/libtock-rs" +description = "libtock ambient light driver" + +[dependencies] +libtock_platform = { path = "../../platform" } + +[dev-dependencies] +libtock_unittest = { path = "../../unittest" } diff --git a/apis/ambient_light/src/lib.rs b/apis/ambient_light/src/lib.rs new file mode 100644 index 00000000..0f8de578 --- /dev/null +++ b/apis/ambient_light/src/lib.rs @@ -0,0 +1,87 @@ +#![no_std] + +use core::cell::Cell; +use libtock_platform::{ + share, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, +}; + +pub struct AmbientLight(S); + +impl AmbientLight { + /// Returns Ok() if the driver was present.This does not necessarily mean + /// that the driver is working. + pub fn exists() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() + } + + /// Initiate a light intensity reading. + pub fn read_intensity() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, READ_INTENSITY, 0, 0).to_result() + } + + /// Register an events listener + pub fn register_listener<'share, F: Fn(u32)>( + listener: &'share IntensityListener, + subscribe: share::Handle>, + ) -> Result<(), ErrorCode> { + S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) + } + + /// Unregister the events listener + pub fn unregister_listener() { + S::unsubscribe(DRIVER_NUM, 0) + } + + /// Initiate a synchronous light intensity measurement. + /// Returns Ok(intensity_value) if the operation was successful + /// intensity_value is returned in lux + pub fn read_intensity_sync() -> Result { + let intensity_cell: Cell> = Cell::new(None); + let listener = IntensityListener(|intensity_val| { + intensity_cell.set(Some(intensity_val)); + }); + + share::scope(|subscribe| { + Self::register_listener(&listener, subscribe)?; + Self::read_intensity()?; + while intensity_cell.get() == None { + S::yield_wait(); + } + + match intensity_cell.get() { + None => Err(ErrorCode::Busy), + Some(intensity_val) => Ok(intensity_val), + } + }) + } +} + +/// A wrapper around a closure to be registered and called when +/// a luminance reading is done. +/// +/// ```ignore +/// let listener = IntensityListener(|intensity_val| { +/// // make use of the intensity value +/// }); +/// ``` +pub struct IntensityListener(pub F); + +impl Upcall> for IntensityListener { + fn upcall(&self, intensity: u32, _arg1: u32, _arg2: u32) { + self.0(intensity) + } +} + +#[cfg(test)] +mod tests; + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x60002; + +// Command IDs + +const EXISTS: u32 = 0; +const READ_INTENSITY: u32 = 1; diff --git a/apis/ambient_light/src/tests.rs b/apis/ambient_light/src/tests.rs new file mode 100644 index 00000000..9d5e267e --- /dev/null +++ b/apis/ambient_light/src/tests.rs @@ -0,0 +1,76 @@ +use core::cell::Cell; +use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; +use libtock_unittest::fake; + +use crate::IntensityListener; + +type AmbientLight = super::AmbientLight; + +#[test] +fn no_driver() { + let _kernel = fake::Kernel::new(); + assert_eq!(AmbientLight::exists(), Err(ErrorCode::NoDevice)); +} + +#[test] +fn driver_check() { + let kernel = fake::Kernel::new(); + let driver = fake::AmbientLight::new(); + kernel.add_driver(&driver); + + assert_eq!(AmbientLight::exists(), Ok(())); +} + +#[test] +fn read_temperature() { + let kernel = fake::Kernel::new(); + let driver = fake::AmbientLight::new(); + kernel.add_driver(&driver); + + assert_eq!(AmbientLight::read_intensity(), Ok(())); + assert!(driver.is_busy()); + + assert_eq!(AmbientLight::read_intensity(), Err(ErrorCode::Busy)); + assert_eq!(AmbientLight::read_intensity_sync(), Err(ErrorCode::Busy)); +} + +#[test] +fn register_unregister_listener() { + let kernel = fake::Kernel::new(); + let driver = fake::AmbientLight::new(); + kernel.add_driver(&driver); + + let intensity_cell: Cell> = Cell::new(None); + let listener = IntensityListener(|val| { + intensity_cell.set(Some(val)); + }); + share::scope(|subscribe| { + assert_eq!(AmbientLight::read_intensity(), Ok(())); + driver.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert_eq!( + AmbientLight::register_listener(&listener, subscribe), + Ok(()) + ); + assert_eq!(AmbientLight::read_intensity(), Ok(())); + driver.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(intensity_cell.get(), Some(100)); + + AmbientLight::unregister_listener(); + assert_eq!(AmbientLight::read_intensity(), Ok(())); + driver.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + }); +} + +#[test] +fn read_temperature_sync() { + let kernel = fake::Kernel::new(); + let driver = fake::AmbientLight::new(); + kernel.add_driver(&driver); + + driver.set_value_sync(1000); + assert_eq!(AmbientLight::read_intensity_sync(), Ok(1000)); +} diff --git a/apis/buzzer/Cargo.toml b/apis/buzzer/Cargo.toml new file mode 100644 index 00000000..09f460da --- /dev/null +++ b/apis/buzzer/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "libtock_buzzer" +version = "0.1.0" +authors = ["Tock Project Developers "] +license = "MIT/Apache-2.0" +edition = "2021" +repository = "https://www.github.com/tock/libtock-rs" +description = "libtock buzzer driver" + +[dependencies] +libtock_platform = { path = "../../platform" } + +[dev-dependencies] +libtock_unittest = { path = "../../unittest" } diff --git a/apis/buzzer/src/lib.rs b/apis/buzzer/src/lib.rs new file mode 100644 index 00000000..a540b789 --- /dev/null +++ b/apis/buzzer/src/lib.rs @@ -0,0 +1,173 @@ +//! Implementation started by : https://github.com/teodorobert +//! Continued and modified by : https://github.com/SheepSeb +#![no_std] + +use core::cell::Cell; +use core::time::Duration; + +use libtock_platform::{ + share, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, +}; +pub struct Buzzer(S); + +impl Buzzer { + /// Returns Ok() if the driver was present.This does not necessarily mean + /// that the driver is working. + pub fn exists() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() + } + + /// Initiate a tone + pub fn tone(freq: u32, duration: Duration) -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, BUZZER_ON, freq, duration.as_millis() as u32).to_result() + } + + /// Register an events listener + pub fn register_listener<'share, F: Fn(u32)>( + listener: &'share BuzzerListener, + subscribe: share::Handle>, + ) -> Result<(), ErrorCode> { + S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) + } + + /// Unregister the events listener + pub fn unregister_listener() { + S::unsubscribe(DRIVER_NUM, 0) + } + + /// Initiate a synchronous tone + /// Returns Ok() if the operation was successful + pub fn tone_sync(freq: u32, duration: Duration) -> Result<(), ErrorCode> { + let buzzer_cell: Cell> = Cell::new(None); + let listener = BuzzerListener(|buzzer_val| { + buzzer_cell.set(Some(buzzer_val)); + }); + share::scope(|subscribe| { + Self::register_listener(&listener, subscribe)?; + Self::tone(freq, duration)?; + while buzzer_cell.get() == None { + S::yield_wait(); + } + match buzzer_cell.get() { + None => Err(ErrorCode::Fail), + Some(_) => Ok(()), + } + }) + } +} + +pub struct BuzzerListener(pub F); +impl Upcall> for BuzzerListener { + fn upcall(&self, _arg0: u32, _arg1: u32, _arg2: u32) { + (self.0)(_arg0); + } +} + +#[cfg(test)] +mod tests; + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x90000; + +// Command IDs +const EXISTS: u32 = 0; +const BUZZER_ON: u32 = 1; + +/// The notes that can be played by the buzzer +#[allow(unused)] +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum Note { + B0 = 31, + C1 = 33, + CS1 = 35, + D1 = 37, + DS1 = 39, + E1 = 41, + F1 = 44, + FS1 = 46, + G1 = 49, + GS1 = 52, + A1 = 55, + AS1 = 58, + B1 = 62, + C2 = 65, + CS2 = 69, + D2 = 73, + DS2 = 78, + E2 = 82, + F2 = 87, + FS2 = 93, + G2 = 98, + GS2 = 104, + A2 = 110, + AS2 = 117, + B2 = 123, + C3 = 131, + CS3 = 139, + D3 = 147, + DS3 = 156, + E3 = 165, + F3 = 175, + FS3 = 185, + G3 = 196, + GS3 = 208, + A3 = 220, + AS3 = 233, + B3 = 247, + C4 = 262, + CS4 = 277, + D4 = 294, + DS4 = 311, + E4 = 330, + F4 = 349, + FS4 = 370, + G4 = 392, + GS4 = 415, + A4 = 440, + AS4 = 466, + B4 = 494, + C5 = 523, + CS5 = 554, + D5 = 587, + DS5 = 622, + E5 = 659, + F5 = 698, + FS5 = 740, + G5 = 784, + GS5 = 831, + A5 = 880, + AS5 = 932, + B5 = 988, + C6 = 1047, + CS6 = 1109, + D6 = 1175, + DS6 = 1245, + E6 = 1319, + F6 = 1397, + FS6 = 1480, + G6 = 1568, + GS6 = 1661, + A6 = 1760, + AS6 = 1865, + B6 = 1976, + C7 = 2093, + CS7 = 2217, + D7 = 2349, + DS7 = 2489, + E7 = 2637, + F7 = 2794, + FS7 = 2960, + G7 = 3136, + GS7 = 3322, + A7 = 3520, + AS7 = 3729, + B7 = 3951, + C8 = 4186, + CS8 = 4435, + D8 = 4699, + DS8 = 4978, +} diff --git a/apis/buzzer/src/tests.rs b/apis/buzzer/src/tests.rs new file mode 100644 index 00000000..176b9782 --- /dev/null +++ b/apis/buzzer/src/tests.rs @@ -0,0 +1,44 @@ +use core::time::Duration; +use libtock_platform::ErrorCode; +use libtock_unittest::fake; + +type Buzzer = super::Buzzer; + +#[test] +fn no_driver() { + let _kernel = fake::Kernel::new(); + assert_eq!(Buzzer::exists(), Err(ErrorCode::NoDevice)); +} + +#[test] +fn driver_check() { + let kernel = fake::Kernel::new(); + let driver = fake::Buzzer::new(); + kernel.add_driver(&driver); + + assert_eq!(Buzzer::exists(), Ok(())); +} + +#[test] +fn tone() { + let kernel = fake::Kernel::new(); + let driver = fake::Buzzer::new(); + kernel.add_driver(&driver); + let duration = Duration::from_millis(100); + assert_eq!(Buzzer::tone(1000, duration), Ok(())); + assert!(driver.is_busy()); + + assert_eq!(Buzzer::tone(1000, duration), Err(ErrorCode::Busy)); +} + +#[test] +fn tone_sync() { + let kernel = fake::Kernel::new(); + let driver = fake::Buzzer::new(); + kernel.add_driver(&driver); + + let duration = Duration::from_millis(100); + + driver.set_tone_sync(1000, 100); + assert_eq!(Buzzer::tone_sync(1000, duration), Ok(())); +} diff --git a/apis/ninedof/Cargo.toml b/apis/ninedof/Cargo.toml new file mode 100644 index 00000000..66bb2721 --- /dev/null +++ b/apis/ninedof/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "libtock_ninedof" +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 nine degrees of freedom driver" + +[dependencies] +libtock_platform = { path = "../../platform" } +libm = "0.2.7" + +[dev-dependencies] +libtock_unittest = { path = "../../unittest" } diff --git a/apis/ninedof/src/lib.rs b/apis/ninedof/src/lib.rs new file mode 100644 index 00000000..75380fb9 --- /dev/null +++ b/apis/ninedof/src/lib.rs @@ -0,0 +1,161 @@ +#![no_std] + +use core::cell::Cell; +use libtock_platform::{ + share, share::Handle, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, +}; + +pub struct NineDof(S); + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct NineDofData { + pub x: i32, + pub y: i32, + pub z: i32, +} + +impl NineDof { + /// Returns Ok() if the driver was present.This does not necessarily mean + /// that the driver is working. + pub fn exists() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() + } + + /// Initiate a accelerometer measurement. + /// This function is used both for synchronous and asynchronous readings + pub fn read_accelerometer() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, READ_ACCELEROMETER, 0, 0).to_result() + } + + /// Initiate a magnetometer measurement. + /// This function is used both for synchronous and asynchronous readings + pub fn read_magnetometer() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, READ_MAGNETOMETER, 0, 0).to_result() + } + + /// Initiate a gyroscope measurement. + /// This function is used both for synchronous and asynchronous readings + pub fn read_gyro() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, READ_GYRO, 0, 0).to_result() + } + + /// Unregister an events listener + pub fn unregister_listener() { + S::unsubscribe(DRIVER_NUM, 0) + } + + /// Register an events listener + pub fn register_listener<'share, F: Fn(NineDofData)>( + listener: &'share NineDofListener, + subscribe: Handle>, + ) -> Result<(), ErrorCode> { + S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) + } + + /// Initiate a synchronous accelerometer measurement. + /// Returns Ok(accelerometer_value) if the operation was successful + /// Returns Err(ErrorCode) if the operation was unsuccessful + pub fn read_accelerometer_sync() -> Result { + let data_cell: Cell> = Cell::new(None); + let listener = NineDofListener(|data| { + data_cell.set(Some(data)); + }); + share::scope(|subscribe| { + Self::register_listener(&listener, subscribe)?; + Self::read_accelerometer()?; + while data_cell.get() == None { + S::yield_wait(); + } + match data_cell.get() { + None => Err(ErrorCode::Fail), + Some(data) => Ok(data), + } + }) + } + + /// Initiate a synchronous magnetometer measurement. + /// Returns Ok(data) if the operation was successful + /// Returns Err(ErrorCode) if the operation was unsuccessful + pub fn read_magnetometer_sync() -> Result { + let data_cell: Cell> = Cell::new(None); + let listener = NineDofListener(|data| { + data_cell.set(Some(data)); + }); + share::scope(|subscribe| { + Self::register_listener(&listener, subscribe)?; + Self::read_magnetometer()?; + while data_cell.get() == None { + S::yield_wait(); + } + match data_cell.get() { + None => Err(ErrorCode::Fail), + Some(data) => Ok(data), + } + }) + } + + /// Initiate a synchronous gyroscope measurement. + /// Returns Ok(data) as NineDofData if the operation was successful + /// Returns Err(ErrorCode) if the operation was unsuccessful + pub fn read_gyroscope_sync() -> Result { + let data_cell: Cell> = Cell::new(None); + let listener = NineDofListener(|data| { + data_cell.set(Some(data)); + }); + share::scope(|subscribe| { + Self::register_listener(&listener, subscribe)?; + Self::read_gyro()?; + while data_cell.get() == None { + S::yield_wait(); + } + match data_cell.get() { + None => Err(ErrorCode::Fail), + Some(data) => Ok(data), + } + }) + } + + /// Calculate the magnitude of the accelerometer reading + /// Returns value of magnitude if the operation was successful + /// Returns 0.0 if the operation was unsuccessful + pub fn read_accelerometer_mag() -> f64 { + let data = Self::read_accelerometer_sync(); + + match data { + Ok(data) => { + let x = data.x as f64; + let y = data.y as f64; + let z = data.z as f64; + libm::sqrt(x * x + y * y + z * z) + } + Err(_) => 0.0, + } + } +} + +pub struct NineDofListener(pub F); + +impl Upcall> for NineDofListener { + fn upcall(&self, arg0: u32, arg1: u32, arg2: u32) { + (self.0)(NineDofData { + x: arg0 as i32, + y: arg1 as i32, + z: arg2 as i32, + }) + } +} + +#[cfg(test)] +mod tests; + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x60004; + +// Command IDs +const EXISTS: u32 = 0; +const READ_ACCELEROMETER: u32 = 1; +const READ_MAGNETOMETER: u32 = 100; +const READ_GYRO: u32 = 200; diff --git a/apis/ninedof/src/tests.rs b/apis/ninedof/src/tests.rs new file mode 100644 index 00000000..65f3d4a0 --- /dev/null +++ b/apis/ninedof/src/tests.rs @@ -0,0 +1,191 @@ +use core::cell::Cell; +use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; +use libtock_unittest::fake; + +use crate::NineDofData; + +type NineDof = super::NineDof; + +#[test] +fn no_driver() { + let _kernel = fake::Kernel::new(); + assert_eq!(NineDof::exists(), Err(ErrorCode::NoDevice)); +} + +#[test] +fn driver_check() { + let kernel = fake::Kernel::new(); + let driver = fake::NineDof::new(); + kernel.add_driver(&driver); + + assert_eq!(NineDof::exists(), Ok(())); +} + +#[test] +fn driver_busy() { + let kernel = fake::Kernel::new(); + let driver = fake::NineDof::new(); + kernel.add_driver(&driver); + + assert_eq!(NineDof::read_accelerometer(), Ok(())); + assert!(driver.is_busy()); + + assert_eq!(NineDof::read_accelerometer(), Err(ErrorCode::Busy)); + assert_eq!(NineDof::read_accelerometer_sync(), Err(ErrorCode::Busy)); +} + +#[test] +fn read_accelerometer() { + let kernel = fake::Kernel::new(); + let driver = fake::NineDof::new(); + kernel.add_driver(&driver); + + let acceleration_listener: Cell> = Cell::new(None); + let acceleration_listener = crate::NineDofListener(|data| { + acceleration_listener.set(Some(data)); + }); + + share::scope(|subscribe| { + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!( + NineDof::register_listener(&acceleration_listener, subscribe), + Ok(()) + ); + assert_eq!(NineDof::read_accelerometer(), Ok(())); + driver.set_value(fake::NineDofData { x: 1, y: 2, z: 3 }); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + }); +} + +#[test] +fn read_magnetometer() { + let kernel = fake::Kernel::new(); + let driver = fake::NineDof::new(); + kernel.add_driver(&driver); + + let magnetometer_listener: Cell> = Cell::new(None); + let magnetometer_listener = crate::NineDofListener(|data| { + magnetometer_listener.set(Some(data)); + }); + + share::scope(|subscribe| { + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!( + NineDof::register_listener(&magnetometer_listener, subscribe), + Ok(()) + ); + assert_eq!(NineDof::read_accelerometer(), Ok(())); + driver.set_value(fake::NineDofData { x: 1, y: 2, z: 3 }); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + }); +} + +#[test] +fn read_gyro() { + let kernel = fake::Kernel::new(); + let driver = fake::NineDof::new(); + kernel.add_driver(&driver); + + let gyro_listener: Cell> = Cell::new(None); + let gyro_listener = crate::NineDofListener(|data| { + gyro_listener.set(Some(data)); + }); + + share::scope(|subscribe| { + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!( + NineDof::register_listener(&gyro_listener, subscribe), + Ok(()) + ); + assert_eq!(NineDof::read_accelerometer(), Ok(())); + driver.set_value(fake::NineDofData { x: 1, y: 2, z: 3 }); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + }); +} + +#[test] +fn register_unregister_listener() { + let kernel = fake::Kernel::new(); + let driver = fake::NineDof::new(); + kernel.add_driver(&driver); + + let acceleration_listener: Cell> = Cell::new(None); + let acceleration_listener = crate::NineDofListener(|data| { + acceleration_listener.set(Some(data)); + }); + let magnetometer_listener: Cell> = Cell::new(None); + let magnetometer_listener = crate::NineDofListener(|data| { + magnetometer_listener.set(Some(data)); + }); + let gyro_listener: Cell> = Cell::new(None); + let gyro_listener = crate::NineDofListener(|data| { + gyro_listener.set(Some(data)); + }); + + share::scope(|subscribe| { + assert_eq!(NineDof::read_accelerometer(), Ok(())); + driver.set_value(fake::NineDofData { x: 1, y: 2, z: 3 }); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert_eq!( + NineDof::register_listener(&acceleration_listener, subscribe), + Ok(()) + ); + + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!(NineDof::read_gyro(), Ok(())); + driver.set_value(fake::NineDofData { x: 4, y: 5, z: 6 }); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + + assert_eq!( + NineDof::register_listener(&gyro_listener, subscribe), + Ok(()) + ); + + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!(NineDof::read_magnetometer(), Ok(())); + driver.set_value(fake::NineDofData { x: 7, y: 8, z: 9 }); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + assert_eq!( + NineDof::register_listener(&magnetometer_listener, subscribe), + Ok(()) + ); + }) +} + +#[test] +fn read_accelerometer_sync() { + let kernel = fake::Kernel::new(); + let driver = fake::NineDof::new(); + kernel.add_driver(&driver); + + driver.set_value_sync(fake::NineDofData { x: 1, y: 2, z: 3 }); + let data = NineDof::read_accelerometer_sync(); + assert_eq!(data, Ok(NineDofData { x: 1, y: 2, z: 3 })); +} + +#[test] +fn read_magnetometer_sync() { + let kernel = fake::Kernel::new(); + let driver = fake::NineDof::new(); + kernel.add_driver(&driver); + + driver.set_value_sync(fake::NineDofData { x: 1, y: 2, z: 3 }); + + let data = NineDof::read_magnetometer_sync(); + + assert_eq!(data, Ok(NineDofData { x: 1, y: 2, z: 3 })); +} + +#[test] +fn read_gyro_sync() { + let kernel = fake::Kernel::new(); + let driver = fake::NineDof::new(); + kernel.add_driver(&driver); + + driver.set_value_sync(fake::NineDofData { x: 1, y: 2, z: 3 }); + let value = NineDof::read_gyroscope_sync(); + assert_eq!(value, Ok(NineDofData { x: 1, y: 2, z: 3 })); +} diff --git a/apis/sound_pressure/Cargo.toml b/apis/sound_pressure/Cargo.toml new file mode 100644 index 00000000..b714f5d3 --- /dev/null +++ b/apis/sound_pressure/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "libtock_sound_pressure" +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 sound pressure driver" + +[dependencies] +libtock_platform = { path = "../../platform" } + +[dev-dependencies] +libtock_unittest = { path = "../../unittest" } diff --git a/apis/sound_pressure/src/lib.rs b/apis/sound_pressure/src/lib.rs new file mode 100644 index 00000000..4d3934a2 --- /dev/null +++ b/apis/sound_pressure/src/lib.rs @@ -0,0 +1,93 @@ +#![no_std] + +use core::cell::Cell; +use libtock_platform::{ + share, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, +}; + +pub struct SoundPressure(S); + +impl SoundPressure { + /// Returns Ok() if the driver was present.This does not necessarily mean + /// that the driver is working. + pub fn exists() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() + } + + /// Initiate a pressure measurement. + /// This function is used both for synchronous and asynchronous readings + pub fn read() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, READ_PRESSURE, 0, 0).to_result() + } + + /// Register an events listener + pub fn register_listener<'share, F: Fn(u32)>( + listener: &'share SoundPressureListener, + subscribe: share::Handle>, + ) -> Result<(), ErrorCode> { + S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) + } + + /// Unregister the events listener + pub fn unregister_listener() { + S::unsubscribe(DRIVER_NUM, 0) + } + + /// Enable sound pressure measurement + pub fn enable() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, 2, 0, 0).to_result() + } + + /// Disable sound pressure measurement + pub fn disable() -> Result<(), ErrorCode> { + S::command(DRIVER_NUM, 3, 0, 0).to_result() + } + + /// Initiate a synchronous pressure measurement. + /// Returns Ok(pressure_value) if the operation was successful + /// pressure_value is between 0 and 255 + pub fn read_sync() -> Result { + let pressure_cell: Cell> = Cell::new(None); + let listener = SoundPressureListener(|pressure_val| { + pressure_cell.set(Some(pressure_val)); + }); + share::scope(|subscribe| { + Self::register_listener(&listener, subscribe)?; + Self::read()?; + while pressure_cell.get() == None { + S::yield_wait(); + } + match pressure_cell.get() { + None => Err(ErrorCode::Fail), + Some(pressure_val) => { + if !(0..=256).contains(&pressure_val) { + Err(ErrorCode::Invalid) + } else { + Ok(pressure_val as u8) + } + } + } + }) + } +} + +pub struct SoundPressureListener(pub F); +impl Upcall> for SoundPressureListener { + fn upcall(&self, pressure_val: u32, _arg1: u32, _arg2: u32) { + (self.0)(pressure_val); + } +} + +#[cfg(test)] +mod tests; + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x60006; + +// Command IDs + +const EXISTS: u32 = 0; +const READ_PRESSURE: u32 = 1; diff --git a/apis/sound_pressure/src/tests.rs b/apis/sound_pressure/src/tests.rs new file mode 100644 index 00000000..f44e6fbb --- /dev/null +++ b/apis/sound_pressure/src/tests.rs @@ -0,0 +1,75 @@ +use core::cell::Cell; +use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; +use libtock_unittest::fake; + +type SoundPressure = super::SoundPressure; + +#[test] +fn no_driver() { + let _kernel = fake::Kernel::new(); + assert_eq!(SoundPressure::exists(), Err(ErrorCode::NoDevice)); +} + +#[test] +fn driver_check() { + let kernel = fake::Kernel::new(); + let driver = fake::SoundPressure::new(); + kernel.add_driver(&driver); + + assert_eq!(SoundPressure::exists(), Ok(())); +} + +#[test] +fn driver_busy() { + let kernel = fake::Kernel::new(); + let driver = fake::SoundPressure::new(); + kernel.add_driver(&driver); + + assert_eq!(SoundPressure::read(), Ok(())); + assert!(driver.is_busy()); + + assert_eq!(SoundPressure::read(), Err(ErrorCode::Busy)); + assert_eq!(SoundPressure::read_sync(), Err(ErrorCode::Busy)); +} + +#[test] +fn read_pressure() { + let kernel = fake::Kernel::new(); + let driver = fake::SoundPressure::new(); + kernel.add_driver(&driver); + + let pressure_cell: Cell> = Cell::new(None); + let listener = crate::SoundPressureListener(|pressure_val| { + pressure_cell.set(Some(pressure_val)); + }); + + share::scope(|subscribe| { + assert_eq!(SoundPressure::read(), Ok(())); + driver.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert_eq!( + SoundPressure::register_listener(&listener, subscribe), + Ok(()) + ); + assert_eq!(SoundPressure::read(), Ok(())); + driver.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(pressure_cell.get(), Some(100)); + + SoundPressure::unregister_listener(); + assert_eq!(SoundPressure::read(), Ok(())); + driver.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + }); +} + +#[test] +fn read_pressure_sync() { + let kernel = fake::Kernel::new(); + let driver = fake::SoundPressure::new(); + kernel.add_driver(&driver); + + driver.set_value_sync(100); + assert_eq!(SoundPressure::read_sync(), Ok(100)); +} diff --git a/examples/adc.rs b/examples/adc.rs new file mode 100644 index 00000000..954120c1 --- /dev/null +++ b/examples/adc.rs @@ -0,0 +1,31 @@ +//! A simple libtock-rs example. Checks for adc driver +//! and samples the sensor every 2 seconds. + +#![no_main] +#![no_std] + +use core::fmt::Write; +use libtock::console::Console; + +use libtock::adc::Adc; +use libtock::alarm::{Alarm, Milliseconds}; +use libtock::runtime::{set_main, stack_size}; + +set_main! {main} +stack_size! {0x200} + +fn main() { + if Adc::exists().is_err() { + writeln!(Console::writer(), "adc driver unavailable").unwrap(); + return; + } + + loop { + match Adc::read_single_sample_sync() { + Ok(adc_val) => writeln!(Console::writer(), "Sample: {}\n", adc_val).unwrap(), + Err(_) => writeln!(Console::writer(), "error while reading sample",).unwrap(), + } + + Alarm::sleep_for(Milliseconds(2000)).unwrap(); + } +} diff --git a/examples/ambient_light.rs b/examples/ambient_light.rs new file mode 100644 index 00000000..14a7e8b3 --- /dev/null +++ b/examples/ambient_light.rs @@ -0,0 +1,36 @@ +//! A simple libtock-rs example. Checks for ambient light driver +//! and samples the sensor every 2 seconds. + +#![no_main] +#![no_std] + +use core::fmt::Write; +use libtock::console::Console; + +use libtock::alarm::{Alarm, Milliseconds}; +use libtock::ambient_light::AmbientLight; +use libtock::runtime::{set_main, stack_size}; + +set_main! {main} +stack_size! {0x200} + +fn main() { + if AmbientLight::exists().is_err() { + writeln!(Console::writer(), "ambient light driver unavailable").unwrap(); + return; + } + + loop { + match AmbientLight::read_intensity_sync() { + Ok(intensity_val) => writeln!( + Console::writer(), + "Light intensity: {} lux\n", + intensity_val + ) + .unwrap(), + Err(_) => writeln!(Console::writer(), "error while reading light intensity",).unwrap(), + } + + Alarm::sleep_for(Milliseconds(2000)).unwrap(); + } +} diff --git a/examples/music.rs b/examples/music.rs new file mode 100644 index 00000000..5a854911 --- /dev/null +++ b/examples/music.rs @@ -0,0 +1,105 @@ +//! Implementation done by : https://github.com/teodorobert +//! A simple libtock-rs example. Plays Ode of Joy using the buzzer. +#![no_main] +#![no_std] + +use core::fmt::Write; +use core::time::Duration; +use libtock::buzzer::{Buzzer, Note}; +use libtock::console::Console; +use libtock::runtime::{set_main, stack_size}; + +set_main! {main} +stack_size! {0x800} + +// Adapted from https://github.com/robsoncouto/arduino-songs + +// Notes in the form of (note_frequency, note_delay in musical terms) +const MELODY: [(Note, i32); 62] = [ + (Note::E4, 4), + (Note::E4, 4), + (Note::F4, 4), + (Note::G4, 4), + (Note::G4, 4), + (Note::F4, 4), + (Note::E4, 4), + (Note::D4, 4), + (Note::C4, 4), + (Note::C4, 4), + (Note::D4, 4), + (Note::E4, 4), + (Note::E4, -4), + (Note::D4, 8), + (Note::D4, 2), + (Note::E4, 4), + (Note::E4, 4), + (Note::F4, 4), + (Note::G4, 4), + (Note::G4, 4), + (Note::F4, 4), + (Note::E4, 4), + (Note::D4, 4), + (Note::C4, 4), + (Note::C4, 4), + (Note::D4, 4), + (Note::E4, 4), + (Note::D4, -4), + (Note::C4, 8), + (Note::C4, 2), + (Note::D4, 4), + (Note::D4, 4), + (Note::E4, 4), + (Note::C4, 4), + (Note::D4, 4), + (Note::E4, 8), + (Note::F4, 8), + (Note::E4, 4), + (Note::C4, 4), + (Note::D4, 4), + (Note::E4, 8), + (Note::F4, 8), + (Note::E4, 4), + (Note::D4, 4), + (Note::C4, 4), + (Note::D4, 4), + (Note::G3, 2), + (Note::E4, 4), + (Note::E4, 4), + (Note::F4, 4), + (Note::G4, 4), + (Note::G4, 4), + (Note::F4, 4), + (Note::E4, 4), + (Note::D4, 4), + (Note::C4, 4), + (Note::C4, 4), + (Note::D4, 4), + (Note::E4, 4), + (Note::D4, -4), + (Note::C4, 8), + (Note::C4, 2), +]; + +const TEMPO: u32 = 114; +const WHOLE_NOTE: u32 = (60000 * 4) / TEMPO; + +fn main() { + if let Err(_) = Buzzer::exists() { + writeln!(Console::writer(), "There is no available buzzer").unwrap(); + return; + } + + writeln!(Console::writer(), "Ode to Joy").unwrap(); + + for (frequency, duration) in MELODY.iter() { + let mut note_duration: Duration = + Duration::from_millis((WHOLE_NOTE / duration.unsigned_abs()) as u64); + // let mut note_duration = WHOLE_NOTE / duration.unsigned_abs(); + if duration < &0 { + note_duration = note_duration * 15 / 10; + } + + let note_duration = note_duration * 9 / 10; + Buzzer::tone_sync(*frequency as u32 * 3, note_duration).unwrap(); + } +} diff --git a/examples/ninedof.rs b/examples/ninedof.rs new file mode 100644 index 00000000..81bbbe47 --- /dev/null +++ b/examples/ninedof.rs @@ -0,0 +1,71 @@ +//! Libtock-rs example for the ninedof sensor. + +#![no_main] +#![no_std] + +use core::fmt::Write; +use libtock::console::Console; + +use libtock::alarm::{Alarm, Milliseconds}; +use libtock::ninedof::NineDof; +use libtock::runtime::{set_main, stack_size}; + +set_main! {main} +stack_size! {0x2000} + +fn main() { + if NineDof::exists().is_err() { + writeln!(Console::writer(), "NineDof driver unavailable").unwrap(); + return; + } + + writeln!(Console::writer(), "NineDof driver available").unwrap(); + loop { + let accelerometer_data = NineDof::read_accelerometer_sync(); + let magnetomer_data = NineDof::read_magnetometer_sync(); + let gyroscope_data = NineDof::read_gyroscope_sync(); + + match accelerometer_data { + Ok(data) => { + writeln!( + Console::writer(), + "Accelerometer: x: {}, y: {}, z: {}", + data.x, + data.y, + data.z + ) + .unwrap(); + } + Err(_) => writeln!(Console::writer(), "error while reading accelerometer").unwrap(), + } + + match magnetomer_data { + Ok(data) => { + writeln!( + Console::writer(), + "Magnetometer: x: {}, y: {}, z: {}", + data.x, + data.y, + data.z + ) + .unwrap(); + } + Err(_) => writeln!(Console::writer(), "error while reading magnetometer").unwrap(), + } + + match gyroscope_data { + Ok(data) => { + writeln!( + Console::writer(), + "Gyroscope: x: {}, y: {}, z: {}", + data.x, + data.y, + data.z + ) + .unwrap(); + } + Err(_) => writeln!(Console::writer(), "error while reading gyroscope").unwrap(), + } + Alarm::sleep_for(Milliseconds(700)).unwrap(); + } +} diff --git a/examples/sound_pressure.rs b/examples/sound_pressure.rs new file mode 100644 index 00000000..ba49e660 --- /dev/null +++ b/examples/sound_pressure.rs @@ -0,0 +1,45 @@ +//! This example shows how to use the sound pressure driver. +//! It checks for the sound pressure driver and samples the sensor every second. + +#![no_main] +#![no_std] + +use core::fmt::Write; +use libtock::console::Console; + +use libtock::alarm::{Alarm, Milliseconds}; +use libtock::runtime::{set_main, stack_size}; +use libtock::sound_pressure::SoundPressure; + +set_main! {main} +stack_size! {0x200} + +fn main() { + if SoundPressure::exists().is_err() { + writeln!(Console::writer(), "Sound pressure driver not found").unwrap(); + return; + } + + writeln!(Console::writer(), "Sound pressure driver found").unwrap(); + let enable = SoundPressure::enable(); + match enable { + Ok(()) => { + writeln!(Console::writer(), "Sound pressure driver enabled").unwrap(); + loop { + match SoundPressure::read_sync() { + Ok(sound_pressure_val) => writeln!( + Console::writer(), + "Sound Pressure: {}\n", + sound_pressure_val + ) + .unwrap(), + Err(_) => { + writeln!(Console::writer(), "error while reading sound pressure",).unwrap() + } + } + Alarm::sleep_for(Milliseconds(1000)).unwrap(); + } + } + Err(_e) => writeln!(Console::writer(), "Sound pressure driver enable failed",).unwrap(), + } +} diff --git a/src/lib.rs b/src/lib.rs index 70553cee..5d6f32e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,12 @@ extern crate libtock_debug_panic; pub use libtock_platform as platform; pub use libtock_runtime as runtime; +pub mod adc { + use libtock_adc as adc; + pub type Adc = adc::Adc; + pub use adc::ADCListener; +} + pub mod air_quality { use libtock_air_quality as air_quality; pub type AirQuality = air_quality::AirQuality; @@ -17,11 +23,21 @@ pub mod alarm { pub type Alarm = alarm::Alarm; pub use alarm::{Convert, Hz, Milliseconds, Ticks}; } +pub mod ambient_light { + use libtock_ambient_light as ambient_light; + pub type AmbientLight = ambient_light::AmbientLight; + pub use ambient_light::IntensityListener; +} pub mod buttons { use libtock_buttons as buttons; pub type Buttons = buttons::Buttons; pub use buttons::{ButtonListener, ButtonState}; } +pub mod buzzer { + use libtock_buzzer as buzzer; + pub type Buzzer = buzzer::Buzzer; + pub use buzzer::Note; +} pub mod console { use libtock_console as console; pub type Console = console::Console; @@ -44,10 +60,19 @@ pub mod low_level_debug { pub type LowLevelDebug = lldb::LowLevelDebug; pub use lldb::AlertCode; } +pub mod ninedof { + use libtock_ninedof as ninedof; + pub type NineDof = ninedof::NineDof; + pub use ninedof::NineDofListener; +} pub mod proximity { use libtock_proximity as proximity; pub type Proximity = proximity::Proximity; } +pub mod sound_pressure { + use libtock_sound_pressure as sound_pressure; + pub type SoundPressure = sound_pressure::SoundPressure; +} pub mod temperature { use libtock_temperature as temperature; pub type Temperature = temperature::Temperature; diff --git a/unittest/src/fake/adc/mod.rs b/unittest/src/fake/adc/mod.rs new file mode 100644 index 00000000..461576d3 --- /dev/null +++ b/unittest/src/fake/adc/mod.rs @@ -0,0 +1,84 @@ +//! Fake implementation of the Adc API, documented here: +//! +//! Like the real API, `Adc` controls a fake Adc sensor. It provides +//! a function `set_value` used to immediately call an upcall with a Adc value read by the sensor +//! and a function 'set_value_sync' used to call the upcall when the read command is received. + +use crate::{DriverInfo, DriverShareRef}; +use libtock_platform::{CommandReturn, ErrorCode}; +use std::cell::Cell; + +// The `upcall_on_command` field is set to Some(value) if an upcall(with value as its argument) should be called when read command is received, +// or None otherwise. It was needed for testing `read_sync` library function which simulates a synchronous Adc read, +// because it was impossible to schedule an upcall during the `synchronous` read in other ways. +pub struct Adc { + busy: Cell, + upcall_on_command: Cell>, + share_ref: DriverShareRef, +} + +impl Adc { + pub fn new() -> std::rc::Rc { + std::rc::Rc::new(Adc { + busy: Cell::new(false), + upcall_on_command: Cell::new(None), + share_ref: Default::default(), + }) + } + + pub fn is_busy(&self) -> bool { + self.busy.get() + } + pub fn set_value(&self, value: i32) { + if self.busy.get() { + self.share_ref + .schedule_upcall(0, (value as u32, 0, 0)) + .expect("Unable to schedule upcall"); + self.busy.set(false); + } + } + pub fn set_value_sync(&self, value: i32) { + self.upcall_on_command.set(Some(value)); + } +} + +impl crate::fake::SyscallDriver for Adc { + fn info(&self) -> DriverInfo { + DriverInfo::new(DRIVER_NUM).upcall_count(1) + } + + fn register(&self, share_ref: DriverShareRef) { + self.share_ref.replace(share_ref); + } + + fn command(&self, command_id: u32, _argument0: u32, _argument1: u32) -> CommandReturn { + match command_id { + EXISTS => crate::command_return::success(), + + SINGLE_SAMPLE => { + if self.busy.get() { + return crate::command_return::failure(ErrorCode::Busy); + } + self.busy.set(true); + if let Some(val) = self.upcall_on_command.take() { + self.set_value(val); + } + crate::command_return::success() + } + _ => crate::command_return::failure(ErrorCode::NoSupport), + } + } +} + +#[cfg(test)] +mod tests; +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x5; + +// Command IDs + +const EXISTS: u32 = 0; +const SINGLE_SAMPLE: u32 = 1; diff --git a/unittest/src/fake/adc/tests.rs b/unittest/src/fake/adc/tests.rs new file mode 100644 index 00000000..8b2b47f5 --- /dev/null +++ b/unittest/src/fake/adc/tests.rs @@ -0,0 +1,67 @@ +use crate::fake::{self, SyscallDriver}; +use fake::adc::*; +use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; + +//Test the command implementation +#[test] +fn command() { + let adc = Adc::new(); + + assert!(adc.command(EXISTS, 1, 2).is_success()); + + assert!(adc.command(SINGLE_SAMPLE, 0, 0).is_success()); + + assert_eq!( + adc.command(SINGLE_SAMPLE, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + + adc.set_value(100); + assert!(adc.command(SINGLE_SAMPLE, 0, 1).is_success()); + adc.set_value(100); + + adc.set_value_sync(100); + assert!(adc.command(SINGLE_SAMPLE, 0, 1).is_success()); + assert!(adc.command(SINGLE_SAMPLE, 0, 1).is_success()); +} + +// Integration test that verifies Adc works with fake::Kernel and +// libtock_platform::Syscalls. +#[test] +fn kernel_integration() { + use libtock_platform::Syscalls; + let kernel = fake::Kernel::new(); + let adc = Adc::new(); + kernel.add_driver(&adc); + assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 0).is_success()); + assert_eq!( + fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + adc.set_value(100); + assert!(fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 1).is_success()); + + let listener = Cell::>::new(None); + share::scope(|subscribe| { + assert_eq!( + fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), + Ok(()) + ); + + adc.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(listener.get(), Some((100,))); + + adc.set_value(200); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert!(fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 1).is_success()); + adc.set_value(200); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + + adc.set_value_sync(200); + assert!(fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 1).is_success()); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + }); +} diff --git a/unittest/src/fake/ambient_light/mod.rs b/unittest/src/fake/ambient_light/mod.rs new file mode 100644 index 00000000..f412a0b4 --- /dev/null +++ b/unittest/src/fake/ambient_light/mod.rs @@ -0,0 +1,85 @@ +//! Fake implementation of the Ambient Light API, documented here: +//! https://github.com/tock/tock/blob/master/doc/syscalls/60002_luminance.md +//! +//! Like the real API, `AmbientLight` controls a fake ambient light sensor. It provides +//! a function `set_value` used to immediately call an upcall with a intensity value read by the sensor +//! and a function 'set_value_sync' used to call the upcall when the read command is received. + +use crate::{DriverInfo, DriverShareRef}; +use libtock_platform::{CommandReturn, ErrorCode}; +use std::cell::Cell; + +// The `upcall_on_command` field is set to Some(value) if an upcall(with value as its argument) should be called when read command is received, +// or None otherwise. It was needed for testing `read_sync` library function which simulates a synchronous temperature read, +// because it was impossible to schedule an upcall during the `synchronous` read in other ways. +pub struct AmbientLight { + busy: Cell, + upcall_on_command: Cell>, + share_ref: DriverShareRef, +} + +impl AmbientLight { + pub fn new() -> std::rc::Rc { + std::rc::Rc::new(AmbientLight { + busy: Cell::new(false), + upcall_on_command: Cell::new(None), + share_ref: Default::default(), + }) + } + + pub fn is_busy(&self) -> bool { + self.busy.get() + } + pub fn set_value(&self, value: u32) { + if self.busy.get() { + self.share_ref + .schedule_upcall(0, (value as u32, 0, 0)) + .expect("Unable to schedule upcall"); + self.busy.set(false); + } + } + pub fn set_value_sync(&self, value: u32) { + self.upcall_on_command.set(Some(value)); + } +} + +impl crate::fake::SyscallDriver for AmbientLight { + fn info(&self) -> DriverInfo { + DriverInfo::new(DRIVER_NUM).upcall_count(1) + } + + fn register(&self, share_ref: DriverShareRef) { + self.share_ref.replace(share_ref); + } + + fn command(&self, command_id: u32, _argument0: u32, _argument1: u32) -> CommandReturn { + match command_id { + EXISTS => crate::command_return::success(), + + READ_INTENSITY => { + if self.busy.get() { + return crate::command_return::failure(ErrorCode::Busy); + } + self.busy.set(true); + if let Some(val) = self.upcall_on_command.take() { + self.set_value(val); + } + crate::command_return::success() + } + _ => crate::command_return::failure(ErrorCode::NoSupport), + } + } +} + +#[cfg(test)] +mod tests; +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x60002; + +// Command IDs + +const EXISTS: u32 = 0; +const READ_INTENSITY: u32 = 1; diff --git a/unittest/src/fake/ambient_light/tests.rs b/unittest/src/fake/ambient_light/tests.rs new file mode 100644 index 00000000..5a961da6 --- /dev/null +++ b/unittest/src/fake/ambient_light/tests.rs @@ -0,0 +1,67 @@ +use crate::fake::{self, SyscallDriver}; +use fake::ambient_light::*; +use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; + +//Test the command implementation +#[test] +fn command() { + let amb = AmbientLight::new(); + + assert!(amb.command(EXISTS, 1, 2).is_success()); + + assert!(amb.command(READ_INTENSITY, 0, 0).is_success()); + + assert_eq!( + amb.command(READ_INTENSITY, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + + amb.set_value(100); + assert!(amb.command(READ_INTENSITY, 0, 1).is_success()); + amb.set_value(100); + + amb.set_value_sync(100); + assert!(amb.command(READ_INTENSITY, 0, 1).is_success()); + assert!(amb.command(READ_INTENSITY, 0, 1).is_success()); +} + +// Integration test that verifies AmbientLight works with fake::Kernel and +// libtock_platform::Syscalls. +#[test] +fn kernel_integration() { + use libtock_platform::Syscalls; + let kernel = fake::Kernel::new(); + let ambient_light = AmbientLight::new(); + kernel.add_driver(&ambient_light); + assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 0).is_success()); + assert_eq!( + fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + ambient_light.set_value(100); + assert!(fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 1).is_success()); + + let listener = Cell::>::new(None); + share::scope(|subscribe| { + assert_eq!( + fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), + Ok(()) + ); + + ambient_light.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(listener.get(), Some((100,))); + + ambient_light.set_value(200); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert!(fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 1).is_success()); + ambient_light.set_value(200); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + + ambient_light.set_value_sync(200); + assert!(fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 1).is_success()); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + }); +} diff --git a/unittest/src/fake/buzzer/mod.rs b/unittest/src/fake/buzzer/mod.rs new file mode 100644 index 00000000..a8551256 --- /dev/null +++ b/unittest/src/fake/buzzer/mod.rs @@ -0,0 +1,89 @@ +//! Fake implementation of the Buzzer API, documented here: +//! +//! Like the real API, `Buzzer` controls a fake buzzer. It provides +//! a function `set_tone` used to immediately call an upcall with a tone set by the buzzer +//! and a function 'set_tone_sync' used to call the upcall when the tone command is received. + +use crate::{DriverInfo, DriverShareRef}; +use core::time::Duration; +use libtock_platform::{CommandReturn, ErrorCode}; +use std::cell::Cell; + +// The `upcall_on_command` field is set to Some(value) if an upcall(with value as its argument) should be called when tone command is received, +// or None otherwise. It was needed for testing `tone_sync` library function which simulates a synchronous tone set, +// because it was impossible to schedule an upcall during the `synchronous` tone set in other ways. +pub struct Buzzer { + busy: Cell, + upcall_on_command: [Cell>; 2], + share_ref: DriverShareRef, +} + +impl Buzzer { + pub fn new() -> std::rc::Rc { + std::rc::Rc::new(Buzzer { + busy: Cell::new(false), + upcall_on_command: [Cell::new(None), Cell::new(None)], + share_ref: Default::default(), + }) + } + + pub fn is_busy(&self) -> bool { + self.busy.get() + } + + pub fn set_tone(&self, freq: i32, duration: Duration) { + if self.busy.get() { + self.share_ref + .schedule_upcall(0, (freq as u32, duration.as_millis() as u32, 0)) + .expect("Unable to schedule upcall"); + self.busy.set(false); + } + } + + pub fn set_tone_sync(&self, freq: i32, duration: i32) { + self.upcall_on_command[0].set(Some(freq)); + self.upcall_on_command[1].set(Some(duration)); + } +} + +impl crate::fake::SyscallDriver for Buzzer { + fn info(&self) -> DriverInfo { + DriverInfo::new(DRIVER_NUM).upcall_count(1) + } + + fn register(&self, share_ref: DriverShareRef) { + self.share_ref.replace(share_ref); + } + + fn command(&self, command_num: u32, _argument0: u32, _argument1: u32) -> CommandReturn { + match command_num { + EXISTS => crate::command_return::success(), + TONE => { + if self.busy.get() { + return crate::command_return::failure(ErrorCode::Busy); + } + self.busy.set(true); + if let Some(freq) = self.upcall_on_command[0].take() { + if let Some(duration) = self.upcall_on_command[1].take() { + self.set_tone(freq, Duration::from_millis(duration as u64)); + } + } + crate::command_return::success() + } + _ => crate::command_return::failure(ErrorCode::NoSupport), + } + } +} + +#[cfg(test)] +mod tests; + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x90000; + +// Command IDs +const EXISTS: u32 = 0; +const TONE: u32 = 1; diff --git a/unittest/src/fake/buzzer/tests.rs b/unittest/src/fake/buzzer/tests.rs new file mode 100644 index 00000000..b556ff1f --- /dev/null +++ b/unittest/src/fake/buzzer/tests.rs @@ -0,0 +1,60 @@ +use crate::fake::{self, SyscallDriver}; +use fake::buzzer::*; +use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; + +#[test] +fn command() { + let buzzer = Buzzer::new(); + assert!(buzzer.command(EXISTS, 1, 2).is_success()); + assert!(buzzer.command(TONE, 0, 0).is_success()); + + assert_eq!( + buzzer.command(TONE, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + + buzzer.set_tone(100, Duration::from_millis(100)); + assert!(buzzer.command(TONE, 0, 1).is_success()); + buzzer.set_tone(100, Duration::from_millis(100)); + + buzzer.set_tone_sync(100, 100); + assert!(buzzer.command(TONE, 0, 1).is_success()); + assert!(buzzer.command(TONE, 0, 1).is_success()); +} + +#[test] +fn kernel_integration() { + use libtock_platform::Syscalls; + let kernel = fake::Kernel::new(); + let buzzer = Buzzer::new(); + kernel.add_driver(&buzzer); + + assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, TONE, 0, 0).is_success()); + assert_eq!( + fake::Syscalls::command(DRIVER_NUM, TONE, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + buzzer.set_tone(100, Duration::from_millis(100)); + assert!(fake::Syscalls::command(DRIVER_NUM, TONE, 0, 1).is_success()); + + let listener = Cell::>::new(None); + share::scope(|subscribe| { + assert_eq!( + fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), + Ok(()) + ); + + buzzer.set_tone(100, Duration::from_millis(100)); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(listener.get(), Some((100,))); + + buzzer.set_tone(200, Duration::from_millis(100)); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert!(fake::Syscalls::command(DRIVER_NUM, TONE, 0, 1).is_success()); + buzzer.set_tone(200, Duration::from_millis(100)); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(listener.get(), Some((200,))); + }); +} diff --git a/unittest/src/fake/mod.rs b/unittest/src/fake/mod.rs index f4d029f5..72d3f5b8 100644 --- a/unittest/src/fake/mod.rs +++ b/unittest/src/fake/mod.rs @@ -9,28 +9,38 @@ //! `use libtock_unittest::fake` and refer to the type with the `fake::` prefix //! (e.g. `fake::Console`). +mod adc; mod air_quality; mod alarm; +mod ambient_light; mod buttons; +mod buzzer; mod console; mod gpio; mod kernel; mod leds; mod low_level_debug; +mod ninedof; mod proximity; +mod sound_pressure; mod syscall_driver; mod syscalls; mod temperature; +pub use adc::Adc; pub use air_quality::AirQuality; pub use alarm::Alarm; +pub use ambient_light::AmbientLight; pub use buttons::Buttons; +pub use buzzer::Buzzer; pub use console::Console; pub use gpio::{Gpio, GpioMode, InterruptEdge, PullMode}; pub use kernel::Kernel; pub use leds::Leds; pub use low_level_debug::{LowLevelDebug, Message}; +pub use ninedof::{NineDof, NineDofData}; pub use proximity::Proximity; +pub use sound_pressure::SoundPressure; pub use syscall_driver::SyscallDriver; pub use syscalls::Syscalls; pub use temperature::Temperature; diff --git a/unittest/src/fake/ninedof/mod.rs b/unittest/src/fake/ninedof/mod.rs new file mode 100644 index 00000000..dc304847 --- /dev/null +++ b/unittest/src/fake/ninedof/mod.rs @@ -0,0 +1,109 @@ +//! Fake implementation of the NineDof API, documented here: +//! +//! Like the real API, `NineDof` controls a fake 9dof sensor. It provides +//! a function `set_value` used to immediately call an upcall with a 9dof value read by the sensor +//! and a function 'set_value_sync' used to call the upcall when the read command is received. + +use crate::{DriverInfo, DriverShareRef}; +use libtock_platform::{CommandReturn, ErrorCode}; +use std::cell::Cell; + +pub struct NineDof { + busy: Cell, + upcall_on_command: Cell>, + share_ref: DriverShareRef, +} +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct NineDofData { + pub x: i32, + pub y: i32, + pub z: i32, +} + +impl NineDof { + pub fn new() -> std::rc::Rc { + std::rc::Rc::new(NineDof { + busy: Cell::new(false), + upcall_on_command: Cell::new(None), + share_ref: Default::default(), + }) + } + + pub fn is_busy(&self) -> bool { + self.busy.get() + } + pub fn set_value(&self, value: NineDofData) { + if self.busy.get() { + self.share_ref + .schedule_upcall(0, (value.x as u32, value.y as u32, value.z as u32)) + .expect("Unable to schedule upcall"); + self.busy.set(false); + } + } + pub fn set_value_sync(&self, value: NineDofData) { + self.upcall_on_command.set(Some(value)); + } +} + +impl crate::fake::SyscallDriver for NineDof { + fn info(&self) -> DriverInfo { + DriverInfo::new(DRIVER_NUM).upcall_count(1) + } + + fn register(&self, share_ref: DriverShareRef) { + self.share_ref.replace(share_ref); + } + + fn command(&self, command_id: u32, _argument0: u32, _argument1: u32) -> CommandReturn { + match command_id { + EXISTS => crate::command_return::success(), + + READ_ACCELEROMETER => { + if self.busy.get() { + return crate::command_return::failure(ErrorCode::Busy); + } + self.busy.set(true); + if let Some(val) = self.upcall_on_command.take() { + self.set_value(val); + } + crate::command_return::success() + } + READ_MAGNETOMETER => { + if self.busy.get() { + return crate::command_return::failure(ErrorCode::Busy); + } + self.busy.set(true); + if let Some(val) = self.upcall_on_command.take() { + self.set_value(val); + } + crate::command_return::success() + } + READ_GYRO => { + if self.busy.get() { + return crate::command_return::failure(ErrorCode::Busy); + } + self.busy.set(true); + if let Some(val) = self.upcall_on_command.take() { + self.set_value(val); + } + crate::command_return::success() + } + _ => crate::command_return::failure(ErrorCode::NoSupport), + } + } +} + +#[cfg(test)] +mod tests; + +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x60004; + +// Command IDs +const EXISTS: u32 = 0; +const READ_ACCELEROMETER: u32 = 1; +const READ_MAGNETOMETER: u32 = 100; +const READ_GYRO: u32 = 200; diff --git a/unittest/src/fake/ninedof/tests.rs b/unittest/src/fake/ninedof/tests.rs new file mode 100644 index 00000000..0a2ce90b --- /dev/null +++ b/unittest/src/fake/ninedof/tests.rs @@ -0,0 +1,69 @@ +use crate::fake::{self, SyscallDriver}; +use fake::ninedof::*; +use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; + +//Test the command implementation +#[test] +fn command() { + let ninedof = NineDof::new(); + + assert!(ninedof.command(EXISTS, 1, 2).is_success()); + + assert!(ninedof.command(READ_ACCELEROMETER, 0, 0).is_success()); + + assert_eq!( + ninedof.command(READ_ACCELEROMETER, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + + let payload: NineDofData = NineDofData { x: 1, y: 2, z: 3 }; + + ninedof.set_value(payload); + assert!(ninedof.command(READ_ACCELEROMETER, 0, 1).is_success()); + ninedof.set_value(payload); + + ninedof.set_value_sync(payload); + assert!(ninedof.command(READ_ACCELEROMETER, 0, 1).is_success()); + assert!(ninedof.command(READ_ACCELEROMETER, 0, 1).is_success()); +} + +// Integration test that verifies NineDof works with fake::Kernel and +// libtock_platform::Syscalls. +#[test] +fn kernel_integration() { + use libtock_platform::Syscalls; + let kernel = fake::Kernel::new(); + let ninedof = NineDof::new(); + kernel.add_driver(&ninedof); + assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, READ_ACCELEROMETER, 0, 0).is_success()); + assert_eq!( + fake::Syscalls::command(DRIVER_NUM, READ_ACCELEROMETER, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + + let payload: NineDofData = NineDofData { x: 1, y: 2, z: 3 }; + + ninedof.set_value(payload); + assert!(fake::Syscalls::command(DRIVER_NUM, READ_ACCELEROMETER, 0, 1).is_success()); + + let listener = Cell::>::new(None); + share::scope(|subscribe| { + assert_eq!( + fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), + Ok(()) + ); + + ninedof.set_value(payload); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(listener.get(), Some((1, 2, 3))); + + ninedof.set_value(payload); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert!(fake::Syscalls::command(DRIVER_NUM, READ_ACCELEROMETER, 0, 1).is_success()); + ninedof.set_value(payload); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(listener.get(), Some((1, 2, 3))); + }); +} diff --git a/unittest/src/fake/sound_pressure/mod.rs b/unittest/src/fake/sound_pressure/mod.rs new file mode 100644 index 00000000..84140ca6 --- /dev/null +++ b/unittest/src/fake/sound_pressure/mod.rs @@ -0,0 +1,84 @@ +//! Fake implementation of the SoundPressure API, documented here: +//! +//! Like the real API, `SoundPressure` controls a fake sound pressure sensor. It provides +//! a function `set_value` used to immediately call an upcall with a sound pressure value read by the sensor +//! and a function 'set_value_sync' used to call the upcall when the read command is received. +use crate::{DriverInfo, DriverShareRef}; +use libtock_platform::{CommandReturn, ErrorCode}; +use std::cell::Cell; + +// The `upcall_on_command` field is set to Some(value) if an upcall(with value as its argument) should be called when read command is received, +// or None otherwise. It was needed for testing `read_sync` library function which simulates a synchronous sound pressure read, +// because it was impossible to schedule an upcall during the `synchronous` read in other ways. +pub struct SoundPressure { + busy: Cell, + upcall_on_command: Cell>, + share_ref: DriverShareRef, +} + +impl SoundPressure { + pub fn new() -> std::rc::Rc { + std::rc::Rc::new(SoundPressure { + busy: Cell::new(false), + upcall_on_command: Cell::new(None), + share_ref: Default::default(), + }) + } + + pub fn is_busy(&self) -> bool { + self.busy.get() + } + + pub fn set_value(&self, value: u8) { + if self.busy.get() { + self.share_ref + .schedule_upcall(0, (value as u32, 0, 0)) + .expect("Unable to schedule upcall"); + self.busy.set(false); + } + } + + pub fn set_value_sync(&self, value: u8) { + self.upcall_on_command.set(Some(value)); + } +} + +impl crate::fake::SyscallDriver for SoundPressure { + fn info(&self) -> DriverInfo { + DriverInfo::new(DRIVER_NUM).upcall_count(1) + } + + fn register(&self, share_ref: DriverShareRef) { + self.share_ref.replace(share_ref); + } + + fn command(&self, command_id: u32, _argument0: u32, _argument1: u32) -> CommandReturn { + match command_id { + EXISTS => crate::command_return::success(), + + READ_PRESSURE => { + if self.busy.get() { + return crate::command_return::failure(ErrorCode::Busy); + } + self.busy.set(true); + if let Some(val) = self.upcall_on_command.take() { + self.set_value(val as u8); + } + crate::command_return::success() + } + _ => crate::command_return::failure(ErrorCode::NoSupport), + } + } +} + +#[cfg(test)] +mod tests; +// ----------------------------------------------------------------------------- +// Driver number and command IDs +// ----------------------------------------------------------------------------- + +const DRIVER_NUM: u32 = 0x60006; + +// Command IDs +const EXISTS: u32 = 0; +const READ_PRESSURE: u32 = 1; diff --git a/unittest/src/fake/sound_pressure/tests.rs b/unittest/src/fake/sound_pressure/tests.rs new file mode 100644 index 00000000..82bed032 --- /dev/null +++ b/unittest/src/fake/sound_pressure/tests.rs @@ -0,0 +1,64 @@ +use crate::fake::{self, SyscallDriver}; +use fake::sound_pressure::*; +use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; + +//Test the command implementation +#[test] +fn command() { + let sound_pressure = SoundPressure::new(); + + assert!(sound_pressure.command(EXISTS, 1, 2).is_success()); + + assert!(sound_pressure.command(READ_PRESSURE, 0, 0).is_success()); + + assert_eq!( + sound_pressure.command(READ_PRESSURE, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + + sound_pressure.set_value(100); + assert!(sound_pressure.command(READ_PRESSURE, 0, 1).is_success()); + sound_pressure.set_value(100); + + sound_pressure.set_value_sync(100); + assert!(sound_pressure.command(READ_PRESSURE, 0, 1).is_success()); + assert!(sound_pressure.command(READ_PRESSURE, 0, 1).is_success()); +} + +// Integration test that verifies SoundPressure works with fake::Kernel and +// libtock_platform::Syscalls. +#[test] +fn kernel_integration() { + use libtock_platform::Syscalls; + let kernel = fake::Kernel::new(); + let sound_pressure = SoundPressure::new(); + kernel.add_driver(&sound_pressure); + assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); + assert!(fake::Syscalls::command(DRIVER_NUM, READ_PRESSURE, 0, 0).is_success()); + assert_eq!( + fake::Syscalls::command(DRIVER_NUM, READ_PRESSURE, 0, 0).get_failure(), + Some(ErrorCode::Busy) + ); + sound_pressure.set_value(100); + assert!(fake::Syscalls::command(DRIVER_NUM, READ_PRESSURE, 0, 1).is_success()); + + let listener = Cell::>::new(None); + share::scope(|subscribe| { + assert_eq!( + fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), + Ok(()) + ); + + sound_pressure.set_value(100); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(listener.get(), Some((100,))); + + sound_pressure.set_value(200); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); + + assert!(fake::Syscalls::command(DRIVER_NUM, READ_PRESSURE, 0, 1).is_success()); + sound_pressure.set_value(200); + assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); + assert_eq!(listener.get(), Some((200,))); + }); +}