use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use smoltcp::phy::{self, DeviceCapabilities, Medium};
use smoltcp::time::Instant;
use crate::shared::SharedState;
pub struct SmoltcpDevice {
shared: Arc<SharedState>,
mtu: usize,
pending_rx: Option<Vec<u8>>,
pub(crate) frames_emitted: AtomicBool,
}
pub struct SmoltcpRxToken {
frame: Vec<u8>,
}
pub struct SmoltcpTxToken<'a> {
device: &'a mut SmoltcpDevice,
}
impl SmoltcpDevice {
pub fn new(shared: Arc<SharedState>, mtu: usize) -> Self {
Self {
shared,
mtu,
pending_rx: None,
frames_emitted: AtomicBool::new(false),
}
}
pub fn stage_next_frame(&mut self) -> Option<&[u8]> {
if self.pending_rx.is_none() {
self.pending_rx = self.shared.tx_ring.pop();
}
self.pending_rx.as_deref()
}
pub fn drop_staged_frame(&mut self) {
self.pending_rx = None;
}
}
impl phy::Device for SmoltcpDevice {
type RxToken<'a> = SmoltcpRxToken;
type TxToken<'a> = SmoltcpTxToken<'a>;
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let frame = self.pending_rx.take()?;
Some((SmoltcpRxToken { frame }, SmoltcpTxToken { device: self }))
}
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
if self.shared.rx_ring.len() < self.shared.rx_ring.capacity() {
Some(SmoltcpTxToken { device: self })
} else {
None
}
}
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.medium = Medium::Ethernet;
caps.max_transmission_unit = self.mtu + 14;
caps
}
}
impl phy::RxToken for SmoltcpRxToken {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&[u8]) -> R,
{
f(&self.frame)
}
}
impl<'a> phy::TxToken for SmoltcpTxToken<'a> {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let mut buf = vec![0u8; len];
let result = f(&mut buf);
self.device.shared.add_rx_bytes(buf.len());
let _ = self.device.shared.rx_ring.push(buf);
self.device.frames_emitted.store(true, Ordering::Relaxed);
result
}
}