mcumgr_toolkit/transport/
mod.rs1use std::{io, time::Duration};
2
3use miette::Diagnostic;
4use thiserror::Error;
5
6pub mod serial;
8
9#[derive(Debug, PartialEq, Clone, Copy)]
10struct SmpHeader {
11 ver: u8,
12 op: u8,
13 flags: u8,
14 data_length: u16,
15 group_id: u16,
16 sequence_num: u8,
17 command_id: u8,
18}
19
20impl SmpHeader {
21 fn from_bytes(data: [u8; SMP_HEADER_SIZE]) -> Self {
22 Self {
23 ver: (data[0] >> 3) & 0b11,
24 op: data[0] & 0b111,
25 flags: data[1],
26 data_length: u16::from_be_bytes([data[2], data[3]]),
27 group_id: u16::from_be_bytes([data[4], data[5]]),
28 sequence_num: data[6],
29 command_id: data[7],
30 }
31 }
32 fn to_bytes(self) -> [u8; SMP_HEADER_SIZE] {
33 let [length_0, length_1] = self.data_length.to_be_bytes();
34 let [group_id_0, group_id_1] = self.group_id.to_be_bytes();
35 [
36 ((self.ver & 0b11) << 3) | (self.op & 0b111),
37 self.flags,
38 length_0,
39 length_1,
40 group_id_0,
41 group_id_1,
42 self.sequence_num,
43 self.command_id,
44 ]
45 }
46}
47
48const SMP_HEADER_SIZE: usize = 8;
49const SMP_TRANSFER_BUFFER_SIZE: usize = u16::MAX as usize;
50
51mod smp_op {
52 pub(super) const READ: u8 = 0;
53 pub(super) const READ_RSP: u8 = 1;
54 pub(super) const WRITE: u8 = 2;
55 pub(super) const WRITE_RSP: u8 = 3;
56}
57
58#[derive(Error, Debug, Diagnostic)]
60pub enum SendError {
61 #[error("Transport error")]
63 #[diagnostic(code(mcumgr_toolkit::transport::send::transport))]
64 TransportError(#[from] io::Error),
65 #[error("Given data slice was too big")]
67 #[diagnostic(code(mcumgr_toolkit::transport::send::too_big))]
68 DataTooBig,
69}
70
71#[derive(Error, Debug, Diagnostic)]
73pub enum ReceiveError {
74 #[error("Transport error")]
76 #[diagnostic(code(mcumgr_toolkit::transport::recv::transport))]
77 TransportError(#[from] io::Error),
78 #[error("Received unexpected response")]
80 #[diagnostic(code(mcumgr_toolkit::transport::recv::unexpected))]
81 UnexpectedResponse,
82 #[error("Received frame that exceeds configured MTU")]
84 #[diagnostic(code(mcumgr_toolkit::transport::recv::too_big))]
85 FrameTooBig,
86 #[error("Failed to decode base64 data")]
88 #[diagnostic(code(mcumgr_toolkit::transport::recv::base64_decode))]
89 Base64DecodeError(#[from] base64::DecodeSliceError),
90}
91
92pub trait Transport {
94 fn send_raw_frame(
99 &mut self,
100 header: [u8; SMP_HEADER_SIZE],
101 data: &[u8],
102 ) -> Result<(), SendError>;
103
104 fn recv_raw_frame<'a>(
109 &mut self,
110 buffer: &'a mut [u8; SMP_TRANSFER_BUFFER_SIZE],
111 ) -> Result<&'a [u8], ReceiveError>;
112
113 fn send_frame(
126 &mut self,
127 write_operation: bool,
128 sequence_num: u8,
129 group_id: u16,
130 command_id: u8,
131 data: &[u8],
132 ) -> Result<(), SendError> {
133 let header = SmpHeader {
134 ver: 0b01,
135 op: if write_operation {
136 smp_op::WRITE
137 } else {
138 smp_op::READ
139 },
140 flags: 0,
141 data_length: data.len().try_into().map_err(|_| SendError::DataTooBig)?,
142 group_id,
143 sequence_num,
144 command_id,
145 };
146
147 let header_data = header.to_bytes();
148
149 self.send_raw_frame(header_data, data)
150 }
151
152 fn receive_frame<'a>(
167 &mut self,
168 buffer: &'a mut [u8; SMP_TRANSFER_BUFFER_SIZE],
169 write_operation: bool,
170 sequence_num: u8,
171 group_id: u16,
172 command_id: u8,
173 ) -> Result<&'a [u8], ReceiveError> {
174 let data_size = loop {
175 let frame = self.recv_raw_frame(buffer)?;
176
177 let (header_data, data) = frame
178 .split_first_chunk::<SMP_HEADER_SIZE>()
179 .ok_or(ReceiveError::UnexpectedResponse)?;
180
181 let header = SmpHeader::from_bytes(*header_data);
182
183 let expected_op = if write_operation {
184 smp_op::WRITE_RSP
185 } else {
186 smp_op::READ_RSP
187 };
188
189 if header.sequence_num != sequence_num {
192 continue;
193 }
194
195 if (header.group_id != group_id)
196 || (header.command_id != command_id)
197 || (header.op != expected_op)
198 || (usize::from(header.data_length) != data.len())
199 {
200 return Err(ReceiveError::UnexpectedResponse);
201 }
202
203 break data.len();
204 };
205
206 Ok(&buffer[SMP_HEADER_SIZE..SMP_HEADER_SIZE + data_size])
207 }
208
209 fn set_timeout(&mut self, timeout: Duration) -> Result<(), miette::Report>;
214}