embassy_stm32/eth/
mod.rs

1//! Ethernet (ETH)
2#![macro_use]
3
4#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")]
5#[cfg_attr(eth_v2, path = "v2/mod.rs")]
6mod _version;
7mod generic_phy;
8
9use core::mem::MaybeUninit;
10use core::task::Context;
11
12use embassy_hal_internal::PeripheralType;
13use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
14use embassy_sync::waitqueue::AtomicWaker;
15
16pub use self::_version::{InterruptHandler, *};
17pub use self::generic_phy::*;
18use crate::rcc::RccPeripheral;
19
20#[allow(unused)]
21const MTU: usize = 1514;
22const TX_BUFFER_SIZE: usize = 1514;
23const RX_BUFFER_SIZE: usize = 1536;
24
25#[repr(C, align(8))]
26#[derive(Copy, Clone)]
27pub(crate) struct Packet<const N: usize>([u8; N]);
28
29/// Ethernet packet queue.
30///
31/// This struct owns the memory used for reading and writing packets.
32///
33/// `TX` is the number of packets in the transmit queue, `RX` in the receive
34/// queue. A bigger queue allows the hardware to receive more packets while the
35/// CPU is busy doing other things, which may increase performance (especially for RX)
36/// at the cost of more RAM usage.
37pub struct PacketQueue<const TX: usize, const RX: usize> {
38    tx_desc: [TDes; TX],
39    rx_desc: [RDes; RX],
40    tx_buf: [Packet<TX_BUFFER_SIZE>; TX],
41    rx_buf: [Packet<RX_BUFFER_SIZE>; RX],
42}
43
44impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {
45    /// Create a new packet queue.
46    pub const fn new() -> Self {
47        Self {
48            tx_desc: [const { TDes::new() }; TX],
49            rx_desc: [const { RDes::new() }; RX],
50            tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX],
51            rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX],
52        }
53    }
54
55    /// Initialize a packet queue in-place.
56    ///
57    /// This can be helpful to avoid accidentally stack-allocating the packet queue in the stack. The
58    /// Rust compiler can sometimes be a bit dumb when working with large owned values: if you call `new()`
59    /// and then store the returned PacketQueue in its final place (like a `static`), the compiler might
60    /// place it temporarily on the stack then move it. Since this struct is quite big, it may result
61    /// in a stack overflow.
62    ///
63    /// With this function, you can create an uninitialized `static` with type `MaybeUninit<PacketQueue<...>>`
64    /// and initialize it in-place, guaranteeing no stack usage.
65    ///
66    /// After calling this function, calling `assume_init` on the MaybeUninit is guaranteed safe.
67    pub fn init(this: &mut MaybeUninit<Self>) {
68        unsafe {
69            this.as_mut_ptr().write_bytes(0u8, 1);
70        }
71    }
72}
73
74static WAKER: AtomicWaker = AtomicWaker::new();
75
76impl<'d, T: Instance, P: Phy> embassy_net_driver::Driver for Ethernet<'d, T, P> {
77    type RxToken<'a>
78        = RxToken<'a, 'd>
79    where
80        Self: 'a;
81    type TxToken<'a>
82        = TxToken<'a, 'd>
83    where
84        Self: 'a;
85
86    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
87        WAKER.register(cx.waker());
88        if self.rx.available().is_some() && self.tx.available().is_some() {
89            Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx }))
90        } else {
91            None
92        }
93    }
94
95    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
96        WAKER.register(cx.waker());
97        if self.tx.available().is_some() {
98            Some(TxToken { tx: &mut self.tx })
99        } else {
100            None
101        }
102    }
103
104    fn capabilities(&self) -> Capabilities {
105        let mut caps = Capabilities::default();
106        caps.max_transmission_unit = MTU;
107        caps.max_burst_size = Some(self.tx.len());
108        caps
109    }
110
111    fn link_state(&mut self, cx: &mut Context) -> LinkState {
112        if self.phy.poll_link(&mut self.station_management, cx) {
113            LinkState::Up
114        } else {
115            LinkState::Down
116        }
117    }
118
119    fn hardware_address(&self) -> HardwareAddress {
120        HardwareAddress::Ethernet(self.mac_addr)
121    }
122}
123
124/// `embassy-net` RX token.
125pub struct RxToken<'a, 'd> {
126    rx: &'a mut RDesRing<'d>,
127}
128
129impl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> {
130    fn consume<R, F>(self, f: F) -> R
131    where
132        F: FnOnce(&mut [u8]) -> R,
133    {
134        // NOTE(unwrap): we checked the queue wasn't full when creating the token.
135        let pkt = unwrap!(self.rx.available());
136        let r = f(pkt);
137        self.rx.pop_packet();
138        r
139    }
140}
141
142/// `embassy-net` TX token.
143pub struct TxToken<'a, 'd> {
144    tx: &'a mut TDesRing<'d>,
145}
146
147impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> {
148    fn consume<R, F>(self, len: usize, f: F) -> R
149    where
150        F: FnOnce(&mut [u8]) -> R,
151    {
152        // NOTE(unwrap): we checked the queue wasn't full when creating the token.
153        let pkt = unwrap!(self.tx.available());
154        let r = f(&mut pkt[..len]);
155        self.tx.transmit(len);
156        r
157    }
158}
159
160/// Station Management Interface (SMI) on an ethernet PHY
161pub trait StationManagement {
162    /// Read a register over SMI.
163    fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16;
164    /// Write a register over SMI.
165    fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16);
166}
167
168/// Trait for an Ethernet PHY
169pub trait Phy {
170    /// Reset PHY and wait for it to come out of reset.
171    fn phy_reset<S: StationManagement>(&mut self, sm: &mut S);
172    /// PHY initialisation.
173    fn phy_init<S: StationManagement>(&mut self, sm: &mut S);
174    /// Poll link to see if it is up and FD with 100Mbps
175    fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool;
176}
177
178impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {
179    /// Directly expose the SMI interface used by the Ethernet driver.
180    ///
181    /// This can be used to for example configure special PHY registers for compliance testing.
182    pub fn station_management(&mut self) -> &mut impl StationManagement {
183        &mut self.station_management
184    }
185
186    /// Access the user-supplied `Phy`.
187    pub fn phy(&self) -> &P {
188        &self.phy
189    }
190
191    /// Mutably access the user-supplied `Phy`.
192    pub fn phy_mut(&mut self) -> &mut P {
193        &mut self.phy
194    }
195}
196
197trait SealedInstance {
198    fn regs() -> crate::pac::eth::Eth;
199}
200
201/// Ethernet instance.
202#[allow(private_bounds)]
203pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {}
204
205impl SealedInstance for crate::peripherals::ETH {
206    fn regs() -> crate::pac::eth::Eth {
207        crate::pac::ETH
208    }
209}
210impl Instance for crate::peripherals::ETH {}
211
212pin_trait!(RXClkPin, Instance);
213pin_trait!(TXClkPin, Instance);
214pin_trait!(RefClkPin, Instance);
215pin_trait!(MDIOPin, Instance);
216pin_trait!(MDCPin, Instance);
217pin_trait!(RXDVPin, Instance);
218pin_trait!(CRSPin, Instance);
219pin_trait!(RXD0Pin, Instance);
220pin_trait!(RXD1Pin, Instance);
221pin_trait!(RXD2Pin, Instance);
222pin_trait!(RXD3Pin, Instance);
223pin_trait!(TXD0Pin, Instance);
224pin_trait!(TXD1Pin, Instance);
225pin_trait!(TXD2Pin, Instance);
226pin_trait!(TXD3Pin, Instance);
227pin_trait!(TXEnPin, Instance);