Skip to main content

tailtalk_packets/
llap.rs

1/// Packets encapsulated in LocalTalk Link Access Protocol format. Typically only seen on actual LocalTalk
2/// networks but interestingly AsanteTalk (and maybe others) default to this format if they do not see
3/// other Ethernet traffic on the port during boot up.
4use thiserror::Error;
5
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7#[repr(u8)]
8pub enum LlapType {
9    DdpShort = 1,
10    DdpLong = 2,
11    Enquiry = 0x81,
12    Acknowledge = 0x82,
13    Other(u8),
14}
15
16impl From<u8> for LlapType {
17    fn from(orig: u8) -> Self {
18        match orig {
19            1 => LlapType::DdpShort,
20            2 => LlapType::DdpLong,
21            0x81 => LlapType::Enquiry,
22            0x82 => LlapType::Acknowledge,
23            n => LlapType::Other(n),
24        }
25    }
26}
27
28#[derive(Debug)]
29pub struct LlapPacket {
30    pub dst_node: u8,
31    pub src_node: u8,
32    pub type_: LlapType,
33}
34
35#[derive(Error, Debug)]
36pub enum LlapError {
37    #[error("packet too short")]
38    TooShort,
39}
40
41impl LlapPacket {
42    pub const LEN: usize = 3;
43
44    pub fn parse(buf: &[u8]) -> Result<Self, LlapError> {
45        if buf.len() < Self::LEN {
46            return Err(LlapError::TooShort);
47        }
48        Ok(Self {
49            dst_node: buf[0],
50            src_node: buf[1],
51            type_: buf[2].into(),
52        })
53    }
54
55    pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, LlapError> {
56        if buf.len() < Self::LEN {
57            return Err(LlapError::TooShort);
58        }
59        buf[0] = self.dst_node;
60        buf[1] = self.src_node;
61        buf[2] = match self.type_ {
62            LlapType::DdpShort => 1,
63            LlapType::DdpLong => 2,
64            LlapType::Enquiry => 0x81,
65            LlapType::Acknowledge => 0x82,
66            LlapType::Other(n) => n,
67        };
68        Ok(Self::LEN)
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_ethernet() {
78        let packet = LlapPacket {
79            dst_node: 0x42,
80            src_node: 0x24,
81            type_: LlapType::DdpShort,
82        };
83        let mut buf = [0u8; 3];
84        let len = packet.to_bytes(&mut buf).unwrap();
85        assert_eq!(len, 3);
86        assert_eq!(buf, [0x42, 0x24, 1]);
87
88        let parsed = LlapPacket::parse(&buf).unwrap();
89        assert_eq!(parsed.dst_node, 0x42);
90        assert_eq!(parsed.src_node, 0x24);
91        assert_eq!(parsed.type_, LlapType::DdpShort);
92    }
93
94    #[test]
95    fn test_localtalk_data() {
96        let packet = LlapPacket {
97            dst_node: 0x42,
98            src_node: 0x24,
99            type_: LlapType::DdpLong,
100        };
101        let mut buf = [0u8; 3];
102        let len = packet.to_bytes(&mut buf).unwrap();
103        assert_eq!(len, 3);
104        assert_eq!(buf[0], 0x42);
105        assert_eq!(buf[1], 0x24);
106        assert_eq!(buf[2], 2);
107
108        let parsed = LlapPacket::parse(&buf).unwrap();
109        assert_eq!(parsed.dst_node, 0x42);
110        assert_eq!(parsed.src_node, 0x24);
111        assert_eq!(parsed.type_, LlapType::DdpLong);
112    }
113
114    #[test]
115    fn test_localtalk_control() {
116        let packet = LlapPacket {
117            dst_node: 0xFF,
118            src_node: 0x11,
119            type_: LlapType::Enquiry, // ENQ
120        };
121        let mut buf = [0u8; 3];
122        let len = packet.to_bytes(&mut buf).unwrap();
123        assert_eq!(len, 3);
124        assert_eq!(buf, [0xFF, 0x11, 0x81]);
125
126        let parsed = LlapPacket::parse(&buf).unwrap();
127        assert_eq!(parsed.dst_node, 0xFF);
128        assert_eq!(parsed.src_node, 0x11);
129        assert_eq!(parsed.type_, LlapType::Enquiry);
130    }
131}