Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Sound Pressure API #469

Merged
merged 20 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ libtock_low_level_debug = { path = "apis/low_level_debug" }
libtock_platform = { path = "platform" }
libtock_proximity = { path = "apis/proximity" }
libtock_runtime = { path = "runtime" }
libtock_sound_pressure = {path = "apis/sound_pressure"}
jrvanwhy marked this conversation as resolved.
Show resolved Hide resolved
libtock_temperature = { path = "apis/temperature" }

[profile.dev]
Expand Down
14 changes: 14 additions & 0 deletions apis/sound_pressure/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "libtock_sound_pressure"
version = "0.1.0"
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
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" }
93 changes: 93 additions & 0 deletions apis/sound_pressure/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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: Syscalls>(S);

impl<S: Syscalls> SoundPressure<S> {
/// 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<F>,
subscribe: share::Handle<Subscribe<'share, S, DRIVER_NUM, 0>>,
) -> 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<u8, ErrorCode> {
let pressure_cell: Cell<Option<u32>> = 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<F: Fn(u32)>(pub F);
impl<F: Fn(u32)> Upcall<OneId<DRIVER_NUM, 0>> for SoundPressureListener<F> {
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;
75 changes: 75 additions & 0 deletions apis/sound_pressure/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use core::cell::Cell;
use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn};
use libtock_unittest::fake;

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

#[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<Option<u32>> = 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));
}
45 changes: 45 additions & 0 deletions examples/sound_pressure.rs
Original file line number Diff line number Diff line change
@@ -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(),
}
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ pub mod proximity {
use libtock_proximity as proximity;
pub type Proximity = proximity::Proximity<super::runtime::TockSyscalls>;
}
pub mod sound_pressure {
use libtock_sound_pressure as sound_pressure;
pub type SoundPressure = sound_pressure::SoundPressure<super::runtime::TockSyscalls>;
}
pub mod temperature {
use libtock_temperature as temperature;
pub type Temperature = temperature::Temperature<super::runtime::TockSyscalls>;
Expand Down
2 changes: 2 additions & 0 deletions unittest/src/fake/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod kernel;
mod leds;
mod low_level_debug;
mod proximity;
mod sound_pressure;
mod syscall_driver;
mod syscalls;
mod temperature;
Expand All @@ -29,6 +30,7 @@ pub use kernel::Kernel;
pub use leds::Leds;
pub use low_level_debug::{LowLevelDebug, Message};
pub use proximity::Proximity;
pub use sound_pressure::SoundPressure;
pub use syscall_driver::SyscallDriver;
pub use syscalls::Syscalls;
pub use temperature::Temperature;
Expand Down
84 changes: 84 additions & 0 deletions unittest/src/fake/sound_pressure/mod.rs
Original file line number Diff line number Diff line change
@@ -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<bool>,
upcall_on_command: Cell<Option<u8>>,
share_ref: DriverShareRef,
}

impl SoundPressure {
pub fn new() -> std::rc::Rc<SoundPressure> {
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;
Loading