strate_net_silo/
device.rs1use core::sync::atomic::{AtomicUsize, Ordering};
2
3use smoltcp::{
4 phy::{self, Device, DeviceCapabilities, Medium},
5 time::Instant,
6};
7use strate_net::syscalls::{net_recv, net_send, proc_yield, Error};
8
9use crate::{log, log_errno, sleep_micros};
10
11pub(crate) const MAX_FRAME_SIZE: usize = 1514;
12
13static RX_ERR_LOG_BUDGET: AtomicUsize = AtomicUsize::new(64);
14static TX_ERR_LOG_BUDGET: AtomicUsize = AtomicUsize::new(64);
15static TX_SUCCESS_COUNT: AtomicUsize = AtomicUsize::new(0);
16static TX_DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
17
18const NET_SEND_RETRY_LIMIT: usize = 8;
19
20pub(crate) struct Strat9NetDevice;
21
22impl Device for Strat9NetDevice {
23 type RxToken<'a> = Strat9RxToken;
24 type TxToken<'a> = Strat9TxToken;
25
26 fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
27 let mut buf = [0u8; MAX_FRAME_SIZE];
28 match net_recv(&mut buf) {
29 Ok(n) if n > 0 => {
30 let len = core::cmp::min(n, MAX_FRAME_SIZE);
31 Some((Strat9RxToken { buf, len }, Strat9TxToken))
32 }
33 Err(e) => {
34 if e.to_errno() != 11 && RX_ERR_LOG_BUDGET.load(Ordering::Relaxed) > 0 {
35 RX_ERR_LOG_BUDGET.fetch_sub(1, Ordering::Relaxed);
36 log_errno("[strate-net] net_recv errno=", e);
37 }
38 None
39 }
40 _ => None,
41 }
42 }
43
44 fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
45 Some(Strat9TxToken)
46 }
47
48 fn capabilities(&self) -> DeviceCapabilities {
49 let mut caps = DeviceCapabilities::default();
50 caps.max_transmission_unit = MAX_FRAME_SIZE;
51 caps.medium = Medium::Ethernet;
52 caps.max_burst_size = Some(8);
53 caps
54 }
55}
56
57pub(crate) struct Strat9RxToken {
58 buf: [u8; MAX_FRAME_SIZE],
59 len: usize,
60}
61
62impl phy::RxToken for Strat9RxToken {
63 fn consume<R, F>(self, f: F) -> R
64 where
65 F: FnOnce(&[u8]) -> R,
66 {
67 f(&self.buf[..self.len])
68 }
69}
70
71pub(crate) struct Strat9TxToken;
72
73impl phy::TxToken for Strat9TxToken {
74 fn consume<R, F>(self, len: usize, f: F) -> R
75 where
76 F: FnOnce(&mut [u8]) -> R,
77 {
78 if len > MAX_FRAME_SIZE {
79 log("[strate-net] TX frame too large\n");
80 return f(&mut []);
81 }
82
83 let mut buf = [0u8; MAX_FRAME_SIZE];
84 let ret = f(&mut buf[..len]);
85 let mut last_err = None;
86 for attempt in 0..NET_SEND_RETRY_LIMIT {
87 match net_send(&buf[..len]) {
88 Ok(_) => {
89 TX_SUCCESS_COUNT.fetch_add(1, Ordering::Relaxed);
90 last_err = None;
91 break;
92 }
93 Err(e @ (Error::Again | Error::QueueFull)) => {
94 last_err = Some(e);
95 let _ = proc_yield();
96 }
97 Err(e @ Error::IoError) if attempt + 1 < NET_SEND_RETRY_LIMIT => {
98 last_err = Some(e);
99 sleep_micros(50);
100 }
101 Err(e) => {
102 last_err = Some(e);
103 break;
104 }
105 }
106 }
107
108 if let Some(e) = last_err {
109 TX_DROP_COUNT.fetch_add(1, Ordering::Relaxed);
110 if TX_ERR_LOG_BUDGET.load(Ordering::Relaxed) > 0 {
111 TX_ERR_LOG_BUDGET.fetch_sub(1, Ordering::Relaxed);
112 log_errno("[strate-net] net_send errno=", e);
113 }
114 }
115
116 ret
117 }
118}