Skip to content

Commit a89873e

Browse files
synchronous api + example
1 parent 895fd55 commit a89873e

File tree

6 files changed

+233
-0
lines changed

6 files changed

+233
-0
lines changed

apis/rng/src/lib.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,45 @@
1+
#![no_std]
2+
13
#[cfg(test)]
24
mod tests;
5+
6+
use core::cell::Cell;
7+
use libtock_platform::{share, AllowRw, DefaultConfig, ErrorCode, Subscribe, Syscalls};
8+
9+
const DRIVER_NUM: u32 = 0x40001;
10+
const EXISTS: u32 = 0;
11+
const GET_BYTES: u32 = 1;
12+
13+
pub struct Rng<S: Syscalls>(S);
14+
15+
impl<S: Syscalls> Rng<S> {
16+
/// Check if the RNG kernel driver exists
17+
pub fn exists() -> Result<(), ErrorCode> {
18+
S::command(DRIVER_NUM, EXISTS, 0, 0).to_result()
19+
}
20+
21+
/// Ask to fill the provided `buf` with `n` random bytes.
22+
/// If `n > buf.len()`, it will simply fill the whole buffer.
23+
pub fn get_bytes_sync(buf: &mut [u8], n: u32) -> Result<(), ErrorCode> {
24+
let called = Cell::new(false);
25+
share::scope::<(AllowRw<S, DRIVER_NUM, 0>, Subscribe<S, DRIVER_NUM, 0>), _, _>(|handle| {
26+
let (allow_ro, subscribe) = handle.split();
27+
28+
// Share the provided buffer with the kernel
29+
S::allow_rw::<DefaultConfig, DRIVER_NUM, 0>(allow_ro, buf)?;
30+
31+
// Subscribe for an upcall with the kernel
32+
S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &called)?;
33+
34+
// Send the command to the kernel driver to fill the allowed_readwrite buffer
35+
S::command(DRIVER_NUM, GET_BYTES, n, 0).to_result()?;
36+
37+
// Wait for a callback to happen
38+
while !called.get() {
39+
S::yield_wait();
40+
}
41+
42+
Ok(())
43+
})
44+
}
45+
}

