use crate::{
PartitionBuffer, PartitionEntry, PartitionError, PartitionReaderState, PartitionTable,
PartitionWriterState, SliceExt,
};
use core::{mem::MaybeUninit, ops::Deref};
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NorFlashOpError<S: ReadNorFlash> {
PartitionError(PartitionError),
StorageError(S::Error),
}
impl<S: ReadNorFlash> From<PartitionError> for NorFlashOpError<S> {
fn from(error: PartitionError) -> Self {
Self::PartitionError(error)
}
}
impl PartitionTable {
pub fn iter_nor_flash<'s, S>(
&self,
storage: &'s mut S,
calc_md5: bool,
) -> PartitionNorFlashIter<'s, S>
where
S: ReadNorFlash,
{
PartitionNorFlashIter {
storage,
state: PartitionReaderState::new(self.addr, self.size, calc_md5),
buffer: MaybeUninit::uninit(),
}
}
#[cfg(feature = "embedded-storage")]
pub fn read_nor_flash<S, T>(
&self,
storage: &mut S,
check_md5: Option<bool>,
) -> Result<T, NorFlashOpError<S>>
where
S: ReadNorFlash,
T: FromIterator<PartitionEntry>,
{
let mut iter = self.iter_nor_flash(storage, check_md5.is_some());
let result = (&mut iter).collect::<Result<_, _>>()?;
#[cfg(feature = "md5")]
if let Some(mandatory_md5) = check_md5 {
if !iter.check_md5().unwrap_or(!mandatory_md5) {
return Err(PartitionError::InvalidMd5.into());
}
}
Ok(result)
}
#[cfg(feature = "embedded-storage")]
pub fn write_nor_flash<S>(
&self,
storage: &mut S,
partitions: impl IntoIterator<Item = impl AsRef<PartitionEntry>>,
write_md5: bool,
) -> Result<usize, NorFlashOpError<S>>
where
S: NorFlash,
{
const SECTOR_SIZE: usize = PartitionTable::MAX_SIZE;
let mut sector_data = MaybeUninit::<[u8; SECTOR_SIZE]>::uninit();
let sector_data = unsafe { sector_data.assume_init_mut() };
let mut data = &mut sector_data[..];
let mut state = PartitionWriterState::new(self.addr, self.size, write_md5);
for partition in partitions {
if state.is_done() {
return Err(PartitionError::TooManyData.into());
}
let (head, rest) = data.split_array_mut_();
state.write(head, partition)?;
data = rest;
}
#[cfg(feature = "md5")]
if write_md5 {
if state.is_done() {
return Err(PartitionError::TooManyData.into());
}
let (head, rest) = data.split_array_mut_();
state.write_md5(head)?;
data = rest;
}
data.fill(0);
storage
.write(0, sector_data)
.map_err(NorFlashOpError::StorageError)?;
Ok((state.offset() - self.addr) as usize)
}
}
pub struct PartitionNorFlashIter<'s, S> {
storage: &'s mut S,
state: PartitionReaderState,
buffer: MaybeUninit<PartitionBuffer>,
}
impl<'s, S> PartitionNorFlashIter<'s, S> {
pub fn next_partition(&mut self) -> Result<PartitionEntry, NorFlashOpError<S>>
where
S: ReadNorFlash,
{
if self.state.is_done() {
return Err(NorFlashOpError::PartitionError(
PartitionError::NotEnoughData,
));
}
if let Err(error) = self.storage.read(self.state.offset(), unsafe {
self.buffer.assume_init_mut()
}) {
return Err(NorFlashOpError::StorageError(error));
}
self.state
.read(unsafe { self.buffer.assume_init_ref() })
.map_err(From::from)
}
}
impl<'s, S> Deref for PartitionNorFlashIter<'s, S> {
type Target = PartitionReaderState;
fn deref(&self) -> &Self::Target {
&self.state
}
}
impl<'s, S> Iterator for PartitionNorFlashIter<'s, S>
where
S: ReadNorFlash,
{
type Item = Result<PartitionEntry, NorFlashOpError<S>>;
fn next(&mut self) -> Option<Self::Item> {
self.next_partition()
.map(Some)
.or_else(|error| {
if matches!(
error,
NorFlashOpError::PartitionError(PartitionError::NotEnoughData)
) {
Ok(None)
} else {
Err(error)
}
})
.transpose()
}
}