ctaphid_dispatch/
dispatch.rs

1use core::sync::atomic::Ordering;
2
3use crate::types::{InterchangeResponse, Message, Responder, MESSAGE_SIZE};
4
5use ctaphid_app::{App, Command, Error};
6use ref_swap::OptionRefSwap;
7use trussed_core::InterruptFlag;
8
9pub struct Dispatch<'pipe, 'interrupt> {
10    responder: Responder<'pipe>,
11    interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>,
12}
13
14impl<'pipe> Dispatch<'pipe, '_> {
15    pub fn new(responder: Responder<'pipe>) -> Self {
16        Dispatch {
17            responder,
18            interrupt: None,
19        }
20    }
21}
22
23impl<'pipe, 'interrupt> Dispatch<'pipe, 'interrupt> {
24    pub fn with_interrupt(
25        responder: Responder<'pipe>,
26        interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>,
27    ) -> Self {
28        Dispatch {
29            responder,
30            interrupt,
31        }
32    }
33
34    fn find_app<'a, 'b>(
35        command: Command,
36        apps: &'a mut [&'b mut dyn App<'interrupt, MESSAGE_SIZE>],
37    ) -> Option<&'a mut &'b mut dyn App<'interrupt, MESSAGE_SIZE>> {
38        apps.iter_mut()
39            .find(|app| app.commands().contains(&command))
40    }
41
42    // // Using helper here to take potentially large stack burden off of call chain to application.
43    // #[inline(never)]
44    // fn reply_with_request_buffer(&mut self){
45    //     let (_command, message) = self.responder.take_request().unwrap();
46    //     let message = message.clone();
47    //     self.responder.respond(&Ok(message)).expect("responder failed");
48    // }
49
50    // Using helper here to take potentially large stack burden off of call chain to application.
51    #[inline(never)]
52    fn reply_with_error(&mut self, error: Error) {
53        self.reply_or_cancel(InterchangeResponse(Err(error)))
54    }
55
56    fn reply_or_cancel(&mut self, response: InterchangeResponse) {
57        if self.responder.respond(response).is_ok() {
58            return;
59        }
60
61        if self.responder.acknowledge_cancel().is_err() {
62            panic!("Unexpected state: {:?}", self.responder.state());
63        }
64    }
65    fn send_reply_or_cancel(&mut self) {
66        if self.responder.send_response().is_ok() {
67            return;
68        }
69
70        if self.responder.acknowledge_cancel().is_err() {
71            panic!("Unexpected state: {:?}", self.responder.state());
72        }
73    }
74
75    #[inline(never)]
76    fn call_app(
77        &mut self,
78        app: &mut dyn App<'interrupt, MESSAGE_SIZE>,
79        command: Command,
80        request: &Message,
81    ) {
82        let response_buffer = self
83            .responder
84            .response_mut()
85            .expect("App calls should only happen when a respose can be constructed")
86            .0
87            .as_mut()
88            .unwrap();
89
90        // Cancellation is best-effort, and not relevant for actual synchronisation, so relaxed is used
91        let res =
92            if let (Some(app_interrupt), Some(interrupt_ptr)) = (app.interrupt(), self.interrupt) {
93                app_interrupt.set_working();
94                interrupt_ptr.store(Some(app_interrupt), Ordering::Relaxed);
95                let res = app.call(command, request, response_buffer);
96                app_interrupt.set_idle();
97                interrupt_ptr.store(None, Ordering::Relaxed);
98                res
99            } else {
100                app.call(command, request, response_buffer)
101            };
102
103        info_now!("Got res: {:?}", res);
104        if let Err(error) = res {
105            self.reply_with_error(error)
106        } else {
107            self.send_reply_or_cancel()
108        }
109    }
110
111    #[inline(never)]
112    pub fn poll(&mut self, apps: &mut [&mut dyn App<'interrupt, MESSAGE_SIZE>]) -> bool {
113        // We could call take_request directly, but for some reason this doubles stack usage.
114        let mut message_buffer = Message::new();
115        if let Ok((command, message)) = self.responder.request() {
116            // info_now!("cmd: {}", u8::from(command));
117            // info_now!("cmd: {:?}", command);
118
119            message_buffer.extend_from_slice(message).unwrap();
120
121            if let Some(app) = Self::find_app(*command, apps) {
122                // match app.call(command, self.responder.response_mut().unwrap()) {
123                self.call_app(*app, *command, &message_buffer);
124            } else {
125                self.reply_with_error(Error::InvalidCommand);
126            }
127            self.responder.state() == interchange::State::Responded
128        } else {
129            match self.responder.state() {
130                interchange::State::Canceled => self.responder.acknowledge_cancel().is_ok(),
131                interchange::State::Responded => true,
132                _ => false,
133            }
134        }
135    }
136}