summaryrefslogtreecommitdiff
path: root/ebus-rust/src/layer2
diff options
context:
space:
mode:
Diffstat (limited to 'ebus-rust/src/layer2')
-rw-r--r--ebus-rust/src/layer2/crc.rs23
-rw-r--r--ebus-rust/src/layer2/mod.rs263
2 files changed, 286 insertions, 0 deletions
diff --git a/ebus-rust/src/layer2/crc.rs b/ebus-rust/src/layer2/crc.rs
new file mode 100644
index 0000000..6fb782d
--- /dev/null
+++ b/ebus-rust/src/layer2/crc.rs
@@ -0,0 +1,23 @@
+const CRC: [u8; 256] = [
+ 0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75, 0xee,
+ 0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71, 0x47, 0xdc,
+ 0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc, 0x27, 0x11, 0x8a,
+ 0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79, 0x8e, 0x15, 0x23, 0xb8,
+ 0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c, 0xe7, 0x10, 0x8b, 0xbd, 0x26,
+ 0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78, 0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14,
+ 0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5, 0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42,
+ 0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69, 0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70,
+ 0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67, 0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5,
+ 0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63, 0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7,
+ 0x6f, 0xf4, 0xc2, 0x59, 0xae, 0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81,
+ 0x5d, 0xc6, 0xf0, 0x6b, 0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3,
+ 0xc3, 0x58, 0x6e, 0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d,
+ 0xf1, 0x6a, 0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f,
+ 0xa7, 0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49,
+ 0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0, 0x7b,
+];
+
+#[inline]
+pub fn crc(data: &[u8]) -> u8 {
+ data.iter().fold(0, |acc, x| CRC[acc as usize] ^ x)
+}
diff --git a/ebus-rust/src/layer2/mod.rs b/ebus-rust/src/layer2/mod.rs
new file mode 100644
index 0000000..ef07aad
--- /dev/null
+++ b/ebus-rust/src/layer2/mod.rs
@@ -0,0 +1,263 @@
+use std::fmt;
+
+mod crc;
+
+const EBUS_SYN: u8 = 0xaa;
+const EBUS_ESCAPE: u8 = 0xa9;
+const EBUS_ACKOK: u8 = 0x00;
+
+#[derive(Default)]
+pub struct Packet {
+ pub source: u8,
+ pub destination: u8,
+ pub primary: u8,
+ pub secondary: u8,
+ payload_length: u8,
+ payload: Vec<u8>,
+ pub crc: u8,
+ payload_slave_length: u8,
+ payload_slave: Vec<u8>,
+ crc_slave: u8,
+}
+
+impl Packet {
+ // payload returns the un-escaped payload
+ pub fn payload_decoded(&self) -> Vec<u8> {
+ let mut v = Vec::new();
+ let mut i = 0;
+
+ while i < self.payload.len() {
+ let c = self.payload[i];
+ if c == EBUS_ESCAPE && i + 1 < self.payload.len() {
+ i += 1;
+ let c = self.payload[i];
+ if c == 0x0 {
+ v.push(EBUS_ESCAPE);
+ } else if c == 0x1 {
+ v.push(EBUS_SYN);
+ } else {
+ v.push(c);
+ }
+ } else {
+ v.push(c);
+ }
+ i += 1;
+ }
+ v
+ }
+
+ pub fn calc_crc(&self) -> u8 {
+ let x = &[
+ self.source,
+ self.destination,
+ self.primary,
+ self.secondary,
+ self.payload_length,
+ ];
+ crc::crc(&[&x[..], self.payload.as_slice()].concat())
+ }
+}
+
+impl fmt::Debug for Packet {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.payload_slave_length > 0 {
+ f.debug_struct("Packet")
+ .field("source", &format!("{:#02x}", &self.source))
+ .field("destination", &format!("{:#02x}", &self.destination))
+ .field("primary", &format!("{:#02x}", &self.primary))
+ .field("secondary", &format!("{:#02x}", &self.secondary))
+ .field("payload", &format!("{:02x?}", &self.payload))
+ .field("payload_length", &self.payload_length)
+ .field("crc", &format!("{:#02x}", &self.crc))
+ .field(
+ "payload_slave_length",
+ &format!("{:#02x}", &self.payload_slave_length),
+ )
+ .field("payload_slave", &format!("{:02x?}", &self.payload_slave))
+ .field("crc_slave", &format!("{:#02x}", &self.crc_slave))
+ .finish()
+ } else {
+ f.debug_struct("Packet")
+ .field("source", &format!("{:#02x}", &self.source))
+ .field("destination", &format!("{:#02x}", &self.destination))
+ .field("primary", &format!("{:#02x}", &self.primary))
+ .field("secondary", &format!("{:#02x}", &self.secondary))
+ .field("payload", &format!("{:02x?}", &self.payload))
+ .field("payload_length", &self.payload_length)
+ .field("crc", &format!("{:#02x}", &self.crc))
+ .finish()
+ }
+ }
+}
+
+fn read_header(data: &[u8]) -> nom::IResult<&[u8], Packet> {
+ use nom::number::streaming::u8;
+ use nom::sequence::tuple;
+ let (input, (source, destination, primary, secondary, payload_length)) =
+ tuple((u8, u8, u8, u8, u8))(data)?;
+ Ok((
+ input,
+ Packet {
+ source,
+ destination,
+ primary,
+ secondary,
+ payload_length,
+ ..Default::default()
+ },
+ ))
+}
+
+fn read_packet(data: &[u8]) -> nom::IResult<&[u8], Packet> {
+ use nom::bytes::streaming::tag;
+ use nom::combinator::opt;
+ use nom::multi::count;
+ use nom::number::streaming::u8;
+ use nom::sequence::tuple;
+
+ let (input, mut packet) = read_header(data)?;
+
+ let (input2, (payload, crc, _, payload_slave_length)) = tuple((
+ count(u8, packet.payload_length as usize), // payload
+ u8, // crc
+ opt(tag([EBUS_ACKOK])), // ACK but non-ack is tolerated
+ u8, // SYN or payload slave length
+ ))(input)?;
+ packet.payload = payload;
+ packet.crc = crc;
+
+ if payload_slave_length != EBUS_SYN {
+ packet.payload_slave_length = payload_slave_length;
+ let (input3, (payload_slave, crc_slave)) =
+ tuple((count(u8, payload_slave_length as usize), u8))(input2)?;
+ packet.payload_slave = payload_slave;
+ packet.crc_slave = crc_slave;
+ Ok((input3, packet))
+ } else {
+ Ok((input2, packet))
+ }
+}
+
+pub fn parse(data: &[u8]) -> Result<Packet, nom::Err<nom::error::Error<&[u8]>>> {
+ use nom::bytes::streaming::take_while;
+ use nom::sequence::preceded;
+ fn take_syn(data: &[u8]) -> nom::IResult<&[u8], &[u8]> {
+ take_while(|chr| chr == EBUS_SYN)(data)
+ }
+
+ let mut parser = preceded(take_syn, read_packet);
+ let result = parser(data);
+ result.map(|r| r.1)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn teststructure_mastermaster() {
+ let data: Vec<u8> = vec![
+ 170, // Syn
+ 170, // Syn
+ 003, // Source
+ 241, // Destination
+ 008, // primaryCommand
+ 000, // secondaryCommand
+ 008, // payloadLength
+ 128, // p1
+ 040, // p2
+ 230, // p3
+ 002, // p4
+ 000, // p5
+ 002, // p6
+ 000, // p7
+ 010, // p8
+ 128, // CRC
+ 000, // ACK
+ 170, // SYN
+ 170,
+ ];
+
+ let res = parse(&data).unwrap();
+ assert_eq!(res.source, 0003);
+ assert_eq!(res.destination, 241);
+ assert_eq!(res.primary, 008);
+ assert_eq!(res.secondary, 000);
+ assert_eq!(res.payload_length, 0008);
+ assert_eq!(res.crc, 128);
+ assert_eq!(res.payload, vec![128, 040, 230, 002, 000, 002, 000, 010]);
+ }
+
+ #[test]
+ fn testcrc() {
+ let packets = [
+ &[
+ 0x10, 0x03, 0x08, 0x00, 0x08, 0x00, 0x05, 0x00, 0x13, 0x80, 0x40, 0x00, 0x0a, 0x71,
+ 0x00, 0xaa,
+ ][..],
+ &[
+ 0x03, 0xf1, 0x08, 0x00, 0x08, 0x00, 0x14, 0x00, 0x13, 0x80, 0x00, 0x00, 0x0f, 0xc7,
+ 0x00, 0xaa,
+ ][..],
+ &[
+ 0x10, 0x03, 0x05, 0x07, 0x09, 0x00, 0x01, 0x50, 0x00, 0x01, 0x00, 0xff, 0x14, 0xff,
+ 0xa6, 0x00, 0xaa,
+ ][..],
+ &[
+ 0xf1, 0xfe, 0x05, 0x03, 0x08, 0x01, 0x00, 0x40, 0xff, 0x30, 0xff, 0x00, 0x13, 0xd8,
+ 0x00, 0xaa,
+ ][..],
+ &[
+ 0x03, 0xfe, 0x05, 0x03, 0x08, 0x01, 0x00, 0x00, 0x00, 0x30, 0x17, 0x33, 0x13, 0x82,
+ 0x00, 0xaa,
+ ][..],
+ &[
+ // 00000d60: aaaa 1003 0507 09bb 044b 0300 80ff 54ff .........K....T.
+ // 00000d70: 0400 aaaa aaaa aaaa f1fe 0503 0801 0110 ................
+ 0x10, 0x03, 0x05, 0x07, 0x09, 0xbb, 0x04, 0x4b, 0x03, 0x00, 0x80, 0xff, 0x54, 0xff,
+ 0x04, 0x00, 0xaa,
+ ][..],
+ ];
+ for d in &packets {
+ let p = parse(d).unwrap();
+ assert_eq!(p.crc, p.calc_crc(), "{:?}", p);
+ }
+ }
+
+ #[test]
+ fn test_escape() {
+ let data: Vec<u8> = vec![
+ 170, // Syn
+ 170, // Syn
+ 3, // Source
+ 241, // Destination
+ 8, // primaryCommand
+ 0, // secondaryCommand
+ 8, // payloadLength
+ 128, // p1
+ 40, // p2
+ EBUS_ESCAPE, // p3
+ 1, // p4
+ EBUS_ESCAPE, // p5
+ 0, // p6
+ 0, // p7
+ 10, // p8
+ 128, // CRC
+ 0, // ACK
+ 170, // SYN
+ 170,
+ ];
+
+ let res = parse(&data).unwrap();
+ assert_eq!(res.source, 0003);
+ assert_eq!(res.destination, 241);
+ assert_eq!(res.primary, 008);
+ assert_eq!(res.secondary, 000);
+ assert_eq!(res.payload_length, 0008);
+ assert_eq!(res.crc, 128);
+ assert_eq!(
+ res.payload_decoded(),
+ vec![128, 040, EBUS_SYN, EBUS_ESCAPE, 000, 010]
+ );
+ }
+}