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