mediatek_optical/update/
completion.rs1use super::{FlashError, Verified};
4use crate::{drive, layout, receiver, INQUIRY_LEN};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum ReadbackQuality {
10 Complete(Verified),
12 Limited {
14 checked: usize,
16 unverified: usize,
18 },
19}
20
21#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct Startup {
24 inquiry: [u8; INQUIRY_LEN],
25 descriptor: [u8; 0x58],
26}
27impl Startup {
28 pub fn inquiry(&self) -> &[u8; INQUIRY_LEN] {
30 &self.inquiry
31 }
32 pub fn descriptor(&self) -> &[u8; 0x58] {
35 &self.descriptor
36 }
37}
38
39#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct Completion {
42 pub startup: Startup,
44 pub readback: ReadbackQuality,
46}
47impl Completion {
48 pub(crate) fn into_verified<E>(self) -> Result<Verified, FlashError<E>> {
51 match self.readback {
52 ReadbackQuality::Complete(verified) => Ok(verified),
53 ReadbackQuality::Limited {
54 checked,
55 unverified,
56 } => Err(FlashError::Incomplete {
57 checked,
58 unverified,
59 }),
60 }
61 }
62}
63
64#[derive(Debug)]
66pub enum StartupError<E> {
67 Identity(receiver::Error<E>),
69 Read(drive::Error<E>),
71 TargetMismatch,
73}
74impl<E: core::fmt::Debug> core::fmt::Display for StartupError<E> {
75 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
76 match self {
77 Self::Identity(error) => write!(f, "{error}"),
78 Self::Read(error) => write!(f, "{error}"),
79 Self::TargetMismatch => {
80 f.write_str("live firmware identity or build differs from the selected target")
81 }
82 }
83 }
84}
85impl<E: core::fmt::Debug + 'static> std::error::Error for StartupError<E> {
86 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
87 match self {
88 Self::Identity(source) => Some(source),
89 Self::Read(source) => Some(source),
90 Self::TargetMismatch => None,
91 }
92 }
93}
94
95pub(super) fn complete<T: drive::Transport>(
96 reads: &mut T,
97 image: &[u8],
98 result: Result<Verified, FlashError<T::Error>>,
99) -> Result<Completion, FlashError<T::Error>> {
100 let readback = match result {
101 Ok(verified) => ReadbackQuality::Complete(verified),
102 Err(FlashError::Incomplete {
103 checked,
104 unverified,
105 }) => ReadbackQuality::Limited {
106 checked,
107 unverified,
108 },
109 Err(error) => return Err(error),
112 };
113 let startup = confirm_startup(reads, image).map_err(FlashError::Startup)?;
114 Ok(Completion { startup, readback })
115}
116
117fn confirm_startup<T: drive::Transport>(
118 reads: &mut T,
119 image: &[u8],
120) -> Result<Startup, StartupError<T::Error>> {
121 let expected = layout::DESCRIPTOR
122 .of(image)
123 .ok_or(StartupError::TargetMismatch)?;
124 let mode = receiver::detect_mode(reads).map_err(StartupError::Identity)?;
125 if mode.mode() != receiver::Mode::Firmware || mode.inquiry()[8..36] != expected[..28] {
126 return Err(StartupError::TargetMismatch);
127 }
128 let mut descriptor = [0; layout::DESCRIPTOR.len];
129 drive::read_memory(reads, layout::DESCRIPTOR.start as u32, &mut descriptor)
130 .map_err(StartupError::Read)?;
131 if descriptor[..0x58] != expected[..0x58] {
132 return Err(StartupError::TargetMismatch);
133 }
134 Ok(Startup {
135 inquiry: *mode.inquiry(),
136 descriptor: descriptor[..0x58]
137 .try_into()
138 .expect("fixed descriptor length"),
139 })
140}