1use std::{io::Write, mem::size_of, time::Duration};
4
5use bytemuck::{Pod, Zeroable, bytes_of};
6use serde::{Deserialize, Serialize};
7use strum::Display;
8
9use crate::{
10 Error,
11 flasher::{SpiAttachParams, SpiSetParams},
12};
13
14const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
15const ERASE_REGION_TIMEOUT_PER_MB: Duration = Duration::from_secs(30);
16const ERASE_WRITE_TIMEOUT_PER_MB: Duration = Duration::from_secs(40);
17const ERASE_CHIP_TIMEOUT: Duration = Duration::from_secs(120);
18const MEM_END_TIMEOUT: Duration = Duration::from_millis(50);
19const SYNC_TIMEOUT: Duration = Duration::from_millis(100);
20const FLASH_DEFLATE_END_TIMEOUT: Duration = Duration::from_secs(10);
21const FLASH_MD5_TIMEOUT_PER_MB: Duration = Duration::from_secs(8);
22
23const SYNC_FRAME: [u8; 36] = [
26 0x07, 0x07, 0x12, 0x20, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
27 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
28 0x55, 0x55, 0x55, 0x55,
29];
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, Deserialize, Serialize)]
35#[non_exhaustive]
36#[repr(u8)]
37pub enum CommandType {
38 Unknown = 0x00,
40
41 FlashBegin = 0x02,
44 FlashData = 0x03,
46 FlashEnd = 0x04,
48 MemBegin = 0x05,
50 MemEnd = 0x06,
52 MemData = 0x07,
54 Sync = 0x08,
56 WriteReg = 0x09,
58 ReadReg = 0x0A,
60
61 SpiSetParams = 0x0B,
64 SpiAttach = 0x0D,
66 ReadFlashSlow = 0x0E,
70 ChangeBaudrate = 0x0F,
72 FlashDeflBegin = 0x10,
74 FlashDeflData = 0x11,
76 FlashDeflEnd = 0x12,
78 FlashMd5 = 0x13,
80 GetSecurityInfo = 0x14,
82
83 EraseFlash = 0xD0,
86 EraseRegion = 0xD1,
88 ReadFlash = 0xD2,
90 RunUserCode = 0xD3,
92
93 FlashDetect = 0x9F,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
100pub enum CommandResponseValue {
101 ValueU32(u32),
103 ValueU128(u128),
105 Vector(Vec<u8>),
107}
108
109impl TryInto<u32> for CommandResponseValue {
110 type Error = Error;
111
112 fn try_into(self) -> Result<u32, Self::Error> {
113 match self {
114 CommandResponseValue::ValueU32(value) => Ok(value),
115 CommandResponseValue::ValueU128(_) => Err(Error::InvalidResponse(
116 "expected `u32` but found `u128`".into(),
117 )),
118 CommandResponseValue::Vector(_) => Err(Error::InvalidResponse(
119 "expected `u32` but found `Vec`".into(),
120 )),
121 }
122 }
123}
124
125impl TryInto<u128> for CommandResponseValue {
126 type Error = Error;
127
128 fn try_into(self) -> Result<u128, Self::Error> {
129 match self {
130 CommandResponseValue::ValueU32(_) => Err(Error::InvalidResponse(
131 "expected `u128` but found `u32`".into(),
132 )),
133 CommandResponseValue::ValueU128(value) => Ok(value),
134 CommandResponseValue::Vector(_) => Err(Error::InvalidResponse(
135 "expected `u128` but found `Vec`".into(),
136 )),
137 }
138 }
139}
140
141impl TryInto<Vec<u8>> for CommandResponseValue {
142 type Error = Error;
143
144 fn try_into(self) -> Result<Vec<u8>, Self::Error> {
145 match self {
146 CommandResponseValue::ValueU32(_) => Err(Error::InvalidResponse(
147 "expected `Vec` but found `u32`".into(),
148 )),
149 CommandResponseValue::ValueU128(_) => Err(Error::InvalidResponse(
150 "expected `Vec` but found `u128`".into(),
151 )),
152 CommandResponseValue::Vector(value) => Ok(value),
153 }
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
159pub struct CommandResponse {
160 pub resp: u8,
162 pub return_op: u8,
164 pub return_length: u16,
166 pub value: CommandResponseValue,
168 pub error: u8,
170 pub status: u8,
172}
173
174impl CommandType {
175 pub fn timeout(&self) -> Duration {
177 match self {
178 CommandType::MemEnd => MEM_END_TIMEOUT,
179 CommandType::Sync => SYNC_TIMEOUT,
180 CommandType::EraseFlash => ERASE_CHIP_TIMEOUT,
181 CommandType::FlashDeflEnd => FLASH_DEFLATE_END_TIMEOUT,
182 CommandType::FlashMd5 => {
183 log::warn!(
184 "Using default timeout for {self}, this may not be sufficient for large flash regions. Consider using `timeout_for_size` instead."
185 );
186
187 DEFAULT_TIMEOUT
188 }
189 _ => DEFAULT_TIMEOUT,
190 }
191 }
192
193 pub fn timeout_for_size(&self, size: u32) -> Duration {
196 fn calc_timeout(timeout_per_mb: Duration, size: u32) -> Duration {
197 let mb = size as f64 / 1_000_000.0;
198 std::cmp::max(
199 FLASH_DEFLATE_END_TIMEOUT,
200 Duration::from_millis((timeout_per_mb.as_millis() as f64 * mb) as u64),
201 )
202 }
203 match self {
204 CommandType::FlashBegin | CommandType::FlashDeflBegin | CommandType::EraseRegion => {
205 calc_timeout(ERASE_REGION_TIMEOUT_PER_MB, size)
206 }
207 CommandType::FlashData | CommandType::FlashDeflData => {
208 calc_timeout(ERASE_WRITE_TIMEOUT_PER_MB, size)
209 }
210 CommandType::FlashMd5 => calc_timeout(FLASH_MD5_TIMEOUT_PER_MB, size),
211 _ => self.timeout(),
212 }
213 }
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
220#[non_exhaustive]
221pub enum Command<'a> {
222 FlashBegin {
224 size: u32,
226 blocks: u32,
228 block_size: u32,
230 offset: u32,
232 supports_encryption: bool,
234 },
235 FlashData {
237 data: &'a [u8],
239 pad_to: usize,
241 pad_byte: u8,
243 sequence: u32,
245 },
246 FlashEnd {
248 reboot: bool,
253 },
254 MemBegin {
256 size: u32,
258 blocks: u32,
260 block_size: u32,
262 offset: u32,
264 supports_encryption: bool,
266 },
267 MemEnd {
269 no_entry: bool,
271 entry: u32,
273 },
274 MemData {
276 data: &'a [u8],
278 pad_to: usize,
280 pad_byte: u8,
282 sequence: u32,
284 },
285 Sync,
289 WriteReg {
291 address: u32,
293 value: u32,
295 mask: Option<u32>,
297 },
298 ReadReg {
300 address: u32,
302 },
303 SpiSetParams {
305 spi_params: SpiSetParams,
307 },
308 SpiAttach {
310 spi_params: SpiAttachParams,
312 },
313 SpiAttachStub {
315 spi_params: SpiAttachParams,
317 },
318 ChangeBaudrate {
320 new_baud: u32,
322 prior_baud: u32,
324 },
325 FlashDeflBegin {
327 size: u32,
333 blocks: u32,
335 block_size: u32,
337 offset: u32,
339 supports_encryption: bool,
343 },
344 FlashDeflData {
346 data: &'a [u8],
348 pad_to: usize,
350 pad_byte: u8,
352 sequence: u32,
354 },
355 FlashDeflEnd {
357 reboot: bool,
362 },
363 FlashMd5 {
365 offset: u32,
367 size: u32,
369 },
370 EraseFlash,
374 EraseRegion {
378 offset: u32,
380 size: u32,
382 },
383 ReadFlash {
387 offset: u32,
389 size: u32,
391 block_size: u32,
393 max_in_flight: u32,
395 },
396 ReadFlashSlow {
400 offset: u32,
402 size: u32,
404 block_size: u32,
406 max_in_flight: u32,
408 },
409 RunUserCode,
411 FlashDetect,
415 GetSecurityInfo,
419}
420
421impl Command<'_> {
422 pub fn command_type(&self) -> CommandType {
424 match self {
425 Command::FlashBegin { .. } => CommandType::FlashBegin,
426 Command::FlashData { .. } => CommandType::FlashData,
427 Command::FlashEnd { .. } => CommandType::FlashEnd,
428 Command::MemBegin { .. } => CommandType::MemBegin,
429 Command::MemData { .. } => CommandType::MemData,
430 Command::MemEnd { .. } => CommandType::MemEnd,
431 Command::Sync => CommandType::Sync,
432 Command::WriteReg { .. } => CommandType::WriteReg,
433 Command::ReadReg { .. } => CommandType::ReadReg,
434 Command::SpiSetParams { .. } => CommandType::SpiSetParams,
435 Command::SpiAttach { .. } => CommandType::SpiAttach,
436 Command::SpiAttachStub { .. } => CommandType::SpiAttach,
437 Command::ChangeBaudrate { .. } => CommandType::ChangeBaudrate,
438 Command::FlashDeflBegin { .. } => CommandType::FlashDeflBegin,
439 Command::FlashDeflData { .. } => CommandType::FlashDeflData,
440 Command::FlashDeflEnd { .. } => CommandType::FlashDeflEnd,
441 Command::FlashMd5 { .. } => CommandType::FlashMd5,
442 Command::EraseFlash { .. } => CommandType::EraseFlash,
443 Command::EraseRegion { .. } => CommandType::EraseRegion,
444 Command::ReadFlash { .. } => CommandType::ReadFlash,
445 Command::ReadFlashSlow { .. } => CommandType::ReadFlashSlow,
446 Command::RunUserCode { .. } => CommandType::RunUserCode,
447 Command::FlashDetect => CommandType::FlashDetect,
448 Command::GetSecurityInfo => CommandType::GetSecurityInfo,
449 }
450 }
451
452 pub fn timeout_for_size(&self, size: u32) -> Duration {
454 self.command_type().timeout_for_size(size)
455 }
456
457 pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
459 writer.write_all(&[0, self.command_type() as u8])?;
461 match *self {
462 Command::FlashBegin {
463 size,
464 blocks,
465 block_size,
466 offset,
467 supports_encryption,
468 } => {
469 begin_command(
470 writer,
471 size,
472 blocks,
473 block_size,
474 offset,
475 supports_encryption,
476 )?;
477 }
478 Command::FlashData {
479 pad_to,
480 pad_byte,
481 data,
482 sequence,
483 } => {
484 data_command(writer, data, pad_to, pad_byte, sequence)?;
485 }
486 Command::FlashEnd { reboot } => {
487 write_basic(writer, &[u8::from(!reboot)], 0)?;
488 }
489 Command::MemBegin {
490 size,
491 blocks,
492 block_size,
493 offset,
494 supports_encryption,
495 } => {
496 begin_command(
497 writer,
498 size,
499 blocks,
500 block_size,
501 offset,
502 supports_encryption,
503 )?;
504 }
505 Command::MemData {
506 pad_to,
507 pad_byte,
508 data,
509 sequence,
510 } => {
511 data_command(writer, data, pad_to, pad_byte, sequence)?;
512 }
513 Command::MemEnd {
514 no_entry: reboot,
515 entry,
516 } => {
517 #[derive(Zeroable, Pod, Copy, Clone)]
518 #[repr(C)]
519 struct EntryParams {
520 no_entry: u32,
521 entry: u32,
522 }
523 let params = EntryParams {
524 no_entry: u32::from(reboot),
525 entry,
526 };
527 write_basic(writer, bytes_of(¶ms), 0)?;
528 }
529 Command::Sync => {
530 write_basic(writer, &SYNC_FRAME, 0)?;
531 }
532 Command::WriteReg {
533 address,
534 value,
535 mask,
536 } => {
537 #[derive(Zeroable, Pod, Copy, Clone, Debug)]
538 #[repr(C)]
539 struct WriteRegParams {
540 address: u32,
541 value: u32,
542 mask: u32,
543 delay_us: u32,
544 }
545 let params = WriteRegParams {
546 address,
547 value,
548 mask: mask.unwrap_or(0xFFFFFFFF),
549 delay_us: 0,
550 };
551 write_basic(writer, bytes_of(¶ms), 0)?;
552 }
553 Command::ReadReg { address } => {
554 write_basic(writer, &address.to_le_bytes(), 0)?;
555 }
556 Command::SpiSetParams { spi_params } => {
557 write_basic(writer, &spi_params.encode(), 0)?;
558 }
559 Command::SpiAttach { spi_params } => {
560 write_basic(writer, &spi_params.encode(false), 0)?;
561 }
562 Command::SpiAttachStub { spi_params } => {
563 write_basic(writer, &spi_params.encode(true), 0)?;
564 }
565 Command::ChangeBaudrate {
566 new_baud,
567 prior_baud,
568 } => {
569 writer.write_all(&(8u16.to_le_bytes()))?;
571 writer.write_all(&(0u32.to_le_bytes()))?;
573 writer.write_all(&new_baud.to_le_bytes())?;
575 writer.write_all(&prior_baud.to_le_bytes())?;
576 }
577 Command::FlashDeflBegin {
578 size,
579 blocks,
580 block_size,
581 offset,
582 supports_encryption,
583 } => {
584 begin_command(
585 writer,
586 size,
587 blocks,
588 block_size,
589 offset,
590 supports_encryption,
591 )?;
592 }
593 Command::FlashDeflData {
594 pad_to,
595 pad_byte,
596 data,
597 sequence,
598 } => {
599 data_command(writer, data, pad_to, pad_byte, sequence)?;
600 }
601 Command::FlashDeflEnd { reboot } => {
602 write_basic(writer, &[u8::from(!reboot)], 0)?;
605 }
606 Command::FlashMd5 { offset, size } => {
607 writer.write_all(&(16u16.to_le_bytes()))?;
609 writer.write_all(&(0u32.to_le_bytes()))?;
611 writer.write_all(&offset.to_le_bytes())?;
613 writer.write_all(&size.to_le_bytes())?;
614 writer.write_all(&(0u32.to_le_bytes()))?;
615 writer.write_all(&(0u32.to_le_bytes()))?;
616 }
617 Command::EraseFlash => {
618 write_basic(writer, &[], 0)?;
619 }
620 Command::EraseRegion { offset, size } => {
621 writer.write_all(&(8u16.to_le_bytes()))?;
623 writer.write_all(&(0u32.to_le_bytes()))?;
625 writer.write_all(&offset.to_le_bytes())?;
627 writer.write_all(&size.to_le_bytes())?;
628 }
629 Command::ReadFlash {
630 offset,
631 size,
632 block_size,
633 max_in_flight,
634 } => {
635 writer.write_all(&(16u16.to_le_bytes()))?;
637 writer.write_all(&(0u32.to_le_bytes()))?;
639 writer.write_all(&offset.to_le_bytes())?;
641 writer.write_all(&size.to_le_bytes())?;
642 writer.write_all(&block_size.to_le_bytes())?;
643 writer.write_all(&(max_in_flight.to_le_bytes()))?;
644 }
645 Command::ReadFlashSlow {
646 offset,
647 size,
648 block_size,
649 max_in_flight,
650 } => {
651 writer.write_all(&(16u16.to_le_bytes()))?;
653 writer.write_all(&(0u32.to_le_bytes()))?;
655 writer.write_all(&offset.to_le_bytes())?;
657 writer.write_all(&size.to_le_bytes())?;
658 writer.write_all(&block_size.to_le_bytes())?;
659 writer.write_all(&(max_in_flight.to_le_bytes()))?;
660 }
661 Command::RunUserCode => {
662 write_basic(writer, &[], 0)?;
663 }
664 Command::FlashDetect => {
665 write_basic(writer, &[], 0)?;
666 }
667 Command::GetSecurityInfo => {
668 write_basic(writer, &[], 0)?;
669 }
670 };
671 Ok(())
672 }
673}
674
675fn write_basic<W: Write>(mut writer: W, data: &[u8], checksum: u32) -> std::io::Result<()> {
677 writer.write_all(&((data.len() as u16).to_le_bytes()))?;
678 writer.write_all(&(checksum.to_le_bytes()))?;
679 writer.write_all(data)?;
680 Ok(())
681}
682
683fn begin_command<W: Write>(
685 writer: W,
686 size: u32,
687 blocks: u32,
688 block_size: u32,
689 offset: u32,
690 supports_encryption: bool,
691) -> std::io::Result<()> {
692 #[derive(Zeroable, Pod, Copy, Clone, Debug)]
693 #[repr(C)]
694 struct BeginParams {
695 size: u32,
696 blocks: u32,
697 block_size: u32,
698 offset: u32,
699 encrypted: u32,
700 }
701 let params = BeginParams {
702 size,
703 blocks,
704 block_size,
705 offset,
706 encrypted: 0,
707 };
708
709 let bytes = bytes_of(¶ms);
710 let data = if !supports_encryption {
711 let end = bytes.len() - 4;
714 &bytes[0..end]
715 } else {
716 bytes
717 };
718 write_basic(writer, data, 0)
719}
720
721fn data_command<W: Write>(
723 mut writer: W,
724 block_data: &[u8],
725 pad_to: usize,
726 pad_byte: u8,
727 sequence: u32,
728) -> std::io::Result<()> {
729 #[derive(Zeroable, Pod, Copy, Clone, Debug)]
730 #[repr(C)]
731 struct BlockParams {
732 size: u32,
733 sequence: u32,
734 dummy1: u32,
735 dummy2: u32,
736 }
737
738 let pad_length = pad_to.saturating_sub(block_data.len());
739
740 let params = BlockParams {
741 size: (block_data.len() + pad_length) as u32,
742 sequence,
743 dummy1: 0,
744 dummy2: 0,
745 };
746
747 let mut check = checksum(block_data, CHECKSUM_INIT);
748
749 for _ in 0..pad_length {
750 check = checksum(&[pad_byte], check);
751 }
752
753 let total_length = size_of::<BlockParams>() + block_data.len() + pad_length;
754 writer.write_all(&((total_length as u16).to_le_bytes()))?;
755 writer.write_all(&((check as u32).to_le_bytes()))?;
756 writer.write_all(bytes_of(¶ms))?;
757 writer.write_all(block_data)?;
758 for _ in 0..pad_length {
759 writer.write_all(&[pad_byte])?;
760 }
761 Ok(())
762}
763
764const CHECKSUM_INIT: u8 = 0xEF;
765
766fn checksum(data: &[u8], mut checksum: u8) -> u8 {
767 for byte in data {
768 checksum ^= *byte;
769 }
770
771 checksum
772}