1use core::mem::MaybeUninit;
2
3#[cfg(not(feature = "emulation"))]
4pub use esp_hal::peripherals::FLASH as Flash;
5
6#[cfg(multi_core)]
7esp_hal::if_unstable_hal! {
8 use esp_hal::peripherals::CPU_CTRL;
9 use esp_hal::system::Cpu;
10 use esp_hal::system::CpuControl;
11 use esp_hal::system::is_running;
12}
13
14use crate::chip_specific;
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16#[non_exhaustive]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub enum FlashStorageError {
20 IoError,
22 IoTimeout,
24 CantUnlock,
26 NotAligned,
28 OutOfBounds,
30 NotSupported,
32 #[cfg(multi_core)]
37 OtherCoreRunning,
38 Other(i32),
40}
41
42#[inline(always)]
43pub fn check_rc(rc: i32) -> Result<(), FlashStorageError> {
45 match rc {
46 0 => Ok(()),
47 1 => Err(FlashStorageError::IoError),
48 2 => Err(FlashStorageError::IoTimeout),
49 _ => Err(FlashStorageError::Other(rc)),
50 }
51}
52
53#[cfg(feature = "emulation")]
54#[derive(Debug)]
55pub struct Flash<'d> {
56 _phantom: core::marker::PhantomData<&'d ()>,
57}
58
59#[cfg(feature = "emulation")]
60impl<'d> Flash<'d> {
61 pub fn new() -> Self {
62 Flash {
63 _phantom: core::marker::PhantomData,
64 }
65 }
66}
67
68#[derive(Debug)]
69#[cfg_attr(feature = "defmt", derive(defmt::Format))]
70#[cfg_attr(
75 not(feature = "emulation"),
76 doc = concat!(
77 "[partition](https://docs.espressif.com/projects/esp-idf/en/latest/",
78 esp_metadata_generated::chip!(),
79 "/api-guides/partition-tables.html)"
80 )
81)]
82#[cfg_attr(
83 feature = "emulation",
84 doc = "[partition](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html)"
85)]
86pub struct FlashStorage<'d> {
96 pub(crate) capacity: usize,
97 unlocked: bool,
98 pub(crate) multi_core_strategy: MultiCoreStrategy,
99 _flash: Flash<'d>,
100}
101
102impl<'d> FlashStorage<'d> {
103 pub const WORD_SIZE: u32 = 4;
105 pub const SECTOR_SIZE: u32 = 4096;
107 pub const BLOCK_SIZE: u32 = 65536;
109
110 pub fn new(flash: Flash<'d>) -> Self {
116 Self {
117 capacity: chip_specific::get_flash_size() as usize,
118 unlocked: false,
119 multi_core_strategy: cfg_select! {
120 multi_core => MultiCoreStrategy::Error,
121 _ => MultiCoreStrategy::Ignore,
122 },
123 _flash: flash,
124 }
125 }
126
127 #[inline(always)]
128 pub(crate) fn check_alignment<const ALIGN: u32>(
129 &self,
130 offset: u32,
131 length: usize,
132 ) -> Result<(), FlashStorageError> {
133 let offset = offset as usize;
134 if !offset.is_multiple_of(ALIGN as usize) || !length.is_multiple_of(ALIGN as usize) {
135 return Err(FlashStorageError::NotAligned);
136 }
137 Ok(())
138 }
139
140 #[inline(always)]
141 pub(crate) fn check_bounds(&self, offset: u32, length: usize) -> Result<(), FlashStorageError> {
142 let offset = offset as usize;
143 if length > self.capacity || offset > self.capacity - length {
144 return Err(FlashStorageError::OutOfBounds);
145 }
146 Ok(())
147 }
148
149 pub(crate) fn internal_read(
150 &mut self,
151 offset: u32,
152 bytes: &mut [MaybeUninit<u8>],
153 ) -> Result<(), FlashStorageError> {
154 check_rc(chip_specific::spiflash_read(
155 offset,
156 bytes.as_mut_ptr() as *mut u32,
157 bytes.len() as u32,
158 ))
159 }
160
161 #[inline(always)]
162 fn unlock_once(&mut self) -> Result<(), FlashStorageError> {
163 if !self.unlocked {
164 if chip_specific::spiflash_unlock() != 0 {
165 return Err(FlashStorageError::CantUnlock);
166 }
167 self.unlocked = true;
168 }
169 Ok(())
170 }
171
172 pub(crate) fn internal_erase_sector(&mut self, sector: u32) -> Result<(), FlashStorageError> {
173 self.multi_core_strategy.with(|| {
174 self.unlock_once()?;
175 check_rc(chip_specific::spiflash_erase_sector(sector))
176 })
177 }
178
179 pub(crate) fn internal_erase_block(&mut self, block: u32) -> Result<(), FlashStorageError> {
180 self.multi_core_strategy.with(|| {
181 self.unlock_once()?;
182 check_rc(chip_specific::spiflash_erase_block(block))
183 })
184 }
185
186 pub(crate) fn internal_write(
187 &mut self,
188 offset: u32,
189 bytes: &[u8],
190 ) -> Result<(), FlashStorageError> {
191 self.multi_core_strategy.with(|| {
192 self.unlock_once()?;
193 check_rc(chip_specific::spiflash_write(
194 offset,
195 bytes.as_ptr() as *const u32,
196 bytes.len() as u32,
197 ))
198 })
199 }
200
201 pub(crate) fn internal_read_encrypted(
202 &mut self,
203 offset: u32,
204 bytes: &mut [MaybeUninit<u8>],
205 ) -> Result<(), FlashStorageError> {
206 let initialized = unsafe {
208 core::slice::from_raw_parts_mut(bytes.as_mut_ptr().cast::<u8>(), bytes.len())
209 };
210 self.multi_core_strategy
213 .with(|| chip_specific::read_flash_encrypted(offset, initialized))
214 }
215
216 pub(crate) fn internal_write_encrypted(
217 &mut self,
218 offset: u32,
219 bytes: &[u8],
220 ) -> Result<(), FlashStorageError> {
221 self.multi_core_strategy.with(|| {
222 check_rc(chip_specific::spiflash_write_encrypted(
223 offset,
224 bytes.as_ptr() as *mut u32,
225 bytes.len() as u32,
226 ))
227 })
228 }
229}
230
231#[derive(Clone, Copy, PartialEq, Eq, Debug)]
234#[cfg_attr(feature = "defmt", derive(defmt::Format))]
235pub(crate) enum MultiCoreStrategy {
236 #[cfg(multi_core)]
239 Error,
240
241 #[cfg(multi_core)]
243 AutoPark,
244
245 Ignore,
249}
250
251#[cfg(multi_core)]
252impl<'d> FlashStorage<'d> {
253 pub fn multicore_auto_park(mut self) -> FlashStorage<'d> {
256 self.multi_core_strategy = MultiCoreStrategy::AutoPark;
257 self
258 }
259
260 pub unsafe fn multicore_ignore(mut self) -> FlashStorage<'d> {
266 self.multi_core_strategy = MultiCoreStrategy::Ignore;
267 self
268 }
269}
270
271impl MultiCoreStrategy {
272 pub(crate) fn pre_write(&self) -> Result<bool, FlashStorageError> {
278 match self {
279 #[cfg(multi_core)]
280 MultiCoreStrategy::Error => {
281 esp_hal::if_unstable_hal! {
282 for other_cpu in Cpu::other() {
283 if is_running(other_cpu) {
284 return Err(FlashStorageError::OtherCoreRunning);
285 }
286 }
287 }
288 Ok(false)
289 }
290
291 #[cfg(multi_core)]
292 MultiCoreStrategy::AutoPark => {
293 esp_hal::if_unstable_hal! {
294 let mut cpu_ctrl = CpuControl::new(unsafe { CPU_CTRL::steal() });
295 for other_cpu in Cpu::other() {
296 if is_running(other_cpu) {
297 unsafe { cpu_ctrl.park_core(other_cpu) };
298 return Ok(true);
299 }
300 }
301 }
302 Ok(false)
303 }
304
305 MultiCoreStrategy::Ignore => Ok(false),
306 }
307 }
308
309 pub(crate) fn post_write(&self, unpark: bool) {
315 cfg_select! {
316 multi_core => {
317 if let MultiCoreStrategy::AutoPark = self
318 && unpark
319 {
320 esp_hal::if_unstable_hal! {
321 let mut cpu_ctrl = CpuControl::new(unsafe { CPU_CTRL::steal() });
322 for other_cpu in Cpu::other() {
323 cpu_ctrl.unpark_core(other_cpu);
324 }
325 }
326 }
327 }
328 _ => {
329 let _ = unpark;
330 }
331 }
332 }
333
334 pub(crate) fn with<R>(
340 self,
341 f: impl FnOnce() -> Result<R, FlashStorageError>,
342 ) -> Result<R, FlashStorageError> {
343 let unpark = self.pre_write()?;
344
345 let result = f();
346
347 self.post_write(unpark);
348 result
349 }
350}