1
#![cfg_attr(docsrs, procmacros::doc_replace(
2
    "interface_mac" => {
3
        cfg(soc_has_wifi) => "let mac = efuse::interface_mac_address(InterfaceMacAddress::Station);",
4
        _ => "let mac = efuse::interface_mac_address(InterfaceMacAddress::Bluetooth);"
5
    }
6
))]
7
//! # eFuse (one-time programmable configuration)
8
//!
9
//! ## Overview
10
//!
11
//! The `efuse` module provides functionality for reading eFuse data
12
//! from the chip, allowing access to various chip-specific
13
//! information such as:
14
//!
15
//!   * MAC address
16
//!   * Chip revision
17
//!
18
//! and more. It is useful for retrieving chip-specific configuration and
19
//! identification data during runtime.
20
#![cfg_attr(
21
    any(soc_has_wifi, soc_has_bt),
22
    doc = r#"
23

            
24
## Examples
25

            
26
### Reading interface MAC addresses
27

            
28
```rust, no_run
29
# {before_snippet}
30
use esp_hal::efuse::{self, InterfaceMacAddress};
31

            
32
# {interface_mac}
33
println!("MAC: {mac}");
34
println!("MAC bytes: {:02x?}", mac.as_bytes());
35
# {after_snippet}
36
```
37
"#
38
)]
39
use core::{cmp, mem, slice, sync::atomic::Ordering};
40

            
41
use bytemuck::AnyBitPattern;
42
use portable_atomic::AtomicU8;
43

            
44
#[cfg_attr(esp32, path = "esp32/mod.rs")]
45
#[cfg_attr(esp32c2, path = "esp32c2/mod.rs")]
46
#[cfg_attr(esp32c3, path = "esp32c3/mod.rs")]
47
#[cfg_attr(esp32c5, path = "esp32c5/mod.rs")]
48
#[cfg_attr(esp32c6, path = "esp32c6/mod.rs")]
49
#[cfg_attr(esp32c61, path = "esp32c61/mod.rs")]
50
#[cfg_attr(esp32h2, path = "esp32h2/mod.rs")]
51
#[cfg_attr(esp32h4, path = "esp32h4/mod.rs")]
52
#[cfg_attr(esp32p4, path = "esp32p4/mod.rs")]
53
#[cfg_attr(esp32s2, path = "esp32s2/mod.rs")]
54
#[cfg_attr(esp32s3, path = "esp32s3/mod.rs")]
55
#[cfg_attr(esp32s31, path = "esp32s31/mod.rs")]
56
pub(crate) mod implem;
57

            
58
#[instability::unstable]
59
pub use implem::*;
60
use procmacros::doc_replace;
61

            
62
/// The bit field for get access to efuse data.
63
#[derive(Debug, Clone, Copy)]
64
#[instability::unstable]
65
pub struct EfuseField {
66
    /// The block.
67
    pub(crate) block: EfuseBlock,
68
    /// Word number - this is just informational.
69
    pub(crate) _word: u32,
70
    /// Starting bit in the efuse block
71
    pub(crate) bit_start: u32,
72
    /// Number of bits
73
    pub(crate) bit_count: u32,
74
}
75

            
76
impl EfuseField {
77
    pub(crate) const fn new(block: u32, word: u32, bit_start: u32, bit_count: u32) -> Self {
78
        Self {
79
            block: EfuseBlock::from_repr(block).unwrap(),
80
            _word: word,
81
            bit_start,
82
            bit_count,
83
        }
84
    }
85
}
86

            
87
/// Reads field value in a little-endian order.
88
#[inline(always)]
89
#[instability::unstable]
90
pub fn read_field_le<T: AnyBitPattern>(field: EfuseField) -> T {
91
    let EfuseField {
92
        block,
93
        bit_start,
94
        bit_count,
95
        ..
96
    } = field;
97

            
98
    // Represent output value as a bytes slice:
99
    let mut output = mem::MaybeUninit::<T>::uninit();
100
    let mut bytes =
101
        unsafe { slice::from_raw_parts_mut(output.as_mut_ptr() as *mut u8, mem::size_of::<T>()) };
102

            
103
    let bit_off = bit_start as usize;
104
11
    let bit_end = cmp::min(bit_count as usize, bytes.len() * 8) + bit_off;
105

            
106
11
    let mut last_word_off = bit_off / 32;
107
    let mut last_word = unsafe { block.address().add(last_word_off).read_volatile() };
108

            
109
    let word_bit_off = bit_off % 32;
110
    let word_bit_ext = 32 - word_bit_off;
111

            
112
    let mut word_off = last_word_off;
113
    for bit_off in (bit_off..bit_end).step_by(32) {
114
        if word_off != last_word_off {
115
            // Read a new word:
116
            last_word_off = word_off;
117
            last_word = unsafe { block.address().add(last_word_off).read_volatile() };
118
        }
119

            
120
328
        let mut word = last_word >> word_bit_off;
121
        word_off += 1;
122

            
123
        let word_bit_len = cmp::min(bit_end - bit_off, 32);
124
11
        if word_bit_len > word_bit_ext {
125
            // Read the next word:
126
            last_word_off = word_off;
127
            last_word = unsafe { block.address().add(last_word_off).read_volatile() };
128
            // Append bits from a beginning of the next word:
129
4
            word |= last_word.wrapping_shl((32 - word_bit_off) as u32);
130
        };
131

            
132
        if word_bit_len < 32 {
133
            // Mask only needed bits of a word:
134
328
            word &= u32::MAX >> (32 - word_bit_len);
135
        }
136

            
137
        // Represent word as a byte slice:
138
        let byte_len = word_bit_len.div_ceil(8);
139
        let word_bytes =
140
            unsafe { slice::from_raw_parts(&word as *const u32 as *const u8, byte_len) };
141

            
142
        // Copy word bytes to output value bytes:
143
        bytes[..byte_len].copy_from_slice(word_bytes);
144

            
145
        // Move read window forward:
146
        bytes = &mut bytes[byte_len..];
147
    }
148

            
149
    // Fill untouched bytes with zeros:
150
    bytes.fill(0);
151

            
152
11
    unsafe { output.assume_init() }
153
}
154

            
155
/// Reads bit value.
156
///
157
/// Panics if the field's bit length is not equal to 1.
158
#[inline(always)]
159
#[instability::unstable]
160
pub fn read_bit(field: EfuseField) -> bool {
161
    assert_eq!(field.bit_count, 1);
162
    read_field_le::<u8>(field) != 0
163
}
164

            
165
/// Decodes a sign-magnitude number, where bit `sign_bit` carries the sign and the bits below it
166
/// carry the magnitude.
167
///
168
/// The RTC calibration fields store their differences this way rather than in two's complement,
169
/// so a raw read has to be converted before it can be added to a reference point.
170
#[allow(
171
    dead_code,
