1use core::str;
4
5use alloc::vec::Vec;
6
7use crate::{
8 Decode, DecodeError, DecodeWithLength, Encode, FixedString, Version,
9 cdc::{CdcReplyPacket, cmds::USER_CDC},
10 cdc2::{
11 Cdc2Ack, Cdc2CommandPacket, Cdc2ReplyPacket,
12 ecmds::{
13 FILE_CLEANUP, FILE_CTRL, FILE_DIR, FILE_DIR_ENTRY, FILE_ERASE, FILE_EXIT, FILE_FORMAT,
14 FILE_GET_INFO, FILE_INIT, FILE_LINK, FILE_LOAD, FILE_READ, FILE_SET_INFO,
15 FILE_USER_STAT, FILE_WRITE,
16 },
17 },
18 decode::DecodeErrorKind,
19};
20
21#[derive(Debug, Clone, Copy, Eq, PartialEq)]
22#[repr(u8)]
23pub enum FileTransferOperation {
24 Write = 1,
26
27 Read = 2,
29}
30
31#[repr(u8)]
32#[derive(Debug, Clone, Copy, Eq, PartialEq)]
33pub enum FileInitOption {
34 None = 0,
35 Overwrite = 1,
36}
37
38#[repr(u8)]
39#[derive(Debug, Clone, Copy, Eq, PartialEq)]
40pub enum FileTransferTarget {
41 Ddr = 0,
42 Qspi = 1,
43 Cbuf = 2,
44 Vbuf = 3,
45 Ddrc = 4,
46 Ddre = 5,
47 Flash = 6,
48 Radio = 7,
49 A1 = 13,
50 B1 = 14,
51 B2 = 15,
52}
53
54#[repr(u8)]
55#[derive(Debug, Clone, Copy, Eq, PartialEq)]
56pub enum FileVendor {
57 User = 1,
58 Sys = 15,
59 Dev1 = 16,
60 Dev2 = 24,
61 Dev3 = 32,
62 Dev4 = 40,
63 Dev5 = 48,
64 Dev6 = 56,
65 VexVm = 64,
66 Vex = 240,
67 Undefined = 241,
68}
69impl Decode for FileVendor {
70 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
71 match u8::decode(data)? {
72 1 => Ok(Self::User),
73 15 => Ok(Self::Sys),
74 16 => Ok(Self::Dev1),
75 24 => Ok(Self::Dev2),
76 32 => Ok(Self::Dev3),
77 40 => Ok(Self::Dev4),
78 48 => Ok(Self::Dev5),
79 56 => Ok(Self::Dev6),
80 64 => Ok(Self::VexVm),
81 240 => Ok(Self::Vex),
82 241 => Ok(Self::Undefined),
83 v => Err(DecodeError::new::<Self>(DecodeErrorKind::UnexpectedByte {
84 name: "FileVendor",
85 value: v,
86 expected: &[
87 0x01, 0x0F, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0xF0, 0xF1,
88 ],
89 })),
90 }
91 }
92}
93
94#[repr(u8)]
95#[derive(Debug, Clone, Copy, Eq, PartialEq)]
96pub enum FileLoadAction {
97 Run = 0,
98 Stop = 128,
99}
100
101#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
102#[repr(u8)]
103pub enum ExtensionType {
104 #[default]
106 Binary = 0x0,
107
108 Vm = 0x61,
113
114 EncryptedBinary = 0x73,
116}
117
118impl Decode for ExtensionType {
119 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
120 Ok(match u8::decode(data)? {
121 0x0 => Self::Binary,
122 0x61 => Self::Vm,
123 0x73 => Self::EncryptedBinary,
124 unknown => {
125 return Err(DecodeError::new::<Self>(DecodeErrorKind::UnexpectedByte {
126 name: "ExtensionType",
127 value: unknown,
128 expected: &[0x0],
129 }));
130 }
131 })
132 }
133}
134
135#[derive(Debug, Clone, Eq, PartialEq)]
136pub struct FileMetadata {
137 pub extension: FixedString<3>,
138 pub extension_type: ExtensionType,
139 pub timestamp: i32,
140 pub version: Version,
141}
142
143impl Encode for FileMetadata {
144 fn size(&self) -> usize {
145 12
146 }
147
148 fn encode(&self, data: &mut [u8]) {
149 data[..self.extension.len()].copy_from_slice(self.extension.as_bytes());
150 data[3] = self.extension_type as _;
151 self.timestamp.encode(&mut data[4..]);
152 self.version.encode(&mut data[8..]);
153 }
154}
155
156impl Decode for FileMetadata {
157 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
158 Ok(Self {
159 extension: unsafe {
161 FixedString::new_unchecked(
162 str::from_utf8(&<[u8; 3]>::decode(data)?)
163 .map_err(|e| DecodeError::new::<Self>(e.into()))?,
164 )
165 },
166 extension_type: Decode::decode(data).unwrap(),
167 timestamp: i32::decode(data)?,
168 version: Version::decode(data)?,
169 })
170 }
171}
172
173pub type FileTransferInitializePacket =
175 Cdc2CommandPacket<USER_CDC, FILE_INIT, FileTransferInitializePayload>;
176pub type FileTransferInitializeReplyPacket =
177 Cdc2ReplyPacket<USER_CDC, FILE_INIT, FileTransferInitializeReplyPayload>;
178
179#[derive(Debug, Clone, Eq, PartialEq)]
180pub struct FileTransferInitializePayload {
181 pub operation: FileTransferOperation,
182 pub target: FileTransferTarget,
183 pub vendor: FileVendor,
184 pub options: FileInitOption,
185 pub file_size: u32,
186 pub load_address: u32,
187 pub write_file_crc: u32,
188 pub metadata: FileMetadata,
189 pub file_name: FixedString<23>,
190}
191
192impl Encode for FileTransferInitializePayload {
193 fn size(&self) -> usize {
194 28 + self.file_name.size()
195 }
196
197 fn encode(&self, data: &mut [u8]) {
198 [
199 self.operation as u8,
200 self.target as u8,
201 self.vendor as u8,
202 self.options as u8,
203 ]
204 .encode(data);
205 self.file_size.encode(&mut data[4..]);
206 self.load_address.encode(&mut data[8..]);
207 self.write_file_crc.encode(&mut data[12..]);
208 self.metadata.encode(&mut data[16..]);
209 self.file_name.encode(&mut data[28..]);
210 }
211}
212
213#[derive(Debug, Clone, Copy, Eq, PartialEq)]
214pub struct FileTransferInitializeReplyPayload {
215 pub window_size: u16,
217
218 pub file_size: u32,
222
223 pub file_crc: u32,
227}
228
229impl Decode for FileTransferInitializeReplyPayload {
230 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
231 let window_size = u16::decode(data)?;
232 let file_size = u32::decode(data)?;
233 let file_crc = u32::decode(data)?.swap_bytes();
235 Ok(Self {
236 window_size,
237 file_size,
238 file_crc,
239 })
240 }
241}
242
243pub type FileTransferExitPacket = Cdc2CommandPacket<USER_CDC, FILE_EXIT, FileExitAction>;
245pub type FileTransferExitReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_EXIT, ()>;
246
247#[repr(u8)]
249#[derive(Debug, Clone, Copy, Eq, PartialEq)]
250pub enum FileExitAction {
251 DoNothing = 0,
252 RunProgram = 1,
253 Halt = 2,
254 ShowRunScreen = 3,
255}
256impl Encode for FileExitAction {
257 fn size(&self) -> usize {
258 1
259 }
260 fn encode(&self, data: &mut [u8]) {
261 data[0] = *self as _;
262 }
263}
264pub type FileDataWritePacket = Cdc2CommandPacket<USER_CDC, FILE_WRITE, FileDataWritePayload>;
266pub type FileDataWriteReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_WRITE, ()>;
267
268#[derive(Debug, Clone, Eq, PartialEq)]
269pub struct FileDataWritePayload {
270 pub address: i32,
272
273 pub chunk_data: Vec<u8>,
275}
276impl Encode for FileDataWritePayload {
277 fn size(&self) -> usize {
278 4 + self.chunk_data.len()
279 }
280
281 fn encode(&self, data: &mut [u8]) {
282 self.address.encode(data);
283 self.chunk_data.encode(&mut data[4..]);
284 }
285}
286
287pub type FileDataReadPacket = Cdc2CommandPacket<USER_CDC, FILE_READ, FileDataReadPayload>;
289pub type FileDataReadReplyPacket = CdcReplyPacket<USER_CDC, FileDataReadReplyPayload>;
291
292#[derive(Debug, Clone, Copy, Eq, PartialEq)]
293pub struct FileDataReadPayload {
294 pub address: u32,
296
297 pub size: u16,
299}
300impl Encode for FileDataReadPayload {
301 fn size(&self) -> usize {
302 6
303 }
304
305 fn encode(&self, data: &mut [u8]) {
306 self.address.encode(data);
307 self.size.encode(&mut data[4..]);
308 }
309}
310
311#[derive(Debug, Clone, Eq, PartialEq)]
312pub enum FileDataReadReplyContents {
313 Ack { address: u32, data: Vec<u8> },
314 Nack(Cdc2Ack),
315}
316
317impl Decode for FileDataReadReplyContents {
318 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
319 if data.len() == 1 {
320 Ok(Self::Nack(Cdc2Ack::decode(data)?))
321 } else {
322 let address = u32::decode(data)?;
323 let chunk_data = Vec::decode_with_len(data, data.len())?;
324
325 Ok(Self::Ack {
326 address,
327 data: chunk_data,
328 })
329 }
330 }
331}
332
333#[derive(Debug, Clone, Eq, PartialEq)]
334pub struct FileDataReadReplyPayload {
335 pub contents: FileDataReadReplyContents,
336 pub crc: u16,
337}
338impl Decode for FileDataReadReplyPayload {
339 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
340 let ecmd = u8::decode(data)?;
341 if ecmd != FILE_READ {
342 return Err(DecodeError::new::<Self>(DecodeErrorKind::UnexpectedByte {
343 name: "ecmd",
344 value: ecmd,
345 expected: &[FILE_READ],
346 }));
347 }
348
349 let contents = FileDataReadReplyContents::decode(
350 &mut data
351 .get(..data.len() - 2)
352 .ok_or_else(|| DecodeError::new::<Self>(DecodeErrorKind::UnexpectedEnd))?,
353 )?;
354 *data = &data[data.len() - 2..];
355
356 let crc = u16::decode(data)?.swap_bytes();
357
358 Ok(Self { contents, crc })
359 }
360}
361impl FileDataReadReplyPayload {
362 pub fn unwrap(self) -> Result<(u32, Vec<u8>), Cdc2Ack> {
363 match self.contents {
364 FileDataReadReplyContents::Ack { address, data } => Ok((address, data)),
365 FileDataReadReplyContents::Nack(nack) => Err(nack),
366 }
367 }
368}
369
370pub type FileLinkPacket = Cdc2CommandPacket<USER_CDC, FILE_LINK, FileLinkPayload>;
374pub type FileLinkReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_LINK, ()>;
375
376#[derive(Debug, Clone, Eq, PartialEq)]
377pub struct FileLinkPayload {
378 pub vendor: FileVendor,
379 pub reserved: u8,
381 pub required_file: FixedString<23>,
382}
383impl Encode for FileLinkPayload {
384 fn size(&self) -> usize {
385 2 + self.required_file.size()
386 }
387
388 fn encode(&self, data: &mut [u8]) {
389 data[0] = self.vendor as _;
390 data[1] = self.reserved;
391 self.required_file.encode(&mut data[2..]);
392 }
393}
394
395pub type DirectoryFileCountPacket =
396 Cdc2CommandPacket<USER_CDC, FILE_DIR, DirectoryFileCountPayload>;
397pub type DirectoryFileCountReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_DIR, u16>;
398
399#[derive(Debug, Clone, Copy, Eq, PartialEq)]
400pub struct DirectoryFileCountPayload {
401 pub vendor: FileVendor,
402 pub reserved: u8,
404}
405impl Encode for DirectoryFileCountPayload {
406 fn size(&self) -> usize {
407 2
408 }
409 fn encode(&self, data: &mut [u8]) {
410 data[0] = self.vendor as _;
411 data[1] = self.reserved;
412 }
413}
414
415pub type DirectoryEntryPacket = Cdc2CommandPacket<USER_CDC, FILE_DIR_ENTRY, DirectoryEntryPayload>;
416pub type DirectoryEntryReplyPacket =
417 Cdc2ReplyPacket<USER_CDC, FILE_DIR_ENTRY, DirectoryEntryReplyPayload>;
418
419#[derive(Debug, Clone, Copy, Eq, PartialEq)]
420pub struct DirectoryEntryPayload {
421 pub file_index: u8,
422 pub reserved: u8,
423}
424impl Encode for DirectoryEntryPayload {
425 fn size(&self) -> usize {
426 2
427 }
428
429 fn encode(&self, data: &mut [u8]) {
430 data[0] = self.file_index;
431 data[1] = self.reserved;
432 }
433}
434
435#[derive(Debug, Clone, PartialEq, Eq)]
436pub struct DirectoryEntryReplyPayload {
437 pub file_index: u8,
438 pub size: u32,
439
440 pub load_address: u32,
442 pub crc: u32,
443
444 pub metadata: Option<FileMetadata>,
445 pub file_name: FixedString<23>,
446}
447
448impl Decode for DirectoryEntryReplyPayload {
449 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
450 let file_index = u8::decode(data)?;
451 let size = u32::decode(data)?;
452 let load_address = u32::decode(data)?;
453 let crc = u32::decode(data)?;
454
455 let metadata = if data.get(0) == Some(&255) {
456 let _ = <[u8; 12]>::decode(data);
457 None
458 } else {
459 Some(FileMetadata::decode(data)?)
460 };
461
462 let file_name = FixedString::<23>::decode(data)?;
463
464 Ok(Self {
465 file_index,
466 size,
467 load_address,
468 crc,
469 metadata,
470 file_name,
471 })
472 }
473}
474
475pub type FileLoadActionPacket = Cdc2CommandPacket<USER_CDC, FILE_LOAD, FileLoadActionPayload>;
477pub type FileLoadActionReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_LOAD, ()>;
478
479#[derive(Debug, Clone, Eq, PartialEq)]
480pub struct FileLoadActionPayload {
481 pub vendor: FileVendor,
482 pub action: FileLoadAction,
483 pub file_name: FixedString<23>,
484}
485impl Encode for FileLoadActionPayload {
486 fn size(&self) -> usize {
487 2 + self.file_name.size()
488 }
489
490 fn encode(&self, data: &mut [u8]) {
491 data[0] = self.vendor as _;
492 data[1] = self.action as _;
493 self.file_name.encode(&mut data[2..]);
494 }
495}
496pub type FileMetadataPacket = Cdc2CommandPacket<USER_CDC, FILE_GET_INFO, FileMetadataPayload>;
497pub type FileMetadataReplyPacket =
498 Cdc2ReplyPacket<USER_CDC, FILE_GET_INFO, Option<FileMetadataReplyPayload>>;
499
500#[derive(Debug, Clone, Eq, PartialEq)]
501pub struct FileMetadataPayload {
502 pub vendor: FileVendor,
503 pub reserved: u8,
505 pub file_name: FixedString<23>,
506}
507impl Encode for FileMetadataPayload {
508 fn size(&self) -> usize {
509 2 + self.file_name.size()
510 }
511
512 fn encode(&self, data: &mut [u8]) {
513 data[0] = self.vendor as _;
514 data[1] = self.reserved as _;
515 self.file_name.encode(&mut data[2..]);
516 }
517}
518
519#[derive(Debug, Clone, Eq, PartialEq)]
520pub struct FileMetadataReplyPayload {
521 pub linked_vendor: Option<FileVendor>,
523 pub size: u32,
524 pub load_address: u32,
526 pub crc32: u32,
527 pub metadata: FileMetadata,
528}
529impl Decode for Option<FileMetadataReplyPayload> {
530 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
531 let maybe_vid = u8::decode(data).unwrap();
532
533 let linked_vendor = match maybe_vid {
534 0 => None,
536 255 => return Ok(None),
540 vid => Some(FileVendor::decode(&mut [vid].as_slice())?),
541 };
542
543 let size = u32::decode(data)?;
544
545 if size == 0xFFFFFFFF {
550 return Ok(None);
551 }
552
553 let load_address = u32::decode(data)?;
554 let crc32 = u32::decode(data)?;
555 let metadata = FileMetadata::decode(data)?;
556
557 Ok(Some(FileMetadataReplyPayload {
558 linked_vendor,
559 size,
560 load_address,
561 crc32,
562 metadata,
563 }))
564 }
565}
566
567pub type FileMetadataSetPacket = Cdc2CommandPacket<USER_CDC, FILE_SET_INFO, FileMetadataSetPayload>;
568pub type FileMetadataSetReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_SET_INFO, ()>;
569
570#[derive(Debug, Clone, Eq, PartialEq)]
571pub struct FileMetadataSetPayload {
572 pub vendor: FileVendor,
573 pub options: u8,
575 pub load_address: u32,
577 pub metadata: FileMetadata,
578 pub file_name: FixedString<23>,
579}
580impl Encode for FileMetadataSetPayload {
581 fn size(&self) -> usize {
582 18 + self.file_name.size()
583 }
584
585 fn encode(&self, data: &mut [u8]) {
586 data[0] = self.vendor as _;
587 data[1] = self.options as _;
588 self.load_address.encode(&mut data[2..]);
589 self.metadata.encode(&mut data[6..]);
590 self.file_name.encode(&mut data[18..]);
591 }
592}
593
594pub type FileErasePacket = Cdc2CommandPacket<USER_CDC, FILE_ERASE, FileErasePayload>;
595pub type FileEraseReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_ERASE, ()>;
596
597#[derive(Debug, Clone, Eq, PartialEq)]
598pub struct FileErasePayload {
599 pub vendor: FileVendor,
600 pub reserved: u8,
602 pub file_name: FixedString<23>,
603}
604impl Encode for FileErasePayload {
605 fn size(&self) -> usize {
606 2 + self.file_name.size()
607 }
608
609 fn encode(&self, data: &mut [u8]) {
610 data[0] = self.vendor as _;
611 data[1] = self.reserved as _;
612 self.file_name.encode(&mut data[2..]);
613 }
614}
615
616pub type FileCleanUpPacket = Cdc2CommandPacket<USER_CDC, FILE_CLEANUP, ()>;
617pub type FileCleanUpReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_CLEANUP, FileCleanUpReplyPayload>;
618
619#[derive(Debug, Clone, Copy, Eq, PartialEq)]
620pub struct FileCleanUpReplyPayload {
621 count: u16,
622}
623
624impl Decode for FileCleanUpReplyPayload {
625 fn decode(data: &mut &[u8]) -> Result<Self, DecodeError> {
626 Ok(Self {
627 count: Decode::decode(data)?,
628 })
629 }
630}
631
632pub type FileFormatPacket = Cdc2CommandPacket<USER_CDC, FILE_FORMAT, FileFormatConfirmation>;
634pub type FileFormatReplyPacket = Cdc2CommandPacket<USER_CDC, FILE_FORMAT, ()>;
635
636#[derive(Debug, Clone, Copy, Eq, PartialEq)]
637pub struct FileFormatConfirmation {
638 pub confirmation_code: [u8; 4],
640}
641
642impl FileFormatConfirmation {
643 pub const FORMAT_CODE: [u8; 4] = [0x44, 0x43, 0x42, 0x41];
644
645 pub const fn new() -> Self {
646 Self {
647 confirmation_code: Self::FORMAT_CODE,
648 }
649 }
650}
651
652impl Default for FileFormatConfirmation {
653 fn default() -> Self {
654 Self::new()
655 }
656}
657
658impl Encode for FileFormatConfirmation {
659 fn size(&self) -> usize {
660 4
661 }
662
663 fn encode(&self, data: &mut [u8]) {
664 self.confirmation_code.encode(data)
665 }
666}
667
668#[derive(Debug, Clone, Copy, Eq, PartialEq)]
669pub enum FileControlGroup {
670 Radio(RadioChannel),
671}
672
673impl Encode for FileControlGroup {
674 fn size(&self) -> usize {
675 if matches!(self, Self::Radio(_)) { 2 } else { 0 }
676 }
677
678 fn encode(&self, data: &mut [u8]) {
679 #[allow(irrefutable_let_patterns)] if let Self::Radio(channel) = self {
681 data[0] = 0x01;
682 data[1] = *channel as _;
683 }
684 }
685}
686
687#[derive(Debug, Clone, Copy, Eq, PartialEq)]
688#[repr(u8)]
689pub enum RadioChannel {
690 Pit = 0x00,
693
694 Download = 0x01,
698}
699
700pub type FileControlPacket = Cdc2CommandPacket<USER_CDC, FILE_CTRL, FileControlGroup>;
701pub type FileControlReplyPacket = Cdc2ReplyPacket<USER_CDC, FILE_CTRL, ()>;
702
703pub type ProgramStatusPacket = Cdc2CommandPacket<USER_CDC, FILE_USER_STAT, ProgramStatusPayload>;
704pub type ProgramStatusReplyPacket =
705 Cdc2ReplyPacket<USER_CDC, FILE_USER_STAT, ProgramStatusReplyPayload>;
706
707#[derive(Debug, Clone, Eq, PartialEq)]
708pub struct ProgramStatusPayload {
709 pub vendor: FileVendor,
710 pub reserved: u8,
712 pub file_name: FixedString<23>,
714}
715impl Encode for ProgramStatusPayload {
716 fn size(&self) -> usize {
717 2 + self.file_name.size()
718 }
719
720 fn encode(&self, data: &mut [u8]) {
721 data[0] = self.vendor as _;
722 data[1] = self.reserved;
723
724 self.file_name.encode(&mut data[2..]);
725 }
726}
727
728#[derive(Debug, Clone, Copy, Eq, PartialEq)]
729pub struct ProgramStatusReplyPayload {
730 pub slot: u8,
732
733 pub requested_slot: u8,
735}