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    #[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}