Skip to content

Commit 4729c88

Browse files
committed
Update to smoltcp 0.9
We only perform atomic reads/writes for some operations that previously required a reference to `ETHERNET_DMA`, so we can easily eliminate some lifetimes that made life a lot harder by simply conjuring that peripheral, inspired by embassy-net.
1 parent 2ee46e1 commit 4729c88

File tree

6 files changed

+106
-84
lines changed

6 files changed

+106
-84
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ log = { version = "0.4", optional = true }
3131
defmt = { version = "0.3", optional = true }
3232

3333
[dependencies.smoltcp]
34-
version = "0.8.2"
34+
version = "0.9"
3535
default-features = false
3636
optional = true
3737

@@ -72,7 +72,7 @@ cortex-m-rtic = "1.0"
7272
defmt-rtt = "0.4"
7373
panic-probe = { version = "0.3", features = [ "print-defmt" ] }
7474
systick-monotonic = "1.0"
75-
smoltcp = { version = "0.8", features = [ "medium-ethernet", "proto-ipv4", "socket-udp", "socket-tcp", "defmt" ], default-features = false }
75+
smoltcp = { version = "0.9", features = [ "medium-ethernet", "proto-ipv4", "socket-udp", "socket-tcp", "defmt" ], default-features = false }
7676

7777
[[example]]
7878
name = "pktgen"

src/dma/mod.rs

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -176,38 +176,18 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
176176
/// It stops if the ring is full. Call `recv_next()` to free an
177177
/// entry and to demand poll from the hardware.
178178
pub fn rx_is_running(&self) -> bool {
179-
self.rx_ring.running_state(&self.eth_dma).is_running()
179+
self.rx_ring.running_state().is_running()
180180
}
181181

182-
pub(crate) fn recv_next_impl<'rx_borrow>(
183-
eth_dma: &ETHERNET_DMA,
184-
rx_ring: &'rx_borrow mut RxRing,
185-
rx_packet_id: Option<PacketId>,
186-
) -> Result<RxPacket<'rx_borrow>, RxError> {
187-
rx_ring.recv_next(eth_dma, rx_packet_id.map(|p| p.into()))
188-
}
189-
190-
/// Receive the next packet (if any is ready), or return `None`
182+
/// Receive the next packet (if any is ready), or return [`Err`]
191183
/// immediately.
192184
pub fn recv_next(&mut self, packet_id: Option<PacketId>) -> Result<RxPacket, RxError> {
193-
Self::recv_next_impl(&self.eth_dma, &mut self.rx_ring, packet_id)
185+
self.rx_ring.recv_next(packet_id.map(|p| p.into()))
194186
}
195187

196188
/// Is Tx DMA currently running?
197189
pub fn tx_is_running(&self) -> bool {
198-
self.tx_ring.is_running(&self.eth_dma)
199-
}
200-
201-
pub(crate) fn send_impl<F: FnOnce(&mut [u8]) -> R, R>(
202-
eth_dma: &ETHERNET_DMA,
203-
tx_ring: &mut TxRing,
204-
length: usize,
205-
tx_packet_id: Option<PacketId>,
206-
f: F,
207-
) -> Result<R, TxError> {
208-
let result = tx_ring.send(length, tx_packet_id.map(|p| p.into()), f);
209-
tx_ring.demand_poll(eth_dma);
210-
result
190+
self.tx_ring.is_running()
211191
}
212192

213193
/// Send a packet
@@ -217,7 +197,7 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
217197
packet_id: Option<PacketId>,
218198
f: F,
219199
) -> Result<R, TxError> {
220-
Self::send_impl(&self.eth_dma, &mut self.tx_ring, length, packet_id, f)
200+
self.tx_ring.send(length, packet_id.map(|p| p.into()), f)
221201
}
222202

