# trait for a simple hardware
Ethernet controller could look as follows:
```rust
use smoltcp::phy::{self, DeviceCapabilities, Device, Medium};
use smoltcp::time::Instant;
struct StmPhy {
rx_buffer: [u8; 1536],
tx_buffer: [u8; 1536],
}
impl<'a> StmPhy {
fn new() -> StmPhy {
StmPhy {
rx_buffer: [0; 1536],
tx_buffer: [0; 1536],
}
}
}
impl phy::Device for StmPhy {
type RxToken<'a> = StmPhyRxToken<'a> where Self: 'a;
type TxToken<'a> = StmPhyTxToken<'a> where Self: 'a;
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
Some((StmPhyRxToken(&mut self.rx_buffer[..]),
StmPhyTxToken(&mut self.tx_buffer[..])))
}
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
Some(StmPhyTxToken(&mut self.tx_buffer[..]))
}
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1536;
caps.max_burst_size = Some(1);
caps.medium = Medium::Ethernet;
caps
}
}
struct StmPhyRxToken<'a>(&'a mut [u8]);
impl<'a> phy::RxToken for StmPhyRxToken<'a> {
fn consume<R, F>(self, f: F) -> R
where F: FnOnce(& [u8]) -> R
{
// TODO: receive packet into buffer
let result = f(&self.0);
println!("rx called");
result
}
}
struct StmPhyTxToken<'a>(&'a mut [u8]);
impl<'a> phy::TxToken for StmPhyTxToken<'a> {
fn consume<R, F>(self, len: usize, f: F) -> R
where F: FnOnce(&mut [u8]) -> R
{
let result = f(&mut self.0[..len]);
println!("tx called {}", len);
// TODO: send packet out
result
}
}
```
"##
)]
use crate::time::Instant;
#[cfg(all(
any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
unix
))]
mod sys;
mod fault_injector;
#[cfg(feature = "alloc")]
mod fuzz_injector;
#[cfg(feature = "alloc")]
mod loopback;
mod pcap_writer;
#[cfg(all(feature = "phy-raw_socket", unix))]
mod raw_socket;
mod tracer;
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
mod tuntap_interface;
#[cfg(all(
any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
unix
))]
pub use self::sys::wait;
pub use self::fault_injector::FaultInjector;
#[cfg(feature = "alloc")]
pub use self::fuzz_injector::{FuzzInjector, Fuzzer};
#[cfg(feature = "alloc")]
pub use self::loopback::Loopback;
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
#[cfg(all(feature = "phy-raw_socket", unix))]
pub use self::raw_socket::RawSocket;
pub use self::tracer::{Tracer, TracerDirection, TracerPacket};
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
pub use self::tuntap_interface::TunTapInterface;
#[cfg(feature = "proto-ipv4-fragmentation")]
pub const IPV4_FRAGMENT_PAYLOAD_ALIGNMENT: usize = 8;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)]
#[non_exhaustive]
pub struct PacketMeta {
#[cfg(feature = "packetmeta-id")]
pub id: u32,
}
#[derive(Debug, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Checksum {
#[default]
Both,
Rx,
Tx,
None,
}
impl Checksum {
pub fn rx(&self) -> bool {
match *self {
Checksum::Both | Checksum::Rx => true,
_ => false,
}
}
pub fn tx(&self) -> bool {
match *self {
Checksum::Both | Checksum::Tx => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct ChecksumCapabilities {
pub ipv4: Checksum,
pub udp: Checksum,
pub tcp: Checksum,
#[cfg(feature = "proto-ipv4")]
pub icmpv4: Checksum,
#[cfg(feature = "proto-ipv6")]
pub icmpv6: Checksum,
}
impl ChecksumCapabilities {
pub fn ignored() -> Self {
ChecksumCapabilities {
ipv4: Checksum::None,
udp: Checksum::None,
tcp: Checksum::None,
#[cfg(feature = "proto-ipv4")]
icmpv4: Checksum::None,
#[cfg(feature = "proto-ipv6")]
icmpv6: Checksum::None,
}
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct DeviceCapabilities {
pub medium: Medium,
pub max_transmission_unit: usize,
pub max_burst_size: Option<usize>,
pub checksum: ChecksumCapabilities,
}
impl DeviceCapabilities {
pub fn ip_mtu(&self) -> usize {
match self.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => {
self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len()
}
#[cfg(feature = "medium-ip")]
Medium::Ip => self.max_transmission_unit,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => self.max_transmission_unit, }
}
#[cfg(feature = "proto-ipv4-fragmentation")]
pub fn max_ipv4_fragment_size(&self, ip_header_len: usize) -> usize {
let payload_mtu = self.ip_mtu() - ip_header_len;
payload_mtu - (payload_mtu % IPV4_FRAGMENT_PAYLOAD_ALIGNMENT)
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Medium {
#[cfg(feature = "medium-ethernet")]
Ethernet,
#[cfg(feature = "medium-ip")]
Ip,
#[cfg(feature = "medium-ieee802154")]
Ieee802154,
}
impl Default for Medium {
fn default() -> Medium {
#[cfg(feature = "medium-ethernet")]
return Medium::Ethernet;
#[cfg(all(feature = "medium-ip", not(feature = "medium-ethernet")))]
return Medium::Ip;
#[cfg(all(
feature = "medium-ieee802154",
not(feature = "medium-ip"),
not(feature = "medium-ethernet")
))]
return Medium::Ieee802154;
#[cfg(all(
not(feature = "medium-ip"),
not(feature = "medium-ethernet"),
not(feature = "medium-ieee802154")
))]
return panic!("No medium enabled");
}
}
pub trait Device {
type RxToken<'a>: RxToken
where
Self: 'a;
type TxToken<'a>: TxToken
where
Self: 'a;
fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>>;
fn capabilities(&self) -> DeviceCapabilities;
}
pub trait RxToken {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&[u8]) -> R;
fn meta(&self) -> PacketMeta {
PacketMeta::default()
}
}
pub trait TxToken {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R;
#[allow(unused_variables)]
fn set_meta(&mut self, meta: PacketMeta) {}
}