esp_storage/
common.rs

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}