use cortex_m::peripheral::NVIC;
use crate::{peripherals::ETHERNET_DMA, stm32::Interrupt};
#[cfg(feature = "smoltcp-phy")]
mod smoltcp_phy;
#[cfg(feature = "smoltcp-phy")]
pub use smoltcp_phy::*;
#[cfg(feature = "async-await")]
use futures::task::AtomicWaker;
#[cfg(any(feature = "ptp", feature = "async-await"))]
use core::task::Poll;
pub(crate) mod desc;
pub(crate) mod ring;
mod rx;
pub use rx::{RunningState as RxRunningState, RxError, RxPacket, RxRing, RxRingEntry};
mod tx;
pub use tx::{RunningState as TxRunningState, TxError, TxPacket, TxRing, TxRingEntry};
#[cfg(feature = "ptp")]
use crate::ptp::Timestamp;
mod packet_id;
pub use packet_id::PacketId;
pub(crate) const MTU: usize = 1522;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PacketIdNotFound;
pub struct EthernetDMA<'rx, 'tx> {
pub(crate) eth_dma: ETHERNET_DMA,
pub(crate) rx_ring: RxRing<'rx>,
pub(crate) tx_ring: TxRing<'tx>,
#[cfg(feature = "ptp")]
packet_id_counter: u32,
}
impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
pub(crate) fn new(
eth_dma: ETHERNET_DMA,
rx_buffer: &'rx mut [RxRingEntry],
tx_buffer: &'tx mut [TxRingEntry],
) -> Self {
eth_dma.dmabmr.modify(|_, w| w.sr().set_bit());
while eth_dma.dmabmr.read().sr().bit_is_set() {}
eth_dma.dmaomr.modify(|_, w| {
w.dtcefd()
.set_bit()
.rsf()
.set_bit()
.dfrf()
.set_bit()
.tsf()
.set_bit()
.fef()
.set_bit()
.osf()
.set_bit()
});
eth_dma.dmabmr.modify(|_, w| {
#[cfg(not(feature = "stm32f1xx-hal"))]
let w = w.edfe().set_bit();
unsafe {
w.aab()
.set_bit()
.fb()
.set_bit()
.rdp()
.bits(32)
.pbl()
.bits(32)
.pm()
.bits(0b01)
.usp()
.set_bit()
}
});
let mut dma = EthernetDMA {
eth_dma,
rx_ring: RxRing::new(rx_buffer),
tx_ring: TxRing::new(tx_buffer),
#[cfg(feature = "ptp")]
packet_id_counter: 0,
};
dma.rx_ring.start(&dma.eth_dma);
dma.tx_ring.start(&dma.eth_dma);
dma
}
pub fn split(&mut self) -> (&mut RxRing<'rx>, &mut TxRing<'tx>) {
(&mut self.rx_ring, &mut self.tx_ring)
}
#[cfg_attr(
feature = "ptp",
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."
)]
pub fn enable_interrupt(&self) {
self.eth_dma.dmaier.modify(|_, w| {
w
.nise()
.set_bit()
.rie()
.set_bit()
.tie()
.set_bit()
});
unsafe {
NVIC::unmask(Interrupt::ETH);
}
}
pub fn interrupt_handler() -> InterruptReasonSummary {
let eth_dma = unsafe { &*ETHERNET_DMA::ptr() };
let status = eth_dma.dmasr.read();
let status = InterruptReasonSummary {
is_rx: status.rs().bit_is_set(),
is_tx: status.ts().bit_is_set(),
is_error: status.ais().bit_is_set(),
};
eth_dma
.dmasr
.write(|w| w.nis().set_bit().ts().set_bit().rs().set_bit());
#[cfg(feature = "async-await")]
{
if status.is_tx {
EthernetDMA::tx_waker().wake();
}
if status.is_rx {
EthernetDMA::rx_waker().wake();
}
}
status
}
pub fn recv_next(&'_ mut self, packet_id: Option<PacketId>) -> Result<RxPacket<'_>, RxError> {
self.rx_ring.recv_next(packet_id.map(Into::into))
}
pub fn rx_is_running(&self) -> bool {
self.rx_ring.running_state().is_running()
}
pub fn tx_is_running(&self) -> bool {
self.tx_ring.is_running()
}
pub fn send<F>(
&mut self,
length: usize,
packet_id: Option<PacketId>,
f: F,
) -> Result<(), TxError>
where
F: FnOnce(&mut [u8]),
{
let mut tx_packet = self.tx_ring.send_next(length, packet_id)?;
f(&mut tx_packet);
tx_packet.send();
Ok(())
}
pub fn rx_available(&mut self) -> bool {
self.rx_ring.next_entry_available()
}
pub fn tx_available(&mut self) -> bool {
self.tx_ring.next_entry_available()
}
}
impl Drop for EthernetDMA<'_, '_> {
fn drop(&mut self) {
self.tx_ring.stop(&self.eth_dma);
self.rx_ring.stop(&self.eth_dma);
}
}
#[cfg(feature = "async-await")]
impl<'rx, 'tx> EthernetDMA<'rx, 'tx> {
pub(crate) fn rx_waker() -> &'static AtomicWaker {
static WAKER: AtomicWaker = AtomicWaker::new();
&WAKER
}
pub(crate) fn tx_waker() -> &'static AtomicWaker {
static WAKER: AtomicWaker = AtomicWaker::new();
&WAKER
}
pub async fn recv(&'_ mut self, packet_id: Option<PacketId>) -> RxPacket<'_> {
self.rx_ring.recv(packet_id).await
}
pub async fn prepare_packet<'borrow>(
&'borrow mut self,
length: usize,
packet_id: Option<PacketId>,
) -> TxPacket<'borrow, 'tx> {
self.tx_ring.prepare_packet(length, packet_id).await
}
pub async fn rx_or_tx(&mut self) {
let mut polled_once = false;
core::future::poll_fn(|ctx| {
if polled_once {
Poll::Ready(())
} else {
polled_once = true;
EthernetDMA::rx_waker().register(ctx.waker());
EthernetDMA::tx_waker().register(ctx.waker());
Poll::Pending
}
})
.await;
}
}
#[cfg(feature = "ptp")]
impl EthernetDMA<'_, '_> {
pub fn poll_timestamp(
&self,
packet_id: &PacketId,
) -> Poll<Result<Option<Timestamp>, PacketIdNotFound>> {
let tx = self.poll_tx_timestamp(packet_id);
if tx != Poll::Ready(Err(PacketIdNotFound)) {
return tx;
}
Poll::Ready(self.rx_timestamp(packet_id))
}
pub fn rx_timestamp(
&self,
packet_id: &PacketId,
) -> Result<Option<Timestamp>, PacketIdNotFound> {
self.rx_ring.timestamp(packet_id)
}
pub fn wait_for_tx_timestamp(
&self,
packet_id: &PacketId,
) -> Result<Option<Timestamp>, PacketIdNotFound> {
self.tx_ring.wait_for_timestamp(packet_id)
}
pub fn poll_tx_timestamp(
&self,
packet_id: &PacketId,
) -> Poll<Result<Option<Timestamp>, PacketIdNotFound>> {
self.tx_ring.poll_timestamp(packet_id)
}
#[cfg(feature = "async-await")]
pub async fn tx_timestamp(
&mut self,
packet_id: &PacketId,
) -> Result<Option<Timestamp>, PacketIdNotFound> {
self.tx_ring.timestamp(packet_id).await
}
pub fn next_packet_id(&mut self) -> PacketId {
let id = PacketId(self.packet_id_counter);
self.packet_id_counter = self.packet_id_counter.wrapping_add(1);
id
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, Clone, Copy)]
pub struct InterruptReasonSummary {
pub is_rx: bool,
pub is_tx: bool,
pub is_error: bool,
}