use super::PacketId;
use crate::peripherals::ETHERNET_DMA;
#[cfg(feature = "ptp")]
use super::{PacketIdNotFound, Timestamp};
mod descriptor;
pub use descriptor::{TxDescriptor, TxRingEntry};
#[cfg(any(feature = "ptp", feature = "async-await"))]
use core::task::Poll;
#[derive(Debug, PartialEq)]
pub enum TxError {
WouldBlock,
}
pub struct TxRing<'a> {
entries: &'a mut [TxRingEntry],
next_entry: usize,
}
impl<'ring> TxRing<'ring> {
pub(crate) fn new(entries: &'ring mut [TxRingEntry]) -> Self {
TxRing {
entries,
next_entry: 0,
}
}
pub(crate) fn start(&mut self, eth_dma: ÐERNET_DMA) {
{
let mut previous: Option<&mut TxRingEntry> = None;
for entry in self.entries.iter_mut() {
if let Some(prev_entry) = &mut previous {
prev_entry.setup(Some(entry));
}
previous = Some(entry);
}
if let Some(entry) = &mut previous {
entry.setup(None);
}
}
let ring_ptr = self.entries[0].desc() as *const TxDescriptor;
eth_dma
.dmatdlar
.write(|w| unsafe { w.stl().bits(ring_ptr as u32) });
#[cfg(feature = "fence")]
core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
eth_dma.dmaomr.modify(|_, w| w.st().set_bit());
}
pub(crate) fn stop(&self, eth_dma: ÐERNET_DMA) {
eth_dma.dmaomr.modify(|_, w| w.st().clear_bit());
while self.is_running() {}
}
pub fn next_entry_available(&self) -> bool {
self.entries[self.next_entry].is_available()
}
fn send_next_impl(&mut self) -> Result<usize, TxError> {
let entries_len = self.entries.len();
let entry_num = self.next_entry;
let entry = &mut self.entries[entry_num];
if entry.is_available() {
self.next_entry = (self.next_entry + 1) % entries_len;
Ok(entry_num)
} else {
Err(TxError::WouldBlock)
}
}
pub fn send_next<'borrow>(
&'borrow mut self,
length: usize,
packet_id: Option<PacketId>,
) -> Result<TxPacket<'borrow, 'ring>, TxError> {
let entry = self.send_next_impl()?;
let tx_buffer = self.entries[entry].buffer_mut();
assert!(length <= tx_buffer.len(), "Not enough space in TX buffer");
Ok(TxPacket {
ring: self,
idx: entry,
length,
packet_id,
})
}
#[cfg(feature = "async-await")]
pub async fn prepare_packet<'borrow>(
&'borrow mut self,
length: usize,
packet_id: Option<PacketId>,
) -> TxPacket<'borrow, 'ring> {
let entry = core::future::poll_fn(|ctx| match self.send_next_impl() {
Ok(packet) => Poll::Ready(packet),
Err(_) => {
crate::dma::EthernetDMA::tx_waker().register(ctx.waker());
Poll::Pending
}
})
.await;
let tx_buffer = self.entries[entry].buffer_mut();
assert!(length <= tx_buffer.len(), "Not enough space in TX buffer");
TxPacket {
ring: self,
idx: entry,
length,
packet_id,
}
}
pub(crate) fn demand_poll(&self) {
let eth_dma = unsafe { &*ETHERNET_DMA::ptr() };
eth_dma.dmatpdr.write(|w| {
#[cfg(any(feature = "stm32f4xx-hal", feature = "stm32f7xx-hal"))]
{
w.tpd().poll()
}
#[cfg(feature = "stm32f1xx-hal")]
unsafe {
w.tpd().bits(0)
}
});
}
pub fn is_running(&self) -> bool {
self.running_state().is_running()
}
pub(crate) fn running_state(&self) -> RunningState {
let eth_dma = unsafe { &*ETHERNET_DMA::ptr() };
match eth_dma.dmasr.read().tps().bits() {
0b000 => RunningState::Stopped,
0b001 => RunningState::Running,
0b010 => RunningState::Running,
0b011 => RunningState::Running,
0b100 | 0b101 => RunningState::Reserved,
0b110 => RunningState::Suspended,
_ => RunningState::Unknown,
}
}
}
#[cfg(feature = "ptp")]
impl TxRing<'_> {
fn entry_for_id(&self, id: &PacketId) -> Option<usize> {
self.entries.iter().enumerate().find_map(
|(idx, e)| {
if e.has_packet_id(id) {
Some(idx)
} else {
None
}
},
)
}
fn entry_available(&self, index: usize) -> bool {
self.entries[index].is_available()
}
fn entry_timestamp(&self, index: usize) -> Option<Timestamp> {
self.entries[index].timestamp()
}
pub fn wait_for_timestamp(
&self,
packet_id: &PacketId,
) -> Result<Option<Timestamp>, PacketIdNotFound> {
loop {
if let Poll::Ready(res) = self.poll_timestamp(packet_id) {
return res;
}
}
}
pub fn poll_timestamp(
&self,
packet_id: &PacketId,
) -> Poll<Result<Option<Timestamp>, PacketIdNotFound>> {
let entry = if let Some(entry) = self.entry_for_id(packet_id) {
entry
} else {
return Poll::Ready(Err(PacketIdNotFound));
};
if self.entry_available(entry) {
Poll::Ready(Ok(self.entry_timestamp(entry)))
} else {
Poll::Pending
}
}
#[cfg(feature = "async-await")]
pub async fn timestamp(
&mut self,
packet_id: &PacketId,
) -> Result<Option<Timestamp>, PacketIdNotFound> {
core::future::poll_fn(move |ctx| {
let res = self.poll_timestamp(packet_id);
if res.is_pending() {
crate::dma::EthernetDMA::tx_waker().register(ctx.waker());
}
res
})
.await
}
}
#[derive(Debug, PartialEq)]
pub enum RunningState {
Stopped,
Running,
Reserved,
Suspended,
Unknown,
}
impl RunningState {
pub fn is_running(&self) -> bool {
*self == RunningState::Running
}
}
pub struct TxPacket<'borrow, 'ring> {
ring: &'borrow mut TxRing<'ring>,
idx: usize,
length: usize,
packet_id: Option<PacketId>,
}
impl core::ops::Deref for TxPacket<'_, '_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.ring.entries[self.idx].buffer()[..self.length]
}
}
impl core::ops::DerefMut for TxPacket<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.ring.entries[self.idx].buffer_mut()[..self.length]
}
}
impl TxPacket<'_, '_> {
pub fn send(self) {
drop(self);
}
}
impl Drop for TxPacket<'_, '_> {
fn drop(&mut self) {
self.ring.entries[self.idx].send(self.length, self.packet_id.clone());
self.ring.demand_poll();
}
}