pub(crate) type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
PartitionTooSmall,
NotEnoughPartitions,
NoOtadata,
PartitionOrder,
NotStarted,
Incomplete,
Crc,
Verify,
Flash,
UnknownBootPartition,
SeqExhausted,
InvalidResumeInfo,
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(match self {
Self::PartitionTooSmall => "image does not fit into the OTA partition",
Self::NotEnoughPartitions => "less than 2 OTA partitions",
Self::NoOtadata => "no valid otadata partition",
Self::PartitionOrder => "OTA partitions are not consecutive",
Self::NotStarted => "OTA update not started",
Self::Incomplete => "image not completely written",
Self::Crc => "image crc mismatch",
Self::Verify => "image read back from flash has a different crc",
Self::Flash => "flash read/write failed",
Self::UnknownBootPartition => "running partition not found in otadata",
Self::SeqExhausted => "otadata sequence number exhausted",
Self::InvalidResumeInfo => "inconsistent resume info",
})
}
}
impl core::error::Error for Error {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Progress {
pub written: u32,
pub total: u32,
}
impl Progress {
pub fn percent(&self) -> u8 {
(self.permille() / 10) as u8
}
pub fn permille(&self) -> u16 {
match self.total {
0 => 1000,
total => (u64::from(self.written.min(total)) * 1000 / u64::from(total)) as u16,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ResumeInfo {
pub size: u32,
pub remaining: u32,
pub target_crc: u32,
pub last_crc: u32,
}
#[derive(Clone, Debug)]
pub(crate) struct FlashProgress {
pub last_crc: u32,
pub flash_offset: u32,
pub flash_size: u32,
pub remaining: u32,
pub target_partition: usize,
pub target_crc: u32,
}
#[derive(Debug)]
pub(crate) struct PartitionInfo {
pub ota_partitions: [(u32, u32); 16],
pub ota_partitions_count: usize,
pub otadata_offset: u32,
pub otadata_size: u32,
}
#[repr(u32)]
#[derive(Debug, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum OtaImgState {
New = 0x0,
PendingVerify = 0x1,
Valid = 0x2,
Invalid = 0x3,
Aborted = 0x4,
Undefined = 0xFFFFFFFF,
}
impl OtaImgState {
pub(crate) const fn from_u32(value: u32) -> Self {
match value {
0x0 => Self::New,
0x1 => Self::PendingVerify,
0x2 => Self::Valid,
0x3 => Self::Invalid,
0x4 => Self::Aborted,
_ => Self::Undefined,
}
}
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct EspOtaSelectEntry {
pub seq: u32,
pub seq_label: [u8; 20],
pub ota_state: OtaImgState,
pub crc: u32,
}
impl EspOtaSelectEntry {
pub(crate) const SIZE: usize = 32;
pub(crate) fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self {
let [s0, s1, s2, s3, label @ .., t0, t1, t2, t3, c0, c1, c2, c3] = *bytes;
Self {
seq: u32::from_le_bytes([s0, s1, s2, s3]),
seq_label: label,
ota_state: OtaImgState::from_u32(u32::from_le_bytes([t0, t1, t2, t3])),
crc: u32::from_le_bytes([c0, c1, c2, c3]),
}
}
pub(crate) fn to_bytes(&self) -> [u8; Self::SIZE] {
let mut bytes = [0; Self::SIZE];
bytes[0..4].copy_from_slice(&self.seq.to_le_bytes());
bytes[4..24].copy_from_slice(&self.seq_label);
bytes[24..28].copy_from_slice(&(self.ota_state as u32).to_le_bytes());
bytes[28..32].copy_from_slice(&self.crc.to_le_bytes());
bytes
}
pub(crate) fn check_crc(&mut self) {
if !crate::helpers::is_crc_seq_correct(self.seq, self.crc) {
self.seq = 0; }
}
}