The Realtek Wi-Fi driver that simply devours its competitors.
Devourer is a userspace Wi-Fi driver for Realtek's 802.11n, 802.11ac and 802.11ax USB adapters — the cheap, everywhere-available dongles that power most long-range FPV video links. It talks to the chip directly over libusb: no kernel module, no DKMS tree to patch every time your kernel updates, no root filesystem to taint. Build one static library, link it, and you have raw monitor-mode RX and packet injection across five Realtek hardware backends, from a single API.
It is the OpenIPC project's driver of choice for long-range digital video links.
- No kernel driver, no driver hell. Everything runs in your process via libusb — on Linux, macOS, Windows and Android alike, including platforms the vendor drivers never supported. On Android that means no root and no custom kernel: PixelPilot uses devourer as the receive path of an FPV ground station running on an ordinary phone, with the adapter opened straight from the USB permission the app is granted.
- Faster on-air than the kernel driver. Ready-to-receive and
ready-to-transmit come up quicker than the vendor
.koon every supported chip, and raw injection skips the kernel networking stack the vendor driver drags every frame through (mac80211 → cfg80211 → qdisc → skb → driver xmit) — so it sustains the same channel occupancy at 3–4× less host CPU, which matters most on an embedded transmitter (numbers). - Per-packet control. Every injected frame carries its own radiotap header: rate, bandwidth, guard interval, coding, STBC — even TX power and channel — can change frame by frame. That turns one dongle into an adaptive-link engine: unequal error protection for video layers, live power control, per-packet frequency hopping.
- Frequency hopping at FHSS speed, and a hopset that adapts. A channel hop costs ~0.5–2.5 ms depending on chip — fast enough to hop on every packet (how) — in a keyed order an observer cannot predict. The link also learns: a channel that stops delivering is dropped from the schedule by authenticated agreement between the two ends, and revisited later by keyed probes in case it recovers (how).
- Wi-Fi 6, on the same API. The 802.11ax parts (RTL8852BU/8852CU) run one HAL over both dies, with HE injection, 160 MHz on the 8852C and 6 GHz on the 8832CU. Including the standard's long-range corner — HE ER SU + DCM, worth roughly 8–10 dB stacked, and the only extended-range lever that works against someone else's 802.11ax gear (how). Trigger frames air correctly for scheduled-uplink work, though the hardware-timed response needs AP firmware these parts don't ship (what closes, what doesn't).
- A link that changes channel on evidence, not on a timer. Beside per-slot hopping there's the slow lever: a second adapter surveys candidates while the video keeps flowing, delivery on the live channel decides when a move is worth it, and the two ends migrate under an authenticated ground-proposes/drone-commits protocol that cannot split-brain (how).
- Narrowband modes the kernel can't do. 5 and 10 MHz channels on the backends that advertise them — including the decade-old RTL8812AU and RTL8814AU the vendor never gave narrowband — half/quarter the bandwidth, more range from the same power. RTL8733B does 10 MHz only (how).
- Hardware time, for coordinating radios. Every received frame is stamped with the chip's microsecond MAC clock (TSF) on every generation, and the 64-bit timer reads back directly — the primitive multi-radio setups need. Independent receivers correlate their clocks to sub-microsecond, and a time-division burst schedule locks to a transmitter ~25× tighter than the host clock manages — enough to interleave a robust narrowband link and a wide high-throughput one on one shared channel (bandwidth TDMA).
- Aggregation and hardware ACKs in userspace. USB TX aggregation, 802.11 A-MPDU for +30% on-air goodput, and a hardware ACK/BlockAck responder that turns unicast into a reliable hardware-ARQ link — with per-frame TX-status reports as the transmit-side link sensor (how).
- A radio lab in a dongle. Channel sounding, per-antenna signal quality, beamforming report capture (enough to do motion sensing), spectrum sweeps, link-health diagnosis that tells you whether to add or back off power.
- Clean library API. One
DeviceConfigstruct at construction, runtime setters for everything that changes mid-flight, zero environment-variable magic inside the library.
New to low-level RF? Start with the visual RF primer — fifteen short animations that make the rest click. Its sibling, the visual driver primer, does the same for the silicon and vendor-driver vocabulary (firmware, efuse, DMAC/CMAC, halbb/halrf, IQK…).
Bandwidth cells are devourer's measured on-air TX throughput (Mbps, HT MCS7, 20 MHz) per band:
| Part | RF / streams | 2.4 GHz (ch6) | UNII-1 (ch36) | UNII-2/3 (ch149) | 6 GHz (ch5) | Notes |
|---|---|---|---|---|---|---|
| RTL8812AU | 2T2R | 56 | 52 | 52 | — | CHANEVE CHW50L (0bda:8812). 5/10 MHz capable |
| RTL8811AU | 1T1R | — | — | — | — | 1T1R cut of 8812 silicon; rides the 8812 code path. Not benchmarked. 5/10 MHz capable |
| RTL8814AU | 4T4R, 3-SS max | 65 | †(32) | †(32) | — | 0bda:8813; tested on COMFAST CF-938AC and CF-960AC — antenna builds differ in realised RX diversity. 5/10 MHz capable |
| RTL8821AU | 1T1R + BT | 54 | 32 | 28 | — | TP-Link Archer T2U Plus (2357:0120) |
| RTL8822BU | 2T2R + BT | 52 | 50 | 49 | — | TP-Link Archer T3U (2357:012d). 5/10 MHz capable |
| RTL8812BU | 1T1R + BT | — | — | — | — | 1T1R cut of 8822B silicon; rides the 8822BU code path. Not benchmarked. 5/10 MHz capable |
| RTL8811CU | 1T1R + BT | 36 | 29 | 28 | — | COMFAST CF-811AC (0bda:c811). 5/10 MHz capable |
| RTL8821CU | 1T1R + BT | — | — | — | — | rides the 8811CU (8821C) code path. 5/10 MHz capable |
| RTL8812CU | 2T2R | 65 | 60 | 60 | — | LB-LINK WDN1300H (0bda:c812). 5/10 MHz capable |
| RTL8822CU | 2T2R + BT | — | — | — | — | not benchmarked (0bda:c82c). 5/10 MHz capable |
| RTL8812EU | 2T2R | ‡ | 51 | 47 | — | LB-LINK BL-M8812EU2 (0bda:a81a); bare 5 GHz FPV module. 5/10 MHz capable. ‡ 2.4 GHz TX airs energy but no receiver decodes it — the vendor kernel driver behaves identically on this module (quirks) |
| RTL8822EU | 2T2R + BT | — | — | — | — | not benchmarked. 5/10 MHz capable |
| RTL8731BU | 1T1R | — | — | — | — | bare unbranded 1T1R module (0bda:f72b, cut D): monitor RX and CCK/legacy/HT raw TX validated on 2.4 GHz, legacy/HT on 5 GHz, 20/40 MHz. Not benchmarked. Status and test limits |
| RTL8733BU | 1T1R + BT | 62 | 50 | 50 | — | LB-LINK BL-M8733BU2-L (0bda:b733); rides the RTL8731BU (RTL8733B) code path. 10 MHz capable |
| RTL8821CE (PCIe) | 1T1R + BT | — | — | — | — | Radxa X4 onboard Wi-Fi (10ec:c821); not benchmarked |
| RTL8852BU (11ax) | 2T2R + BT | 43 | 36 | 33 | — | TP-Link Archer TX20U Nano (35bc:0108); Wi-Fi 6, dual-band. 5/10 MHz capable; HE ER SU + DCM extended range |
| RTL8832BU (11ax) | 2T2R | — | — | — | — | Wi-Fi-only SKU of the 8852B die; rides the 8852BU code path. Not benchmarked. 5/10 MHz capable; HE ER SU + DCM extended range |
| RTL8832CU (11ax) | 2T2R + BT | 40 | 33 | 32 | 32 | TP-Link Archer TX50UH (35bc:0101); Wi-Fi 6E tri-band (2.4/5/6 GHz). 5/10 and 160 MHz capable; HE ER SU + DCM extended range. Host-push injection over USB 2.0 (~50% duty ceiling); 6G TX+RX validated |
| RTL8852CU (11ax) | 2T2R + BT | — | — | — | — | "8852" branding of the same 8852C die; rides the 8832CU code path. Not benchmarked. 5/10 and 160 MHz capable; HE ER SU + DCM extended range |
| MT7612U (MediaTek) | 2T2R | ‖ | ‖ | — | — | Alfa AWUS036ACM / Aukey USBAC1200 (0e8d:7612) and 15 OEM ids; the one non-Realtek backend, opt-in with -DDEVOURER_MT7612U=ON. 20/40/80 MHz; no 5/10 MHz, no fast retune. Measured record |
† = works on-air but the reading varies run-to-run (bracketed = best clean
reading).
‖ = measured, but not by this column's method, so not comparable with the
rows above. These cells are USRP duty cycle × PHY rate; the MT7612U bench has
no USRP, and its HT MCS7 / 20 MHz figures — 34.03 Mbit/s single-frame, 44.55
with A-MPDU — are receiver frame counts instead, which is the instrument this
project deliberately does not judge TX by. Both directions are validated
against the kernel mt76x2u driver on 2.4 GHz and UNII-1:
the regression matrix.
These cells are single-frame injection (the default TX path), measured as
channel occupancy × PHY rate. A-MPDU (SetAmpduMode) does not move them on
a chip already near the PHY ceiling — it raises goodput (delivered payload)
~30% at MCS7/20 by amortizing per-frame overhead, which an occupancy metric
can't show. See aggregation & hardware ACK.
Out of scope: the pre-HalMAC PCIe parts (RTL8812AE/8821AE). The 11ax
"Kestrel" generation (RTL8852BU / RTL8852CU, a fourth HAL under src/kestrel/,
vendor references reference/rtl8852bu + reference/rtl8852cu) has RX, TX, and
channel/bandwidth (5/10/20/40/80 MHz on both dies, 160 MHz on the 8852C)
on-air validated — and the tri-band RTL8832CU adds 6 GHz (WiFi 6E),
benchmarked above at ~5 GHz-parity throughput. The BB/RF plane is Realtek's own
halbb/halrf C compiled verbatim (register tables, per-channel config, DACK/
RX-DCK, plus IQK on the 8852C); TSSI/DPK on both dies and IQK on the 8852B are
gated off with on-air evidence — they degrade TX under the fixed-power model.
The 8852A-family (e.g. RTL8832AU) stays out of scope — its only vendor driver
is a frozen 2021 drop.
The RTL8733B backend covers the 1T1R 802.11n RTL8731BU/RTL8733BU family with 20/40 MHz monitor RX and raw CCK/OFDM/HT injection (CCK is 2.4 GHz, 20 MHz, long preamble only). It intentionally does not advertise VHT, LDPC, SGI, STBC, or experimental 5/10 MHz operation; see the validation record for the tested and deferred matrix.
The MT7612U backend (src/mt7612u/) is the one non-Realtek family, behind
the same IRadio contract and sharing none of the Realtek HAL — this is 32-bit
registers over EP0 plus an in-band MCU on EP8/EP5, with firmware uploaded at
bring-up. It is OFF by default; build it with -DDEVOURER_MT7612U=ON, and note
that the firmware is not embedded the way every Realtek blob is: mt7662.bin
and mt7662_rom_patch.bin ship zstd-compressed in linux-firmware under their
own licence, so they are searched for at runtime
(DeviceConfig.mt7612u.firmware_dir). Monitor RX with per-chain RSSI, raw
injection at any rate the TXWI can express, hardware ACK, A-MPDU and 20/40/80
MHz tuning all work; there is no fast retune (≈50 ms fast / ≈530 ms full), no
5/10 MHz, no HE, no 160 MHz, no beacons, and unicast injection is a 40× cliff.
SetTxMode is refused — the C library has no session-default rate, so the rate
goes in each frame's radiotap header instead, where it always wins. Every
number and its counterpart.
Heads up — some Realtek sticks ship in "ZeroCD" mode and first enumerate as a USB flash drive holding a Windows installer (
0bda:1a2bis the canonical offender). If the device won't open, checklsusb;usb_modeswitchflips it to the real NIC.
Toolchain: CMake ≥ 3.15, a C++20 compiler, libusb-1.0.
# Debian/Ubuntu
sudo apt install build-essential cmake pkg-config libusb-1.0-0-dev
# macOS (Homebrew)
brew install cmake pkg-config libusb
cmake -S . -B build
cmake --build build -jOn Windows, install libusb via vcpkg (vcpkg install libusb) and set
VCPKG_ROOT before configuring.
Then, with a supported dongle plugged in:
sudo ./build/rxdemo # receive: monitor mode, prints frames
sudo ./build/txdemo # transmit: injects a test beacon
DEVOURER_CHANNEL=100 DEVOURER_TX_RATE=MCS7/40 sudo -E ./build/txdemoThe demos find the first supported adapter automatically; DEVOURER_PID /
DEVOURER_VID pin a specific one. Every configuration knob the demos accept
is an environment variable — the complete catalogue, with value grammar, is
the env: tags in src/DeviceConfig.h.
| binary | what it shows |
|---|---|
rxdemo |
monitor-mode RX loop with per-frame signal telemetry |
txdemo |
packet injection, rate/power/channel control, hopping |
streamtx / duplex |
stdin-driven TX / full-duplex packet link |
svctx |
per-video-layer rate ladders (unequal error protection) |
txpower |
runtime TX-power API walkthrough |
tdma |
TSF-slotted burst TDMA (narrowband ↔ wide on one channel) |
chanmig / chanscout |
evidence-driven channel migration: the protocol, and the survey adapter that feeds it |
dwelltx |
dwell-1 hopping data plane on the standard Linux driver |
kestrelprobe |
Wi-Fi 6 (RTL8852B/C) bring-up probe, layer by layer |
rtl8733bprobe |
RTL8731BU/RTL8733BU identity, power, firmware and PHY diagnostics |
timesync |
over-the-air clock distribution (master / slave / UE roles) |
sense |
Wi-Fi motion sensing from beamforming reports |
doctor |
adapter-health triage → HEALTHY / SUSPECT / FAILING |
pcieprobe |
PCIe transport bring-up validation, layer by layer |
precoder |
OFDM subcarrier shaping proof-of-concept |
All chips compile in by default; per-chip CMake options (DEVOURER_JAGUAR1,
DEVOURER_8814, DEVOURER_JAGUAR2_8822B, DEVOURER_JAGUAR2_8821C,
DEVOURER_JAGUAR3_8822C, DEVOURER_JAGUAR3_8822E, DEVOURER_8733B,
DEVOURER_KESTREL_8852B, DEVOURER_KESTREL_8852C) drop unneeded firmware and
tables — an 8812AU-only
rxdemo is ~1.6 MB against ~6.3 MB with everything on, and dropping just the
two Wi-Fi 6 dies takes it to ~4.2 MB (their verbatim-vendored halbb/halrf plane
is the single largest contributor). DEVOURER_PCIE (default OFF, Linux-only)
adds the vfio-pci transport for the RTL8821CE.
You own libusb: init it, open the device, detach any kernel driver, claim
interface 0 — then hand the handle to the factory. examples/rx/main.cpp is
the full boilerplate; the minimal RX path is:
auto logger = std::make_shared<Logger>();
WiFiDriver driver(logger);
auto dev = driver.CreateRadio(handle); // handle is already claimed
dev->Init(packetProcessor, SelectedChannel{
.Channel = 36,
.ChannelOffset = 0,
.ChannelWidth = CHANNEL_WIDTH_20,
});packetProcessor is your void(const Packet&) callback. For TX, call
InitWrite and then send_packet(buffer, len), where the buffer starts with
a radiotap header describing how the frame should fly.
Construction-time options travel in a devourer::DeviceConfig
(src/DeviceConfig.h documents every field):
devourer::DeviceConfig cfg;
cfg.rx.keep_corrupted = true; // deliver CRC-failed frames too
auto dev = driver.CreateRadio(handle, ctx, lock, cfg);Anything that changes mid-session is a runtime setter on the device:
SetTxMode, SetTxPowerOffsetQdb, SetRxPathMask, FastRetune, ...
The device class is chosen automatically from the chip behind the handle;
one IRadio interface covers all five hardware backends.
Start here. There is a lot below; this is the order that works. Read the visual RF primer first — fifteen animations covering the concepts every other doc assumes (subcarriers, EVM, AGC, hopping, OFDMA, extended range). Then pick the one thing you came for: building a video link → adaptive link; surviving interference → FHSS; getting more range → narrowband; coordinating several radios → time distribution; making the driver itself do something new → visual driver primer, then logging for the event schema every test script reads. If a chip is misbehaving, skip to adapter doctor and the per-chip quirks notes at the bottom.
Primers:
- Visual RF primer — animated intro to the concepts behind everything below.
- Visual driver primer — animated intro to the chip and vendor-driver machinery: registers, efuse, firmware, MAC, PHY tables, calibration, coexistence, firmware offload.
Wi-Fi 6 (802.11ax):
- HE extended range — the ER SU / DCM range ladder, what each rung buys and costs, and the on-air matrix across both dies.
- HE trigger-based uplink — Trigger frames, resource units, TWT and sounding: the API, and an honest account of which paths the shipped client firmware executes and which it silently drops.
Link engineering:
- Adaptive link — the energy-minimizing video-link controller design, its validation, and the building blocks: what each knob (power, rate, bandwidth, hopping) measurably buys.
- Fused FEC — the cross-layer error-protection stack: per-layer PHY rates, corrupt-frame salvage, outer erasure code.
- Aggregation & hardware ACK — USB TX aggregation,
per-frame CCX TX-status reports, 802.11 A-MPDU (
SetAmpduMode, +30% on-air goodput), and the hardware ACK/BlockAck responder for reliable-unicast links. - wfb-ng tuning — the most efficient wfb-ng configuration, and the SDR-measured devourer-vs-wfb-ng TX comparison.
Spectrum agility:
- Frequency hopping — how per-packet hopping
works and what it costs on each chip, including the 8822B/C/E firmware
channel-switch fast path (
DEVOURER_FASTRETUNE_FW). - Kernel channel-switch baseline + firmware offload + MCC/FCS + dwell-1 A/B injection + N-channel hopping — how the standard Linux/Realtek drivers retune measured against devourer, where the chip firmware's own H2C 0x1D switch beats them, and a two-context per-slot data plane with zero wrong-channel over 100 k slots.
- FHSS — the anti-jam design article: keyed SipHash hop schedules, slot-locked lockstep RX, and jammer resilience — measured delivery against parked and following jammers, and where a follower breaks. It carries on into the adaptive half: how the two ends agree on a change to the hopset without either trusting the other, why the endpoint that must decode is the one that decides, why a transmitter may argue only that a move leaves it worse off rather than that it disagrees, and what stops an adversary who can make channels look bad from herding the link onto one it then jams.
- Adaptive channel migration — the slow counterpart to hopping: a scout adapter surveying candidates while the video keeps flowing, a scoring engine where the receiver's delivery is authoritative, and a gate that only moves a working link on evidence. The wire protocol is the authenticated ground-proposes/drone-commits exchange, and its validation is the failure matrix every row of which converges without split-brain.
- Narrowband — 5/10 MHz channels across the Jaguar generations, plus the unadvertised RTL8733B experiment: the baseband re-clock, the per-chip register machinery, and the walls (RF re-latch edges, per-die clock coupling, the 5 MHz/5 GHz CFO limit).
- Spectrum sensing — RX energy sweeps down to 5 MHz bins: a coarse per-bin H(f) from the dongle itself.
- Pseudo preamble puncturing — how close per-tone RX masks/notches get to using a wide channel with a dirty slice.
Timing & coordination:
- Time distribution — LTE-eNB-style over-the-air clock distribution off the hardware beacon TSF: sub-µs downlink, TSF adoption, µs-fine TBTT steering and a converging closed-loop uplink timing advance.
- Timing accuracy — measured comparison vs NTP/PTP over
Wi-Fi (why the hardware TSF beats software timestamps ~3000×, why PTP can't run),
and the USB-vs-PCIe transport-latency microbench (
tests/reglat.cpp). - AP mode — devourer as an infrastructure access point a real Linux station associates with: beacon → probe/auth/assoc → DHCP/ARP/ICMP → ping, open or WPA2-PSK (4-way handshake + software CCMP), validated against rtw88.
- Scheduled MAC — four measured contracts under a slot scheduler: submit→air guard time, dynamic beacon grants, hardware ACK/TxReport, per-UE RX attribution.
- Multi-AP cellular — what the shared clock enables: coordinated cells, make-before-break handover, roaming robot UEs.
Measurement & instrumentation:
- LA-mode IQ capture — raw complex baseband into the TX packet buffer; per-tone H(k)/CSI offline, from the dongle alone.
- Spatial diversity, bench testing near-field — measurement guides for the built-in radio instrumentation.
- Beamforming self-sounding — per-subcarrier CSI from two adapters via the VHT sounding exchange; and its sibling victim sensing — motion sensing from captured beamforming reports.
- Adapter doctor — dying-dongle triage: EFUSE read-stability, firmware-boot and RX-smoke probes with a HEALTHY / SUSPECT / FAILING verdict.
- Performance — devourer vs. kernel driver on startup time, on-air throughput, and host CPU (3–4× lower); the TX submission modes and the tuning levers, with the methodology.
Chip specifics & internals:
- 8822E quirks — the RTL8812EU/8822EU definitive quirks list: what the chip needs, what devourer does, the reproducers.
- 8852C quirks — the same for the Wi-Fi 6 die, including which sensing facilities are 2.4 GHz-only and why.
- Logging — the two-plane output schema: JSONL machine events on stdout, human diagnostics on stderr.
Headless selftests run with ctest. Hardware regression is
tests/regress.py: a TX/RX matrix between devourer and the kernel driver
across plugged-in adapters, with optional full-pair, encoding-sweep, and
third-adapter-sniffer modes — see tests/README.md. It
covers the MediaTek backend too (--mt7612u-fw-dir, and --tx-pid/--rx-pid
accept a sysfs id so two adapters of one model can be told apart).
GPL-2.0. See LICENSE.