#![cfg_attr(docsrs, procmacros::doc_replace(
"interface_mac" => {
cfg(soc_has_wifi) => "let mac = efuse::interface_mac_address(InterfaceMacAddress::Station);",
_ => "let mac = efuse::interface_mac_address(InterfaceMacAddress::Bluetooth);"
}
))]
use core::{cmp, mem, slice, sync::atomic::Ordering};
use bytemuck::AnyBitPattern;
use portable_atomic::AtomicU8;
#[cfg_attr(esp32, path = "esp32/mod.rs")]
#[cfg_attr(esp32c2, path = "esp32c2/mod.rs")]
#[cfg_attr(esp32c3, path = "esp32c3/mod.rs")]
#[cfg_attr(esp32c5, path = "esp32c5/mod.rs")]
#[cfg_attr(esp32c6, path = "esp32c6/mod.rs")]
#[cfg_attr(esp32c61, path = "esp32c61/mod.rs")]
#[cfg_attr(esp32h2, path = "esp32h2/mod.rs")]
#[cfg_attr(esp32s2, path = "esp32s2/mod.rs")]
#[cfg_attr(esp32s3, path = "esp32s3/mod.rs")]
pub(crate) mod implem;
#[instability::unstable]
pub use implem::*;
use procmacros::doc_replace;
#[derive(Debug, Clone, Copy)]
#[instability::unstable]
pub struct EfuseField {
pub(crate) block: EfuseBlock,
pub(crate) _word: u32,
pub(crate) bit_start: u32,
pub(crate) bit_count: u32,
}
impl EfuseField {
pub(crate) const fn new(block: u32, word: u32, bit_start: u32, bit_count: u32) -> Self {
Self {
block: EfuseBlock::from_repr(block).unwrap(),
_word: word,
bit_start,
bit_count,
}
}
}
#[inline(always)]
#[instability::unstable]
pub fn read_field_le<T: AnyBitPattern>(field: EfuseField) -> T {
let EfuseField {
block,
bit_start,
bit_count,
..
} = field;
let mut output = mem::MaybeUninit::<T>::uninit();
let mut bytes =
unsafe { slice::from_raw_parts_mut(output.as_mut_ptr() as *mut u8, mem::size_of::<T>()) };
let bit_off = bit_start as usize;
let bit_end = cmp::min(bit_count as usize, bytes.len() * 8) + bit_off;
let mut last_word_off = bit_off / 32;
let mut last_word = unsafe { block.address().add(last_word_off).read_volatile() };
let word_bit_off = bit_off % 32;
let word_bit_ext = 32 - word_bit_off;
let mut word_off = last_word_off;
for bit_off in (bit_off..bit_end).step_by(32) {
if word_off != last_word_off {
last_word_off = word_off;
last_word = unsafe { block.address().add(last_word_off).read_volatile() };
}
let mut word = last_word >> word_bit_off;
word_off += 1;
let word_bit_len = cmp::min(bit_end - bit_off, 32);
if word_bit_len > word_bit_ext {
last_word_off = word_off;
last_word = unsafe { block.address().add(last_word_off).read_volatile() };
word |= last_word.wrapping_shl((32 - word_bit_off) as u32);
};
if word_bit_len < 32 {
word &= u32::MAX >> (32 - word_bit_len);
}
let byte_len = word_bit_len.div_ceil(8);
let word_bytes =
unsafe { slice::from_raw_parts(&word as *const u32 as *const u8, byte_len) };
bytes[..byte_len].copy_from_slice(word_bytes);
bytes = &mut bytes[byte_len..];
}
bytes.fill(0);
unsafe { output.assume_init() }
}
#[inline(always)]
#[instability::unstable]
pub fn read_bit(field: EfuseField) -> bool {
assert_eq!(field.bit_count, 1);
read_field_le::<u8>(field) != 0
}
#[instability::unstable]
pub fn override_mac_address(mac: MacAddress) -> Result<(), SetMacError> {
if MAC_OVERRIDE_STATE
.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
return Err(SetMacError::AlreadySet);
}
unsafe {
MAC_OVERRIDE = mac;
}
MAC_OVERRIDE_STATE.store(2, Ordering::Release);
Ok(())
}
#[procmacros::doc_replace]
#[instability::unstable]
pub fn base_mac_address() -> MacAddress {
let mut mac_addr = [0u8; 6];
let mac0 = read_field_le::<[u8; 4]>(crate::efuse::MAC0);
let mac1 = read_field_le::<[u8; 2]>(crate::efuse::MAC1);
mac_addr[0] = mac1[1];
mac_addr[1] = mac1[0];
mac_addr[2] = mac0[3];
mac_addr[3] = mac0[2];
mac_addr[4] = mac0[1];
mac_addr[5] = mac0[0];
MacAddress::new_eui48(mac_addr)
}
#[procmacros::doc_replace(
"interface_mac_example" => {
cfg(soc_has_wifi) => "let mac = efuse::interface_mac_address(InterfaceMacAddress::Station);",
_ => "let mac = efuse::interface_mac_address(InterfaceMacAddress::Bluetooth);"
}
)]
#[cfg(any(soc_has_wifi, soc_has_bt))]
pub fn interface_mac_address(kind: InterfaceMacAddress) -> MacAddress {
let mut mac = if MAC_OVERRIDE_STATE.load(Ordering::Acquire) == 2 {
unsafe { MAC_OVERRIDE }
} else {
base_mac_address()
};
match kind {
#[cfg(soc_has_wifi)]
InterfaceMacAddress::Station => {
}
#[cfg(soc_has_wifi)]
InterfaceMacAddress::AccessPoint => {
derive_local_mac(&mut mac);
}
#[cfg(soc_has_bt)]
InterfaceMacAddress::Bluetooth => {
derive_local_mac(&mut mac);
mac.0[5] = mac.0[5].wrapping_add(1);
}
}
mac
}
#[doc_replace]
#[inline]
pub fn chip_revision() -> ChipRevision {
ChipRevision {
major: major_chip_version(),
minor: minor_chip_version(),
}
}
#[doc_replace]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ChipRevision {
pub major: u8,
pub minor: u8,
}
impl PartialOrd for ChipRevision {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ChipRevision {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
match self.major.cmp(&other.major) {
cmp::Ordering::Equal => self.minor.cmp(&other.minor),
ord => ord,
}
}
}
impl ChipRevision {
#[inline]
pub const fn from_combined(revision: u16) -> Self {
let major = revision / 100;
let minor = revision % 100;
::core::assert!(
major <= u8::MAX as u16 && minor <= u8::MAX as u16,
"`ChipRevision` cannot represent revision",
);
Self {
major: major as u8,
minor: minor as u8,
}
}
#[inline]
pub const fn combined(self) -> u16 {
::core::assert!(
self.minor < 100,
"`ChipRevision` cannot be represented using the combined representation",
);
(self.major as u16) * 100 + (self.minor as u16)
}
#[inline]
pub const fn from_packed(packed: u16) -> Self {
Self {
major: ((packed >> 8) & 0xFF) as u8,
minor: (packed & 0xFF) as u8,
}
}
#[inline]
pub const fn packed(self) -> u16 {
(self.major as u16) << 8 | (self.minor as u16)
}
}
static MAC_OVERRIDE_STATE: AtomicU8 = AtomicU8::new(0);
static mut MAC_OVERRIDE: MacAddress = MacAddress::new_eui48([0; 6]);
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum SetMacError {
AlreadySet,
}
impl core::fmt::Display for SetMacError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
SetMacError::AlreadySet => write!(f, "The MAC address has already been set"),
}
}
}
impl core::error::Error for SetMacError {}
#[cfg(any(soc_has_wifi, soc_has_bt))]
fn derive_local_mac(mac: &mut MacAddress) {
let bytes = &mut mac.0;
let base = bytes[0];
for i in 0..64 {
let derived = (base | 0x02) ^ (i << 2);
if derived != base {
bytes[0] = derived;
break;
}
}
}
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(any(soc_has_wifi, soc_has_bt))]
#[non_exhaustive]
pub enum InterfaceMacAddress {
#[cfg(soc_has_wifi)]
#[cfg_attr(soc_has_wifi, default)]
Station,
#[cfg(soc_has_wifi)]
AccessPoint,
#[cfg(soc_has_bt)]
#[cfg_attr(not(soc_has_wifi), default)]
Bluetooth,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MacAddress([u8; 6]);
impl MacAddress {
#[instability::unstable]
pub const fn new_eui48(bytes: [u8; 6]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl core::fmt::Display for MacAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for (i, b) in self.0.iter().enumerate() {
if i != 0 {
f.write_str(":")?;
}
write!(f, "{b:02x}")?;
}
Ok(())
}
}