hsm_usb_protocol/
frame.rs1use crate::{HEADER_SIZE, HID_REPORT_SIZE, MAX_PAYLOAD_SIZE};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct Frame<'a>
31{
32 pub opcode: u8,
35
36 pub payload: &'a [u8],
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43pub enum FrameParseError
44{
45 WrongReportSize
47 {
48 actual: usize
50 },
51
52 PayloadLenTooLarge
54 {
55 declared: usize
57 },
58}
59
60impl<'a> Frame<'a>
61{
62 pub fn parse(buf: &'a [u8]) -> Result<Self, FrameParseError>
72 {
73 if buf.len() != HID_REPORT_SIZE
74 {
75 return Err(FrameParseError::WrongReportSize { actual: buf.len() });
76 }
77
78 let opcode = buf[0];
79 let len = u16::from_le_bytes([buf[1], buf[2]]) as usize;
80
81 if len > MAX_PAYLOAD_SIZE
82 {
83 return Err(FrameParseError::PayloadLenTooLarge { declared: len });
84 }
85
86 Ok(Self
87 {
88 opcode,
89 payload: &buf[HEADER_SIZE..HEADER_SIZE + len],
90 })
91 }
92
93 pub fn write
104 (
105 opcode: u8,
106 payload: &[u8],
107 out: &mut [u8],
108 ) -> Result<(), FrameBuildError>
109 {
110 if out.len() != HID_REPORT_SIZE
111 {
112 return Err(FrameBuildError::WrongOutputSize { actual: out.len() });
113 }
114 if payload.len() > MAX_PAYLOAD_SIZE
115 {
116 return Err(FrameBuildError::PayloadTooLarge { len: payload.len() });
117 }
118
119 for byte in out.iter_mut()
123 {
124 *byte = 0;
125 }
126
127 out[0] = opcode;
128 let len = u16::try_from(payload.len())
130 .map_err(|_| FrameBuildError::PayloadTooLarge {len: payload.len()})?;
131 let [lo, hi] = len.to_le_bytes();
132 out[1] = lo;
133 out[2] = hi;
134 out[HEADER_SIZE..HEADER_SIZE + payload.len()].copy_from_slice(payload);
135 Ok(())
136 }
137
138 pub fn to_report(opcode: u8, payload: &[u8]) -> Result<[u8; HID_REPORT_SIZE], FrameBuildError>
145 {
146 let mut report = [0u8; HID_REPORT_SIZE];
147 Self::write(opcode, payload, &mut report)?;
148 Ok(report)
149 }
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154#[cfg_attr(feature = "defmt", derive(defmt::Format))]
155pub enum FrameBuildError
156{
157 WrongOutputSize
159 {
160 actual: usize
162 },
163
164 PayloadTooLarge
166 {
167 len: usize
169 },
170}
171
172#[cfg(test)]
173mod tests
174{
175 use super::*;
176
177 #[test]
178 fn write_then_parse_round_trip_with_empty_payload()
179 {
180 let report = Frame::to_report(0x01, &[]).unwrap();
181 let frame = Frame::parse(&report).unwrap();
182 assert_eq!(frame.opcode, 0x01);
183 assert_eq!(frame.payload.len(), 0);
184 }
185
186 #[test]
187 fn write_then_parse_round_trip_with_typical_payload()
188 {
189 let payload = [0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE];
190 let report = Frame::to_report(0x03, &payload).unwrap();
191 let frame = Frame::parse(&report).unwrap();
192 assert_eq!(frame.opcode, 0x03);
193 assert_eq!(frame.payload, &payload);
194 }
195
196 #[test]
197 fn write_pads_with_zeros()
198 {
199 let report = Frame::to_report(0xAA, &[0x11, 0x22, 0x33]).unwrap();
200 assert_eq!(report[0], 0xAA);
201 assert_eq!(report[1], 3); assert_eq!(report[2], 0); assert_eq!(&report[3..6], &[0x11, 0x22, 0x33]);
204 assert!(report[6..].iter().all(|b| *b == 0));
205 }
206
207 #[test]
208 fn write_at_max_payload_succeeds()
209 {
210 let payload = [0x42u8; MAX_PAYLOAD_SIZE];
211 let report = Frame::to_report(0x10, &payload).unwrap();
212 let frame = Frame::parse(&report).unwrap();
213 assert_eq!(frame.payload.len(), MAX_PAYLOAD_SIZE);
214 assert_eq!(frame.payload, &payload);
215 }
216
217 #[test]
218 fn write_rejects_oversized_payload()
219 {
220 let oversized = [0u8; MAX_PAYLOAD_SIZE + 1];
221 let mut out = [0u8; HID_REPORT_SIZE];
222 let err = Frame::write(0x00, &oversized, &mut out).unwrap_err();
223 assert_eq!(err, FrameBuildError::PayloadTooLarge { len: MAX_PAYLOAD_SIZE + 1 });
224 }
225
226 #[test]
227 fn write_rejects_wrong_output_size()
228 {
229 let mut out = [0u8; 63];
230 let err = Frame::write(0x00, &[], &mut out).unwrap_err();
231 assert_eq!(err, FrameBuildError::WrongOutputSize { actual: 63 });
232 }
233
234 #[test]
235 fn parse_rejects_wrong_report_size()
236 {
237 let short = [0u8; 32];
238 let err = Frame::parse(&short).unwrap_err();
239 assert_eq!(err, FrameParseError::WrongReportSize { actual: 32 });
240 }
241
242 #[test]
243 fn parse_rejects_oversized_payload_len()
244 {
245 let mut report = [0u8; HID_REPORT_SIZE];
246 report[0] = 0x42;
247 let oversized = u16::try_from(MAX_PAYLOAD_SIZE + 1).unwrap();
251 let [lo, hi] = oversized.to_le_bytes();
252 report[1] = lo;
253 report[2] = hi;
254 let err = Frame::parse(&report).unwrap_err();
255 assert_eq!(
256 err,
257 FrameParseError::PayloadLenTooLarge { declared: MAX_PAYLOAD_SIZE + 1 }
258 );
259 }
260
261 #[test]
262 fn parse_ignores_padding_bytes()
263 {
264 let mut report = [0u8; HID_REPORT_SIZE];
265 report[0] = 0x01;
266 report[1] = 2;
267 report[2] = 0;
268 report[3] = 0xAB;
269 report[4] = 0xCD;
270 for byte in report.iter_mut().skip(5)
272 {
273 *byte = 0xFF;
274 }
275 let frame = Frame::parse(&report).unwrap();
276 assert_eq!(frame.opcode, 0x01);
277 assert_eq!(frame.payload, &[0xAB, 0xCD]);
278 }
279
280 #[test]
281 fn len_uses_little_endian_encoding()
282 {
283 let mut report = [0u8; HID_REPORT_SIZE];
290 report[0] = 0x00;
291 report[1] = 0x02; report[2] = 0x01; let err = Frame::parse(&report).unwrap_err();
294 assert_eq!(err, FrameParseError::PayloadLenTooLarge { declared: 258 });
295 }
296}