Tock V2 port - rebased and updated (#620)
* Changes from #580 * fixes USB cancel panic * style fixes * Update src/env/tock/storage.rs Co-authored-by: Zach Halvorsen <zhalvorsen@google.com> --------- Co-authored-by: Zach Halvorsen <zhalvorsen@google.com>
This commit is contained in:
82
third_party/lang-items/Cargo.lock
generated
vendored
82
third_party/lang-items/Cargo.lock
generated
vendored
@@ -6,32 +6,53 @@ version = 3
|
||||
name = "lang_items"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_core",
|
||||
"libtock_console",
|
||||
"libtock_drivers",
|
||||
"libtock_leds",
|
||||
"libtock_low_level_debug",
|
||||
"libtock_platform",
|
||||
"libtock_runtime",
|
||||
"linked_list_allocator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_codegen"
|
||||
name = "libtock_console"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_codegen",
|
||||
"libtock_platform",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_drivers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_core",
|
||||
"libtock_console",
|
||||
"libtock_platform",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_leds"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_platform",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_low_level_debug"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_platform",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_platform"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "libtock_runtime"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_platform",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -39,38 +60,3 @@ name = "linked_list_allocator"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e322f259d225fbae43a1b053b2dc6a5968a6bdf8b205f5de684dab485b95030e"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
|
||||
16
third_party/lang-items/Cargo.toml
vendored
16
third_party/lang-items/Cargo.toml
vendored
@@ -8,10 +8,22 @@ authors = [
|
||||
license = "MIT/Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(any(target_arch = "arm", target_arch = "riscv32"))'.dependencies.libtock_runtime]
|
||||
path = "../../third_party/libtock-rs/runtime"
|
||||
default-features = false
|
||||
features = ["no_auto_layout", "alloc_init"]
|
||||
|
||||
[dependencies]
|
||||
libtock_core = { path = "../../third_party/libtock-rs/core", default-features = false, features = ["alloc_init", "custom_panic_handler", "custom_alloc_error_handler"] }
|
||||
libtock_drivers = { path = "../libtock-drivers" }
|
||||
linked_list_allocator = { version = "0.10.2", default-features = false, features = ["const_mut_refs"] }
|
||||
libtock_platform = { path = "../../third_party/libtock-rs/platform" }
|
||||
libtock_low_level_debug = { path = "../../third_party/libtock-rs/apis/low_level_debug" }
|
||||
libtock_leds = { path = "../../third_party/libtock-rs/apis/leds" }
|
||||
libtock_console = { path = "../../third_party/libtock-rs/apis/console" }
|
||||
|
||||
[dependencies.linked_list_allocator]
|
||||
version = "0.10.4"
|
||||
default-features = false
|
||||
features = ["const_mut_refs"]
|
||||
|
||||
[features]
|
||||
debug_allocations = []
|
||||
|
||||
32
third_party/lang-items/src/allocator.rs
vendored
32
third_party/lang-items/src/allocator.rs
vendored
@@ -9,14 +9,17 @@ use core::sync::atomic;
|
||||
#[cfg(feature = "debug_allocations")]
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
#[cfg(any(feature = "debug_allocations", feature = "panic_console"))]
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::Console;
|
||||
#[cfg(feature = "panic_console")]
|
||||
use libtock_platform::{ErrorCode, Syscalls};
|
||||
use libtock_runtime::TockSyscalls;
|
||||
use linked_list_allocator::Heap;
|
||||
|
||||
static mut HEAP: Heap = Heap::empty();
|
||||
|
||||
#[no_mangle]
|
||||
unsafe fn libtock_alloc_init(app_heap_start: usize, app_heap_size: usize) {
|
||||
HEAP.init(app_heap_start as *mut u8, app_heap_size);
|
||||
unsafe fn libtock_alloc_init(app_heap_bottom: *mut u8, app_heap_size: usize) {
|
||||
HEAP.init(app_heap_bottom, app_heap_size);
|
||||
}
|
||||
|
||||
// With the "debug_allocations" feature, we use `AtomicUsize` to store the
|
||||
@@ -54,7 +57,7 @@ unsafe impl GlobalAlloc for TockAllocator {
|
||||
self.count.fetch_add(1, atomic::Ordering::SeqCst);
|
||||
self.size.fetch_add(layout.size(), atomic::Ordering::SeqCst);
|
||||
writeln!(
|
||||
Console::new(),
|
||||
Console::<TockSyscalls>::writer(),
|
||||
"alloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
|
||||
layout.size(),
|
||||
layout.align(),
|
||||
@@ -73,7 +76,7 @@ unsafe impl GlobalAlloc for TockAllocator {
|
||||
self.count.fetch_sub(1, atomic::Ordering::SeqCst);
|
||||
self.size.fetch_sub(layout.size(), atomic::Ordering::SeqCst);
|
||||
writeln!(
|
||||
Console::new(),
|
||||
Console::<TockSyscalls>::writer(),
|
||||
"dealloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
|
||||
layout.size(),
|
||||
layout.align(),
|
||||
@@ -93,17 +96,20 @@ static ALLOCATOR: TockAllocator = TockAllocator::new();
|
||||
|
||||
#[alloc_error_handler]
|
||||
unsafe fn alloc_error_handler(_layout: Layout) -> ! {
|
||||
util::signal_oom();
|
||||
util::signal_panic();
|
||||
util::Util::<TockSyscalls>::signal_oom();
|
||||
util::Util::<TockSyscalls>::signal_panic();
|
||||
|
||||
#[cfg(feature = "panic_console")]
|
||||
{
|
||||
writeln!(Console::new(), "Couldn't allocate: {:?}", _layout).ok();
|
||||
// Force the kernel to report the panic cause, by reading an invalid address.
|
||||
// The memory protection unit should be setup by the Tock kernel to prevent apps from accessing
|
||||
// address zero.
|
||||
core::ptr::read_volatile(0 as *const usize);
|
||||
writeln!(
|
||||
Console::<TockSyscalls>::writer(),
|
||||
"Couldn't allocate: {:?}",
|
||||
_layout
|
||||
)
|
||||
.ok();
|
||||
TockSyscalls::exit_terminate(ErrorCode::Fail as u32);
|
||||
}
|
||||
|
||||
util::cycle_leds()
|
||||
#[cfg(not(feature = "panic_console"))]
|
||||
util::Util::<TockSyscalls>::cycle_leds()
|
||||
}
|
||||
|
||||
2
third_party/lang-items/src/lib.rs
vendored
2
third_party/lang-items/src/lib.rs
vendored
@@ -10,7 +10,7 @@ mod util;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[no_mangle]
|
||||
unsafe fn libtock_alloc_init(_app_heap_start: usize, _app_heap_size: usize) {
|
||||
unsafe fn libtock_alloc_init(_app_heap_bottom: *mut u8, _app_heap_size: usize) {
|
||||
// Stub so that the symbol is present.
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
29
third_party/lang-items/src/panic_handler.rs
vendored
29
third_party/lang-items/src/panic_handler.rs
vendored
@@ -1,26 +1,25 @@
|
||||
//! Custom panic handler for OpenSK
|
||||
|
||||
use crate::util;
|
||||
#[cfg(feature = "panic_console")]
|
||||
use core::fmt::Write;
|
||||
use core::panic::PanicInfo;
|
||||
#[cfg(feature = "panic_console")]
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::Console;
|
||||
#[allow(unused_imports)]
|
||||
use libtock_platform::{ErrorCode, Syscalls};
|
||||
use libtock_runtime::TockSyscalls;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(_info: &PanicInfo) -> ! {
|
||||
util::signal_panic();
|
||||
fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
|
||||
util::Util::<TockSyscalls>::signal_panic();
|
||||
|
||||
#[cfg(feature = "panic_console")]
|
||||
{
|
||||
let mut console = Console::new();
|
||||
writeln!(console, "{}", _info).ok();
|
||||
console.flush();
|
||||
// Force the kernel to report the panic cause, by reading an invalid address.
|
||||
// The memory protection unit should be setup by the Tock kernel to prevent apps from accessing
|
||||
// address zero.
|
||||
unsafe {
|
||||
core::ptr::read_volatile(0 as *const usize);
|
||||
}
|
||||
let mut writer = Console::<TockSyscalls>::writer();
|
||||
writeln!(writer, "{}", _info).ok();
|
||||
// Exit with a non-zero exit code to indicate failure.
|
||||
TockSyscalls::exit_terminate(ErrorCode::Fail as u32);
|
||||
}
|
||||
|
||||
util::flash_all_leds();
|
||||
#[cfg(not(feature = "panic_console"))]
|
||||
util::Util::<TockSyscalls>::flash_all_leds();
|
||||
}
|
||||
|
||||
86
third_party/lang-items/src/util.rs
vendored
86
third_party/lang-items/src/util.rs
vendored
@@ -1,45 +1,55 @@
|
||||
use libtock_drivers::led;
|
||||
use libtock_drivers::timer::{self, Duration};
|
||||
use libtock_drivers::timer;
|
||||
use libtock_leds::Leds;
|
||||
use libtock_low_level_debug::{AlertCode, LowLevelDebug};
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::Syscalls;
|
||||
use platform::DefaultConfig;
|
||||
|
||||
// Signal a panic using the LowLevelDebug capsule (if available).
|
||||
pub fn signal_panic() {
|
||||
let _ = libtock_core::syscalls::command1_insecure(8, 1, 1);
|
||||
}
|
||||
pub struct Util<S: Syscalls, C: platform::subscribe::Config = DefaultConfig>(S, C);
|
||||
|
||||
// Signal an out-of-memory error using the LowLevelDebug capsule (if available).
|
||||
pub fn signal_oom() {
|
||||
let _ = libtock_core::syscalls::command1_insecure(8, 2, 1);
|
||||
}
|
||||
|
||||
pub fn flash_all_leds() -> ! {
|
||||
// Flash all LEDs (if available). All errors from syscalls are ignored: we are already inside a
|
||||
// panic handler so there is nothing much to do if simple drivers (timer, LEDs) don't work.
|
||||
loop {
|
||||
if let Ok(leds) = led::all() {
|
||||
for led in leds {
|
||||
let _ = led.on();
|
||||
}
|
||||
}
|
||||
let _ = timer::sleep(Duration::from_ms(100));
|
||||
if let Ok(leds) = led::all() {
|
||||
for led in leds {
|
||||
let _ = led.off();
|
||||
}
|
||||
}
|
||||
let _ = timer::sleep(Duration::from_ms(100));
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Util<S, C> {
|
||||
/// Signal a panic using the LowLevelDebug capsule (if available).
|
||||
pub fn signal_panic() {
|
||||
LowLevelDebug::<S>::print_alert_code(AlertCode::Panic);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cycle_leds() -> ! {
|
||||
// Cycle though all LEDs (if available). All errors from syscalls are ignored: we are already
|
||||
// inside an error handler so there is nothing much to do if simple drivers (timer, LEDs) don't
|
||||
// work.
|
||||
loop {
|
||||
if let Ok(leds) = led::all() {
|
||||
for led in leds {
|
||||
let _ = led.on();
|
||||
let _ = timer::sleep(Duration::from_ms(100));
|
||||
let _ = led.off();
|
||||
/// Signal an out-of-memory error using the LowLevelDebug capsule (if available).
|
||||
pub fn signal_oom() {
|
||||
LowLevelDebug::<S>::print_alert_code(AlertCode::WrongLocation);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn flash_all_leds() -> ! {
|
||||
// Flash all LEDs (if available). All errors from syscalls are ignored: we are already inside a
|
||||
// panic handler so there is nothing much to do if simple drivers (timer, LEDs) don't work.
|
||||
loop {
|
||||
if let Ok(led_count) = Leds::<S>::count() {
|
||||
for led in 0..led_count {
|
||||
let _ = Leds::<S>::on(led);
|
||||
}
|
||||
}
|
||||
let _ = timer::Alarm::<S, C>::sleep_for(timer::Milliseconds(100));
|
||||
if let Ok(led_count) = Leds::<S>::count() {
|
||||
for led in 0..led_count {
|
||||
let _ = Leds::<S>::off(led);
|
||||
}
|
||||
}
|
||||
let _ = timer::Alarm::<S, C>::sleep_for(timer::Milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cycle_leds() -> ! {
|
||||
// Cycle though all LEDs (if available). All errors from syscalls are ignored: we are already
|
||||
// inside an error handler so there is nothing much to do if simple drivers (timer, LEDs) don't
|
||||
// work.
|
||||
loop {
|
||||
if let Ok(leds) = Leds::<S>::count() {
|
||||
for led in 0..leds {
|
||||
let _ = Leds::<S>::on(led);
|
||||
let _ = timer::Alarm::<S, C>::sleep_for(timer::Milliseconds(100));
|
||||
let _ = Leds::<S>::off(led);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
51
third_party/libtock-drivers/Cargo.lock
generated
vendored
51
third_party/libtock-drivers/Cargo.lock
generated
vendored
@@ -3,59 +3,20 @@
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "libtock_codegen"
|
||||
name = "libtock_console"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_codegen",
|
||||
"libtock_platform",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_drivers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_core",
|
||||
"libtock_console",
|
||||
"libtock_platform",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
name = "libtock_platform"
|
||||
version = "0.1.0"
|
||||
|
||||
5
third_party/libtock-drivers/Cargo.toml
vendored
5
third_party/libtock-drivers/Cargo.toml
vendored
@@ -9,9 +9,10 @@ license = "MIT/Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libtock_core = { path = "../../third_party/libtock-rs/core" }
|
||||
libtock_console = { path = "../../third_party/libtock-rs/apis/console" }
|
||||
libtock_platform = { path = "../../third_party/libtock-rs/platform" }
|
||||
|
||||
[features]
|
||||
debug_ctap = []
|
||||
verbose_usb = ["debug_ctap"]
|
||||
with_nfc=[]
|
||||
with_nfc = []
|
||||
|
||||
170
third_party/libtock-drivers/src/buttons.rs
vendored
170
third_party/libtock-drivers/src/buttons.rs
vendored
@@ -1,170 +0,0 @@
|
||||
use crate::result::{OtherError, TockResult};
|
||||
use core::marker::PhantomData;
|
||||
use libtock_core::callback::{CallbackSubscription, Consumer};
|
||||
use libtock_core::syscalls;
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x00003;
|
||||
|
||||
mod command_nr {
|
||||
pub const COUNT: usize = 0;
|
||||
pub const ENABLE_INTERRUPT: usize = 1;
|
||||
pub const DISABLE_INTERRUPT: usize = 2;
|
||||
pub const READ: usize = 3;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const SUBSCRIBE_CALLBACK: usize = 0;
|
||||
}
|
||||
|
||||
pub fn with_callback<CB>(callback: CB) -> WithCallback<CB> {
|
||||
WithCallback { callback }
|
||||
}
|
||||
|
||||
pub struct WithCallback<CB> {
|
||||
callback: CB,
|
||||
}
|
||||
|
||||
struct ButtonConsumer;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
impl<CB: FnMut(usize, ButtonState)> WithCallback<CB> {
|
||||
pub fn init(&mut self) -> TockResult<Buttons> {
|
||||
let count = syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?;
|
||||
|
||||
let subscription = syscalls::subscribe::<ButtonConsumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::SUBSCRIBE_CALLBACK,
|
||||
self,
|
||||
)?;
|
||||
|
||||
Ok(Buttons {
|
||||
count: count as usize,
|
||||
subscription,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Buttons<'a> {
|
||||
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,
|
||||
button_count: usize,
|
||||
lifetime: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ButtonIter<'a> {
|
||||
type Item = ButtonHandle<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.curr_button < self.button_count {
|
||||
let item = ButtonHandle {
|
||||
button_num: self.curr_button,
|
||||
lifetime: PhantomData,
|
||||
};
|
||||
self.curr_button += 1;
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ButtonHandle<'a> {
|
||||
button_num: usize,
|
||||
lifetime: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> ButtonHandle<'a> {
|
||||
pub fn enable(&mut self) -> TockResult<Button> {
|
||||
syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::ENABLE_INTERRUPT,
|
||||
self.button_num,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Ok(Button { handle: self })
|
||||
}
|
||||
|
||||
pub fn disable(&mut self) -> TockResult<()> {
|
||||
syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::DISABLE_INTERRUPT,
|
||||
self.button_num,
|
||||
0,
|
||||
)?;
|
||||
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
115
third_party/libtock-drivers/src/console.rs
vendored
115
third_party/libtock-drivers/src/console.rs
vendored
@@ -1,115 +0,0 @@
|
||||
use crate::util;
|
||||
use core::cell::Cell;
|
||||
use core::fmt;
|
||||
use libtock_core::{callback, syscalls};
|
||||
|
||||
const DRIVER_NUMBER: usize = 1;
|
||||
|
||||
mod command_nr {
|
||||
pub const WRITE: usize = 1;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const SET_ALARM: usize = 1;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const SHARE_BUFFER: usize = 1;
|
||||
}
|
||||
|
||||
pub const BUFFER_SIZE: usize = 1024;
|
||||
|
||||
pub struct Console {
|
||||
allow_buffer: [u8; BUFFER_SIZE],
|
||||
count_pending: usize,
|
||||
}
|
||||
|
||||
impl Console {
|
||||
pub fn new() -> Console {
|
||||
Console {
|
||||
allow_buffer: [0; BUFFER_SIZE],
|
||||
count_pending: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
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;
|
||||
|
||||
Console::write_unbuffered(&mut self.allow_buffer[..count]);
|
||||
}
|
||||
|
||||
pub fn write_unbuffered(buf: &mut [u8]) {
|
||||
let count = buf.len();
|
||||
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf);
|
||||
if result.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
let is_written = Cell::new(false);
|
||||
let mut is_written_alarm = || is_written.set(true);
|
||||
let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::SET_ALARM,
|
||||
&mut is_written_alarm,
|
||||
);
|
||||
if subscription.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::WRITE, count, 0);
|
||||
if result_code.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
util::yieldk_for(|| is_written.get());
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Console {
|
||||
fn drop(&mut self) {
|
||||
self.flush();
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for Console {
|
||||
fn write_str(&mut self, string: &str) -> Result<(), fmt::Error> {
|
||||
self.write(string);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
49
third_party/libtock-drivers/src/crp.rs
vendored
49
third_party/libtock-drivers/src/crp.rs
vendored
@@ -1,15 +1,17 @@
|
||||
use crate::result::TockResult;
|
||||
use libtock_core::syscalls;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{DefaultConfig, Syscalls};
|
||||
use platform::ErrorCode;
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x00008;
|
||||
const DRIVER_NUMBER: u32 = 0x00008;
|
||||
|
||||
mod command_nr {
|
||||
pub const AVAILABLE: usize = 0;
|
||||
pub const GET_PROTECTION: usize = 1;
|
||||
pub const SET_PROTECTION: usize = 2;
|
||||
pub const AVAILABLE: u32 = 0;
|
||||
pub const GET_PROTECTION: u32 = 1;
|
||||
pub const SET_PROTECTION: u32 = 2;
|
||||
}
|
||||
|
||||
#[derive(PartialOrd, PartialEq)]
|
||||
#[derive(PartialOrd, PartialEq, Eq)]
|
||||
pub enum ProtectionLevel {
|
||||
/// Unsupported feature
|
||||
Unknown = 0,
|
||||
@@ -25,8 +27,10 @@ pub enum ProtectionLevel {
|
||||
FullyLocked = 0xff,
|
||||
}
|
||||
|
||||
impl From<usize> for ProtectionLevel {
|
||||
fn from(value: usize) -> Self {
|
||||
pub struct Crp<S: Syscalls, C: platform::subscribe::Config = DefaultConfig>(S, C);
|
||||
|
||||
impl From<u32> for ProtectionLevel {
|
||||
fn from(value: u32) -> Self {
|
||||
match value {
|
||||
1 => ProtectionLevel::NoProtection,
|
||||
2 => ProtectionLevel::JtagDisabled,
|
||||
@@ -36,17 +40,24 @@ impl From<usize> for ProtectionLevel {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_available() -> TockResult<()> {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::AVAILABLE, 0, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Crp<S, C> {
|
||||
pub fn is_available() -> TockResult<()> {
|
||||
S::command(DRIVER_NUMBER, command_nr::AVAILABLE, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
|
||||
pub fn get_protection() -> TockResult<ProtectionLevel> {
|
||||
let current_level = syscalls::command(DRIVER_NUMBER, command_nr::GET_PROTECTION, 0, 0)?;
|
||||
Ok(current_level.into())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_protection(level: ProtectionLevel) -> TockResult<()> {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::SET_PROTECTION, level as usize, 0)?;
|
||||
Ok(())
|
||||
pub fn get_protection() -> TockResult<ProtectionLevel> {
|
||||
let protection_level = S::command(DRIVER_NUMBER, command_nr::GET_PROTECTION, 0, 0)
|
||||
.to_result::<u32, ErrorCode>()?;
|
||||
|
||||
Ok(protection_level.into())
|
||||
}
|
||||
|
||||
pub fn set_protection(level: ProtectionLevel) -> TockResult<()> {
|
||||
S::command(DRIVER_NUMBER, command_nr::SET_PROTECTION, level as u32, 0)
|
||||
.to_result::<(), ErrorCode>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
84
third_party/libtock-drivers/src/led.rs
vendored
84
third_party/libtock-drivers/src/led.rs
vendored
@@ -1,84 +0,0 @@
|
||||
use crate::result::{OtherError, TockError, TockResult};
|
||||
use libtock_core::syscalls;
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x00002;
|
||||
|
||||
mod command_nr {
|
||||
pub const COUNT: usize = 0;
|
||||
pub const ON: usize = 1;
|
||||
pub const OFF: usize = 2;
|
||||
pub const TOGGLE: usize = 3;
|
||||
}
|
||||
|
||||
pub struct Led {
|
||||
led_num: usize,
|
||||
}
|
||||
|
||||
pub fn count() -> TockResult<usize> {
|
||||
let count = syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
pub fn get(led_num: usize) -> TockResult<Led> {
|
||||
let led_count = count()?;
|
||||
if led_num < led_count {
|
||||
Ok(Led { led_num })
|
||||
} else {
|
||||
Err(TockError::Other(OtherError::OutOfRange))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all() -> TockResult<LedIter> {
|
||||
let led_count = count()?;
|
||||
Ok(LedIter {
|
||||
curr_led: 0,
|
||||
led_count,
|
||||
})
|
||||
}
|
||||
|
||||
impl Led {
|
||||
pub fn set_state(&self, state: bool) -> TockResult<()> {
|
||||
if state {
|
||||
self.on()
|
||||
} else {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LedIter {
|
||||
curr_led: usize,
|
||||
led_count: usize,
|
||||
}
|
||||
|
||||
impl Iterator for LedIter {
|
||||
type Item = Led;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.curr_led < self.led_count {
|
||||
let item = Led {
|
||||
led_num: self.curr_led,
|
||||
};
|
||||
self.curr_led += 1;
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
3
third_party/libtock-drivers/src/lib.rs
vendored
3
third_party/libtock-drivers/src/lib.rs
vendored
@@ -1,9 +1,6 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod buttons;
|
||||
pub mod console;
|
||||
pub mod crp;
|
||||
pub mod led;
|
||||
#[cfg(feature = "with_nfc")]
|
||||
pub mod nfc;
|
||||
pub mod result;
|
||||
|
||||
127
third_party/libtock-drivers/src/nfc.rs
vendored
127
third_party/libtock-drivers/src/nfc.rs
vendored
@@ -1,103 +1,124 @@
|
||||
use crate::result::TockResult;
|
||||
use crate::util;
|
||||
use core::cell::Cell;
|
||||
use core::mem;
|
||||
use libtock_core::{callback, syscalls};
|
||||
use libtock_platform as platform;
|
||||
use platform::{share, AllowRo, AllowRw, DefaultConfig, ErrorCode, Subscribe, Syscalls};
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x30003;
|
||||
const DRIVER_NUMBER: u32 = 0x30003;
|
||||
|
||||
mod command_nr {
|
||||
pub const CHECK: usize = 0;
|
||||
pub const TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const EMULATE: usize = 3;
|
||||
pub const CONFIGURE: usize = 4;
|
||||
pub const CHECK: u32 = 0;
|
||||
pub const TRANSMIT: u32 = 1;
|
||||
pub const RECEIVE: u32 = 2;
|
||||
pub const EMULATE: u32 = 3;
|
||||
pub const CONFIGURE: u32 = 4;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const TRANSMIT: u32 = 1;
|
||||
pub const RECEIVE: u32 = 2;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const TRANSMIT: u32 = 1;
|
||||
pub const RECEIVE: u32 = 2;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RecvOp {
|
||||
pub result_code: usize,
|
||||
pub recv_amount: usize,
|
||||
pub result_code: u32,
|
||||
pub recv_amount: u32,
|
||||
}
|
||||
|
||||
pub struct NfcTag {}
|
||||
pub trait Config:
|
||||
platform::allow_rw::Config + platform::allow_ro::Config + platform::subscribe::Config
|
||||
{
|
||||
}
|
||||
impl<T: platform::allow_rw::Config + platform::allow_ro::Config + platform::subscribe::Config>
|
||||
Config for T
|
||||
{
|
||||
}
|
||||
|
||||
impl NfcTag {
|
||||
pub struct NfcTag<S: Syscalls, C: Config = DefaultConfig>(S, C);
|
||||
|
||||
impl<S: Syscalls, C: Config> NfcTag<S, C> {
|
||||
/// Check the existence of an NFC driver.
|
||||
pub fn setup() -> bool {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).is_ok()
|
||||
S::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).is_success()
|
||||
}
|
||||
|
||||
pub fn enable_emulation() -> bool {
|
||||
NfcTag::emulate(true)
|
||||
NfcTag::<S, C>::emulate(true)
|
||||
}
|
||||
|
||||
pub fn disable_emulation() -> bool {
|
||||
NfcTag::emulate(false)
|
||||
NfcTag::<S, C>::emulate(false)
|
||||
}
|
||||
|
||||
fn emulate(enabled: bool) -> bool {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::EMULATE, enabled as usize, 0).is_ok()
|
||||
S::command(DRIVER_NUMBER, command_nr::EMULATE, enabled as u32, 0).is_success()
|
||||
}
|
||||
|
||||
/// Configure the tag type command.
|
||||
pub fn configure(tag_type: u8) -> bool {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::CONFIGURE, tag_type as usize, 0).is_ok()
|
||||
S::command(DRIVER_NUMBER, command_nr::CONFIGURE, tag_type as u32, 0).is_success()
|
||||
}
|
||||
|
||||
/// 1. Share with the driver a buffer.
|
||||
/// 2. Subscribe to having a successful receive callback.
|
||||
/// 3. Issue the request for reception.
|
||||
pub fn receive(buf: &mut [u8; 256]) -> TockResult<RecvOp> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?;
|
||||
// set callback with 2 arguments, to receive ReturnCode and RX Amount
|
||||
let recv_data = Cell::new(None);
|
||||
let mut callback = |result, amount| {
|
||||
recv_data.set(Some(RecvOp {
|
||||
result_code: result,
|
||||
recv_amount: amount,
|
||||
}))
|
||||
};
|
||||
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::RECEIVE,
|
||||
&mut callback,
|
||||
)?;
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0)?;
|
||||
util::yieldk_for(|| recv_data.get().is_some());
|
||||
mem::drop(subscription);
|
||||
mem::drop(result);
|
||||
Ok(recv_data.get().unwrap())
|
||||
let recv: Cell<Option<(u32, u32)>> = Cell::new(None);
|
||||
share::scope::<
|
||||
(
|
||||
AllowRw<_, DRIVER_NUMBER, { allow_nr::RECEIVE }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow_rw, subscribe) = handle.split();
|
||||
S::allow_rw::<C, DRIVER_NUMBER, { allow_nr::RECEIVE }>(allow_rw, buf)?;
|
||||
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>(subscribe, &recv)?;
|
||||
S::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
|
||||
util::Util::<S>::yieldk_for(|| recv.get().is_some());
|
||||
|
||||
let (result_code, recv_amount) = recv.get().unwrap();
|
||||
let recv_op = RecvOp {
|
||||
result_code,
|
||||
recv_amount,
|
||||
};
|
||||
Ok(recv_op)
|
||||
})
|
||||
}
|
||||
|
||||
/// 1. Share with the driver a buffer containing the app's reply.
|
||||
/// 2. Subscribe to having a successful transmission callback.
|
||||
/// 3. Issue the request for transmitting.
|
||||
pub fn transmit(buf: &mut [u8], amount: usize) -> TockResult<usize> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT, buf)?;
|
||||
pub fn transmit(buf: &mut [u8], amount: u32) -> TockResult<u32> {
|
||||
// set callback with 1 argument, to receive ReturnCode
|
||||
let result_code = Cell::new(None);
|
||||
let mut callback = |result| result_code.set(Some(result));
|
||||
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::TRANSMIT,
|
||||
&mut callback,
|
||||
)?;
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT, amount, 0)?;
|
||||
util::yieldk_for(|| result_code.get().is_some());
|
||||
mem::drop(subscription);
|
||||
mem::drop(result);
|
||||
Ok(result_code.get().unwrap())
|
||||
let result: Cell<Option<(u32,)>> = Cell::new(None);
|
||||
share::scope::<
|
||||
(
|
||||
AllowRo<_, DRIVER_NUMBER, { allow_nr::TRANSMIT }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow_ro, subscribe) = handle.split();
|
||||
S::allow_ro::<C, DRIVER_NUMBER, { allow_nr::TRANSMIT }>(allow_ro, buf)?;
|
||||
|
||||
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>(subscribe, &result)?;
|
||||
S::command(DRIVER_NUMBER, command_nr::TRANSMIT, amount, 0)
|
||||
.to_result::<(), ErrorCode>()?;
|
||||
|
||||
util::Util::<S>::yieldk_for(|| result.get().is_some());
|
||||
|
||||
Ok(result.get().unwrap().0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
61
third_party/libtock-drivers/src/result.rs
vendored
61
third_party/libtock-drivers/src/result.rs
vendored
@@ -1,6 +1,6 @@
|
||||
use core::fmt;
|
||||
|
||||
pub use libtock_core::result::*;
|
||||
use libtock_platform::ErrorCode;
|
||||
|
||||
pub type TockResult<T> = Result<T, TockError>;
|
||||
|
||||
@@ -33,9 +33,7 @@ impl<T> FlexUnwrap<T> for TockResult<T> {
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum TockError {
|
||||
Subscribe(SubscribeError),
|
||||
Command(CommandError),
|
||||
Allow(AllowError),
|
||||
Command(ErrorCode),
|
||||
Format,
|
||||
Other(OtherError),
|
||||
}
|
||||
@@ -44,64 +42,21 @@ pub enum TockError {
|
||||
impl core::fmt::Debug for TockError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
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::Command(error_code) => {
|
||||
f.write_fmt(format_args!("CommandError: {:?}", error_code))
|
||||
}
|
||||
TockError::Format => f.write_str("TockError::Format"),
|
||||
TockError::Other(e) => e.fmt(f),
|
||||
TockError::Other(e) => f.write_fmt(format_args!("OtherError: {:?}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SubscribeError> for TockError {
|
||||
fn from(subscribe_error: SubscribeError) -> Self {
|
||||
TockError::Subscribe(subscribe_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommandError> for TockError {
|
||||
fn from(command_error: CommandError) -> Self {
|
||||
impl From<ErrorCode> for TockError {
|
||||
fn from(command_error: ErrorCode) -> Self {
|
||||
TockError::Command(command_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AllowError> for TockError {
|
||||
fn from(allow_error: AllowError) -> Self {
|
||||
TockError::Allow(allow_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<fmt::Error> for TockError {
|
||||
fn from(fmt::Error: fmt::Error) -> Self {
|
||||
TockError::Format
|
||||
|
||||
95
third_party/libtock-drivers/src/rng.rs
vendored
95
third_party/libtock-drivers/src/rng.rs
vendored
@@ -1,45 +1,78 @@
|
||||
use crate::util;
|
||||
use core::cell::Cell;
|
||||
use libtock_core::{callback, syscalls};
|
||||
//! Userspace interface for easy access to the random number generator
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x40001;
|
||||
use crate::util::Util;
|
||||
use core::cell::Cell;
|
||||
use core::convert::TryInto;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{share, AllowRw, DefaultConfig, Subscribe, Syscalls};
|
||||
use platform::ErrorCode;
|
||||
|
||||
/// Driver number for the random number generator
|
||||
const DRIVER_NUMBER: u32 = 0x40001;
|
||||
|
||||
mod command_nr {
|
||||
pub const REQUEST_RNG: usize = 1;
|
||||
pub const REQUEST_RNG: u32 = 1;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const BUFFER_FILLED: usize = 0;
|
||||
pub const BUFFER_FILLED: u32 = 0;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const SHARE_BUFFER: usize = 0;
|
||||
pub const SHARE_BUFFER: u32 = 0;
|
||||
}
|
||||
|
||||
pub fn fill_buffer(buf: &mut [u8]) -> bool {
|
||||
let buf_len = buf.len();
|
||||
/// System call configuration trait for `Rng`
|
||||
pub trait Config: platform::allow_rw::Config + platform::subscribe::Config {}
|
||||
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf);
|
||||
if result.is_err() {
|
||||
return false;
|
||||
impl<T: platform::allow_rw::Config + platform::subscribe::Config> Config for T {}
|
||||
|
||||
pub struct Rng<S: Syscalls, C: Config = DefaultConfig>(S, C);
|
||||
|
||||
impl<S: Syscalls, C: Config> Rng<S, C> {
|
||||
pub fn fill_buffer(buf: &mut [u8]) -> bool {
|
||||
let buf_len = buf.len();
|
||||
let is_filled = Cell::new(false);
|
||||
|
||||
share::scope::<
|
||||
(
|
||||
AllowRw<_, DRIVER_NUMBER, { allow_nr::SHARE_BUFFER }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::BUFFER_FILLED }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow_rw, subscribe) = handle.split();
|
||||
let result = S::allow_rw::<C, DRIVER_NUMBER, { allow_nr::SHARE_BUFFER }>(allow_rw, buf);
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Automatically sets `is_filled` to true as soon as the buffer is filled.
|
||||
let subscription =
|
||||
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::BUFFER_FILLED }>(
|
||||
subscribe, &is_filled,
|
||||
);
|
||||
if subscription.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Requests the random number generator to fill the buffer.
|
||||
let result_code: Result<(), ErrorCode> = S::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::REQUEST_RNG,
|
||||
buf_len.try_into().unwrap(),
|
||||
0,
|
||||
)
|
||||
.to_result();
|
||||
if result_code.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Yields until the buffer is filled.
|
||||
Util::<S>::yieldk_for(|| is_filled.get());
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
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());
|
||||
true
|
||||
}
|
||||
|
||||
375
third_party/libtock-drivers/src/timer.rs
vendored
375
third_party/libtock-drivers/src/timer.rs
vendored
@@ -1,203 +1,269 @@
|
||||
//! The alarm driver
|
||||
//!
|
||||
//! # Example
|
||||
//! ```
|
||||
//! // Wait for timeout
|
||||
//! Alarm::sleep(Alarm::Milliseconds(2500));
|
||||
//! ```
|
||||
//!
|
||||
//! Adapted from the [libtock-rs](https://github.com/tock/libtock-rs/blob/master/apis/alarm/src/lib.rs) alarm driver interface
|
||||
|
||||
use crate::result::{FlexUnwrap, OtherError, TockError, TockResult};
|
||||
use crate::util;
|
||||
use crate::util::Util;
|
||||
use core::cell::Cell;
|
||||
use core::isize;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, AddAssign, Sub};
|
||||
use libtock_core::callback::{CallbackSubscription, Consumer};
|
||||
use libtock_core::result::{CommandError, EALREADY};
|
||||
use libtock_core::syscalls;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{share, DefaultConfig, ErrorCode, Syscalls};
|
||||
use platform::share::Handle;
|
||||
use platform::subscribe::OneId;
|
||||
use platform::{Subscribe, Upcall};
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x00000;
|
||||
pub struct Alarm<S: Syscalls, C: platform::subscribe::Config = DefaultConfig>(S, C);
|
||||
|
||||
mod command_nr {
|
||||
pub const IS_DRIVER_AVAILABLE: usize = 0;
|
||||
pub const GET_CLOCK_FREQUENCY: usize = 1;
|
||||
pub const GET_CLOCK_VALUE: usize = 2;
|
||||
pub const STOP_ALARM: usize = 3;
|
||||
pub const SET_ALARM: usize = 4;
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Hz(pub u32);
|
||||
|
||||
pub trait Convert {
|
||||
/// Converts a time unit by rounding up.
|
||||
fn to_ticks(self, freq: Hz) -> Ticks;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const SUBSCRIBE_CALLBACK: usize = 0;
|
||||
}
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Ticks(pub u32);
|
||||
|
||||
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),
|
||||
impl Convert for Ticks {
|
||||
fn to_ticks(self, _freq: Hz) -> Ticks {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ticks() -> TockResult<usize> {
|
||||
Ok(syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::GET_CLOCK_VALUE,
|
||||
0,
|
||||
0,
|
||||
)?)
|
||||
pub fn get_ticks<S: Syscalls>() -> TockResult<u32> {
|
||||
Ok(S::command(DRIVER_NUM, command::TIME, 0, 0).to_result::<u32, ErrorCode>()?)
|
||||
}
|
||||
|
||||
pub fn get_clock_frequency() -> TockResult<usize> {
|
||||
Ok(syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::GET_CLOCK_FREQUENCY,
|
||||
0,
|
||||
0,
|
||||
)?)
|
||||
pub fn get_clock_frequency<S: Syscalls>() -> TockResult<u32> {
|
||||
Ok(S::command(DRIVER_NUM, command::FREQUENCY, 0, 0).to_result::<u32, ErrorCode>()?)
|
||||
}
|
||||
|
||||
pub fn with_callback<CB>(callback: CB) -> WithCallback<'static, CB> {
|
||||
WithCallback {
|
||||
callback,
|
||||
clock_frequency: ClockFrequency { hz: 0 },
|
||||
phantom: PhantomData,
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Milliseconds(pub u32);
|
||||
|
||||
impl Convert for Milliseconds {
|
||||
fn to_ticks(self, freq: Hz) -> Ticks {
|
||||
// Saturating multiplication will top out at about 1 hour at 1MHz.
|
||||
// It's large enough for an alarm, and much simpler than failing
|
||||
// or losing precision for short sleeps.
|
||||
|
||||
/// u32::div_ceil is still unstable.
|
||||
fn div_ceil(a: u32, other: u32) -> u32 {
|
||||
let d = a / other;
|
||||
let m = a % other;
|
||||
if m == 0 {
|
||||
d
|
||||
} else {
|
||||
d + 1
|
||||
}
|
||||
}
|
||||
Ticks(div_ceil(self.0.saturating_mul(freq.0), 1000))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WithCallback<'a, CB> {
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Alarm<S, C> {
|
||||
/// Run a check against the console capsule to ensure it is present.
|
||||
///
|
||||
/// Returns number of concurrent notifications supported,
|
||||
/// 0 if unbounded.
|
||||
#[inline(always)]
|
||||
pub fn driver_check() -> Result<u32, ErrorCode> {
|
||||
S::command(DRIVER_NUM, command::DRIVER_CHECK, 0, 0).to_result()
|
||||
}
|
||||
|
||||
pub fn get_frequency() -> Result<Hz, ErrorCode> {
|
||||
S::command(DRIVER_NUM, command::FREQUENCY, 0, 0)
|
||||
.to_result()
|
||||
.map(Hz)
|
||||
}
|
||||
|
||||
pub fn sleep_for<T: Convert>(time: T) -> Result<(), ErrorCode> {
|
||||
let freq = Self::get_frequency()?;
|
||||
let ticks = time.to_ticks(freq);
|
||||
|
||||
let called: Cell<Option<(u32, u32)>> = Cell::new(None);
|
||||
share::scope(|subscribe| {
|
||||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::CALLBACK }>(subscribe, &called)?;
|
||||
|
||||
S::command(DRIVER_NUM, command::SET_RELATIVE, ticks.0, 0)
|
||||
.to_result()
|
||||
.map(|_when: u32| ())?;
|
||||
|
||||
loop {
|
||||
S::yield_wait();
|
||||
if let Some((_when, _ref)) = called.get() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Timer<S: Syscalls, C: platform::subscribe::Config = DefaultConfig> {
|
||||
clock_frequency: Hz,
|
||||
s: PhantomData<S>,
|
||||
c: PhantomData<C>,
|
||||
}
|
||||
|
||||
pub struct WithCallback<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)> {
|
||||
callback: CB,
|
||||
clock_frequency: ClockFrequency,
|
||||
phantom: PhantomData<&'a mut ()>,
|
||||
clock_frequency: Hz,
|
||||
s: PhantomData<S>,
|
||||
c: PhantomData<C>,
|
||||
}
|
||||
|
||||
struct TimerEventConsumer;
|
||||
|
||||
impl<CB: FnMut(ClockValue, Alarm)> Consumer<WithCallback<'_, CB>> for TimerEventConsumer {
|
||||
fn consume(data: &mut WithCallback<CB>, clock_value: usize, alarm_id: usize, _: usize) {
|
||||
(data.callback)(
|
||||
ClockValue {
|
||||
num_ticks: clock_value as isize,
|
||||
clock_frequency: data.clock_frequency,
|
||||
},
|
||||
Alarm { alarm_id },
|
||||
);
|
||||
pub fn with_callback<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)>(
|
||||
callback: CB,
|
||||
) -> TimerUpcallConsumer<S, C, CB> {
|
||||
TimerUpcallConsumer {
|
||||
data: WithCallback {
|
||||
callback,
|
||||
clock_frequency: Hz(0),
|
||||
s: PhantomData,
|
||||
c: PhantomData,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, CB: FnMut(ClockValue, Alarm)> WithCallback<'a, CB> {
|
||||
pub fn init(&'a mut self) -> TockResult<Timer<'a>> {
|
||||
let num_notifications =
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::IS_DRIVER_AVAILABLE, 0, 0)?;
|
||||
pub struct TimerUpcallConsumer<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)> {
|
||||
data: WithCallback<S, C, CB>,
|
||||
}
|
||||
|
||||
impl<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)>
|
||||
Upcall<OneId<DRIVER_NUM, { subscribe::CALLBACK }>> for TimerUpcallConsumer<S, C, CB>
|
||||
{
|
||||
fn upcall(&self, expired_tick_val: u32, _ref_tick: u32, _: u32) {
|
||||
(self.data.callback)(ClockValue::new(
|
||||
expired_tick_val as isize,
|
||||
self.data.clock_frequency,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)>
|
||||
TimerUpcallConsumer<S, C, CB>
|
||||
{
|
||||
/// Initializes the data of the containing [WithCallback], i.e. number of notifications, clock frequency.
|
||||
pub fn init(&mut self) -> TockResult<Timer<S, C>> {
|
||||
// Check if the alarm driver works.
|
||||
S::command(DRIVER_NUM, command::DRIVER_CHECK, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
// Alarm driver only returns success as only a single concurrent timer is supported.
|
||||
|
||||
let clock_frequency =
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0)?;
|
||||
S::command(DRIVER_NUM, command::FREQUENCY, 0, 0).to_result::<u32, ErrorCode>()?;
|
||||
|
||||
if clock_frequency == 0 {
|
||||
if clock_frequency < 1_000 {
|
||||
// The alarm's frequency must be at least 1 kHz.
|
||||
return Err(OtherError::TimerDriverErroneousClockFrequency.into());
|
||||
}
|
||||
|
||||
let clock_frequency = ClockFrequency {
|
||||
hz: clock_frequency,
|
||||
};
|
||||
|
||||
let subscription = syscalls::subscribe::<TimerEventConsumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::SUBSCRIBE_CALLBACK,
|
||||
self,
|
||||
)?;
|
||||
let clock_frequency = Hz(clock_frequency);
|
||||
|
||||
Ok(Timer {
|
||||
num_notifications,
|
||||
clock_frequency,
|
||||
subscription,
|
||||
c: PhantomData,
|
||||
s: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Enables the timer by subscribing for the countdown.
|
||||
/// This needs to be a separate method as it needs to be called in the same `share::scope`
|
||||
pub fn enable<'share, 'a: 'share>(
|
||||
&'a self,
|
||||
handle: Handle<Subscribe<'share, S, DRIVER_NUM, { subscribe::CALLBACK }>>,
|
||||
) -> Result<(), ErrorCode> {
|
||||
// Register the upcall for the timer.
|
||||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::CALLBACK }>(handle, self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Timer<'a> {
|
||||
num_notifications: usize,
|
||||
clock_frequency: ClockFrequency,
|
||||
#[allow(dead_code)] // Used in drop
|
||||
subscription: CallbackSubscription<'a>,
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Default for Timer<S, C> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Timer<'a> {
|
||||
pub fn num_notifications(&self) -> usize {
|
||||
self.num_notifications
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Timer<S, C> {
|
||||
pub fn new() -> Self {
|
||||
let clock_frequency = Alarm::<S, C>::get_frequency().unwrap();
|
||||
|
||||
Self {
|
||||
clock_frequency,
|
||||
s: PhantomData,
|
||||
c: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_frequency(&self) -> ClockFrequency {
|
||||
pub fn sleep(duration: Duration<isize>) -> TockResult<()> {
|
||||
let expired = Cell::new(false);
|
||||
let mut with_callback = with_callback::<S, C, _>(|_| expired.set(true));
|
||||
|
||||
let mut timer = with_callback.init().flex_unwrap();
|
||||
timer.set_alarm(duration).flex_unwrap();
|
||||
|
||||
Util::<S>::yieldk_for(|| expired.get());
|
||||
|
||||
match timer.stop_alarm() {
|
||||
Ok(_) | Err(TockError::Command(ErrorCode::Already)) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the clock frequency of the timer.
|
||||
pub fn clock_frequency(&self) -> Hz {
|
||||
self.clock_frequency
|
||||
}
|
||||
|
||||
pub fn get_current_clock(&self) -> TockResult<ClockValue> {
|
||||
/// Returns the current counter tick value.
|
||||
pub fn get_current_counter_ticks(&self) -> TockResult<ClockValue> {
|
||||
let ticks = S::command(DRIVER_NUM, command::TIME, 0, 0).to_result::<u32, ErrorCode>()?;
|
||||
|
||||
Ok(ClockValue {
|
||||
num_ticks: syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0)?
|
||||
as isize,
|
||||
clock_frequency: self.clock_frequency,
|
||||
num_ticks: ticks as isize,
|
||||
clock_frequency: self.clock_frequency(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stop_alarm(&mut self, alarm: Alarm) -> TockResult<()> {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::STOP_ALARM, alarm.alarm_id, 0)?;
|
||||
/// Stops the currently active alarm.
|
||||
pub fn stop_alarm(&mut self) -> TockResult<()> {
|
||||
S::unsubscribe(DRIVER_NUM, subscribe::CALLBACK);
|
||||
S::command(DRIVER_NUM, command::STOP, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_alarm(&mut self, duration: Duration<isize>) -> TockResult<Alarm> {
|
||||
let now = self.get_current_clock()?;
|
||||
let freq = self.clock_frequency.hz();
|
||||
let duration_ms = duration.ms() as 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(OtherError::TimerDriverDurationOutOfRange.into()),
|
||||
}
|
||||
} else {
|
||||
match (freq / 1000).checked_mul(duration_ms) {
|
||||
Some(y) => y,
|
||||
None => return Err(OtherError::TimerDriverDurationOutOfRange.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let alarm_instant = now.num_ticks() as usize + ticks;
|
||||
pub fn set_alarm(&mut self, duration: Duration<isize>) -> TockResult<()> {
|
||||
let freq = self.clock_frequency;
|
||||
let duration_ms = duration.ms() as u32;
|
||||
let ticks = Milliseconds(duration_ms).to_ticks(freq);
|
||||
|
||||
let alarm_id = syscalls::command(DRIVER_NUMBER, command_nr::SET_ALARM, alarm_instant, 0)?;
|
||||
S::command(DRIVER_NUM, command::SET_RELATIVE, ticks.0, 0)
|
||||
.to_result::<u32, ErrorCode>()
|
||||
.map(|_when| ())?;
|
||||
|
||||
Ok(Alarm { alarm_id })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct ClockFrequency {
|
||||
hz: usize,
|
||||
}
|
||||
|
||||
impl ClockFrequency {
|
||||
pub fn hz(&self) -> usize {
|
||||
self.hz
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ClockValue {
|
||||
num_ticks: isize,
|
||||
clock_frequency: ClockFrequency,
|
||||
clock_frequency: Hz,
|
||||
}
|
||||
|
||||
impl ClockValue {
|
||||
pub const fn new(num_ticks: isize, clock_hz: usize) -> ClockValue {
|
||||
pub const fn new(num_ticks: isize, clock_hz: Hz) -> ClockValue {
|
||||
ClockValue {
|
||||
num_ticks,
|
||||
clock_frequency: ClockFrequency { hz: clock_hz },
|
||||
clock_frequency: clock_hz,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,17 +279,17 @@ impl ClockValue {
|
||||
}
|
||||
|
||||
pub fn ms(&self) -> isize {
|
||||
ClockValue::scale_int(self.num_ticks, 1000, self.clock_frequency.hz() as isize)
|
||||
ClockValue::scale_int(self.num_ticks, 1000, self.clock_frequency.0 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.0 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::scale_int(duration.ms, self.clock_frequency.0 as isize, 1000);
|
||||
ClockValue {
|
||||
num_ticks: self.num_ticks.wrapping_add(duration_ticks),
|
||||
clock_frequency: self.clock_frequency,
|
||||
@@ -243,16 +309,6 @@ impl ClockValue {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Alarm {
|
||||
alarm_id: usize,
|
||||
}
|
||||
|
||||
impl Alarm {
|
||||
pub fn alarm_id(&self) -> usize {
|
||||
self.alarm_id
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Duration<T> {
|
||||
ms: T,
|
||||
@@ -350,3 +406,26 @@ where
|
||||
self.ms += duration.ms();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Driver number and command IDs
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
pub const DRIVER_NUM: u32 = 0;
|
||||
|
||||
// Command IDs
|
||||
#[allow(unused)]
|
||||
mod command {
|
||||
pub const DRIVER_CHECK: u32 = 0;
|
||||
pub const FREQUENCY: u32 = 1;
|
||||
pub const TIME: u32 = 2;
|
||||
pub const STOP: u32 = 3;
|
||||
|
||||
pub const SET_RELATIVE: u32 = 5;
|
||||
pub const SET_ABSOLUTE: u32 = 6;
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub mod subscribe {
|
||||
pub const CALLBACK: u32 = 0;
|
||||
}
|
||||
|
||||
744
third_party/libtock-drivers/src/usb_ctap_hid.rs
vendored
744
third_party/libtock-drivers/src/usb_ctap_hid.rs
vendored
@@ -12,322 +12,510 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use crate::console::Console;
|
||||
use crate::result::{OutOfRangeError, TockError, TockResult};
|
||||
use crate::timer::Duration;
|
||||
use crate::util::Util;
|
||||
use crate::{timer, util};
|
||||
use core::cell::Cell;
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use core::fmt::Write;
|
||||
use libtock_core::result::{CommandError, EALREADY, EBUSY, SUCCESS};
|
||||
use libtock_core::{callback, syscalls};
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use libtock_console::Console;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{share, DefaultConfig, ErrorCode, Syscalls};
|
||||
use platform::share::Handle;
|
||||
use platform::subscribe::OneId;
|
||||
use platform::{AllowRo, AllowRw, Subscribe, Upcall};
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x20009;
|
||||
const DRIVER_NUMBER: u32 = 0x20009;
|
||||
|
||||
/// Ids for commands
|
||||
mod command_nr {
|
||||
pub const CHECK: usize = 0;
|
||||
pub const CONNECT: usize = 1;
|
||||
pub const _TRANSMIT: usize = 2;
|
||||
pub const RECEIVE: usize = 3;
|
||||
pub const TRANSMIT_OR_RECEIVE: usize = 4;
|
||||
pub const CANCEL: usize = 5;
|
||||
pub const CHECK: u32 = 0;
|
||||
pub const CONNECT: u32 = 1;
|
||||
pub const TRANSMIT: u32 = 2;
|
||||
pub const RECEIVE: u32 = 3;
|
||||
pub const TRANSMIT_OR_RECEIVE: u32 = 4;
|
||||
pub const CANCEL: u32 = 5;
|
||||
}
|
||||
|
||||
/// Ids for subscribe numbers
|
||||
mod subscribe_nr {
|
||||
pub const _TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
pub mod callback_status {
|
||||
pub const TRANSMITTED: usize = 1;
|
||||
pub const RECEIVED: usize = 2;
|
||||
}
|
||||
pub const TRANSMIT: u32 = 0;
|
||||
pub const RECEIVE: u32 = 1;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const _TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
mod ro_allow_nr {
|
||||
pub const TRANSMIT: u32 = 0;
|
||||
}
|
||||
|
||||
pub fn setup() -> bool {
|
||||
let result = syscalls::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0);
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let result = syscalls::command(DRIVER_NUMBER, command_nr::CONNECT, 0, 0);
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
mod rw_allow_nr {
|
||||
pub const RECEIVE: u32 = 0;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SendOrRecvStatus {
|
||||
Timeout,
|
||||
Sent,
|
||||
Received(usize),
|
||||
Received(u32),
|
||||
}
|
||||
|
||||
/// Waits to receive a packet.
|
||||
///
|
||||
/// Returns None if the transaction timed out, else its status.
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Receiving packet with timeout of {}ms",
|
||||
timeout_delay.ms(),
|
||||
)
|
||||
.unwrap();
|
||||
pub trait Config:
|
||||
platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config
|
||||
{
|
||||
}
|
||||
|
||||
let result = recv_with_timeout_detail(buf, timeout_delay);
|
||||
impl<T: platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config>
|
||||
Config for T
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
if let Ok(SendOrRecvStatus::Received(endpoint)) = result {
|
||||
pub struct UsbCtapHidListener<F: Fn(u32, u32)>(pub F);
|
||||
|
||||
impl<const SUB_NUM: u32, F: Fn(u32, u32)> Upcall<OneId<DRIVER_NUMBER, SUB_NUM>>
|
||||
for UsbCtapHidListener<F>
|
||||
{
|
||||
fn upcall(&self, direction: u32, endpoint: u32, _: u32) {
|
||||
self.0(direction, endpoint)
|
||||
}
|
||||
}
|
||||
pub struct UsbCtapHid<S: Syscalls, C: Config = DefaultConfig>(S, C);
|
||||
|
||||
impl<S: Syscalls, C: Config> UsbCtapHid<S, C> {
|
||||
/// Register an listener to call with the arguments.
|
||||
///
|
||||
/// Only one listener can be registered at a time.
|
||||
fn register_listener<'share, const SUB_NUM: u32, F: Fn(u32, u32)>(
|
||||
listener: &'share UsbCtapHidListener<F>,
|
||||
subscribe: Handle<Subscribe<'share, S, DRIVER_NUMBER, SUB_NUM>>,
|
||||
) -> Result<(), ErrorCode> {
|
||||
S::subscribe::<_, _, C, DRIVER_NUMBER, SUB_NUM>(subscribe, listener)
|
||||
}
|
||||
|
||||
/// Unregisters the listener.
|
||||
///
|
||||
/// Can be called even if there was no previously registered listener.
|
||||
fn unregister_listener(subscribe_num: u32) {
|
||||
S::unsubscribe(DRIVER_NUMBER, subscribe_num);
|
||||
}
|
||||
|
||||
/// Checks whether the driver is available and tries to setup the connection.
|
||||
pub fn setup() -> bool {
|
||||
let result =
|
||||
S::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).to_result::<(), ErrorCode>();
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let result =
|
||||
S::command(DRIVER_NUMBER, command_nr::CONNECT, 0, 0).to_result::<(), ErrorCode>();
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Waits to receive a packet.
|
||||
///
|
||||
/// Returns None if the transaction timed out, else its status.
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
buf as &[u8],
|
||||
Console::<S>::writer(),
|
||||
"Receiving packet with timeout of {} ms",
|
||||
timeout_delay.ms(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = Self::recv_with_timeout_detail(buf, timeout_delay);
|
||||
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
if let Ok(SendOrRecvStatus::Received(endpoint)) = result {
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
buf as &[u8],
|
||||
endpoint as u8,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Sends a packet to a given endpoint.
|
||||
///
|
||||
/// Returns the transmission status.
|
||||
pub fn send(
|
||||
buf: &[u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: u32,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Sending packet on endpoint {} with timeout of {} ms = {:02x?}",
|
||||
endpoint,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Either sends or receives a packet within a given time.
|
||||
///
|
||||
/// Because USB transactions are initiated by the host, we don't decide whether an IN transaction
|
||||
/// (send for us), an OUT transaction (receive for us), or no transaction at all will happen next.
|
||||
///
|
||||
/// - If an IN transaction happens first, the initial content of buf is sent to the host and the
|
||||
/// Sent status is returned.
|
||||
/// - If an OUT transaction happens first, the content of buf is replaced by the packet received
|
||||
/// from the host and Received status is returned. In that case, the original content of buf is not
|
||||
/// sent to the host, and it's up to the caller to retry sending or to handle the packet received
|
||||
/// from the host.
|
||||
/// If the timeout elapses, return None.
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn send_or_recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: usize,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Sending packet with timeout of {}ms = {:02x?}",
|
||||
timeout_delay.ms(),
|
||||
buf as &[u8]
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = send_or_recv_with_timeout_detail(buf, timeout_delay, endpoint);
|
||||
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
if let Ok(SendOrRecvStatus::Received(received_endpoint)) = result {
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
timeout_delay.ms(),
|
||||
buf as &[u8],
|
||||
received_endpoint,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Self::send_detail(buf, timeout_delay, endpoint)
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?;
|
||||
|
||||
let status = Cell::new(None);
|
||||
let mut alarm = |direction, endpoint| {
|
||||
status.set(Some(match direction {
|
||||
subscribe_nr::callback_status::RECEIVED => Ok(SendOrRecvStatus::Received(endpoint)),
|
||||
// Unknown direction or "transmitted" sent by the kernel.
|
||||
_ => Err(OutOfRangeError.into()),
|
||||
}));
|
||||
};
|
||||
|
||||
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::RECEIVE,
|
||||
&mut alarm,
|
||||
)?;
|
||||
|
||||
// Setup a time-out callback.
|
||||
let mut timeout_callback = timer::with_callback(|_, _| {
|
||||
status.set(Some(Ok(SendOrRecvStatus::Timeout)));
|
||||
});
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
let timeout_alarm = timeout.set_alarm(timeout_delay)?;
|
||||
|
||||
// Trigger USB reception.
|
||||
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0)?;
|
||||
|
||||
util::yieldk_for(|| status.get().is_some());
|
||||
let status = status.get().unwrap();
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm(timeout_alarm) {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(CommandError {
|
||||
return_code: EALREADY,
|
||||
..
|
||||
})) => {
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"The receive timeout already expired, but the callback wasn't executed."
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
/// Either sends or receives a packet within a given time.
|
||||
///
|
||||
/// Because USB transactions are initiated by the host, we don't decide whether an IN transaction
|
||||
/// (send for us), an OUT transaction (receive for us), or no transaction at all will happen next.
|
||||
///
|
||||
/// - If an IN transaction happens first, the initial content of buf is sent to the host and the
|
||||
/// Sent status is returned.
|
||||
/// - If an OUT transaction happens first, the content of buf is replaced by the packet received
|
||||
/// from the host and Received status is returned. In that case, the original content of buf is not
|
||||
/// sent to the host, and it's up to the caller to retry sending or to handle the packet received
|
||||
/// from the host.
|
||||
/// If the timeout elapses, return None.
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn send_or_recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: u32,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap();
|
||||
let result_code =
|
||||
unsafe { syscalls::raw::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) };
|
||||
match result_code {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
SUCCESS | EALREADY => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
EBUSY => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::new(), "Couldn't cancel the USB receive").unwrap();
|
||||
}
|
||||
_ => panic!(
|
||||
"Unexpected error when cancelling USB receive: {:?}",
|
||||
result_code
|
||||
),
|
||||
}
|
||||
}
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Sending packet with timeout of {} ms = {:02x?}",
|
||||
timeout_delay.ms(),
|
||||
buf as &[u8]
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
core::mem::drop(result);
|
||||
core::mem::drop(subscription);
|
||||
core::mem::drop(result_code);
|
||||
status
|
||||
}
|
||||
let result = Self::send_or_recv_with_timeout_detail(buf, timeout_delay, endpoint);
|
||||
|
||||
fn send_or_recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: usize,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf)?;
|
||||
|
||||
let status = Cell::new(None);
|
||||
let mut alarm = |direction, endpoint| {
|
||||
status.set(Some(match direction {
|
||||
subscribe_nr::callback_status::TRANSMITTED => Ok(SendOrRecvStatus::Sent),
|
||||
subscribe_nr::callback_status::RECEIVED => Ok(SendOrRecvStatus::Received(endpoint)),
|
||||
// Unknown direction sent by the kernel.
|
||||
_ => Err(OutOfRangeError.into()),
|
||||
}));
|
||||
};
|
||||
|
||||
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::TRANSMIT_OR_RECEIVE,
|
||||
&mut alarm,
|
||||
)?;
|
||||
|
||||
// Setup a time-out callback.
|
||||
let mut timeout_callback = timer::with_callback(|_, _| {
|
||||
status.set(Some(Ok(SendOrRecvStatus::Timeout)));
|
||||
});
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
let timeout_alarm = timeout.set_alarm(timeout_delay)?;
|
||||
|
||||
// Trigger USB transmission.
|
||||
let result_code = syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::TRANSMIT_OR_RECEIVE,
|
||||
endpoint as usize,
|
||||
0,
|
||||
)?;
|
||||
|
||||
util::yieldk_for(|| status.get().is_some());
|
||||
let status = status.get().unwrap();
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm(timeout_alarm) {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(CommandError {
|
||||
return_code: EALREADY,
|
||||
..
|
||||
})) => {
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"The send/receive timeout already expired, but the callback wasn't executed."
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap();
|
||||
let result_code = unsafe {
|
||||
syscalls::raw::command(DRIVER_NUMBER, command_nr::CANCEL, endpoint as usize, 0)
|
||||
};
|
||||
match result_code {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
SUCCESS | EALREADY => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
EBUSY => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::new(), "Couldn't cancel the transaction").unwrap();
|
||||
}
|
||||
_ => panic!(
|
||||
"Unexpected error when cancelling USB transaction: {:?}",
|
||||
result_code
|
||||
),
|
||||
if let Ok(SendOrRecvStatus::Received(received_endpoint)) = result {
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
buf as &[u8],
|
||||
received_endpoint as u8,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::new(), "Cancelled USB transaction!").unwrap();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
core::mem::drop(result);
|
||||
core::mem::drop(subscription);
|
||||
core::mem::drop(result_code);
|
||||
status
|
||||
fn recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let status: Cell<Option<SendOrRecvStatus>> = Cell::new(None);
|
||||
|
||||
let alarm = UsbCtapHidListener(|direction, endpoint| match direction {
|
||||
subscribe_nr::RECEIVE => status.set(Some(SendOrRecvStatus::Received(endpoint))),
|
||||
// Unknown direction or "transmitted" sent by the kernel
|
||||
_ => status.set(None),
|
||||
});
|
||||
|
||||
let mut timeout_callback =
|
||||
timer::with_callback::<S, C, _>(|_| status.set(Some(SendOrRecvStatus::Timeout)));
|
||||
let status = share::scope::<
|
||||
(
|
||||
AllowRw<_, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>,
|
||||
Subscribe<S, { timer::DRIVER_NUM }, { timer::subscribe::CALLBACK }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow, subscribe_recv, subscribe_timer) = handle.split();
|
||||
S::allow_rw::<C, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>(allow, buf)?;
|
||||
|
||||
Self::register_listener::<{ subscribe_nr::RECEIVE }, _>(&alarm, subscribe_recv)?;
|
||||
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
timeout_callback.enable(subscribe_timer)?;
|
||||
timeout
|
||||
.set_alarm(timeout_delay)
|
||||
.map_err(|_| ErrorCode::Fail)?;
|
||||
|
||||
S::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
|
||||
Util::<S>::yieldk_for(|| status.get().is_some());
|
||||
Self::unregister_listener(subscribe_nr::RECEIVE);
|
||||
|
||||
let status = match status.get() {
|
||||
Some(status) => Ok::<SendOrRecvStatus, TockError>(status),
|
||||
None => Err(OutOfRangeError.into()),
|
||||
}?;
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm() {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(ErrorCode::Already)) => {
|
||||
if matches!(status, SendOrRecvStatus::Timeout) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
write!(Console::<S>::writer(), ".").unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
Ok::<SendOrRecvStatus, TockError>(status)
|
||||
});
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Cancelling USB receive due to timeout"
|
||||
)
|
||||
.unwrap();
|
||||
let result =
|
||||
S::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0).to_result::<(), ErrorCode>();
|
||||
match result {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
Ok(_) | Err(ErrorCode::Already) => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
Err(ErrorCode::Busy) => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::<S>::writer(), "Couldn't cancel the USB receive").unwrap();
|
||||
}
|
||||
Err(e) => panic!("Unexpected error when cancelling USB receive: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
status
|
||||
}
|
||||
|
||||
fn send_detail(
|
||||
buf: &[u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: u32,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let status: Cell<Option<SendOrRecvStatus>> = Cell::new(None);
|
||||
let alarm = UsbCtapHidListener(|direction, _| {
|
||||
let option = match direction {
|
||||
subscribe_nr::TRANSMIT => Some(SendOrRecvStatus::Sent),
|
||||
_ => None,
|
||||
};
|
||||
status.set(option);
|
||||
});
|
||||
|
||||
let mut timeout_callback =
|
||||
timer::with_callback::<S, C, _>(|_| status.set(Some(SendOrRecvStatus::Timeout)));
|
||||
let status = share::scope::<
|
||||
(
|
||||
AllowRo<_, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>,
|
||||
Subscribe<S, { timer::DRIVER_NUM }, { timer::subscribe::CALLBACK }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow, subscribe_send, subscribe_timer) = handle.split();
|
||||
|
||||
S::allow_ro::<C, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>(allow, buf)?;
|
||||
|
||||
Self::register_listener::<{ subscribe_nr::TRANSMIT }, _>(&alarm, subscribe_send)?;
|
||||
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
timeout_callback.enable(subscribe_timer)?;
|
||||
timeout
|
||||
.set_alarm(timeout_delay)
|
||||
.map_err(|_| ErrorCode::Fail)?;
|
||||
|
||||
S::command(DRIVER_NUMBER, command_nr::TRANSMIT, endpoint as u32, 0)
|
||||
.to_result::<(), ErrorCode>()?;
|
||||
|
||||
util::Util::<S>::yieldk_for(|| status.get().is_some());
|
||||
Self::unregister_listener(subscribe_nr::TRANSMIT);
|
||||
|
||||
let status = match status.get() {
|
||||
Some(status) => Ok::<SendOrRecvStatus, TockError>(status),
|
||||
None => Err(OutOfRangeError.into()),
|
||||
}?;
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm() {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(ErrorCode::Already)) => {
|
||||
if matches!(status, SendOrRecvStatus::Timeout) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"The send timeout already expired, but the callback wasn't executed."
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
Ok::<SendOrRecvStatus, TockError>(status)
|
||||
});
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Cancelling USB transmit due to timeout"
|
||||
)
|
||||
.unwrap();
|
||||
let result = S::command(DRIVER_NUMBER, command_nr::CANCEL, endpoint as u32, 0)
|
||||
.to_result::<(), ErrorCode>();
|
||||
match result {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
Ok(_) | Err(ErrorCode::Already) => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
Err(ErrorCode::Busy) => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::<S>::writer(), "Couldn't cancel the USB receive").unwrap();
|
||||
}
|
||||
Err(e) => panic!("Unexpected error when cancelling USB receive: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
status
|
||||
}
|
||||
|
||||
fn send_or_recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: u32,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let status: Cell<Option<SendOrRecvStatus>> = Cell::new(None);
|
||||
let alarm = UsbCtapHidListener(|direction, endpoint| {
|
||||
let option = match direction {
|
||||
subscribe_nr::TRANSMIT => Some(SendOrRecvStatus::Sent),
|
||||
subscribe_nr::RECEIVE => Some(SendOrRecvStatus::Received(endpoint)),
|
||||
_ => None,
|
||||
};
|
||||
status.set(option);
|
||||
});
|
||||
let mut recv_buf = [0; 64];
|
||||
|
||||
// init the time-out callback but don't enable it yet
|
||||
let mut timeout_callback = timer::with_callback::<S, C, _>(|_| {
|
||||
status.set(Some(SendOrRecvStatus::Timeout));
|
||||
});
|
||||
let status = share::scope::<
|
||||
(
|
||||
AllowRo<_, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>,
|
||||
AllowRw<_, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>,
|
||||
Subscribe<_, { timer::DRIVER_NUM }, { timer::subscribe::CALLBACK }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow_ro, allow_rw, sub_send, sub_recv, sub_timer) = handle.split();
|
||||
|
||||
S::allow_ro::<C, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>(allow_ro, buf)?;
|
||||
S::allow_rw::<C, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>(allow_rw, &mut recv_buf)?;
|
||||
|
||||
Self::register_listener::<{ subscribe_nr::TRANSMIT }, _>(&alarm, sub_send)?;
|
||||
Self::register_listener::<{ subscribe_nr::RECEIVE }, _>(&alarm, sub_recv)?;
|
||||
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
timeout_callback.enable(sub_timer)?;
|
||||
timeout.set_alarm(timeout_delay)?;
|
||||
|
||||
// Trigger USB transmission.
|
||||
S::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::TRANSMIT_OR_RECEIVE,
|
||||
endpoint as u32,
|
||||
0,
|
||||
)
|
||||
.to_result::<(), ErrorCode>()?;
|
||||
|
||||
util::Util::<S>::yieldk_for(|| status.get().is_some());
|
||||
Self::unregister_listener(subscribe_nr::TRANSMIT);
|
||||
Self::unregister_listener(subscribe_nr::RECEIVE);
|
||||
|
||||
let status = match status.get() {
|
||||
Some(status) => Ok::<SendOrRecvStatus, TockError>(status),
|
||||
None => Err(OutOfRangeError.into()),
|
||||
}?;
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm() {
|
||||
Ok(_) => (),
|
||||
Err(TockError::Command(ErrorCode::Already)) => {
|
||||
if matches!(status, SendOrRecvStatus::Timeout) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"The send/receive timeout already expired, but the callback wasn't executed."
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
Ok::<SendOrRecvStatus, TockError>(status)
|
||||
});
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Cancelling USB transaction due to timeout"
|
||||
)
|
||||
.unwrap();
|
||||
let result = S::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0)
|
||||
.to_result::<(), ErrorCode>();
|
||||
match result {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
Ok(_) | Err(ErrorCode::Already) => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
Err(ErrorCode::Busy) => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::<S>::writer(), "Couldn't cancel the transaction").unwrap();
|
||||
}
|
||||
Err(e) => panic!("Unexpected error when cancelling USB transaction: {:?}", e),
|
||||
}
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::<S>::writer(), "Cancelled USB transaction!").unwrap();
|
||||
}
|
||||
|
||||
if matches!(status, Ok(SendOrRecvStatus::Received(_))) {
|
||||
buf.copy_from_slice(&recv_buf);
|
||||
}
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
14
third_party/libtock-drivers/src/util.rs
vendored
14
third_party/libtock-drivers/src/util.rs
vendored
@@ -1,9 +1,13 @@
|
||||
use libtock_core::syscalls;
|
||||
use libtock_platform::Syscalls;
|
||||
|
||||
pub fn yieldk_for<F: Fn() -> bool>(cond: F) {
|
||||
while !cond() {
|
||||
unsafe {
|
||||
syscalls::raw::yieldk();
|
||||
pub struct Util<S: Syscalls>(S);
|
||||
|
||||
impl<S: Syscalls> Util<S> {
|
||||
// Yielding manually is discouraged as it conflicts with Rust's safety guarantees.
|
||||
// If you need to wait for a condition, use futures::wait_until and .await.
|
||||
pub fn yieldk_for<F: Fn() -> bool>(cond: F) {
|
||||
while !cond() {
|
||||
S::yield_wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
third_party/libtock-rs
vendored
2
third_party/libtock-rs
vendored
Submodule third_party/libtock-rs updated: 15e837e449...d07eaa2df1
2
third_party/tock
vendored
2
third_party/tock
vendored
Submodule third_party/tock updated: e916444378...44f39d7c8c
Reference in New Issue
Block a user