stm32_eth/dma/
mod.rs

1//! Ethernet DMA access and configuration.
2
3use cortex_m::peripheral::NVIC;
4
5use crate::{peripherals::ETHERNET_DMA, stm32::Interrupt};
6
7#[cfg(feature = "smoltcp-phy")]
8mod smoltcp_phy;
9#[cfg(feature = "smoltcp-phy")]
10pub use smoltcp_phy::*;
11
12#[cfg(feature = "async-await")]
13use futures::task::AtomicWaker;
14
15#[cfg(any(feature = "ptp", feature = "async-await"))]
16use core::task::Poll;
17
18pub(crate) mod desc;
19
20pub(crate) mod ring;
21
22mod rx;
23pub use rx::{RunningState as RxRunningState, RxError, RxPacket, RxRing, RxRingEntry};
24
25mod tx;
26pub use tx::{RunningState as TxRunningState, TxError, TxPacket, TxRing, TxRingEntry};
27
28#[cfg(feature = "ptp")]
29use crate::ptp::Timestamp;
30
31mod packet_id;
32pub use packet_id::PacketId;
33
34/// From the datasheet: *VLAN Frame maxsize = 1522*
35pub(crate) const MTU: usize = 1522;
36
37#[cfg_attr(feature = "defmt", derive(defmt::Format))]
38#[derive(Clone, Copy, Debug, PartialEq)]
39/// This struct is returned if a packet ID is not associated
40/// with any TX or RX descriptors.
41pub struct PacketIdNotFound;
42
43/// Ethernet DMA.
44pub struct EthernetDMA<'rx, 'tx> {
45    pub(crate) eth_dma: ETHERNET_DMA,
46    pub(crate) rx_ring: RxRing<'rx>,
47    pub(crate) tx_ring: TxRing<'tx>,
48
49    #[cfg(feature = "ptp")]
50    packet_id_counter: u32,
51}
52
53impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
54    /// Create and initialise the ethernet DMA
55    ///
56    /// # Note
57    /// - Make sure that the buffers reside in a memory region that is
58    /// accessible by the peripheral. Core-Coupled Memory (CCM) is
59    /// usually not accessible.
60    pub(crate) fn new(
61        eth_dma: ETHERNET_DMA,
62        rx_buffer: &'rx mut [RxRingEntry],
63        tx_buffer: &'tx mut [TxRingEntry],
64    ) -> Self {
65        // reset DMA bus mode register
66        eth_dma.dmabmr.modify(|_, w| w.sr().set_bit());
67
68        // Wait until done
69        while eth_dma.dmabmr.read().sr().bit_is_set() {}
70
71        // operation mode register
72        eth_dma.dmaomr.modify(|_, w| {
73            // Dropping of TCP/IP checksum error frames disable
74            w.dtcefd()
75                .set_bit()
76                // Receive store and forward
77                .rsf()
78                .set_bit()
79                // Disable flushing of received frames
80                .dfrf()
81                .set_bit()
82                // Transmit store and forward
83                .tsf()
84                .set_bit()
85                // Forward error frames
86                .fef()
87                .set_bit()
88                // Operate on second frame
89                .osf()
90                .set_bit()
91        });
92
93        // bus mode register
94        eth_dma.dmabmr.modify(|_, w| {
95            // For any non-f107 chips, we must use enhanced descriptor format to support checksum
96            // offloading and/or timestamps.
97            #[cfg(not(feature = "stm32f1xx-hal"))]
98            let w = w.edfe().set_bit();
99
100            unsafe {
101                // Address-aligned beats
102                w.aab()
103                    .set_bit()
104                    // Fixed burst
105                    .fb()
106                    .set_bit()
107                    // Rx DMA PBL
108                    .rdp()
109                    .bits(32)
110                    // Programmable burst length
111                    .pbl()
112                    .bits(32)
113                    // Rx Tx priority ratio 2:1
114                    .pm()
115                    .bits(0b01)
116                    // Use separate PBL
117                    .usp()
118                    .set_bit()
119            }
120        });
121
122        let mut dma = EthernetDMA {
123            eth_dma,
124            rx_ring: RxRing::new(rx_buffer),
125            tx_ring: TxRing::new(tx_buffer),
126
127            #[cfg(feature = "ptp")]
128            packet_id_counter: 0,
129        };
130
131        dma.rx_ring.start(&dma.eth_dma);
132        dma.tx_ring.start(&dma.eth_dma);
133
134        dma
135    }
136
137    /// Split the [`EthernetDMA`] into concurrently operating send and
138    /// receive parts.
139    pub fn split(&mut self) -> (&mut RxRing<'rx>, &mut TxRing<'tx>) {
140        (&mut self.rx_ring, &mut self.tx_ring)
141    }
142
143    /// Enable RX and TX interrupts
144    ///
145    /// In your handler you must call
146    /// [`EthernetDMA::interrupt_handler()`] or [`stm32_eth::eth_interrupt_handler`](crate::eth_interrupt_handler)
147    /// to clear interrupt pending bits. Otherwise the interrupt will reoccur immediately.
148    ///
149    /// [`EthernetPTP::interrupt_handler()`]: crate::ptp::EthernetPTP::interrupt_handler
150    #[cfg_attr(
151        feature = "ptp",
152        doc = "If you have PTP enabled, you must also call [`EthernetPTP::interrupt_handler()`] if you wish to make use of the PTP timestamp trigger feature."
153    )]
154    pub fn enable_interrupt(&self) {
155        self.eth_dma.dmaier.modify(|_, w| {
156            w
157                // Normal interrupt summary enable
158                .nise()
159                .set_bit()
160                // Receive Interrupt Enable
161                .rie()
162                .set_bit()
163                // Transmit Interrupt Enable
164                .tie()
165                .set_bit()
166        });
167
168        // Enable ethernet interrupts
169        unsafe {
170            NVIC::unmask(Interrupt::ETH);
171        }
172    }
173
174    /// Handle the DMA parts of the `ETH` interrupt.
175    pub fn interrupt_handler() -> InterruptReasonSummary {
176        // SAFETY: we only perform atomic reads/writes through `eth_dma`.
177        let eth_dma = unsafe { &*ETHERNET_DMA::ptr() };
178
179        let status = eth_dma.dmasr.read();
180
181        let status = InterruptReasonSummary {
182            is_rx: status.rs().bit_is_set(),
183            is_tx: status.ts().bit_is_set(),
184            is_error: status.ais().bit_is_set(),
185        };
186
187        eth_dma
188            .dmasr
189            .write(|w| w.nis().set_bit().ts().set_bit().rs().set_bit());
190
191        #[cfg(feature = "async-await")]
192        {
193            if status.is_tx {
194                EthernetDMA::tx_waker().wake();
195            }
196
197            if status.is_rx {
198                EthernetDMA::rx_waker().wake();
199            }
200        }
201
202        status
203    }
204
205    /// Try to receive a packet.
206    ///
207    /// If no packet is available, this function returns [`Err(RxError::WouldBlock)`](RxError::WouldBlock).
208    ///
209    /// It may also return another kind of [`RxError`].
210    pub fn recv_next(&mut self, packet_id: Option<PacketId>) -> Result<RxPacket, RxError> {
211        self.rx_ring.recv_next(packet_id.map(Into::into))
212    }
213
214    /// Is Rx DMA currently running?
215    ///
216    /// It stops if the ring is full. Call [`EthernetDMA::recv_next()`] to free an
217    /// entry and to demand poll from the hardware.
218    pub fn rx_is_running(&self) -> bool {
219        self.rx_ring.running_state().is_running()
220    }
221
222    /// Is Tx DMA currently running?
223    pub fn tx_is_running(&self) -> bool {
224        self.tx_ring.is_running()
225    }
226
227    /// Try to send a packet with data.
228    ///
229    /// If there are no free TX slots, this function will
230    /// return [`Err(TxError::WouldBlock)`](TxError::WouldBlock).
231    pub fn send<F>(
232        &mut self,
233        length: usize,
234        packet_id: Option<PacketId>,
235        f: F,
236    ) -> Result<(), TxError>
237    where
238        F: FnOnce(&mut [u8]),
239    {
240        let mut tx_packet = self.tx_ring.send_next(length, packet_id)?;
241        f(&mut tx_packet);
242        tx_packet.send();
243        Ok(())
244    }
245
246    /// Check if there is a packet available for reading.
247    ///
248    /// If this function returns true, it is guaranteed that the
249    /// next call to [`EthernetDMA::recv_next`] will return [`Ok`].
250    pub fn rx_available(&mut self) -> bool {
251        self.rx_ring.next_entry_available()
252    }
253
254    /// Check if sending a packet now would succeed.
255    ///
256    /// If this function returns true, it is guaranteed that
257    /// the next call to [`EthernetDMA::send`] will return [`Ok`]
258    pub fn tx_available(&mut self) -> bool {
259        self.tx_ring.next_entry_available()
260    }
261}
262
263impl Drop for EthernetDMA<'_, '_> {
264    // On drop, stop all DMA actions.
265    fn drop(&mut self) {
266        self.tx_ring.stop(&self.eth_dma);
267
268        self.rx_ring.stop(&self.eth_dma);
269    }
270}
271
272#[cfg(feature = "async-await")]
273impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
274    pub(crate) fn rx_waker() -> &'static AtomicWaker {
275        static WAKER: AtomicWaker = AtomicWaker::new();
276        &WAKER
277    }
278
279    pub(crate) fn tx_waker() -> &'static AtomicWaker {
280        static WAKER: AtomicWaker = AtomicWaker::new();
281        &WAKER
282    }
283
284    /// Receive a packet.
285    ///
286    /// See [`RxRing::recv`].
287    pub async fn recv(&mut self, packet_id: Option<PacketId>) -> RxPacket {
288        self.rx_ring.recv(packet_id).await
289    }
290
291    /// Prepare a packet for sending.
292    ///
293    /// See [`TxRing::prepare_packet`].
294    pub async fn prepare_packet<'borrow>(
295        &'borrow mut self,
296        length: usize,
297        packet_id: Option<PacketId>,
298    ) -> TxPacket<'borrow, 'tx> {
299        self.tx_ring.prepare_packet(length, packet_id).await
300    }
301
302    /// Wait for an RX or TX interrupt to have
303    /// occured.
304    pub async fn rx_or_tx(&mut self) {
305        let mut polled_once = false;
306        core::future::poll_fn(|ctx| {
307            if polled_once {
308                Poll::Ready(())
309            } else {
310                polled_once = true;
311                EthernetDMA::rx_waker().register(ctx.waker());
312                EthernetDMA::tx_waker().register(ctx.waker());
313                Poll::Pending
314            }
315        })
316        .await;
317    }
318}
319
320#[cfg(feature = "ptp")]
321impl EthernetDMA<'_, '_> {
322    /// Try to get the timestamp for the given packet ID.
323    ///
324    /// This function will attempt to find both RX and TX timestamps,
325    /// so make sure that the provided packet ID is unique between the two.
326    pub fn poll_timestamp(
327        &self,
328        packet_id: &PacketId,
329    ) -> Poll<Result<Option<Timestamp>, PacketIdNotFound>> {
330        // Check if it's a TX packet
331        let tx = self.poll_tx_timestamp(packet_id);
332
333        if tx != Poll::Ready(Err(PacketIdNotFound)) {
334            return tx;
335        }
336
337        // It's not a TX packet, check if it's an RX packet
338        Poll::Ready(self.rx_timestamp(packet_id))
339    }
340
341    /// Get the RX timestamp for the given packet ID.
342    pub fn rx_timestamp(
343        &self,
344        packet_id: &PacketId,
345    ) -> Result<Option<Timestamp>, PacketIdNotFound> {
346        self.rx_ring.timestamp(packet_id)
347    }
348
349    /// Blockingly wait until the TX timestamp for
350    /// the given ID is available.
351    pub fn wait_for_tx_timestamp(
352        &self,
353        packet_id: &PacketId,
354    ) -> Result<Option<Timestamp>, PacketIdNotFound> {
355        self.tx_ring.wait_for_timestamp(packet_id)
356    }
357
358    /// Poll to check if the TX timestamp for the given
359    /// ID is available.
360    pub fn poll_tx_timestamp(
361        &self,
362        packet_id: &PacketId,
363    ) -> Poll<Result<Option<Timestamp>, PacketIdNotFound>> {
364        self.tx_ring.poll_timestamp(packet_id)
365    }
366
367    /// Get the TX timestamp for the given ID.
368    #[cfg(feature = "async-await")]
369    pub async fn tx_timestamp(
370        &mut self,
371        packet_id: &PacketId,
372    ) -> Result<Option<Timestamp>, PacketIdNotFound> {
373        self.tx_ring.timestamp(packet_id).await
374    }
375
376    /// Get the next packet ID.
377    pub fn next_packet_id(&mut self) -> PacketId {
378        let id = PacketId(self.packet_id_counter);
379        self.packet_id_counter = self.packet_id_counter.wrapping_add(1);
380        id
381    }
382}
383
384/// A summary of the reasons for the interrupt
385/// that occured
386#[cfg_attr(feature = "defmt", derive(defmt::Format))]
387#[derive(Debug, Clone, Copy)]
388pub struct InterruptReasonSummary {
389    /// The interrupt was caused by an RX event.
390    pub is_rx: bool,
391    /// The interrupt was caused by an TX event.
392    pub is_tx: bool,
393    /// The interrupt was caused by an error event.
394    pub is_error: bool,
395}