Add request interrupt mechanism

This commit is contained in:
Sosthène Guédon
2023-06-20 17:31:45 +02:00
committed by sosthene-nitrokey
parent d9eb980da1
commit 67534037c6
3 changed files with 59 additions and 11 deletions
+6
View File
@@ -14,6 +14,8 @@ delog = "0.1"
heapless = "0.7"
heapless-bytes = "0.3"
interchange = "0.3.0"
ref-swap = "0.1.0"
trussed = "0.1.0"
[features]
default = []
@@ -25,3 +27,7 @@ log-info = []
log-debug = []
log-warn = []
log-error = []
[patch.crates-io]
ref-swap = { git = "https://github.com/nitrokey/ref-swap.git", rev = "de0330e85b479074ae03bcc05888cfeff682f61e" }
trussed = { git = "https://github.com/sosthene-nitrokey/trussed.git", rev = "6f095e14b27bd58ab8fa56cf6c616266d92fbfb4" }
+8 -1
View File
@@ -1,10 +1,17 @@
use trussed::interrupt::InterruptFlag;
pub use crate::command::Command;
pub use crate::types::{AppResult, Error, Message};
/// trait interface for a CTAPHID application.
/// The application chooses which commands to register to, and will be called upon
/// when the commands are received in the CTAPHID layer. Only one application can be registered to a particular command.
pub trait App {
pub trait App<'interrupt> {
/// Get access to the app interrupter
fn interrupt(&self) -> Option<&'interrupt InterruptFlag> {
None
}
/// Define which CTAPHID commands to register to.
fn commands(&self) -> &'static [Command];
+45 -10
View File
@@ -1,19 +1,41 @@
use core::sync::atomic::Ordering;
use crate::app::App;
use crate::types::{Command, Error, InterchangeResponse, Message, Responder};
pub struct Dispatch<'pipe> {
use trussed::interrupt::InterruptFlag;
use ref_swap::OptionRefSwap;
pub struct Dispatch<'pipe, 'interrupt> {
responder: Responder<'pipe>,
interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>,
}
impl<'pipe> Dispatch<'pipe> {
impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> {
pub fn new(responder: Responder<'pipe>) -> Self {
Dispatch { responder }
Dispatch {
responder,
interrupt: None,
}
}
}
impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> {
pub fn with_interrupt(
responder: Responder<'pipe>,
interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>,
) -> Self {
Dispatch {
responder,
interrupt,
}
}
fn find_app<'a, 'b>(
command: Command,
apps: &'a mut [&'b mut dyn App],
) -> Option<&'a mut &'b mut dyn App> {
apps: &'a mut [&'b mut dyn App<'interrupt>],
) -> Option<&'a mut &'b mut dyn App<'interrupt>> {
apps.iter_mut()
.find(|app| app.commands().contains(&command))
}
@@ -52,7 +74,7 @@ impl<'pipe> Dispatch<'pipe> {
}
#[inline(never)]
fn call_app(&mut self, app: &mut dyn App, command: Command, request: &Message) {
fn call_app(&mut self, app: &mut dyn App<'interrupt>, command: Command, request: &Message) {
let response_buffer = self
.responder
.response_mut()
@@ -61,16 +83,29 @@ impl<'pipe> Dispatch<'pipe> {
.as_mut()
.unwrap();
if let Err(error) = app.call(command, request, response_buffer) {
self.reply_with_error(error);
// Cancellation is best-effort, and not relevant for actual synchronisation, so relaxed is used
let res =
if let (Some(app_interrupt), Some(interrupt_ptr)) = (app.interrupt(), self.interrupt) {
app_interrupt.set_working();
interrupt_ptr.store(Some(app_interrupt), Ordering::Relaxed);
let res = app.call(command, request, response_buffer);
app_interrupt.set_idle();
interrupt_ptr.store(None, Ordering::Relaxed);
res
} else {
app.call(command, request, response_buffer)
};
info_now!("Got res: {:?}", res);
if let Err(error) = res {
self.reply_with_error(error)
} else {
self.send_reply_or_cancel()
}
}
#[inline(never)]
pub fn poll<'a>(&mut self, apps: &mut [&'a mut dyn App]) -> bool {
info!("ctaphid sees state: {:?}", self.responder.state());
pub fn poll(&mut self, apps: &mut [&mut dyn App<'interrupt>]) -> bool {
let maybe_request = self.responder.take_request();
if let Some((command, message)) = maybe_request {
// info_now!("cmd: {}", u8::from(command));