use core::ops::{Deref, DerefMut};
use alloc::sync::Arc;
use log::trace;
use crate::descriptor::{TxAdvDescCmd, TxAdvDescType};
use super::*;
struct RingInner {
base: Ring<AdvTxDesc>,
finished: usize,
}
impl Deref for RingInner {
type Target = super::Ring<AdvTxDesc>;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for RingInner {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl RingInner {
fn new(base: Ring<AdvTxDesc>) -> Self {
Self { base, finished: 0 }
}
pub fn init(&mut self) -> Result<(), DError> {
debug!("init tx");
let bus_addr = self.base.bus_addr();
self.reg_write(TDBAL, (bus_addr & 0xFFFFFFFF) as u32);
self.reg_write(TDBAH, (bus_addr >> 32) as u32);
let size_bytes = self.base.size_bytes();
self.reg_write(TDLEN, size_bytes as u32);
self.reg_write(TXDCTL, TXDCTL::WTHRESH.val(1).value);
self.reg_write(TDH, 0);
self.reg_write(TDT, 0);
self.reg_write(
TXDCTL,
(TXDCTL::WTHRESH.val(1) + TXDCTL::ENABLE::Enabled).value,
);
wait_for(
|| self.reg_read(TXDCTL) & TXDCTL::ENABLE::Enabled.value > 0,
Duration::from_millis(1),
Some(1000),
)?;
debug!("TX ring initialized successfully");
Ok(())
}
pub fn get_tx_head(&self) -> u32 {
self.reg_read(TDH)
}
pub fn get_tx_tail(&self) -> u32 {
self.reg_read(TDT)
}
pub fn send_packet(&mut self, request: Request) -> Result<(), DError> {
if request.buff.len() > PACKET_SIZE as usize {
return Err(DError::InvalidParameter);
}
trace!("send {}", request.buff.len());
request.buff.confirm_write_all();
let tail = self.get_tx_tail() as usize;
let next_tail = (tail + 1) % self.count();
let head = self.get_tx_head() as usize;
if next_tail == head {
return Err(DError::NoMemory); }
let desc = AdvTxDesc::new(
request.bus_addr(),
request.buff.len(),
TxAdvDescType::Data,
&[
TxAdvDescCmd::EOP,
TxAdvDescCmd::RS,
TxAdvDescCmd::IFCS,
TxAdvDescCmd::DEXT,
],
);
self.descriptors.set(tail, desc);
self.meta_ls[tail].request = Some(request);
mb();
self.reg_write(TDT, next_tail as u32);
Ok(())
}
fn next_finished(&mut self) -> Option<Request> {
let head = self.get_tx_head() as usize;
if self.finished == head {
return None; }
let index = self.finished;
trace!("next_finished index: {index}");
unsafe {
let desc = &self.descriptors[index];
if !desc.write.is_done() {
trace!("TxRing: next_finished descriptor not done at index: {index}");
return None; }
}
let request = self.meta_ls[index]
.request
.take()
.expect("Request should be set");
self.finished = (self.finished + 1) % self.count();
Some(request)
}
}
pub struct TxRing(Arc<UnsafeCell<RingInner>>);
unsafe impl Send for TxRing {}
impl TxRing {
#[allow(clippy::arc_with_non_send_sync)]
pub(crate) fn new(idx: usize, mmio_base: NonNull<u8>, size: usize) -> Result<Self, DError> {
let mut ring_inner = RingInner::new(Ring::new(idx, mmio_base, size, PACKET_SIZE as usize)?);
ring_inner.init()?;
let ring = Arc::new(UnsafeCell::new(ring_inner));
Ok(Self(ring))
}
fn this(&self) -> &RingInner {
unsafe { &*self.0.get() }
}
fn this_mut(&mut self) -> &mut RingInner {
unsafe { &mut *self.0.get() }
}
pub fn send(&mut self, request: Request) -> Result<(), DError> {
self.this_mut().send_packet(request)
}
pub fn request_max_count(&self) -> usize {
self.this().count() - 1
}
pub fn is_queue_full(&self) -> bool {
let head = self.this().get_tx_head() as usize;
let tail = self.this().get_tx_tail() as usize;
(tail + 1) % self.this().count() == head
}
pub fn next_finished(&mut self) -> Option<Request> {
self.this_mut().next_finished()
}
}