Releases: jethub-iot/esp-emac-rs
v0.5.0
Breaking release — propagates the upstream esp_hal::ethernet::mac::LinkState shape + Speed::_10M/_100M renames through the mdio-phy feature re-exports. Final crate in the coordinated 0.3 release wave:
| Crate | Version |
|---|---|
eth-mdio-phy |
0.3.0 (publish FIRST) |
eth-phy-lan87xx |
0.3.0 |
eth-phy-lan867x |
0.2.0 |
esp-emac |
0.5.0 ← this release (publish LAST) |
Pre-1.0 SemVer caveat. Cargo's caret on
^0.4will not pick up0.5.x. Bump both digits explicitly.
Diff for review
- Previous tag:
v0.4.1—d316cee6ec - This release:
mainHEAD —4d81c0f6fa - Full diff: v0.4.1...main
Publish-order precondition
Both eth-mdio-phy 0.3.0 and eth-phy-lan87xx 0.3.0 must be on crates.io before publishing this release (the first is a registry dependency under the mdio-phy feature; the second is a dev-dependency exercised by the bundled embassy_net_lan8720a.rs example).
Why 0.5.0, not 0.4.2
Under the mdio-phy feature, esp-emac re-exports Speed and Duplex from eth_mdio_phy. The eth-mdio-phy 0.2 → 0.3 rename (Mbps10/100 → _10M/_100M, Option<LinkStatus> → LinkState { up, ... }) bleeds through these re-exports and breaks any downstream code that matches on esp_emac::Speed::Mbps100 etc. Cargo SemVer bumps the left-most non-zero component on any breaking change — pre-1.0 that's the minor.
Breaking — transitive eth-mdio-phy 0.3
- Bump dependency on
eth-mdio-phyto^0.3and dev-dependency oneth-phy-lan87xxto^0.3. Under themdio-phyfeature this crate re-exportsSpeedandDuplexfrometh_mdio_phy, so downstream consumers see the upstream renames:Speed::Mbps10/Speed::Mbps100→Speed::_10M/Speed::_100M(matchesesp_hal::ethernet::phy::Speedvariant naming).LinkStatus { speed, duplex }+Option<LinkStatus>signalling →LinkState { up: bool, speed, duplex }.- PHY drivers'
poll_linkreturn:Result<Option<LinkStatus>, _>→Result<LinkState, _>.
Speed and Duplex are #[non_exhaustive] in the trait crate (eth-mdio-phy) — any remaining match arms over them in downstream code must add a wildcard. LinkState is an open struct, so match/destructure patterns continue to work without ...
Breaking — defmt 1.0
Bump optional defmt dependency from ^0.3 to ^1.0. esp-hal 1.1.x and embassy-net 0.9.x already require defmt ^1.0.1; without this bump any downstream that enables the defmt feature on this crate alongside a current esp-hal / embassy-net release would end up with two defmt versions in its lock graph (defmt global-logger ABI is not version-compatible across 0.3 → 1.0 — real link error, not a noisy warning). Use sites are only defmt::Format derives and defmt::warn!(...) calls, source-compatible across the jump.
Migration
// before (0.4.x with eth-mdio-phy 0.2)
match phy.poll_link(mdio) {
Ok(Some(link)) => {
emac.set_speed(link.speed); // Speed::Mbps100
emac.set_duplex(link.duplex);
}
Ok(None) => { /* link down */ }
Err(e) => return Err(e.into()),
}
// after (0.5.0 with eth-mdio-phy 0.3)
match phy.poll_link(mdio) {
Ok(state) if state.up => {
emac.set_speed(state.speed); // Speed::_100M
emac.set_duplex(state.duplex);
}
Ok(_) => { /* link down */ }
Err(e) => return Err(e.into()),
}If your firmware copy-pasted the 0.4.x example pattern with use esp_emac::emac::{Duplex as EmacDuplex, Speed as EmacSpeed}; — drop both aliases. Under the mdio-phy feature esp_emac::Speed and Duplex are now the same type identity as eth_mdio_phy::Speed and Duplex, no conversion needed at the seam.
Changed
examples/embassy_net_lan8720a.rs: rewrite the PHY poll loop to use the newResult<LinkState, _>shape and drop thePhySpeed/PhyDuplexrename aliases.src/emac.rs: doc-comments and internal match arms updated to the_10M/_100Mvariant names.README.md: drop thePhySpeed/PhyDuplexalias-naming guidance; correct the HW checksum offload paragraph to reflect 0.4.1's engine-disable (it had still claimed "unconditional" from the 0.3.0 era).
Notes
- No source-level changes to the public
Emac/EmacDriverAPI surface beyond the transitive type renames above. - No behavioural diff observed on JXD-PM380-E1ETH bench vs 0.4.1 across TX/RX paths, DMA descriptor handling, link bring-up sequence, instrumentation. Wave-level smoke test: link UP
_100M Full, DHCP, iperf2 cycles complete.
esp-emac 0.4.1 — disable HW checksum offload (TX+RX) on ESP32 rev v3.1
Patch release — disable broken Synopsys GMAC checksum offload in both
directions on ESP32 rev v3.1.
The v0.3.0 release relied on the hardware checksum engine (TDES0.CIC = 0b11
for TX insertion, GMACCONFIG.IPC = 1 for RX verification) and told
smoltcp to skip software computation. Both sides turn out to be unreliable
on at least rev v3.1 silicon:
- TX: only the first 60-byte iperf2 control header reaches the peer
intact; every bulk segment that follows carries a wrong TCP/UDP checksum
and is dropped by the receiver, so the connection collapses after ~15 s. - RX:
GMACCONFIG.IPC = 1symmetrically marks incoming valid TCP/UDP
frames as checksum-errored, which the DMA then silently drops at
DMAOPERATION.DT = 0before they reach the CPU descriptor ring.
Empirical signature: iperf2 downlink throughput collapsing to 0 Mbps
while uplink still passes data through at a reduced rate.
Both bits are now 0. Driver::capabilities() advertises
ChecksumCapabilities::default(), so smoltcp computes and verifies
IPv4/TCP/UDP/ICMP checksums in software in both directions. A few extra
cycles per frame in exchange for correct frames.
Verified
iperf2 -s against the production server (Linux, iperf 2.1.9) on a
LAN8720A RMII board:
[iperf] done: UP=13 Mbps (16 MB) DN=10 Mbps (11 MB)
i.e. equivalent to the v0.3.0 baseline measured under HW-offload but
now reliable end to end.
Other fixes in this release
- Layout invariants for
TxDescriptorcovered byconst_assert!+
offset_of!checks (back-port from rebased descriptor work). Acquire/Releasememory fences around DMA descriptor ownership
flips indma::engine.current_speed/current_duplexcached fields with idempotency
guards inEmac::set_link_*.- Conditional
Checksumimport (test-only) — removes a clippy unused-
import warning under--lib.
Compatibility
No API breaks vs 0.4.0. Drop-in replacement.
Not yet on crates.io
Per the eth-crate publication gate (eth-mdio-phy + eth-phy-lan87xx +
eth-phy-lan867x + esp-emac ship together after a coordinated
review), this tag is git-only. Use a submodule / git dependency until
the bundle is published.
Full changelog: https://github.com/jethub-iot/esp-emac-rs/blob/v0.4.1/CHANGELOG.md
esp-emac v0.3.0
Added
Hardware checksum offload for the EMAC: TX descriptors now request full TCP/UDP/ICMP + IPv4-header checksum insertion (`TDES0.CIC = 0b11`), and `GMACCONFIG.IPC` is set so the DMA verifies received checksums and silently drops bad frames before they reach the host. The embassy-net driver advertises `Checksum::None` for `ipv4`, `tcp`, `udp`, and `icmpv4` so smoltcp skips redundant software computation.
Behavior is correct on either side: a peer talking to us sees normal Ethernet frames with valid checksums, and frames we receive with bad checksums never appear at the application. Two new unit tests cover the descriptor flag and the advertised capabilities.
Verified on jxd-pm380-e1eth + LAN8720A. No measurable throughput delta on the iperf2 loopback (smoltcp packet pipeline is the dominant cost), but eliminates a real per-packet CPU expense and enables future zero-copy work.
Compatibility
Same as 0.2.x: `esp-hal = "1.1"`, `embassy-net = "0.9"`, `embassy-executor = "0.10"`, `xtensa-esp32-none-elf`. MSRV unchanged at 1.88.
See `CHANGELOG.md` for full notes.
esp-emac 0.2.0
First public release of esp-emac on crates.io. Bundled with eth-mdio-phy 0.2.0, eth-phy-lan87xx 0.2.0, and eth-phy-lan867x 0.1.0.
Pre-flight:
eth-mdio-phy 0.2.0must already be on crates.io before this release publishes —cargo publishwill pull the registry copy of that trait crate during--verify. Publish in this order:eth-mdio-phy→eth-phy-lan87xx→eth-phy-lan867x→esp-emac.
Breaking
SpeedandDuplexare now re-exports ofeth_mdio_phy::{Speed, Duplex}(gated by featuremdio-phy) rather than locally defined enums withFrom<eth_mdio_phy::*>conversions. Call sites that used to writeemac.set_speed(status.speed.into())drop the.into()— the types are literally the same now. Without themdio-phyfeature the symbols are no longer exposed from the crate; drop down tocrate::regs::mac::set_speed_100mbps/set_duplex_fullfor raw register control.Emac::stop()now returnsErr(EmacError::TxFlushTimeout)when the FTF poll exhaustsTX_FIFO_FLUSH_TIMEOUT_USinstead of silently returningOk(()). The rest of the teardown still runs unconditionally, so the driver state still ends up atInitializedandstart()is safe to retry — the new error is a recoverable warning, not a state-machine corruption signal. Callers that pattern-match onOk(())need a wildcard or explicit handling of the new variant.- MSRV bumped from 1.75 to 1.88, matching what
esp-hal = "1.1"declares in its own manifest (esp-hal 1.0carried the same 1.88 pin). The previous declaration was mis-advertised. - Bump
eth-mdio-phydependency pin from^0.1.1to^0.2.0. Trait crate'sSpeed/Duplexbecame#[non_exhaustive]in that release. - Bump
embassy-syncfrom^0.7to^0.8(cascade fromesp-hal 1.1requirement). - Bump
esp-halfrom^1.0.0to^1.1.0.
Added
EmacError::TxFlushTimeoutvariant (lands as a non-breaking variant addition becauseEmacErroris#[non_exhaustive]).Emac::set_speed/set_duplexnow match eachSpeed/Duplexvariant explicitly. The trait-crate types became#[non_exhaustive]ineth-mdio-phy 0.2, so a future variant (e.g. a hypotheticalSpeed::Mbps1000) compiles transparently but has no register encoding on ESP32 EMAC. Such inputs are clamped to 100 Mbps (highest mode the peripheral physically supports) / Full duplex, with adefmt::warn!under thedefmtfeature flagging the mismatch.
Fixed
- Two clippy errors under
cargo +esp clippy --target xtensa-esp32-none-elf -D warnings:redundant_guardsatemac.rs:208(collapsedRmiiClockConfig::InternalApll { gpio, .. } if matches!(gpio, ClkGpio::Gpio0)intoRmiiClockConfig::InternalApll { gpio: ClkGpio::Gpio0, .. }) andlet_unit_valueatemac.rs:474(esp_hal::interrupt::enablereturns(), the priorlet _ =was dead). examples/embassy_net_lan8720a.rsnow compiles againstesp-rtos 0.3.0andembassy-executor 0.10:esp_rtos::starttakes aSoftwareInterrupt<'static, 0>second argument, andSpawner::spawnnow returns()while#[task]fns returnResult<SpawnToken<_>, SpawnError>(the.unwrap()belongs on the task call, not the spawn)..cargo/config.tomlnow emits-Tlinkall.xso the xtensa example links from a clean target dir. Pre-existing latent bug —linkall.xshipped from esp-hal'sOUT_DIRviacargo:rustc-link-search, but nothing emitted a-T, so a fresh build would fail with hundreds ofundefined referencesymbols (_stack_end_cpu0, every interrupt-vector entry from the esp32 PAC). Now caught by theexample-xtensaCI job.
Documentation
- New "Recovery from task respawn" section in the embassy-net module rustdoc covering the
static mut EMACre-borrow path:init()is one-shot, so a respawned task must callstop()+start()to bring the engine back up rather than silently ignoringEmacError::AlreadyInitialized. [package.metadata.docs.rs]now setsdefault-target = "riscv32imc-unknown-none-elf"and drops theesp-halfeature. docs.rs cannot satisfyxtensa-esp32-none-elf(rustc upstream has no xtensa target, and docs.rs does not carry the Microchip fork), so the previous metadata produced a "Documentation: failed" badge. Matches the convention every other esp-rs crate (esp-hal, esp-println, esp-backtrace, esp-storage, esp-radio) already uses.
Compatibility
| Component | Version |
|---|---|
eth-mdio-phy |
0.2.0 |
eth-phy-lan87xx |
0.2.0 |
eth-phy-lan867x |
0.1.0 |
esp-emac (this crate) |
0.2.0 |
esp-hal |
1.1.0 |
esp-rtos |
0.3.0 |
embassy-executor |
0.10 |
Pre-1.0 SemVer note. Cargo's caret on
^0.1will not pick up0.2.x— both digits are treated as the major axis below 1.0. Update youresp-emac = "0.1"requirement to"0.2"to pick up this release.
Full CHANGELOG: CHANGELOG.md