Skip to main content

tailtalk_packets/
atp.rs

1use byteorder::{BigEndian, ByteOrder};
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5pub enum AtpError {
6    #[error("invalid size - expected at least {expected} bytes but found {found}")]
7    InvalidSize { expected: usize, found: usize },
8    #[error("unknown function code {code}")]
9    UnknownFunction { code: u8 },
10}
11
12#[derive(Debug, Copy, Clone, PartialEq, Eq)]
13pub enum AtpFunction {
14    Request = 1,
15    Response = 2,
16    Release = 3,
17}
18
19impl TryFrom<u8> for AtpFunction {
20    type Error = AtpError;
21
22    fn try_from(value: u8) -> Result<Self, Self::Error> {
23        match value {
24            1 => Ok(AtpFunction::Request),
25            2 => Ok(AtpFunction::Response),
26            3 => Ok(AtpFunction::Release),
27            _ => Err(AtpError::UnknownFunction { code: value }),
28        }
29    }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct AtpPacket {
34    pub function: AtpFunction,
35    pub xo: bool,
36    pub eom: bool,
37    pub sts: bool,
38    pub bitmap_seq_num: u8,
39    pub tid: u16,
40    pub user_bytes: [u8; 4],
41}
42
43impl AtpPacket {
44    pub const HEADER_LEN: usize = 8;
45
46    pub fn parse(buf: &[u8]) -> Result<Self, AtpError> {
47        if buf.len() < Self::HEADER_LEN {
48            return Err(AtpError::InvalidSize {
49                expected: Self::HEADER_LEN,
50                found: buf.len(),
51            });
52        }
53
54        let control = buf[0];
55        // Function code is in bits 7-6
56        let function_code = (control >> 6) & 0x03;
57        let function = AtpFunction::try_from(function_code)?;
58
59        // XO: Bit 5
60        let xo = (control & 0x20) != 0;
61        // EOM: Bit 4
62        let eom = (control & 0x10) != 0;
63        // STS: Bit 3
64        let sts = (control & 0x08) != 0;
65
66        let bitmap_seq_num = buf[1];
67        let tid = BigEndian::read_u16(&buf[2..4]);
68        let mut user_bytes = [0u8; 4];
69        user_bytes.copy_from_slice(&buf[4..8]);
70
71        Ok(Self {
72            function,
73            xo,
74            eom,
75            sts,
76            bitmap_seq_num,
77            tid,
78            user_bytes,
79        })
80    }
81
82    pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, AtpError> {
83        let total_len = Self::HEADER_LEN;
84        if buf.len() < total_len {
85            return Err(AtpError::InvalidSize {
86                expected: total_len,
87                found: buf.len(),
88            });
89        }
90
91        let mut control = (self.function as u8) << 6;
92        if self.xo {
93            control |= 0x20;
94        }
95        if self.eom {
96            control |= 0x10;
97        }
98        if self.sts {
99            control |= 0x08;
100        }
101
102        buf[0] = control;
103        buf[1] = self.bitmap_seq_num;
104        BigEndian::write_u16(&mut buf[2..4], self.tid);
105        buf[4..8].copy_from_slice(&self.user_bytes);
106
107        Ok(total_len)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_parse_atp_request() {
117        // Request (01), XO (1), EOM (0), STS (0)
118        // Control: 01100000 = 0x60
119        // Bitmap: 0xFF
120        // TID: 0x1234
121        // User Bytes: 0x01, 0x02, 0x03, 0x04
122        // Data: 0xAA, 0xBB
123        let data: &[u8] = &[0x60, 0xFF, 0x12, 0x34, 0x01, 0x02, 0x03, 0x04, 0xAA, 0xBB];
124
125        let packet = AtpPacket::parse(data).expect("failed to parse");
126
127        assert_eq!(packet.function, AtpFunction::Request);
128        assert!(packet.xo);
129        assert!(!packet.eom);
130        assert!(!packet.sts);
131        assert_eq!(packet.bitmap_seq_num, 0xFF);
132        assert_eq!(packet.tid, 0x1234);
133        assert_eq!(packet.user_bytes, [1, 2, 3, 4]);
134    }
135
136    #[test]
137    fn test_encode_atp_response() {
138        let packet = AtpPacket {
139            function: AtpFunction::Response,
140            xo: false,
141            eom: true,
142            sts: false,
143            bitmap_seq_num: 1, // Sequence number 1
144            tid: 0x5678,
145            user_bytes: [0xDE, 0xAD, 0xBE, 0xEF],
146        };
147
148        // Response (10), XO (0), EOM (1), STS (0)
149        // Control: 10010000 = 0x90
150        let expected: &[u8] = &[0x90, 0x01, 0x56, 0x78, 0xDE, 0xAD, 0xBE, 0xEF];
151
152        let mut buf = [0u8; 8];
153        let len = packet.to_bytes(&mut buf).expect("failed to encode");
154
155        assert_eq!(len, 8);
156        assert_eq!(&buf, expected);
157    }
158
159    #[test]
160    fn test_round_trip() {
161        let original = AtpPacket {
162            function: AtpFunction::Release,
163            xo: false, // Release usually doesn't use XO/EOM/STS same way but structure allows it
164            eom: false,
165            sts: true,
166            bitmap_seq_num: 0,
167            tid: 9999,
168            user_bytes: [5, 6, 7, 8],
169        };
170
171        let mut buf = [0u8; 8];
172        let len = original.to_bytes(&mut buf).expect("failed to encode");
173        assert_eq!(len, 8);
174
175        let parsed = AtpPacket::parse(&buf).expect("failed to parse");
176        assert_eq!(original, parsed);
177    }
178}