eculocate

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit ba2c97fa3162bfb94be5b979abe0e54b17c9c11a
parent 58659923a359979856b07fca33bf2067598993b7
Author: filipton <filipton12@gmail.com>
Date:   Thu,  4 Jul 2024 20:33:40 +0200

feat: ota write chunk

Diffstat:
MREADME.md | 2++
Msrc/lib.rs | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 94 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md @@ -6,7 +6,9 @@ For now only works on esp32s3 (esp32c3 in the near future). ## Todo - [ ] Fully working library +- [ ] Partitions macro (to generate PARTITIONS const at compile time) - [ ] Other esp32's (like esp32c3, esp32s2, etc..) +- [ ] Rollbacks - [ ] Simple example ## Resources diff --git a/src/lib.rs b/src/lib.rs @@ -17,38 +17,94 @@ pub const PARTITIONS: [core::ops::Range<u32>; PARTITIONS_COUNT] = [ pub const OTADATA_OFFSET: u32 = 0xd000; pub const OTADATA_SIZE: u32 = 0x2000; -// I need to use generics, because after adding esp-storage dependency to +// NOTE: I need to use generics, because after adding esp-storage dependency to // this project its not compiling LULE pub struct Ota<S> where S: ReadStorage + Storage, { flash: S, + ota_offset: Option<u32>, + flash_size: u32, + ota_remaining: u32, } +// TODO: add OtaError enum + impl<S> Ota<S> where S: ReadStorage + Storage, { - pub fn set_target_ota_boot_partition(&mut self, target: usize) { - let flash = &mut self.flash; - let mut bytes = [0; 32]; + pub fn new(flash: S) -> Self { + Ota { + flash, + ota_offset: None, + ota_remaining: 0, + flash_size: 0, + } + } + + /// Sets ota_offset as next partitions offset + pub fn with_next_partition_offset(self) -> Self { + let next_part = Self::get_next_ota_partition(); + let ota_offset = next_part.map(|i| PARTITIONS[i].start); + + Ota { ota_offset, ..self } + } + + /// Sets the firmware flash size + pub fn set_flash_size(&mut self, size: u32) { + self.ota_remaining = size; + self.flash_size = size; + } + + /// Returns ota progress in f32 (0..1) + pub fn get_ota_progress(&self) -> f32 { + (self.flash_size - self.ota_remaining) as f32 / self.flash_size as f32 + } + + /// Writes next firmware chunk + pub fn ota_write_chunk(&mut self, chunk: &[u8]) -> Result<bool, ()> { + if self.flash_size == 0 { + log::error!("Cant write chunk without set_flash_size()"); + return Err(()); + } - _ = flash.read(OTADATA_OFFSET, &mut bytes); - let crc_1 = u32::from_le_bytes(bytes[(32 - 4)..32].try_into().unwrap()); - let seq_1 = helpers::seq_or_default(&bytes[..4], crc_1, 0); + if self.ota_remaining == 0 { + return Ok(true); + } - _ = flash.read(OTADATA_OFFSET + (OTADATA_SIZE >> 1), &mut bytes); - let crc_2 = u32::from_le_bytes(bytes[(32 - 4)..32].try_into().unwrap()); - let seq_2 = helpers::seq_or_default(&bytes[..4], crc_2, 0); + let ota_offset = self.ota_offset.as_mut().ok_or_else(|| ())?; + let write_size = chunk.len() as u32; + let write_size = write_size.min(self.ota_remaining) as usize; - let mut target_seq = seq_1.max(seq_2); + self.flash + .write(*ota_offset, &chunk[..write_size]) + .map_err(|_| ())?; + + log::info!( + "Wrote {} bytes to ota partition at 0x{:x}", + write_size, + ota_offset + ); + + *ota_offset += write_size as u32; + self.ota_remaining -= write_size as u32; + Ok(self.ota_remaining == 0) + } + + /// Sets ota boot target partition + pub fn set_target_ota_boot_partition(&mut self, target: usize) { + let (seq1, seq2) = self.get_ota_boot_sequences(); + + let mut target_seq = seq1.max(seq2); while helpers::seq_to_part(target_seq) != target || target_seq == 0 { target_seq += 1; } + let flash = &mut self.flash; let target_crc = crc32::calc_crc32(&target_seq.to_le_bytes(), 0xFFFFFFFF); - if seq_1 > seq_2 { + if seq1 > seq2 { let offset = OTADATA_OFFSET + (OTADATA_SIZE >> 1); _ = flash.write(offset, &target_seq.to_le_bytes()); @@ -59,18 +115,38 @@ where } } + /// Returns current OTA boot sequences + /// + /// NOTE: if crc doesn't match, it returns 0 for that seq + pub fn get_ota_boot_sequences(&mut self) -> (u32, u32) { + let mut bytes = [0; 32]; + + _ = self.flash.read(OTADATA_OFFSET, &mut bytes); + let crc1 = u32::from_le_bytes(bytes[(32 - 4)..32].try_into().unwrap()); + let seq1 = helpers::seq_or_default(&bytes[..4], crc1, 0); + + _ = self + .flash + .read(OTADATA_OFFSET + (OTADATA_SIZE >> 1), &mut bytes); + let crc2 = u32::from_le_bytes(bytes[(32 - 4)..32].try_into().unwrap()); + let seq2 = helpers::seq_or_default(&bytes[..4], crc2, 0); + + (seq1, seq2) + } + + /// Returns currently booted partition index pub fn get_currently_booted_partition() -> Option<usize> { paddr::esp_get_current_running_partition() } /// BUG: this wont work if user has ota partitions not starting from ota0 /// or if user skips some ota partitions: ota0, ota2, ota3... + /// + /// NOTE: This isn't reading from ota_boot_sequences, maybe in the future + /// it will read from them to eliminate possibility of wrong PADDR result. + /// (ESP-IDF has if's for PADDR-chain so it can fail somehow) pub fn get_next_ota_partition() -> Option<usize> { let curr_part = paddr::esp_get_current_running_partition(); curr_part.map(|next_part| (next_part + 1) % PARTITIONS_COUNT) } - - pub fn dsa(&mut self) { - //self.flash.write(offset, bytes) - } }