1use crate::{
10 MctpPacketError,
11 buffer_encoding::{BufferEncoding, DecodeError, EncodeError, EncodingDecoder, EncodingEncoder},
12 error::MctpPacketResult,
13 medium::{MctpMedium, MctpMediumFrame},
14};
15
16#[derive(Debug, Copy, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28pub struct SerialEncoding;
29
30impl BufferEncoding for SerialEncoding {
31 fn write_byte(wire_buf: &mut [u8], byte: u8) -> Result<usize, EncodeError> {
32 match byte {
33 0x7E => {
34 if wire_buf.len() < 2 {
35 return Err(EncodeError::BufferFull);
36 }
37 wire_buf[0] = 0x7D;
38 wire_buf[1] = 0x5E;
39 Ok(2)
40 }
41 0x7D => {
42 if wire_buf.len() < 2 {
43 return Err(EncodeError::BufferFull);
44 }
45 wire_buf[0] = 0x7D;
46 wire_buf[1] = 0x5D;
47 Ok(2)
48 }
49 b => match wire_buf.first_mut() {
50 Some(slot) => {
51 *slot = b;
52 Ok(1)
53 }
54 None => Err(EncodeError::BufferFull),
55 },
56 }
57 }
58
59 fn read_byte(wire_buf: &[u8]) -> Result<(u8, usize), DecodeError> {
60 match wire_buf.first().copied() {
61 None => Err(DecodeError::PrematureEnd),
62 Some(0x7D) => match wire_buf.get(1).copied() {
63 None => Err(DecodeError::PrematureEnd),
64 Some(0x5E) => Ok((0x7E, 2)),
65 Some(0x5D) => Ok((0x7D, 2)),
66 Some(_) => Err(DecodeError::InvalidEscape),
67 },
68 Some(b) => Ok((b, 1)),
72 }
73 }
74
75 fn wire_size_of(decoded: &[u8]) -> usize {
76 decoded
77 .iter()
78 .map(|&b| if b == 0x7E || b == 0x7D { 2 } else { 1 })
79 .sum()
80 }
81}
82
83pub const SP_EID: crate::endpoint_id::EndpointId = crate::endpoint_id::EndpointId::Id(0x08);
85pub const EC_EID: crate::endpoint_id::EndpointId = crate::endpoint_id::EndpointId::Id(0x0A);
87pub const CONST_MTU: usize = 251;
89
90const SERIAL_REVISION: u8 = 0x01;
91const END_FLAG: u8 = 0x7E;
92const HEADER_LEN: usize = 2;
94const MAX_TRAILER_WIRE: usize = 5;
97
98const FCS_ALGO: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
103
104#[derive(Debug, Copy, Clone, PartialEq, Eq)]
105#[cfg_attr(feature = "defmt", derive(defmt::Format))]
106pub struct MctpSerialMediumFrame {
107 pub revision: u8,
108 pub byte_count: u8,
111 pub fcs: u16,
112}
113
114impl MctpMediumFrame<MctpSerialMedium> for MctpSerialMediumFrame {
115 fn packet_size(&self) -> usize {
116 self.byte_count as usize
120 }
121
122 fn reply_context(&self) {}
123}
124
125#[derive(Debug, Copy, Clone, PartialEq, Eq)]
126#[cfg_attr(feature = "defmt", derive(defmt::Format))]
127pub struct MctpSerialMedium;
128
129impl MctpMedium for MctpSerialMedium {
130 type Frame = MctpSerialMediumFrame;
131 type Error = &'static str;
132 type ReplyContext = ();
133 type Encoding = SerialEncoding;
134
135 fn max_message_body_size(&self) -> usize {
136 CONST_MTU
137 }
138
139 fn deserialize<'buf>(
140 &self,
141 packet: &'buf [u8],
142 ) -> MctpPacketResult<(Self::Frame, EncodingDecoder<'buf, Self::Encoding>), Self> {
143 if packet.len() < HEADER_LEN + 3 {
145 return Err(MctpPacketError::MediumError(
146 "packet too short for serial frame",
147 ));
148 }
149 let revision = packet[0];
150 if revision != SERIAL_REVISION {
151 return Err(MctpPacketError::MediumError("unsupported serial revision"));
152 }
153 let byte_count = packet[1];
154 if (byte_count as usize) > CONST_MTU {
155 return Err(MctpPacketError::MediumError("byte_count exceeds MTU"));
156 }
157
158 let body_wire_start = HEADER_LEN;
162 let mut decoded = [0u8; CONST_MTU];
163 let mut decoded_len = 0usize;
164 let mut wire_pos = 0usize; while decoded_len < byte_count as usize {
167 let (b, n) =
168 SerialEncoding::read_byte(&packet[body_wire_start + wire_pos..]).map_err(|e| {
169 match e {
170 DecodeError::PrematureEnd => {
171 MctpPacketError::MediumError("premature end in body")
172 }
173 DecodeError::InvalidEscape => {
174 MctpPacketError::MediumError("invalid escape in body")
175 }
176 }
177 })?;
178 if b == END_FLAG && n == 1 {
179 return Err(MctpPacketError::MediumError("unexpected 0x7E in body"));
184 }
185 decoded[decoded_len] = b;
186 decoded_len += 1;
187 wire_pos += n;
188 }
189 let body_wire_end = body_wire_start + wire_pos;
190
191 let (fcs_msb, n_msb) = SerialEncoding::read_byte(&packet[body_wire_end..])
193 .map_err(|_| MctpPacketError::MediumError("invalid escape in fcs"))?;
194 let (fcs_lsb, n_lsb) = SerialEncoding::read_byte(&packet[body_wire_end + n_msb..])
195 .map_err(|_| MctpPacketError::MediumError("invalid escape in fcs"))?;
196 let trailer_pos = body_wire_end + n_msb + n_lsb;
197
198 if trailer_pos >= packet.len() || packet[trailer_pos] != END_FLAG {
199 return Err(MctpPacketError::MediumError("missing end flag"));
200 }
201 if trailer_pos + 1 != packet.len() {
202 return Err(MctpPacketError::MediumError(
203 "trailing bytes after end flag",
204 ));
205 }
206
207 let mut digest = FCS_ALGO.digest();
209 digest.update(&[revision, byte_count]);
210 digest.update(&decoded[..decoded_len]);
211 let computed_fcs = digest.finalize();
212 let wire_fcs = u16::from_be_bytes([fcs_msb, fcs_lsb]);
214 if wire_fcs != computed_fcs {
215 return Err(MctpPacketError::MediumError("fcs mismatch"));
216 }
217
218 Ok((
219 MctpSerialMediumFrame {
220 revision,
221 byte_count,
222 fcs: wire_fcs,
223 },
224 EncodingDecoder::<Self::Encoding>::new(&packet[body_wire_start..body_wire_end]),
225 ))
226 }
227
228 fn serialize<'buf, F>(
229 &self,
230 _reply_context: Self::ReplyContext,
231 buffer: &'buf mut [u8],
232 message_writer: F,
233 ) -> MctpPacketResult<&'buf [u8], Self>
234 where
235 F: for<'a> FnOnce(&mut EncodingEncoder<'a, Self::Encoding>) -> MctpPacketResult<(), Self>,
236 {
237 if buffer.len() < HEADER_LEN + MAX_TRAILER_WIRE {
238 return Err(MctpPacketError::MediumError(
239 "buffer too small for serial frame",
240 ));
241 }
242 let buffer_len = buffer.len();
243
244 let body_wire_len = {
248 let body_buf = &mut buffer[HEADER_LEN..buffer_len - MAX_TRAILER_WIRE];
249 let mut encoder = EncodingEncoder::<Self::Encoding>::new(body_buf);
250 message_writer(&mut encoder)?;
251 encoder.wire_position()
252 };
253
254 let mut decoded = [0u8; CONST_MTU];
258 let mut decoded_len = 0usize;
259 let mut wire_pos = 0usize;
260 while wire_pos < body_wire_len {
261 let (b, n) = SerialEncoding::read_byte(
262 &buffer[HEADER_LEN + wire_pos..HEADER_LEN + body_wire_len],
263 )
264 .map_err(|_| MctpPacketError::MediumError("internal: failed to re-decode body"))?;
265 if decoded_len >= CONST_MTU {
266 return Err(MctpPacketError::MediumError("body exceeds MTU"));
267 }
268 decoded[decoded_len] = b;
269 decoded_len += 1;
270 wire_pos += n;
271 }
272 if decoded_len > u8::MAX as usize {
275 return Err(MctpPacketError::MediumError(
276 "body exceeds byte_count u8 cap",
277 ));
278 }
279 let byte_count = decoded_len as u8;
280
281 let mut digest = FCS_ALGO.digest();
283 digest.update(&[SERIAL_REVISION, byte_count]);
284 digest.update(&decoded[..decoded_len]);
285 let fcs = digest.finalize();
286 let [fcs_msb, fcs_lsb] = fcs.to_be_bytes();
288
289 buffer[0] = SERIAL_REVISION;
295 buffer[1] = byte_count;
296
297 let fcs_start = HEADER_LEN + body_wire_len;
301 let n_msb = SerialEncoding::write_byte(&mut buffer[fcs_start..], fcs_msb)
302 .map_err(|_| MctpPacketError::MediumError("internal: failed to encode fcs"))?;
303 let n_lsb = SerialEncoding::write_byte(&mut buffer[fcs_start + n_msb..], fcs_lsb)
304 .map_err(|_| MctpPacketError::MediumError("internal: failed to encode fcs"))?;
305 let end_pos = fcs_start + n_msb + n_lsb;
306
307 buffer[end_pos] = END_FLAG;
310
311 Ok(&buffer[..end_pos + 1])
312 }
313}
314
315#[cfg(test)]
316mod encoding_tests {
317 use super::*;
318 use crate::buffer_encoding::EncodingDecoder;
319
320 #[test]
321 fn write_byte_stuffs_7e() {
322 let mut buf = [0u8; 4];
323 let n = SerialEncoding::write_byte(&mut buf, 0x7E).unwrap();
324 assert_eq!(n, 2);
325 assert_eq!(&buf[..2], &[0x7D, 0x5E]);
326 }
327
328 #[test]
329 fn write_byte_stuffs_7d() {
330 let mut buf = [0u8; 4];
331 let n = SerialEncoding::write_byte(&mut buf, 0x7D).unwrap();
332 assert_eq!(n, 2);
333 assert_eq!(&buf[..2], &[0x7D, 0x5D]);
334 }
335
336 #[test]
337 fn write_byte_passthrough_plain() {
338 let mut buf = [0u8; 1];
339 let n = SerialEncoding::write_byte(&mut buf, 0x41).unwrap();
340 assert_eq!(n, 1);
341 assert_eq!(buf, [0x41]);
342 }
343
344 #[test]
345 fn write_byte_full_buffer_plain() {
346 let mut buf = [];
347 assert_eq!(
348 SerialEncoding::write_byte(&mut buf, 0x41).unwrap_err(),
349 EncodeError::BufferFull
350 );
351 }
352
353 #[test]
354 fn write_byte_full_buffer_escape() {
355 let mut buf = [0u8; 1];
356 assert_eq!(
357 SerialEncoding::write_byte(&mut buf, 0x7E).unwrap_err(),
358 EncodeError::BufferFull
359 );
360 }
361
362 #[test]
363 fn read_byte_unstuffs_7e() {
364 assert_eq!(SerialEncoding::read_byte(&[0x7D, 0x5E]).unwrap(), (0x7E, 2));
365 }
366
367 #[test]
368 fn read_byte_unstuffs_7d() {
369 assert_eq!(SerialEncoding::read_byte(&[0x7D, 0x5D]).unwrap(), (0x7D, 2));
370 }
371
372 #[test]
373 fn read_byte_passthrough_plain() {
374 assert_eq!(SerialEncoding::read_byte(&[0x41]).unwrap(), (0x41, 1));
375 }
376
377 #[test]
378 fn read_byte_raw_7e_passes_through() {
379 assert_eq!(SerialEncoding::read_byte(&[0x7E]).unwrap(), (0x7E, 1));
382 }
383
384 #[test]
385 fn read_byte_premature_end_empty() {
386 assert_eq!(
387 SerialEncoding::read_byte(&[]).unwrap_err(),
388 DecodeError::PrematureEnd
389 );
390 }
391
392 #[test]
393 fn read_byte_premature_end_after_escape() {
394 assert_eq!(
395 SerialEncoding::read_byte(&[0x7D]).unwrap_err(),
396 DecodeError::PrematureEnd
397 );
398 }
399
400 #[test]
401 fn read_byte_invalid_escape() {
402 assert_eq!(
403 SerialEncoding::read_byte(&[0x7D, 0xAA]).unwrap_err(),
404 DecodeError::InvalidEscape
405 );
406 }
407
408 #[test]
409 fn wire_size_of_mixed() {
410 assert_eq!(
411 SerialEncoding::wire_size_of(&[0x41, 0x7E, 0x42, 0x7D, 0x43]),
412 7
413 );
414 }
415
416 #[test]
417 fn wire_size_of_empty() {
418 assert_eq!(SerialEncoding::wire_size_of(&[]), 0);
419 }
420
421 #[test]
422 fn roundtrip_all_byte_values() {
423 let mut decoded = [0u8; 256];
427 for (i, slot) in decoded.iter_mut().enumerate() {
428 *slot = i as u8;
429 }
430 let mut wire = [0u8; 512];
431 let mut wpos = 0usize;
432 for &b in &decoded {
433 wpos += SerialEncoding::write_byte(&mut wire[wpos..], b).unwrap();
434 }
435 assert_eq!(wpos, SerialEncoding::wire_size_of(&decoded));
436 let mut dec = EncodingDecoder::<SerialEncoding>::new(&wire[..wpos]);
437 for &expected in &decoded {
438 assert_eq!(dec.read().unwrap(), expected);
439 }
440 assert_eq!(dec.read().unwrap_err(), DecodeError::PrematureEnd);
441 }
442}
443
444#[cfg(test)]
445mod fixtures {
446 pub(crate) const FIXTURE_BASIC_RX: &[u8] =
462 &[0x01, 0x04, 0xAA, 0xBB, 0xCC, 0xDD, 0x6D, 0xA1, 0x7E];
463
464 pub(crate) const FIXTURE_PAYLOAD_CONTAINS_7E: &[u8] =
465 &[0x01, 0x03, 0xAA, 0x7D, 0x5E, 0xCC, 0xFB, 0xE7, 0x7E];
466
467 pub(crate) const FIXTURE_PAYLOAD_CONTAINS_7D: &[u8] =
468 &[0x01, 0x03, 0xAA, 0x7D, 0x5D, 0xCC, 0xD1, 0x8F, 0x7E];
469
470 pub(crate) const FIXTURE_PAYLOAD_CONTAINS_BOTH: &[u8] =
471 &[0x01, 0x03, 0x7D, 0x5E, 0x7D, 0x5D, 0x42, 0x50, 0x97, 0x7E];
472
473 pub(crate) const FIXTURE_MAX_MTU_FRAME: &[u8] = &[
476 0x01, 0xFB, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
477 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
478 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
479 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
480 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
481 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
482 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
483 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
484 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x5D, 0x7D, 0x5E, 0x7F, 0x80, 0x81, 0x82,
485 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91,
486 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0,
487 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
488 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
489 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD,
490 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC,
491 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB,
492 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA,
493 0xF6, 0x07, 0x7E,
494 ];
495
496 pub(crate) const FIXTURE_EMPTY_PAYLOAD: &[u8] = &[0x01, 0x00, 0x16, 0x9F, 0x7E];
497
498 pub(crate) const FIXTURE_FCS_VALID: &[u8] = &[0x01, 0x03, 0x10, 0x20, 0x30, 0x76, 0xDB, 0x7E];
499
500 pub(crate) const FIXTURE_FCS_INVALID: &[u8] = &[0x01, 0x03, 0x10, 0x20, 0x30, 0x89, 0xDB, 0x7E];
502
503 pub(crate) const FIXTURE_INVALID_ESCAPE: &[u8] = &[0x01, 0x02, 0x7D, 0xAA, 0x00, 0x00, 0x7E];
507
508 pub(crate) const FIXTURE_PREMATURE_END_FLAG: &[u8] =
511 &[0x01, 0x03, 0xAA, 0x7E, 0xCC, 0x00, 0x00, 0x7E];
512}
513
514#[cfg(test)]
515mod medium_tests {
516 use super::{fixtures::*, *};
517
518 fn drain_decoder(mut dec: EncodingDecoder<'_, SerialEncoding>) -> ([u8; CONST_MTU], usize) {
519 let mut out = [0u8; CONST_MTU];
520 let mut n = 0;
521 while let Ok(b) = dec.read() {
522 out[n] = b;
523 n += 1;
524 }
525 (out, n)
526 }
527
528 #[test]
529 fn decode_basic_rx_succeeds() {
530 let (frame, dec) = MctpSerialMedium.deserialize(FIXTURE_BASIC_RX).unwrap();
531 assert_eq!(frame.revision, 0x01);
532 assert_eq!(frame.byte_count, 4);
533 assert_eq!(frame.fcs, 0x6DA1);
534 let (decoded, n) = drain_decoder(dec);
535 assert_eq!(&decoded[..n], &[0xAA, 0xBB, 0xCC, 0xDD]);
536 }
537
538 #[test]
539 fn decode_payload_contains_7e() {
540 let (frame, dec) = MctpSerialMedium
541 .deserialize(FIXTURE_PAYLOAD_CONTAINS_7E)
542 .unwrap();
543 assert_eq!(frame.byte_count, 3);
544 let (decoded, n) = drain_decoder(dec);
545 assert_eq!(&decoded[..n], &[0xAA, 0x7E, 0xCC]);
546 }
547
548 #[test]
549 fn decode_payload_contains_7d() {
550 let (frame, dec) = MctpSerialMedium
551 .deserialize(FIXTURE_PAYLOAD_CONTAINS_7D)
552 .unwrap();
553 assert_eq!(frame.byte_count, 3);
554 let (decoded, n) = drain_decoder(dec);
555 assert_eq!(&decoded[..n], &[0xAA, 0x7D, 0xCC]);
556 }
557
558 #[test]
559 fn decode_payload_contains_both() {
560 let (frame, dec) = MctpSerialMedium
561 .deserialize(FIXTURE_PAYLOAD_CONTAINS_BOTH)
562 .unwrap();
563 assert_eq!(frame.byte_count, 3);
564 let (decoded, n) = drain_decoder(dec);
565 assert_eq!(&decoded[..n], &[0x7E, 0x7D, 0x42]);
566 }
567
568 #[test]
569 fn decode_max_mtu_frame() {
570 let (frame, dec) = MctpSerialMedium.deserialize(FIXTURE_MAX_MTU_FRAME).unwrap();
571 assert_eq!(frame.byte_count as usize, CONST_MTU);
572 let (decoded, n) = drain_decoder(dec);
573 assert_eq!(n, CONST_MTU);
574 for (i, &b) in decoded[..n].iter().enumerate() {
575 assert_eq!(b, i as u8, "mismatch at idx {i}");
576 }
577 }
578
579 #[test]
580 fn decode_empty_payload() {
581 let (frame, dec) = MctpSerialMedium.deserialize(FIXTURE_EMPTY_PAYLOAD).unwrap();
582 assert_eq!(frame.byte_count, 0);
583 let (_, n) = drain_decoder(dec);
584 assert_eq!(n, 0);
585 }
586
587 #[test]
588 fn decode_fcs_valid() {
589 assert!(MctpSerialMedium.deserialize(FIXTURE_FCS_VALID).is_ok());
590 }
591
592 #[test]
593 fn decode_fcs_invalid_rejects() {
594 match MctpSerialMedium.deserialize(FIXTURE_FCS_INVALID) {
595 Err(crate::MctpPacketError::MediumError("fcs mismatch")) => {}
596 other => panic!(
597 "expected MediumError(\"fcs mismatch\"), got {:?}",
598 other.err()
599 ),
600 }
601 }
602
603 #[test]
604 fn decode_invalid_escape_rejects() {
605 match MctpSerialMedium.deserialize(FIXTURE_INVALID_ESCAPE) {
606 Err(crate::MctpPacketError::MediumError("invalid escape in body")) => {}
607 other => panic!(
608 "expected MediumError(\"invalid escape in body\"), got {:?}",
609 other.err()
610 ),
611 }
612 }
613
614 #[test]
615 fn decode_premature_end_flag_rejects() {
616 match MctpSerialMedium.deserialize(FIXTURE_PREMATURE_END_FLAG) {
617 Err(crate::MctpPacketError::MediumError("unexpected 0x7E in body")) => {}
618 other => panic!(
619 "expected MediumError(\"unexpected 0x7E in body\"), got {:?}",
620 other.err()
621 ),
622 }
623 }
624
625 fn fixture_roundtrip(wire: &[u8]) {
626 let m = MctpSerialMedium;
627 let (_frame, dec) = m.deserialize(wire).unwrap();
628 let (decoded, n) = drain_decoder(dec);
629 let mut out = [0u8; 1024];
630 let serialized = m
631 .serialize((), &mut out, |e| {
632 e.write_all(&decoded[..n])
633 .map_err(|_| MctpPacketError::MediumError("write failed"))
634 })
635 .unwrap();
636 assert_eq!(serialized, wire);
637 }
638
639 #[test]
640 fn fixture_roundtrip_basic_rx() {
641 fixture_roundtrip(FIXTURE_BASIC_RX);
642 }
643
644 #[test]
645 fn fixture_roundtrip_payload_contains_7e() {
646 fixture_roundtrip(FIXTURE_PAYLOAD_CONTAINS_7E);
647 }
648
649 #[test]
650 fn fixture_roundtrip_payload_contains_7d() {
651 fixture_roundtrip(FIXTURE_PAYLOAD_CONTAINS_7D);
652 }
653
654 #[test]
655 fn fixture_roundtrip_payload_contains_both() {
656 fixture_roundtrip(FIXTURE_PAYLOAD_CONTAINS_BOTH);
657 }
658
659 #[test]
660 fn fixture_roundtrip_max_mtu_frame() {
661 fixture_roundtrip(FIXTURE_MAX_MTU_FRAME);
662 }
663
664 #[test]
665 fn fixture_roundtrip_empty_payload() {
666 fixture_roundtrip(FIXTURE_EMPTY_PAYLOAD);
667 }
668
669 #[test]
670 fn fixture_roundtrip_fcs_valid() {
671 fixture_roundtrip(FIXTURE_FCS_VALID);
672 }
673
674 #[test]
675 fn public_api_smoke() {
676 let _: crate::MctpSerialMedium = crate::MctpSerialMedium;
677 let _: crate::SerialEncoding = crate::SerialEncoding;
678 assert_eq!(crate::CONST_MTU, 251);
679 assert_eq!(crate::SP_EID, crate::EndpointId::Id(0x08));
680 assert_eq!(crate::EC_EID, crate::EndpointId::Id(0x0A));
681 }
682
683 #[test]
684 fn packetize_with_stuffing_respects_mtu() {
685 use crate::{
693 endpoint_id::EndpointId, mctp_message_tag::MctpMessageTag,
694 mctp_packet_context::MctpReplyContext, mctp_sequence_number::MctpSequenceNumber,
695 serialize::SerializePacketState,
696 };
697
698 let payload = [0x7E_u8; 251];
699 let mut assembly = [0u8; 1024];
700 let medium = MctpSerialMedium;
701 let reply_context = MctpReplyContext::<MctpSerialMedium> {
702 destination_endpoint_id: EndpointId::Id(0x0A),
703 source_endpoint_id: EndpointId::Id(0x08),
704 packet_sequence_number: MctpSequenceNumber::new(0),
705 message_tag: MctpMessageTag::default(),
706 medium_context: (),
707 };
708 let mut state = SerializePacketState {
709 medium: &medium,
710 reply_context,
711 current_packet_num: 0,
712 serialized_message_header: false,
713 message_buffer: &payload[..],
714 assembly_buffer: &mut assembly[..],
715 };
716
717 let mut total_decoded_body = 0usize;
718 let mut packet_count = 0usize;
719 loop {
720 let pkt = match state.next() {
724 Some(Ok(pkt)) => {
725 let mut tmp = [0u8; 1024];
726 tmp[..pkt.len()].copy_from_slice(pkt);
727 (tmp, pkt.len())
728 }
729 Some(Err(e)) => panic!("serialize error: {e:?}"),
730 None => break,
731 };
732 packet_count += 1;
733 let (frame, dec) = medium.deserialize(&pkt.0[..pkt.1]).unwrap();
736 assert!(frame.byte_count as usize >= 4);
739 let payload_decoded = frame.byte_count as usize - 4;
740 total_decoded_body += payload_decoded;
741 let _ = dec; let wire_body_len = pkt.1 - 2 - 1 ;
745 assert!(
749 wire_body_len <= CONST_MTU + 4,
750 "packet {packet_count} body exceeds MTU + worst-case FCS: {wire_body_len}"
751 );
752 }
753 assert!(
754 packet_count >= 2,
755 "expected multi-packet split, got {packet_count}"
756 );
757 assert_eq!(total_decoded_body, payload.len());
758 }
759}