172
    reason = "not every chip has sign-magnitude calibration fields"
173
)]
174
pub(crate) fn sign_magnitude(value: u32, sign_bit: u32) -> i32 {
175
    let sign_mask = 1 << sign_bit;
176
4
    if value & sign_mask != 0 {
177
        -((value & !sign_mask) as i32)
178
    } else {
179
        value as i32
180
    }
181
}
182

            
183
/// Overrides the base MAC address used by [`interface_mac_address`].
184
///
185
/// After a successful call, [`interface_mac_address`] will derive
186
/// per-interface addresses from the overridden base instead of the factory
187
/// eFuse MAC. [`base_mac_address`] is unaffected and continues to return the
188
/// factory eFuse value.
189
///
190
/// The override does not persist across device resets.
191
/// Can only be called once. Returns `Err(SetMacError::AlreadySet)`
192
/// otherwise.
193
#[instability::unstable]
194
2
pub fn override_mac_address(mac: MacAddress) -> Result<(), SetMacError> {
195
    if MAC_OVERRIDE_STATE
196
        .compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
197
        .is_err()
198
    {
199
        return Err(SetMacError::AlreadySet);
200
    }
201

            
202
    unsafe {
203
1
        MAC_OVERRIDE = mac;
204
    }
205

            
206
    MAC_OVERRIDE_STATE.store(2, Ordering::Release);
207

            
208
    Ok(())
209
1
}
210

            
211
#[procmacros::doc_replace]
212
/// Returns the base MAC address programmed into eFuse during manufacturing.
213
///
214
/// This always reads directly from the hardware eFuse storage. To get the
215
/// effective MAC for a specific radio interface (which may be overridden via
216
/// [`override_mac_address`]), use [`interface_mac_address`] instead.
217
///
218
/// # Examples
219
///
220
/// ```rust, no_run
221
/// # {before_snippet}
222
/// use esp_hal::efuse;
223
///
224
/// let mac = efuse::base_mac_address();
225
/// println!("Base MAC: {mac}");
226
/// # {after_snippet}
227
/// ```
228
#[instability::unstable]
229
2
pub fn base_mac_address() -> MacAddress {
230
    let mut mac_addr = [0u8; 6];
231

            
232
12
    let mac0 = read_field_le::<[u8; 4]>(crate::efuse::MAC0);
233
12
    let mac1 = read_field_le::<[u8; 2]>(crate::efuse::MAC1);
234

            
235
    // MAC address is stored in big endian, so load the bytes in reverse:
236
    mac_addr[0] = mac1[1];
237
    mac_addr[1] = mac1[0];
238
    mac_addr[2] = mac0[3];
239
    mac_addr[3] = mac0[2];
240
    mac_addr[4] = mac0[1];
241
    mac_addr[5] = mac0[0];
242

            
243
    MacAddress::new_eui48(mac_addr)
244
2
}
245

            
246
#[procmacros::doc_replace(
247
    "interface_mac_example" => {
248
        cfg(soc_has_wifi) => "let mac = efuse::interface_mac_address(InterfaceMacAddress::Station);",
249
        _ => "let mac = efuse::interface_mac_address(InterfaceMacAddress::Bluetooth);"
250
    }
