Skip to content
Open
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
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ libtock_leds = { path = "apis/leds" }
libtock_low_level_debug = { path = "apis/low_level_debug" }
libtock_platform = { path = "platform" }
libtock_proximity = { path = "apis/proximity" }
libtock_rng = { path = "apis/rng" }
libtock_runtime = { path = "runtime" }
libtock_temperature = { path = "apis/temperature" }

Expand All @@ -42,6 +43,7 @@ members = [
"apis/leds",
"apis/low_level_debug",
"apis/proximity",
"apis/rng",
"apis/temperature",
"panic_handlers/debug_panic",
"panic_handlers/small_panic",
Expand Down
14 changes: 14 additions & 0 deletions apis/rng/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "libtock_rng"
version = "0.1.0"
authors = ["Tock Project Developers <[email protected]>"]
license = "MIT/Apache-2.0"
edition = "2021"
repository = "https://www.github.com/tock/libtock-rs"
description = "libtock rng driver"

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

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

use core::cell::Cell;

use libtock_platform::{share, AllowRw, DefaultConfig, ErrorCode, Subscribe, Syscalls};
pub struct Rng<S: Syscalls>(S);

impl<S: Syscalls> Rng<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()
}

/// Register a listener to be called when the random generation is finished
pub fn register_listener<'share>(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn register_listener<'share>(
pub fn register_listener<'share>(&'share mut buffer,
listener: &'share RandomDataListener,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to implement these changes but I don't know how could that unregister_listener function return the buffer (where would it get it from). I also don't understand how could I share the buffer back from the listener.

listener: &'share Cell<Option<(u32, u32)>>,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do that two u32 values from the listener mean?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first one is always 0, and the second one represents the number of random bytes successfuly written in the buffer.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to

https://github.com/DanutAldea/libtock-rs/blob/b70eb0cbe1766a37585472235e77f755a7c12786/apis/ambient_light/src/lib.rs#L67-L73

I suggest adding a RandomDataListener, as the user wants to get a function called when data is available.

pub struct RandomDataListener<share etc, F: Fn(&'share [u8])>(pub F);

impl<F: Fn(u32)> Upcall<OneId<DRIVER_NUM, 0>> for IntensityListener<F> {
    fn upcall(&self, _arg0: u32, len: u32, _arg2: u32) {
        // unshare buffer
        f(buffer[0..len]);
        // share buffer back
    }
}

Add an unregister_listener function that returns Result<&'share [u8], ErrorCode> to unshare the buffer earlier.

subscribe: share::Handle<Subscribe<'share, S, DRIVER_NUM, 0>>,
) -> Result<(), ErrorCode> {
S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener)
}

/// Sets the buffer in which the random numbers will be written
pub fn set_buffer<'share>(
buffer: &'share mut [u8],
allow_rw: share::Handle<AllowRw<'share, S, DRIVER_NUM, RW_ALLOW>>,
) -> Result<(), ErrorCode> {
S::allow_rw::<DefaultConfig, DRIVER_NUM, RW_ALLOW>(allow_rw, buffer)
}

/// Initiates an async random generation for n bytes
/// A buffer and a callback should have been set before
pub fn get_random(n: u32) -> Result<(), ErrorCode> {
S::command(DRIVER_NUM, ASK_FOR_RANDOM_BYTES, n, 0).to_result()
}

/// Initiates a synchronous random number generation
/// `n` random bytes will be written in `buf`
/// n must be smaller or equal to buf.len()
/// returns the number of bytes successfully written or error
pub fn get_random_sync(buf: &mut [u8], n: u32) -> Result<u32, ErrorCode> {
if n > (buf.len() as u32) {
return Err(ErrorCode::Size);
}

let listener: Cell<Option<(u32, u32)>> = Cell::new(None);
share::scope::<
(
AllowRw<_, DRIVER_NUM, RW_ALLOW>,
Subscribe<_, DRIVER_NUM, 0>,
),
_,
_,
>(|handle| {
let (allow_rw, subscribe) = handle.split();

Self::set_buffer(buf, allow_rw)?;
Self::register_listener(&listener, subscribe)?;
Self::get_random(n)?;
while listener.get() == None {
S::yield_wait();
}

Ok(())
})?;

match listener.get() {
Some((_, bytes_received)) => Ok(bytes_received),
None => Err(ErrorCode::Fail),
}
}
}

