Skip to main content

esp_hal/ethernet/
embassy_net.rs

1//! `embassy-net` driver integration for the EMAC Ethernet peripheral.
2//!
3//! This module provides an [`embassy_net_driver_02::Driver`] implementation
4//! for [`Ethernet`] operating in async mode, enabling the
5//! Ethernet peripheral to be used as a network interface with
6//! [`embassy-net`](https://crates.io/crates/embassy-net).
7//!
8//! # Usage
9//!
10//! After obtaining an `Ethernet<'_, Async, P>` instance, pass it directly to
11//! `embassy_net::new()` — the [`Driver`] impl is inherent on the type.
12
13use core::task::Context;
14
15use embassy_net_driver_02::{
16    Capabilities,
17    Checksum,
18    Driver,
19    HardwareAddress,
20    LinkState,
21    RxToken,
22    TxToken,
23};
24
25use super::{Ethernet, RX_WAKER, TX_WAKER, mac::EmacRegs};
26use crate::{
27    Async,
28    ethernet::{
29        dma::{RDesRing, TDesRing},
30        phy::Phy,
31    },
32};
33
34/// Maximum Ethernet frame size (header + payload, no FCS).
35const MTU: usize = 1514;
36
37// ── Token types ───────────────────────────────────────────────────────────────
38
39/// Received-frame token.
40///
41/// Holds a mutable borrow of the RX ring and a shared borrow of the MAC
42/// register block needed to resume the RX DMA after releasing the descriptor.
43/// The closure passed to [`consume`][RxToken::consume] receives a `&mut [u8]`
44/// pointing directly into the DMA RX buffer — no copy is performed.
45pub struct EthernetRxToken<'a, 'd> {
46    rx: &'a mut RDesRing<'d>,
47}
48
49/// Transmit token.
50///
51/// Holds a mutable borrow of the TX ring and a shared borrow of the MAC
52/// register block needed to trigger a TX poll after committing the frame.
53/// The closure passed to [`consume`][TxToken::consume] receives a `&mut [u8]`
54/// pointing directly into the DMA TX buffer — no copy is performed.
55pub struct EthernetTxToken<'a, 'd> {
56    tx: &'a mut TDesRing<'d>,
57}
58
59impl<'a, 'd> RxToken for EthernetRxToken<'a, 'd> {
60    fn consume<R, F>(self, f: F) -> R
61    where
62        F: FnOnce(&mut [u8]) -> R,
63    {
64        // receive() loops past error frames and returns a direct reference into
65        // the DMA buffer. The descriptor is still CPU-owned while f runs.
66        // NOTE: unwrap is safe — Driver::receive() verified a valid frame exists
67        // and we hold exclusive access to the ring via &'a mut.
68        let pkt = unwrap!(self.rx.receive(), "RX packet vanished");
69        let r = f(pkt);
70        // After f returns, the &mut [u8] borrow on the ring ends (NLL),
71        // so we can recycle the descriptor.
72        self.rx.pop();
73        // Poke the RX DMA in case it suspended waiting for a CPU-owned descriptor.
74        EmacRegs.demand_rx_poll();
75        r
76    }
77}
78
79impl<'a, 'd> TxToken for EthernetTxToken<'a, 'd> {
80    fn consume<R, F>(self, len: usize, f: F) -> R
81    where
82        F: FnOnce(&mut [u8]) -> R,
83    {
84        let capped = len.min(MTU);
85        // Get a direct mutable reference into the DMA TX buffer — no copy.
86        // NOTE: unwrap is safe — Driver::transmit/receive() verified capacity.
87        let buf = unwrap!(self.tx.available_buf(), "TX slot vanished");
88        let r = f(&mut buf[..capped]);
89        // After f returns the &mut borrow on buf ends, we can commit.
90        self.tx.commit(capped);
91        EmacRegs.demand_tx_poll();
92        r
93    }
94}
95
96// ── Driver impl ───────────────────────────────────────────────────────────────
97
98impl<'d, P: Phy> Driver for Ethernet<'d, Async, P> {
99    type RxToken<'a>
100        = EthernetRxToken<'a, 'd>
101    where
102        Self: 'a;
103
104    type TxToken<'a>
105        = EthernetTxToken<'a, 'd>
106    where
107        Self: 'a;
108
109    fn receive(&mut self, cx: &mut Context<'_>) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
110        RX_WAKER.register(cx.waker());
111        TX_WAKER.register(cx.waker());
112
113        // Check availability as plain booleans so the borrows end before we
114        // split &mut self into the two ring references for the tokens.
115        let rx_ready = self.rx.receive().is_some();
116        // Poke the RX DMA unconditionally: receive() may have recycled error
117        // frames back to DMA ownership without a poll-demand write, which
118        // would leave the GMAC RX channel suspended.
119        EmacRegs.demand_rx_poll();
120        let tx_ready = self.tx.available_buf().is_some();
121
122        if rx_ready && tx_ready {
123            Some((
124                EthernetRxToken { rx: &mut self.rx },
125                EthernetTxToken { tx: &mut self.tx },
126            ))
127        } else {
128            None
129        }
130    }
131
132    fn transmit(&mut self, cx: &mut Context<'_>) -> Option<Self::TxToken<'_>> {
133        TX_WAKER.register(cx.waker());
134        if self.tx.available_buf().is_some() {
135            Some(EthernetTxToken { tx: &mut self.tx })
136        } else {
137            None
138        }
139    }
140
141    fn link_state(&mut self, cx: &mut Context<'_>) -> LinkState {
142        let state = self.poll_link(Some(cx));
143        if state.up {
144            self.set_speed(state.speed);
145            self.set_duplex(state.duplex);
146            LinkState::Up
147        } else {
148            LinkState::Down
149        }
150    }
151
152    fn capabilities(&self) -> Capabilities {
153        let mut caps = Capabilities::default();
154        caps.max_transmission_unit = MTU;
155        caps.max_burst_size = Some(self.tx.len());
156        // Checksums are offloaded to hardware in both directions (RX COE + TX
157        // insertion via the descriptor CIC bits), so smoltcp does neither.
158        caps.checksum.ipv4 = Checksum::None;
159        caps.checksum.tcp = Checksum::None;
160        caps.checksum.udp = Checksum::None;
161        caps.checksum.icmpv4 = Checksum::None;
162        caps.checksum.icmpv6 = Checksum::None;
163        caps
164    }
165
166    fn hardware_address(&self) -> HardwareAddress {
167        HardwareAddress::Ethernet(self.mac_addr())
168    }
169}