1use core::fmt::Debug;
4use thiserror::Error;
5
6use crate::{
7 COMMAND_HEADER, REPLY_HEADER,
8 crc::{VEX_CRC16, crc16},
9 decode::{Decode, DecodeError, DecodeErrorKind},
10 encode::{Encode, MessageEncoder},
11 varint::VarU16,
12};
13
14pub mod controller;
15pub mod factory;
16pub mod file;
17pub mod system;
18
19pub mod ecmds {
21 pub const FILE_CTRL: u8 = 0x10;
23 pub const FILE_INIT: u8 = 0x11;
24 pub const FILE_EXIT: u8 = 0x12;
25 pub const FILE_WRITE: u8 = 0x13;
26 pub const FILE_READ: u8 = 0x14;
27 pub const FILE_LINK: u8 = 0x15;
28 pub const FILE_DIR: u8 = 0x16;
29 pub const FILE_DIR_ENTRY: u8 = 0x17;
30 pub const FILE_LOAD: u8 = 0x18;
31 pub const FILE_GET_INFO: u8 = 0x19;
32 pub const FILE_SET_INFO: u8 = 0x1A;
33 pub const FILE_ERASE: u8 = 0x1B;
34 pub const FILE_USER_STAT: u8 = 0x1C;
35 pub const FILE_VISUALIZE: u8 = 0x1D;
36 pub const FILE_CLEANUP: u8 = 0x1E;
37 pub const FILE_FORMAT: u8 = 0x1F;
38
39 pub const SYS_FLAGS: u8 = 0x20;
41 pub const DEV_STATUS: u8 = 0x21;
42 pub const SYS_STATUS: u8 = 0x22;
43 pub const FDT_STATUS: u8 = 0x23;
44 pub const LOG_STATUS: u8 = 0x24;
45 pub const LOG_READ: u8 = 0x25;
46 pub const RADIO_STATUS: u8 = 0x26;
47 pub const USER_READ: u8 = 0x27;
48 pub const SYS_SCREEN_CAP: u8 = 0x28;
49 pub const SYS_USER_PROG: u8 = 0x29;
50 pub const SYS_DASH_TOUCH: u8 = 0x2A;
51 pub const SYS_DASH_SEL: u8 = 0x2B;
52 pub const SYS_DASH_EBL: u8 = 0x2C;
53 pub const SYS_DASH_DIS: u8 = 0x2D;
54 pub const SYS_KV_LOAD: u8 = 0x2E;
55 pub const SYS_KV_SAVE: u8 = 0x2F;
56
57 pub const SYS_C_INFO_14: u8 = 0x31;
59 pub const SYS_C_INFO_58: u8 = 0x32;
60
61 pub const CON_RADIO_INFO: u8 = 0x35;
63 pub const CON_VER_FLASH: u8 = 0x39;
64 pub const CON_RADIO_MODE: u8 = 0x41;
65 pub const CON_VER_EXPECT: u8 = 0x49;
66 pub const CON_FLASH_ERASE: u8 = 0x3B;
67 pub const CON_FLASH_WRITE: u8 = 0x3C;
68 pub const CON_FLASH_VALIDATE: u8 = 0x3E;
69 pub const CON_RADIO_FORCE: u8 = 0x3F;
70 pub const CON_COMP_CTRL: u8 = 0xC1;
71
72 pub const FACTORY_STATUS: u8 = 0xF1;
74 pub const FACTORY_RESET: u8 = 0xF2;
75 pub const FACTORY_PING: u8 = 0xF4;
76 pub const FACTORY_PONG: u8 = 0xF5;
77 pub const FACTORY_HW_STATUS: u8 = 0xF9;
78 pub const FACTORY_CHAL: u8 = 0xFC;
79 pub const FACTORY_RESP: u8 = 0xFD;
80 pub const FACTORY_SPECIAL: u8 = 0xFE;
81 pub const FACTORY_EBL: u8 = 0xFF;
82}
83
84#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
90#[repr(u8)]
91pub enum Cdc2Ack {
92 #[error("Packet was recieved successfully. (NACK 0x76)")]
94 Ack = 0x76,
95
96 #[error("V5 device sent back a general negative-acknowledgement. (NACK 0xFF)")]
98 Nack = 0xFF,
99
100 #[error("Packet CRC checksum did not validate. (NACK 0xCE)")]
102 NackPacketCrc = 0xCE,
103
104 #[error("Packet payload length was either too short or too long. (NACK 0xD0)")]
106 NackPacketLength = 0xD0,
107
108 #[error("Attempted to transfer too much data. (NACK 0xD1)")]
110 NackTransferSize = 0xD1,
111
112 #[error("Program CRC checksum did not validate. (NACK 0xD2)")]
114 NackProgramCrc = 0xD2,
115
116 #[error("Invalid program file. (NACK 0xD3)")]
118 NackProgramFile = 0xD3,
119
120 #[error(
122 "Attempted to perform a file transfer operation before one was initialized. (NACK 0xD4)"
123 )]
124 NackUninitializedTransfer = 0xD4,
125
126 #[error("File transfer was initialized incorrectly. (NACK 0xD5)")]
128 NackInvalidInitialization = 0xD5,
129
130 #[error("File transfer was not padded to a four byte boundary. (NACK 0xD6)")]
132 NackAlignment = 0xD6,
133
134 #[error("File transfer address did not match. (NACK 0xD7)")]
136 NackAddress = 0xD7,
137
138 #[error("File transfer download length did not match. (NACK 0xD8)")]
140 NackIncomplete = 0xD8,
141
142 #[error("Attempted to transfer file to a directory that does not exist. (NACK 0xD9)")]
144 NackNoDirectory = 0xD9,
145
146 #[error("Limit for user files has been reached. (NACK 0xDA)")]
148 NackMaxUserFiles = 0xDA,
149
150 #[error("File already exists. (NACK 0xDB)")]
152 NackFileAlreadyExists = 0xDB,
153
154 #[error("Filesystem storage is full. (NACK 0xDC)")]
156 NackFileStorageFull = 0xDC,
157
158 #[error("Packet timed out. (NACK 0x00)")]
160 Timeout = 0x00,
161
162 #[error("Internal write error occurred. (NACK 0x01)")]
164 WriteError = 0x01,
165}
166
167impl Decode for Cdc2Ack {
168 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
169 match u8::decode(data)? {
170 0x76 => Ok(Self::Ack),
171 0xFF => Ok(Self::Nack),
172 0xCE => Ok(Self::NackPacketCrc),
173 0xD0 => Ok(Self::NackPacketLength),
174 0xD1 => Ok(Self::NackTransferSize),
175 0xD2 => Ok(Self::NackProgramCrc),
176 0xD3 => Ok(Self::NackProgramFile),
177 0xD4 => Ok(Self::NackUninitializedTransfer),
178 0xD5 => Ok(Self::NackInvalidInitialization),
179 0xD6 => Ok(Self::NackAlignment),
180 0xD7 => Ok(Self::NackAddress),
181 0xD8 => Ok(Self::NackIncomplete),
182 0xD9 => Ok(Self::NackNoDirectory),
183 0xDA => Ok(Self::NackMaxUserFiles),
184 0xDB => Ok(Self::NackFileAlreadyExists),
185 0xDC => Ok(Self::NackFileStorageFull),
186 0x00 => Ok(Self::Timeout),
187 0x01 => Ok(Self::WriteError),
188 v => Err(DecodeError::new::<Self>(DecodeErrorKind::UnexpectedByte {
189 name: "Cdc2Ack",
190 value: v,
191 expected: &[
192 0x76, 0xFF, 0xCE, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
193 0xDA, 0xDB, 0xDC, 0x00, 0x01,
194 ],
195 })),
196 }
197 }
198}
199
200#[derive(Debug, Clone, Copy, Eq, PartialEq)]
229pub struct Cdc2CommandPacket<const CMD: u8, const ECMD: u8, P: Encode> {
230 payload: P,
231}
232
233impl<P: Encode, const CMD: u8, const ECMD: u8> Cdc2CommandPacket<CMD, ECMD, P> {
234 pub const HEADER: [u8; 4] = COMMAND_HEADER;
235
236 pub fn new(payload: P) -> Self {
240 Self { payload }
241 }
242}
243
244impl<const CMD: u8, const ECMD: u8, P: Encode> Encode for Cdc2CommandPacket<CMD, ECMD, P> {
245 fn size(&self) -> usize {
246 let payload_size = self.payload.size();
247
248 8 + if payload_size > (u8::MAX >> 1) as _ {
249 2
250 } else {
251 1
252 } + payload_size
253 }
254
255 fn encode(&self, data: &mut [u8]) {
256 Self::HEADER.encode(data);
257 data[4] = CMD;
258 data[5] = ECMD;
259
260 let mut enc = MessageEncoder::new_with_position(data, 6);
261
262 enc.write(&VarU16::new(self.payload.size() as u16));
264 enc.write(&self.payload);
265
266 let crc16 = VEX_CRC16.checksum(&enc.get_ref()[0..enc.position()]);
269 enc.write(&crc16.to_be_bytes());
270 }
271}
272
273#[derive(Clone, Copy, Eq, PartialEq)]
306pub struct Cdc2ReplyPacket<const CMD: u8, const ECMD: u8, P: Decode> {
307 pub size: u16,
309
310 pub payload: Result<P, Cdc2Ack>,
312
313 pub crc16: u16,
315}
316
317impl<const CMD: u8, const ECMD: u8, P: Decode> Cdc2ReplyPacket<CMD, ECMD, P> {
318 pub const HEADER: [u8; 2] = REPLY_HEADER;
319
320 pub fn ack(&self) -> Cdc2Ack {
321 *self.payload.as_ref().err().unwrap_or(&Cdc2Ack::Ack)
322 }
323}
324
325impl<const CMD: u8, const ECMD: u8, P: Decode> Decode for Cdc2ReplyPacket<CMD, ECMD, P> {
326 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
327 let original_data = *data;
328
329 if <[u8; 2]>::decode(data)? != Self::HEADER {
330 return Err(DecodeError::new::<Self>(DecodeErrorKind::InvalidHeader));
331 }
332
333 let cmd = u8::decode(data)?;
334 if cmd != CMD {
335 return Err(DecodeError::new::<Self>(DecodeErrorKind::UnexpectedByte {
336 name: "cmd",
337 value: cmd,
338 expected: &[CMD],
339 }));
340 }
341
342 let payload_size = VarU16::decode(data)?;
343 let payload_size_size = payload_size.size() as usize;
344 let payload_size = payload_size.into_inner();
345
346 let expected_crc16 =
353 crc16::<Self>(original_data.get(0..((payload_size as usize) + payload_size_size + 1)))?;
354
355 let ecmd = u8::decode(data)?;
356 if ecmd != ECMD {
357 return Err(DecodeError::new::<Self>(DecodeErrorKind::UnexpectedByte {
358 name: "ecmd",
359 value: ecmd,
360 expected: &[ECMD],
361 }));
362 }
363
364 let ack = Cdc2Ack::decode(data)?;
365 let mut payload_data = data
366 .get(0..(payload_size as usize) - 4)
367 .ok_or_else(|| DecodeError::new::<Self>(DecodeErrorKind::UnexpectedEnd))?;
368
369 *data = &data[(payload_size as usize) - 4..];
370 let payload = if ack == Cdc2Ack::Ack {
371 Ok(P::decode(&mut payload_data)?)
372 } else {
373 Err(ack)
374 };
375
376 let crc16 = u16::decode(data)?;
377 if crc16 != expected_crc16 {
378 return Err(DecodeError::new::<Self>(DecodeErrorKind::Checksum {
379 value: crc16,
380 expected: expected_crc16,
381 }));
382 }
383
384 Ok(Self {
385 size: payload_size,
386 payload,
387 crc16,
388 })
389 }
390}