#[cfg(test)]
mod tests;

// -----------------------------------------------------------------------------
// Driver number and command IDs
// -----------------------------------------------------------------------------

const DRIVER_NUM: u32 = 0x40001;

// Command IDs

const EXISTS: u32 = 0;
const ASK_FOR_RANDOM_BYTES: u32 = 1;

const RW_ALLOW: u32 = 0;
80 changes: 80 additions & 0 deletions apis/rng/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use super::*;
use core::cell::Cell;
use libtock_platform::{share, AllowRw, ErrorCode, Subscribe, YieldNoWaitReturn};
use libtock_unittest::fake;

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

#[test]
fn no_driver() {
let _kernel = fake::Kernel::new();
assert_eq!(Rng::exists(), Err(ErrorCode::NoDevice));
}

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

assert_eq!(Rng::exists(), Ok(()));
}

#[test]
fn get_random() {
let kernel = fake::Kernel::new();
let driver = fake::Rng::new();
kernel.add_driver(&driver);

let mut buf: [u8; 10] = [0; 10];
let listener: Cell<Option<(u32, u32)>> = Cell::new(None);

share::scope::<
(
AllowRw<_, DRIVER_NUM, RW_ALLOW>,
Subscribe<_, DRIVER_NUM, 0>,
),
_,
_,
>(
|handle: share::Handle<(
AllowRw<fake::Syscalls, DRIVER_NUM, RW_ALLOW>,
Subscribe<fake::Syscalls, DRIVER_NUM, 0>,
)>| {
let (allow_rw, subscribe) = handle.split();

assert!(Rng::set_buffer(&mut buf, allow_rw).is_ok());
assert!(Rng::register_listener(&listener, subscribe).is_ok());

assert!(Rng::get_random(5).is_ok());

driver.add_bytes(&[1, 2, 3]);

assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall);

driver.add_bytes(&[4, 5, 6]);

assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall);

assert_eq!(listener.get(), Some((0, 5)));
},
);
assert!(buf[..5].eq(&[1, 2, 3, 4, 5]));
}
#[test]
fn get_sync() {
let kernel = fake::Kernel::new();
let driver = fake::Rng::new();
kernel.add_driver(&driver);

driver.add_bytes_sync(&[10, 20, 30, 40, 50]);

let mut buf: [u8; 10] = [0; 10];

assert_eq!(Rng::get_random_sync(&mut buf, 5), Ok(5));
assert!(buf[..5].eq(&[10, 20, 30, 40, 50]));

driver.add_bytes_sync(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);

assert_eq!(Rng::get_random_sync(&mut buf, 11), Err(ErrorCode::Size));
}
48 changes: 48 additions & 0 deletions examples/rng.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! A simple libtock-rs example. Checks for random number generator driver
//! asks for some random numbers and prints them.

#![no_main]
#![no_std]

use core::fmt::Write;
use libtock::console::Console;
use libtock::rng::Rng;
use libtock::runtime::{set_main, stack_size};
set_main! {main}
stack_size! {0x200}

