seeed_erpc/
codec.rs

1use super::ids::*;
2use nom::{error::ParseError, number::streaming, IResult, Input};
3
4const BASIC_CODEC_VERSION: u8 = 1;
5
6/// Data precluding an RPC payload, describing which RPC it is and other metadata
7#[derive(Clone, Debug)]
8pub struct Header {
9    pub service: Service,
10    pub request: u8,
11    pub msg_type: MsgType,
12    pub sequence: u32, // incrementing number.
13}
14
15impl Header {
16    /// Encodes the RPC into its wire format
17    pub fn as_bytes(&self) -> [u8; 8] {
18        let header: u32 = (BASIC_CODEC_VERSION as u32) << 24
19            | ((self.service as u32) << 16)
20            | ((self.request as u32) << 8)
21            | (self.msg_type as u32);
22        let header = header.to_le_bytes();
23
24        let seq = self.sequence.to_le_bytes();
25
26        [
27            header[0], header[1], header[2], header[3], seq[0], seq[1], seq[2], seq[3],
28        ]
29    }
30
31    /// Decodes an RPC header from a byte slice or other compatible type
32    pub fn parse<I, E: ParseError<I>>(i: I) -> IResult<I, Self, E>
33    where
34        I: Input<Item = u8>,
35    {
36        let (i, header) = streaming::le_u32(i)?;
37        let (i, sequence) = streaming::le_u32(i)?;
38        Ok((
39            i,
40            Self {
41                service: (((header >> 16) & 0xff) as u8).into(),
42                request: ((header >> 8) & 0xff) as u8,
43                msg_type: ((header & 0xff) as u8).into(),
44                sequence,
45            },
46        ))
47    }
48}
49
50/// Wraps a complete RPC (Header + data) on stream transports, like a UART.
51#[derive(Clone, Debug)]
52pub struct FrameHeader {
53    pub msg_length: u16,
54    pub crc16: u16,
55}
56
57impl FrameHeader {
58    /// Builds a frame header which will wrap the provided msg.
59    pub fn new_from_msg(msg: &[u8]) -> Self {
60        Self {
61            msg_length: msg.len() as u16,
62            crc16: crc16(msg),
63        }
64    }
65
66    /// Encodes the frame header in its wire format.
67    pub fn as_bytes(&self) -> [u8; 4] {
68        let (l, c) = (self.msg_length.to_le_bytes(), self.crc16.to_le_bytes());
69        [l[0], l[1], c[0], c[1]]
70    }
71
72    /// Nom parser which decodes a leading FrameHeader from the input. Can be
73    /// chained with other nom parsers.
74    pub fn parse<I, E: ParseError<I>>(i: I) -> IResult<I, Self, E>
75    where
76        I: Input<Item = u8>,
77    {
78        let (i, msg_length) = streaming::le_u16(i)?;
79        let (i, crc16) = streaming::le_u16(i)?;
80        Ok((i, Self { msg_length, crc16 }))
81    }
82
83    /// Checks the CRC matches that computed from the provided payload.
84    pub fn check_crc<I, E>(&self, data: I) -> Result<(), super::Err<E>>
85    where
86        I: Input<Item = u8>,
87    {
88        if crc16(data) == self.crc16 {
89            Ok(())
90        } else {
91            Err(super::Err::CRCMismatch)
92        }
93    }
94}
95
96/// computes the CRC value used in the Wio Terminal eRPC codec
97pub(crate) fn crc16<I>(data: I) -> u16
98where
99    I: Input<Item = u8>,
100{
101    let mut crc: u32 = 0xEF4A;
102
103    for b in data.iter_elements() {
104        crc ^= (b as u32) << 8;
105        for _ in 0..8 {
106            let mut temp: u32 = crc << 1;
107            if (crc & 0x8000) != 0 {
108                temp ^= 0x1021;
109            }
110            crc = temp;
111        }
112    }
113
114    crc as u16
115}