1
1
//! Ethernet DMA access and configuration.
2
2
3
- use core:: borrow :: Borrow ;
3
+ use core:: task :: Poll ;
4
4
5
5
use cortex_m:: peripheral:: NVIC ;
6
+ use futures:: task:: AtomicWaker ;
6
7
7
8
use crate :: { peripherals:: ETHERNET_DMA , stm32:: Interrupt } ;
8
9
@@ -16,12 +17,10 @@ pub(crate) mod desc;
16
17
pub ( crate ) mod ring;
17
18
18
19
mod rx;
19
- use rx:: RxRing ;
20
- pub use rx:: { RxError , RxPacket , RxRingEntry } ;
20
+ pub use rx:: { RunningState as RxRunningState , RxError , RxPacket , RxRing , RxRingEntry } ;
21
21
22
22
mod tx;
23
- use tx:: TxRing ;
24
- pub use tx:: { TxError , TxRingEntry } ;
23
+ pub use tx:: { RunningState as TxRunningState , TxError , TxPacket , TxRing , TxRingEntry } ;
25
24
26
25
#[ cfg( feature = "ptp" ) ]
27
26
use crate :: ptp:: Timestamp ;
@@ -32,17 +31,11 @@ pub use packet_id::PacketId;
32
31
/// From the datasheet: *VLAN Frame maxsize = 1522*
33
32
pub ( crate ) const MTU : usize = 1522 ;
34
33
35
- /// An error that can occur when retrieving a timestamp from an
36
- /// RX or TX descriptor handled by the DMA.
37
- #[ cfg( feature = "ptp" ) ]
38
34
#[ cfg_attr( feature = "defmt" , derive( defmt:: Format ) ) ]
39
- pub enum TimestampError {
40
- /// The descriptor with the given packet ID has not been
41
- /// timestamped yet.
42
- NotYetTimestamped ,
43
- /// No active descriptors have the given packet ID.
44
- IdNotFound ,
45
- }
35
+ #[ derive( Clone , Copy , Debug , PartialEq ) ]
36
+ /// This struct is returned if a packet ID is not associated
37
+ /// with any TX or RX descriptors.
38
+ pub struct PacketIdNotFound ;
46
39
47
40
/// Ethernet DMA.
48
41
pub struct EthernetDMA < ' rx , ' tx > {
@@ -122,8 +115,8 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
122
115
123
116
let mut dma = EthernetDMA {
124
117
eth_dma,
125
- rx_ring : RxRing :: new ( rx_buffer) ,
126
- tx_ring : TxRing :: new ( tx_buffer) ,
118
+ rx_ring : RxRing :: new ( rx_buffer, EthernetDMA :: rx_waker ( ) ) ,
119
+ tx_ring : TxRing :: new ( tx_buffer, EthernetDMA :: tx_waker ( ) ) ,
127
120
} ;
128
121
129
122
dma. rx_ring . start ( & dma. eth_dma ) ;
@@ -132,10 +125,26 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
132
125
dma
133
126
}
134
127
128
+ pub ( crate ) fn rx_waker ( ) -> & ' static AtomicWaker {
129
+ static WAKER : AtomicWaker = AtomicWaker :: new ( ) ;
130
+ & WAKER
131
+ }
132
+
133
+ pub ( crate ) fn tx_waker ( ) -> & ' static AtomicWaker {
134
+ static WAKER : AtomicWaker = AtomicWaker :: new ( ) ;
135
+ & WAKER
136
+ }
137
+
138
+ /// Split the [`EthernetDMA`] into concurrently operating send and
139
+ /// receive parts.
140
+ pub fn split ( & mut self ) -> ( & mut RxRing < ' rx > , & mut TxRing < ' tx > ) {
141
+ ( & mut self . rx_ring , & mut self . tx_ring )
142
+ }
143
+
135
144
/// Enable RX and TX interrupts
136
145
///
137
146
/// In your handler you must call
138
- /// [`eth_interrupt_handler ()`](fn.eth_interrupt_handler.html) to
147
+ /// [`EthernetDMA::interrupt_handler ()`] to
139
148
/// clear interrupt pending bits. Otherwise the interrupt will
140
149
/// reoccur immediately.
141
150
pub fn enable_interrupt ( & self ) {
@@ -158,79 +167,78 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
158
167
}
159
168
}
160
169
161
- /// Calls [`eth_interrupt_handler()`]
162
- #[ cfg_attr(
163
- feature = "ptp" ,
164
- doc = " and collects/caches TX timestamps. (See [`EthernetDMA::get_timestamp_for_id`] for retrieval)"
165
- ) ]
166
- pub fn interrupt_handler ( & mut self ) -> InterruptReasonSummary {
167
- let eth_dma = & self . eth_dma ;
168
- let status = eth_interrupt_handler_impl ( eth_dma) ;
169
- #[ cfg( feature = "ptp" ) ]
170
- self . collect_timestamps ( ) ;
170
+ /// Handle the DMA parts of the ETH interrupt.
171
+ pub fn interrupt_handler ( ) -> InterruptReasonSummary {
172
+ // SAFETY: we only perform atomic reads/writes through `eth_dma`.
173
+ let eth_dma = unsafe { & * ETHERNET_DMA :: ptr ( ) } ;
174
+
175
+ let status = eth_dma. dmasr . read ( ) ;
176
+
177
+ let status = InterruptReasonSummary {
178
+ is_rx : status. rs ( ) . bit_is_set ( ) ,
179
+ is_tx : status. ts ( ) . bit_is_set ( ) ,
180
+ is_error : status. ais ( ) . bit_is_set ( ) ,
181
+ } ;
182
+
183
+ eth_dma
184
+ . dmasr
185
+ . write ( |w| w. nis ( ) . set_bit ( ) . ts ( ) . set_bit ( ) . rs ( ) . set_bit ( ) ) ;
186
+
187
+ if status. is_tx {
188
+ EthernetDMA :: tx_waker ( ) . wake ( ) ;
189
+ }
190
+
191
+ if status. is_rx {
192
+ EthernetDMA :: rx_waker ( ) . wake ( ) ;
193
+ }
194
+
171
195
status
172
196
}
173
197
198
+ /// Try to receive a packet.
199
+ ///
200
+ /// If no packet is available, this function returns [`Err(RxError::WouldBlock)`](RxError::WouldBlock).
201
+ ///
202
+ /// It may also return another kind of [`RxError`].
203
+ pub fn recv_next ( & mut self , packet_id : Option < PacketId > ) -> Result < RxPacket , RxError > {
204
+ self . rx_ring . recv_next ( packet_id. map ( Into :: into) )
205
+ }
206
+
207
+ /// Receive a packet.
208
+ pub async fn recv ( & mut self ) -> RxPacket {
209
+ self . rx_ring . recv ( ) . await
210
+ }
211
+
174
212
/// Is Rx DMA currently running?
175
213
///
176
- /// It stops if the ring is full. Call ` recv_next()` to free an
214
+ /// It stops if the ring is full. Call [`EthernetDMA:: recv_next()`] to free an
177
215
/// entry and to demand poll from the hardware.
178
216
pub fn rx_is_running ( & self ) -> bool {
179
217
self . rx_ring . running_state ( ) . is_running ( )
180
218
}
181
219
182
- /// Receive the next packet (if any is ready), or return [`Err`]
183
- /// immediately.
184
- pub fn recv_next ( & mut self , packet_id : Option < PacketId > ) -> Result < RxPacket , RxError > {
185
- self . rx_ring . recv_next ( packet_id. map ( |p| p. into ( ) ) )
186
- }
187
-
188
220
/// Is Tx DMA currently running?
189
221
pub fn tx_is_running ( & self ) -> bool {
190
222
self . tx_ring . is_running ( )
191
223
}
192
224
193
- /// Send a packet
194
- pub fn send < F : FnOnce ( & mut [ u8 ] ) -> R , R > (
225
+ /// Try to send a packet with data.
226
+ ///
227
+ /// If there are no free TX slots, this function will
228
+ /// return [`Err(TxError::WouldBlock)`](TxError::WouldBlock).
229
+ pub fn send < F > (
195
230
& mut self ,
196
231
length : usize ,
197
232
packet_id : Option < PacketId > ,
198
233
f : F ,
199
- ) -> Result < R , TxError > {
200
- self . tx_ring . send ( length, packet_id. map ( |p| p. into ( ) ) , f)
201
- }
202
-
203
- #[ cfg( feature = "ptp" ) ]
204
- /// Get a timestamp for the given ID
205
- ///
206
- /// Both RX and TX timestamps can be obtained reliably as follows:
207
- /// 1. When an ethernet interrupt occurs, call [`EthernetDMA::interrupt_handler`] (_not_ [`eth_interrupt_handler`]).
208
- /// 2. Before calling [`interrupt_handler`](EthernetDMA::interrupt_handler) again, retrieve timestamps of sent and received frames using this function.
209
- ///
210
- /// Retrieving RX timestamps can also be done using [`RxPacket::timestamp`].
211
- pub fn get_timestamp_for_id < ' a , PKT > (
212
- & mut self ,
213
- packet_id : PKT ,
214
- ) -> Result < Timestamp , TimestampError >
234
+ ) -> Result < ( ) , TxError >
215
235
where
216
- PKT : Into < PacketId > ,
236
+ F : FnOnce ( & mut [ u8 ] ) ,
217
237
{
218
- let Self {
219
- tx_ring, rx_ring, ..
220
- } = self ;
221
-
222
- let internal_packet_id = packet_id. into ( ) ;
223
-
224
- tx_ring
225
- . get_timestamp_for_id ( internal_packet_id. clone ( ) )
226
- . or_else ( |_| rx_ring. get_timestamp_for_id ( internal_packet_id) )
227
- }
228
-
229
- /// Collect the timestamps from the TX descriptor
230
- /// ring
231
- #[ cfg( feature = "ptp" ) ]
232
- fn collect_timestamps ( & mut self ) {
233
- self . tx_ring . collect_timestamps ( ) ;
238
+ let mut tx_packet = self . tx_ring . send_next ( length, packet_id) ?;
239
+ f ( & mut tx_packet) ;
240
+ tx_packet. send ( ) ;
241
+ Ok ( ( ) )
234
242
}
235
243
236
244
/// Check if there is a packet available for reading.
@@ -250,6 +258,62 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
250
258
}
251
259
}
252
260
261
+ #[ cfg( feature = "ptp" ) ]
262
+ impl EthernetDMA < ' _ , ' _ > {
263
+ /// Try to get the timestamp for the given packet ID.
264
+ ///
265
+ /// This function will attempt to find both RX and TX timestamps,
266
+ /// so make sure that the provided packet ID is unique between the two.
267
+ pub fn poll_timestamp (
268
+ & self ,
269
+ packet_id : & PacketId ,
270
+ ) -> Poll < Result < Option < Timestamp > , PacketIdNotFound > > {
271
+ // Check if it's a TX packet
272
+ let tx = self . poll_tx_timestamp ( packet_id) ;
273
+
274
+ if tx != Poll :: Ready ( Err ( PacketIdNotFound ) ) {
275
+ return tx;
276
+ }
277
+
278
+ // It's not a TX packet, check if it's an RX packet
279
+ Poll :: Ready ( self . rx_timestamp ( packet_id) )
280
+ }
281
+
282
+ /// Get the RX timestamp for the given packet ID.
283
+ pub fn rx_timestamp (
284
+ & self ,
285
+ packet_id : & PacketId ,
286
+ ) -> Result < Option < Timestamp > , PacketIdNotFound > {
287
+ self . rx_ring . timestamp ( packet_id)
288
+ }
289
+
290
+ /// Blockingly wait until the TX timestamp for
291
+ /// the given ID is available.
292
+ pub fn wait_for_tx_timestamp (
293
+ & self ,
294
+ packet_id : & PacketId ,
295
+ ) -> Result < Option < Timestamp > , PacketIdNotFound > {
296
+ self . tx_ring . wait_for_timestamp ( packet_id)
297
+ }
298
+
299
+ /// Poll to check if the TX timestamp for the given
300
+ /// ID is available.
301
+ pub fn poll_tx_timestamp (
302
+ & self ,
303
+ packet_id : & PacketId ,
304
+ ) -> Poll < Result < Option < Timestamp > , PacketIdNotFound > > {
305
+ self . tx_ring . poll_timestamp ( packet_id)
306
+ }
307
+
308
+ /// Get the TX timestamp for the given ID.
309
+ pub async fn tx_timestamp (
310
+ & mut self ,
311
+ packet_id : & PacketId ,
312
+ ) -> Result < Option < Timestamp > , PacketIdNotFound > {
313
+ self . tx_ring . timestamp ( packet_id) . await
314
+ }
315
+ }
316
+
253
317
/// A summary of the reasons for the interrupt
254
318
/// that occured
255
319
#[ cfg_attr( feature = "defmt" , derive( defmt:: Format ) ) ]
@@ -262,30 +326,3 @@ pub struct InterruptReasonSummary {
262
326
/// The interrupt was caused by an error event.
263
327
pub is_error : bool ,
264
328
}
265
-
266
- /// The handler for `ETH` interrupts.
267
- ///
268
- /// There are two ways to call this:
269
- ///
270
- /// * Indirectly by using [`EthernetDMA::interrupt_handler`] driver instance that your interrupt handler has access to.
271
- /// * By unsafely getting `Peripherals`.
272
- pub fn eth_interrupt_handler ( eth_dma : & crate :: hal:: pac:: ETHERNET_DMA ) -> InterruptReasonSummary {
273
- let eth_dma: & ETHERNET_DMA = eth_dma. borrow ( ) ;
274
- eth_interrupt_handler_impl ( eth_dma)
275
- }
276
-
277
- fn eth_interrupt_handler_impl ( eth_dma : & ETHERNET_DMA ) -> InterruptReasonSummary {
278
- let status = eth_dma. dmasr . read ( ) ;
279
-
280
- let status = InterruptReasonSummary {
281
- is_rx : status. rs ( ) . bit_is_set ( ) ,
282
- is_tx : status. ts ( ) . bit_is_set ( ) ,
283
- is_error : status. ais ( ) . bit_is_set ( ) ,
284
- } ;
285
-
286
- eth_dma
287
- . dmasr
288
- . write ( |w| w. nis ( ) . set_bit ( ) . ts ( ) . set_bit ( ) . rs ( ) . set_bit ( ) ) ;
289
-
290
- status
291
- }
0 commit comments