1use thiserror::Error;
5
6#[derive(Error, Debug)]
7pub enum SmpError {
8 #[error("payload decoding error: {0}")]
9 PayloadDecodingError(#[from] Box<dyn std::error::Error>),
10 #[error("smp frame decoding error")]
11 InvalidFrame,
12 #[error("unexpected sequence number")]
13 UnexpectedSeq,
14}
15
16#[derive(Debug, Clone, Copy)]
17pub enum OpCode {
18 ReadRequest = 0,
19 ReadResponse = 1,
20 WriteRequest = 2,
21 WriteResponse = 3,
22}
23
24impl From<u8> for OpCode {
25 fn from(num: u8) -> Self {
26 match num {
27 0 => OpCode::ReadRequest,
28 1 => OpCode::ReadResponse,
29 2 => OpCode::WriteRequest,
30 3 => OpCode::WriteResponse,
31 _ => panic!("unknown opcode"),
32 }
33 }
34}
35
36impl From<OpCode> for u8 {
37 fn from(op: OpCode) -> Self {
38 match op {
39 OpCode::ReadRequest => 0,
40 OpCode::ReadResponse => 1,
41 OpCode::WriteRequest => 2,
42 OpCode::WriteResponse => 3,
43 }
44 }
45}
46
47#[derive(Debug, Clone, Copy)]
48pub enum Group {
49 Default,
50 ApplicationManagement,
51 Statistics,
52 FileManagement,
53 ShellManagement,
54 ZephyrCommand,
55 Custom(u16),
56}
57
58impl From<u16> for Group {
59 fn from(num: u16) -> Self {
60 match num {
61 0 => Self::Default,
62 1 => Self::ApplicationManagement,
63 2 => Self::Statistics,
64 8 => Self::FileManagement,
65 9 => Self::ShellManagement,
66 63 => Self::ZephyrCommand,
67 num => Self::Custom(num),
68 }
69 }
70}
71
72impl From<Group> for u16 {
73 fn from(g: Group) -> Self {
74 match g {
75 Group::Default => 0,
76 Group::ApplicationManagement => 1,
77 Group::Statistics => 2,
78 Group::FileManagement => 8,
79 Group::ShellManagement => 9,
80 Group::ZephyrCommand => 63,
81 Group::Custom(num) => num,
82 }
83 }
84}
85
86pub enum ReturnCode {
87 Ok = 0,
88 Unknown = 1,
89 OutOfMemory = 2,
90 UserDefined = 256,
92}
93
94#[derive(Debug, Clone)]
97pub struct SmpFrame<T> {
98 pub operation: OpCode,
99 pub flags: u8,
100 pub group: Group,
101 pub sequence: u8,
102 pub command: u8,
103 pub data: T,
104}
105
106impl<T> SmpFrame<T> {
107 pub fn new(operation: OpCode, sequence: u8, group: Group, command: u8, payload: T) -> Self {
109 Self {
110 operation,
111 flags: 0,
112 group,
113 sequence,
114 command,
115 data: payload,
116 }
117 }
118}
119
120impl<T> SmpFrame<T> {
121 pub fn encode<E, R: AsRef<[u8]>>(
124 &self,
125 encode_payload: impl FnOnce(&T) -> Result<R, E>,
126 ) -> Result<Vec<u8>, E> {
127 let mut buf: Vec<u8> = Vec::with_capacity(12);
128
129 let encoded = encode_payload(&self.data)?;
130 let data: &[u8] = encoded.as_ref();
131
132 buf.push(self.operation.into());
133 buf.push(self.flags);
134 buf.extend_from_slice(&(data.len() as u16).to_be_bytes());
135 let group: u16 = self.group.into();
136 buf.extend_from_slice(&group.to_be_bytes());
137 buf.push(self.sequence);
138 buf.push(self.command);
139
140 buf.extend(data.iter());
141
142 Ok(buf)
143 }
144
145 pub fn decode(
148 buf: &[u8],
149 decode_payload: impl FnOnce(&[u8]) -> Result<T, Box<dyn std::error::Error>>,
150 ) -> Result<SmpFrame<T>, SmpError> {
151 if buf.len() < 8 {
152 return Err(SmpError::InvalidFrame);
153 }
154
155 let operation = OpCode::from(buf[0] & 0x07);
156 let group = Group::from(u16::from_be_bytes([buf[4], buf[5]]));
157 let data_len = u16::from_be_bytes([buf[2], buf[3]]);
158 let _flags = buf[1];
159 let sequence = buf[6];
160 let command = buf[7];
161
162 if buf.len() < (8 + data_len) as usize {
163 return Err(SmpError::InvalidFrame);
164 }
165
166 let data_buf = &buf[8..(8 + data_len as usize)];
167 let data = decode_payload(data_buf)?;
168
169 Ok(SmpFrame::new(operation, sequence, group, command, data))
170 }
171}
172
173#[cfg(feature = "payload-cbor")]
174impl<T: serde::Serialize> SmpFrame<T> {
175 pub fn encode_with_cbor(&self) -> Vec<u8> {
178 self.encode(|data| {
180 let mut buf = Vec::new();
181 match ciborium::ser::into_writer(data, &mut buf) {
182 Ok(_) => Ok(buf),
183 Err(e) => Err(e),
184 }
185 })
186 .unwrap()
187 }
188}
189
190#[cfg(feature = "payload-cbor")]
191impl<T: serde::de::DeserializeOwned> SmpFrame<T> {
192 pub fn decode_with_cbor(buf: &[u8]) -> Result<SmpFrame<T>, SmpError> {
195 Self::decode(buf, |buf| {
196 let x: T = ciborium::de::from_reader(buf)?;
197 Ok(x)
198 })
199 }
200}