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 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 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}