← Blog
0.7.0 · SRDX · TWO-PI

SRDX Two-Pi Session Bringup — Advertisement Protocol + Integration Test

June 22, 2026 · sigil-os ac5329f · Sigil-Docs
srdx two-pi networking 0.7.0 milestone

srdx_advertise.sg + srdx_twopi_test.sg (sigil-os ac5329f) complete the two-Pi SRDX session bringup layer. The advertisement protocol solves the bare-metal discovery problem: two Pi 4Bs on the same LAN, no DHCP, no DNS, no IP pre-configuration — the host broadcasts raw 32B ADV frames and the viewer discovers it. The integration test validates the full bringup flow from ADV build/parse through Mode-2 fallback logic to netplay tick paths. This is the last OS-side piece before two-Pi hardware session testing on real silicon.


The bare-metal discovery problem

When two Pi 4Bs run sigilOS on a LAN, neither has a pre-known IP address. There's no DHCP server on a bare-metal demo table, no DNS, no configuration file. The host Pi needs to broadcast its presence; the viewer Pi needs to find it without being told where to look.

srdx_advertise.sg solves this with a minimal raw-frame broadcast protocol over the same NIC transport that SRDX already uses (sys 113-116: raw-send/recv). The host broadcasts a 32B ADV frame every ~60 frames; the viewer polls raw_recv and parses the frame. When a valid ADV is received, the viewer has the host's MAC and port — enough to initiate the SRDX negotiate phase.

ADV frames carry no secrets. MAC and port are safe to broadcast on a local LAN. Session credentials are exchanged in the negotiate phase (HELLO→READY, srdx_negotiate_host/srdx_negotiate_viewer), not in ADV.


ADV frame format — 32 bytes over raw NIC

Magic bytes [0-3]
SRDA (0x53, 0x52, 0x44, 0x41) — 4 bytes. srdx_adv_parse checks this first; any frame without the magic is silently discarded.
Version [4]
1 byte, currently 1. Future protocol versions can be distinguished here.
Reserved [5-7]
3 bytes zero-padded. Word-aligns the MAC field.
Host MAC [8-13]
6 bytes, raw Ethernet MAC of the host NIC. Copied from the NIC's MAC register at bringup. Used by the viewer to address the negotiate_host call.
Reserved [14-15]
2 bytes zero-padded. Word-aligns port.
Port [16-17]
2 bytes, little-endian. Host's SRDX listener port (default 5900). LE encoding: port % 256 at [16], port / 256 at [17].
Zero-pad [18-31]
14 bytes zero. Brings frame to 32 bytes total, fitting in one raw-NIC minimum-size frame.

Host + viewer API

srdx_adv_build(adv_buf)
Fill a 32B ADV frame at adv_buf. Writes magic, version=1, MAC from NIC register, port LE-encoded. Called once at session init; the resulting buffer is broadcast repeatedly.
srdx_adv_send(adv_buf)
srdx_adv_build(adv_buf) + raw_send(sys 113). Call ~every 60 frames on the host side. Fires-and-forgets; raw_send returns immediately regardless of whether any viewer received it.
srdx_adv_recv(adv_buf)
Poll raw_recv(sys 116) into adv_buf. Returns 0 if no frame received this poll (non-blocking). Called in the viewer's discovery loop.
srdx_adv_parse(adv_buf, out_mac, out_port)
Validate magic bytes, extract MAC into out_mac (6 bytes), decode port from LE at [16-17] into out_port. Returns 0 on success, -1 if magic mismatch. The viewer calls this on every received frame; non-SRDX frames are discarded silently.

Two-Pi real-hardware session flow

The complete bringup sequence, from power-on to active session:

HOST Pi                              VIEWER Pi
─────────────────────────────────── ─────────────────────────────────────
srdx_link() → ok                    srdx_link() → ok
srdx_adv_build(adv_buf)
[every 60 frames]:                  [discovery poll loop]:
  srdx_adv_send(adv_buf)  ────────→   srdx_adv_recv(adv_buf) → 0 (miss)
  srdx_adv_send(adv_buf)  ────────→   srdx_adv_recv(adv_buf) → 32B
                                        srdx_adv_parse → mac + port
                           ←──────── srdx_negotiate_viewer(mac, port)
srdx_negotiate_host()
HELLO→READY handshake    ←────────→  HELLO→READY handshake
[active session]:
Mode-2: srdx_netplay_tick()         Mode-2: srdx_netplay_tick()
Mode-1 fallback: pixel stream        Mode-1 fallback: pixel stream

No IP addresses. No pre-configuration. The only shared knowledge is that both devices are on the same LAN and running sigilOS.


srdx_twopi_test.sg — integration test

CheckWhat it verifies
ADV build magicadv_buf[0-3] = 'SRDA' after srdx_adv_build
ADV build port LEport=5900: adv_buf[16]=0x10, adv_buf[17]=0x17 (5900=0x1710)
ADV build MAC copyMAC bytes [8-13] match NIC register source
ADV parse magic validsrdx_adv_parse on valid frame → 0, mac+port extracted
ADV parse magic invalidsrdx_adv_parse on junk frame → -1
Mode-2 fallback: empty ringsrdx_mode2_should_fallback(ring, 0 frames) → 0 (no fallback)
Mode-2 fallback: in-rangering not desync, peer_seq within window → 0
Mode-2 fallback: desyncring desync flag set → 1 (should fall back to Mode-1)
netplay tick no-peersrdx_netplay_tick(session, no peer) → rollback=0
Mode-1 fallback tickOVL w=0 (no overlay) → srdx_tick returns 0

PASS: TWOPI-PASS adv=ok parse=ok fallback=ok tick=ok