device_envoy/
flash_array.rs1use core::array;
9use core::cell::RefCell;
10use crc32fast::Hasher;
11use defmt::{error, info};
12use embassy_rp::Peri;
13use embassy_rp::flash::{Blocking, ERASE_SIZE, Flash as EmbassyFlash};
14use embassy_rp::peripherals::FLASH;
15use embassy_sync::blocking_mutex::Mutex;
16use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
17use portable_atomic::{AtomicU32, Ordering};
18use serde::{Deserialize, Serialize};
19use static_cell::StaticCell;
20
21use crate::{Error, Result};
22
23#[cfg(feature = "pico2")]
25const INTERNAL_FLASH_SIZE: usize = 4 * 1024 * 1024;
26
27#[cfg(all(not(feature = "pico2"), feature = "pico1"))]
29const INTERNAL_FLASH_SIZE: usize = 2 * 1024 * 1024;
30
31#[cfg(all(not(feature = "pico2"), not(feature = "pico1")))]
33pub const INTERNAL_FLASH_SIZE: usize = 2 * 1024 * 1024;
34
35const MAGIC: u32 = 0x424C_4B53; const HEADER_SIZE: usize = 4 + 4 + 2; const CRC_SIZE: usize = 4;
38const MAX_PAYLOAD_SIZE: usize = ERASE_SIZE - HEADER_SIZE - CRC_SIZE; const TOTAL_BLOCKS: u32 = (INTERNAL_FLASH_SIZE / ERASE_SIZE) as u32;
40
41struct FlashManager {
43 flash: Mutex<
44 CriticalSectionRawMutex,
45 RefCell<EmbassyFlash<'static, FLASH, Blocking, INTERNAL_FLASH_SIZE>>,
46 >,
47 next_block: AtomicU32,
48}
49
50impl FlashManager {
51 fn new(peripheral: Peri<'static, FLASH>) -> Self {
52 Self {
53 flash: Mutex::new(core::cell::RefCell::new(EmbassyFlash::new_blocking(
54 peripheral,
55 ))),
56 next_block: AtomicU32::new(0),
57 }
58 }
59
60 fn with_flash<R>(
61 &self,
62 f: impl FnOnce(&mut EmbassyFlash<'static, FLASH, Blocking, INTERNAL_FLASH_SIZE>) -> Result<R>,
63 ) -> Result<R> {
64 self.flash.lock(|flash| {
65 let mut flash_ref = flash.borrow_mut();
66 f(&mut *flash_ref)
67 })
68 }
69
70 fn reserve<const N: usize>(&'static self) -> Result<[FlashBlock; N]> {
71 let start = self.next_block.fetch_add(N as u32, Ordering::SeqCst);
72 let end = start.checked_add(N as u32).ok_or(Error::IndexOutOfBounds)?;
73 if end > TOTAL_BLOCKS {
74 self.next_block.fetch_sub(N as u32, Ordering::SeqCst);
76 return Err(Error::IndexOutOfBounds);
77 }
78 Ok(array::from_fn(|idx| FlashBlock {
79 manager: self,
80 block: start + idx as u32,
81 }))
82 }
83}
84
85pub struct FlashBlock {
89 manager: &'static FlashManager,
90 block: u32,
91}
92
93impl FlashBlock {
94 pub fn load<T>(&mut self) -> Result<Option<T>>
98 where
99 T: Serialize + for<'de> Deserialize<'de>,
100 {
101 load_block(self.manager, self.block)
102 }
103
104 pub fn save<T>(&mut self, value: &T) -> Result<()>
108 where
109 T: Serialize + for<'de> Deserialize<'de>,
110 {
111 save_block(self.manager, self.block, value)
112 }
113
114 pub fn clear(&mut self) -> Result<()> {
116 clear_block(self.manager, self.block)
117 }
118}
119
120pub(crate) struct FlashArrayStatic {
122 manager_cell: StaticCell<FlashManager>,
123 manager_ref: Mutex<CriticalSectionRawMutex, core::cell::RefCell<Option<&'static FlashManager>>>,
124}
125
126impl FlashArrayStatic {
127 #[must_use]
128 const fn new() -> Self {
129 Self {
130 manager_cell: StaticCell::new(),
131 manager_ref: Mutex::new(core::cell::RefCell::new(None)),
132 }
133 }
134
135 fn manager(&'static self, peripheral: Peri<'static, FLASH>) -> &'static FlashManager {
136 self.manager_ref.lock(|slot_cell| {
137 let mut slot = slot_cell.borrow_mut();
138 if slot.is_none() {
139 let manager_mut = self.manager_cell.init(FlashManager::new(peripheral));
140 let manager_ref: &'static FlashManager = manager_mut;
141 *slot = Some(manager_ref);
142 }
143 slot.expect("manager initialized")
144 })
145 }
146}
147
148pub struct FlashArray<const N: usize>;
226
227impl<const N: usize> FlashArray<N> {
228 pub fn new(peripheral: Peri<'static, FLASH>) -> Result<[FlashBlock; N]> {
232 static FLASH_STATIC: FlashArrayStatic = FlashArrayStatic::new();
233 let manager = FLASH_STATIC.manager(peripheral);
234 manager.reserve::<N>()
235 }
236}
237
238fn save_block<T>(manager: &'static FlashManager, block: u32, value: &T) -> Result<()>
239where
240 T: Serialize + for<'de> Deserialize<'de>,
241{
242 let mut payload_buffer = [0u8; MAX_PAYLOAD_SIZE];
243 let payload_len = postcard::to_slice(value, &mut payload_buffer)
244 .map_err(|_| {
245 error!(
246 "Flash: Serialization failed or data too large (max {} bytes)",
247 MAX_PAYLOAD_SIZE
248 );
249 Error::FormatError
250 })?
251 .len();
252
253 let mut buffer = [0xFFu8; ERASE_SIZE];
254 buffer[0..4].copy_from_slice(&MAGIC.to_le_bytes());
255 buffer[4..8].copy_from_slice(&compute_type_hash::<T>().to_le_bytes());
256 buffer[8..10].copy_from_slice(&(payload_len as u16).to_le_bytes());
257 buffer[HEADER_SIZE..HEADER_SIZE + payload_len].copy_from_slice(&payload_buffer[..payload_len]);
258
259 let crc_offset = HEADER_SIZE + payload_len;
260 let crc = compute_crc(&buffer[0..crc_offset]);
261 buffer[crc_offset..crc_offset + CRC_SIZE].copy_from_slice(&crc.to_le_bytes());
262
263 let offset = block_offset(block);
264 manager.with_flash(|flash| {
265 flash
266 .blocking_erase(offset, offset + ERASE_SIZE as u32)
267 .map_err(Error::Flash)?;
268 flash
269 .blocking_write(offset, &buffer)
270 .map_err(Error::Flash)?;
271 Ok(())
272 })?;
273
274 info!("Flash: Saved {} bytes to block {}", payload_len, block);
275 Ok(())
276}
277
278fn load_block<T>(manager: &'static FlashManager, block: u32) -> Result<Option<T>>
279where
280 T: Serialize + for<'de> Deserialize<'de>,
281{
282 let offset = block_offset(block);
283 let mut buffer = [0u8; ERASE_SIZE];
284
285 manager.with_flash(|flash| {
286 flash
287 .blocking_read(offset, &mut buffer)
288 .map_err(Error::Flash)?;
289 Ok(())
290 })?;
291
292 let magic = u32::from_le_bytes(buffer[0..4].try_into().unwrap());
293 if magic != MAGIC {
294 info!("Flash: No data at block {}", block);
295 return Ok(None);
296 }
297
298 let stored_type_hash = u32::from_le_bytes(buffer[4..8].try_into().unwrap());
299 let expected_type_hash = compute_type_hash::<T>();
300 if stored_type_hash != expected_type_hash {
301 info!(
302 "Flash: Type mismatch at block {} (expected hash {}, found {})",
303 block, expected_type_hash, stored_type_hash
304 );
305 return Ok(None);
306 }
307
308 let payload_len = u16::from_le_bytes(buffer[8..10].try_into().unwrap()) as usize;
309 if payload_len > MAX_PAYLOAD_SIZE {
310 error!(
311 "Flash: Invalid payload length {} at block {}",
312 payload_len, block
313 );
314 return Err(Error::StorageCorrupted);
315 }
316
317 let crc_offset = HEADER_SIZE + payload_len;
318 let stored_crc = u32::from_le_bytes(
319 buffer[crc_offset..crc_offset + CRC_SIZE]
320 .try_into()
321 .unwrap(),
322 );
323 let computed_crc = compute_crc(&buffer[0..crc_offset]);
324 if stored_crc != computed_crc {
325 error!(
326 "Flash: CRC mismatch at block {} (expected {}, found {})",
327 block, computed_crc, stored_crc
328 );
329 return Err(Error::StorageCorrupted);
330 }
331
332 let payload = &buffer[HEADER_SIZE..HEADER_SIZE + payload_len];
333 let value: T = postcard::from_bytes(payload).map_err(|_| {
334 error!("Flash: Deserialization failed at block {}", block);
335 Error::StorageCorrupted
336 })?;
337
338 info!("Flash: Loaded data from block {}", block);
339 Ok(Some(value))
340}
341
342fn clear_block(manager: &'static FlashManager, block: u32) -> Result<()> {
343 let offset = block_offset(block);
344 manager.with_flash(|flash| {
345 flash
346 .blocking_erase(offset, offset + ERASE_SIZE as u32)
347 .map_err(Error::Flash)?;
348 Ok(())
349 })?;
350 info!("Flash: Cleared block {}", block);
351 Ok(())
352}
353
354fn block_offset(block_id: u32) -> u32 {
356 let capacity = INTERNAL_FLASH_SIZE as u32;
357 capacity - (block_id + 1) * ERASE_SIZE as u32
358}
359
360fn compute_type_hash<T>() -> u32 {
362 const FNV_PRIME: u32 = 16_777_619;
363 const FNV_OFFSET: u32 = 2_166_136_261;
364
365 let type_name = core::any::type_name::<T>();
366 let mut hash = FNV_OFFSET;
367
368 for byte in type_name.bytes() {
369 hash ^= u32::from(byte);
370 hash = hash.wrapping_mul(FNV_PRIME);
371 }
372
373 hash
374}
375
376fn compute_crc(data: &[u8]) -> u32 {
378 let mut hasher = Hasher::new();
379 hasher.update(data);
380 hasher.finalize()
381}