ctaphid_dispatch/
dispatch.rs

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