Skip to main content

mcumgr_toolkit/transport/
mod.rs

1use std::{io, time::Duration};
2
3use miette::Diagnostic;
4use thiserror::Error;
5
6/// Serial port based transport
7pub 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/// Error while sending a command request
59#[derive(Error, Debug, Diagnostic)]
60pub enum SendError {
61    /// An error occurred in the underlying transport
62    #[error("Transport error")]
63    #[diagnostic(code(mcumgr_toolkit::transport::send::transport))]
64    TransportError(#[from] io::Error),
65    /// Unable to send data because it is too big
66    #[error("Given data slice was too big")]
67    #[diagnostic(code(mcumgr_toolkit::transport::send::too_big))]
68    DataTooBig,
69}
70
71/// Error while receiving a command response
72#[derive(Error, Debug, Diagnostic)]
73pub enum ReceiveError {
74    /// An error occurred in the underlying transport
75    #[error("Transport error")]
76    #[diagnostic(code(mcumgr_toolkit::transport::recv::transport))]
77    TransportError(#[from] io::Error),
78    /// We received a response that did not fit to our request
79    #[error("Received unexpected response")]
80    #[diagnostic(code(mcumgr_toolkit::transport::recv::unexpected))]
81    UnexpectedResponse,
82    /// The response we received is bigger than the configured MTU
83    #[error("Received frame that exceeds configured MTU")]
84    #[diagnostic(code(mcumgr_toolkit::transport::recv::too_big))]
85    FrameTooBig,
86    /// The response we received is not base64 encoded
87    #[error("Failed to decode base64 data")]
88    #[diagnostic(code(mcumgr_toolkit::transport::recv::base64_decode))]
89    Base64DecodeError(#[from] base64::DecodeSliceError),
90}
91
92/// Defines the API of the SMP transport layer
93pub trait Transport {
94    /// Send a raw SMP frame over the bus.
95    ///
96    /// This function must be provided by the implementing struct
97    /// but should not be called directly.
98    fn send_raw_frame(
99        &mut self,
100        header: [u8; SMP_HEADER_SIZE],
101        data: &[u8],
102    ) -> Result<(), SendError>;
103
104    /// Receive a raw SMP frame from the bus.
105    ///
106    /// This function must be provided by the implementing struct
107    /// but should not be called directly.
108    fn recv_raw_frame<'a>(
109        &mut self,
110        buffer: &'a mut [u8; SMP_TRANSFER_BUFFER_SIZE],
111    ) -> Result<&'a [u8], ReceiveError>;
112
113    /// Send an SMP frame over the bus.
114    ///
115    /// # Arguments
116    ///
117    /// * `write_operation` - If the frame contains a write or read operation.
118    /// * `sequence_num` - A sequence number. Must be different every time this function is called.
119    /// * `group_id` - The group ID of the command.
120    /// * `command_id` - The command ID.
121    /// * `data` - The payload data of the command, most likely CBOR encoded.
122    ///
123    /// **IMPORTANT:** Be aware that the entire header + data must fit within one SMP protocol frame.
124    ///
125    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    /// Receive an SMP frame from the bus.
153    ///
154    /// # Arguments
155    ///
156    /// * `buffer` - A buffer that the data will be read into.
157    /// * `write_operation` - If this is the response to a write or read operation.
158    /// * `sequence_num` - A sequence number. Must match the sequence_num of the accompanying [`Transport::send_frame`] call.
159    /// * `group_id` - The group ID of the command.
160    /// * `command_id` - The command ID.
161    ///
162    /// # Return
163    ///
164    /// The payload data of the response, most likely CBOR encoded.
165    ///
166    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            // Receiving packets with the wrong sequence number is not an error,
190            // they should simply be silently ignored.
191            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    /// Changes the communication timeout.
210    ///
211    /// When the device does not respond to packets within the set
212    /// duration, an error will be raised.
213    fn set_timeout(&mut self, timeout: Duration) -> Result<(), miette::Report>;
214}