esp_partition_table/
norfl.rs

1use crate::{
2    PartitionBuffer, PartitionEntry, PartitionError, PartitionReaderState, PartitionTable,
3    PartitionWriterState,
4};
5use core::{mem::MaybeUninit, ops::Deref};
6use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
7
8/// Error type for embedded storage operations
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum NorFlashOpError<S: ReadNorFlash> {
11    /// Partition specific error
12    PartitionError(PartitionError),
13    /// Storage specific error
14    StorageError(S::Error),
15}
16
17impl<S: ReadNorFlash> From<PartitionError> for NorFlashOpError<S> {
18    fn from(error: PartitionError) -> Self {
19        Self::PartitionError(error)
20    }
21}
22
23impl PartitionTable {
24    /// Get iterator over partitions from table
25    ///
26    /// If `md5` feature isn't enabled `calc_md5` argument will be ignored.
27    pub fn iter_nor_flash<'s, S>(
28        &self,
29        storage: &'s mut S,
30        calc_md5: bool,
31    ) -> PartitionNorFlashIter<'s, S>
32    where
33        S: ReadNorFlash,
34    {
35        PartitionNorFlashIter {
36            storage,
37            state: PartitionReaderState::new(self.addr, self.size, calc_md5),
38            buffer: MaybeUninit::uninit(),
39        }
40    }
41
42    /// Read partitions from table
43    ///
44    /// The `check_md5` argument means following:
45    /// - None - ignore MD5 checksum
46    /// - Some(false) - check MD5 when found (optional MD5)
47    /// - Some(true) - MD5 checksum is mandatory
48    ///
49    /// If `md5` feature isn't enabled `check_md5` argument will be ignored.
50    #[cfg(feature = "embedded-storage")]
51    pub fn read_nor_flash<S, T>(
52        &self,
53        storage: &mut S,
54        check_md5: Option<bool>,
55    ) -> Result<T, NorFlashOpError<S>>
56    where
57        S: ReadNorFlash,
58        T: FromIterator<PartitionEntry>,
59    {
60        let mut iter = self.iter_nor_flash(storage, check_md5.is_some());
61        let result = (&mut iter).collect::<Result<_, _>>()?;
62
63        #[cfg(feature = "md5")]
64        if let Some(mandatory_md5) = check_md5 {
65            if !iter.check_md5().unwrap_or(!mandatory_md5) {
66                return Err(PartitionError::InvalidMd5.into());
67            }
68        }
69
70        Ok(result)
71    }
72
73    /// Write partitions into table
74    ///
75    /// If `md5` feature isn't enabled `write_md5` argument will be ignored.
76    #[cfg(feature = "embedded-storage")]
77    pub fn write_nor_flash<S>(
78        &self,
79        storage: &mut S,
80        partitions: impl IntoIterator<Item = impl AsRef<PartitionEntry>>,
81        write_md5: bool,
82    ) -> Result<usize, NorFlashOpError<S>>
83    where
84        S: NorFlash,
85    {
86        // The following is not supported by the compiler
87        // (can't use generic parameters from outer function)
88        // const SECTOR_SIZE: usize = S::ERASE_SIZE;
89        const SECTOR_SIZE: usize = PartitionTable::MAX_SIZE;
90
91        let mut sector_data = MaybeUninit::<[u8; SECTOR_SIZE]>::uninit();
92        let sector_data = unsafe { sector_data.assume_init_mut() };
93        let mut data = &mut sector_data[..];
94        let mut state = PartitionWriterState::new(self.addr, self.size, write_md5);
95
96        for partition in partitions {
97            if state.is_done() {
98                return Err(PartitionError::TooManyData.into());
99            }
100
101            let (head, rest) = data
102                .split_first_chunk_mut()
103                .ok_or(PartitionError::NotEnoughData)?;
104
105            state.write(head, partition)?;
106
107            data = rest;
108        }
109
110        #[cfg(feature = "md5")]
111        if write_md5 {
112            if state.is_done() {
113                return Err(PartitionError::TooManyData.into());
114            }
115
116            let (head, rest) = data
117                .split_first_chunk_mut()
118                .ok_or(PartitionError::NotEnoughData)?;
119
120            state.write_md5(head)?;
121
122            data = rest;
123        }
124
125        data.fill(0);
126
127        storage
128            .write(0, sector_data)
129            .map_err(NorFlashOpError::StorageError)?;
130
131        Ok((state.offset() - self.addr) as usize)
132    }
133}
134
135/// Iterator over embedded partition table
136pub struct PartitionNorFlashIter<'s, S> {
137    storage: &'s mut S,
138    state: PartitionReaderState,
139    buffer: MaybeUninit<PartitionBuffer>,
140}
141
142impl<S> PartitionNorFlashIter<'_, S> {
143    /// Read next partition entry
144    pub fn next_partition(&mut self) -> Result<PartitionEntry, NorFlashOpError<S>>
145    where
146        S: ReadNorFlash,
147    {
148        if self.state.is_done() {
149            return Err(NorFlashOpError::PartitionError(
150                PartitionError::NotEnoughData,
151            ));
152        }
153
154        // Assume that partition data buffer aligned and bigger than S::READ_SIZE
155        if let Err(error) = self.storage.read(self.state.offset(), unsafe {
156            self.buffer.assume_init_mut()
157        }) {
158            return Err(NorFlashOpError::StorageError(error));
159        }
160
161        self.state
162            .read(unsafe { self.buffer.assume_init_ref() })
163            .map_err(From::from)
164    }
165}
166
167impl<S> Deref for PartitionNorFlashIter<'_, S> {
168    type Target = PartitionReaderState;
169
170    fn deref(&self) -> &Self::Target {
171        &self.state
172    }
173}
174
175impl<S> Iterator for PartitionNorFlashIter<'_, S>
176where
177    S: ReadNorFlash,
178{
179    type Item = Result<PartitionEntry, NorFlashOpError<S>>;
180
181    fn next(&mut self) -> Option<Self::Item> {
182        self.next_partition()
183            .map(Some)
184            .or_else(|error| {
185                if matches!(
186                    error,
187                    NorFlashOpError::PartitionError(PartitionError::NotEnoughData)
188                ) {
189                    Ok(None)
190                } else {
191                    Err(error)
192                }
193            })
194            .transpose()
195    }
196}