use super::{FlashError, Verified};
use crate::{drive, layout, receiver, INQUIRY_LEN};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReadbackQuality {
Complete(Verified),
Limited {
checked: usize,
unverified: usize,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Startup {
inquiry: [u8; INQUIRY_LEN],
descriptor: [u8; 0x58],
}
impl Startup {
pub fn inquiry(&self) -> &[u8; INQUIRY_LEN] {
&self.inquiry
}
pub fn descriptor(&self) -> &[u8; 0x58] {
&self.descriptor
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Completion {
pub startup: Startup,
pub readback: ReadbackQuality,
}
impl Completion {
pub(crate) fn into_verified<E>(self) -> Result<Verified, FlashError<E>> {
match self.readback {
ReadbackQuality::Complete(verified) => Ok(verified),
ReadbackQuality::Limited {
checked,
unverified,
} => Err(FlashError::Incomplete {
checked,
unverified,
}),
}
}
}
#[derive(Debug)]
pub enum StartupError<E> {
Identity(receiver::Error<E>),
Read(drive::Error<E>),
TargetMismatch,
}
impl<E: core::fmt::Debug> core::fmt::Display for StartupError<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Identity(error) => write!(f, "{error}"),
Self::Read(error) => write!(f, "{error}"),
Self::TargetMismatch => {
f.write_str("live firmware identity or build differs from the selected target")
}
}
}
}
impl<E: core::fmt::Debug + 'static> std::error::Error for StartupError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Identity(source) => Some(source),
Self::Read(source) => Some(source),
Self::TargetMismatch => None,
}
}
}
pub(super) fn complete<T: drive::Transport>(
reads: &mut T,
image: &[u8],
result: Result<Verified, FlashError<T::Error>>,
) -> Result<Completion, FlashError<T::Error>> {
let readback = match result {
Ok(verified) => ReadbackQuality::Complete(verified),
Err(FlashError::Incomplete {
checked,
unverified,
}) => ReadbackQuality::Limited {
checked,
unverified,
},
Err(error) => return Err(error),
};
let startup = confirm_startup(reads, image).map_err(FlashError::Startup)?;
Ok(Completion { startup, readback })
}
fn confirm_startup<T: drive::Transport>(
reads: &mut T,
image: &[u8],
) -> Result<Startup, StartupError<T::Error>> {
let expected = layout::DESCRIPTOR
.of(image)
.ok_or(StartupError::TargetMismatch)?;
let mode = receiver::detect_mode(reads).map_err(StartupError::Identity)?;
if mode.mode() != receiver::Mode::Firmware || mode.inquiry()[8..36] != expected[..28] {
return Err(StartupError::TargetMismatch);
}
let mut descriptor = [0; layout::DESCRIPTOR.len];
drive::read_memory(reads, layout::DESCRIPTOR.start as u32, &mut descriptor)
.map_err(StartupError::Read)?;
if descriptor[..0x58] != expected[..0x58] {
return Err(StartupError::TargetMismatch);
}
Ok(Startup {
inquiry: *mode.inquiry(),
descriptor: descriptor[..0x58]
.try_into()
.expect("fixed descriptor length"),
})
}