Skip to main content

esp_emac/dma/
engine.rs

1// SPDX-License-Identifier: GPL-2.0-or-later OR Apache-2.0
2// Copyright (c) Viacheslav Bocharov <v@baodeep.com> and JetHome (r)
3
4//! DMA engine managing TX/RX descriptor rings and frame I/O.
5//!
6//! The engine owns the descriptor rings and data buffers, providing
7//! a high-level interface for frame transmission and reception without
8//! any register access. Register programming is handled by the caller.
9
10use core::sync::atomic::{fence, Ordering};
11
12use crate::dma::descriptor::{RxDescriptor, TxDescriptor};
13use crate::dma::ring::DescriptorRing;
14use crate::error::EmacError;
15
16/// DMA engine with statically allocated buffers.
17///
18/// # Const generics
19/// - `RX`: number of RX descriptors/buffers
20/// - `TX`: number of TX descriptors/buffers
21/// - `BUF`: buffer size per descriptor (bytes)
22pub struct DmaEngine<const RX: usize, const TX: usize, const BUF: usize> {
23    /// RX descriptor ring.
24    rx_ring: DescriptorRing<RxDescriptor, RX>,
25    /// TX descriptor ring.
26    tx_ring: DescriptorRing<TxDescriptor, TX>,
27    /// RX data buffers.
28    rx_buffers: [[u8; BUF]; RX],
29    /// TX data buffers.
30    tx_buffers: [[u8; BUF]; TX],
31    /// Whether the engine has been initialized.
32    initialized: bool,
33}
34
35impl<const RX: usize, const TX: usize, const BUF: usize> DmaEngine<RX, TX, BUF> {
36    /// Create a new DMA engine (all zeroed, not yet initialized).
37    #[must_use]
38    pub const fn new() -> Self {
39        Self {
40            rx_ring: DescriptorRing::new([const { RxDescriptor::new() }; RX]),
41            tx_ring: DescriptorRing::new([const { TxDescriptor::new() }; TX]),
42            rx_buffers: [[0u8; BUF]; RX],
43            tx_buffers: [[0u8; BUF]; TX],
44            initialized: false,
45        }
46    }
47
48    /// Initialize descriptor chains.
49    ///
50    /// Sets up chained descriptors: each points to its buffer and the next
51    /// descriptor. The last descriptor chains back to the first (circular).
52    ///
53    /// Returns `(rx_base_addr, tx_base_addr)` for programming DMA registers.
54    pub fn init(&mut self) -> (u32, u32) {
55        // Set up RX descriptors: each points to its buffer and next descriptor.
56        for i in 0..RX {
57            let next_idx = (i + 1) % RX;
58            let buffer_ptr = self.rx_buffers[i].as_mut_ptr();
59            let next_desc = self.rx_ring.get(next_idx) as *const RxDescriptor;
60            self.rx_ring
61                .get(i)
62                .setup_chained(buffer_ptr, BUF, next_desc);
63        }
64
65        // Set up TX descriptors: each points to its buffer and next descriptor.
66        for i in 0..TX {
67            let next_idx = (i + 1) % TX;
68            let buffer_ptr = self.tx_buffers[i].as_ptr();
69            let next_desc = self.tx_ring.get(next_idx) as *const TxDescriptor;
70            self.tx_ring.get(i).setup_chained(buffer_ptr, next_desc);
71        }
72
73        self.rx_ring.reset();
74        self.tx_ring.reset();
75        self.initialized = true;
76
77        // Ensure all descriptor setup writes (buffer pointers, chain links,
78        // OWN bits set by reset()) are visible to DMA before the caller
79        // programs the descriptor base address into hardware. Without this
80        // fence the compiler could reorder the return value computation
81        // ahead of the descriptor writes.
82        fence(Ordering::Release);
83
84        let rx_base = self.rx_ring.base_addr() as u32;
85        let tx_base = self.tx_ring.base_addr() as u32;
86        (rx_base, tx_base)
87    }
88
89    /// Check if initialized.
90    #[inline(always)]
91    #[must_use]
92    pub fn is_initialized(&self) -> bool {
93        self.initialized
94    }
95
96    /// Calculate total static memory usage in bytes.
97    #[must_use]
98    pub const fn memory_usage() -> usize {
99        let rx_desc = RX * RxDescriptor::SIZE;
100        let tx_desc = TX * TxDescriptor::SIZE;
101        let rx_buf = RX * BUF;
102        let tx_buf = TX * BUF;
103        rx_desc + tx_desc + rx_buf + tx_buf
104    }
105
106    // ── Transmission ──────────────────────────────────────
107
108    /// Check if there's room to transmit a frame of given length.
109    #[must_use]
110    pub fn can_transmit(&self, len: usize) -> bool {
111        if len == 0 || len > BUF * TX {
112            return false;
113        }
114        let needed = len.div_ceil(BUF);
115        self.tx_available() >= needed
116    }
117
118    /// Count available (CPU-owned) TX descriptors starting from current.
119    #[must_use]
120    pub fn tx_available(&self) -> usize {
121        let mut count = 0;
122        for i in 0..TX {
123            let idx = (self.tx_ring.current_index() + i) % TX;
124            if !self.tx_ring.get(idx).is_owned() {
125                count += 1;
126            } else {
127                break;
128            }
129        }
130        count
131    }
132
133    /// Submit a frame for transmission. Returns number of bytes sent.
134    ///
135    /// For frames larger than `BUF`, uses multiple descriptors (scatter-gather).
136    /// Descriptors are given to DMA in reverse order to prevent a race where
137    /// the DMA starts processing before all descriptors are ready.
138    pub fn transmit(&mut self, data: &[u8]) -> Result<usize, EmacError> {
139        if data.is_empty() {
140            return Err(EmacError::InvalidLength);
141        }
142
143        if data.len() > BUF * TX {
144            return Err(EmacError::FrameTooLarge);
145        }
146
147        let desc_count = data.len().div_ceil(BUF);
148        if self.tx_available() < desc_count {
149            return Err(EmacError::NoDescriptorsAvailable);
150        }
151
152        let current = self.tx_ring.current_index();
153        let mut remaining = data.len();
154        let mut offset = 0usize;
155
156        // Prepare each descriptor with its data chunk.
157        for i in 0..desc_count {
158            let idx = (current + i) % TX;
159            let desc = self.tx_ring.get(idx);
160
161            if desc.is_owned() {
162                return Err(EmacError::DescriptorBusy);
163            }
164
165            let chunk_size = core::cmp::min(remaining, BUF);
166            self.tx_buffers[idx][..chunk_size].copy_from_slice(&data[offset..offset + chunk_size]);
167            desc.prepare(chunk_size, i == 0, i == desc_count - 1);
168
169            remaining -= chunk_size;
170            offset += chunk_size;
171        }
172
173        // Ensure payload writes (buffer data, length, flags via prepare()) are
174        // visible to DMA before any OWN bit handoff. On ESP32 LX6 there is no
175        // write-back data cache, so this is effectively a compiler fence —
176        // it prevents the compiler from reordering the data writes after the
177        // OWN write below. Matches the upstream esp-hal TX commit pattern.
178        fence(Ordering::Release);
179
180        // Give to DMA in reverse order (prevents race condition).
181        for i in (0..desc_count).rev() {
182            let idx = (current + i) % TX;
183            self.tx_ring.get(idx).set_owned();
184        }
185
186        // Order the OWN writes against the subsequent ring-index update and
187        // any TX poll-demand register write the caller may issue.
188        fence(Ordering::Release);
189
190        self.tx_ring.advance_by(desc_count);
191        Ok(data.len())
192    }
193
194    /// Reclaim completed TX descriptors (return from DMA to CPU ownership).
195    ///
196    /// Returns the number of descriptors reclaimed.
197    pub fn tx_reclaim(&mut self) -> usize {
198        // Synchronize with DMA writes before we read descriptor OWN bits.
199        fence(Ordering::Acquire);
200        let mut reclaimed = 0;
201        for i in 0..TX {
202            let idx = (self.tx_ring.current_index() + i) % TX;
203            let desc = self.tx_ring.get(idx);
204            if !desc.is_owned() {
205                reclaimed += 1;
206            }
207        }
208        reclaimed
209    }
210
211    // ── Reception ─────────────────────────────────────────
212
213    /// Check if there is something for `receive` to do on the current
214    /// descriptor. Returns `true` when:
215    ///
216    /// - the current descriptor is CPU-owned and carries an error flag
217    ///   (CRC, overflow, etc.) — `receive` will recycle it as part of
218    ///   its error path, so the driver still needs to be woken; or
219    /// - a complete (possibly multi-descriptor) frame is available, as
220    ///   determined by `peek_frame_length`.
221    ///
222    /// Two earlier implementations both had bugs:
223    /// 1. `!current.is_owned() && current.is_last()` missed multi-
224    ///    descriptor frames whose head wasn't itself `LAST`.
225    /// 2. `peek_frame_length().is_some()` fixed (1) but missed error
226    ///    frames (peek declines to report a length for them), so the
227    ///    embassy-net driver never called `receive` to flush a
228    ///    CRC-failed frame — RX would wedge as the ring filled with
229    ///    untouched error descriptors.
230    #[must_use]
231    pub fn rx_available(&self) -> bool {
232        // Synchronize with DMA writes before we read descriptor state.
233        fence(Ordering::Acquire);
234        let desc = self.rx_ring.current();
235        if desc.is_owned() {
236            return false;
237        }
238        // Errored descriptor still needs draining via `receive` so the
239        // chain doesn't fill up with bad frames; peek would say None.
240        if desc.has_error() {
241            return true;
242        }
243        self.peek_frame_length().is_some()
244    }
245
246    /// Receive a frame into the provided buffer.
247    ///
248    /// Returns the number of bytes received, or `None` if no frame is ready.
249    /// For single-descriptor frames, copies payload from the RX buffer.
250    /// For multi-descriptor frames, copies from each descriptor's buffer.
251    pub fn receive(&mut self, buffer: &mut [u8]) -> Result<Option<usize>, EmacError> {
252        // Synchronize with DMA writes before we read descriptor state and payload.
253        fence(Ordering::Acquire);
254        let first_desc = self.rx_ring.current();
255
256        // Not owned by CPU — no frame ready.
257        if first_desc.is_owned() {
258            return Ok(None);
259        }
260
261        // Single-descriptor frame (common case).
262        if first_desc.is_first() && first_desc.is_last() {
263            if first_desc.has_error() {
264                first_desc.recycle();
265                // RX recycle uses fence-after-OWN (not fence-before-OWN like
266                // TX commit) because at the recycle point no payload writes
267                // precede OWN — buffer/size were set during init. The fence
268                // orders the OWN write against the subsequent ring-index
269                // update and any RX poll-demand register write. Matches the
270                // ESP-IDF emac_esp_dma_receive_frame() pattern (OWN write
271                // then DMA_CACHE_WB; the cache flush is a no-op on ESP32
272                // since there is no write-back data cache).
273                fence(Ordering::Release);
274                self.rx_ring.advance();
275                return Err(EmacError::FrameError);
276            }
277
278            let frame_len = first_desc.payload_length();
279            if buffer.len() < frame_len {
280                first_desc.recycle();
281                fence(Ordering::Release);
282                self.rx_ring.advance();
283                return Err(EmacError::BufferTooSmall);
284            }
285
286            let idx = self.rx_ring.current_index();
287            buffer[..frame_len].copy_from_slice(&self.rx_buffers[idx][..frame_len]);
288            first_desc.recycle();
289            fence(Ordering::Release);
290            self.rx_ring.advance();
291            return Ok(Some(frame_len));
292        }
293
294        // Multi-descriptor frame: must start with first segment.
295        if !first_desc.is_first() {
296            self.flush_rx_frame();
297            return Ok(None);
298        }
299
300        if first_desc.has_error() {
301            self.flush_rx_frame();
302            return Err(EmacError::FrameError);
303        }
304
305        // Walk descriptors to find total frame length.
306        let mut frame_len = 0usize;
307        let mut desc_count = 0usize;
308        let current = self.rx_ring.current_index();
309
310        for i in 0..RX {
311            let idx = (current + i) % RX;
312            let desc = self.rx_ring.get(idx);
313
314            if desc.is_owned() {
315                // Frame not complete yet.
316                return Ok(None);
317            }
318
319            desc_count += 1;
320
321            if desc.is_last() {
322                frame_len = desc.payload_length();
323                break;
324            }
325        }
326
327        if frame_len == 0 {
328            return Ok(None);
329        }
330
331        if buffer.len() < frame_len {
332            self.flush_rx_frame();
333            return Err(EmacError::BufferTooSmall);
334        }
335
336        // Copy data from all descriptors.
337        let mut copied = 0usize;
338        let last_desc_i = desc_count - 1;
339
340        for i in 0..desc_count {
341            let idx = (current + i) % RX;
342            let copy_len = if i == last_desc_i {
343                frame_len - copied
344            } else {
345                BUF
346            };
347            let copy_len = core::cmp::min(copy_len, frame_len - copied);
348
349            if copy_len > 0 {
350                buffer[copied..copied + copy_len]
351                    .copy_from_slice(&self.rx_buffers[idx][..copy_len]);
352                copied += copy_len;
353            }
354            self.rx_ring.get(idx).recycle();
355        }
356
357        // Single fence after the batch of recycles — orders all OWN writes
358        // against the subsequent ring-index update. See the single-descriptor
359        // path above for the full reasoning behind the fence-after-OWN pattern.
360        fence(Ordering::Release);
361        self.rx_ring.advance_by(desc_count);
362        Ok(Some(frame_len))
363    }
364
365    /// Get the length of the next available frame without consuming it.
366    #[must_use]
367    pub fn peek_frame_length(&self) -> Option<usize> {
368        // Synchronize with DMA writes before we read descriptor state.
369        fence(Ordering::Acquire);
370        let desc = self.rx_ring.current();
371
372        if desc.is_owned() {
373            return None;
374        }
375
376        if desc.has_error() {
377            return None;
378        }
379
380        // Complete single-descriptor frame.
381        if desc.is_first() && desc.is_last() {
382            return Some(desc.payload_length());
383        }
384
385        // Multi-descriptor: walk to find the last descriptor.
386        if desc.is_first() {
387            for i in 1..RX {
388                let idx = (self.rx_ring.current_index() + i) % RX;
389                let d = self.rx_ring.get(idx);
390
391                if d.is_owned() {
392                    return None;
393                }
394
395                if d.is_last() {
396                    return Some(d.payload_length());
397                }
398            }
399        }
400
401        None
402    }
403
404    /// Count free RX descriptors (owned by DMA, ready to receive).
405    #[must_use]
406    pub fn rx_free_count(&self) -> usize {
407        let mut count = 0;
408        for i in 0..RX {
409            if self.rx_ring.get(i).is_owned() {
410                count += 1;
411            }
412        }
413        count
414    }
415
416    /// Reset all descriptors to initial state.
417    ///
418    /// Re-initializes the chains and returns base addresses.
419    pub fn reset(&mut self) -> (u32, u32) {
420        self.init()
421    }
422
423    /// Discard the current RX frame (for errors or incomplete frames).
424    fn flush_rx_frame(&mut self) {
425        fence(Ordering::Acquire);
426        loop {
427            let desc = self.rx_ring.current();
428
429            if desc.is_owned() {
430                break;
431            }
432
433            let is_last = desc.is_last();
434            desc.recycle();
435            // Per-iteration fence — each recycle hands OWN to DMA, and we
436            // need ordering against the next loop iteration reading of the
437            // next descriptor's state and the ring advance.
438            fence(Ordering::Release);
439            self.rx_ring.advance();
440
441            if is_last {
442                break;
443            }
444        }
445    }
446
447    /// RX ring base address (for debugging).
448    #[must_use]
449    pub fn rx_ring_base(&self) -> u32 {
450        self.rx_ring.base_addr() as u32
451    }
452
453    /// TX ring base address (for debugging).
454    #[must_use]
455    pub fn tx_ring_base(&self) -> u32 {
456        self.tx_ring.base_addr() as u32
457    }
458
459    /// Current RX ring index.
460    #[must_use]
461    pub fn rx_current_index(&self) -> usize {
462        self.rx_ring.current_index()
463    }
464
465    /// Current TX ring index.
466    #[must_use]
467    pub fn tx_current_index(&self) -> usize {
468        self.tx_ring.current_index()
469    }
470}
471
472impl<const RX: usize, const TX: usize, const BUF: usize> Default for DmaEngine<RX, TX, BUF> {
473    fn default() -> Self {
474        Self::new()
475    }
476}
477
478// SAFETY: DmaEngine can be shared between threads when properly synchronized.
479unsafe impl<const RX: usize, const TX: usize, const BUF: usize> Sync for DmaEngine<RX, TX, BUF> {}
480
481// SAFETY: DmaEngine can be sent between threads.
482unsafe impl<const RX: usize, const TX: usize, const BUF: usize> Send for DmaEngine<RX, TX, BUF> {}
483
484// =============================================================================
485// Tests
486// =============================================================================
487
488#[cfg(test)]
489mod tests {
490    use super::*;
491    use crate::dma::descriptor::bits::rdes0;
492
493    // ── Initialization ────────────────────────────────────
494
495    #[test]
496    fn new_not_initialized() {
497        let engine: DmaEngine<4, 4, 256> = DmaEngine::new();
498        assert!(!engine.is_initialized());
499    }
500
501    #[test]
502    fn init_sets_initialized() {
503        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
504        let _ = engine.init();
505        assert!(engine.is_initialized());
506    }
507
508    #[test]
509    fn init_returns_base_addresses() {
510        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
511        let (rx_base, tx_base) = engine.init();
512
513        // Both addresses must be non-zero.
514        assert_ne!(rx_base, 0);
515        assert_ne!(tx_base, 0);
516
517        // RX and TX rings must have different addresses.
518        assert_ne!(rx_base, tx_base);
519    }
520
521    #[test]
522    fn init_chains_rx_descriptors() {
523        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
524        let (rx_base, _) = engine.init();
525
526        // After init, all RX descriptors are owned by DMA (setup_chained sets OWN).
527        for i in 0..4 {
528            let desc = engine.rx_ring.get(i);
529            assert!(desc.is_owned(), "RX desc {} should be DMA-owned", i);
530            assert_ne!(desc.buffer_addr(), 0, "RX desc {} buffer must be set", i);
531            assert_ne!(desc.next_desc_addr(), 0, "RX desc {} chain must be set", i);
532        }
533
534        // Last descriptor chains back to first (circular).
535        assert_eq!(engine.rx_ring.get(3).next_desc_addr(), rx_base);
536    }
537
538    #[test]
539    fn init_chains_tx_descriptors() {
540        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
541        let (_, tx_base) = engine.init();
542
543        // After init, all TX descriptors are CPU-owned (setup_chained does not set OWN).
544        for i in 0..4 {
545            let desc = engine.tx_ring.get(i);
546            assert!(!desc.is_owned(), "TX desc {} should be CPU-owned", i);
547            assert_ne!(desc.buffer_addr(), 0, "TX desc {} buffer must be set", i);
548            assert_ne!(desc.next_desc_addr(), 0, "TX desc {} chain must be set", i);
549        }
550
551        // Last descriptor chains back to first (circular).
552        assert_eq!(engine.tx_ring.get(3).next_desc_addr(), tx_base);
553    }
554
555    // ── Memory usage ──────────────────────────────────────
556
557    #[test]
558    fn memory_usage_calculation() {
559        // 4 * 32 (rx desc) + 4 * 32 (tx desc) + 4 * 256 (rx buf) + 4 * 256 (tx buf)
560        // = 128 + 128 + 1024 + 1024 = 2304
561        let usage = DmaEngine::<4, 4, 256>::memory_usage();
562        assert_eq!(usage, 2304);
563    }
564
565    #[test]
566    fn memory_usage_scales() {
567        let small = DmaEngine::<2, 2, 512>::memory_usage();
568        let large = DmaEngine::<10, 10, 1600>::memory_usage();
569        assert!(large > small);
570    }
571
572    // ── TX available / can_transmit ───────────────────────
573
574    #[test]
575    fn tx_available_after_init() {
576        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
577        let _ = engine.init();
578        assert_eq!(engine.tx_available(), 4);
579    }
580
581    #[test]
582    fn can_transmit_empty() {
583        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
584        let _ = engine.init();
585        assert!(!engine.can_transmit(0));
586    }
587
588    #[test]
589    fn can_transmit_single_buffer() {
590        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
591        let _ = engine.init();
592        assert!(engine.can_transmit(100));
593        assert!(engine.can_transmit(256));
594    }
595
596    #[test]
597    fn can_transmit_too_large() {
598        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
599        let _ = engine.init();
600        // 4 * 256 = 1024, anything above should fail.
601        assert!(!engine.can_transmit(1025));
602    }
603
604    #[test]
605    fn can_transmit_multi_buffer() {
606        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
607        let _ = engine.init();
608        // 512 bytes needs 2 descriptors of 256 each.
609        assert!(engine.can_transmit(512));
610        // 1024 bytes needs all 4 descriptors.
611        assert!(engine.can_transmit(1024));
612    }
613
614    // ── Transmit ──────────────────────────────────────────
615
616    #[test]
617    fn transmit_single_frame() {
618        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
619        let _ = engine.init();
620
621        let data = [0xABu8; 100];
622        let result = engine.transmit(&data);
623        assert_eq!(result, Ok(100));
624    }
625
626    #[test]
627    fn transmit_sets_own_bit() {
628        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
629        let _ = engine.init();
630
631        let data = [0xABu8; 100];
632        let _ = engine.transmit(&data);
633
634        // After transmit, descriptor 0 should be DMA-owned.
635        assert!(engine.tx_ring.get(0).is_owned());
636        // TX ring should have advanced to index 1.
637        assert_eq!(engine.tx_current_index(), 1);
638    }
639
640    #[test]
641    fn transmit_copies_data_to_buffer() {
642        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
643        let _ = engine.init();
644
645        let data = [0xCA; 64];
646        let _ = engine.transmit(&data);
647
648        // Verify the data was copied to the TX buffer.
649        assert_eq!(&engine.tx_buffers[0][..64], &data[..]);
650    }
651
652    #[test]
653    fn transmit_scatter_gather() {
654        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
655        let _ = engine.init();
656
657        // 400 bytes needs 2 descriptors (256 + 144).
658        let data = [0xBBu8; 400];
659        let result = engine.transmit(&data);
660        assert_eq!(result, Ok(400));
661
662        // Both descriptors should be DMA-owned.
663        assert!(engine.tx_ring.get(0).is_owned());
664        assert!(engine.tx_ring.get(1).is_owned());
665
666        // Ring should have advanced by 2.
667        assert_eq!(engine.tx_current_index(), 2);
668    }
669
670    #[test]
671    fn transmit_when_full() {
672        let mut engine: DmaEngine<2, 2, 256> = DmaEngine::new();
673        let _ = engine.init();
674
675        // Fill both TX slots.
676        let data = [0xAAu8; 100];
677        assert!(engine.transmit(&data).is_ok());
678        assert!(engine.transmit(&data).is_ok());
679
680        // Third transmit should fail.
681        let result = engine.transmit(&data);
682        assert_eq!(result, Err(EmacError::NoDescriptorsAvailable));
683    }
684
685    #[test]
686    fn transmit_empty_data_returns_error() {
687        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
688        let _ = engine.init();
689
690        let result = engine.transmit(&[]);
691        assert_eq!(result, Err(EmacError::InvalidLength));
692    }
693
694    #[test]
695    fn transmit_too_large_returns_error() {
696        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
697        let _ = engine.init();
698
699        let data = [0u8; 2048]; // 4 * 256 = 1024, so 2048 is too large.
700        let result = engine.transmit(&data);
701        assert_eq!(result, Err(EmacError::FrameTooLarge));
702    }
703
704    // ── TX reclaim ────────────────────────────────────────
705
706    #[test]
707    fn tx_reclaim_returns_completed() {
708        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
709        let _ = engine.init();
710
711        // Transmit a frame (gives descriptor 0 to DMA).
712        let _ = engine.transmit(&[0xAA; 100]);
713        assert!(engine.tx_ring.get(0).is_owned());
714
715        // Simulate DMA completion by clearing OWN bit.
716        engine.tx_ring.get(0).clear_owned();
717
718        let reclaimed = engine.tx_reclaim();
719        // All 4 descriptors are CPU-owned now (desc 0 cleared + 1,2,3 never submitted).
720        assert_eq!(reclaimed, 4);
721    }
722
723    // ── RX available ──────────────────────────────────────
724
725    #[test]
726    fn rx_available_no_frame() {
727        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
728        let _ = engine.init();
729
730        // After init, all RX descriptors are DMA-owned.
731        assert!(!engine.rx_available());
732    }
733
734    #[test]
735    fn receive_no_frame_returns_none() {
736        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
737        let _ = engine.init();
738
739        let mut buf = [0u8; 256];
740        let result = engine.receive(&mut buf);
741        assert_eq!(result, Ok(None));
742    }
743
744    // ── Simulated RX ──────────────────────────────────────
745
746    /// Helper: simulate a received single-descriptor frame.
747    ///
748    /// Writes data into the RX buffer at `desc_index`, then sets the
749    /// descriptor's RDES0 to indicate a complete frame (first+last, frame
750    /// length, CPU-owned).
751    fn simulate_rx_frame(engine: &mut DmaEngine<4, 4, 256>, desc_index: usize, data: &[u8]) {
752        // Copy payload into the RX buffer.
753        engine.rx_buffers[desc_index][..data.len()].copy_from_slice(data);
754
755        // Frame length in RDES0 includes the 4-byte CRC.
756        let frame_len_with_crc = (data.len() + 4) as u32;
757        let rdes0_val =
758            rdes0::FIRST_DESC | rdes0::LAST_DESC | (frame_len_with_crc << rdes0::FRAME_LEN_SHIFT);
759        // OWN bit is NOT set — CPU owns it (simulates DMA completion).
760        engine.rx_ring.get(desc_index).set_raw_rdes0(rdes0_val);
761    }
762
763    #[test]
764    fn receive_simulated_frame() {
765        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
766        let _ = engine.init();
767
768        // Simulate receiving a 64-byte frame at descriptor 0.
769        let payload = [0xDE; 64];
770        simulate_rx_frame(&mut engine, 0, &payload);
771
772        let mut buf = [0u8; 256];
773        let result = engine.receive(&mut buf);
774        assert_eq!(result, Ok(Some(64)));
775        assert_eq!(&buf[..64], &payload[..]);
776    }
777
778    #[test]
779    fn receive_advances_ring() {
780        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
781        let _ = engine.init();
782
783        simulate_rx_frame(&mut engine, 0, &[0xAA; 32]);
784        assert_eq!(engine.rx_current_index(), 0);
785
786        let mut buf = [0u8; 256];
787        let _ = engine.receive(&mut buf);
788        assert_eq!(engine.rx_current_index(), 1);
789    }
790
791    #[test]
792    fn receive_recycles_descriptor() {
793        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
794        let _ = engine.init();
795
796        simulate_rx_frame(&mut engine, 0, &[0xAA; 32]);
797        let mut buf = [0u8; 256];
798        let _ = engine.receive(&mut buf);
799
800        // After receive, the descriptor should be recycled (DMA-owned again).
801        assert!(engine.rx_ring.get(0).is_owned());
802    }
803
804    #[test]
805    fn peek_frame_length_returns_size() {
806        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
807        let _ = engine.init();
808
809        simulate_rx_frame(&mut engine, 0, &[0xBB; 100]);
810
811        let len = engine.peek_frame_length();
812        assert_eq!(len, Some(100));
813
814        // peek should NOT consume the frame.
815        assert!(engine.rx_available());
816    }
817
818    #[test]
819    fn peek_frame_length_none_when_empty() {
820        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
821        let _ = engine.init();
822
823        assert_eq!(engine.peek_frame_length(), None);
824    }
825
826    /// Simulate a two-descriptor RX frame: `desc[start]` carries
827    /// `FIRST` + payload[..mid], `desc[start+1]` carries `LAST` +
828    /// payload[mid..] with the total frame length in its RDES0.
829    fn simulate_rx_frame_two_descriptors(
830        engine: &mut DmaEngine<4, 4, 256>,
831        start: usize,
832        chunks: (&[u8], &[u8]),
833    ) {
834        let (a, b) = chunks;
835        engine.rx_buffers[start][..a.len()].copy_from_slice(a);
836        engine.rx_buffers[start + 1][..b.len()].copy_from_slice(b);
837
838        // First descriptor: FIRST, no LAST, no length encoded
839        // (length lives in the LAST descriptor on this Synopsys core).
840        engine.rx_ring.get(start).set_raw_rdes0(rdes0::FIRST_DESC);
841
842        // Last descriptor: LAST, no FIRST, total frame length (incl. CRC).
843        let frame_len_with_crc = (a.len() + b.len() + 4) as u32;
844        let rdes0_val = rdes0::LAST_DESC | (frame_len_with_crc << rdes0::FRAME_LEN_SHIFT);
845        engine.rx_ring.get(start + 1).set_raw_rdes0(rdes0_val);
846    }
847
848    #[test]
849    fn rx_available_true_for_errored_frame() {
850        // Regression: switching `rx_available` to `peek_frame_length`
851        // accidentally hid CPU-owned-but-errored descriptors. The
852        // embassy driver gates on `rx_available`, so an errored frame
853        // would never make it to `receive` and the descriptor would
854        // sit unrecycled until something else drained the chain.
855        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
856        let _ = engine.init();
857
858        // Mark desc[0] as CPU-owned with the ERR_SUMMARY bit set,
859        // FIRST + LAST so it could be a complete (but broken) frame.
860        let rdes0_val = rdes0::FIRST_DESC | rdes0::LAST_DESC | rdes0::ERR_SUMMARY | rdes0::CRC_ERR;
861        engine.rx_ring.get(0).set_raw_rdes0(rdes0_val);
862
863        // peek_frame_length still returns None for errored descriptors
864        // — that's intentional, callers shouldn't get a length back.
865        assert_eq!(engine.peek_frame_length(), None);
866        // …but rx_available must say "yes, drain me" so the driver
867        // wakes up and lets `receive` recycle the descriptor.
868        assert!(
869            engine.rx_available(),
870            "rx_available must signal errored frames so receive can flush them"
871        );
872    }
873
874    #[test]
875    fn rx_available_true_for_multi_descriptor_frame() {
876        // Regression: previous `rx_available` returned `!current.is_owned() &&
877        // current.is_last()`, which silently said "no frame" for any RX
878        // chain where the current descriptor was the FIRST (not LAST)
879        // chunk. Now it must agree with `peek_frame_length`.
880        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
881        let _ = engine.init();
882
883        let chunk_a = [0xAA; 200];
884        let chunk_b = [0xBB; 100];
885        simulate_rx_frame_two_descriptors(&mut engine, 0, (&chunk_a, &chunk_b));
886
887        // `current` points at desc[0] which is FIRST but NOT LAST. The
888        // pre-fix implementation returned `false` here.
889        assert!(
890            engine.rx_available(),
891            "rx_available must report a multi-descriptor frame as ready"
892        );
893        assert_eq!(engine.peek_frame_length(), Some(300));
894    }
895
896    #[test]
897    fn rx_free_count_after_init() {
898        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
899        let _ = engine.init();
900
901        // After init, all RX descriptors are DMA-owned (free for receiving).
902        assert_eq!(engine.rx_free_count(), 4);
903    }
904
905    #[test]
906    fn rx_free_count_after_receive() {
907        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
908        let _ = engine.init();
909
910        simulate_rx_frame(&mut engine, 0, &[0xCC; 48]);
911        // One descriptor is now CPU-owned (received but not yet processed).
912        assert_eq!(engine.rx_free_count(), 3);
913
914        // Process it.
915        let mut buf = [0u8; 256];
916        let _ = engine.receive(&mut buf);
917
918        // After receive, it's recycled back to DMA.
919        assert_eq!(engine.rx_free_count(), 4);
920    }
921
922    #[test]
923    fn receive_buffer_too_small() {
924        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
925        let _ = engine.init();
926
927        simulate_rx_frame(&mut engine, 0, &[0xDD; 200]);
928
929        let mut buf = [0u8; 32]; // Too small for 200 bytes.
930        let result = engine.receive(&mut buf);
931        assert_eq!(result, Err(EmacError::BufferTooSmall));
932
933        // Descriptor should still be recycled after error.
934        assert!(engine.rx_ring.get(0).is_owned());
935    }
936
937    // ── Reset ─────────────────────────────────────────────
938
939    #[test]
940    fn reset_reinitializes() {
941        let mut engine: DmaEngine<4, 4, 256> = DmaEngine::new();
942        let _ = engine.init();
943
944        // Transmit something to change state.
945        let _ = engine.transmit(&[0xAA; 100]);
946        assert_eq!(engine.tx_current_index(), 1);
947
948        // Reset.
949        let (rx_base, tx_base) = engine.reset();
950        assert_ne!(rx_base, 0);
951        assert_ne!(tx_base, 0);
952
953        // Ring indices should be back to 0.
954        assert_eq!(engine.rx_current_index(), 0);
955        assert_eq!(engine.tx_current_index(), 0);
956
957        // All TX descriptors should be CPU-owned.
958        assert_eq!(engine.tx_available(), 4);
959
960        // All RX descriptors should be DMA-owned.
961        assert_eq!(engine.rx_free_count(), 4);
962    }
963
964    // ── Default trait ─────────────────────────────────────
965
966    #[test]
967    fn default_trait() {
968        let d1: DmaEngine<4, 4, 256> = DmaEngine::new();
969        let d2: DmaEngine<4, 4, 256> = DmaEngine::default();
970        assert_eq!(d1.is_initialized(), d2.is_initialized());
971    }
972}