Add blocking API to FirmwareUpdater, and allow for a split prepare/write api
This commit is contained in:
parent
3c24ad2db6
commit
7f16b1cd23
@ -660,12 +660,6 @@ impl FirmwareUpdater {
|
|||||||
) -> Result<(), F::Error> {
|
) -> Result<(), F::Error> {
|
||||||
assert!(data.len() >= F::ERASE_SIZE);
|
assert!(data.len() >= F::ERASE_SIZE);
|
||||||
|
|
||||||
trace!(
|
|
||||||
"Writing firmware at offset 0x{:x} len {}",
|
|
||||||
self.dfu.from + offset,
|
|
||||||
data.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
flash
|
flash
|
||||||
.erase(
|
.erase(
|
||||||
(self.dfu.from + offset) as u32,
|
(self.dfu.from + offset) as u32,
|
||||||
@ -679,7 +673,141 @@ impl FirmwareUpdater {
|
|||||||
self.dfu.from + offset + data.len()
|
self.dfu.from + offset + data.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut write_offset = self.dfu.from + offset;
|
FirmwareWriter(self)
|
||||||
|
.write_firmware(offset, data, flash, block_size)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepare for an incoming DFU update by erasing the entire DFU area and
|
||||||
|
/// returning a `FirmwareWriter`.
|
||||||
|
///
|
||||||
|
/// Using this instead of `write_firmware` allows for an optimized API in
|
||||||
|
/// exchange for added complexity.
|
||||||
|
pub fn prepare_update<F: NorFlash>(&mut self, flash: &mut F) -> Result<FirmwareWriter, F::Error> {
|
||||||
|
flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?;
|
||||||
|
|
||||||
|
trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
|
||||||
|
|
||||||
|
Ok(FirmwareWriter(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Blocking API
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Mark to trigger firmware swap on next boot.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
||||||
|
pub fn mark_updated_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> {
|
||||||
|
assert_eq!(aligned.len(), F::WRITE_SIZE);
|
||||||
|
self.set_magic_blocking(aligned, SWAP_MAGIC, flash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
||||||
|
pub fn mark_booted_blocking<F: NorFlash>(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> {
|
||||||
|
assert_eq!(aligned.len(), F::WRITE_SIZE);
|
||||||
|
self.set_magic_blocking(aligned, BOOT_MAGIC, flash)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_magic_blocking<F: NorFlash>(
|
||||||
|
&mut self,
|
||||||
|
aligned: &mut [u8],
|
||||||
|
magic: u8,
|
||||||
|
flash: &mut F,
|
||||||
|
) -> Result<(), F::Error> {
|
||||||
|
flash.read(self.state.from as u32, aligned)?;
|
||||||
|
|
||||||
|
if aligned.iter().any(|&b| b != magic) {
|
||||||
|
aligned.fill(0);
|
||||||
|
|
||||||
|
flash.write(self.state.from as u32, aligned)?;
|
||||||
|
flash.erase(self.state.from as u32, self.state.to as u32)?;
|
||||||
|
|
||||||
|
aligned.fill(magic);
|
||||||
|
flash.write(self.state.from as u32, aligned)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write data to a flash page.
|
||||||
|
///
|
||||||
|
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Failing to meet alignment and size requirements may result in a panic.
|
||||||
|
pub fn write_firmware_blocking<F: NorFlash>(
|
||||||
|
&mut self,
|
||||||
|
offset: usize,
|
||||||
|
data: &[u8],
|
||||||
|
flash: &mut F,
|
||||||
|
block_size: usize,
|
||||||
|
) -> Result<(), F::Error> {
|
||||||
|
assert!(data.len() >= F::ERASE_SIZE);
|
||||||
|
|
||||||
|
flash.erase(
|
||||||
|
(self.dfu.from + offset) as u32,
|
||||||
|
(self.dfu.from + offset + data.len()) as u32,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
"Erased from {} to {}",
|
||||||
|
self.dfu.from + offset,
|
||||||
|
self.dfu.from + offset + data.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
FirmwareWriter(self).write_firmware_blocking(offset, data, flash, block_size)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepare for an incoming DFU update by erasing the entire DFU area and
|
||||||
|
/// returning a `FirmwareWriter`.
|
||||||
|
///
|
||||||
|
/// Using this instead of `write_firmware_blocking` allows for an optimized
|
||||||
|
/// API in exchange for added complexity.
|
||||||
|
pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<FirmwareWriter, F::Error> {
|
||||||
|
flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?;
|
||||||
|
|
||||||
|
trace!("Erased from {} to {}", self.dfu.from, self.dfu.to);
|
||||||
|
|
||||||
|
Ok(FirmwareWriter(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FirmwareWriter allows writing blocks to an already erased flash.
|
||||||
|
pub struct FirmwareWriter<'a>(&'a mut FirmwareUpdater);
|
||||||
|
|
||||||
|
impl<'a> FirmwareWriter<'a> {
|
||||||
|
/// Write data to a flash page.
|
||||||
|
///
|
||||||
|
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Failing to meet alignment and size requirements may result in a panic.
|
||||||
|
pub async fn write_firmware<F: AsyncNorFlash>(
|
||||||
|
&mut self,
|
||||||
|
offset: usize,
|
||||||
|
data: &[u8],
|
||||||
|
flash: &mut F,
|
||||||
|
block_size: usize,
|
||||||
|
) -> Result<(), F::Error> {
|
||||||
|
trace!(
|
||||||
|
"Writing firmware at offset 0x{:x} len {}",
|
||||||
|
self.0.dfu.from + offset,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut write_offset = self.0.dfu.from + offset;
|
||||||
for chunk in data.chunks(block_size) {
|
for chunk in data.chunks(block_size) {
|
||||||
trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
|
trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
|
||||||
flash.write(write_offset as u32, chunk).await?;
|
flash.write(write_offset as u32, chunk).await?;
|
||||||
@ -702,6 +830,50 @@ impl FirmwareUpdater {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write data to a flash page.
|
||||||
|
///
|
||||||
|
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Failing to meet alignment and size requirements may result in a panic.
|
||||||
|
pub fn write_firmware_blocking<F: NorFlash>(
|
||||||
|
&mut self,
|
||||||
|
offset: usize,
|
||||||
|
data: &[u8],
|
||||||
|
flash: &mut F,
|
||||||
|
block_size: usize,
|
||||||
|
) -> Result<(), F::Error> {
|
||||||
|
trace!(
|
||||||
|
"Writing firmware at offset 0x{:x} len {}",
|
||||||
|
self.0.dfu.from + offset,
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut write_offset = self.0.dfu.from + offset;
|
||||||
|
for chunk in data.chunks(block_size) {
|
||||||
|
trace!("Wrote chunk at {}: {:?}", write_offset, chunk);
|
||||||
|
flash.write(write_offset as u32, chunk)?;
|
||||||
|
write_offset += chunk.len();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
trace!("Wrote data, reading back for verification");
|
||||||
|
|
||||||
|
let mut buf: [u8; 4096] = [0; 4096];
|
||||||
|
let mut data_offset = 0;
|
||||||
|
let mut read_offset = self.dfu.from + offset;
|
||||||
|
for chunk in buf.chunks_mut(block_size) {
|
||||||
|
flash.read(read_offset as u32, chunk).await?;
|
||||||
|
trace!("Read chunk at {}: {:?}", read_offset, chunk);
|
||||||
|
assert_eq!(&data[data_offset..data_offset + block_size], chunk);
|
||||||
|
read_offset += chunk.len();
|
||||||
|
data_offset += chunk.len();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
Reference in New Issue
Block a user