#![cfg_attr(docsrs, doc(cfg(feature = "embassy-net")))]
use core::{marker::PhantomData, task::Context};
use embassy_net_driver::{
Capabilities, ChecksumCapabilities, Driver, HardwareAddress, LinkState, RxToken, TxToken,
};
use crate::driver::error::Result;
use crate::hal::mdio::MdioBus;
use crate::internal::constants::{MAX_FRAME_SIZE, MTU};
use crate::internal::register::dma::DmaRegs;
use crate::phy::{LinkStatus, PhyDriver};
use crate::sync::primitives::{AtomicWaker, CriticalSectionCell};
use crate::{Emac, InterruptStatus};
pub struct EmbassyEmacState {
rx_waker: AtomicWaker,
tx_waker: AtomicWaker,
link_waker: AtomicWaker,
link_state: CriticalSectionCell<LinkState>,
}
impl EmbassyEmacState {
pub const fn new(initial_link: LinkState) -> Self {
Self {
rx_waker: AtomicWaker::new(),
tx_waker: AtomicWaker::new(),
link_waker: AtomicWaker::new(),
link_state: CriticalSectionCell::new(initial_link),
}
}
pub fn link_state(&self) -> LinkState {
self.link_state.with_ref(|state| *state)
}
pub fn set_link_state(&self, state: LinkState) {
self.link_state.with(|current| {
*current = state;
});
self.link_waker.wake();
}
pub fn update_link_from_phy<M: MdioBus, P: PhyDriver>(
&self,
phy: &mut P,
mdio: &mut M,
) -> Result<Option<LinkStatus>> {
let status = phy.link_status(mdio)?;
if status.is_some() {
self.set_link_state(LinkState::Up);
} else {
self.set_link_state(LinkState::Down);
}
Ok(status)
}
pub fn on_interrupt(&self, status: InterruptStatus) {
if status.rx_complete || status.rx_buf_unavailable {
self.rx_waker.wake();
}
if status.tx_complete || status.tx_buf_unavailable {
self.tx_waker.wake();
}
if status.has_error() {
self.rx_waker.wake();
self.tx_waker.wake();
}
}
pub fn handle_interrupt(&self) {
let status = InterruptStatus::from_raw(DmaRegs::status());
self.on_interrupt(status);
DmaRegs::set_status(status.to_raw());
}
}
pub struct EmbassyEmac<'a, const RX: usize, const TX: usize, const BUF: usize> {
emac: *mut Emac<RX, TX, BUF>,
state: &'a EmbassyEmacState,
_marker: PhantomData<&'a mut Emac<RX, TX, BUF>>,
}
impl<'a, const RX: usize, const TX: usize, const BUF: usize> EmbassyEmac<'a, RX, TX, BUF> {
pub fn new(emac: &'a mut Emac<RX, TX, BUF>, state: &'a EmbassyEmacState) -> Self {
Self {
emac: emac as *mut Emac<RX, TX, BUF>,
state,
_marker: PhantomData,
}
}
pub fn state(&self) -> &EmbassyEmacState {
self.state
}
}
pub struct EmbassyRxToken<'a, const RX: usize, const TX: usize, const BUF: usize> {
emac: *mut Emac<RX, TX, BUF>,
_marker: PhantomData<&'a mut Emac<RX, TX, BUF>>,
}
impl<const RX: usize, const TX: usize, const BUF: usize> RxToken
for EmbassyRxToken<'_, RX, TX, BUF>
{
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let mut buffer = [0u8; MAX_FRAME_SIZE];
let emac = unsafe { &mut *self.emac };
let len = emac.receive(&mut buffer).unwrap_or(0);
f(&mut buffer[..len])
}
}
pub struct EmbassyTxToken<'a, const RX: usize, const TX: usize, const BUF: usize> {
emac: *mut Emac<RX, TX, BUF>,
_marker: PhantomData<&'a mut Emac<RX, TX, BUF>>,
}
impl<const RX: usize, const TX: usize, const BUF: usize> TxToken
for EmbassyTxToken<'_, RX, TX, BUF>
{
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let len = len.min(MAX_FRAME_SIZE);
let mut buffer = [0u8; MAX_FRAME_SIZE];
let result = f(&mut buffer[..len]);
let emac = unsafe { &mut *self.emac };
let _ = emac.transmit(&buffer[..len]);
result
}
}
impl<const RX: usize, const TX: usize, const BUF: usize> Driver for EmbassyEmac<'_, RX, TX, BUF> {
type RxToken<'a>
= EmbassyRxToken<'a, RX, TX, BUF>
where
Self: 'a;
type TxToken<'a>
= EmbassyTxToken<'a, RX, TX, BUF>
where
Self: 'a;
fn receive(&mut self, cx: &mut Context<'_>) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let emac = unsafe { &mut *self.emac };
if !emac.rx_available() {
self.state.rx_waker.register(cx.waker());
if !emac.rx_available() {
return None;
}
}
Some((
EmbassyRxToken {
emac: self.emac,
_marker: PhantomData,
},
EmbassyTxToken {
emac: self.emac,
_marker: PhantomData,
},
))
}
fn transmit(&mut self, cx: &mut Context<'_>) -> Option<Self::TxToken<'_>> {
let emac = unsafe { &mut *self.emac };
if !emac.tx_ready() {
self.state.tx_waker.register(cx.waker());
if !emac.tx_ready() {
return None;
}
}
Some(EmbassyTxToken {
emac: self.emac,
_marker: PhantomData,
})
}
fn link_state(&mut self, cx: &mut Context<'_>) -> LinkState {
self.state.link_waker.register(cx.waker());
self.state.link_state()
}
fn capabilities(&self) -> Capabilities {
let mut caps = Capabilities::default();
caps.max_transmission_unit = MTU;
caps.max_burst_size = Some(1);
caps.checksum = ChecksumCapabilities::default();
caps
}
fn hardware_address(&self) -> HardwareAddress {
let emac = unsafe { &*self.emac };
HardwareAddress::Ethernet(*emac.mac_address())
}
}