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 #[inline(always)]
91 pub(crate) fn check_alignment<const ALIGN: u32>(
92 &self,
93 offset: u32,
94 length: usize,
95 ) -> Result<(), FlashStorageError> {
96 let offset = offset as usize;
97 if offset % ALIGN as usize != 0 || length % ALIGN as usize != 0 {
98 return Err(FlashStorageError::NotAligned);
99 }
100 Ok(())
101 }
102
103 #[inline(always)]
104 pub(crate) fn check_bounds(&self, offset: u32, length: usize) -> Result<(), FlashStorageError> {
105 let offset = offset as usize;
106 if length > self.capacity || offset > self.capacity - length {
107 return Err(FlashStorageError::OutOfBounds);
108 }
109 Ok(())
110 }
111
112 #[allow(clippy::all)]
113 #[inline(never)]
114 #[unsafe(link_section = ".rwtext")]
115 pub(crate) fn internal_read(
116 &mut self,
117 offset: u32,
118 bytes: &mut [u8],
119 ) -> Result<(), FlashStorageError> {
120 check_rc(chip_specific::spiflash_read(
121 offset,
122 bytes.as_ptr() as *mut u32,
123 bytes.len() as u32,
124 ))
125 }
126
127 #[inline(always)]
128 fn unlock_once(&mut self) -> Result<(), FlashStorageError> {
129 if !self.unlocked {
130 if chip_specific::spiflash_unlock() != 0 {
131 return Err(FlashStorageError::CantUnlock);
132 }
133 self.unlocked = true;
134 }
135 Ok(())
136 }
137
138 #[inline(never)]
139 #[unsafe(link_section = ".rwtext")]
140 pub(crate) fn internal_erase(&mut self, sector: u32) -> Result<(), FlashStorageError> {
141 self.unlock_once()?;
142
143 check_rc(chip_specific::spiflash_erase_sector(sector))
144 }
145
146 #[inline(never)]
147 #[unsafe(link_section = ".rwtext")]
148 pub(crate) fn internal_write(
149 &mut self,
150 offset: u32,
151 bytes: &[u8],
152 ) -> Result<(), FlashStorageError> {
153 self.unlock_once()?;
154
155 check_rc(chip_specific::spiflash_write(
156 offset,
157 bytes.as_ptr() as *const u32,
158 bytes.len() as u32,
159 ))
160 }
161}