radio_esp32::net
The smoltcp link layer for an associated Wi-Fi station.
Phase 6 of doc/plan-radio.md, issue #68. smoltcp is the TCP/IP stack —
Rust, no_std, and heapless: it never allocates on its own, working instead
from buffers we hand it. That suits ground rule 3, where only this tier
allocates and the kernel stays static; every buffer here comes from the
radio heap ([kernel::heap]).
What this file is
Section titled “What this file is”The device seam — the boundary smoltcp’s [phy::Device] trait sits on.
An associated station moves raw Ethernet frames two ways:
- Transmit hands a frame straight to the blob’s
esp_wifi_internal_tx, the same raw-L2 path the 4-way handshake sends EAPOL through ([crate::supplicant]). smoltcp writes the frame into the token’s buffer; [TxToken::consume] passes it down. - Receive drains an [
RxQueue] the MAC’s receive callback fills. Each frame the blob delivers is copied into a free ring slot on the driver’s task; [phy::Device::receive] hands the oldest one to smoltcp.
Status
Section titled “Status”6.2/6.3/6.4: the stack reaches the internet. The [RxQueue] and
[WifiDevice] carry traffic both ways; [rx_trampoline] is the MAC’s
receive callback filling the ring. [obtain_dhcp_lease] runs DHCP to a
lease, and [dns_tcp_probe] takes it further — one interface running DHCP,
then a DNS resolution, then a TCP connection with a byte exchange.
Validated on an ESP32-DevKitC against a real AP: a DHCP lease, example.com
resolved, a TCP connection to it, and an HTTP/1.1 200 OK read back, all
within a second of association. The ring behaviour is host-tested besides.
Still to come for #68: 6.5 TLS, deferred by the plan.
Structs
Section titled “Structs”struct Lease { pub ip: [u8; 4], pub prefix_len: u8, pub router: Option<[u8; 4]>, pub dns: Option<[u8; 4]>,}A DHCP lease, in plain bytes so the caller needs no smoltcp types.
Implements: Clone, Copy, Debug
RxQueue
Section titled “RxQueue”struct RxQueue { /* private fields */ }A single-producer/single-consumer ring of received frames.
The MAC receive callback is the producer, on the driver’s task; the
interface poll is the consumer, on the network task. A full ring drops the
incoming frame rather than blocking the MAC — TCP recovers from a loss, a
wedged receive path does not. The [Spinlock] is held only for the index
arithmetic and the copy, never across a call out.
Implements: Default
WifiDevice
Section titled “WifiDevice”struct WifiDevice<'q> { /* private fields */ }smoltcp’s view of the associated station: an Ethernet device whose transmit
goes to the blob and whose receive drains an [RxQueue].
Zero-copy is not worth it here — the blob owns the receive buffer only for
the duration of its callback, so a frame is copied into the ring on arrival
regardless. The device borrows the queue rather than owning it: the queue is
a static the receive callback also names.
Implements: Device
WifiRxToken
Section titled “WifiRxToken”struct WifiRxToken { /* private fields */ }A received frame, owned by the token for the length of the consume call.
Implements: RxToken
WifiTxToken
Section titled “WifiTxToken”struct WifiTxToken;The right to transmit one frame: smoltcp fills the buffer, and consume
hands it to the blob.
Implements: TxToken
Constants
Section titled “Constants”const MTU: usize;Largest Ethernet II frame smoltcp will build or accept, headers included and FCS excluded (the MAC appends and strips the FCS). 14-byte header + 1500 MTU.
RX_SLOTS
Section titled “RX_SLOTS”const RX_SLOTS: usize;How many received frames the queue holds before it drops the oldest. Sized
to ride out a poll interval’s worth of arrivals without stalling the MAC;
16 × [MTU] is ~24 KiB out of the radio heap.