etp/
protocol.rs

1use std::sync::atomic::AtomicU16;
2
3pub(crate) static PRIVATE_TX_ID: AtomicU16 = AtomicU16::new(0);
4
5pub(crate) fn get_transaction_id() -> u16 {
6    PRIVATE_TX_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst)
7}
8
9pub const ETP_MESSAGE_HEADER_SIZE: usize = 8;
10pub const ETP_MESSAGE_HEADER_STATUS_INDEX: usize = 3;
11#[derive(Clone, Debug)]
12pub(crate) struct EtpMessageHeader {
13    pub length: u16,
14    pub payload_type: EtpPayloadType,
15    pub status: EtpStatusCodes,
16    pub transaction_id: u16,
17    pub operation: EtpOperations,
18}
19
20impl EtpMessageHeader {
21    pub fn new(
22        payload_length: u16,
23        status: EtpStatusCodes,
24        payload_type: EtpPayloadType,
25        operation: EtpOperations,
26    ) -> Self {
27        EtpMessageHeader {
28            length: payload_length + ETP_MESSAGE_HEADER_SIZE as u16,
29            payload_type,
30            status,
31            transaction_id: get_transaction_id(),
32            operation,
33        }
34    }
35}
36
37impl Into<Vec<u8>> for EtpMessageHeader {
38    fn into(self) -> Vec<u8> {
39        let mut header = Vec::new();
40        // Everything is little endian
41        header.extend_from_slice(&self.length.to_le_bytes());
42        header.push(self.payload_type as u8);
43        header.push(self.status as u8);
44        header.extend_from_slice(&self.transaction_id.to_le_bytes());
45        let op = self.operation as u16;
46        header.extend_from_slice(&op.to_le_bytes());
47
48        header
49    }
50}
51
52impl TryFrom<&[u8]> for EtpMessageHeader {
53    type Error = &'static str;
54
55    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
56        if value.len() != 8 {
57            return Err("Invalid byte length for ETP message header");
58        }
59        let length = u16::from_le_bytes([value[0], value[1]]);
60        let payload_type = value[2];
61        let status = EtpStatusCodes::try_from(value[ETP_MESSAGE_HEADER_STATUS_INDEX])?;
62        let transaction_id = u16::from_le_bytes([value[4], value[5]]);
63        let operation = u16::from_le_bytes([value[6], value[7]]);
64
65        Ok(EtpMessageHeader {
66            length,
67            payload_type: EtpPayloadType::try_from(payload_type)
68                .map_err(|_| "Invalid payload type")?,
69            status,
70            transaction_id,
71            operation: EtpOperations::try_from(operation).map_err(|_| "Invalid operation")?,
72        })
73    }
74}
75
76#[repr(u16)]
77#[derive(Clone, Debug, PartialEq)]
78pub(crate) enum EtpOperations {
79    GetFirmwareInfo = 0x0000,
80    Reset = 0x0001,
81    GetSupportedOperations = 0x0002,
82    ConfigureTransport = 0x0003,
83    GetGpioInfo = 0x0100,
84    GpioInit = 0x0101,
85    GpioRead = 0x0102,
86    GpioWrite = 0x0103,
87    DebugPrint = 0x00db,
88}
89
90impl TryFrom<u16> for EtpOperations {
91    type Error = &'static str;
92
93    fn try_from(value: u16) -> Result<Self, Self::Error> {
94        match value {
95            0x0000 => Ok(EtpOperations::GetFirmwareInfo),
96            0x0001 => Ok(EtpOperations::Reset),
97            0x0002 => Ok(EtpOperations::GetSupportedOperations),
98            0x0003 => Ok(EtpOperations::ConfigureTransport),
99            0x0100 => Ok(EtpOperations::GetGpioInfo),
100            0x0101 => Ok(EtpOperations::GpioInit),
101            0x0102 => Ok(EtpOperations::GpioRead),
102            0x0103 => Ok(EtpOperations::GpioWrite),
103            0x00db => Ok(EtpOperations::DebugPrint),
104            _ => Err("Invalid ETP operation"),
105        }
106    }
107}
108
109#[repr(u8)]
110pub(crate) enum FirmwareInfoCommands {
111    // TODO: Protocol version?
112    Version = 0x01,
113    FirmwareVersion = 0x02,
114    BuildDate = 0x03,
115    HardwareType = 0x04,
116}
117
118impl TryFrom<u8> for FirmwareInfoCommands {
119    type Error = &'static str;
120
121    fn try_from(value: u8) -> Result<Self, Self::Error> {
122        match value {
123            0x01 => Ok(FirmwareInfoCommands::Version),
124            0x02 => Ok(FirmwareInfoCommands::FirmwareVersion),
125            0x03 => Ok(FirmwareInfoCommands::BuildDate),
126            0x04 => Ok(FirmwareInfoCommands::HardwareType),
127            _ => Err("Invalid firmware info command"),
128        }
129    }
130}
131
132#[repr(u8)]
133#[derive(Debug, Clone, PartialEq)]
134pub(crate) enum ResetType {
135    ReInit = 0x01,
136    SoftReset = 0x02,
137    HardReset = 0x03,
138    Bootloader = 0x04,
139}
140
141impl TryFrom<u8> for ResetType {
142    type Error = &'static str;
143
144    fn try_from(value: u8) -> Result<Self, Self::Error> {
145        match value {
146            0x01 => Ok(ResetType::ReInit),
147            0x02 => Ok(ResetType::SoftReset),
148            0x03 => Ok(ResetType::HardReset),
149            0x04 => Ok(ResetType::Bootloader),
150            _ => Err("Invalid reset type"),
151        }
152    }
153}
154
155#[repr(u8)]
156#[derive(Debug, Clone, PartialEq)]
157pub(crate) enum EtpPayloadType {
158    Command = 0x01,
159    Data = 0x02,
160    Response = 0x03,
161    Event = 0x04,
162}
163
164impl TryFrom<u8> for EtpPayloadType {
165    type Error = &'static str;
166
167    fn try_from(value: u8) -> Result<Self, Self::Error> {
168        match value {
169            0x01 => Ok(EtpPayloadType::Command),
170            0x02 => Ok(EtpPayloadType::Data),
171            0x03 => Ok(EtpPayloadType::Response),
172            0x04 => Ok(EtpPayloadType::Event),
173            _ => Err("Invalid ETP payload type"),
174        }
175    }
176}
177
178#[repr(u8)]
179#[derive(Debug, Clone, PartialEq)]
180pub(crate) enum TranportTypes {
181    Uart = 0x01,
182    Usb = 0x02,
183    Wifi = 0x03,
184    Ble = 0x04,
185    Bluetooth = 0x05,
186    TcpIp = 0x06,
187}
188
189impl TryFrom<u8> for TranportTypes {
190    type Error = &'static str;
191
192    fn try_from(value: u8) -> Result<Self, Self::Error> {
193        match value {
194            0x01 => Ok(TranportTypes::Uart),
195            0x02 => Ok(TranportTypes::Usb),
196            0x03 => Ok(TranportTypes::Wifi),
197            0x04 => Ok(TranportTypes::Ble),
198            0x05 => Ok(TranportTypes::Bluetooth),
199            0x06 => Ok(TranportTypes::TcpIp),
200            _ => Err("Invalid transport type"),
201        }
202    }
203}
204
205#[derive(Debug, Clone, PartialEq)]
206pub(crate) enum EtpStatusCodes {
207    Success = 0x00,
208    InvalidCommand = 0x01,
209    InvalidParameter = 0x02,
210    InvalidPort = 0x40,
211    Failure = 0xFF,
212}
213
214impl TryFrom<u8> for EtpStatusCodes {
215    type Error = &'static str;
216
217    fn try_from(value: u8) -> Result<Self, Self::Error> {
218        match value {
219            0x00 => Ok(EtpStatusCodes::Success),
220            0x01 => Ok(EtpStatusCodes::InvalidCommand),
221            0x02 => Ok(EtpStatusCodes::InvalidParameter),
222            0x40 => Ok(EtpStatusCodes::InvalidPort),
223            0xFF => Ok(EtpStatusCodes::Failure),
224            _ => Err("Invalid ETP status code"),
225        }
226    }
227}
228
229#[derive(Debug, Clone, PartialEq)]
230pub(crate) enum EtpFirmwareInfoCmd {
231    ProtocolVersion = 0x01,
232    FirmwareVersion = 0x02,
233    BuildDate = 0x03,
234    HardwareType = 0x04,
235}
236
237impl TryFrom<u8> for EtpFirmwareInfoCmd {
238    type Error = &'static str;
239
240    fn try_from(value: u8) -> Result<Self, Self::Error> {
241        match value {
242            0x01 => Ok(EtpFirmwareInfoCmd::ProtocolVersion),
243            0x02 => Ok(EtpFirmwareInfoCmd::FirmwareVersion),
244            0x03 => Ok(EtpFirmwareInfoCmd::BuildDate),
245            0x04 => Ok(EtpFirmwareInfoCmd::HardwareType),
246            _ => Err("Invalid ETP firmware info command"),
247        }
248    }
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254
255    #[test]
256    fn test_get_transaction_id() {
257        let tx_id = get_transaction_id();
258        assert_eq!(tx_id, 0);
259        let tx_id = get_transaction_id();
260        assert_eq!(tx_id, 1);
261
262        PRIVATE_TX_ID.store(0xFFFF, std::sync::atomic::Ordering::SeqCst);
263        let tx_id = get_transaction_id();
264        assert_eq!(tx_id, 0xFFFF);
265
266        let tx_id = get_transaction_id();
267        assert_eq!(tx_id, 0);
268    }
269
270    #[test]
271    fn test_message_header() {
272        let header = EtpMessageHeader {
273            length: 0x1234,
274            payload_type: EtpPayloadType::Command,
275            status: EtpStatusCodes::Success,
276            transaction_id: 0x5678,
277            operation: EtpOperations::GetFirmwareInfo,
278        };
279
280        let header_bytes: Vec<u8> = header.clone().into();
281        assert_eq!(header_bytes.len(), ETP_MESSAGE_HEADER_SIZE);
282
283        let parsed_header = EtpMessageHeader::try_from(&header_bytes[..]).unwrap();
284        assert_eq!(parsed_header.length, header.length);
285        assert_eq!(parsed_header.payload_type, header.payload_type);
286        assert_eq!(parsed_header.transaction_id, header.transaction_id);
287        assert_eq!(parsed_header.operation, header.operation);
288
289        assert_eq!(header_bytes[0], 0x34);
290        assert_eq!(header_bytes[1], 0x12);
291        assert_eq!(header_bytes[2], 0x01);
292        assert_eq!(header_bytes[3], 0x00);
293        assert_eq!(header_bytes[4], 0x78);
294        assert_eq!(header_bytes[5], 0x56);
295        assert_eq!(header_bytes[6], 0x00);
296        assert_eq!(header_bytes[7], 0x00);
297
298        let header_replica: EtpMessageHeader =
299            EtpMessageHeader::try_from(&header_bytes[..]).unwrap();
300
301        assert_eq!(header_replica.length, header.length);
302        assert_eq!(header_replica.payload_type, header.payload_type);
303        assert_eq!(header_replica.status, header.status);
304        assert_eq!(header_replica.transaction_id, header.transaction_id);
305        assert_eq!(header_replica.operation, header.operation);
306    }
307}