#![cfg_attr(docsrs, doc(cfg(feature = "smoltcp")))]
use crate::driver::config::State;
use crate::driver::emac::Emac;
use crate::internal::constants::{MAX_FRAME_SIZE, MTU};
use smoltcp::phy::{Checksum, ChecksumCapabilities, Device, DeviceCapabilities, Medium};
use smoltcp::time::Instant;
pub struct EmacRxToken<'a, const RX: usize, const TX: usize, const BUF: usize> {
emac: *mut Emac<RX, TX, BUF>,
_marker: core::marker::PhantomData<&'a mut Emac<RX, TX, BUF>>,
}
impl<'a, const RX: usize, const TX: usize, const BUF: usize> smoltcp::phy::RxToken
for EmacRxToken<'a, RX, TX, BUF>
{
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&[u8]) -> R,
{
let mut buffer = [0u8; MAX_FRAME_SIZE];
let emac = unsafe { &mut *self.emac };
let len = emac.receive(&mut buffer).unwrap_or_default();
f(&buffer[..len])
}
}
pub struct EmacTxToken<'a, const RX: usize, const TX: usize, const BUF: usize> {
emac: *mut Emac<RX, TX, BUF>,
_marker: core::marker::PhantomData<&'a mut Emac<RX, TX, BUF>>,
}
impl<'a, const RX: usize, const TX: usize, const BUF: usize> smoltcp::phy::TxToken
for EmacTxToken<'a, 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> Device for Emac<RX, TX, BUF> {
type RxToken<'a>
= EmacRxToken<'a, RX, TX, BUF>
where
Self: 'a;
type TxToken<'a>
= EmacTxToken<'a, RX, TX, BUF>
where
Self: 'a;
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if self.state() != State::Running {
return None;
}
if !self.rx_available() {
return None;
}
let self_ptr = self as *mut Self;
Some((
EmacRxToken {
emac: self_ptr,
_marker: core::marker::PhantomData,
},
EmacTxToken {
emac: self_ptr,
_marker: core::marker::PhantomData,
},
))
}
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
if self.state() != State::Running {
return None;
}
if !self.tx_ready() {
return None;
}
Some(EmacTxToken {
emac: self as *mut Self,
_marker: core::marker::PhantomData,
})
}
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.medium = Medium::Ethernet;
caps.max_transmission_unit = MTU;
caps.max_burst_size = Some(1);
caps.checksum = ChecksumCapabilities::default();
caps.checksum.ipv4 = Checksum::Both;
caps.checksum.udp = Checksum::Both;
caps.checksum.tcp = Checksum::Both;
caps.checksum.icmpv4 = Checksum::Both;
caps
}
}
pub fn ethernet_address<const RX: usize, const TX: usize, const BUF: usize>(
emac: &Emac<RX, TX, BUF>,
) -> smoltcp::wire::EthernetAddress {
smoltcp::wire::EthernetAddress(*emac.mac_address())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_max_frame_size() {
assert_eq!(MAX_FRAME_SIZE, 1522);
}
#[test]
fn test_mtu() {
assert_eq!(MTU, 1500);
}
#[test]
fn capabilities_medium_is_ethernet() {
let medium = Medium::Ethernet;
assert_eq!(medium, Medium::Ethernet);
}
#[test]
fn capabilities_mtu_matches_constant() {
assert_eq!(MTU, 1500);
}
#[test]
fn checksum_variants_are_constructable() {
let _none = Checksum::None;
let _tx = Checksum::Tx;
let _rx = Checksum::Rx;
let both = Checksum::Both;
assert!(matches!(both, Checksum::Both));
}
#[test]
fn checksum_capabilities_is_constructable() {
let caps = ChecksumCapabilities::default();
let _ipv4 = caps.ipv4;
let _udp = caps.udp;
let _tcp = caps.tcp;
let _icmpv4 = caps.icmpv4;
}
#[test]
fn device_capabilities_default_has_medium_ethernet() {
let caps = DeviceCapabilities::default();
assert_eq!(caps.medium, Medium::Ethernet);
}
#[test]
fn device_capabilities_default_has_no_max_burst() {
let caps = DeviceCapabilities::default();
assert_eq!(caps.max_burst_size, None);
}
#[test]
fn phantom_data_is_zero_sized() {
use core::mem::size_of;
assert_eq!(
size_of::<core::marker::PhantomData<&mut Emac<10, 10, 1600>>>(),
0
);
}
}