Skip to content

Commit 0c95315

Browse files
committed
Added L2vL3 drivers section in porting guide
1 parent 0d80309 commit 0c95315

1 file changed

Lines changed: 90 additions & 9 deletions

File tree

docs/porting_guide.md

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
- [1. Scope](#1-scope)
66
- [2. Where a port plugs in](#2-where-a-port-plugs-in)
77
- [3. The link-layer device interface](#3-the-link-layer-device-interface)
8+
- [3.1 L2 versus L3 drivers](#31-l2-versus-l3-drivers)
89
- [4. Designing a device driver](#4-designing-a-device-driver)
910
- [4.1 The driver contract](#41-the-driver-contract)
10-
- [4.2 A driver without DMA (PIO / host-backed)](#42-a-driver-without-dma-pio--host-backed)
11+
- [4.2 The simplest drivers: loopback, PIO, host-backed](#42-the-simplest-drivers-loopback-pio-host-backed)
1112
- [4.3 A driver with DMA: descriptor rings](#43-a-driver-with-dma-descriptor-rings)
1213
- [4.4 DMA ownership and the poll path](#44-dma-ownership-and-the-poll-path)
1314
- [4.5 DMA ownership and the send path](#45-dma-ownership-and-the-send-path)
@@ -125,9 +126,45 @@ back-pointer to your driver state, the wolfIP equivalent of lwIP's
125126
`wolfIP_getdev(s)` and additional interfaces with `wolfIP_getdev_ex(s, idx)`.
126127

127128
`buf` is always a **single, contiguous, linear** frame buffer owned by the
128-
stack — there is no `pbuf` chain to walk. The frame includes the full Ethernet
129-
header. Your driver must not retain the `buf` pointer after the callback
130-
returns.
129+
stack — there is no `pbuf` chain to walk. On an L2 interface the frame includes
130+
the full Ethernet header (see below). Your driver must not retain the `buf`
131+
pointer after the callback returns.
132+
133+
### 3.1 L2 versus L3 drivers
134+
135+
The `non_ethernet` flag selects the driver class:
136+
137+
- **L2 / Ethernet driver** (`non_ethernet = 0`, the default). The driver moves
138+
complete Ethernet frames: a 14-byte Ethernet header followed by the payload.
139+
wolfIP performs ARP / neighbour resolution and builds and parses the Ethernet
140+
header itself. The `buf` passed to `poll` and `send` begins at the Ethernet
141+
header. The TAP, LPC, VA416xx, and GEM drivers are all L2.
142+
143+
- **L3 / point-to-point driver** (`non_ethernet = 1`). The link carries bare IP
144+
packets — there is no Ethernet header and no ARP. On transmit, wolfIP strips
145+
the 14-byte Ethernet header it built before calling your `send`, so `send`
146+
receives the IP packet (`buf + ETH_HEADER_LEN`, `len - ETH_HEADER_LEN`). On
147+
receive, your `poll` must return a buffer that begins at the IP header. The
148+
built-in loopback interface and TUN-style devices
149+
(`src/port/posix/linux_tun.c`, `IFF_TUN`) are L3.
150+
151+
| | L2 (Ethernet) | L3 (point-to-point) |
152+
|---|---|---|
153+
| `non_ethernet` | `0` | `1` |
154+
| Frame at `poll` / `send` | Ethernet header + IP | IP only |
155+
| ARP / neighbour resolution | performed by wolfIP | skipped |
156+
| Examples | TAP, LPC, VA416xx, GEM | loopback `lo`, TUN |
157+
158+
The stack applies the L3 stripping in `wolfIP_ll_send_frame()`:
159+
160+
```c
161+
if (ll->non_ethernet)
162+
return ll->send(ll, (uint8_t *)buf + ETH_HEADER_LEN, len - ETH_HEADER_LEN);
163+
```
164+
165+
The `mtu` field always describes wolfIP's internal frame budget *including*
166+
Ethernet headroom; on an L3 link the maximum IP payload handed to `send` is
167+
therefore `mtu - ETH_HEADER_LEN`.
131168
132169
---
133170
@@ -164,12 +201,56 @@ before writing a line of driver code:
164201
hardware is busy, return `0` (poll) or `-WOLFIP_EAGAIN` (send) and let the
165202
next poll cycle make progress.
166203
167-
### 4.2 A driver without DMA (PIO / host-backed)
204+
### 4.2 The simplest drivers: loopback, PIO, host-backed
205+
206+
The very simplest driver has no hardware at all. When `WOLFIP_ENABLE_LOOPBACK`
207+
is set (and `WOLFIP_MAX_INTERFACES > 1`), `wolfIP_init()` installs an L3
208+
loopback interface at index 0 — `ifname` `"lo"`, `non_ethernet = 1`, address
209+
`127.0.0.1/8` — whose `poll`/`send` move IP packets through a small in-memory
210+
queue (`src/wolfip.c`):
211+
212+
```c
213+
static int wolfIP_loopback_send(struct wolfIP_ll_dev *ll, void *buf, uint32_t len)
214+
{
215+
struct wolfIP *s = WOLFIP_CONTAINER_OF(ll, struct wolfIP, ll_dev);
216+
217+
if (len == 0 || len > IP_MTU_MAX)
218+
return 0;
219+
if (s->loopback_count >= WOLFIP_LOOPBACK_QUEUE_DEPTH)
220+
return -WOLFIP_EAGAIN; /* queue full: retry later */
221+
/* buf is the IP packet — the Ethernet header was already stripped for
222+
* this non_ethernet device. Store as-is; wolfIP_poll re-adds the prefix. */
223+
memcpy(s->loopback_buf[s->loopback_tail], buf, len);
224+
s->loopback_count++;
225+
return (int)len;
226+
}
227+
228+
static int wolfIP_loopback_poll(struct wolfIP_ll_dev *ll, void *buf, uint32_t len)
229+
{
230+
struct wolfIP *s = WOLFIP_CONTAINER_OF(ll, struct wolfIP, ll_dev);
231+
uint32_t pending;
232+
233+
if (s->loopback_count == 0)
234+
return 0; /* nothing queued */
235+
pending = s->loopback_pending_len[s->loopback_head];
236+
if (pending > len)
237+
return 0;
238+
memcpy(buf, s->loopback_buf[s->loopback_head], pending);
239+
s->loopback_count--;
240+
return (int)pending; /* one IP packet */
241+
}
242+
```
243+
244+
This is the `poll`/`send` contract in its purest form: `send` queues a packet
245+
(or returns `-WOLFIP_EAGAIN` when the queue is full), `poll` dequeues one packet
246+
(or returns `0` when empty). No Ethernet header, no DMA, no cache maintenance —
247+
exactly what an L3 driver does, with an in-memory queue standing in for the wire.
168248

169-
The simplest possible driver does a register/FIFO read on poll and a
170-
register/FIFO write on send. The POSIX TAP driver is the canonical minimal
171-
example — the "hardware" is a host file descriptor, but the shape is identical
172-
to a small MCU MAC that exposes an RX/TX FIFO (`src/port/posix/tap_linux.c`):
249+
The next simplest driver is a programmed-I/O Ethernet (L2) MAC: a register/FIFO
250+
read on poll and a register/FIFO write on send. The POSIX TAP driver is the
251+
canonical minimal example — the "hardware" is a host file descriptor, but the
252+
shape is identical to a small MCU MAC that exposes an RX/TX FIFO
253+
(`src/port/posix/tap_linux.c`):
173254

174255
```c
175256
static int tap_poll(struct wolfIP_ll_dev *ll, void *buf, uint32_t len)

0 commit comments

Comments
 (0)