use core::sync::atomic::{Ordering, fence};
use crate::{dma::aligned::InternalMemory, reg_access::VolatileCell};
pub const MAX_FRAME_SIZE: usize = 1524;
pub const MIN_RX_FRAME_SIZE: usize = 14;
pub const TDES0_OWN: u32 = 1 << 31;
pub const TDES0_IC: u32 = 1 << 30;
pub const TDES0_LS: u32 = 1 << 29;
pub const TDES0_FS: u32 = 1 << 28;
pub const TDES0_CIC_FULL: u32 = 3 << 22;
pub const TDES0_CHAINED: u32 = 1 << 20;
pub const RDES0_OWN: u32 = 1 << 31;
pub const RDES0_FL_SHIFT: u32 = 16;
pub const RDES0_FL_MASK: u32 = 0x3fff << RDES0_FL_SHIFT;
pub const RDES0_ES: u32 = 1 << 15;
pub const RDES0_FS: u32 = 1 << 9;
pub const RDES0_LS: u32 = 1 << 8;
pub const RDES1_BUF1_SIZE_MASK: u32 = 0x1fff;
pub const RDES1_CHAINED: u32 = 1 << 14;
cfg_select! {
esp32p4 => {
pub const RDES0_ESA: u32 = 1 << 0;
pub const RDES4_IP_HEADER_ERROR: u32 = 1 << 3;
pub const RDES4_IP_PAYLOAD_ERROR: u32 = 1 << 4;
pub const RDES4_IP_CHECKSUM_BYPASSED: u32 = 1 << 5;
pub const RDES4_IPV4_PACKET: u32 = 1 << 6;
pub const RDES4_IPV6_PACKET: u32 = 1 << 7;
}
_ => {}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OwnedBy {
Cpu,
Dma,
}
#[repr(C)]
pub struct TDes {
pub(super) tdes0: VolatileCell<u32>,
pub(super) tdes1: VolatileCell<u32>,
pub(super) buf_addr: VolatileCell<u32>,
pub(super) next_desc: VolatileCell<u32>,
_tdes4: VolatileCell<u32>,
_tdes5: VolatileCell<u32>,
_tdes6: VolatileCell<u32>,
_tdes7: VolatileCell<u32>,
}
impl TDes {
pub const fn new_zeroed() -> Self {
Self {
tdes0: VolatileCell::new(0),
tdes1: VolatileCell::new(0),
buf_addr: VolatileCell::new(0),
next_desc: VolatileCell::new(0),
_tdes4: VolatileCell::new(0),
_tdes5: VolatileCell::new(0),
_tdes6: VolatileCell::new(0),
_tdes7: VolatileCell::new(0),
}
}
pub fn owned_by(&self) -> OwnedBy {
if self.tdes0.get() & TDES0_OWN != 0 {
OwnedBy::Dma
} else {
OwnedBy::Cpu
}
}
pub fn set_owned_by(&mut self, owner: OwnedBy) {
let v = self.tdes0.get();
self.tdes0.set(match owner {
OwnedBy::Cpu => v & !TDES0_OWN,
OwnedBy::Dma => v | TDES0_OWN,
});
}
fn set_chained(&mut self) {
self.tdes0.set(self.tdes0.get() | TDES0_CHAINED);
}
fn set_len_and_flags(&mut self, len: usize) {
self.tdes1.set(len as u32 & RDES1_BUF1_SIZE_MASK);
self.tdes0.set(
(self.tdes0.get() & TDES0_CHAINED) | TDES0_FS | TDES0_LS | TDES0_IC | TDES0_CIC_FULL,
);
}
fn set_buffer_addr(&mut self, addr: *const u8) {
self.buf_addr.set(addr as u32);
}
fn set_next_desc(&mut self, addr: *const TDes) {
self.next_desc.set(addr as u32);
}
}
#[repr(C)]
pub struct RDes {
pub(super) rdes0: VolatileCell<u32>,
pub(super) rdes1: VolatileCell<u32>,
pub(super) buf_addr: VolatileCell<u32>,
pub(super) next_desc: VolatileCell<u32>,
#[cfg_attr(
not(esp32p4),
allow(dead_code, reason = "only read for P4 RX checksum offload")
)]
rdes4: VolatileCell<u32>,
_rdes5: VolatileCell<u32>,
_rdes6: VolatileCell<u32>,
_rdes7: VolatileCell<u32>,
}
impl RDes {
pub const fn new_zeroed() -> Self {
Self {
rdes0: VolatileCell::new(0),
rdes1: VolatileCell::new(0),
buf_addr: VolatileCell::new(0),
next_desc: VolatileCell::new(0),
rdes4: VolatileCell::new(0),
_rdes5: VolatileCell::new(0),
_rdes6: VolatileCell::new(0),
_rdes7: VolatileCell::new(0),
}
}
pub fn owned_by(&self) -> OwnedBy {
if self.rdes0.get() & RDES0_OWN != 0 {
OwnedBy::Dma
} else {
OwnedBy::Cpu
}
}
fn set_rdes0(&mut self, value: u32) {
self.rdes0.set(value);
}
fn set_owned_by(&mut self, owner: OwnedBy) {
let v = self.rdes0.get();
self.set_rdes0(match owner {
OwnedBy::Cpu => v & !RDES0_OWN,
OwnedBy::Dma => v | RDES0_OWN,
});
}
fn is_complete_frame(&self) -> bool {
let s = self.rdes0.get();
s & RDES0_FS != 0 && s & RDES0_LS != 0
}
fn frame_len(&self) -> usize {
((self.rdes0.get() & RDES0_FL_MASK) >> RDES0_FL_SHIFT) as usize
}
#[cfg(esp32p4)]
fn checksum_error(&self) -> bool {
if self.rdes0.get() & RDES0_ESA == 0 {
return false;
}
let ext = self.rdes4.get();
let is_ip = ext & (RDES4_IPV4_PACKET | RDES4_IPV6_PACKET) != 0;
let bypassed = ext & RDES4_IP_CHECKSUM_BYPASSED != 0;
is_ip && !bypassed && ext & (RDES4_IP_HEADER_ERROR | RDES4_IP_PAYLOAD_ERROR) != 0
}
fn configure_buffer(&mut self, size: usize) {
self.rdes1.set(
(self.rdes1.get() & !RDES1_BUF1_SIZE_MASK)
| (size as u32 & RDES1_BUF1_SIZE_MASK)
| RDES1_CHAINED,
);
}
fn set_buffer_addr(&mut self, addr: *const u8) {
self.buf_addr.set(addr as u32);
}
fn set_next_desc(&mut self, addr: *const RDes) {
self.next_desc.set(addr as u32);
}
}
pub struct EthernetDmaStorage<const RX: usize, const TX: usize> {
pub(super) rx_descs: [InternalMemory<RDes>; RX],
pub(super) tx_descs: [InternalMemory<TDes>; TX],
pub(super) rx_bufs: [InternalMemory<[u8; MAX_FRAME_SIZE]>; RX],
pub(super) tx_bufs: [InternalMemory<[u8; MAX_FRAME_SIZE]>; TX],
}
impl<const RX: usize, const TX: usize> Default for EthernetDmaStorage<RX, TX> {
fn default() -> Self {
Self::new()
}
}
impl<const RX: usize, const TX: usize> EthernetDmaStorage<RX, TX> {
pub const fn new() -> Self {
Self {
rx_descs: [const { InternalMemory::new(RDes::new_zeroed()) }; RX],
tx_descs: [const { InternalMemory::new(TDes::new_zeroed()) }; TX],
rx_bufs: [const { InternalMemory::new([0u8; MAX_FRAME_SIZE]) }; RX],
tx_bufs: [const { InternalMemory::new([0u8; MAX_FRAME_SIZE]) }; TX],
}
}
}
unsafe impl<const RX: usize, const TX: usize> Send for EthernetDmaStorage<RX, TX> {}
unsafe impl<const RX: usize, const TX: usize> Sync for EthernetDmaStorage<RX, TX> {}
pub struct TDesRing<'a> {
descriptors: &'a mut [InternalMemory<TDes>],
buffers: &'a mut [InternalMemory<[u8; MAX_FRAME_SIZE]>],
index: usize,
}
impl<'a> TDesRing<'a> {
pub fn new(
descriptors: &'a mut [InternalMemory<TDes>],
buffers: &'a mut [InternalMemory<[u8; MAX_FRAME_SIZE]>],
) -> Self {
assert!(!descriptors.is_empty());
assert_eq!(descriptors.len(), buffers.len());
let mut ring = Self {
descriptors,
buffers,
index: 0,
};
ring.reset();
ring
}
pub(crate) fn len(&self) -> usize {
self.descriptors.len()
}
pub fn reset(&mut self) {
let n = self.descriptors.len();
for i in 0..n {
let next = self.descriptors[(i + 1) % n].as_ptr();
let buf_addr = self.buffers[i].as_ptr().cast::<u8>();
let mut desc = self.descriptors[i].get_mut();
desc.tdes0.set(0);
desc.tdes1.set(0);
desc.set_chained();
desc.set_buffer_addr(buf_addr);
desc.set_next_desc(next);
desc.set_owned_by(OwnedBy::Cpu);
#[cfg(soc_internal_memory_cached)]
self.descriptors[i].get_mut().writeback();
}
self.index = 0;
fence(Ordering::Release);
}
pub fn base_ptr(&self) -> *const TDes {
self.descriptors[0].as_ptr()
}
pub fn transmit(&mut self, frame: &[u8]) -> Result<(), TxError> {
if frame.len() > MAX_FRAME_SIZE {
return Err(TxError::FrameTooLarge);
}
if let Some(buf) = self.available_buf() {
buf[..frame.len()].copy_from_slice(frame);
self.commit(frame.len());
Ok(())
} else {
Err(TxError::RingFull)
}
}
pub fn has_capacity(&self) -> bool {
let desc = self.descriptors[self.index].get_ref();
#[cfg(soc_internal_memory_cached)]
desc.invalidate();
fence(Ordering::Acquire);
desc.owned_by() == OwnedBy::Cpu
}
pub fn available_buf(&mut self) -> Option<&mut [u8; MAX_FRAME_SIZE]> {
if self.has_capacity() {
let idx = self.index;
Some(self.buffers[idx].get_mut().into_inner())
} else {
None
}
}
pub fn commit(&mut self, len: usize) {
let idx = self.index;
let n = self.descriptors.len();
#[cfg(soc_internal_memory_cached)]
self.buffers[idx].get_mut().writeback();
let mut desc = self.descriptors[idx].get_mut();
desc.set_len_and_flags(len);
desc.set_owned_by(OwnedBy::Dma);
#[cfg(soc_internal_memory_cached)]
desc.writeback();
fence(Ordering::Release);
self.index = (idx + 1) % n;
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TxError {
RingFull,
FrameTooLarge,
}
pub struct RDesRing<'a> {
descriptors: &'a mut [InternalMemory<RDes>],
buffers: &'a mut [InternalMemory<[u8; MAX_FRAME_SIZE]>],
index: usize,
}
impl<'a> RDesRing<'a> {
pub fn new(
descriptors: &'a mut [InternalMemory<RDes>],
buffers: &'a mut [InternalMemory<[u8; MAX_FRAME_SIZE]>],
) -> Self {
assert!(!descriptors.is_empty());
assert_eq!(descriptors.len(), buffers.len());
let mut ring = Self {
descriptors,
buffers,
index: 0,
};
ring.reset();
ring
}
pub fn reset(&mut self) {
let n = self.descriptors.len();
for i in 0..n {
let next = self.descriptors[(i + 1) % n].as_ptr();
let buf_addr = self.buffers[i].as_ptr().cast::<u8>();
#[cfg(soc_internal_memory_cached)]
self.buffers[i].get_mut().invalidate();
let mut desc = self.descriptors[i].get_mut();
desc.rdes0.set(0);
desc.configure_buffer(MAX_FRAME_SIZE);
desc.set_buffer_addr(buf_addr);
desc.set_next_desc(next);
desc.set_owned_by(OwnedBy::Dma);
#[cfg(soc_internal_memory_cached)]
desc.writeback();
}
self.index = 0;
fence(Ordering::Release);
}
pub fn base_ptr(&self) -> *const RDes {
self.descriptors[0].as_ptr()
}
pub fn receive(&mut self) -> Option<&mut [u8]> {
loop {
let idx = self.index;
let len = {
let desc = self.descriptors[idx].get_ref();
#[cfg(soc_internal_memory_cached)]
desc.invalidate();
fence(Ordering::Acquire);
if desc.owned_by() != OwnedBy::Cpu {
return None;
}
let status = desc.rdes0.get();
let is_complete = desc.is_complete_frame();
let frame_len = desc.frame_len();
let checksum_bad = cfg_select! {
esp32p4 => desc.checksum_error(),
_ => false,
};
if status & RDES0_ES != 0 || !is_complete || checksum_bad {
None
} else {
let len = frame_len.saturating_sub(4);
if (MIN_RX_FRAME_SIZE..=MAX_FRAME_SIZE).contains(&len) {
Some(len)
} else {
None
}
}
};
let Some(len) = len else {
self.recycle_current();
continue;
};
#[cfg(soc_internal_memory_cached)]
self.buffers[idx].get_mut().invalidate();
fence(Ordering::Acquire);
return Some(&mut self.buffers[idx].get_mut().into_inner()[..len]);
}
}
pub fn pop(&mut self) {
self.recycle_current();
}
pub fn has_packet(&self) -> bool {
let desc = self.descriptors[self.index].get_ref();
#[cfg(soc_internal_memory_cached)]
desc.invalidate();
fence(Ordering::Acquire);
desc.owned_by() == OwnedBy::Cpu
}
fn recycle_current(&mut self) {
let idx = self.index;
let n = self.descriptors.len();
let mut desc = self.descriptors[idx].get_mut();
desc.set_rdes0(RDES0_OWN);
#[cfg(soc_internal_memory_cached)]
desc.writeback();
fence(Ordering::Release);
self.index = (idx + 1) % n;
}
}