251
)]
252
/// Returns the MAC address for a specific interface, derived from the base
253
/// MAC.
254
///
255
/// By default, addresses are derived from the factory eFuse MAC returned
256
/// by [`base_mac_address`]. If [`override_mac_address`] has been called, the
257
/// overridden base address is used instead.
258
///
259
/// Each chip is programmed with a unique base MAC address during manufacturing.
260
/// Different interfaces (Wi-Fi Station, SoftAP, Bluetooth, etc.) each use a
261
/// MAC address derived from this base address. The Station interface uses
262
/// the base MAC directly, while others use locally administered variants
263
/// produced by modifying the first octet to set the local-admin bit; some
264
/// interfaces (such as Bluetooth) additionally adjust the last octet to
265
/// obtain a distinct address.
266
///
267
/// # Examples
268
///
269
/// ```rust, no_run
270
/// # {before_snippet}
271
/// use esp_hal::efuse::{self, InterfaceMacAddress};
272
///
273
/// # {interface_mac_example}
274
/// println!("MAC: {mac}");
275
/// # {after_snippet}
276
/// ```
277
#[cfg(any(soc_has_wifi, soc_has_bt))]
278
13
pub fn interface_mac_address(kind: InterfaceMacAddress) -> MacAddress {
279
13
    let mut mac = if MAC_OVERRIDE_STATE.load(Ordering::Acquire) == 2 {
280
1
        unsafe { MAC_OVERRIDE }
281
    } else {
282
        base_mac_address()
283
    };
284

            
285
12
    match kind {
286
        #[cfg(soc_has_wifi)]
287
        InterfaceMacAddress::Station => {
288
            // base MAC
289
        }
290
        #[cfg(soc_has_wifi)]
291
        InterfaceMacAddress::AccessPoint => {
292
            derive_local_mac(&mut mac);
293
        }
294
        #[cfg(soc_has_bt)]
295
        InterfaceMacAddress::Bluetooth => {
296
            derive_local_mac(&mut mac);
297

            
298
            mac.0[5] = mac.0[5].wrapping_add(1);
299
        }
300
    }
301
13
    mac
302
13
}
303

            
304
#[doc_replace]
305
/// Returns the hardware revision.
306
///
307
/// # Examples
308
///
309
/// ```rust,no_run
310
/// # {before_snippet}
311
/// let rev = esp_hal::efuse::chip_revision();
312
/// println!("Chip revision: {}.{}", rev.major, rev.minor);
313
/// # {after_snippet}
314
/// ```
315
#[inline]
316
pub fn chip_revision() -> ChipRevision {
317
    ChipRevision {
318
        major: major_chip_version(),
319
        minor: minor_chip_version(),
320
    }
321
}
322

            
323
#[doc_replace]
324
/// Represents the hardware revision.
325
///
326
/// The type supports converting between two separate u16-based representations:
327
///
328
/// - Combined: a `u16` calculated as `major * 100 + minor`. The combined representation is more
329
///   often used by ESP-IDF, and working with it involves integer division. The combined
330
///   representation assumes minor is less than 100.
331
/// - Packed: a `u16` with the major revision in the high byte and the minor revision in the low
332
///   byte.
333
///
334
/// # Examples
335
///
336
/// ```rust,no_run
337
/// # {before_snippet}
338
/// use esp_hal::efuse::ChipRevision;
339
///
340
/// let rev = esp_hal::efuse::chip_revision();
341
/// println!("Chip revision: {}.{}", rev.major, rev.minor);
342
///
343
/// // You can compare against other ChipRevision objects
344
/// if rev < ChipRevision::from_combined(300) {
345
///     // You can print a debug representation
346
///     println!("Chip revision is too old: {:?}", rev);
347
/// }
348
/// if rev >= ChipRevision::from_packed(0x400) {
349
///     println!("Chip revision is too new: {:?}", rev);
350
/// }
351
///
352
/// // You can convert into two different numeric representations.
353
/// assert!(rev.packed() >= 0x300);
354
/// assert!(rev.combined() >= 300);
355
/// # {after_snippet}
356
/// ```
357
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
358
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
359
pub struct ChipRevision {
360
    /// The major revision number.
361
    pub major: u8,
362

            
363
    /// The minor revision number.
364
    pub minor: u8,
365
}
366

            
367
impl PartialOrd for ChipRevision {
368
    #[inline]
369
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
370
        Some(self.cmp(other))
371
    }
