1use core::mem::MaybeUninit;
2
3use crate::chip_specific;
4
5#[derive(Debug)]
6#[non_exhaustive]
7pub enum FlashStorageError {
8 IoError,
9 IoTimeout,
10 CantUnlock,
11 NotAligned,
12 OutOfBounds,
13 Other(i32),
14}
15
16#[inline(always)]
17pub fn check_rc(rc: i32) -> Result<(), FlashStorageError> {
18 match rc {
19 0 => Ok(()),
20 1 => Err(FlashStorageError::IoError),
21 2 => Err(FlashStorageError::IoTimeout),
22 _ => Err(FlashStorageError::Other(rc)),
23 }
24}
25
26#[derive(Debug)]
27pub struct FlashStorage {
28 pub(crate) capacity: usize,
29 unlocked: bool,
30}
31
32impl Default for FlashStorage {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38impl FlashStorage {
39 pub const WORD_SIZE: u32 = 4;
40 pub const SECTOR_SIZE: u32 = 4096;
41
42 pub fn new() -> FlashStorage {
43 let mut storage = FlashStorage {
44 capacity: 0,
45 unlocked: false,
46 };
47
48 #[cfg(not(any(feature = "esp32", feature = "esp32s2")))]
49 const ADDR: u32 = 0x0000;
50 #[cfg(any(feature = "esp32", feature = "esp32s2"))]
51 const ADDR: u32 = 0x1000;
52
53 let mut buffer = crate::buffer::FlashWordBuffer::uninit();
54 storage.internal_read(ADDR, buffer.as_bytes_mut()).unwrap();
55 let buffer = unsafe { buffer.assume_init_bytes() };
56 let mb = match buffer[3] & 0xf0 {
57 0x00 => 1,
58 0x10 => 2,
59 0x20 => 4,
60 0x30 => 8,
61 0x40 => 16,
62 0x50 => 32,
63 _ => 0,
64 };
65 storage.capacity = mb * 1024 * 1024;
66 storage
67 }
68
69 #[inline(always)]
70 pub(crate) fn check_alignment<const ALIGN: u32>(
71 &self,
72 offset: u32,
73 length: usize,
74 ) -> Result<(), FlashStorageError> {
75 let offset = offset as usize;
76 if offset % ALIGN as usize != 0 || length % ALIGN as usize != 0 {
77 return Err(FlashStorageError::NotAligned);
78 }
79 Ok(())
80 }
81
82 #[inline(always)]
83 pub(crate) fn check_bounds(&self, offset: u32, length: usize) -> Result<(), FlashStorageError> {
84 let offset = offset as usize;
85 if length > self.capacity || offset > self.capacity - length {
86 return Err(FlashStorageError::OutOfBounds);
87 }
88 Ok(())
89 }
90
91 #[allow(clippy::all)]
92 #[inline(never)]
93 #[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
94 pub(crate) fn internal_read(
95 &mut self,
96 offset: u32,
97 bytes: &mut [MaybeUninit<u8>],
98 ) -> Result<(), FlashStorageError> {
99 check_rc(chip_specific::spiflash_read(
100 offset,
101 bytes.as_mut_ptr() as *mut u32,
102 bytes.len() as u32,
103 ))
104 }
105
106 #[inline(always)]
107 fn unlock_once(&mut self) -> Result<(), FlashStorageError> {
108 if !self.unlocked {
109 if chip_specific::spiflash_unlock() != 0 {
110 return Err(FlashStorageError::CantUnlock);
111 }
112 self.unlocked = true;
113 }
114 Ok(())
115 }
116
117 #[inline(never)]
118 #[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
119 pub(crate) fn internal_erase(&mut self, sector: u32) -> Result<(), FlashStorageError> {
120 self.unlock_once()?;
121
122 check_rc(chip_specific::spiflash_erase_sector(sector))
123 }
124
125 #[inline(never)]
126 #[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
127 pub(crate) fn internal_write(
128 &mut self,
129 offset: u32,
130 bytes: &[u8],
131 ) -> Result<(), FlashStorageError> {
132 self.unlock_once()?;
133
134 check_rc(chip_specific::spiflash_write(
135 offset,
136 bytes.as_ptr() as *const u32,
137 bytes.len() as u32,
138 ))
139 }
140}