stm32_eth/dma/rx/
descriptor.rs

1use crate::dma::{
2    desc::Descriptor,
3    ring::{RingDescriptor, RingEntry},
4};
5
6use crate::dma::PacketId;
7
8#[cfg(feature = "ptp")]
9use crate::ptp::Timestamp;
10
11/// Errors that can occur during RX
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13#[derive(Debug, PartialEq)]
14pub(crate) enum RxDescriptorError {
15    /// The received packet was truncated
16    Truncated,
17    /// An error occured with the DMA
18    DmaError,
19}
20
21/// RX timestamp valid
22/// NOTE(allow): unused if not(feature = "ptp")
23#[allow(unused)]
24const RXDESC_0_TIMESTAMP_VALID: u32 = 1 << 7;
25/// Owned by DMA engine
26const RXDESC_0_OWN: u32 = 1 << 31;
27/// First descriptor
28const RXDESC_0_FS: u32 = 1 << 9;
29/// Last descriptor
30const RXDESC_0_LS: u32 = 1 << 8;
31/// Error summary
32const RXDESC_0_ES: u32 = 1 << 15;
33/// Frame length
34const RXDESC_0_FL_MASK: u32 = 0x3FFF;
35const RXDESC_0_FL_SHIFT: usize = 16;
36
37const RXDESC_1_RBS_SHIFT: usize = 0;
38const RXDESC_1_RBS_MASK: u32 = 0x0fff << RXDESC_1_RBS_SHIFT;
39/// Second address chained
40const RXDESC_1_RCH: u32 = 1 << 14;
41/// End Of Ring
42const RXDESC_1_RER: u32 = 1 << 15;
43
44#[repr(C)]
45/// An RX DMA Descriptor
46pub struct RxDescriptor {
47    desc: Descriptor,
48    buffer1: Option<u32>,
49    next_descriptor: Option<u32>,
50    packet_id: Option<PacketId>,
51    #[cfg(feature = "ptp")]
52    cached_timestamp: Option<Timestamp>,
53}
54
55impl Default for RxDescriptor {
56    fn default() -> Self {
57        Self::new()
58    }
59}
60
61impl RxDescriptor {
62    /// Creates an zeroed RxDescriptor.
63    pub const fn new() -> Self {
64        Self {
65            desc: Descriptor::new(),
66            buffer1: None,
67            next_descriptor: None,
68            packet_id: None,
69            #[cfg(feature = "ptp")]
70            cached_timestamp: None,
71        }
72    }
73
74    /// Is owned by the DMA engine?
75    fn is_owned(&self) -> bool {
76        (self.desc.read(0) & RXDESC_0_OWN) == RXDESC_0_OWN
77    }
78
79    /// Pass ownership to the DMA engine
80    ///
81    /// Overrides old timestamp data
82    pub fn set_owned(&mut self) {
83        self.write_buffer1();
84        self.write_buffer2();
85
86        // "Preceding reads and writes cannot be moved past subsequent writes."
87        #[cfg(feature = "fence")]
88        core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
89        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::Release);
90
91        unsafe {
92            self.desc.write(0, RXDESC_0_OWN);
93        }
94
95        // Used to flush the store buffer as fast as possible to make the buffer available for the
96        // DMA.
97        #[cfg(feature = "fence")]
98        core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
99    }
100
101    fn has_error(&self) -> bool {
102        (self.desc.read(0) & RXDESC_0_ES) == RXDESC_0_ES
103    }
104
105    /// Descriptor contains first buffer of frame
106    fn is_first(&self) -> bool {
107        (self.desc.read(0) & RXDESC_0_FS) == RXDESC_0_FS
108    }
109
110    /// Descriptor contains last buffers of frame
111    fn is_last(&self) -> bool {
112        (self.desc.read(0) & RXDESC_0_LS) == RXDESC_0_LS
113    }
114
115    /// Get PTP timestamps if available
116    #[cfg(feature = "ptp")]
117    pub fn timestamp(&self) -> Option<Timestamp> {
118        #[cfg(not(feature = "stm32f1xx-hal"))]
119        let is_valid = { self.desc.read(0) & RXDESC_0_TIMESTAMP_VALID == RXDESC_0_TIMESTAMP_VALID };
120
121        #[cfg(feature = "stm32f1xx-hal")]
122        // There is no "timestamp valid" indicator bit
123        // on STM32F1XX
124        let is_valid = true;
125
126        let timestamp = Timestamp::from_descriptor(&self.desc);
127
128        if is_valid && self.is_last() {
129            timestamp
130        } else {
131            None
132        }
133    }
134
135    /// Rewrite buffer1 to the last value we wrote to it
136    ///
137    /// In our case, the address of the data buffer for this descriptor
138    ///
139    /// This only has to be done on stm32f107. For f4 and f7, enhanced descriptors
140    /// must be enabled for timestamping support, which we enable by default.
141    fn write_buffer1(&mut self) {
142        let buffer_addr = self
143            .buffer1
144            .expect("Writing buffer1 of an RX descriptor, but `buffer_address` is None");
145
146        unsafe {
147            self.desc.write(2, buffer_addr);
148        }
149    }
150
151    fn set_buffer1(&mut self, buffer: *const u8, len: usize) {
152        self.buffer1 = Some(buffer as u32);
153        self.write_buffer1();
154        unsafe {
155            self.desc.modify(1, |w| {
156                (w & !RXDESC_1_RBS_MASK) | ((len as u32) << RXDESC_1_RBS_SHIFT)
157            });
158        }
159    }
160
161    /// Rewrite buffer2 to the last value we wrote it to
162    ///
163    /// In our case, the address of the next descriptor (may be zero)
164    ///
165    /// This only has to be done on stm32f107. For f4 and f7, enhanced descriptors
166    /// must be enabled for timestamping support, which we enable by default.
167    fn write_buffer2(&mut self) {
168        let addr = self
169            .next_descriptor
170            .expect("Writing buffer2 of an RX descriptor, but `next_descriptor` is None");
171
172        unsafe {
173            self.desc.write(3, addr);
174        }
175    }
176
177    // points to next descriptor (RCH)
178    fn set_buffer2(&mut self, buffer: *const u8) {
179        self.next_descriptor = Some(buffer as u32);
180        self.write_buffer2();
181    }
182
183    fn set_end_of_ring(&mut self) {
184        unsafe {
185            self.desc.modify(1, |w| w | RXDESC_1_RER);
186        }
187    }
188
189    fn get_frame_len(&self) -> usize {
190        ((self.desc.read(0) >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize
191    }
192}
193
194/// An RX DMA Ring Descriptor entry
195pub type RxRingEntry = RingEntry<RxDescriptor>;
196
197impl RingDescriptor for RxDescriptor {
198    fn setup(&mut self, buffer: *const u8, len: usize, next: Option<&Self>) {
199        // Defer this initialization to this function, so we can have `RingEntry` on bss.
200        unsafe {
201            self.desc.write(1, RXDESC_1_RCH);
202        }
203        self.set_buffer1(buffer, len);
204        match next {
205            Some(next) => self.set_buffer2(&next.desc as *const Descriptor as *const u8),
206            None => {
207                #[allow(clippy::zero_ptr)]
208                self.set_buffer2(0 as *const u8);
209                self.set_end_of_ring();
210            }
211        };
212        self.set_owned();
213    }
214}
215
216impl RxRingEntry {
217    /// The initial value for an Rx Ring Entry
218    pub const RX_INIT: Self = Self::new();
219
220    pub(super) fn is_available(&self) -> bool {
221        !self.desc().is_owned()
222    }
223
224    /// Only call this if [`RxRingEntry::is_available`]
225    pub(super) fn recv(&mut self, packet_id: Option<PacketId>) -> Result<usize, RxDescriptorError> {
226        if self.desc().has_error() {
227            self.desc_mut().set_owned();
228            Err(RxDescriptorError::DmaError)
229        } else if self.desc().is_first() && self.desc().is_last() {
230            let frame_len = self.desc().get_frame_len();
231
232            // "Subsequent reads and writes cannot be moved ahead of preceding reads."
233            core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::Acquire);
234
235            #[cfg(feature = "ptp")]
236            {
237                // Cache the PTP timestamp
238                self.desc_mut().cached_timestamp = self.desc().timestamp();
239            }
240
241            // Set the Packet ID for this descriptor.
242            self.desc_mut().packet_id = packet_id;
243
244            Ok(frame_len)
245        } else {
246            self.desc_mut().set_owned();
247            Err(RxDescriptorError::Truncated)
248        }
249    }
250}
251
252#[cfg(feature = "ptp")]
253impl RxRingEntry {
254    pub fn has_packet_id(&self, id: &PacketId) -> bool {
255        Some(id) == self.desc().packet_id.as_ref()
256    }
257
258    pub fn read_timestamp(&self) -> Option<Timestamp> {
259        self.desc().cached_timestamp.clone()
260    }
261}