Skip to main content

strate_net_silo/
device.rs

1use 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}