mcumgr_toolkit/transport/
mod.rs1use std::time::Duration;
2
3use miette::Diagnostic;
4use thiserror::Error;
5
6pub mod serial;
8
9#[cfg(feature = "ble")]
11pub mod ble;
12
13pub mod udp;
15
16#[derive(Debug, PartialEq, Clone, Copy)]
17struct SmpHeader {
18 ver: u8,
19 op: u8,
20 flags: u8,
21 data_length: u16,
22 group_id: u16,
23 sequence_num: u8,
24 command_id: u8,
25}
26
27impl SmpHeader {
28 fn from_bytes(data: [u8; SMP_HEADER_SIZE]) -> Self {
29 Self {
30 ver: (data[0] >> 3) & 0b11,
31 op: data[0] & 0b111,
32 flags: data[1],
33 data_length: u16::from_be_bytes([data[2], data[3]]),
34 group_id: u16::from_be_bytes([data[4], data[5]]),
35 sequence_num: data[6],
36 command_id: data[7],
37 }
38 }
39 fn to_bytes(self) -> [u8; SMP_HEADER_SIZE] {
40 let [length_0, length_1] = self.data_length.to_be_bytes();
41 let [group_id_0, group_id_1] = self.group_id.to_be_bytes();
42 [
43 ((self.ver & 0b11) << 3) | (self.op & 0b111),
44 self.flags,
45 length_0,
46 length_1,
47 group_id_0,
48 group_id_1,
49 self.sequence_num,
50 self.command_id,
51 ]
52 }
53}
54
55const SMP_HEADER_SIZE: usize = 8;
56const SMP_TRANSFER_BUFFER_SIZE: usize = u16::MAX as usize;
57
58mod smp_op {
59 pub(super) const READ: u8 = 0;
60 pub(super) const READ_RSP: u8 = 1;
61 pub(super) const WRITE: u8 = 2;
62 pub(super) const WRITE_RSP: u8 = 3;
63}
64
65#[derive(Error, Debug, Diagnostic)]
67pub enum SendError {
68 #[error("A timeout occurred")]
70 #[diagnostic(code(mcumgr_toolkit::transport::send::timeout))]
71 Timeout,
72 #[error("Transport error")]
74 #[diagnostic(code(mcumgr_toolkit::transport::send::transport))]
75 TransportError(#[source] Box<dyn std::error::Error + Sync + Send + 'static>),
76 #[error("Given data slice was too big")]
78 #[diagnostic(code(mcumgr_toolkit::transport::send::too_big))]
79 DataTooBig,
80}
81
82#[derive(Error, Debug, Diagnostic)]
84pub enum ReceiveError {
85 #[error("A timeout occurred")]
87 #[diagnostic(code(mcumgr_toolkit::transport::recv::timeout))]
88 Timeout,
89 #[error("Transport error")]
91 #[diagnostic(code(mcumgr_toolkit::transport::recv::transport))]
92 TransportError(#[source] Box<dyn std::error::Error + Sync + Send + 'static>),
93 #[error("Received unexpected response")]
95 #[diagnostic(code(mcumgr_toolkit::transport::recv::unexpected))]
96 UnexpectedResponse,
97 #[error("Received frame that exceeds configured MTU")]
99 #[diagnostic(code(mcumgr_toolkit::transport::recv::too_big))]
100 FrameTooBig,
101 #[error("Failed to decode base64 data")]
103 #[diagnostic(code(mcumgr_toolkit::transport::recv::base64_decode))]
104 Base64DecodeError(#[from] base64::DecodeSliceError),
105}
106
107impl From<std::io::Error> for SendError {
108 fn from(e: std::io::Error) -> Self {
109 if std::io::ErrorKind::TimedOut == e.kind() {
110 Self::Timeout
111 } else {
112 Self::TransportError(e.into())
113 }
114 }
115}
116
117impl From<std::io::Error> for ReceiveError {
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
127#[cfg(feature = "ble")]
128impl From<btleplug::Error> for SendError {
129 fn from(e: btleplug::Error) -> Self {
130 if let btleplug::Error::TimedOut(_) = e {
131 Self::Timeout
132 } else {
133 Self::TransportError(e.into())
134 }
135 }
136}
137
138#[cfg(feature = "ble")]
139impl From<btleplug::Error> for ReceiveError {
140 fn from(e: btleplug::Error) -> Self {
141 if let btleplug::Error::TimedOut(_) = e {
142 Self::Timeout
143 } else {
144 Self::TransportError(e.into())
145 }
146 }
147}
148
149pub trait Transport {
151 fn send_raw_frame(
156 &mut self,
157 header: [u8; SMP_HEADER_SIZE],
158 data: &[u8],
159 ) -> Result<(), SendError>;
160
161 fn recv_raw_frame<'a>(
166 &mut self,
167 buffer: &'a mut [u8; SMP_TRANSFER_BUFFER_SIZE],
168 ) -> Result<&'a [u8], ReceiveError>;
169
170 fn send_frame(
183 &mut self,
184 write_operation: bool,
185 sequence_num: u8,
186 group_id: u16,
187 command_id: u8,
188 data: &[u8],
189 ) -> Result<(), SendError> {
190 let header = SmpHeader {
191 ver: 0b01,
192 op: if write_operation {
193 smp_op::WRITE
194 } else {
195 smp_op::READ
196 },
197 flags: 0,
198 data_length: data.len().try_into().map_err(|_| SendError::DataTooBig)?,
199 group_id,
200 sequence_num,
201 command_id,
202 };
203
204 let header_data = header.to_bytes();
205
206 self.send_raw_frame(header_data, data)
207 }
208
209 fn receive_frame<'a>(
224 &mut self,
225 buffer: &'a mut [u8; SMP_TRANSFER_BUFFER_SIZE],
226 write_operation: bool,
227 sequence_num: u8,
228 group_id: u16,
229 command_id: u8,
230 ) -> Result<&'a [u8], ReceiveError> {
231 let data_size = loop {
232 let frame = self.recv_raw_frame(buffer)?;
233
234 let (header_data, data) = frame
235 .split_first_chunk::<SMP_HEADER_SIZE>()
236 .ok_or(ReceiveError::UnexpectedResponse)?;
237
238 let header = SmpHeader::from_bytes(*header_data);
239
240 let expected_op = if write_operation {
241 smp_op::WRITE_RSP
242 } else {
243 smp_op::READ_RSP
244 };
245
246 if header.sequence_num != sequence_num {
249 continue;
250 }
251
252 if (header.group_id != group_id)
253 || (header.command_id != command_id)
254 || (header.op != expected_op)
255 || (usize::from(header.data_length) != data.len())
256 {
257 return Err(ReceiveError::UnexpectedResponse);
258 }
259
260 break data.len();
261 };
262
263 Ok(&buffer[SMP_HEADER_SIZE..SMP_HEADER_SIZE + data_size])
264 }
265
266 fn set_timeout(
271 &mut self,
272 timeout: Duration,
273 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
274
275 fn max_smp_frame_size(&self) -> usize {
284 usize::MAX
285 }
286}