372
}
373

            
374
impl Ord for ChipRevision {
375
    #[inline]
376
    fn cmp(&self, other: &Self) -> cmp::Ordering {
377
        match self.major.cmp(&other.major) {
378
            cmp::Ordering::Equal => self.minor.cmp(&other.minor),
379
            ord => ord,
380
        }
381
    }
382
}
383

            
384
impl ChipRevision {
385
    /// Creates a new [`ChipRevision`] from a combined revision value.
386
    #[inline]
387
    pub const fn from_combined(revision: u16) -> Self {
388
        let major = revision / 100;
389
        let minor = revision % 100;
390
        ::core::assert!(
391
            major <= u8::MAX as u16 && minor <= u8::MAX as u16,
392
            "`ChipRevision` cannot represent revision",
393
        );
394
        Self {
395
            major: major as u8,
396
            minor: minor as u8,
397
        }
398
    }
399

            
400
    /// Returns the combined revision value as a `u16`.
401
    ///
402
    /// The combined revision value is a `u16` calculated as `major * 100 + minor`.
403
    #[inline]
404
    pub const fn combined(self) -> u16 {
405
        ::core::assert!(
406
            self.minor < 100,
407
            "`ChipRevision` cannot be represented using the combined representation",
408
        );
409
        (self.major as u16) * 100 + (self.minor as u16)
410
    }
411

            
412
    /// Creates a new [`ChipRevision`] from a packed revision value.
413
    ///
414
    /// The packed revision value is a `u16` with the major revision in the high byte and the minor
415
    /// revision in the low byte.
416
    #[inline]
417
    pub const fn from_packed(packed: u16) -> Self {
418
        Self {
419
            major: ((packed >> 8) & 0xFF) as u8,
420
            minor: (packed & 0xFF) as u8,
421
        }
422
    }
423

            
424
    /// Returns the packed revision value as a `u16`.
425
    ///
426
    /// The packed revision value is a `u16` with the major revision in the high byte and the minor
427
    /// revision in the low byte.
428
    #[inline]
429
    pub const fn packed(self) -> u16 {
430
        (self.major as u16) << 8 | (self.minor as u16)
431
    }
432
}
433

            
434
// Indicates the state of setting the mac address
435
// 0 -- unset
436
// 1 -- in the process of being set
437
// 2 -- set
438
//
439
// Values other than 0 indicate that we cannot attempt setting the mac address
440
// again, and values other than 2 indicate that we should read the mac address
441
// from eFuse.
442
static MAC_OVERRIDE_STATE: AtomicU8 = AtomicU8::new(0);
443
static mut MAC_OVERRIDE: MacAddress = MacAddress::new_eui48([0; 6]);
444

            
445
/// Error indicating issues with setting the MAC address.
446
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
447
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
448
#[instability::unstable]
449
pub enum SetMacError {
450
    /// The MAC address has already been set and cannot be changed.
451
    AlreadySet,
452
}
453

            
454
impl core::fmt::Display for SetMacError {
455
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
456
        match self {
457
            SetMacError::AlreadySet => write!(f, "The MAC address has already been set"),
458
        }
459
    }
