Skip to main content

esp_hal/ethernet/
xarxa.rs

1//! `xarxa` driver integration for the EMAC Ethernet peripheral.
2//!
3//! This module provides a [`xarxa_driver::Driver`] implementation for
4//! [`Ethernet`] operating in async mode, enabling the Ethernet peripheral to be
5//! used as a network interface with [`xarxa`](https://crates.io/crates/xarxa).
6//!
7//! ## Usage
8//!
9//! After obtaining an `Ethernet<'_, Async, P>` instance, pass it directly to the
10//! stack — the [`Driver`] impl is inherent on the type.
11//!
12//! Unlike the `embassy-net` driver, this one copies each frame between the DMA
13//! ring and a pool buffer, because `xarxa` passes ownership of packet buffers
14//! instead of lending the driver's own memory.
15
16use core::{
17    any::TypeId,
18    task::{Context, Waker},
19};
20
21use xarxa_driver::{
22    Capabilities,
23    ChecksumOffload,
24    Driver,
25    HardwareAddress,
26    LinkState,
27    NotSupported,
28    PacketBuf,
29    config::PACKET_BUF_SIZE,
30};
31
32use super::{Ethernet, RX_WAKER, TX_WAKER, mac::EmacRegs};
33use crate::{Async, DriverMode, ethernet::phy::Phy};
34
35/// Maximum Ethernet frame size (header + payload, no FCS).
36///
37/// The packet pool has a fixed buffer size, which caps the frames this driver
38/// can pass on to the stack.
39const MTU: usize = if 1514 < PACKET_BUF_SIZE {
40    1514
41} else {
42    PACKET_BUF_SIZE
43};
44
45impl<'d, Dm: DriverMode + 'static, P: Phy> Driver for Ethernet<'d, Dm, P> {
46    fn capabilities(&self) -> Capabilities {
47        let mut caps = Capabilities::default();
48        caps.max_transmission_unit = MTU;
49        // `Checksum` names the direction(s) computed in software. Hardware
50        // offload is advertised so the stack skips that side.
51        let checksum = ChecksumOffload {
52            tx: property!("ethernet.tx_checksum_offload"),
53            rx: property!("ethernet.rx_checksum_offload"),
54        };
55        caps.checksum.ipv4 = checksum;
56        caps.checksum.tcp = checksum;
57        caps.checksum.udp = checksum;
58        caps.checksum.icmpv4 = checksum;
59        caps.checksum.icmpv6 = checksum;
60        caps
61    }
62
63    fn hardware_address(&self) -> HardwareAddress {
64        HardwareAddress::Ethernet(self.mac_addr())
65    }
66
67    fn link_state(&mut self) -> LinkState {
68        let state = self.poll_link(None);
69        if state.up {
70            self.set_speed(state.speed);
71            self.set_duplex(state.duplex);
72            LinkState::Up
73        } else {
74            LinkState::Down
75        }
76    }
77
78    fn register_waker(&mut self, waker: &Waker) -> Result<(), NotSupported> {
79        if TypeId::of::<Dm>() == TypeId::of::<Async>() {
80            // The driver has one waker per event, the stack has one waker for all of them.
81            RX_WAKER.register(waker);
82            TX_WAKER.register(waker);
83            // The PHY keeps the waker to report a link change. A PHY that cannot do so wakes the
84            // stack at once, which makes it poll the link state instead.
85            self.poll_link(Some(&mut Context::from_waker(waker)));
86            Ok(())
87        } else {
88            Err(NotSupported)
89        }
90    }
91
92    fn receive(&mut self) -> Option<PacketBuf> {
93        let Some(frame) = self.rx.receive() else {
94            // receive() may have recycled error frames back to DMA ownership without a
95            // poll-demand write, which would leave the GMAC RX channel suspended.
96            EmacRegs.demand_rx_poll();
97            return None;
98        };
99
100        let buffer = if frame.len() > PACKET_BUF_SIZE {
101            warn!(
102                "Dropping a {} byte frame, the packet buffer holds {} bytes",
103                frame.len(),
104                PACKET_BUF_SIZE
105            );
106            None
107        } else if let Some(mut buffer) = PacketBuf::try_new() {
108            buffer.set_len(frame.len());
109            buffer.copy_from_slice(frame);
110            Some(buffer)
111        } else {
112            // The pool is shared with the rest of the stack and can be empty. Dropping the
113            // frame is the only option, the descriptor must go back to the DMA.
114            None
115        };
116
117        self.rx.pop();
118        EmacRegs.demand_rx_poll();
119
120        buffer
121    }
122
123    fn can_transmit(&mut self) -> bool {
124        self.tx.available_buf().is_some()
125    }
126
127    fn transmit(&mut self, buffer: PacketBuf) -> Result<(), PacketBuf> {
128        let Some(slot) = self.tx.available_buf() else {
129            return Err(buffer);
130        };
131
132        let len = buffer.len().min(MTU);
133        slot[..len].copy_from_slice(&buffer[..len]);
134
135        self.tx.commit(len);
136        EmacRegs.demand_tx_poll();
137
138        Ok(())
139    }
140}