Update third_party/libtock-drivers to support OpenSK.
This commit is contained in:
71
third_party/libtock-drivers/Cargo.toml
vendored
71
third_party/libtock-drivers/Cargo.toml
vendored
@@ -1,67 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libtock"
|
name = "libtock_drivers"
|
||||||
version = "0.2.0"
|
version = "0.1.0"
|
||||||
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
|
authors = [
|
||||||
|
"Tock Project Developers <tock-dev@googlegroups.com>"
|
||||||
|
"Guillaume Endignoux <guillaumee@google.com>",
|
||||||
|
]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[features]
|
|
||||||
alloc = ["libtock_core/alloc"]
|
|
||||||
custom_panic_handler = ["libtock_core/custom_panic_handler"]
|
|
||||||
custom_alloc_error_handler = ["libtock_core/custom_alloc_error_handler"]
|
|
||||||
__internal_disable_gpio_in_integration_test = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libtock_core = { path = "core" }
|
libtock_core = { path = "../../third_party/libtock-rs/core" }
|
||||||
libtock_codegen = { path = "codegen" }
|
|
||||||
futures = { version = "0.3.1", default-features = false, features = ["unstable", "cfg-target-has-atomic"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[features]
|
||||||
corepack = { version = "0.4.0", default-features = false, features = ["alloc"] }
|
debug_ctap = []
|
||||||
# We pin the serde version because newer serde versions may not be compatible
|
verbose_usb = ["debug_ctap"]
|
||||||
# with the nightly toolchain used by libtock-rs.
|
|
||||||
serde = { version = "=1.0.84", default-features = false, features = ["derive"] }
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "alloc_error"
|
|
||||||
path = "examples-features/alloc_error.rs"
|
|
||||||
required-features = ["alloc", "custom_alloc_error_handler"]
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "ble_scanning"
|
|
||||||
path = "examples-features/ble_scanning.rs"
|
|
||||||
required-features = ["alloc"]
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "libtock_test"
|
|
||||||
path = "examples-features/libtock_test.rs"
|
|
||||||
required-features = ["alloc"]
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "panic"
|
|
||||||
path = "examples-features/panic.rs"
|
|
||||||
required-features = ["custom_panic_handler"]
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "simple_ble"
|
|
||||||
path = "examples-features/simple_ble.rs"
|
|
||||||
required-features = ["alloc"]
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
panic = "abort"
|
|
||||||
lto = true
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
panic = "abort"
|
|
||||||
lto = true
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[workspace]
|
|
||||||
exclude = [ "tock" ]
|
|
||||||
members = [
|
|
||||||
"codegen",
|
|
||||||
"core",
|
|
||||||
"test_runner",
|
|
||||||
"tools/print_sizes",
|
|
||||||
]
|
|
||||||
|
|||||||
207
third_party/libtock-drivers/src/buttons.rs
vendored
207
third_party/libtock-drivers/src/buttons.rs
vendored
@@ -1,10 +1,7 @@
|
|||||||
use crate::callback::CallbackSubscription;
|
use crate::result::{OtherError, TockResult};
|
||||||
use crate::callback::Consumer;
|
|
||||||
use crate::result::OtherError;
|
|
||||||
use crate::result::OutOfRangeError;
|
|
||||||
use crate::result::TockResult;
|
|
||||||
use crate::syscalls;
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
use libtock_core::callback::{CallbackSubscription, Consumer};
|
||||||
|
use libtock_core::syscalls;
|
||||||
|
|
||||||
const DRIVER_NUMBER: usize = 0x00003;
|
const DRIVER_NUMBER: usize = 0x00003;
|
||||||
|
|
||||||
@@ -19,87 +16,98 @@ mod subscribe_nr {
|
|||||||
pub const SUBSCRIBE_CALLBACK: usize = 0;
|
pub const SUBSCRIBE_CALLBACK: usize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
pub fn with_callback<CB>(callback: CB) -> WithCallback<CB> {
|
||||||
pub struct ButtonsDriverFactory;
|
WithCallback { callback }
|
||||||
|
}
|
||||||
|
|
||||||
impl ButtonsDriverFactory {
|
pub struct WithCallback<CB> {
|
||||||
pub fn init_driver(&mut self) -> TockResult<ButtonsDriver> {
|
callback: CB,
|
||||||
let buttons_driver = ButtonsDriver {
|
}
|
||||||
num_buttons: syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?,
|
|
||||||
lifetime: PhantomData,
|
struct ButtonConsumer;
|
||||||
};
|
|
||||||
Ok(buttons_driver)
|
impl<CB: FnMut(usize, ButtonState)> Consumer<WithCallback<CB>> for ButtonConsumer {
|
||||||
|
fn consume(data: &mut WithCallback<CB>, button_num: usize, state: usize, _: usize) {
|
||||||
|
(data.callback)(button_num, state.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ButtonsDriver<'a> {
|
impl<CB: FnMut(usize, ButtonState)> WithCallback<CB> {
|
||||||
num_buttons: usize,
|
pub fn init(&mut self) -> TockResult<Buttons> {
|
||||||
lifetime: PhantomData<&'a ()>,
|
let count = syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?;
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ButtonsDriver<'a> {
|
let subscription = syscalls::subscribe::<ButtonConsumer, _>(
|
||||||
pub fn num_buttons(&self) -> usize {
|
|
||||||
self.num_buttons
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the button at 0-based index `button_num`
|
|
||||||
pub fn get(&self, button_num: usize) -> Result<Button, OutOfRangeError> {
|
|
||||||
if button_num < self.num_buttons {
|
|
||||||
Ok(Button {
|
|
||||||
button_num,
|
|
||||||
lifetime: PhantomData,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(OutOfRangeError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn buttons(&self) -> Buttons {
|
|
||||||
Buttons {
|
|
||||||
num_buttons: self.num_buttons,
|
|
||||||
curr_button: 0,
|
|
||||||
lifetime: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subscribe<CB: Fn(usize, ButtonState)>(
|
|
||||||
&self,
|
|
||||||
callback: &'a mut CB,
|
|
||||||
) -> TockResult<CallbackSubscription> {
|
|
||||||
syscalls::subscribe::<ButtonsEventConsumer, _>(
|
|
||||||
DRIVER_NUMBER,
|
DRIVER_NUMBER,
|
||||||
subscribe_nr::SUBSCRIBE_CALLBACK,
|
subscribe_nr::SUBSCRIBE_CALLBACK,
|
||||||
callback,
|
self,
|
||||||
)
|
)?;
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ButtonsEventConsumer;
|
Ok(Buttons {
|
||||||
|
count: count as usize,
|
||||||
impl<CB: Fn(usize, ButtonState)> Consumer<CB> for ButtonsEventConsumer {
|
subscription,
|
||||||
fn consume(callback: &mut CB, button_num: usize, button_state: usize, _: usize) {
|
})
|
||||||
let button_state = match button_state {
|
|
||||||
0 => ButtonState::Released,
|
|
||||||
1 => ButtonState::Pressed,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
callback(button_num, button_state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Buttons<'a> {
|
pub struct Buttons<'a> {
|
||||||
num_buttons: usize,
|
count: usize,
|
||||||
|
#[allow(dead_code)] // Used in drop
|
||||||
|
subscription: CallbackSubscription<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum ButtonsError {
|
||||||
|
NotSupported,
|
||||||
|
SubscriptionFailed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Buttons<'a> {
|
||||||
|
pub fn iter_mut(&mut self) -> ButtonIter {
|
||||||
|
ButtonIter {
|
||||||
|
curr_button: 0,
|
||||||
|
button_count: self.count,
|
||||||
|
lifetime: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum ButtonState {
|
||||||
|
Pressed,
|
||||||
|
Released,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for ButtonState {
|
||||||
|
fn from(state: usize) -> ButtonState {
|
||||||
|
match state {
|
||||||
|
0 => ButtonState::Released,
|
||||||
|
1 => ButtonState::Pressed,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> IntoIterator for &'b mut Buttons<'a> {
|
||||||
|
type Item = ButtonHandle<'b>;
|
||||||
|
type IntoIter = ButtonIter<'b>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.iter_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ButtonIter<'a> {
|
||||||
curr_button: usize,
|
curr_button: usize,
|
||||||
|
button_count: usize,
|
||||||
lifetime: PhantomData<&'a ()>,
|
lifetime: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Buttons<'a> {
|
impl<'a> Iterator for ButtonIter<'a> {
|
||||||
type Item = Button<'a>;
|
type Item = ButtonHandle<'a>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.curr_button < self.num_buttons {
|
if self.curr_button < self.button_count {
|
||||||
let item = Button {
|
let item = ButtonHandle {
|
||||||
button_num: self.curr_button,
|
button_num: self.curr_button,
|
||||||
lifetime: PhantomData,
|
lifetime: PhantomData,
|
||||||
};
|
};
|
||||||
@@ -111,57 +119,52 @@ impl<'a> Iterator for Buttons<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
pub struct ButtonHandle<'a> {
|
||||||
pub enum ButtonState {
|
|
||||||
Pressed,
|
|
||||||
Released,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ButtonState> for bool {
|
|
||||||
fn from(button_state: ButtonState) -> Self {
|
|
||||||
match button_state {
|
|
||||||
ButtonState::Released => false,
|
|
||||||
ButtonState::Pressed => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Button<'a> {
|
|
||||||
button_num: usize,
|
button_num: usize,
|
||||||
lifetime: PhantomData<&'a ()>,
|
lifetime: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Button<'a> {
|
impl<'a> ButtonHandle<'a> {
|
||||||
pub fn button_num(&self) -> usize {
|
pub fn enable(&mut self) -> TockResult<Button> {
|
||||||
self.button_num
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&self) -> TockResult<ButtonState> {
|
|
||||||
let button_state = syscalls::command(DRIVER_NUMBER, command_nr::READ, self.button_num, 0)?;
|
|
||||||
match button_state {
|
|
||||||
0 => Ok(ButtonState::Released),
|
|
||||||
1 => Ok(ButtonState::Pressed),
|
|
||||||
_ => Err(OtherError::ButtonsDriverInvalidState.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable_interrupt(&self) -> TockResult<()> {
|
|
||||||
syscalls::command(
|
syscalls::command(
|
||||||
DRIVER_NUMBER,
|
DRIVER_NUMBER,
|
||||||
command_nr::ENABLE_INTERRUPT,
|
command_nr::ENABLE_INTERRUPT,
|
||||||
self.button_num,
|
self.button_num,
|
||||||
0,
|
0,
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
|
||||||
|
Ok(Button { handle: self })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_interrupt(&self) -> TockResult<()> {
|
pub fn disable(&mut self) -> TockResult<()> {
|
||||||
syscalls::command(
|
syscalls::command(
|
||||||
DRIVER_NUMBER,
|
DRIVER_NUMBER,
|
||||||
command_nr::DISABLE_INTERRUPT,
|
command_nr::DISABLE_INTERRUPT,
|
||||||
self.button_num,
|
self.button_num,
|
||||||
0,
|
0,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Button<'a> {
|
||||||
|
handle: &'a ButtonHandle<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum ButtonError {
|
||||||
|
ActivationFailed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Button<'a> {
|
||||||
|
pub fn read(&self) -> TockResult<ButtonState> {
|
||||||
|
let button_state =
|
||||||
|
syscalls::command(DRIVER_NUMBER, command_nr::READ, self.handle.button_num, 0)?;
|
||||||
|
match button_state {
|
||||||
|
0 => Ok(ButtonState::Released),
|
||||||
|
1 => Ok(ButtonState::Pressed),
|
||||||
|
_ => Err(OtherError::ButtonsDriverInvalidState.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
109
third_party/libtock-drivers/src/console.rs
vendored
109
third_party/libtock-drivers/src/console.rs
vendored
@@ -1,11 +1,7 @@
|
|||||||
use crate::callback::Identity0Consumer;
|
use crate::util;
|
||||||
use crate::executor;
|
|
||||||
use crate::futures;
|
|
||||||
use crate::result::TockResult;
|
|
||||||
use crate::syscalls;
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::mem;
|
use libtock_core::{callback, syscalls};
|
||||||
|
|
||||||
const DRIVER_NUMBER: usize = 1;
|
const DRIVER_NUMBER: usize = 1;
|
||||||
|
|
||||||
@@ -21,62 +17,97 @@ mod allow_nr {
|
|||||||
pub const SHARE_BUFFER: usize = 1;
|
pub const SHARE_BUFFER: usize = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
const BUFFER_SIZE: usize = 1024;
|
||||||
pub struct ConsoleDriver;
|
|
||||||
|
|
||||||
impl ConsoleDriver {
|
|
||||||
pub fn create_console(self) -> Console {
|
|
||||||
Console {
|
|
||||||
allow_buffer: [0; 64],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Console {
|
pub struct Console {
|
||||||
allow_buffer: [u8; 64],
|
allow_buffer: [u8; BUFFER_SIZE],
|
||||||
|
count_pending: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Console {
|
impl Console {
|
||||||
pub fn write<S: AsRef<[u8]>>(&mut self, text: S) -> TockResult<()> {
|
pub fn new() -> Console {
|
||||||
let mut not_written_yet = text.as_ref();
|
Console {
|
||||||
while !not_written_yet.is_empty() {
|
allow_buffer: [0; BUFFER_SIZE],
|
||||||
let num_bytes_to_print = self.allow_buffer.len().min(not_written_yet.len());
|
count_pending: 0,
|
||||||
self.allow_buffer[..num_bytes_to_print]
|
|
||||||
.copy_from_slice(¬_written_yet[..num_bytes_to_print]);
|
|
||||||
self.flush(num_bytes_to_print)?;
|
|
||||||
not_written_yet = ¬_written_yet[num_bytes_to_print..];
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self, num_bytes_to_print: usize) -> TockResult<()> {
|
fn is_empty(&self) -> bool {
|
||||||
let shared_memory = syscalls::allow(
|
self.count_pending == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_full(&self) -> bool {
|
||||||
|
self.allow_buffer.len() == self.count_pending
|
||||||
|
}
|
||||||
|
|
||||||
|
fn available_len(&self) -> usize {
|
||||||
|
self.allow_buffer.len() - self.count_pending
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<S: AsRef<[u8]>>(&mut self, text: S) {
|
||||||
|
let mut not_written_yet = text.as_ref();
|
||||||
|
while !not_written_yet.is_empty() {
|
||||||
|
let num_bytes_to_print = self.available_len().min(not_written_yet.len());
|
||||||
|
self.allow_buffer[self.count_pending..(self.count_pending + num_bytes_to_print)]
|
||||||
|
.copy_from_slice(¬_written_yet[..num_bytes_to_print]);
|
||||||
|
self.count_pending += num_bytes_to_print;
|
||||||
|
|
||||||
|
if self.is_full() {
|
||||||
|
self.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
not_written_yet = ¬_written_yet[num_bytes_to_print..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush(&mut self) {
|
||||||
|
if self.is_empty() {
|
||||||
|
// Don't trigger any syscall if the buffer is empty.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = self.count_pending;
|
||||||
|
// Clear the buffer even in case of error, to avoid an infinite loop.
|
||||||
|
self.count_pending = 0;
|
||||||
|
|
||||||
|
let result = syscalls::allow(
|
||||||
DRIVER_NUMBER,
|
DRIVER_NUMBER,
|
||||||
allow_nr::SHARE_BUFFER,
|
allow_nr::SHARE_BUFFER,
|
||||||
&mut self.allow_buffer[..num_bytes_to_print],
|
&mut self.allow_buffer[..count],
|
||||||
)?;
|
);
|
||||||
|
if result.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let is_written = Cell::new(false);
|
let is_written = Cell::new(false);
|
||||||
let mut is_written_alarm = || is_written.set(true);
|
let mut is_written_alarm = || is_written.set(true);
|
||||||
let subscription = syscalls::subscribe::<Identity0Consumer, _>(
|
let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
|
||||||
DRIVER_NUMBER,
|
DRIVER_NUMBER,
|
||||||
subscribe_nr::SET_ALARM,
|
subscribe_nr::SET_ALARM,
|
||||||
&mut is_written_alarm,
|
&mut is_written_alarm,
|
||||||
)?;
|
);
|
||||||
|
if subscription.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
syscalls::command(DRIVER_NUMBER, command_nr::WRITE, num_bytes_to_print, 0)?;
|
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::WRITE, count, 0);
|
||||||
|
if result_code.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
unsafe { executor::block_on(futures::wait_until(|| is_written.get())) };
|
util::yieldk_for(|| is_written.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mem::drop(subscription);
|
impl Drop for Console {
|
||||||
mem::drop(shared_memory);
|
fn drop(&mut self) {
|
||||||
|
self.flush();
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Write for Console {
|
impl fmt::Write for Console {
|
||||||
fn write_str(&mut self, string: &str) -> Result<(), fmt::Error> {
|
fn write_str(&mut self, string: &str) -> Result<(), fmt::Error> {
|
||||||
self.write(string).map_err(|_| fmt::Error)
|
self.write(string);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
173
third_party/libtock-drivers/src/led.rs
vendored
173
third_party/libtock-drivers/src/led.rs
vendored
@@ -1,7 +1,5 @@
|
|||||||
use crate::result::OutOfRangeError;
|
use crate::result::{OtherError, TockError, TockResult};
|
||||||
use crate::result::TockResult;
|
use libtock_core::syscalls;
|
||||||
use crate::syscalls::command;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
const DRIVER_NUMBER: usize = 0x00002;
|
const DRIVER_NUMBER: usize = 0x00002;
|
||||||
|
|
||||||
@@ -12,64 +10,70 @@ mod command_nr {
|
|||||||
pub const TOGGLE: usize = 3;
|
pub const TOGGLE: usize = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
pub struct Led {
|
||||||
pub struct LedsDriverFactory;
|
led_num: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl LedsDriverFactory {
|
pub fn count() -> TockResult<usize> {
|
||||||
pub fn init_driver(&mut self) -> TockResult<LedsDriver> {
|
let count = syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?;
|
||||||
let driver = LedsDriver {
|
Ok(count)
|
||||||
num_leds: command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?,
|
}
|
||||||
lifetime: PhantomData,
|
|
||||||
};
|
pub fn get(led_num: usize) -> TockResult<Led> {
|
||||||
Ok(driver)
|
let led_count = count()?;
|
||||||
|
if led_num < led_count {
|
||||||
|
Ok(Led { led_num })
|
||||||
|
} else {
|
||||||
|
Err(TockError::Other(OtherError::OutOfRange))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LedsDriver<'a> {
|
pub fn all() -> TockResult<LedIter> {
|
||||||
num_leds: usize,
|
let led_count = count()?;
|
||||||
lifetime: PhantomData<&'a ()>,
|
Ok(LedIter {
|
||||||
|
curr_led: 0,
|
||||||
|
led_count,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LedsDriver<'a> {
|
impl Led {
|
||||||
pub fn num_leds(&self) -> usize {
|
pub fn set_state(&self, state: bool) -> TockResult<()> {
|
||||||
self.num_leds
|
if state {
|
||||||
}
|
self.on()
|
||||||
|
|
||||||
pub fn leds(&self) -> Leds {
|
|
||||||
Leds {
|
|
||||||
num_leds: self.num_leds,
|
|
||||||
curr_led: 0,
|
|
||||||
lifetime: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the led at 0-based index `led_num`
|
|
||||||
pub fn get(&self, led_num: usize) -> Result<Led, OutOfRangeError> {
|
|
||||||
if led_num < self.num_leds {
|
|
||||||
Ok(Led {
|
|
||||||
led_num,
|
|
||||||
lifetime: PhantomData,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Err(OutOfRangeError)
|
self.off()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on(&self) -> TockResult<()> {
|
||||||
|
syscalls::command(DRIVER_NUMBER, command_nr::ON, self.led_num, 0)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn off(&self) -> TockResult<()> {
|
||||||
|
syscalls::command(DRIVER_NUMBER, command_nr::OFF, self.led_num, 0)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle(&self) -> TockResult<()> {
|
||||||
|
syscalls::command(DRIVER_NUMBER, command_nr::TOGGLE, self.led_num, 0)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Leds<'a> {
|
#[derive(Copy, Clone)]
|
||||||
num_leds: usize,
|
pub struct LedIter {
|
||||||
curr_led: usize,
|
curr_led: usize,
|
||||||
lifetime: PhantomData<&'a ()>,
|
led_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Leds<'a> {
|
impl Iterator for LedIter {
|
||||||
type Item = Led<'a>;
|
type Item = Led;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.curr_led < self.num_leds {
|
if self.curr_led < self.led_count {
|
||||||
let item = Led {
|
let item = Led {
|
||||||
led_num: self.curr_led,
|
led_num: self.curr_led,
|
||||||
lifetime: PhantomData,
|
|
||||||
};
|
};
|
||||||
self.curr_led += 1;
|
self.curr_led += 1;
|
||||||
Some(item)
|
Some(item)
|
||||||
@@ -78,84 +82,3 @@ impl<'a> Iterator for Leds<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Led<'a> {
|
|
||||||
led_num: usize,
|
|
||||||
lifetime: PhantomData<&'a ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Led<'a> {
|
|
||||||
pub fn led_num(&self) -> usize {
|
|
||||||
self.led_num
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(&self, state: impl Into<LedState>) -> TockResult<()> {
|
|
||||||
match state.into() {
|
|
||||||
LedState::On => self.on(),
|
|
||||||
LedState::Off => self.off(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on(&self) -> TockResult<()> {
|
|
||||||
command(DRIVER_NUMBER, command_nr::ON, self.led_num, 0)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn off(&self) -> TockResult<()> {
|
|
||||||
command(DRIVER_NUMBER, command_nr::OFF, self.led_num, 0)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toggle(&self) -> TockResult<()> {
|
|
||||||
command(DRIVER_NUMBER, command_nr::TOGGLE, self.led_num, 0)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub enum LedState {
|
|
||||||
On,
|
|
||||||
Off,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for LedState {
|
|
||||||
fn from(from_value: bool) -> Self {
|
|
||||||
if from_value {
|
|
||||||
LedState::On
|
|
||||||
} else {
|
|
||||||
LedState::Off
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::command_nr;
|
|
||||||
use super::DRIVER_NUMBER;
|
|
||||||
use crate::result::TockResult;
|
|
||||||
use crate::syscalls;
|
|
||||||
use crate::syscalls::raw::Event;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn single_led_can_be_enabled() {
|
|
||||||
let events = syscalls::raw::run_recording_events::<TockResult<()>, _>(|next_return| {
|
|
||||||
let mut drivers = unsafe { crate::drivers::retrieve_drivers_unsafe() };
|
|
||||||
|
|
||||||
next_return.set(1);
|
|
||||||
|
|
||||||
let leds_driver = drivers.leds.init_driver()?;
|
|
||||||
next_return.set(0);
|
|
||||||
|
|
||||||
let led = leds_driver.get(0)?;
|
|
||||||
led.on()?;
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
assert_eq!(
|
|
||||||
events,
|
|
||||||
vec![
|
|
||||||
Event::Command(DRIVER_NUMBER, command_nr::COUNT, 0, 0),
|
|
||||||
Event::Command(DRIVER_NUMBER, command_nr::ON, 0, 0),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
23
third_party/libtock-drivers/src/lib.rs
vendored
23
third_party/libtock-drivers/src/lib.rs
vendored
@@ -1,25 +1,10 @@
|
|||||||
#![cfg_attr(not(test), no_std)]
|
#![no_std]
|
||||||
|
|
||||||
pub mod adc;
|
|
||||||
pub mod ble_composer;
|
|
||||||
pub mod ble_parser;
|
|
||||||
pub mod buttons;
|
pub mod buttons;
|
||||||
pub mod console;
|
pub mod console;
|
||||||
pub mod debug;
|
pub mod led;
|
||||||
pub mod drivers;
|
|
||||||
pub mod electronics;
|
|
||||||
pub mod executor;
|
|
||||||
pub mod futures;
|
|
||||||
pub mod gpio;
|
|
||||||
pub mod hmac;
|
|
||||||
pub mod leds;
|
|
||||||
pub mod result;
|
pub mod result;
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
pub mod sensors;
|
|
||||||
pub mod simple_ble;
|
|
||||||
pub mod temperature;
|
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
pub mod usb_ctap_hid;
|
||||||
pub use drivers::retrieve_drivers;
|
pub mod util;
|
||||||
pub use libtock_codegen::main;
|
|
||||||
pub use libtock_core::*;
|
|
||||||
|
|||||||
68
third_party/libtock-drivers/src/result.rs
vendored
68
third_party/libtock-drivers/src/result.rs
vendored
@@ -4,6 +4,32 @@ pub use libtock_core::result::*;
|
|||||||
|
|
||||||
pub type TockResult<T> = Result<T, TockError>;
|
pub type TockResult<T> = Result<T, TockError>;
|
||||||
|
|
||||||
|
// We sometimes need to handle errors in a `TockResult` by calling `unwrap`. However,
|
||||||
|
// `Result::unwrap` requires that the error type implements `core::fmt::Debug`. Under the hood,
|
||||||
|
// this requires dynamic dispatch, which has non-negligible overhead on code size. Therefore errors
|
||||||
|
// don't derive from `Debug` in libtock-rs.
|
||||||
|
//
|
||||||
|
// Instead one can call `.ok().unwrap()` which relies on `Option::unwrap` and doesn't require any
|
||||||
|
// debugging of the error type.
|
||||||
|
//
|
||||||
|
// This trait allows to flexibly use `Result::unwrap` or `Option::unwrap` and is configured to do
|
||||||
|
// so depending on the `debug_ctap` feature.
|
||||||
|
pub trait FlexUnwrap<T> {
|
||||||
|
fn flex_unwrap(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FlexUnwrap<T> for TockResult<T> {
|
||||||
|
#[cfg(feature = "debug_ctap")]
|
||||||
|
fn flex_unwrap(self) -> T {
|
||||||
|
self.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "debug_ctap"))]
|
||||||
|
fn flex_unwrap(self) -> T {
|
||||||
|
self.ok().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum TockError {
|
pub enum TockError {
|
||||||
Subscribe(SubscribeError),
|
Subscribe(SubscribeError),
|
||||||
@@ -13,10 +39,47 @@ pub enum TockError {
|
|||||||
Other(OtherError),
|
Other(OtherError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "arm", target_arch = "riscv32")))]
|
#[cfg(feature = "debug_ctap")]
|
||||||
impl core::fmt::Debug for TockError {
|
impl core::fmt::Debug for TockError {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
writeln!(f, "impl Debug only for test builds")
|
match self {
|
||||||
|
TockError::Subscribe(SubscribeError {
|
||||||
|
driver_number,
|
||||||
|
subscribe_number,
|
||||||
|
return_code,
|
||||||
|
}) => f
|
||||||
|
.debug_struct("SubscribeError")
|
||||||
|
.field("driver", driver_number)
|
||||||
|
.field("subscribe", subscribe_number)
|
||||||
|
.field("return_code", return_code)
|
||||||
|
.finish(),
|
||||||
|
TockError::Command(CommandError {
|
||||||
|
driver_number,
|
||||||
|
command_number,
|
||||||
|
arg1,
|
||||||
|
arg2,
|
||||||
|
return_code,
|
||||||
|
}) => f
|
||||||
|
.debug_struct("CommandError")
|
||||||
|
.field("driver", driver_number)
|
||||||
|
.field("command", command_number)
|
||||||
|
.field("arg1", arg1)
|
||||||
|
.field("arg2", arg2)
|
||||||
|
.field("return_code", return_code)
|
||||||
|
.finish(),
|
||||||
|
TockError::Allow(AllowError {
|
||||||
|
driver_number,
|
||||||
|
allow_number,
|
||||||
|
return_code,
|
||||||
|
}) => f
|
||||||
|
.debug_struct("AllowError")
|
||||||
|
.field("driver", driver_number)
|
||||||
|
.field("allow", allow_number)
|
||||||
|
.field("return_code", return_code)
|
||||||
|
.finish(),
|
||||||
|
TockError::Format => f.write_str("TockError::Format"),
|
||||||
|
TockError::Other(e) => e.fmt(f),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +108,7 @@ impl From<fmt::Error> for TockError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "debug_ctap", derive(Debug))]
|
||||||
pub enum OtherError {
|
pub enum OtherError {
|
||||||
ButtonsDriverInvalidState,
|
ButtonsDriverInvalidState,
|
||||||
GpioDriverInvalidState,
|
GpioDriverInvalidState,
|
||||||
|
|||||||
49
third_party/libtock-drivers/src/rng.rs
vendored
49
third_party/libtock-drivers/src/rng.rs
vendored
@@ -1,9 +1,6 @@
|
|||||||
use crate::callback::Identity0Consumer;
|
use crate::util;
|
||||||
use crate::futures;
|
|
||||||
use crate::result::TockResult;
|
|
||||||
use crate::syscalls;
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core::mem;
|
use libtock_core::{callback, syscalls};
|
||||||
|
|
||||||
const DRIVER_NUMBER: usize = 0x40001;
|
const DRIVER_NUMBER: usize = 0x40001;
|
||||||
|
|
||||||
@@ -19,24 +16,30 @@ mod allow_nr {
|
|||||||
pub const SHARE_BUFFER: usize = 0;
|
pub const SHARE_BUFFER: usize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[non_exhaustive]
|
pub fn fill_buffer(buf: &mut [u8]) -> bool {
|
||||||
pub struct RngDriver;
|
let buf_len = buf.len();
|
||||||
|
|
||||||
impl RngDriver {
|
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf);
|
||||||
pub async fn fill_buffer(&mut self, buf: &mut [u8]) -> TockResult<()> {
|
if result.is_err() {
|
||||||
let buf_len = buf.len();
|
return false;
|
||||||
let shared_memory = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf)?;
|
|
||||||
let is_filled = Cell::new(false);
|
|
||||||
let mut is_filled_alarm = || is_filled.set(true);
|
|
||||||
let subscription = syscalls::subscribe::<Identity0Consumer, _>(
|
|
||||||
DRIVER_NUMBER,
|
|
||||||
subscribe_nr::BUFFER_FILLED,
|
|
||||||
&mut is_filled_alarm,
|
|
||||||
)?;
|
|
||||||
syscalls::command(DRIVER_NUMBER, command_nr::REQUEST_RNG, buf_len, 0)?;
|
|
||||||
futures::wait_until(|| is_filled.get()).await;
|
|
||||||
mem::drop(subscription);
|
|
||||||
mem::drop(shared_memory);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let is_filled = Cell::new(false);
|
||||||
|
let mut is_filled_alarm = || is_filled.set(true);
|
||||||
|
let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
|
||||||
|
DRIVER_NUMBER,
|
||||||
|
subscribe_nr::BUFFER_FILLED,
|
||||||
|
&mut is_filled_alarm,
|
||||||
|
);
|
||||||
|
if subscription.is_err() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::REQUEST_RNG, buf_len, 0);
|
||||||
|
if result_code.is_err() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
util::yieldk_for(|| is_filled.get());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
562
third_party/libtock-drivers/src/timer.rs
vendored
562
third_party/libtock-drivers/src/timer.rs
vendored
@@ -1,17 +1,12 @@
|
|||||||
//! Async timer driver. Can be used for (non-busy) sleeping.
|
use crate::result::{FlexUnwrap, OtherError, TockError, TockResult};
|
||||||
|
use crate::util;
|
||||||
use crate::callback::CallbackSubscription;
|
|
||||||
use crate::callback::Consumer;
|
|
||||||
use crate::futures;
|
|
||||||
use crate::result::OtherError;
|
|
||||||
use crate::result::TockError;
|
|
||||||
use crate::result::TockResult;
|
|
||||||
use crate::result::EALREADY;
|
|
||||||
use crate::syscalls;
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core::isize;
|
use core::isize;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ops::{Add, AddAssign, Sub};
|
use core::ops::{Add, AddAssign, Sub};
|
||||||
|
use libtock_core::callback::{CallbackSubscription, Consumer};
|
||||||
|
use libtock_core::result::{CommandError, EALREADY};
|
||||||
|
use libtock_core::syscalls;
|
||||||
|
|
||||||
const DRIVER_NUMBER: usize = 0x00000;
|
const DRIVER_NUMBER: usize = 0x00000;
|
||||||
|
|
||||||
@@ -27,6 +22,33 @@ mod subscribe_nr {
|
|||||||
pub const SUBSCRIBE_CALLBACK: usize = 0;
|
pub const SUBSCRIBE_CALLBACK: usize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sleep(duration: Duration<isize>) -> TockResult<()> {
|
||||||
|
let expired = Cell::new(false);
|
||||||
|
let mut with_callback = with_callback(|_, _| expired.set(true));
|
||||||
|
|
||||||
|
let mut timer = with_callback.init().flex_unwrap();
|
||||||
|
let timer_alarm = timer.set_alarm(duration).flex_unwrap();
|
||||||
|
|
||||||
|
util::yieldk_for(|| expired.get());
|
||||||
|
|
||||||
|
match timer.stop_alarm(timer_alarm) {
|
||||||
|
Ok(())
|
||||||
|
| Err(TockError::Command(CommandError {
|
||||||
|
return_code: EALREADY,
|
||||||
|
..
|
||||||
|
})) => Ok(()),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_callback<CB>(callback: CB) -> WithCallback<'static, CB> {
|
||||||
|
WithCallback {
|
||||||
|
callback,
|
||||||
|
clock_frequency: ClockFrequency { hz: 0 },
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WithCallback<'a, CB> {
|
pub struct WithCallback<'a, CB> {
|
||||||
callback: CB,
|
callback: CB,
|
||||||
clock_frequency: ClockFrequency,
|
clock_frequency: ClockFrequency,
|
||||||
@@ -136,13 +158,13 @@ impl<'a> Timer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct ClockFrequency {
|
pub struct ClockFrequency {
|
||||||
hz: usize,
|
hz: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClockFrequency {
|
impl ClockFrequency {
|
||||||
pub fn hz(self) -> usize {
|
pub fn hz(&self) -> usize {
|
||||||
self.hz
|
self.hz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,21 +176,53 @@ pub struct ClockValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClockValue {
|
impl ClockValue {
|
||||||
pub fn num_ticks(self) -> isize {
|
pub const fn new(num_ticks: isize, clock_hz: usize) -> ClockValue {
|
||||||
self.num_ticks
|
ClockValue {
|
||||||
}
|
num_ticks,
|
||||||
|
clock_frequency: ClockFrequency { hz: clock_hz },
|
||||||
pub fn ms(self) -> isize {
|
|
||||||
if self.num_ticks.abs() < isize::MAX / 1000 {
|
|
||||||
(1000 * self.num_ticks) / self.clock_frequency.hz() as isize
|
|
||||||
} else {
|
|
||||||
1000 * (self.num_ticks / self.clock_frequency.hz() as isize)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ms_f64(self) -> f64 {
|
pub fn num_ticks(&self) -> isize {
|
||||||
|
self.num_ticks
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes (value * factor) / divisor, even when value * factor >= isize::MAX.
|
||||||
|
fn scale_int(value: isize, factor: isize, divisor: isize) -> isize {
|
||||||
|
// As long as isize is not i64, this should be fine. If not, this is an alternative:
|
||||||
|
// factor * (value / divisor) + ((value % divisor) * factor) / divisor
|
||||||
|
((value as i64 * factor as i64) / divisor as i64) as isize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ms(&self) -> isize {
|
||||||
|
ClockValue::scale_int(self.num_ticks, 1000, self.clock_frequency.hz() as isize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ms_f64(&self) -> f64 {
|
||||||
1000.0 * (self.num_ticks as f64) / (self.clock_frequency.hz() as f64)
|
1000.0 * (self.num_ticks as f64) / (self.clock_frequency.hz() as f64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn wrapping_add(self, duration: Duration<isize>) -> ClockValue {
|
||||||
|
// This is a precision preserving formula for scaling an isize.
|
||||||
|
let duration_ticks =
|
||||||
|
ClockValue::scale_int(duration.ms, self.clock_frequency.hz() as isize, 1000);
|
||||||
|
ClockValue {
|
||||||
|
num_ticks: self.num_ticks.wrapping_add(duration_ticks),
|
||||||
|
clock_frequency: self.clock_frequency,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrapping_sub(self, other: ClockValue) -> Option<Duration<isize>> {
|
||||||
|
if self.clock_frequency == other.clock_frequency {
|
||||||
|
let clock_duration = ClockValue {
|
||||||
|
num_ticks: self.num_ticks - other.num_ticks,
|
||||||
|
clock_frequency: self.clock_frequency,
|
||||||
|
};
|
||||||
|
Some(Duration::from_ms(clock_duration.ms()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Alarm {
|
pub struct Alarm {
|
||||||
@@ -278,467 +332,3 @@ where
|
|||||||
self.ms += duration.ms();
|
self.ms += duration.ms();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, PartialEq, Eq)]
|
|
||||||
pub(crate) struct ActiveTimer {
|
|
||||||
instant: u32,
|
|
||||||
set_at: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Context for the time driver.
|
|
||||||
/// You can create a context as follows:
|
|
||||||
/// ```no_run
|
|
||||||
/// # use libtock::result::TockResult;
|
|
||||||
/// # async fn doc() -> TockResult<()> {
|
|
||||||
/// let mut drivers = libtock::retrieve_drivers()?;
|
|
||||||
/// let mut timer_context = drivers.timer;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct DriverContext {
|
|
||||||
pub(crate) active_timer: Cell<Option<ActiveTimer>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DriverContext {
|
|
||||||
/// Create a driver timer from a context.
|
|
||||||
pub fn create_timer_driver(&mut self) -> TimerDriver {
|
|
||||||
TimerDriver {
|
|
||||||
callback: Callback,
|
|
||||||
context: self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_callback<CB>(&mut self, callback: CB) -> WithCallback<CB> {
|
|
||||||
WithCallback {
|
|
||||||
callback,
|
|
||||||
clock_frequency: ClockFrequency { hz: 0 },
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Timer driver instance. You can create a TimerDriver from a DriverContext as follows:
|
|
||||||
/// ```no_run
|
|
||||||
/// # use libtock::result::TockResult;
|
|
||||||
/// # async fn doc() -> TockResult<()> {
|
|
||||||
/// # let mut drivers = libtock::retrieve_drivers()?;
|
|
||||||
/// # let mut timer_context = drivers.timer;
|
|
||||||
/// let mut timer_driver = timer_context.create_timer_driver();
|
|
||||||
/// let timer_driver = timer_driver.activate()?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub struct TimerDriver<'a> {
|
|
||||||
callback: Callback,
|
|
||||||
context: &'a DriverContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Callback;
|
|
||||||
|
|
||||||
struct ParallelTimerConsumer;
|
|
||||||
|
|
||||||
impl<'a> Consumer<Callback> for ParallelTimerConsumer {
|
|
||||||
fn consume(_: &mut Callback, _: usize, _: usize, _: usize) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Activated time driver. Updates current time in the context and manages
|
|
||||||
/// active alarms.
|
|
||||||
/// Example usage (sleep for 1 second):
|
|
||||||
/// ```no_run
|
|
||||||
/// # use libtock::result::TockResult;
|
|
||||||
/// # use libtock::timer::Duration;
|
|
||||||
/// # async fn doc() -> TockResult<()> {
|
|
||||||
/// # let mut drivers = libtock::retrieve_drivers()?;
|
|
||||||
/// # let mut timer_driver = drivers.timer.create_timer_driver();
|
|
||||||
/// let timer_driver = timer_driver.activate()?;
|
|
||||||
/// timer_driver.sleep(Duration::from_ms(1000)).await?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
pub struct ParallelSleepDriver<'a> {
|
|
||||||
_callback_subscription: CallbackSubscription<'a>,
|
|
||||||
context: &'a DriverContext,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TimerDriver<'a> {
|
|
||||||
/// Activate the timer driver, will return a ParallelSleepDriver which
|
|
||||||
/// can used to sleep.
|
|
||||||
pub fn activate(&'a mut self) -> TockResult<ParallelSleepDriver<'a>> {
|
|
||||||
let subscription = syscalls::subscribe::<ParallelTimerConsumer, _>(
|
|
||||||
DRIVER_NUMBER,
|
|
||||||
subscribe_nr::SUBSCRIBE_CALLBACK,
|
|
||||||
&mut self.callback,
|
|
||||||
)?;
|
|
||||||
let driver = ParallelSleepDriver {
|
|
||||||
_callback_subscription: subscription,
|
|
||||||
context: &self.context,
|
|
||||||
};
|
|
||||||
Ok(driver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ParallelSleepDriver<'a> {
|
|
||||||
/// Sleep for the given duration
|
|
||||||
pub async fn sleep(&self, duration: Duration<usize>) -> TockResult<()> {
|
|
||||||
let now = get_current_ticks()?;
|
|
||||||
let freq = get_clock_frequency()?;
|
|
||||||
let alarm_instant = Self::compute_alarm_instant(duration.ms, now, freq)?;
|
|
||||||
let this_alarm = ActiveTimer {
|
|
||||||
instant: alarm_instant as u32,
|
|
||||||
set_at: now as u32,
|
|
||||||
};
|
|
||||||
|
|
||||||
let suspended_timer: Cell<Option<ActiveTimer>> = Cell::new(None);
|
|
||||||
|
|
||||||
futures::wait_until(|| {
|
|
||||||
self.activate_current_timer(this_alarm, &suspended_timer)
|
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn activate_timer(&self, timer: ActiveTimer) -> TockResult<()> {
|
|
||||||
set_alarm_at(timer.instant as usize)?;
|
|
||||||
let now = get_current_ticks()?;
|
|
||||||
if !is_over(timer, now as u32) {
|
|
||||||
self.context.active_timer.set(Some(timer));
|
|
||||||
} else {
|
|
||||||
self.wakeup_soon()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wakeup_soon(&self) -> TockResult<()> {
|
|
||||||
self.context.active_timer.set(None);
|
|
||||||
|
|
||||||
for i in 0.. {
|
|
||||||
let now = get_current_ticks()?;
|
|
||||||
|
|
||||||
let next_timer = ActiveTimer {
|
|
||||||
instant: now as u32 + i,
|
|
||||||
set_at: now as u32,
|
|
||||||
};
|
|
||||||
set_alarm_at(next_timer.instant as usize)?;
|
|
||||||
let now = get_current_ticks()?;
|
|
||||||
if !is_over(next_timer, now as u32) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
stop_alarm_at(next_timer.instant as usize)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_alarm_instant(
|
|
||||||
duration_ms: usize,
|
|
||||||
num_ticks: usize,
|
|
||||||
freq: usize,
|
|
||||||
) -> TockResult<usize> {
|
|
||||||
let ticks = match duration_ms.checked_mul(freq) {
|
|
||||||
Some(x) => x / 1000,
|
|
||||||
None => {
|
|
||||||
// Divide the largest of the two operands by 1000, to improve precision of the
|
|
||||||
// result.
|
|
||||||
if duration_ms > freq {
|
|
||||||
match (duration_ms / 1000).checked_mul(freq) {
|
|
||||||
Some(y) => y,
|
|
||||||
None => {
|
|
||||||
return Err(TockError::Other(OtherError::TimerDriverDurationOutOfRange))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match (freq / 1000).checked_mul(duration_ms) {
|
|
||||||
Some(y) => y,
|
|
||||||
None => {
|
|
||||||
return Err(TockError::Other(OtherError::TimerDriverDurationOutOfRange))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let alarm_instant = num_ticks + ticks;
|
|
||||||
Ok(alarm_instant)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn activate_current_timer(
|
|
||||||
&self,
|
|
||||||
this_alarm: ActiveTimer,
|
|
||||||
suspended_timer: &Cell<Option<ActiveTimer>>,
|
|
||||||
) -> TockResult<bool> {
|
|
||||||
let now = get_current_ticks()?;
|
|
||||||
|
|
||||||
if let Some(active) = self.context.active_timer.get() {
|
|
||||||
if left_is_later(active, this_alarm) {
|
|
||||||
suspended_timer.set(Some(active));
|
|
||||||
self.activate_timer(this_alarm)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.activate_timer(this_alarm)?;
|
|
||||||
}
|
|
||||||
if is_over(this_alarm, now as u32) {
|
|
||||||
if let Some(paused) = suspended_timer.get() {
|
|
||||||
self.activate_timer(paused)?;
|
|
||||||
} else {
|
|
||||||
self.context.active_timer.set(None);
|
|
||||||
}
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_current_ticks() -> TockResult<usize> {
|
|
||||||
syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0).map_err(|err| err.into())
|
|
||||||
}
|
|
||||||
fn set_alarm_at(instant: usize) -> TockResult<()> {
|
|
||||||
syscalls::command(DRIVER_NUMBER, command_nr::SET_ALARM, instant, 0)
|
|
||||||
.map(|_| ())
|
|
||||||
.map_err(|err| err.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stop_alarm_at(instant: usize) -> TockResult<()> {
|
|
||||||
match syscalls::command(DRIVER_NUMBER, command_nr::STOP_ALARM, instant, 0) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(error) => match error.return_code {
|
|
||||||
EALREADY => Ok(()),
|
|
||||||
_ => Err(TockError::Command(error)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_clock_frequency() -> TockResult<usize> {
|
|
||||||
syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0)
|
|
||||||
.map_err(|err| err.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_over(timer: ActiveTimer, now: u32) -> bool {
|
|
||||||
now.wrapping_sub(timer.set_at) >= timer.instant.wrapping_sub(timer.set_at)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn left_is_later(alarm_1: ActiveTimer, alarm_2: ActiveTimer) -> bool {
|
|
||||||
if alarm_1.set_at <= alarm_1.instant && alarm_2.set_at <= alarm_2.instant {
|
|
||||||
return alarm_1.instant > alarm_2.instant;
|
|
||||||
}
|
|
||||||
if alarm_1.set_at <= alarm_1.instant && alarm_2.set_at >= alarm_2.instant {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if alarm_1.set_at >= alarm_1.instant && alarm_2.set_at <= alarm_2.instant {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if alarm_1.set_at >= alarm_1.instant && alarm_2.set_at >= alarm_2.instant {
|
|
||||||
return alarm_1.instant > alarm_2.instant;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
#[test]
|
|
||||||
pub fn duration_bigger_than_frequency() {
|
|
||||||
let x = ParallelSleepDriver::compute_alarm_instant(10000, 0, 1000)
|
|
||||||
.ok()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(x, 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn frequency_bigger_than_duration() {
|
|
||||||
let x = ParallelSleepDriver::compute_alarm_instant(1000, 0, 10000)
|
|
||||||
.ok()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(x, 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn fails_if_duration_is_too_large() {
|
|
||||||
let x =
|
|
||||||
ParallelSleepDriver::compute_alarm_instant(core::usize::MAX, 0, core::usize::MAX - 1);
|
|
||||||
assert!(x.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn fails_if_frequency_is_too_large() {
|
|
||||||
let x =
|
|
||||||
ParallelSleepDriver::compute_alarm_instant(core::usize::MAX - 1, 0, core::usize::MAX);
|
|
||||||
assert!(x.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn alarm_before_systick_wrap_expired() {
|
|
||||||
assert_eq!(
|
|
||||||
super::is_over(
|
|
||||||
super::ActiveTimer {
|
|
||||||
instant: 2u32,
|
|
||||||
set_at: 1u32
|
|
||||||
},
|
|
||||||
3u32
|
|
||||||
),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn alarm_before_systick_wrap_not_expired() {
|
|
||||||
assert_eq!(
|
|
||||||
super::is_over(
|
|
||||||
super::ActiveTimer {
|
|
||||||
instant: 3u32,
|
|
||||||
set_at: 1u32
|
|
||||||
},
|
|
||||||
2u32
|
|
||||||
),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn alarm_after_systick_wrap_expired() {
|
|
||||||
assert_eq!(
|
|
||||||
super::is_over(
|
|
||||||
super::ActiveTimer {
|
|
||||||
instant: 1u32,
|
|
||||||
set_at: 3u32
|
|
||||||
},
|
|
||||||
2u32
|
|
||||||
),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn alarm_after_systick_wrap_time_before_systick_wrap_not_expired() {
|
|
||||||
assert_eq!(
|
|
||||||
super::is_over(
|
|
||||||
super::ActiveTimer {
|
|
||||||
instant: 1u32,
|
|
||||||
set_at: 3u32
|
|
||||||
},
|
|
||||||
4u32
|
|
||||||
),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn alarm_after_systick_wrap_time_after_systick_wrap_not_expired() {
|
|
||||||
assert_eq!(
|
|
||||||
super::is_over(
|
|
||||||
super::ActiveTimer {
|
|
||||||
instant: 1u32,
|
|
||||||
set_at: 3u32
|
|
||||||
},
|
|
||||||
0u32
|
|
||||||
),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn left_later_than_the_other_both_not_wrapped() {
|
|
||||||
let later = super::ActiveTimer {
|
|
||||||
instant: 3u32,
|
|
||||||
set_at: 1u32,
|
|
||||||
};
|
|
||||||
let earlier = super::ActiveTimer {
|
|
||||||
instant: 2u32,
|
|
||||||
set_at: 1u32,
|
|
||||||
};
|
|
||||||
assert_eq!(super::left_is_later(later, earlier), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn right_later_than_the_other_both_not_wrapped() {
|
|
||||||
let later = super::ActiveTimer {
|
|
||||||
instant: 2u32,
|
|
||||||
set_at: 1u32,
|
|
||||||
};
|
|
||||||
let earlier = super::ActiveTimer {
|
|
||||||
instant: 3u32,
|
|
||||||
set_at: 1u32,
|
|
||||||
};
|
|
||||||
assert_eq!(super::left_is_later(later, earlier), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn left_later_left_wrapped() {
|
|
||||||
let later = super::ActiveTimer {
|
|
||||||
instant: 1u32,
|
|
||||||
set_at: 3u32,
|
|
||||||
};
|
|
||||||
let earlier = super::ActiveTimer {
|
|
||||||
instant: 2u32,
|
|
||||||
set_at: 1u32,
|
|
||||||
};
|
|
||||||
assert_eq!(super::left_is_later(later, earlier), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn right_later_right_wrapped() {
|
|
||||||
let later = super::ActiveTimer {
|
|
||||||
instant: 3u32,
|
|
||||||
set_at: 1u32,
|
|
||||||
};
|
|
||||||
let earlier = super::ActiveTimer {
|
|
||||||
instant: 1u32,
|
|
||||||
set_at: 3u32,
|
|
||||||
};
|
|
||||||
assert_eq!(super::left_is_later(later, earlier), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn left_later_both_wrapped() {
|
|
||||||
let later = super::ActiveTimer {
|
|
||||||
instant: 2u32,
|
|
||||||
set_at: 3u32,
|
|
||||||
};
|
|
||||||
let earlier = super::ActiveTimer {
|
|
||||||
instant: 1u32,
|
|
||||||
set_at: 3u32,
|
|
||||||
};
|
|
||||||
assert_eq!(super::left_is_later(later, earlier), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn right_later_both_wrapped() {
|
|
||||||
let later = super::ActiveTimer {
|
|
||||||
instant: 1u32,
|
|
||||||
set_at: 3u32,
|
|
||||||
};
|
|
||||||
let earlier = super::ActiveTimer {
|
|
||||||
instant: 2u32,
|
|
||||||
set_at: 3u32,
|
|
||||||
};
|
|
||||||
assert_eq!(super::left_is_later(later, earlier), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn inequality_is_strict() {
|
|
||||||
let later = super::ActiveTimer {
|
|
||||||
instant: 2u32,
|
|
||||||
set_at: 1u32,
|
|
||||||
};
|
|
||||||
let earlier = super::ActiveTimer {
|
|
||||||
instant: 2u32,
|
|
||||||
set_at: 1u32,
|
|
||||||
};
|
|
||||||
assert_eq!(super::left_is_later(later, earlier), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn inequality_is_strict_wrapped() {
|
|
||||||
let later = super::ActiveTimer {
|
|
||||||
instant: 1u32,
|
|
||||||
set_at: 2u32,
|
|
||||||
};
|
|
||||||
let earlier = super::ActiveTimer {
|
|
||||||
instant: 1u32,
|
|
||||||
set_at: 2u32,
|
|
||||||
};
|
|
||||||
assert_eq!(super::left_is_later(later, earlier), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
9
third_party/libtock-drivers/src/util.rs
vendored
Normal file
9
third_party/libtock-drivers/src/util.rs
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use libtock_core::syscalls;
|
||||||
|
|
||||||
|
pub fn yieldk_for<F: Fn() -> bool>(cond: F) {
|
||||||
|
while !cond() {
|
||||||
|
unsafe {
|
||||||
|
syscalls::raw::yieldk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user