460
}
461

            
462
impl core::error::Error for SetMacError {}
463

            
464
/// Helper function.
465
/// Serves to derive a local MAC by adjusting the first octet of the given base MAC.
466
/// See https://github.com/esp-rs/esp-hal/blob/0881d747c53e43ee847bef3068076a48ce8d27f0/esp-radio/src/common_adapter.rs#L151-L159
467
#[cfg(any(soc_has_wifi, soc_has_bt))]
468
fn derive_local_mac(mac: &mut MacAddress) {
469
    let bytes = &mut mac.0;
470
    let base = bytes[0];
471

            
472
    for i in 0..64 {
473
5
        let derived = (base | 0x02) ^ (i << 2);
474
5
        if derived != base {
475
            bytes[0] = derived;
476
            break;
477
        }
478
    }
479
}
480

            
481
/// Interface selection for [`interface_mac_address`].
482
///
483
/// Each interface uses a distinct MAC address derived from either the base MAC or the overridden
484
/// MAC if [`override_mac_address`] has been called.
485
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Default)]
486
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
487
#[cfg(any(soc_has_wifi, soc_has_bt))]
488
#[non_exhaustive]
489
pub enum InterfaceMacAddress {
490
    /// Wi-Fi station. Equivalent to the base MAC address or overridden via
491
    /// [`override_mac_address`].
492
    #[cfg(soc_has_wifi)]
493
    #[cfg_attr(soc_has_wifi, default)]
494
    Station,
495
    /// Wi-Fi SoftAP.
496
    #[cfg(soc_has_wifi)]
497
    AccessPoint,
498
    /// Bluetooth (BT/BLE).
499
    #[cfg(soc_has_bt)]
500
    #[cfg_attr(not(soc_has_wifi), default)]
501
    Bluetooth,
502
}
503

            
504
/// Hardware (MAC) address.
505
///
506
/// Use [`as_bytes`](Self::as_bytes) for raw access, or the
507
/// [`Display`](core::fmt::Display) impl for colon-separated hex
508
/// (e.g. `aa:bb:cc:dd:ee:ff`).
509
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
510
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
511
pub struct MacAddress([u8; 6]);
512

            
513
impl MacAddress {
514
    /// Creates a new `MacAddress` from the given bytes.
515
    #[instability::unstable]
516
    pub const fn new_eui48(bytes: [u8; 6]) -> Self {
517
2
        Self(bytes)
518
    }
519

            
520
    /// Returns the address bytes.
521
    pub fn as_bytes(&self) -> &[u8] {
522
        &self.0
523
    }
524
}
525

            
526
impl core::fmt::Display for MacAddress {
527
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
528
        for (i, b) in self.0.iter().enumerate() {
529
            if i != 0 {
530
                f.write_str(":")?;
531
            }
532
            write!(f, "{b:02x}")?;
533
        }
534
        Ok(())
535
    }
536
}