mcumgr_toolkit/transport/
mod.rs1use std::time::Duration;
2
3use miette::Diagnostic;
4use thiserror::Error;
5
6pub mod serial;
8
9pub mod udp;
11
12#[derive(Debug, PartialEq, Clone, Copy)]
13struct SmpHeader {
14 ver: u8,
15 op: u8,
16 flags: u8,
17 data_length: u16,
18 group_id: u16,
19 sequence_num: u8,
20 command_id: u8,
21}
22
23impl SmpHeader {
24 fn from_bytes(data: [u8; SMP_HEADER_SIZE]) -> Self {
25 Self {
26 ver: (data[0] >> 3) & 0b11,
27 op: data[0] & 0b111,
28 flags: data[1],
29 data_length: u16::from_be_bytes([data[2], data[3]]),
30 group_id: u16::from_be_bytes([data[4], data[5]]),
31 sequence_num: data[6],
32 command_id: data[7],
33 }
34 }
35 fn to_bytes(self) -> [u8; SMP_HEADER_SIZE] {
36 let [length_0, length_1] = self.data_length.to_be_bytes();
37 let [group_id_0, group_id_1] = self.group_id.to_be_bytes();
38 [
39 ((self.ver & 0b11) << 3) | (self.op & 0b111),
40 self.flags,
41 length_0,
42 length_1,
43 group_id_0,
44 group_id_1,
45 self.sequence_num,
46 self.command_id,
47 ]
48 }
49}
50
51const SMP_HEADER_SIZE: usize = 8;
52const SMP_TRANSFER_BUFFER_SIZE: usize = u16::MAX as usize;
53
54mod smp_op {
55 pub(super) const READ: u8 = 0;
56 pub(super) const READ_RSP: u8 = 1;
57 pub(super) const WRITE: u8 = 2;
58 pub(super) const WRITE_RSP: u8 = 3;
59}
60
61#[derive(Error, Debug, Diagnostic)]
63pub enum SendError {
64 #[error("A timeout occurred")]
66 #[diagnostic(code(mcumgr_toolkit::transport::send::timeout))]
67 Timeout,
68 #[error("Transport error")]
70 #[diagnostic(code(mcumgr_toolkit::transport::send::transport))]
71 TransportError(#[source] Box<dyn std::error::Error + Sync + Send + 'static>),
72 #[error("Given data slice was too big")]
74 #[diagnostic(code(mcumgr_toolkit::transport::send::too_big))]
75 DataTooBig,
76}
77
78#[derive(Error, Debug, Diagnostic)]
80pub enum ReceiveError {
81 #[error("A timeout occurred")]
83 #[diagnostic(code(mcumgr_toolkit::transport::recv::timeout))]
84 Timeout,
85 #[error("Transport error")]
87 #[diagnostic(code(mcumgr_toolkit::transport::recv::transport))]
88 TransportError(#[source] Box<dyn std::error::Error + Sync + Send + 'static>),
89 #[error("Received unexpected response")]
91 #[diagnostic(code(mcumgr_toolkit::transport::recv::unexpected))]
92 UnexpectedResponse,
93 #[error("Received frame that exceeds configured MTU")]
95 #[diagnostic(code(mcumgr_toolkit::transport::recv::too_big))]
96 FrameTooBig,
97 #[error("Failed to decode base64 data")]
99 #[diagnostic(code(mcumgr_toolkit::transport::recv::base64_decode))]
100 Base64DecodeError(#[from] base64::DecodeSliceError),
101}
102
103impl From<std::io::Error> for SendError {
104 fn from(e: std::io::Error) -> Self {
105 if std::io::ErrorKind::TimedOut == e.kind() {
106 Self::Timeout
107 } else {
108 Self::TransportError(e.into())
109 }
110 }
111}
112
113impl From<std::io::Error> for ReceiveError {
114 fn from(e: std::io::Error) -> Self {
115 if std::io::ErrorKind::TimedOut == e.kind() {
116 Self::Timeout
117 } else {
118 Self::TransportError(e.into())
119 }
120 }
121}
122
123pub trait Transport {
125 fn send_raw_frame(
130 &mut self,
131 header: [u8; SMP_HEADER_SIZE],
132 data: &[u8],
133 ) -> Result<(), SendError>;
134
135 fn recv_raw_frame<'a>(
140 &mut self,
141 buffer: &'a mut [u8; SMP_TRANSFER_BUFFER_SIZE],
142 ) -> Result<&'a [u8], ReceiveError>;
143
144 fn send_frame(
157 &mut self,
158 write_operation: bool,
159 sequence_num: u8,
160 group_id: u16,
161 command_id: u8,
162 data: &[u8],
163 ) -> Result<(), SendError> {
164 let header = SmpHeader {
165 ver: 0b01,
166 op: if write_operation {
167 smp_op::WRITE
168 } else {
169 smp_op::READ
170 },
171 flags: 0,
172 data_length: data.len().try_into().map_err(|_| SendError::DataTooBig)?,
173 group_id,
174 sequence_num,
175 command_id,
176 };
177
178 let header_data = header.to_bytes();
179
180 self.send_raw_frame(header_data, data)
181 }
182
183 fn receive_frame<'a>(
198 &mut self,
199 buffer: &'a mut [u8; SMP_TRANSFER_BUFFER_SIZE],
200 write_operation: bool,
201 sequence_num: u8,
202 group_id: u16,
203 command_id: u8,
204 ) -> Result<&'a [u8], ReceiveError> {
205 let data_size = loop {
206 let frame = self.recv_raw_frame(buffer)?;
207
208 let (header_data, data) = frame
209 .split_first_chunk::<SMP_HEADER_SIZE>()
210 .ok_or(ReceiveError::UnexpectedResponse)?;
211
212 let header = SmpHeader::from_bytes(*header_data);
213
214 let expected_op = if write_operation {
215 smp_op::WRITE_RSP
216 } else {
217 smp_op::READ_RSP
218 };
219
220 if header.sequence_num != sequence_num {
223 continue;
224 }
225
226 if (header.group_id != group_id)
227 || (header.command_id != command_id)
228 || (header.op != expected_op)
229 || (usize::from(header.data_length) != data.len())
230 {
231 return Err(ReceiveError::UnexpectedResponse);
232 }
233
234 break data.len();
235 };
236
237 Ok(&buffer[SMP_HEADER_SIZE..SMP_HEADER_SIZE + data_size])
238 }
239
240 fn set_timeout(
245 &mut self,
246 timeout: Duration,
247 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
248
249 fn max_smp_frame_size(&self) -> usize {
258 usize::MAX
259 }
260}