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 ) -> Result<&'_ [u8], ExecuteError> {
81 let sequence_num = self.next_seqnum;
82 self.next_seqnum = self.next_seqnum.wrapping_add(1);
83
84 self.transport
85 .send_frame(write_operation, sequence_num, group_id, command_id, data)?;
86
87 self.transport
88 .receive_frame(
89 &mut self.receive_buffer,
90 write_operation,
91 sequence_num,
92 group_id,
93 command_id,
94 )
95 .map_err(Into::into)
96 }
97
98 fn transceive_command_with_retries(
99 &mut self,
100 write_operation: bool,
101 group_id: u16,
102 command_id: u8,
103 data: &[u8],
104 num_retries: u8,
105 ) -> Result<&'_ [u8], ExecuteError> {
106 let mut this = self;
107
108 let mut counter = 0;
109
110 polonius_loop!(|this| -> Result<&'polonius [u8], ExecuteError> {
111 let result = this.transceive_command(write_operation, group_id, command_id, data);
112
113 if counter >= num_retries {
114 polonius_return!(result)
115 }
116 counter += 1;
117
118 match result {
119 Ok(_) => polonius_return!(result),
120 Err(e) => {
121 let mut lowest_err: &dyn std::error::Error = &e;
122 while let Some(lower_err) = lowest_err.source() {
123 lowest_err = lower_err;
124 }
125 log::warn!("Retry transmission, error occurred: {lowest_err}");
126 }
127 }
128 })
129 }
130}
131
132impl Connection {
133 pub fn new<T: IntoTransport>(transport: T) -> Self {
135 Self {
136 inner: Mutex::new(Inner {
137 transceiver: Transceiver {
138 transport: transport.into_transport(),
139 next_seqnum: rand::random(),
140 receive_buffer: Box::new([0; SMP_TRANSFER_BUFFER_SIZE]),
141 },
142 send_buffer: Box::new([0; SMP_BODY_MAX_SIZE]),
143 retries: DEFAULT_RETRIES,
144 }),
145 }
146 }
147
148 pub fn max_transport_frame_size(&self) -> usize {
151 self.inner
152 .lock()
153 .unwrap()
154 .transceiver
155 .transport
156 .max_smp_frame_size()
157 }
158
159 pub fn set_timeout(
164 &self,
165 timeout: Duration,
166 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
167 self.inner
168 .lock()
169 .unwrap()
170 .transceiver
171 .transport
172 .set_timeout(timeout)
173 }
174
175 pub fn set_retries(&self, retries: u8) {
180 self.inner.lock().unwrap().retries = retries;
181 }
182
183 pub fn execute_command<R: McuMgrCommand>(
185 &self,
186 request: &R,
187 ) -> Result<R::Response, ExecuteError> {
188 self.execute_command_impl(request, true)
189 }
190
191 pub fn execute_command_without_retries<R: McuMgrCommand>(
195 &self,
196 request: &R,
197 ) -> Result<R::Response, ExecuteError> {
198 self.execute_command_impl(request, false)
199 }
200
201 fn execute_command_impl<R: McuMgrCommand>(
202 &self,
203 request: &R,
204 use_retries: bool,
205 ) -> Result<R::Response, ExecuteError> {
206 let mut lock_guard = self.inner.lock().unwrap();
207 let locked_self: &mut Inner = &mut lock_guard;
208
209 let mut cursor = Cursor::new(locked_self.send_buffer.as_mut_slice());
210 ciborium::into_writer(request.data(), &mut cursor)
211 .into_diagnostic()
212 .map_err(Into::into)
213 .map_err(ExecuteError::EncodeFailed)?;
214 let data_size = cursor.position() as usize;
215 let data = &locked_self.send_buffer[..data_size];
216
217 log::debug!("TX data: {}", hex::encode(data));
218
219 let write_operation = request.is_write_operation();
220 let group_id = request.group_id();
221 let command_id = request.command_id();
222
223 let response = locked_self.transceiver.transceive_command_with_retries(
224 write_operation,
225 group_id,
226 command_id,
227 data,
228 if use_retries { locked_self.retries } else { 0 },
229 )?;
230
231 log::debug!("RX data: {}", hex::encode(response));
232
233 let err: ErrResponse = ciborium::from_reader(Cursor::new(response))
234 .into_diagnostic()
235 .map_err(Into::into)
236 .map_err(ExecuteError::DecodeFailed)?;
237
238 if let Some(ErrResponseV2 { rc, group }) = err.err {
239 return Err(ExecuteError::ErrorResponse(DeviceError::V2 { group, rc }));
240 }
241
242 if let Some(rc) = err.rc {
243 if rc != MCUmgrErr::MGMT_ERR_EOK as i32 {
244 return Err(ExecuteError::ErrorResponse(DeviceError::V1 {
245 rc,
246 rsn: err.rsn,
247 }));
248 }
249 }
250
251 let decoded_response: R::Response = ciborium::from_reader(Cursor::new(response))
252 .into_diagnostic()
253 .map_err(Into::into)
254 .map_err(ExecuteError::DecodeFailed)?;
255
256 Ok(decoded_response)
257 }
258
259 pub fn execute_raw_command(
269 &self,
270 write_operation: bool,
271 group_id: u16,
272 command_id: u8,
273 data: &[u8],
274 use_retries: bool,
275 ) -> Result<Box<[u8]>, ExecuteError> {
276 let mut lock_guard = self.inner.lock().unwrap();
277 let locked_self: &mut Inner = &mut lock_guard;
278
279 locked_self
280 .transceiver
281 .transceive_command_with_retries(
282 write_operation,
283 group_id,
284 command_id,
285 data,
286 if use_retries { locked_self.retries } else { 0 },
287 )
288 .map(|val| val.into())
289 }
290}