eth-mdio-phy
Trait crate that decouples MDIO bus implementations from the PHYs
they talk to. #![no_std], no allocations, no platform dependency.
Installation
[]
= "0.2"
| Feature | Default | Pulls in |
|---|---|---|
defmt |
off | defmt::Format derives on LinkStatus, PhyCapabilities, PhyError |
MSRV: 1.75. Pure #![no_std] — works on any target.
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. Consumers tracking this crate must bump the minor explicitly when a new minor release lands.
Compatibility
This crate exposes only traits and constants — no transitive runtime dependencies. It is consumed by:
| Consumer | Role |
|---|---|
MAC implementations (e.g. esp-emac, STM32 ETH peripherals, FPGA SMI controllers) |
Provide MdioBus |
PHY drivers (e.g. eth-phy-lan87xx) |
Implement PhyDriver |
Higher-level Ethernet stacks (e.g. embassy-net adaptors) |
Compose the two and stay PHY-agnostic |
What's exposed
MdioBus— the wire-level read/write trait. Any controller that can issue MDIO Clause 22 transactions (ESP32 EMAC SMI, STM32 ETH MDIO, FPGA SMI peripheral, a GPIO bit-bang implementation, a mock for tests) implements this trait.PhyDriver— what every PHY driver in this stack provides:init,poll_link,capabilities. PHY-agnostic code (a DHCP test, a link-state watchdog, anembassy-netdriver) can target this trait instead of a specific chip.ieee802_3— Clause 22 standard register addresses (BMCR,BMSR,ANAR,ANLPAR,PHYIDR1/2) and the bit constants inside them, plus convenience helperssoft_reset,enable_auto_negotiation,is_link_up,read_phy_id,read_capabilities,force_link. Reuse these in any chip-specific driver instead of re-deriving the bit numbers.- Shared types —
Speed { Mbps10, Mbps100 },Duplex { Half, Full },LinkStatus { speed, duplex },PhyCapabilities(chip identification + advertised abilities).
Implementing your own PHY driver
A PHY driver is just a struct + impl PhyDriver. Each method takes
the bus generically — there is no associated Bus type, so the same
driver instance can talk to a real EspMdio and a MockMdioBus
within one session.
use ;
Application code then drives the PHY through the trait without caring which chip is on the board:
#
# #
Implementing your own MdioBus
If your MAC has an SMI peripheral, just wrap its read/write registers in a struct:
use MdioBus;
If your board doesn't have a hardware SMI peripheral (some Cortex-M0
parts, FPGA bring-ups, exotic MCUs) a simple Clause 22 GPIO bit-bang
sits in well under 100 lines of embedded-hal::digital::OutputPin
manipulation. The Clause 22 frame is:
preamble (32 ones)
ST (01) — start of frame
OP (10 = read, 01 = write)
PHY addr (5 bits)
REG addr (5 bits)
TA (z0 read / 10 write) — turnaround
DATA (16 bits)
The ieee802_3 register constants in this crate save you from
re-deriving the bit numbers; the wire-level shifting is the only
chip-specific code.
Mocking for tests
Two patterns work well; pick whichever maps better to what your driver actually does.
Scripted-response mock — pre-load the exact reads in order, record
writes for assertions, optionally inject a failure at a specific call
index. This is what eth-phy-lan87xx uses to
verify its init/poll sequences:
use MdioBus;
;
Map-based mock — random-access registers, useful when the driver's behaviour depends on arbitrary read order or many cross-register side-effects:
use MdioBus;
use HashMap;
use Infallible;
Both keep tests on the host, no hardware-in-the-loop fixture required.
Why split MdioBus and PhyDriver
Three concrete reasons we hit on real hardware:
- Cross-MAC reuse. A LAN87xx driver written against
MdioBusworks equally well behind the ESP32 EMAC SMI controller, an STM32 ETH MDIO peripheral, or a host-side bit-bang implementation used by tests. - Faster bring-up. Auto-negotiation diagnostics, link-state
watchdogs, and DHCP smoke tests can target
PhyDriverand run against any concrete PHY without recompilation. - Testability. A
MockMdioBusindev-dependencieslets the PHY driver be unit-tested with deterministic register state — no QEMU, no hardware-in-the-loop fixture needed for the wire-level logic.
Relation to the rest of the stack
- MAC implementations provide a
MdioBus. For ESP32 seeesp_emac::mdio::EspMdio. - PHY drivers implement
PhyDriver. For LAN87xx seeeth-phy-lan87xx. - Higher-level Ethernet stacks (e.g.
embassy-netadaptors) compose the two and stay PHY-agnostic.
License
Licensed under either of:
- GNU General Public License, Version 2.0 or later
- Apache License, Version 2.0
at your option.