esp_hal/soc/
mod.rs

1use core::ops::Range;
2
3use portable_atomic::{AtomicU8, Ordering};
4
5pub use self::implementation::*;
6
7#[cfg_attr(esp32, path = "esp32/mod.rs")]
8#[cfg_attr(esp32c2, path = "esp32c2/mod.rs")]
9#[cfg_attr(esp32c3, path = "esp32c3/mod.rs")]
10#[cfg_attr(esp32c6, path = "esp32c6/mod.rs")]
11#[cfg_attr(esp32h2, path = "esp32h2/mod.rs")]
12#[cfg_attr(esp32s2, path = "esp32s2/mod.rs")]
13#[cfg_attr(esp32s3, path = "esp32s3/mod.rs")]
14mod implementation;
15
16mod efuse_field;
17
18#[cfg(feature = "psram")]
19mod psram_common;
20
21// Using static mut should be fine since we are only writing to it once during
22// initialization. As other tasks and interrupts are not running yet, the worst
23// that can happen is, that the user creates a DMA buffer before initializing
24// the HAL. This will access the PSRAM range, returning an empty range - which
25// is, at that point, true. The user has no (safe) means to allocate in PSRAM
26// before initializing the HAL.
27#[cfg(feature = "psram")]
28static mut MAPPED_PSRAM: MappedPsram = MappedPsram { memory_range: 0..0 };
29
30pub(crate) fn psram_range() -> Range<usize> {
31    cfg_if::cfg_if! {
32        if #[cfg(feature = "psram")] {
33            #[allow(static_mut_refs)]
34            unsafe { MAPPED_PSRAM.memory_range.clone() }
35        } else {
36            0..0
37        }
38    }
39}
40
41/// The lower bound of the system's DRAM (Data RAM) address space.
42const SOC_DRAM_LOW: usize = esp_metadata::memory_region_start!("DRAM");
43
44/// The upper bound of the system's DRAM (Data RAM) address space.
45const SOC_DRAM_HIGH: usize = esp_metadata::memory_region_end!("DRAM");
46
47const DRAM: Range<usize> = SOC_DRAM_LOW..SOC_DRAM_HIGH;
48
49#[cfg(feature = "psram")]
50pub struct MappedPsram {
51    memory_range: Range<usize>,
52}
53
54// Indicates the state of setting the mac address
55// 0 -- unset
56// 1 -- in the process of being set
57// 2 -- set
58//
59// Values other than 0 indicate that we cannot attempt setting the mac address
60// again, and values other than 2 indicate that we should read the mac address
61// from eFuse.
62#[cfg_attr(not(feature = "unstable"), allow(unused))]
63static MAC_OVERRIDE_STATE: AtomicU8 = AtomicU8::new(0);
64#[cfg_attr(not(feature = "unstable"), allow(unused))]
65static mut MAC_OVERRIDE: [u8; 6] = [0; 6];
66
67/// Error indicating issues with setting the MAC address.
68#[derive(PartialEq, Eq, Copy, Clone, Debug)]
69#[cfg_attr(not(feature = "unstable"), allow(unused))]
70pub enum SetMacError {
71    /// The MAC address has already been set and cannot be changed.
72    AlreadySet,
73}
74
75#[cfg_attr(not(feature = "unstable"), allow(unused))]
76impl self::efuse::Efuse {
77    /// Set the base mac address
78    ///
79    /// The new value will be returned by `read_mac_address` instead of the one
80    /// hard-coded in eFuse. This does not persist across device resets.
81    ///
82    /// Can only be called once. Returns `Err(SetMacError::AlreadySet)`
83    /// otherwise.
84    pub fn set_mac_address(mac: [u8; 6]) -> Result<(), SetMacError> {
85        if MAC_OVERRIDE_STATE
86            .compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed)
87            .is_err()
88        {
89            return Err(SetMacError::AlreadySet);
90        }
91
92        unsafe {
93            MAC_OVERRIDE = mac;
94        }
95
96        MAC_OVERRIDE_STATE.store(2, Ordering::Relaxed);
97
98        Ok(())
99    }
100
101    /// Get base mac address
102    ///
103    /// By default this reads the base mac address from eFuse, but it can be
104    /// overridden by `set_mac_address`.
105    pub fn mac_address() -> [u8; 6] {
106        if MAC_OVERRIDE_STATE.load(Ordering::Relaxed) == 2 {
107            unsafe { MAC_OVERRIDE }
108        } else {
109            Self::read_base_mac_address()
110        }
111    }
112}
113
114#[allow(unused)]
115pub(crate) fn is_valid_ram_address(address: usize) -> bool {
116    addr_in_range(address, DRAM)
117}
118
119#[allow(unused)]
120pub(crate) fn is_slice_in_dram<T>(slice: &[T]) -> bool {
121    slice_in_range(slice, DRAM)
122}
123
124#[allow(unused)]
125pub(crate) fn is_valid_psram_address(address: usize) -> bool {
126    addr_in_range(address, psram_range())
127}
128
129#[allow(unused)]
130pub(crate) fn is_slice_in_psram<T>(slice: &[T]) -> bool {
131    slice_in_range(slice, psram_range())
132}
133
134#[allow(unused)]
135pub(crate) fn is_valid_memory_address(address: usize) -> bool {
136    is_valid_ram_address(address) || is_valid_psram_address(address)
137}
138
139fn slice_in_range<T>(slice: &[T], range: Range<usize>) -> bool {
140    let slice = slice.as_ptr_range();
141    let start = slice.start as usize;
142    let end = slice.end as usize;
143    // `end` is >= `start`, so we don't need to check that `end > range.start`
144    // `end` is also one past the last element, so it can be equal to the range's
145    // end which is also one past the memory region's last valid address.
146    addr_in_range(start, range.clone()) && end <= range.end
147}
148
149pub(crate) fn addr_in_range(addr: usize, range: Range<usize>) -> bool {
150    range.contains(&addr)
151}