1use std::{io::Cursor, sync::Mutex, time::Duration};
2
3use crate::{
4 DEFAULT_RETRIES,
5 commands::{ErrResponse, ErrResponseV2, McuMgrCommand},
6 smp_errors::{DeviceError, MCUmgrErr},
7 transport::{
8 IntoTransport, ReceiveError, SMP_BODY_MAX_SIZE, SMP_TRANSFER_BUFFER_SIZE, SendError,
9 Transport,
10 },
11};
12
13use miette::{Diagnostic, IntoDiagnostic};
14use polonius_the_crab::prelude::*;
15use thiserror::Error;
16
17struct Transceiver {
18 transport: Box<dyn Transport + Send>,
19 next_seqnum: u8,
20 receive_buffer: Box<[u8; SMP_TRANSFER_BUFFER_SIZE]>,
21}
22
23struct Inner {
24 transceiver: Transceiver,
25 send_buffer: Box<[u8; SMP_BODY_MAX_SIZE]>,
26 retries: u8,
27}
28
29pub struct Connection {
34 inner: Mutex<Inner>,
35}
36
37#[derive(Error, Debug, Diagnostic)]
39pub enum ExecuteError {
40 #[error("Sending failed")]
42 #[diagnostic(code(mcumgr_toolkit::connection::execute::send))]
43 SendFailed(#[from] SendError),
44 #[error("Receiving failed")]
46 #[diagnostic(code(mcumgr_toolkit::connection::execute::receive))]
47 ReceiveFailed(#[from] ReceiveError),
48 #[error("CBOR encoding failed")]
50 #[diagnostic(code(mcumgr_toolkit::connection::execute::encode))]
51 EncodeFailed(#[source] Box<dyn miette::Diagnostic + Send + Sync>),
52 #[error("CBOR decoding failed")]
54 #[diagnostic(code(mcumgr_toolkit::connection::execute::decode))]
55 DecodeFailed(#[source] Box<dyn miette::Diagnostic + Send + Sync>),
56 #[error("Device returned error code: {0}")]
58 #[diagnostic(code(mcumgr_toolkit::connection::execute::device_error))]
59 ErrorResponse(DeviceError),
60}
61
62impl ExecuteError {
63 pub fn command_not_supported(&self) -> bool {
65 if let Self::ErrorResponse(DeviceError::V1 { rc, .. }) = self {
66 *rc == MCUmgrErr::MGMT_ERR_ENOTSUP as i32
67 } else {
68 false
69 }
70 }
71}
72
73impl Transceiver {
74 fn transceive_command(
75 &mut self,
76 write_operation: bool,
77 group_id: u16,
78 command_id: u8,
79 data: &[u8],
80 sequence_num: u8,
81 ) -> Result<&'_ [u8], ExecuteError> {
82 self.transport
83 .send_frame(write_operation, sequence_num, group_id, command_id, data)?;
84
85 self.transport
86 .receive_frame(
87 &mut self.receive_buffer,
88 write_operation,
89 sequence_num,
90 group_id,
91 command_id,
92 )
93 .map_err(Into::into)
94 }
95
96 fn transceive_command_with_retries(
97 &mut self,
98 write_operation: bool,
99 group_id: u16,
100 command_id: u8,
101 data: &[u8],
102 num_retries: u8,
103 ) -> Result<&'_ [u8], ExecuteError> {
104 let mut this = self;
105
106 let mut counter = 0;
107
108 let sequence_num = this.next_seqnum;
117 this.next_seqnum = this.next_seqnum.wrapping_add(1);
118
119 polonius_loop!(|this| -> Result<&'polonius [u8], ExecuteError> {
120 let result =
121 this.transceive_command(write_operation, group_id, command_id, data, sequence_num);
122
123 if counter >= num_retries {
124 polonius_return!(result)
125 }
126 counter += 1;
127
128 match result {
129 Ok(_) => polonius_return!(result),
130 Err(e) => {
131 let mut lowest_err: &dyn std::error::Error = &e;
132 while let Some(lower_err) = lowest_err.source() {
133 lowest_err = lower_err;
134 }
135 log::warn!("Retry transmission, error occurred: {lowest_err}");
136 }
137 }
138 })
139 }
140}
141
142impl Connection {
143 pub fn new<T: IntoTransport>(transport: T) -> Self {
145 Self {
146 inner: Mutex::new(Inner {
147 transceiver: Transceiver {
148 transport: transport.into_transport(),
149 next_seqnum: rand::random(),
150 receive_buffer: Box::new([0; SMP_TRANSFER_BUFFER_SIZE]),
151 },
152 send_buffer: Box::new([0; SMP_BODY_MAX_SIZE]),
153 retries: DEFAULT_RETRIES,
154 }),
155 }
156 }
157
158 pub fn max_transport_frame_size(&self) -> usize {
161 self.inner
162 .lock()
163 .unwrap()
164 .transceiver
165 .transport
166 .max_smp_frame_size()
167 }
168
169 pub fn device_rx_buffer_overhead(&self) -> usize {
172 self.inner
173 .lock()
174 .unwrap()
175 .transceiver
176 .transport
177 .device_rx_buffer_overhead()
178 }
179
180 pub fn set_timeout(
185 &self,
186 timeout: Duration,
187 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
188 self.inner
189 .lock()
190 .unwrap()
191 .transceiver
192 .transport
193 .set_timeout(timeout)
194 }
195
196 pub fn set_retries(&self, retries: u8) {
201 self.inner.lock().unwrap().retries = retries;
202 }
203
204 pub fn execute_command<R: McuMgrCommand>(
206 &self,
207 request: &R,
208 ) -> Result<R::Response, ExecuteError> {
209 self.execute_command_impl(request, true)
210 }
211
212 pub fn execute_command_without_retries<R: McuMgrCommand>(
216 &self,
217 request: &R,
218 ) -> Result<R::Response, ExecuteError> {
219 self.execute_command_impl(request, false)
220 }
221
222 fn execute_command_impl<R: McuMgrCommand>(
223 &self,
224 request: &R,
225 use_retries: bool,
226 ) -> Result<R::Response, ExecuteError> {
227 let mut lock_guard = self.inner.lock().unwrap();
228 let locked_self: &mut Inner = &mut lock_guard;
229
230 let mut cursor = Cursor::new(locked_self.send_buffer.as_mut_slice());
231 ciborium::into_writer(request.data(), &mut cursor)
232 .into_diagnostic()
233 .map_err(Into::into)
234 .map_err(ExecuteError::EncodeFailed)?;
235 let data_size = cursor.position() as usize;
236 let data = &locked_self.send_buffer[..data_size];
237
238 log::debug!("TX data: {}", hex::encode(data));
239
240 let write_operation = request.is_write_operation();
241 let group_id = request.group_id();
242 let command_id = request.command_id();
243
244 let response = locked_self.transceiver.transceive_command_with_retries(
245 write_operation,
246 group_id,
247 command_id,
248 data,
249 if use_retries { locked_self.retries } else { 0 },
250 )?;
251
252 log::debug!("RX data: {}", hex::encode(response));
253
254 let err: ErrResponse = ciborium::from_reader(Cursor::new(response))
255 .into_diagnostic()
256 .map_err(Into::into)
257 .map_err(ExecuteError::DecodeFailed)?;
258
259 if let Some(ErrResponseV2 { rc, group }) = err.err {
260 return Err(ExecuteError::ErrorResponse(DeviceError::V2 { group, rc }));
261 }
262
263 if let Some(rc) = err.rc {
264 if rc != MCUmgrErr::MGMT_ERR_EOK as i32 {
265 return Err(ExecuteError::ErrorResponse(DeviceError::V1 {
266 rc,
267 rsn: err.rsn,
268 }));
269 }
270 }
271
272 let decoded_response: R::Response = ciborium::from_reader(Cursor::new(response))
273 .into_diagnostic()
274 .map_err(Into::into)
275 .map_err(ExecuteError::DecodeFailed)?;
276
277 Ok(decoded_response)
278 }
279
280 pub fn execute_raw_command(
290 &self,
291 write_operation: bool,
292 group_id: u16,
293 command_id: u8,
294 data: &[u8],
295 use_retries: bool,
296 ) -> Result<Box<[u8]>, ExecuteError> {
297 let mut lock_guard = self.inner.lock().unwrap();
298 let locked_self: &mut Inner = &mut lock_guard;
299
300 locked_self
301 .transceiver
302 .transceive_command_with_retries(
303 write_operation,
304 group_id,
305 command_id,
306 data,
307 if use_retries { locked_self.retries } else { 0 },
308 )
309 .map(|val| val.into())
310 }
311}