Skip to main content

esp_idf_svc/
partition.rs

1//! ESP IDF partitions API
2//!
3//! This API provides access to the partitions in the ESP32 flash memory - with operations for reading, writing, and erasing.
4//! The ESP-IDF Wear-Leveling algorithm is also supported.
5//!
6//! Note that ESP-IDF partitions are not created or dropped by this API - they always pre-existing and the API provides access to them.
7//! To define your partitions, you need to use the ESP-IDF partition table CSV file, as described here:
8//! <https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html>
9
10use core::{borrow::BorrowMut, ffi::CStr};
11
12use esp_idf_hal::sys::*;
13
14use crate::handle::RawHandle;
15
16#[cfg(feature = "embedded-storage")]
17pub use embedded_storage::{EspEncrypted, EspFlashError};
18
19/// The type of a partition
20#[non_exhaustive]
21#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
22pub enum EspPartitionType {
23    /// Application partition
24    App(EspAppPartitionSubtype),
25    /// Data partition
26    Data(EspDataPartitionSubtype),
27    /// Unknown partition type
28    Unknown,
29}
30
31impl EspPartitionType {
32    const fn raw(&self) -> (u32, u32) {
33        match self {
34            EspPartitionType::App(subtype) => {
35                let subtype = match subtype {
36                    EspAppPartitionSubtype::Factory => {
37                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_FACTORY
38                    }
39                    EspAppPartitionSubtype::Test => {
40                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_TEST
41                    }
42                    EspAppPartitionSubtype::Ota(subtype) => {
43                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_OTA_MIN + *subtype as u32
44                    }
45                    EspAppPartitionSubtype::Unknown => {
46                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_ANY
47                    }
48                };
49
50                (esp_partition_type_t_ESP_PARTITION_TYPE_APP, subtype)
51            }
52            EspPartitionType::Data(subtype) => {
53                let subtype = match subtype {
54                    EspDataPartitionSubtype::Ota => {
55                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_OTA
56                    }
57                    EspDataPartitionSubtype::Phy => {
58                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_PHY
59                    }
60                    EspDataPartitionSubtype::Nvs => {
61                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_NVS
62                    }
63                    EspDataPartitionSubtype::Coredump => {
64                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_COREDUMP
65                    }
66                    EspDataPartitionSubtype::NvsKeys => {
67                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS
68                    }
69                    EspDataPartitionSubtype::Efuse => {
70                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM
71                    }
72                    EspDataPartitionSubtype::Undefined => {
73                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_UNDEFINED
74                    }
75                    EspDataPartitionSubtype::EspHttpd => {
76                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD
77                    }
78                    EspDataPartitionSubtype::Fat => {
79                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_FAT
80                    }
81                    EspDataPartitionSubtype::Spiffs => {
82                        esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_SPIFFS
83                    }
84                    // Note: only available in the latest patch releases
85                    // #[cfg(not(esp_idf_version_major = "4"))]
86                    // EspDataPartitionSubtype::LittleFs => {
87                    //     esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_LITTLEFS
88                    // }
89                    _ => esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_ANY,
90                };
91
92                (esp_partition_type_t_ESP_PARTITION_TYPE_DATA, subtype)
93            }
94            EspPartitionType::Unknown => (
95                esp_partition_type_t_ESP_PARTITION_TYPE_ANY,
96                esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_ANY,
97            ),
98        }
99    }
100}
101/// The subtype of an application partition
102#[non_exhaustive]
103#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
104pub enum EspAppPartitionSubtype {
105    /// Factory partition
106    Factory,
107    /// Test partition
108    Test,
109    /// OTA partition
110    Ota(u8),
111    /// Unknown app partition subtype
112    Unknown,
113}
114
115/// The subtype of a data partition
116#[non_exhaustive]
117#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
118pub enum EspDataPartitionSubtype {
119    /// OTA data partition
120    Ota,
121    /// PHY data partition
122    Phy,
123    /// NVS data partition
124    Nvs,
125    /// Core dump data partition
126    Coredump,
127    /// NVS keys data partition (for encryption)
128    NvsKeys,
129    /// EFUSE data partition
130    Efuse,
131    /// Undefined data partition
132    Undefined,
133    /// ESPHTTPD data partition
134    EspHttpd,
135    /// FAT FS partition
136    Fat,
137    /// SPIFFS partition
138    Spiffs,
139    // /// LittleFS partition
140    // LittleFs,
141    /// Unknown data partition subtype
142    Unknown,
143}
144
145/// The type of memory mapping
146#[cfg(not(esp_idf_version_major = "4"))]
147#[non_exhaustive]
148#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
149pub enum EspMemMapType {
150    /// Data
151    Data,
152    /// Instruction (code)
153    Instruction,
154}
155
156/// Represents a memory-mapping of a partition region
157///
158/// Drop this to unmap the memory region
159#[cfg(not(esp_idf_version_major = "4"))]
160pub struct EspMemMappedPartition<'a> {
161    handle: esp_partition_mmap_handle_t,
162    start: usize,
163    _t: core::marker::PhantomData<&'a mut ()>,
164}
165
166#[cfg(not(esp_idf_version_major = "4"))]
167impl EspMemMappedPartition<'_> {
168    /// Returns the start address of the memory-mapped region
169    pub const fn start(&self) -> usize {
170        self.start
171    }
172}
173
174#[cfg(not(esp_idf_version_major = "4"))]
175impl Drop for EspMemMappedPartition<'_> {
176    fn drop(&mut self) {
177        unsafe {
178            esp_partition_munmap(self.handle);
179        }
180    }
181}
182
183/// An iterator over the partitions in the ESP32 flash memory
184pub struct EspPartitionIterator {
185    raw_iter: esp_partition_iterator_t,
186}
187
188impl EspPartitionIterator {
189    /// Create a new partition iterator
190    ///
191    /// # Arguments
192    /// - `partition_type`: The type of partitions to iterate over
193    ///
194    /// # Safety
195    /// Only one partition iterator should be created at a time
196    pub unsafe fn new(partition_type: Option<EspPartitionType>) -> Result<Self, EspError> {
197        let (partition_type, partition_subtype) = partition_type
198            .map(|partition_type| partition_type.raw())
199            .unwrap_or((
200                esp_partition_type_t_ESP_PARTITION_TYPE_ANY,
201                esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_ANY,
202            ));
203
204        let raw_iter = esp_partition_find(partition_type, partition_subtype, core::ptr::null());
205
206        Ok(Self { raw_iter })
207    }
208
209    /// Return the next partition in the iterator
210    pub fn next_partition(&mut self) -> Option<EspPartition> {
211        if self.raw_iter.is_null() {
212            return None;
213        }
214
215        let partition = unsafe { esp_partition_get(self.raw_iter) };
216
217        let value = if partition.is_null() {
218            None
219        } else {
220            Some(unsafe { EspPartition::wrap(partition) })
221        };
222
223        self.raw_iter = unsafe { esp_partition_next(self.raw_iter) };
224
225        value
226    }
227}
228
229impl Drop for EspPartitionIterator {
230    fn drop(&mut self) {
231        unsafe {
232            esp_partition_iterator_release(self.raw_iter);
233        }
234    }
235}
236
237impl Iterator for EspPartitionIterator {
238    type Item = EspPartition;
239
240    fn next(&mut self) -> Option<Self::Item> {
241        self.next_partition()
242    }
243}
244
245/// Represents a partition in the ESP32 flash memory
246#[repr(transparent)]
247pub struct EspPartition(*const esp_partition_t);
248
249impl EspPartition {
250    /// Wrap a raw pointer into an `EspPartition` instance
251    ///
252    /// # Safety
253    /// The raw pointer should be a valid one
254    /// It should not be shared in multiple `EspPartition` instances
255    pub unsafe fn wrap(partition: *const esp_partition_t) -> Self {
256        Self(partition)
257    }
258
259    /// Create a new `EspPartition` instance for an existing partition identified by its label
260    ///
261    /// # Arguments
262    /// - `label`: The label of the partition
263    ///
264    /// Return `None` if the partition with the label does not exist
265    /// or `Some` with the partition if it exists.
266    ///
267    /// # Safety
268    /// Only a single partition should be active at any point in time for that label.
269    #[cfg(feature = "alloc")]
270    pub unsafe fn new(label: &str) -> Result<Option<Self>, EspError> {
271        let cstr = crate::private::cstr::to_cstring_arg(label)?;
272
273        Self::cnew(&cstr)
274    }
275
276    /// Create a new `EspPartition` instance for an existing partition identified by its C-string label
277    ///
278    /// # Arguments
279    /// - `clabel`: The label of the partition as a C string
280    ///
281    /// Return `None` if the partition with the label does not exist
282    /// or `Some` with the partition if it exists.
283    ///
284    /// # Safety
285    /// Only a single partition should be active at any point in time for that label.
286    pub unsafe fn cnew(clabel: &CStr) -> Result<Option<Self>, EspError> {
287        let partition = esp_partition_find_first(
288            esp_partition_type_t_ESP_PARTITION_TYPE_ANY,
289            esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_ANY,
290            clabel.as_ptr(),
291        );
292
293        if partition.is_null() {
294            Ok(None)
295        } else {
296            Ok(Some(Self(partition)))
297        }
298    }
299
300    /// Find and return the first partition of a specific type
301    ///
302    /// # Arguments
303    /// - `partition_type`: The type of the partition to find
304    ///
305    /// Return `None` if a partition of the specified type does not exist
306    /// or `Some` with the first partition of the specified type if it exists.
307    ///
308    /// # Safety
309    /// User should not end up with two `EspPartition` instances representing the same ESP IDF partition.
310    pub unsafe fn find_first(partition_type: EspPartitionType) -> Result<Option<Self>, EspError> {
311        let (partition_type, partition_subtype) = partition_type.raw();
312
313        let partition =
314            esp_partition_find_first(partition_type, partition_subtype, core::ptr::null());
315
316        if partition.is_null() {
317            Ok(None)
318        } else {
319            Ok(Some(Self(partition)))
320        }
321    }
322
323    /// Return the label of the partition as a C string
324    pub fn clabel(&self) -> &CStr {
325        unsafe { CStr::from_ptr((*self.0).label.as_ptr()) }
326    }
327
328    /// Return the label of the partition
329    pub fn label(&self) -> &str {
330        self.clabel().to_str().unwrap()
331    }
332
333    /// Return the type of the partition
334    #[allow(non_upper_case_globals)]
335    pub fn partition_type(&self) -> EspPartitionType {
336        match unsafe { (*self.0).type_ } {
337            esp_partition_type_t_ESP_PARTITION_TYPE_APP => {
338                EspPartitionType::App(match unsafe { (*self.0).subtype } {
339                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_FACTORY => {
340                        EspAppPartitionSubtype::Factory
341                    }
342                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_TEST => {
343                        EspAppPartitionSubtype::Test
344                    }
345                    other => {
346                        if (esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_OTA_MIN
347                            ..=esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_OTA_MAX)
348                            .contains(&other)
349                        {
350                            EspAppPartitionSubtype::Ota(
351                                (other - esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_APP_OTA_MIN)
352                                    as _,
353                            )
354                        } else {
355                            EspAppPartitionSubtype::Unknown
356                        }
357                    }
358                })
359            }
360            esp_partition_type_t_ESP_PARTITION_TYPE_DATA => {
361                EspPartitionType::Data(match unsafe { (*self.0).subtype } {
362                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_OTA => {
363                        EspDataPartitionSubtype::Ota
364                    }
365                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_PHY => {
366                        EspDataPartitionSubtype::Phy
367                    }
368                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_NVS => {
369                        EspDataPartitionSubtype::Nvs
370                    }
371                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_COREDUMP => {
372                        EspDataPartitionSubtype::Coredump
373                    }
374                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS => {
375                        EspDataPartitionSubtype::NvsKeys
376                    }
377                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM => {
378                        EspDataPartitionSubtype::Efuse
379                    }
380                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_UNDEFINED => {
381                        EspDataPartitionSubtype::Undefined
382                    }
383                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD => {
384                        EspDataPartitionSubtype::EspHttpd
385                    }
386                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_FAT => {
387                        EspDataPartitionSubtype::Fat
388                    }
389                    esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_SPIFFS => {
390                        EspDataPartitionSubtype::Spiffs
391                    }
392                    // #[cfg(not(esp_idf_version_major = "4"))]
393                    // esp_partition_subtype_t_ESP_PARTITION_SUBTYPE_DATA_LITTLEFS => {
394                    //     EspDataPartitionSubtype::LittleFs
395                    // }
396                    _ => EspDataPartitionSubtype::Unknown,
397                })
398            }
399            _ => EspPartitionType::Unknown,
400        }
401    }
402
403    /// Return the address/offset of the partition in the flash storage
404    pub fn address(&self) -> usize {
405        unsafe { (*self.0).address as _ }
406    }
407
408    /// Return the size of the partition in bytes in the flash storage
409    pub fn size(&self) -> usize {
410        unsafe { (*self.0).size as _ }
411    }
412
413    /// Return the erase size block of the partition in bytes
414    #[cfg(not(esp_idf_version_major = "4"))]
415    pub fn erase_size(&self) -> usize {
416        unsafe { (*self.0).erase_size as _ }
417    }
418
419    /// Return `true` if the partition is encrypted
420    pub fn encrypted(&self) -> bool {
421        unsafe { (*self.0).encrypted }
422    }
423
424    /// Return `true` if the partition is read-only
425    #[cfg(any(
426        all(not(esp_idf_version_major = "4"), not(esp_idf_version_major = "5")),
427        all(
428            esp_idf_version_major = "5",
429            not(esp_idf_version_minor = "0"),
430            not(esp_idf_version_minor = "1"),
431        )
432    ))]
433    pub fn readonly(&self) -> bool {
434        unsafe { (*self.0).readonly }
435    }
436
437    /// Read data from the partition, performing decryption of the
438    /// data if the partition is encrypted.
439    ///
440    /// # Arguments
441    /// - `offset`: The offset in the partition to read from, in bytes
442    /// - `buf`: The buffer to read the data into
443    ///
444    /// Return an error if the read operation failed.
445    /// The read operation would fail if the offset and buffer length are
446    /// beyond the partition bounds.
447    ///
448    /// The read operation will also fail if the offset and the buffer length
449    /// are not aligned with the partition read alignment.
450    pub fn read(&mut self, offset: usize, buf: &mut [u8]) -> Result<(), EspError> {
451        esp!(unsafe {
452            esp_partition_read(self.0, offset as _, buf.as_ptr() as *mut _, buf.len() as _)
453        })
454    }
455
456    /// Write data to the partition, performing encryption of the
457    /// data if the partition is encrypted.
458    ///
459    /// # Arguments
460    /// - `offset`: The offset in the partition to write to, in bytes
461    /// - `data`: The data to write to the partition
462    ///
463    /// Return an error if the write operation failed.
464    /// The write operation would fail if the offset and data length are
465    /// beyond the partition bounds.
466    ///
467    /// The write operation will also fail if the offset and the data length
468    /// are not aligned with the partition write alignment.
469    pub fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), EspError> {
470        esp!(unsafe {
471            esp_partition_write(
472                self.0,
473                offset as _,
474                data.as_ptr() as *const _,
475                data.len() as _,
476            )
477        })
478    }
479
480    /// Erase a region of the partition
481    ///
482    /// # Arguments
483    /// - `offset`: The offset in the partition to start erasing from, in bytes
484    /// - `size`: The size of the region to erase
485    ///
486    /// Return an error if the erase operation failed.
487    /// The erase operation would fail if the offset and size are
488    /// beyond the partition bounds.
489    ///
490    /// The erase operation will also fail if the offset and the size
491    /// are not aligned with the partition erase block returned by `erase_size`.
492    pub fn erase(&mut self, offset: usize, size: usize) -> Result<(), EspError> {
493        esp!(unsafe { esp_partition_erase_range(self.0, offset as _, size as _) })
494    }
495
496    /// Read data from the partition without performing decryption
497    ///
498    /// Identical to `read` if the partition is not encrypted.
499    pub fn read_raw(&mut self, offset: usize, buf: &mut [u8]) -> Result<(), EspError> {
500        esp!(unsafe {
501            esp_partition_read_raw(self.0, offset as _, buf.as_ptr() as *mut _, buf.len() as _)
502        })
503    }
504
505    /// Write data to the partition without performing encryption
506    ///
507    /// Identical to `write` if the partition is not encrypted.
508    pub fn write_raw(&mut self, offset: usize, data: &[u8]) -> Result<(), EspError> {
509        esp!(unsafe {
510            esp_partition_write_raw(
511                self.0,
512                offset as _,
513                data.as_ptr() as *const _,
514                data.len() as _,
515            )
516        })
517    }
518
519    /// Map a region of the partition to memory
520    ///
521    /// # Arguments
522    /// - `offset`: The offset in the partition to map from, in bytes
523    /// - `size`: The size of the region to map, in bytes
524    /// - `mmap_type`: The type of memory mapping
525    ///
526    /// Return an error if the memory mapping operation failed.
527    ///
528    /// # Safety
529    /// TBD
530    #[cfg(not(esp_idf_version_major = "4"))]
531    pub unsafe fn mmap(
532        &mut self,
533        offset: usize,
534        size: usize,
535        mmap_type: EspMemMapType,
536    ) -> Result<EspMemMappedPartition<'_>, EspError> {
537        let mut handle: esp_partition_mmap_handle_t = Default::default();
538        let mut out: *const core::ffi::c_void = core::ptr::null_mut();
539
540        esp!(esp_partition_mmap(
541            self.0,
542            offset as _,
543            size as _,
544            mmap_type as _,
545            &mut out,
546            &mut handle
547        ))?;
548
549        Ok(EspMemMappedPartition {
550            handle,
551            start: out as _,
552            _t: core::marker::PhantomData,
553        })
554    }
555}
556
557impl RawHandle for EspPartition {
558    type Handle = *const esp_partition_t;
559
560    fn handle(&self) -> Self::Handle {
561        self.0
562    }
563}
564
565unsafe impl Send for EspPartition {}
566
567/// Represents a partition wrapped with the ESP-IDF Wear-Leveling algorithm
568pub struct EspWlPartition<T> {
569    _partition: T,
570    handle: wl_handle_t,
571}
572
573impl<T> EspWlPartition<T>
574where
575    T: BorrowMut<EspPartition>,
576{
577    /// Wrap the provided raw partition with the ESP-IDF Wear-Leveling algorithm
578    ///
579    /// Return an error if the wrap operation failed, or the WL partition
580    /// if the operation succeeded.
581    ///
582    /// Arguments:
583    /// - `partition`: The partition to mount
584    pub fn new(mut partition: T) -> Result<Self, EspError> {
585        let mut handle: wl_handle_t = Default::default();
586
587        esp!(unsafe { wl_mount(partition.borrow_mut().0, &mut handle) })?;
588
589        Ok(Self {
590            _partition: partition,
591            handle,
592        })
593    }
594
595    /// Return the size of the mounted WL partition
596    pub fn size(&self) -> usize {
597        unsafe { wl_size(self.handle) as _ }
598    }
599
600    /// Return the size of a sector in the mounted WL partition
601    pub fn sector_size(&self) -> usize {
602        unsafe { wl_sector_size(self.handle) as _ }
603    }
604
605    /// Read data from the mounted WL partition
606    ///
607    /// # Arguments
608    /// - `offset`: The offset in the partition to read from, in bytes
609    /// - `buf`: The buffer to read the data into
610    ///
611    /// Return an error if the read operation failed.
612    /// The read operation would fail if the offset and buffer length are
613    /// beyond the partition bounds.
614    ///
615    /// The read operation will also fail if the offset and the buffer length
616    /// are not aligned with the partition read alignment.
617    pub fn read(&mut self, offset: usize, buf: &mut [u8]) -> Result<(), EspError> {
618        esp!(unsafe {
619            wl_read(
620                self.handle,
621                offset as _,
622                buf.as_ptr() as *mut _,
623                buf.len() as _,
624            )
625        })
626    }
627
628    /// Write data to the mounted WL partition
629    ///
630    /// # Arguments
631    /// - `offset`: The offset in the partition to write to, in bytes
632    /// - `data`: The data to write to the partition
633    ///
634    /// Return an error if the write operation failed.
635    /// The write operation would fail if the offset and data length are
636    /// beyond the partition bounds.
637    ///
638    /// The write operation will also fail if the offset and the data length
639    /// are not aligned with the partition write alignment.
640    pub fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), EspError> {
641        esp!(unsafe {
642            wl_write(
643                self.handle,
644                offset as _,
645                data.as_ptr() as *const _,
646                data.len() as _,
647            )
648        })
649    }
650
651    /// Erase a region of the mounted WL partition
652    ///
653    /// # Arguments
654    /// - `offset`: The offset in the partition to start erasing from, in bytes
655    /// - `size`: The size of the region to erase, in bytes
656    ///
657    /// Return an error if the erase operation failed.
658    /// The erase operation would fail if the offset and size are
659    /// beyond the partition bounds.
660    pub fn erase(&mut self, offset: usize, size: usize) -> Result<(), EspError> {
661        esp!(unsafe { wl_erase_range(self.handle, offset as _, size as _) })
662    }
663}
664
665impl<T> RawHandle for EspWlPartition<T> {
666    type Handle = wl_handle_t;
667
668    fn handle(&self) -> Self::Handle {
669        self.handle
670    }
671}
672
673impl<T> Drop for EspWlPartition<T> {
674    fn drop(&mut self) {
675        esp!(unsafe { wl_unmount(self.handle) }).unwrap();
676    }
677}
678
679unsafe impl<T> Send for EspWlPartition<T> where T: Send {}
680
681#[cfg(feature = "embedded-storage")]
682mod embedded_storage {
683    use core::borrow::BorrowMut;
684    use core::fmt;
685
686    use embedded_storage::nor_flash::{
687        ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
688    };
689    use embedded_storage::ReadStorage;
690
691    use esp_idf_hal::sys::{EspError, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_SIZE};
692
693    use super::{EspPartition, EspWlPartition};
694
695    impl ReadStorage for EspPartition {
696        type Error = EspError;
697
698        fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
699            EspPartition::read(self, offset as _, buf)
700        }
701
702        fn capacity(&self) -> usize {
703            self.size()
704        }
705    }
706
707    impl ErrorType for EspPartition {
708        type Error = EspFlashError;
709    }
710
711    impl ReadNorFlash for EspPartition {
712        const READ_SIZE: usize = 1;
713
714        fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
715            EspPartition::read(self, offset as _, buf)?;
716
717            Ok(())
718        }
719
720        fn capacity(&self) -> usize {
721            self.size()
722        }
723    }
724
725    impl NorFlash for EspPartition {
726        const WRITE_SIZE: usize = 1;
727        const ERASE_SIZE: usize = 4096;
728
729        fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
730            if to < from {
731                Err(EspFlashError(EspError::from_infallible::<
732                    ESP_ERR_INVALID_SIZE,
733                >()))?;
734            }
735
736            EspPartition::erase(self, from as _, (to - from) as _)?;
737
738            Ok(())
739        }
740
741        fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
742            EspPartition::write(self, offset as _, bytes)?;
743
744            Ok(())
745        }
746    }
747
748    impl MultiwriteNorFlash for EspPartition {}
749
750    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
751    pub struct EspFlashError(pub EspError);
752
753    impl From<EspError> for EspFlashError {
754        fn from(e: EspError) -> Self {
755            Self(e)
756        }
757    }
758
759    impl fmt::Display for EspFlashError {
760        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
761            self.0.fmt(f)
762        }
763    }
764
765    #[cfg(feature = "std")]
766    impl std::error::Error for EspFlashError {}
767
768    impl NorFlashError for EspFlashError {
769        fn kind(&self) -> NorFlashErrorKind {
770            match self.0.code() as _ {
771                ESP_ERR_INVALID_ARG => NorFlashErrorKind::NotAligned,
772                ESP_ERR_INVALID_SIZE => NorFlashErrorKind::OutOfBounds,
773                _ => NorFlashErrorKind::Other,
774            }
775        }
776    }
777
778    impl<T> ReadStorage for EspWlPartition<T>
779    where
780        T: BorrowMut<EspPartition>,
781    {
782        type Error = EspError;
783
784        fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
785            EspWlPartition::read(self, offset as _, buf)
786        }
787
788        fn capacity(&self) -> usize {
789            self.size()
790        }
791    }
792
793    impl<T> ErrorType for EspWlPartition<T> {
794        type Error = EspFlashError;
795    }
796
797    impl<T> ReadNorFlash for EspWlPartition<T>
798    where
799        T: BorrowMut<EspPartition>,
800    {
801        const READ_SIZE: usize = 1;
802
803        fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
804            EspWlPartition::read(self, offset as _, buf)?;
805
806            Ok(())
807        }
808
809        fn capacity(&self) -> usize {
810            self.size()
811        }
812    }
813
814    impl<T> NorFlash for EspWlPartition<T>
815    where
816        T: BorrowMut<EspPartition>,
817    {
818        const WRITE_SIZE: usize = 1;
819        const ERASE_SIZE: usize = 4096;
820
821        fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
822            if to < from {
823                Err(EspFlashError(EspError::from_infallible::<
824                    ESP_ERR_INVALID_SIZE,
825                >()))?;
826            }
827
828            EspWlPartition::erase(self, from as _, (to - from) as _)?;
829
830            Ok(())
831        }
832
833        fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
834            EspWlPartition::write(self, offset as _, bytes)?;
835
836            Ok(())
837        }
838    }
839
840    impl<T> MultiwriteNorFlash for EspWlPartition<T> where T: BorrowMut<EspPartition> {}
841
842    /// A wrapper marker type for encrypted partitions
843    ///
844    /// The reason why it is necessary is because for encrypted partitions
845    /// the write size is 16 bytes, while for non-encrypted partitions
846    /// the write size is 1 byte.
847    pub struct EspEncrypted<T>(T);
848
849    impl<T> EspEncrypted<T> {
850        /// Wrap the provided partition with the encrypted marker
851        pub const fn new(partition: T) -> Self {
852            Self(partition)
853        }
854
855        /// Release the partition from the encrypted marker
856        pub fn release(self) -> T {
857            self.0
858        }
859    }
860
861    impl<T> ErrorType for EspEncrypted<T>
862    where
863        T: ErrorType,
864    {
865        type Error = T::Error;
866    }
867
868    impl<T> ReadStorage for EspEncrypted<T>
869    where
870        T: ReadStorage,
871    {
872        type Error = T::Error;
873
874        fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
875            self.0.read(offset, buf)
876        }
877
878        fn capacity(&self) -> usize {
879            self.0.capacity()
880        }
881    }
882
883    impl<T> ReadNorFlash for EspEncrypted<T>
884    where
885        T: ReadNorFlash,
886    {
887        const READ_SIZE: usize = T::READ_SIZE;
888
889        fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
890            self.0.read(offset, buf)
891        }
892
893        fn capacity(&self) -> usize {
894            self.0.capacity()
895        }
896    }
897
898    impl<T> NorFlash for EspEncrypted<T>
899    where
900        T: NorFlash,
901    {
902        // Because the partition is encrypted
903        // See https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/partition.html#_CPPv419esp_partition_writePK15esp_partition_t6size_tPKv6size_t
904        const WRITE_SIZE: usize = 16;
905
906        const ERASE_SIZE: usize = T::ERASE_SIZE;
907
908        fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
909            self.0.erase(from, to)
910        }
911
912        fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
913            self.0.write(offset, bytes)
914        }
915    }
916}