esp_storage/
common.rs

1use core::ops::{Deref, DerefMut};
2
3use crate::chip_specific;
4
5#[repr(C, align(4))]
6pub struct FlashSectorBuffer {
7    // NOTE: Ensure that no unaligned fields are added above `data` to maintain its required
8    // alignment
9    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}