mcumgr_toolkit/transport/
mod.rs1use std::time::Duration;
2
3use miette::Diagnostic;
4use thiserror::Error;
5
6use polonius_the_crab::prelude::*;
7
8pub mod serial;
10
11#[cfg(feature = "ble")]
13pub mod ble;
14
15pub mod udp;
17
18#[derive(Debug, PartialEq, Clone, Copy)]
19struct SmpHeader {
20 ver: u8,
21 op: u8,
22 flags: u8,
23 data_length: u16,
24 group_id: u16,
25 sequence_num: u8,
26 command_id: u8,
27}
28
29impl SmpHeader {
30 fn from_bytes(data: [u8; SMP_HEADER_SIZE]) -> Self {
31 Self {
32 ver: (data[0] >> 3) & 0b11,
33 op: data[0] & 0b111,
34 flags: data[1],
35 data_length: u16::from_be_bytes([data[2], data[3]]),
36 group_id: u16::from_be_bytes([data[4], data[5]]),
37 sequence_num: data[6],
38 command_id: data[7],
39 }
40 }
41 fn to_bytes(self) -> [u8; SMP_HEADER_SIZE] {
42 let [length_0, length_1] = self.data_length.to_be_bytes();
43 let [group_id_0, group_id_1] = self.group_id.to_be_bytes();
44 [
45 ((self.ver & 0b11) << 3) | (self.op & 0b111),
46 self.flags,
47 length_0,
48 length_1,
49 group_id_0,
50 group_id_1,
51 self.sequence_num,
52 self.command_id,
53 ]
54 }
55}
56
57pub const SMP_HEADER_SIZE: usize = 8;
59
60pub const SMP_BODY_MAX_SIZE: usize = u16::MAX as usize;
64
65pub const SMP_TRANSFER_BUFFER_SIZE: usize = SMP_HEADER_SIZE + SMP_BODY_MAX_SIZE;
67
68mod smp_op {
69 pub(super) const READ: u8 = 0;
70 pub(super) const READ_RSP: u8 = 1;
71 pub(super) const WRITE: u8 = 2;
72 pub(super) const WRITE_RSP: u8 = 3;
73}
74
75#[derive(Error, Debug, Diagnostic)]
77pub enum SendError {
78 #[error("A timeout occurred")]
80 #[diagnostic(code(mcumgr_toolkit::transport::send::timeout))]
81 Timeout,
82 #[error("Transport error")]
84 #[diagnostic(code(mcumgr_toolkit::transport::send::transport))]
85 TransportError(#[source] Box<dyn std::error::Error + Sync + Send + 'static>),
86 #[error("Given data slice was too big")]
88 #[diagnostic(code(mcumgr_toolkit::transport::send::too_big))]
89 DataTooBig,
90}
91
92#[derive(Error, Debug, Diagnostic)]
94pub enum ReceiveError {
95 #[error("A timeout occurred")]
97 #[diagnostic(code(mcumgr_toolkit::transport::recv::timeout))]
98 Timeout,
99 #[error("Transport error")]
101 #[diagnostic(code(mcumgr_toolkit::transport::recv::transport))]
102 TransportError(#[source] Box<dyn std::error::Error + Sync + Send + 'static>),
103 #[error("Received unexpected response")]
105 #[diagnostic(code(mcumgr_toolkit::transport::recv::unexpected))]
106 UnexpectedResponse,
107 #[error("Received frame that exceeds configured MTU")]
109 #[diagnostic(code(mcumgr_toolkit::transport::recv::too_big))]
110 FrameTooBig,
111 #[error("Failed to decode base64 data")]
113 #[diagnostic(code(mcumgr_toolkit::transport::recv::base64_decode))]
114 Base64DecodeError(#[from] base64::DecodeSliceError),
115}
116
117impl From<std::io::Error> for SendError {
118 fn from(e: std::io::Error) -> Self {
119 if std::io::ErrorKind::TimedOut == e.kind() {
120 Self::Timeout
121 } else {
122 Self::TransportError(e.into())
123 }
124 }
125}
126
127impl From<std::io::Error> for ReceiveError {
128 fn from(e: std::io::Error) -> Self {
129 if std::io::ErrorKind::TimedOut == e.kind() {
130 Self::Timeout
131 } else {
132 Self::TransportError(e.into())
133 }
134 }
135}
136
137#[cfg(feature = "ble")]
138impl From<btleplug::Error> for SendError {
139 fn from(e: btleplug::Error) -> Self {
140 if let btleplug::Error::TimedOut(_) = e {
141 Self::Timeout
142 } else {
143 Self::TransportError(e.into())
144 }
145 }
146}
147
148#[cfg(feature = "ble")]
149impl From<btleplug::Error> for ReceiveError {
150 fn from(e: btleplug::Error) -> Self {
151 if let btleplug::Error::TimedOut(_) = e {
152 Self::Timeout
153 } else {
154 Self::TransportError(e.into())
155 }
156 }
157}
158
159pub trait Transport {
161 fn send_raw_frame(
166 &mut self,
167 header: [u8; SMP_HEADER_SIZE],
168 data: &[u8],
169 ) -> Result<(), SendError>;
170
171 fn recv_raw_frame<'a>(
176 &mut self,
177 buffer: &'a mut [u8; SMP_TRANSFER_BUFFER_SIZE],
178 ) -> Result<&'a [u8], ReceiveError>;
179
180 fn send_frame(
193 &mut self,
194 write_operation: bool,
195 sequence_num: u8,
196 group_id: u16,
197 command_id: u8,
198 data: &[u8],
199 ) -> Result<(), SendError> {
200 let header = SmpHeader {
201 ver: 0b01,
202 op: if write_operation {
203 smp_op::WRITE
204 } else {
205 smp_op::READ
206 },
207 flags: 0,
208 data_length: data.len().try_into().map_err(|_| SendError::DataTooBig)?,
209 group_id,
210 sequence_num,
211 command_id,
212 };
213
214 let header_data = header.to_bytes();
215
216 self.send_raw_frame(header_data, data)
217 }
218
219 fn receive_frame<'a>(
234 &mut self,
235 mut buffer: &'a mut [u8; SMP_TRANSFER_BUFFER_SIZE],
236 write_operation: bool,
237 sequence_num: u8,
238 group_id: u16,
239 command_id: u8,
240 ) -> Result<&'a [u8], ReceiveError> {
241 polonius_loop!(|buffer| -> Result<&'polonius [u8], ReceiveError> {
242 let frame = polonius_try!(self.recv_raw_frame(buffer));
243
244 let (header_data, data) = match frame.split_first_chunk::<SMP_HEADER_SIZE>() {
245 Some(parts) => parts,
246 None => polonius_break!(Err(ReceiveError::UnexpectedResponse)),
247 };
248
249 let header = SmpHeader::from_bytes(*header_data);
250
251 if header.sequence_num != sequence_num {
254 polonius_continue!();
255 }
256
257 let expected_op = if write_operation {
258 smp_op::WRITE_RSP
259 } else {
260 smp_op::READ_RSP
261 };
262
263 if (header.group_id != group_id)
264 || (header.command_id != command_id)
265 || (header.op != expected_op)
266 || (usize::from(header.data_length) != data.len())
267 {
268 polonius_break!(Err(ReceiveError::UnexpectedResponse));
269 }
270
271 polonius_return!(Ok(data));
272 })
273 }
274
275 fn set_timeout(
280 &mut self,
281 timeout: Duration,
282 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
283
284 fn max_smp_frame_size(&self) -> usize {
296 usize::MAX
297 }
298}
299
300pub trait IntoTransport {
302 fn into_transport(self) -> Box<dyn Transport + Send>;
304}
305
306impl<T> IntoTransport for T
307where
308 T: Transport + Send + 'static,
309{
310 fn into_transport(self) -> Box<dyn Transport + Send> {
311 Box::new(self)
312 }
313}
314
315impl IntoTransport for Box<dyn Transport + Send> {
316 fn into_transport(self) -> Box<dyn Transport + Send> {
317 self
318 }
319}