examples/rng.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
use core::fmt::Write;
5+
use libtock::alarm::Alarm;
6+
use libtock::console::Console;
7+
use libtock::rng::Rng;
8+
use libtock::runtime::{set_main, stack_size};
9+
use libtock_alarm::Milliseconds;
10+
11+
set_main! {main}
12+
stack_size! {0x300}
13+
14+
struct Randomness<'a, const N: usize>(&'a [u8; N]);
15+
16+
impl<'a, const N: usize> core::fmt::Display for Randomness<'a, N> {
17+
fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
18+
let mut console_writer = Console::writer();
19+
let mut bytes = self.0.iter();
20+
while let Some(&byte) = bytes.next() {
21+
write!(console_writer, "{byte:02x}")?;
22+
}
23+
Ok(())
24+
}
25+
}
26+
27+
fn main() {
28+
if let Err(e) = Rng::exists() {
29+
writeln!(
30+
Console::writer(),
31+
"RNG DRIVER DOESN'T EXIST\nERROR: {e:?}\n"
32+
)
33+
.unwrap();
34+
return;
35+
}
36+
37+
let mut buffer: [u8; 32] = Default::default();
38+
let n: u32 = 32;
39+
let mut console_writer = Console::writer();
40+
loop {
41+
match Rng::get_bytes_sync(&mut buffer, n) {
42+
Ok(()) => {
43+
write!(console_writer, "Randomness: {}\n", Randomness(&buffer)).unwrap();
44+
}
45+
Err(e) => writeln!(console_writer, "Error while getting bytes {e:?}\n").unwrap(),
46+
}
47+
Alarm::sleep_for(Milliseconds(2000)).unwrap();
48+
}
49+
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ pub mod proximity {
6969
use libtock_proximity as proximity;
7070
pub type Proximity = proximity::Proximity<super::runtime::TockSyscalls>;
7171
}
72+
pub mod rng {
73+
use libtock_rng as rng;
74+
pub type Rng = rng::Rng<super::runtime::TockSyscalls>;
75+
}
7276
pub mod sound_pressure {
7377
use libtock_sound_pressure as sound_pressure;
7478
pub type SoundPressure = sound_pressure::SoundPressure<super::runtime::TockSyscalls>;

unittest/src/fake/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod leds;
2222
mod low_level_debug;
2323
mod ninedof;
2424
mod proximity;
25+
mod rng;
2526
mod sound_pressure;
2627
mod syscall_driver;
2728
mod syscalls;
@@ -40,6 +41,7 @@ pub use leds::Leds;
4041
pub use low_level_debug::{LowLevelDebug, Message};
4142
pub use ninedof::{NineDof, NineDofData};
4243
pub use proximity::Proximity;
44+
pub use rng::Rng;
4345
pub use sound_pressure::SoundPressure;
4446
pub use syscall_driver::SyscallDriver;
4547
pub use syscalls::Syscalls;

unittest/src/fake/rng/mod.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use libtock_platform::ErrorCode;
2+
3+
use crate::{DriverInfo, DriverShareRef, RwAllowBuffer};
4+
use std::cell::{Cell, RefCell};
5+
use std::ops::DerefMut;
6+
7+
pub struct Rng {
8+
busy: Cell<bool>,
9+
share_ref: DriverShareRef,
10+
buffer: RefCell<RwAllowBuffer>,
11+
bytes: Cell<Vec<u8>>,
12+
}
13+
14+
impl Rng {
15+
pub fn new() -> std::rc::Rc<Rng> {
16+
std::rc::Rc::new(Rng {
17+
busy: Cell::new(false),
18+
share_ref: Default::default(),
19+
buffer: Default::default(),
20+
bytes: Default::default(),
21+
})
22+
}
23+
24+
pub fn is_busy(&self) -> bool {
25+
self.busy.get()
26+
}
27+
}
28+
29+
impl crate::fake::SyscallDriver for Rng {
30+
fn info(&self) -> crate::DriverInfo {
31+
DriverInfo::new(DRIVER_NUM).upcall_count(1)
32+
}
33+
34+
fn register(&self, share_ref: DriverShareRef) {
35+
self.share_ref.replace(share_ref);
36+
}
37+
38+
fn command(&self, command_id: u32, argument0: u32, _: u32) -> libtock_platform::CommandReturn {
39+
match command_id {
40+
EXISTS => crate::command_return::success(),
41+
GET_BYTES => {
42+
if self.busy.get() {
43+
return crate::command_return::failure(ErrorCode::Busy);
44+
}
45+
self.busy.set(true);
46+
// Mutably borrows RwAllowBuffer from the RefCell as to not take the buffer out
47+
let mut allow_rw_buffer = self.buffer.borrow_mut();
48+
let inner_buffer = (*allow_rw_buffer).deref_mut();
49+
inner_buffer.copy_from_slice(
50+
&self.bytes.take()[0..core::cmp::min(inner_buffer.len(), argument0 as usize)],
51+
);
52+
crate::command_return::success()
53+
}
54+
_ => crate::command_return::failure(ErrorCode::NoSupport),
55+
}
56+
}
57+
58+
fn allow_readwrite(
59+
&self,
60+
buffer_num: u32,
61+
buffer: crate::RwAllowBuffer,
62+
) -> Result<crate::RwAllowBuffer, (crate::RwAllowBuffer, libtock_platform::ErrorCode)> {
63+
if buffer_num == 0 {
64+
Ok(self.buffer.replace(buffer))
65+
} else {
66+
Err((buffer, ErrorCode::Invalid))
67+
}
68+
}
69+
}
70+
71+
#[cfg(test)]
72+
mod tests;
73+
74+
// -----------------
75+
// RNG DRIVER NUMBER
76+
// -----------------
77+
const DRIVER_NUM: u32 = 0x40001;
78+
79+
// -------------------
80+
// RNG COMMAND NUMBERS
81+
// -------------------
82+
const EXISTS: u32 = 0;
83+
const GET_BYTES: u32 = 1;

unittest/src/fake/rng/tests.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use libtock_platform::{share, DefaultConfig};
2+
3+
use crate::{fake, RwAllowBuffer};
4+
5+
// Tests the command implementation.
6+
#[test]
7+
fn command() {
8+
use fake::SyscallDriver;
9+
10+
let rng = fake::Rng::new();
11+
12+
// Test driver response to an existence check request
13+
assert!(rng.command(fake::rng::EXISTS, 0, 0).is_success());
14+
15+
// Test driver response for sharing a buffer
16+
// to a valid and invalid buffer index, respectively
17+
assert!(rng.allow_readwrite(0, RwAllowBuffer::default()).is_ok());
18+
assert!(rng.allow_readwrite(1, RwAllowBuffer::default()).is_err());
19+
}
20+
21+
// Integration test that verifies Console works with fake::Kernel and
22+
// libtock_platform::Syscalls.
23+
#[test]
24+
fn kernel_integration() {
25+
use libtock_platform::Syscalls;
26+
27+
let kernel = fake::Kernel::new();
28+
let rng = fake::Rng::new();
29+
// Predetermined sample of RNG data "read" from the RNG low-level driver
30+
// that will be put into the shared buffer
31+
let bytes = [0_u8, 1_u8, 2_u8, 3_u8, 4_u8];
32+
rng.bytes.set(bytes.to_vec());
33+
kernel.add_driver(&rng);
34+
35+
// Test driver response to an existence check request
36+
assert!(fake::Syscalls::command(fake::rng::DRIVER_NUM, 0, 0, 0).is_success());
37+
38+
// Buffer to be shared with the kernel and filled with random bytes
39+
let mut buffer: [u8; 5] = [0; 5];
40+
share::scope(|allow_rw| {
41+
// Register the provided buffer
42+
fake::Syscalls::allow_rw::<DefaultConfig, { fake::rng::DRIVER_NUM }, 0>(
43+
allow_rw,
44+
&mut buffer,
45+
)
46+
.unwrap();
47+
// Test driver response to buffer fill requests
48+
assert!(fake::Syscalls::command(fake::rng::DRIVER_NUM, 1, 5, 0).is_success());
49+
});
50+
// Additionally check that the buffer has been correctly filled
51+
assert_eq!(buffer.cmp(&bytes), core::cmp::Ordering::Equal);
52+
}

0 commit comments

Comments
 (0)