devourer
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.
Why devourer
- 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
.ko on 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
DeviceConfig struct 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…).
Supported hardware
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 |
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); 6 GHz TX+RX validated (cells --band 6, with the 8852CE as the peer) |
| RTL8852CU (11ax) |
2T2R + BT |
— |
— |
— |
— |
the Bluetooth-combo SKU of the 8832CU die (same C8852C variant, same code path; the 8832CU is the Wi-Fi-only cut). Not separately benchmarked |
| RTL8852CE (11ax, PCIe) |
2T2R + BT |
— |
— |
— |
— |
M.2 2230 module (10ec:c852); rides the 8852C code path over vfio-pci. Monitor RX + TX injection on-air validated on 5 and 6 GHz against a USB 8852C witness (cells); not benchmarked |
| 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 / RTL8852CU / RTL8852CE add 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:1a2b is the canonical
offender). If the device won’t open, check lsusb; usb_modeswitch flips
it to the real NIC.
Quick start
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 -j
On 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/txdemo
The 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.
Example binaries
| 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 and RTL8852CE.
Using the library
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.
Going deeper
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.
- Stream timing — per-frame air-side timing in the
stream link: backlog, capture→send and a hardware-clocked one-way latency
per frame, carried in header bytes no receiver otherwise reads.
- 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.
- Film-set preview link — twenty-five
cameras into a video village at ~100 ms: why it is a channel plan, where the
latency budget really goes, and what the ecosystem is missing.
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.
Testing
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).
License
GPL-2.0. See LICENSE.
评论