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>
impl<const RX: usize, const TX: usize, const BUF: usize> Emac<RX, TX, BUF>
Sourcepub const fn new(config: EmacConfig) -> Self
pub const fn new(config: EmacConfig) -> Self
Create a new (uninitialized) driver.
pub fn state(&self) -> EmacState
pub fn mac_address(&self) -> [u8; 6]
pub fn config(&self) -> &EmacConfig
Sourcepub const fn memory_usage() -> usize
pub const fn memory_usage() -> usize
Total static memory used by this EMAC instance.
Sourcepub fn set_mac_address(&mut self, mac: [u8; 6])
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.
Sourcepub fn set_speed(&mut self, speed: Speed)
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.
Sourcepub fn set_duplex(&mut self, duplex: Duplex)
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.
Sourcepub fn init(&mut self, delay: &mut impl DelayNs) -> Result<(), EmacError>
pub fn init(&mut self, delay: &mut impl DelayNs) -> Result<(), EmacError>
Initialize the EMAC peripheral.
Sequence (mirrors the canonical ESP32 GMAC bring-up):
- APLL 50 MHz programming — only when MCU is the RMII clock master
(
RmiiClockConfig::InternalApll); skipped forExternal. - RMII reference-clock pad routing (GPIO0 input for External, GPIO16/17 output for InternalApll).
- SMI + RMII data-pin routing.
- DPORT EMAC peripheral clock enable.
- PHY interface mode (RMII) + clock source select.
- EMAC extension clocks + RAM power-up.
- DMA software reset.
- MAC config defaults (PS/FES/DM/ACS/JD/WD).
- DMA bus mode + operation mode defaults.
- DMA descriptor chains and base-address registers.
- MAC address program.
Sourcepub fn stop(&mut self, delay: &mut impl DelayNs) -> Result<(), EmacError>
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 exhaustedTX_FIFO_FLUSH_TIMEOUT_US. Teardown still completed — at least one in-flight TX frame may have been truncated on the wire.stateisInitializedeither way, so a follow-upstart()is the recoverable path. There is no in-crate “full re-init” —Emac::initis one-shot — so a terminal recovery means a peripheral or SoC reset from the application layer.Err(EmacError::NotInitialized)if called fromUninitialized.
Idempotent on an already-stopped driver: calling stop while
in Initialized returns Ok(()) without touching hardware.
Sourcepub fn transmit(&mut self, data: &[u8]) -> Result<usize, EmacError>
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.
Sourcepub fn receive(&mut self, buffer: &mut [u8]) -> Result<Option<usize>, EmacError>
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.
Sourcepub fn rx_available(&self) -> bool
pub fn rx_available(&self) -> bool
Whether a received frame is currently waiting in the ring.
Sourcepub fn can_transmit(&self, len: usize) -> bool
pub fn can_transmit(&self, len: usize) -> bool
Whether the TX ring has room for a frame of len bytes.
Sourcepub fn tx_ready(&self) -> bool
pub fn tx_ready(&self) -> bool
Whether at least one TX descriptor is available for the next frame.
Sourcepub fn interrupt_status(&self) -> InterruptStatus
pub fn interrupt_status(&self) -> InterruptStatus
Read and parse the DMA status register.
Sourcepub fn clear_interrupts_raw(&self, raw: u32)
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 DMASTATUS — RS/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.
Sourcepub fn handle_interrupt(&self) -> InterruptStatus
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.