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:
kaczmarczyck
2023-05-05 09:55:16 +02:00
committed by GitHub
parent 645c1ba3a7
commit f25cdd6acc
78 changed files with 4079 additions and 4699 deletions

View File

@@ -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"

View File

@@ -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 = []

View File

@@ -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()),
}
}
}

View File

@@ -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(&not_written_yet[..num_bytes_to_print]);
self.count_pending += num_bytes_to_print;
if self.is_full() {
self.flush();
}
not_written_yet = &not_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(())
}
}

View File

@@ -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(())
}
}

View File

@@ -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
}
}
}

View File

@@ -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;

View File

@@ -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)
})
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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
}
}

View File

@@ -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();
}
}
}