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