Skip to main content

Emac

Struct Emac 

Source
pub struct Emac<const RX: usize = 10, const TX: usize = 10, const BUF: usize = 1600> { /* private fields */ }
Expand description

ESP32 EMAC driver with statically allocated DMA buffers.

The DMA descriptor chain is self-referential, so the driver MUST be placed in its final memory location BEFORE init is called.

Implementations§

Source§

impl<const RX: usize, const TX: usize, const BUF: usize> Emac<RX, TX, BUF>

Source

pub const fn new(config: EmacConfig) -> Self

Create a new (uninitialized) driver.

Source

pub fn state(&self) -> EmacState

Source

pub fn mac_address(&self) -> [u8; 6]

Source

pub fn config(&self) -> &EmacConfig

Source

pub const fn memory_usage() -> usize

Total static memory used by this EMAC instance.

Source

pub fn set_mac_address(&mut self, mac: [u8; 6])

Set the MAC address.

If the driver has been initialized, the hardware filter registers are updated immediately.

Source

pub fn set_speed(&mut self, speed: Speed)

Apply the link speed reported by the PHY.

The ESP32 EMAC peripheral physically supports only 10 Mbps and 100 Mbps. Speed is #[non_exhaustive] in the trait crate, so future variants (e.g. a hypothetical Mbps1000) compile but have no register encoding here. They are clamped to 100 Mbps — the highest mode the EMAC actually supports — and a warning is emitted under the defmt feature so the discrepancy is visible at runtime.

Available only with the mdio-phy feature, which is also what pulls in the Speed type from eth_mdio_phy. Without the feature, drop down to crate::regs::mac::set_speed_100mbps.

Source

pub fn set_duplex(&mut self, duplex: Duplex)

Apply the duplex mode reported by the PHY.

Duplex is #[non_exhaustive] in the trait crate. ESP32 EMAC has only the two MII-canonical modes (Half/Full); any future variant is clamped to Full (the more permissive default) with a defmt::warn! so the unexpected input doesn’t pass silently.

Available only with the mdio-phy feature, which is also what pulls in the Duplex type from eth_mdio_phy. Without the feature, drop down to crate::regs::mac::set_duplex_full.

Source

pub fn init(&mut self, delay: &mut impl DelayNs) -> Result<(), EmacError>

Initialize the EMAC peripheral.

Sequence (mirrors the canonical ESP32 GMAC bring-up):

  1. APLL 50 MHz programming — only when MCU is the RMII clock master (RmiiClockConfig::InternalApll); skipped for External.
  2. RMII reference-clock pad routing (GPIO0 input for External, GPIO16/17 output for InternalApll).
  3. SMI + RMII data-pin routing.
  4. DPORT EMAC peripheral clock enable.
  5. PHY interface mode (RMII) + clock source select.
  6. EMAC extension clocks + RAM power-up.
  7. DMA software reset.
  8. MAC config defaults (PS/FES/DM/ACS/JD/WD).
  9. DMA bus mode + operation mode defaults.
  10. DMA descriptor chains and base-address registers.
  11. MAC address program.
Source

pub fn start(&mut self) -> Result<(), EmacError>

Start TX/RX (DMA + MAC).

Source

pub fn stop(&mut self, delay: &mut impl DelayNs) -> Result<(), EmacError>

Stop TX/RX.

Polls the TX-FIFO flush bit (FTF) for up to TX_FIFO_FLUSH_TIMEOUT_US microseconds, sleeping delay between polls so the DMA actually has time to drain. The rest of the teardown (MAC RX/TX disable, DMA RX stop, interrupt-status clear, state transition to Initialized) runs unconditionally — even on flush timeout the driver winds up in Initialized and is safe to re-start().

Returns:

  • Ok(()) on a clean teardown (FTF self-cleared in time).
  • Err(EmacError::TxFlushTimeout) when the FTF poll exhausted TX_FIFO_FLUSH_TIMEOUT_US. Teardown still completed — at least one in-flight TX frame may have been truncated on the wire. state is Initialized either way, so a follow-up start() is the recoverable path. There is no in-crate “full re-init” — Emac::init is one-shot — so a terminal recovery means a peripheral or SoC reset from the application layer.
  • Err(EmacError::NotInitialized) if called from Uninitialized.

Idempotent on an already-stopped driver: calling stop while in Initialized returns Ok(()) without touching hardware.

Source

pub fn transmit(&mut self, data: &[u8]) -> Result<usize, EmacError>

Transmit a frame.

Does not block on descriptor availability — caller must check can_transmit (or tx_ready for single-descriptor frames) before calling, or be ready to handle EmacError::NoDescriptorsAvailable / EmacError::DescriptorBusy when the TX ring is full, and EmacError::FrameTooLarge when the payload exceeds the ring’s combined capacity.

Source

pub fn receive(&mut self, buffer: &mut [u8]) -> Result<Option<usize>, EmacError>

Receive a frame, if any.

Issues an RX poll-demand whenever a descriptor was potentially recycled by DmaEngine::receive — that includes the success path (Ok(Some(_))) and the error paths (FrameError, BufferTooSmall, …) where the engine still hands the descriptor back to the DMA. Only Ok(None) skips the kick, since nothing in the ring changed. Without this, an errored frame on a suspended ring would leave RX wedged with the RU bit asserted until the next successful receive — exactly the kind of post-error hang we hit in the field.

Source

pub fn rx_available(&self) -> bool

Whether a received frame is currently waiting in the ring.

Source

pub fn can_transmit(&self, len: usize) -> bool

Whether the TX ring has room for a frame of len bytes.

Source

pub fn tx_ready(&self) -> bool

Whether at least one TX descriptor is available for the next frame.

Source

pub fn interrupt_status(&self) -> InterruptStatus

Read and parse the DMA status register.

Source

pub fn clear_interrupts_raw(&self, raw: u32)

Clear DMA status flags via write-1-to-clear.

Writes the raw register snapshot back into DMASTATUS, masked against crate::regs::dma::status::ALL_INTERRUPTS so only the documented W1C interrupt bits are touched. The non-W1C fields in DMASTATUSRS/TS (process state), EB (error bits), MMC/PMT/TTI — are read-only and silently ignored by the hardware on write, but masking them keeps the contract explicit: every bit we send is something we mean to acknowledge.

Pass the raw snapshot you previously read so every W1C bit (including ones not modeled in InterruptStatus such as ERI / ETI / RWT) is acknowledged in a single write.

Source

pub fn handle_interrupt(&self) -> InterruptStatus

Convenience: handle the ISR — read status, clear all flags (via the raw snapshot, so unrepresented W1C bits are also acknowledged), return the parsed copy.

Auto Trait Implementations§

§

impl<const RX: usize = 10, const TX: usize = 10, const BUF: usize = 1600> !Freeze for Emac<RX, TX, BUF>

§

impl<const RX: usize = 10, const TX: usize = 10, const BUF: usize = 1600> !RefUnwindSafe for Emac<RX, TX, BUF>

§

impl<const RX: usize, const TX: usize, const BUF: usize> Send for Emac<RX, TX, BUF>

§

impl<const RX: usize, const TX: usize, const BUF: usize> Sync for Emac<RX, TX, BUF>

§

impl<const RX: usize, const TX: usize, const BUF: usize> Unpin for Emac<RX, TX, BUF>

§

impl<const RX: usize, const TX: usize, const BUF: usize> UnsafeUnpin for Emac<RX, TX, BUF>

§

impl<const RX: usize, const TX: usize, const BUF: usize> UnwindSafe for Emac<RX, TX, BUF>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.