Skip to content

Make Dispatch generic over buffer size and release v0.3.0 #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions dispatch/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

-

## [0.3.0] - 2025-03-21

- Make `Dispatch` generic over the buffer size, rename `MESSAGE_SIZE` to `DEFAULT_MESSAGE_SIZE`, remove the `Message` type and add a `DefaultDispatch` type alias for `Dispatch<_, _, DEFAULT_MESSAGE_SIZE>`.

## [0.2.0] - 2025-01-08

- Optimize stack usage of `Dispatch::poll`
Expand Down
3 changes: 2 additions & 1 deletion dispatch/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ctaphid-dispatch"
version = "0.2.0"
version = "0.3.0"
description = "Dispatch layer after usbd-ctaphid"

authors.workspace = true
Expand All @@ -22,6 +22,7 @@ std = ["delog/std"]

log-all = []
log-none = []
log-trace = []
log-info = []
log-debug = []
log-warn = []
Expand Down
33 changes: 15 additions & 18 deletions dispatch/src/dispatch.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
use core::sync::atomic::Ordering;

use crate::types::{InterchangeResponse, Message, Responder, MESSAGE_SIZE};
use crate::types::{InterchangeResponse, Responder};

use ctaphid_app::{App, Command, Error};
use heapless_bytes::Bytes;
use ref_swap::OptionRefSwap;
use trussed_core::InterruptFlag;

pub struct Dispatch<'pipe, 'interrupt> {
responder: Responder<'pipe>,
pub struct Dispatch<'pipe, 'interrupt, const N: usize> {
responder: Responder<'pipe, N>,
interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>,
}

impl<'pipe> Dispatch<'pipe, '_> {
pub fn new(responder: Responder<'pipe>) -> Self {
impl<'pipe, const N: usize> Dispatch<'pipe, '_, N> {
pub fn new(responder: Responder<'pipe, N>) -> Self {
Dispatch {
responder,
interrupt: None,
}
}
}

impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> {
impl<'pipe, 'interrupt, const N: usize> Dispatch<'pipe, 'interrupt, N> {
pub fn with_interrupt(
responder: Responder<'pipe>,
responder: Responder<'pipe, N>,
interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>,
) -> Self {
Dispatch {
Expand All @@ -33,8 +34,8 @@ impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> {

fn find_app<'a, 'b>(
command: Command,
apps: &'a mut [&'b mut dyn App<'interrupt, MESSAGE_SIZE>],
) -> Option<&'a mut &'b mut dyn App<'interrupt, MESSAGE_SIZE>> {
apps: &'a mut [&'b mut dyn App<'interrupt, N>],
) -> Option<&'a mut &'b mut dyn App<'interrupt, N>> {
apps.iter_mut()
.find(|app| app.commands().contains(&command))
}
Expand All @@ -53,7 +54,7 @@ impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> {
self.reply_or_cancel(InterchangeResponse(Err(error)))
}

fn reply_or_cancel(&mut self, response: InterchangeResponse) {
fn reply_or_cancel(&mut self, response: InterchangeResponse<N>) {
if self.responder.respond(response).is_ok() {
return;
}
Expand All @@ -62,6 +63,7 @@ impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> {
panic!("Unexpected state: {:?}", self.responder.state());
}
}

fn send_reply_or_cancel(&mut self) {
if self.responder.send_response().is_ok() {
return;
Expand All @@ -73,12 +75,7 @@ impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> {
}

#[inline(never)]
fn call_app(
&mut self,
app: &mut dyn App<'interrupt, MESSAGE_SIZE>,
command: Command,
request: &Message,
) {
fn call_app(&mut self, app: &mut dyn App<'interrupt, N>, command: Command, request: &Bytes<N>) {
let response_buffer = self
.responder
.response_mut()
Expand Down Expand Up @@ -109,9 +106,9 @@ impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> {
}

#[inline(never)]
pub fn poll(&mut self, apps: &mut [&mut dyn App<'interrupt, MESSAGE_SIZE>]) -> bool {
pub fn poll(&mut self, apps: &mut [&mut dyn App<'interrupt, N>]) -> bool {
// We could call take_request directly, but for some reason this doubles stack usage.
let mut message_buffer = Message::new();
let mut message_buffer = Bytes::new();
if let Ok((command, message)) = self.responder.request() {
// info_now!("cmd: {}", u8::from(command));
// info_now!("cmd: {:?}", command);
Expand Down
4 changes: 3 additions & 1 deletion dispatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ mod types;
pub use ctaphid_app as app;

pub use dispatch::Dispatch;
pub use types::{Channel, InterchangeResponse, Message, Requester, Responder, MESSAGE_SIZE};
pub use types::{Channel, InterchangeResponse, Requester, Responder, DEFAULT_MESSAGE_SIZE};

pub type DefaultDispatch<'pipe, 'interrupt> = Dispatch<'pipe, 'interrupt, DEFAULT_MESSAGE_SIZE>;
27 changes: 14 additions & 13 deletions dispatch/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
use ctaphid_app::{Command, Error};
use heapless_bytes::Bytes;

pub const MESSAGE_SIZE: usize = 7609;

pub type Message = Bytes<MESSAGE_SIZE>;
pub const DEFAULT_MESSAGE_SIZE: usize = 7609;

/// Wrapper struct that implements [`Default`][] to be able to use [`response_mut`](interchange::Responder::response_mut)
pub struct InterchangeResponse(pub Result<Message, Error>);
pub struct InterchangeResponse<const N: usize>(pub Result<Bytes<N>, Error>);

impl Default for InterchangeResponse {
impl<const N: usize> Default for InterchangeResponse<N> {
fn default() -> Self {
InterchangeResponse(Ok(Message::new()))
InterchangeResponse(Ok(Default::default()))
}
}

impl From<Result<Message, Error>> for InterchangeResponse {
fn from(value: Result<Message, Error>) -> Self {
impl<const N: usize> From<Result<Bytes<N>, Error>> for InterchangeResponse<N> {
fn from(value: Result<Bytes<N>, Error>) -> Self {
Self(value)
}
}

impl From<InterchangeResponse> for Result<Message, Error> {
fn from(value: InterchangeResponse) -> Self {
impl<const N: usize> From<InterchangeResponse<N>> for Result<Bytes<N>, Error> {
fn from(value: InterchangeResponse<N>) -> Self {
value.0
}
}

pub type Responder<'pipe> = interchange::Responder<'pipe, (Command, Message), InterchangeResponse>;
pub type Requester<'pipe> = interchange::Requester<'pipe, (Command, Message), InterchangeResponse>;
pub type Channel = interchange::Channel<(Command, Message), InterchangeResponse>;
pub type Responder<'pipe, const N: usize> =
interchange::Responder<'pipe, (Command, Bytes<N>), InterchangeResponse<N>>;
pub type Requester<'pipe, const N: usize> =
interchange::Requester<'pipe, (Command, Bytes<N>), InterchangeResponse<N>>;
pub type Channel<const N: usize> =
interchange::Channel<(Command, Bytes<N>), InterchangeResponse<N>>;