223203
#[cfg(feature = "ptp")]
@@ -252,6 +232,22 @@ impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
252232
fn collect_timestamps(&mut self) {
253233
self.tx_ring.collect_timestamps();
254234
}
235+
236+
/// Check if there is a packet available for reading.
237+
///
238+
/// If this function returns true, it is guaranteed that the
239+
/// next call to [`EthernetDMA::recv_next`] will return [`Ok`].
240+
pub fn rx_available(&mut self) -> bool {
241+
self.rx_ring.next_entry_available()
242+
}
243+
244+
/// Check if sending a packet now would succeed.
245+
///
246+
/// If this function returns true, it is guaranteed that
247+
/// the next call to [`EthernetDMA::send`] will return [`Ok`]
248+
pub fn tx_available(&mut self) -> bool {
249+
self.tx_ring.next_entry_available()
250+
}
255251
}
256252

257253
/// A summary of the reasons for the interrupt

src/dma/rx.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -352,17 +352,21 @@ impl<'a> RxRing<'a> {
352352
// Start receive
353353
eth_dma.dmaomr.modify(|_, w| w.sr().set_bit());
354354

355-
self.demand_poll(eth_dma);
355+
self.demand_poll();
356356
}
357357

358358
/// Demand that the DMA engine polls the current `RxDescriptor`
359-
/// (when in `RunningState::Stopped`.)
360-
pub fn demand_poll(&self, eth_dma: &ETHERNET_DMA) {
359+
/// (when in [`RunningState::Stopped`].)
360+
pub fn demand_poll(&self) {
361+
// SAFETY: we only perform an atomic write to `dmarpdr`.
362+
let eth_dma = unsafe { &*ETHERNET_DMA::ptr() };
361363
eth_dma.dmarpdr.write(|w| unsafe { w.rpd().bits(1) });
362364
}
363365

364366
/// Get current `RunningState`
365-
pub fn running_state(&self, eth_dma: &ETHERNET_DMA) -> RunningState {
367+
pub fn running_state(&self) -> RunningState {
368+
// SAFETY: we only perform an atomic read of `dmasr`.
369+
let eth_dma = unsafe { &*ETHERNET_DMA::ptr() };
366370
match eth_dma.dmasr.read().rps().bits() {
367371
// Reset or Stop Receive Command issued
368372
0b000 => RunningState::Stopped,
@@ -380,16 +384,24 @@ impl<'a> RxRing<'a> {
380384
}
381385
}
382386

387+
/// Check if we can receive a new packet
388+
pub fn next_entry_available(&self) -> bool {
389+
if !self.running_state().is_running() {
390+
self.demand_poll();
391+
}
392+
393+
!self.entries[self.next_entry].desc().is_owned()
394+
}
395+
383396
/// Receive the next packet (if any is ready), or return `None`
384397
/// immediately.
385398
pub fn recv_next(
386399
&mut self,
387-
eth_dma: &ETHERNET_DMA,
388400
// NOTE(allow): packet_id is unused if ptp is disabled.
389401
#[allow(unused_variables)] packet_id: Option<PacketId>,
390402
) -> Result<RxPacket, RxError> {
391-
if !self.running_state(eth_dma).is_running() {
392-
self.demand_poll(eth_dma);
403+
if !self.running_state().is_running() {
404+
self.demand_poll();
393405
}
394406

395407
let entries_len = self.entries.len();

src/dma/smoltcp_phy.rs

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
use super::{rx::RxPacket, tx::TxError, EthernetDMA};
2-
use core::intrinsics::transmute;
1+
use super::rx::RxRing;
2+
use super::tx::TxRing;
3+
use super::EthernetDMA;
34
use smoltcp::phy::{ChecksumCapabilities, Device, DeviceCapabilities, RxToken, TxToken};
45
use smoltcp::time::Instant;
5-
use smoltcp::Error;
66

77
/// Use this Ethernet driver with [smoltcp](https://github.com/smoltcp-rs/smoltcp)
8-
impl<'a, 'rx, 'tx, 'b> Device<'a> for &'b mut EthernetDMA<'rx, 'tx> {
9-
type RxToken = EthRxToken<'a>;
10-
type TxToken = EthTxToken<'a>;
8+
impl<'a, 'rx, 'tx> Device for &'a mut EthernetDMA<'rx, 'tx> {
9+
type RxToken<'token> = EthRxToken<'token, 'rx> where Self: 'token;
10+
type TxToken<'token> = EthTxToken<'token, 'tx> where Self: 'token;
1111

1212
fn capabilities(&self) -> DeviceCapabilities {
1313
let mut caps = DeviceCapabilities::default();
@@ -17,65 +17,63 @@ impl<'a, 'rx, 'tx, 'b> Device<'a> for &'b mut EthernetDMA<'rx, 'tx> {
1717
caps
1818
}
1919

20-
fn receive(&mut self) -> Option<(Self::RxToken, Self::TxToken)> {
21-
let self_ = unsafe {
22-
// HACK: eliminate lifetimes
23-
transmute::<&mut EthernetDMA<'rx, 'tx>, &mut EthernetDMA<'a, 'a>>(*self)
24-
};
25-
let eth = self_ as *mut EthernetDMA<'a, 'a>;
26-
match self_.recv_next(None) {
27-
Ok(packet) => {
28-
let rx = EthRxToken { packet };
29-
let tx = EthTxToken { eth };
30-
Some((rx, tx))
31-
}
32-
Err(_) => None,
20+
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
21+
if self.tx_available() && self.rx_available() {
22+
let EthernetDMA {
23+
rx_ring, tx_ring, ..
24+
} = self;
25+
26+
let rx = EthRxToken { rx_ring };
27+
28+
let tx = EthTxToken { tx_ring };
29+
Some((rx, tx))
30+
} else {
31+
None
3332
}
3433
}
3534

36-
fn transmit(&mut self) -> Option<Self::TxToken> {
37-
let eth = unsafe {
38-
transmute::<&mut EthernetDMA<'rx, 'tx>, &mut EthernetDMA<'a, 'a>>(*self)
39-
as *mut EthernetDMA<'a, 'a>
40-
};
41-
Some(EthTxToken { eth })
35+
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
36+
if self.tx_available() {
37+
let EthernetDMA { tx_ring, .. } = self;
38+
Some(EthTxToken { tx_ring })
39+
} else {
40+
None
41+
}
4242
}
4343
}
4444

4545
/// An Ethernet RX token that can be consumed in order to receive
4646
/// an ethernet packet.
47-
pub struct EthRxToken<'a> {
48-
packet: RxPacket<'a>,
47+
pub struct EthRxToken<'a, 'rx> {
48+
rx_ring: &'a mut RxRing<'rx>,
4949
}
5050

51-
impl<'a> RxToken for EthRxToken<'a> {
52-
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R, Error>
51+
impl<'dma, 'rx> RxToken for EthRxToken<'dma, 'rx> {
52+
fn consume<R, F>(self, f: F) -> R
5353
where
54-
F: FnOnce(&mut [u8]) -> Result<R, Error>,
54+
F: FnOnce(&mut [u8]) -> R,
5555
{
56-
let result = f(&mut self.packet);
57-
self.packet.free();
56+
// NOTE(unwrap): an `EthRxToken` is only created when `eth.rx_available()`
57+
let mut packet = self.rx_ring.recv_next(None).ok().unwrap();
58+
let result = f(&mut packet);
59+
packet.free();
5860
result
5961
}
6062
}
6163

62-
/// Just a reference to [`Eth`](../struct.EthernetDMA.html) for sending a
63-
/// packet later with [`consume()`](#method.consume).
64-
pub struct EthTxToken<'a> {
65-
eth: *mut EthernetDMA<'a, 'a>,
64+
/// Just a reference to [`EthernetDMA`] for sending a
65+
/// packet later with [`TxToken::consume()`].
66+
pub struct EthTxToken<'a, 'tx> {
67+
tx_ring: &'a mut TxRing<'tx>,
6668
}
6769

68-
impl<'a> TxToken for EthTxToken<'a> {
69-
/// Allocate a [`Buffer`](../struct.Buffer.html), yield with
70-
/// `f(buffer)`, and send it as an Ethernet packet.
71-
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R, Error>
70+
impl<'dma, 'tx> TxToken for EthTxToken<'dma, 'tx> {
71+
fn consume<R, F>(self, len: usize, f: F) -> R
7272
where
73-
F: FnOnce(&mut [u8]) -> Result<R, Error>,
73+
F: FnOnce(&mut [u8]) -> R,
7474
{
75-
let eth = unsafe { &mut *self.eth };
76-
match eth.send(len, None, f) {
77-
Err(TxError::WouldBlock) => Err(Error::Exhausted),
78-
Ok(r) => r,
79-
}
75+
// NOTE(unwrap): an `EthTxToken` is only created if
76+
// there is a descriptor available for sending.
77+
self.tx_ring.send(len, None, f).ok().unwrap()
8078
}
8179
}

src/dma/tx.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ impl<'a> TxRing<'a> {
355355
eth_dma.dmaomr.modify(|_, w| w.st().set_bit());
356356
}
357357

358+
pub fn next_entry_available(&self) -> bool {
359+
!self.entries[self.next_entry].desc().is_owned()
360+
}
361+
358362
pub fn send<F: FnOnce(&mut [u8]) -> R, R>(
359363
&mut self,
360364
length: usize,
@@ -373,6 +377,9 @@ impl<'a> TxRing<'a> {
373377
if self.next_entry >= entries_len {
374378
self.next_entry = 0;
375379
}
380+
381+
self.demand_poll();
382+
376383
Ok(r)
377384
}
378385
None => Err(TxError::WouldBlock),
@@ -381,7 +388,9 @@ impl<'a> TxRing<'a> {
381388

382389
/// Demand that the DMA engine polls the current `TxDescriptor`
383390
/// (when we just transferred ownership to the hardware).
384-
pub fn demand_poll(&self, eth_dma: &ETHERNET_DMA) {
391+
pub fn demand_poll(&self) {
392+
// SAFETY: we only perform an atomic write to `dmatpdr`
393+
let eth_dma = unsafe { &*ETHERNET_DMA::ptr() };
385394
eth_dma.dmatpdr.write(|w| {
386395
#[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))]
387396
{
@@ -396,11 +405,14 @@ impl<'a> TxRing<'a> {
396405
}
397406

398407
/// Is the Tx DMA engine running?
399-
pub fn is_running(&self, eth_dma: &ETHERNET_DMA) -> bool {
400-
self.running_state(eth_dma).is_running()
408+
pub fn is_running(&self) -> bool {
409+
self.running_state().is_running()
401410
}
402411

403-
fn running_state(&self, eth_dma: &ETHERNET_DMA) -> RunningState {
412+
fn running_state(&self) -> RunningState {
413+
// SAFETY: we only perform an atomic read of `dmasr`.
414+
let eth_dma = unsafe { &*ETHERNET_DMA::ptr() };
415+
404416
match eth_dma.dmasr.read().tps().bits() {
405417
// Reset or Stop Transmit Command issued
406418
0b000 => RunningState::Stopped,

src/peripherals.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ mod pac_override_impl {
256256
impl ETHERNET_DMA {
257257
#[doc = r"Pointer to the register block"]
258258
pub const PTR: *const DmaRegisterBlock = 0x4002_9000 as *const _;
259+
260+
pub const fn ptr() -> *const DmaRegisterBlock {
261+
Self::PTR
262+
}
259263
}
260264

261265
impl core::ops::Deref for ETHERNET_DMA {

0 commit comments

Comments
 (0)