1#![cfg_attr(not(target_os = "none"), allow(dead_code))]
8
9#[cfg(target_os = "none")]
10use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
11#[cfg(target_os = "none")]
12use embassy_sync::blocking_mutex::Mutex;
13#[cfg(target_os = "none")]
14use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
15#[cfg(target_os = "none")]
16use serde::{Deserialize, Serialize};
17#[cfg(target_os = "none")]
18use static_cell::StaticCell;
19
20#[cfg(target_os = "none")]
21use crate::{Error, Result};
22#[cfg(target_os = "none")]
23use device_envoy_core::flash_block::{
24 self as core_flash, FlashBlock as CoreFlashBlock, FlashBlockError, FlashDevice,
25};
26
27pub use device_envoy_core::flash_block::FlashBlock;
28
29#[cfg(target_os = "none")]
30const FLASH_BLOCK_SIZE: usize = <esp_storage::FlashStorage<'static> as NorFlash>::ERASE_SIZE;
31#[cfg(target_os = "none")]
32const FLASH_BLOCK_SIZE_U32: u32 = FLASH_BLOCK_SIZE as u32;
33#[cfg(target_os = "none")]
34const DEFAULT_FLASH_REGION_BYTES: u32 = 16 * FLASH_BLOCK_SIZE_U32;
35
36#[cfg(target_os = "none")]
39struct EspFlashAdapter<'a>(&'a mut esp_storage::FlashStorage<'static>);
40
41#[cfg(target_os = "none")]
42impl FlashDevice for EspFlashAdapter<'_> {
43 type Error = esp_storage::FlashStorageError;
44
45 fn read(
46 &mut self,
47 offset: u32,
48 bytes: &mut [u8],
49 ) -> Result<(), esp_storage::FlashStorageError> {
50 ReadNorFlash::read(self.0, offset, bytes)
51 }
52
53 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), esp_storage::FlashStorageError> {
54 NorFlash::write(self.0, offset, bytes)
55 }
56
57 fn erase(&mut self, from: u32, to: u32) -> Result<(), esp_storage::FlashStorageError> {
58 NorFlash::erase(self.0, from, to)
59 }
60}
61
62#[cfg(target_os = "none")]
63fn convert_flash_block_error(e: FlashBlockError<esp_storage::FlashStorageError>) -> Error {
64 match e {
65 FlashBlockError::Io(err) => Error::FlashStorage(err),
66 FlashBlockError::FormatError => Error::FormatError,
67 FlashBlockError::StorageCorrupted => Error::StorageCorrupted,
68 }
69}
70
71#[cfg(target_os = "none")]
72#[derive(Clone, Copy, Debug, Eq, PartialEq)]
73enum FlashRegionRequest {
74 Tail { byte_len: u32 },
75}
76
77#[cfg(target_os = "none")]
78#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79struct ResolvedFlashRegion {
80 start_offset: u32,
81 block_count: u32,
82}
83
84#[cfg(target_os = "none")]
85impl FlashRegionRequest {
86 fn resolve(self, flash_capacity: u32) -> Result<ResolvedFlashRegion> {
87 let Self::Tail { byte_len } = self;
88 if byte_len == 0 || byte_len > flash_capacity {
89 return Err(Error::InvalidFlashRegion);
90 }
91 let start_offset = flash_capacity - byte_len;
92
93 if start_offset % FLASH_BLOCK_SIZE_U32 != 0 || byte_len % FLASH_BLOCK_SIZE_U32 != 0 {
94 return Err(Error::InvalidFlashRegion);
95 }
96 let end_offset = start_offset
97 .checked_add(byte_len)
98 .ok_or(Error::InvalidFlashRegion)?;
99 if end_offset > flash_capacity {
100 return Err(Error::InvalidFlashRegion);
101 }
102 Ok(ResolvedFlashRegion {
103 start_offset,
104 block_count: byte_len / FLASH_BLOCK_SIZE_U32,
105 })
106 }
107}
108
109#[cfg(target_os = "none")]
110struct FlashManager {
111 flash_storage:
112 Mutex<CriticalSectionRawMutex, core::cell::RefCell<esp_storage::FlashStorage<'static>>>,
113 next_block: core::sync::atomic::AtomicU32,
114 requested_region: FlashRegionRequest,
115 resolved_region: ResolvedFlashRegion,
116}
117
118#[cfg(target_os = "none")]
119impl FlashManager {
120 fn new(
121 flash: esp_hal::peripherals::FLASH<'static>,
122 requested_region: FlashRegionRequest,
123 ) -> Result<Self> {
124 let flash_storage = esp_storage::FlashStorage::new(flash);
125 let flash_capacity = ReadNorFlash::capacity(&flash_storage) as u32;
126 let resolved_region = requested_region.resolve(flash_capacity)?;
127 Ok(Self {
128 flash_storage: Mutex::new(core::cell::RefCell::new(flash_storage)),
129 next_block: core::sync::atomic::AtomicU32::new(0),
130 requested_region,
131 resolved_region,
132 })
133 }
134
135 fn with_flash<R>(
136 &self,
137 f: impl FnOnce(&mut esp_storage::FlashStorage<'static>) -> Result<R>,
138 ) -> Result<R> {
139 self.flash_storage.lock(|flash_storage| {
140 let mut flash_storage_ref = flash_storage.borrow_mut();
141 f(&mut flash_storage_ref)
142 })
143 }
144
145 fn reserve<const N: usize>(&'static self) -> Result<[FlashBlockEsp; N]> {
146 let start_block = self
147 .next_block
148 .fetch_add(N as u32, core::sync::atomic::Ordering::SeqCst);
149 let end_block = start_block
150 .checked_add(N as u32)
151 .ok_or(Error::IndexOutOfBounds)?;
152 if end_block > self.resolved_region.block_count {
153 self.next_block
154 .fetch_sub(N as u32, core::sync::atomic::Ordering::SeqCst);
155 return Err(Error::IndexOutOfBounds);
156 }
157
158 Ok(core::array::from_fn(|block_index| FlashBlockEsp {
159 manager: self,
160 block_id: start_block + block_index as u32,
161 }))
162 }
163
164 fn block_offset(&self, block_id: u32) -> Result<u32> {
165 if block_id >= self.resolved_region.block_count {
166 return Err(Error::IndexOutOfBounds);
167 }
168 let reverse_index = self.resolved_region.block_count - 1 - block_id;
169 Ok(self.resolved_region.start_offset + reverse_index * FLASH_BLOCK_SIZE_U32)
170 }
171}
172
173#[cfg(target_os = "none")]
174struct FlashBlockEspStatic {
175 manager_cell: StaticCell<FlashManager>,
176 manager_ref: Mutex<CriticalSectionRawMutex, core::cell::RefCell<Option<&'static FlashManager>>>,
177}
178
179#[cfg(target_os = "none")]
180impl FlashBlockEspStatic {
181 const fn new() -> Self {
182 Self {
183 manager_cell: StaticCell::new(),
184 manager_ref: Mutex::new(core::cell::RefCell::new(None)),
185 }
186 }
187
188 fn manager(
189 &'static self,
190 flash: esp_hal::peripherals::FLASH<'static>,
191 requested_region: FlashRegionRequest,
192 ) -> Result<&'static FlashManager> {
193 self.manager_ref.lock(|manager_slot| {
194 let mut manager_slot = manager_slot.borrow_mut();
195 if let Some(manager) = *manager_slot {
196 if manager.requested_region != requested_region {
197 return Err(Error::FlashRegionMismatch);
198 }
199 return Ok(manager);
200 }
201
202 let manager_ref = self
203 .manager_cell
204 .init(FlashManager::new(flash, requested_region)?);
205 *manager_slot = Some(manager_ref);
206 Ok(manager_ref)
207 })
208 }
209}
210
211#[cfg(target_os = "none")]
212#[cfg(target_os = "none")]
278#[derive(Clone, Copy)]
279pub struct FlashBlockEsp {
280 manager: &'static FlashManager,
281 block_id: u32,
282}
283
284#[cfg(target_os = "none")]
285impl CoreFlashBlock for FlashBlockEsp {
286 type Error = Error;
287
288 fn load<T>(&mut self) -> Result<Option<T>>
289 where
290 T: Serialize + for<'de> Deserialize<'de>,
291 {
292 let block_offset = self.manager.block_offset(self.block_id)?;
293 self.manager.with_flash(|flash_storage| {
294 let mut adapter = EspFlashAdapter(flash_storage);
295 core_flash::load_block::<{ FLASH_BLOCK_SIZE }, T, _>(&mut adapter, block_offset)
296 .map_err(convert_flash_block_error)
297 })
298 }
299
300 fn save<T>(&mut self, value: &T) -> Result<()>
301 where
302 T: Serialize + for<'de> Deserialize<'de>,
303 {
304 let block_offset = self.manager.block_offset(self.block_id)?;
305 self.manager.with_flash(|flash_storage| {
306 let mut adapter = EspFlashAdapter(flash_storage);
307 core_flash::save_block::<{ FLASH_BLOCK_SIZE }, _, _>(&mut adapter, block_offset, value)
308 .map_err(convert_flash_block_error)
309 })
310 }
311
312 fn clear(&mut self) -> Result<()> {
313 let block_offset = self.manager.block_offset(self.block_id)?;
314 self.manager.with_flash(|flash_storage| {
315 let mut adapter = EspFlashAdapter(flash_storage);
316 core_flash::clear_block::<{ FLASH_BLOCK_SIZE }, _>(&mut adapter, block_offset)
317 .map_err(convert_flash_block_error)
318 })
319 }
320}
321
322#[cfg(target_os = "none")]
323impl FlashBlockEsp {
324 pub fn new_array<const N: usize>(
326 flash: esp_hal::peripherals::FLASH<'static>,
327 ) -> Result<[FlashBlockEsp; N]> {
328 Self::new_array_with_request(
329 flash,
330 FlashRegionRequest::Tail {
331 byte_len: DEFAULT_FLASH_REGION_BYTES,
332 },
333 )
334 }
335
336 fn new_array_with_request<const N: usize>(
337 flash: esp_hal::peripherals::FLASH<'static>,
338 requested_region: FlashRegionRequest,
339 ) -> Result<[FlashBlockEsp; N]> {
340 static FLASH_BLOCK_ESP_STATIC: FlashBlockEspStatic = FlashBlockEspStatic::new();
341 let manager = FLASH_BLOCK_ESP_STATIC.manager(flash, requested_region)?;
342 manager.reserve::<N>()
343 }
344}