fn main() {
if Rng::exists().is_err() {
writeln!(
Console::writer(),
"Random number generator driver unavailable"
)
.unwrap();
return;
}

writeln!(
Console::writer(),
"Random number generator driver available"
)
.unwrap();

let mut buf: [u8; 10] = [0; 10];

match Rng::get_random_sync(&mut buf[..], 10) {
Ok(bytes_received) => {
writeln!(
Console::writer(),
"Received {} random bytes. Buf:",
bytes_received,
)
.unwrap();
for byte in buf.iter() {
writeln!(Console::writer(), "{} ", byte).unwrap();
}
}
Err(_) => {
writeln!(Console::writer(), "FAIL").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 rng {
use libtock_rng as rng;
pub type Rng = rng::Rng<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 rng;
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 rng::Rng;
pub use syscall_driver::SyscallDriver;
pub use syscalls::Syscalls;
pub use temperature::Temperature;
Expand Down
119 changes: 119 additions & 0 deletions unittest/src/fake/rng/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use std::cell::{Cell, RefCell};

use crate::{DriverInfo, DriverShareRef, RwAllowBuffer};
use core::cmp;
use libtock_platform::ErrorCode;

pub struct Rng {
buffer: RefCell<RwAllowBuffer>,
remaining: Cell<usize>,
idx: Cell<usize>,
getting_randomness: Cell<bool>,
share_ref: DriverShareRef,
random_numbers: Cell<Option<Vec<u8>>>,
}

impl Rng {
pub fn new() -> std::rc::Rc<Rng> {
std::rc::Rc::new(Rng {
buffer: Default::default(),
remaining: Cell::new(0),
idx: Cell::new(0),
getting_randomness: Cell::new(false),
share_ref: Default::default(),
random_numbers: Cell::new(None),
})
}

pub fn add_bytes(&self, buf: &[u8]) {
if !self.getting_randomness.get() {
return;
}

// this would happen only if the buffer was changed
if self.idx.get() > self.buffer.borrow().len() {
self.idx.set(0);
self.remaining.set(0);
} else {
if self.idx.get() + self.remaining.get() > self.buffer.borrow().len() {
self.remaining
.set(self.buffer.borrow().len() - self.idx.get())
}

let bytes_to_add = cmp::min(self.remaining.get(), buf.len());
self.buffer.borrow_mut()[self.idx.get()..self.idx.get() + bytes_to_add]
.copy_from_slice(&buf[..bytes_to_add]);
self.remaining.set(self.remaining.get() - bytes_to_add);
self.idx.set(self.idx.get() + bytes_to_add);
}

if self.remaining.get() == 0 {
self.share_ref
.schedule_upcall(0, (0, self.idx.get() as u32, 0))
.expect("Unable to schedule upcall");
self.getting_randomness.set(false);
self.random_numbers.set(None);
}
}

pub fn add_bytes_sync(&self, buf: &[u8]) {
self.random_numbers.set(Some(Vec::from(buf)))
}
}

impl crate::fake::SyscallDriver for Rng {
fn info(&self) -> DriverInfo {
DriverInfo::new(DRIVER_NUM).upcall_count(1)
}

fn register(&self, share_ref: DriverShareRef) {
self.share_ref.replace(share_ref);
}

fn allow_readwrite(
&self,
buffer_num: u32,
buffer: RwAllowBuffer,
) -> Result<RwAllowBuffer, (RwAllowBuffer, libtock_platform::ErrorCode)> {
if buffer_num == RW_ALLOW {
Ok(self.buffer.replace(buffer))
} else {
Err((buffer, ErrorCode::Invalid))
}
}

fn command(&self, command_id: u32, data: u32, _: u32) -> libtock_platform::CommandReturn {
match command_id {
EXISTS => crate::command_return::success(),

ASK_FOR_RANDOM_BYTES => {
self.remaining.set(data as usize);
self.idx.set(0);
self.getting_randomness.set(true);

if let Some(numbers) = self.random_numbers.take() {
self.add_bytes(&numbers);
}

crate::command_return::success()
}
_ => crate::command_return::failure(ErrorCode::NoSupport),
}
}
}

#[cfg(test)]
mod tests;

// -----------------------------------------------------------------------------
// Driver number and command IDs
// -----------------------------------------------------------------------------

const DRIVER_NUM: u32 = 0x40001;

// Command IDs

const EXISTS: u32 = 0;
const ASK_FOR_RANDOM_BYTES: u32 = 1;

const RW_ALLOW: u32 = 0;
Loading