commit 14045825f3601924ae7f2fef2624eed08b508d51
parent 3a0dbe5c907677ca5e8bda6c46ab4a0665fff985
Author: Daniel Barlow <dan@telent.net>
Date: Fri, 23 Jan 2026 23:57:25 +0000
extract module for ota flashing
Diffstat:
3 files changed, 172 insertions(+), 122 deletions(-)
diff --git a/ecu-esp32/src/main.rs b/ecu-esp32/src/main.rs
@@ -13,6 +13,7 @@ pub mod ecu;
pub mod wifi;
pub mod mdns;
pub mod streamer;
+pub mod ota;
use esp_hal::{
timer::timg::TimerGroup,
@@ -26,9 +27,10 @@ use embassy_time::Timer;
use log::{info};
-use crate::ecu::{
- Ecu
-};
+use crate::ecu::Ecu;
+use crate::ota::Flasher;
+
+
extern crate alloc;
@@ -65,11 +67,14 @@ async fn main(s : Spawner) {
peripherals.UART0
);
+
+ let flasher = Flasher::new(peripherals.FLASH, peripherals.SHA);
+
join(
crate::wifi::start_wifi(&s,
+ flasher,
peripherals.WIFI,
- peripherals.FLASH,
- peripherals.SHA, &mut ecu),
+ &mut ecu),
async {
loop {
Timer::after_millis(1000).await;
diff --git a/ecu-esp32/src/ota.rs b/ecu-esp32/src/ota.rs
@@ -0,0 +1,156 @@
+use nb::block;
+
+use embassy_time::{Timer};
+
+use ed25519_dalek::{VerifyingKey, Verifier, Signature};
+
+use esp_hal_ota::Ota;
+use esp_storage::FlashStorage;
+
+use esp_hal::sha;
+
+use log::{warn, info, error};
+
+const ERASE_BLOCK_BYTES : usize = 4096;
+const ED25519_SIGNATURE_BYTES : usize = 64;
+const SHA256_BYTES : usize = 32;
+
+async fn read_all(socket: &mut embassy_net::tcp::TcpSocket<'_>,
+ buf: &mut [u8],
+ len: usize) -> usize {
+ let mut n = 0;
+ while n < len {
+ match socket.read(&mut buf[n..len]).await {
+ Ok(0) => {
+ warn!("read EOF");
+ break;
+ },
+ Ok(count) => {
+ n = n + count;
+ }
+ Err(e) => {
+ warn!("read err {:?}", e);
+ return 0; // ew
+ }
+ }
+ }
+ n
+}
+
+pub struct Flasher<'a> {
+ ota : Ota<FlashStorage<'a>>,
+ sha_engine : sha::Sha<'a>
+}
+
+impl Flasher<'_> {
+ pub fn new(flash_peripheral: esp_hal::peripherals::FLASH<'static>,
+ sha_peripheral: esp_hal::peripherals::SHA<'static>) -> Self {
+ let flash = FlashStorage::new(flash_peripheral);
+ let sha_engine = sha::Sha::new(sha_peripheral);
+ let ota = Ota::new(flash).unwrap();
+
+ Self { ota, sha_engine }
+ }
+
+ pub async fn write(&mut self, sock: &mut embassy_net::tcp::TcpSocket<'_>) {
+ write_ota(&mut self.ota, &mut self.sha_engine, sock).await
+ }
+}
+
+
+async fn write_ota(ota : &mut Ota<FlashStorage<'_>>,
+ sha_engine : &mut sha::Sha<'_>,
+ sock: &mut embassy_net::tcp::TcpSocket<'_>) {
+ let mut buf = [0u8; SHA256_BYTES + ERASE_BLOCK_BYTES];
+ let mut buf_offset = 0;
+ let mut signed;
+ let mut signature = [0u8; ED25519_SIGNATURE_BYTES ];
+ let mut next_block_hash = [0u8; SHA256_BYTES];
+
+ // ed25519 signature of first block
+ // first block is sha256 of second block, plus length as u32
+ // second+ block is sha256 of successor block, plus data
+
+ let mut block0 = [0u8; SHA256_BYTES + 4];
+
+ read_all(sock, &mut signature, ED25519_SIGNATURE_BYTES).await;
+ read_all(sock, &mut block0, SHA256_BYTES + 4).await;
+
+ let verifying_key_bytes = include_bytes!("../../pubkey.bin");
+ let verifying_key: VerifyingKey = VerifyingKey::from_bytes(verifying_key_bytes).expect("verify");
+ signed = verifying_key.verify(&mut block0,
+ &Signature::try_from(&signature).expect("sig")).is_ok();
+
+ info!("initial sig checks out? {:?}", signed);
+
+ let (sha, len_bytes) = block0.split_at(SHA256_BYTES);
+ let len = u32::from_be_bytes(len_bytes.try_into().expect("4 bytes"));
+ next_block_hash.clone_from_slice(sha);
+
+ info!("ota length {}", len);
+ if let Err(e) = ota.ota_begin(len, 0) {
+ error!("ota_begin failed {:?}", e);
+ return;
+ };
+
+ // total payload to expect is image size plus per-block hashes
+ let mut remaining = len + (len/(ERASE_BLOCK_BYTES as u32)+ 1) * (SHA256_BYTES as u32);
+
+ while remaining > 0 {
+ // info!("remaining {}, offset {}", remaining, buf_offset);
+ match sock.read(&mut buf[buf_offset..]).await {
+ Ok(0) => {
+ info!("unexpected eof on read");
+ break;
+ }
+ Ok(chunksize) => {
+ buf_offset += chunksize;
+ remaining -= chunksize as u32;
+ }
+ Err(x) => {
+ warn!("error reading ota image: {:?}", x);
+ break;
+ }
+ }
+
+ if remaining <= 0 || buf_offset == SHA256_BYTES + ERASE_BLOCK_BYTES {
+ let mut actual_hash = [0u8; SHA256_BYTES];
+ let mut hasher = sha_engine.start::<sha::Sha256>();
+ let mut p = &buf[..buf_offset];
+ while !p.is_empty() {
+ match block!(hasher.update(p)) {
+ Ok(remaining) => { p = remaining; }
+ _ => ()
+ }
+ }
+ block!(hasher.finish(&mut actual_hash)).expect("hasher.finish expected infallible");
+
+ signed = signed && next_block_hash == actual_hash;
+
+ if signed {
+ if let Err(_) = ota.ota_write_chunk(&buf[ED25519_SIGNATURE_BYTES..buf_offset]) {
+ break;
+ }
+ } else {
+ // accept the rest of the update, just don't flash it.
+ warn!("signature invalid with {remaining} left");
+ }
+ //
+ buf_offset = 0;
+ next_block_hash.copy_from_slice(&buf[0..SHA256_BYTES]);
+ }
+ }
+ if remaining == 0 {
+ info!("wrote all bytes");
+ info!("write {:?}", sock.write(b"BYE\n").await);
+ let _ = sock.flush().await;
+ sock.close();
+ if signed && !ota.ota_flush(false, false).is_ok() {
+ error!("ota failed");
+ }
+ Timer::after_millis(1000).await;
+ esp_hal::system::software_reset();
+ } else {
+ warn!("stream ended early or flash failed with {} bytes left", remaining);
+ }
+}
diff --git a/ecu-esp32/src/wifi.rs b/ecu-esp32/src/wifi.rs
@@ -6,9 +6,7 @@
use alloc::vec::Vec;
use alloc::string::String;
use alloc::format;
-use nb::block;
-use ed25519_dalek::{VerifyingKey, Verifier, Signature};
use embassy_executor::Spawner;
use embassy_net::{
@@ -36,14 +34,10 @@ use esp_radio::{
WifiStaState,
},
};
-use esp_hal_ota::Ota;
-use esp_hal::sha;
-
-use esp_storage::FlashStorage;
use embassy_net::Stack;
-use log::{warn, info, error};
+use log::{warn, info};
use crate::mdns;
@@ -95,9 +89,8 @@ fn dns_records(my_ip_address: [u8;4], serial: Option<usize>) -> Vec<mdns::Resour
}
pub async fn start_wifi(spawner: &Spawner,
+ flasher: crate::ota::Flasher<'_>,
wifi_peripheral: esp_hal::peripherals::WIFI<'static>,
- flash_peripheral: esp_hal::peripherals::FLASH<'static>,
- sha_peripheral: esp_hal::peripherals::SHA<'static>,
ecu: &mut crate::ecu::Ecu<'_>) {
@@ -149,7 +142,7 @@ pub async fn start_wifi(spawner: &Spawner,
join(
join(
crate::streamer::loop_udp_thing(stack, ecu),
- run_tcp_handler(stack, sha_peripheral, flash_peripheral),
+ run_tcp_handler(stack, flasher)
),
async {
loop {
@@ -242,122 +235,18 @@ async fn read_all(socket: &mut embassy_net::tcp::TcpSocket<'_>,
n
}
-const ERASE_BLOCK_BYTES : usize = 4096;
-const ED25519_SIGNATURE_BYTES : usize = 64;
-const SHA256_BYTES : usize = 32;
-
-async fn write_ota(ota : &mut Ota<FlashStorage<'_>>,
- sha_engine : &mut sha::Sha<'_>,
- sock: &mut embassy_net::tcp::TcpSocket<'_>) {
- let mut buf = [0u8; SHA256_BYTES + ERASE_BLOCK_BYTES];
- let mut buf_offset = 0;
- let mut signed;
- let mut signature = [0u8; ED25519_SIGNATURE_BYTES ];
- let mut next_block_hash = [0u8; SHA256_BYTES];
-
- // ed25519 signature of first block
- // first block is sha256 of second block, plus length as u32
- // second+ block is sha256 of successor block, plus data
-
- let mut block0 = [0u8; SHA256_BYTES + 4];
-
- read_all(sock, &mut signature, ED25519_SIGNATURE_BYTES).await;
- read_all(sock, &mut block0, SHA256_BYTES + 4).await;
-
- let verifying_key_bytes = include_bytes!("../../pubkey.bin");
- let verifying_key: VerifyingKey = VerifyingKey::from_bytes(verifying_key_bytes).expect("verify");
- signed = verifying_key.verify(&mut block0,
- &Signature::try_from(&signature).expect("sig")).is_ok();
-
- info!("initial sig checks out? {:?}", signed);
-
- let (sha, len_bytes) = block0.split_at(SHA256_BYTES);
- let len = u32::from_be_bytes(len_bytes.try_into().expect("4 bytes"));
- next_block_hash.clone_from_slice(sha);
-
- info!("ota length {}", len);
- if let Err(e) = ota.ota_begin(len, 0) {
- error!("ota_begin failed {:?}", e);
- return;
- };
-
- // total payload to expect is image size plus per-block hashes
- let mut remaining = len + (len/(ERASE_BLOCK_BYTES as u32)+ 1) * (SHA256_BYTES as u32);
-
- while remaining > 0 {
- // info!("remaining {}, offset {}", remaining, buf_offset);
- match sock.read(&mut buf[buf_offset..]).await {
- Ok(0) => {
- info!("unexpected eof on read");
- break;
- }
- Ok(chunksize) => {
- buf_offset += chunksize;
- remaining -= chunksize as u32;
- }
- Err(x) => {
- warn!("error reading ota image: {:?}", x);
- break;
- }
- }
-
- if remaining <= 0 || buf_offset == SHA256_BYTES + ERASE_BLOCK_BYTES {
- let mut actual_hash = [0u8; SHA256_BYTES];
- let mut hasher = sha_engine.start::<sha::Sha256>();
- let mut p = &buf[..buf_offset];
- while !p.is_empty() {
- match block!(hasher.update(p)) {
- Ok(remaining) => { p = remaining; }
- _ => ()
- }
- }
- block!(hasher.finish(&mut actual_hash));
-
- signed = signed && next_block_hash == actual_hash;
-
- if signed {
- if let Err(_) = ota.ota_write_chunk(&buf[ED25519_SIGNATURE_BYTES..buf_offset]) {
- break;
- }
- } else {
- // accept the rest of the update, just don't flash it.
- warn!("signature invalid with {remaining} left");
- }
- //
- buf_offset = 0;
- next_block_hash.copy_from_slice(&buf[0..SHA256_BYTES]);
- }
- }
- if remaining == 0 {
- info!("wrote all bytes");
- info!("write {:?}", sock.write(b"BYE\n").await);
- let _ = sock.flush().await;
- sock.close();
- if signed && !ota.ota_flush(false, false).is_ok() {
- error!("ota failed");
- }
- Timer::after_millis(1000).await;
- esp_hal::system::software_reset();
- } else {
- warn!("stream ended early or flash failed with {} bytes left", remaining);
- }
-}
fn register_session_key(key : &[u8]) {
info!("got session key{:?}", key);
}
+
async fn run_tcp_handler(stack : Stack<'_>,
- sha_peripheral: esp_hal::peripherals::SHA<'static>,
- flash_peripheral: esp_hal::peripherals::FLASH<'static>) {
+ mut flasher : crate::ota::Flasher<'_>) {
let mut rx_buffer = [0; 4096];
let mut tx_buffer = [0; 4096];
let mut buf = [0u8; 96];
- let flash = FlashStorage::new(flash_peripheral);
- let mut sha_engine = sha::Sha::new(sha_peripheral);
- let mut ota = Ota::new(flash).unwrap();
-
loop {
let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
@@ -377,7 +266,7 @@ async fn run_tcp_handler(stack : Stack<'_>,
match &buf[0..4] {
b"ROM0" => {
info!("OTA request");
- write_ota(&mut ota, &mut sha_engine, &mut socket).await;
+ flasher.write(&mut socket).await;
},
b"KEY0" => {
info!("sessionkey");