Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/cmake-multi-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,67 @@ jobs:
- name: Test
working-directory: build
run: ctest --output-on-failure

# Per-chip compile-time selection (CMakeLists DEVOURER_* options). The default
# matrix above already covers all-chips-ON; this job builds each stripped
# config so a missing #if guard (which only fails to link when a chip's
# sources are dropped) is caught, plus the two invalid configs that must
# fail configure with a FATAL_ERROR.
build-configs:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: jaguar1-only
flags: "-DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF"
- name: jaguar1+8814
flags: "-DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF"
- name: 8822c-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822E=OFF"
- name: 8822e-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF -DDEVOURER_JAGUAR3_8822C=OFF"
- name: jaguar3-only
flags: "-DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF"
steps:
- uses: actions/checkout@v4

- name: Install dependency libraries
run: sudo apt install libusb-1.0-0-dev

- name: Configure (${{ matrix.name }})
run: cmake -B build -DCMAKE_BUILD_TYPE=Release ${{ matrix.flags }} -S ${{ github.workspace }}

- name: Build (${{ matrix.name }})
run: cmake --build build

- name: Test (${{ matrix.name }})
working-directory: build
run: ctest --output-on-failure

# Invalid option combinations must fail at configure time, not silently
# produce a broken binary.
reject-bad-configs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install dependency libraries
run: sudo apt install libusb-1.0-0-dev

- name: Reject "no chip selected"
run: |
if cmake -B build-none -DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=OFF \
-DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF \
-S "${{ github.workspace }}" 2>err.log; then
echo "ERROR: configure should have failed (no chip selected)"; cat err.log; exit 1
fi
grep -q "No chip support selected" err.log

- name: Reject "8814 without jaguar1"
run: |
if cmake -B build-bad -DDEVOURER_JAGUAR1=OFF -DDEVOURER_8814=ON \
-S "${{ github.workspace }}" 2>err.log; then
echo "ERROR: configure should have failed (8814 without jaguar1)"; cat err.log; exit 1
fi
grep -q "requires DEVOURER_JAGUAR1" err.log
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ __pycache__/
.venv/
.pytest_cache/
reference/

# Local lab notes / Claude Code state (never commit)
INVENTORY.md
.claude/
40 changes: 30 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ path with `RFType=RF_TYPE_1T1R` selected via `REG_SYS_CFG` bit 27),
**RTL8814AU** (4T4R RF / 3-SS baseband; host-pushed TX requires the
on-chip 3081 MCU, which devourer boots during firmware download —
a failed FW-boot poll means dead TX while RX still works), and
**RTL8821AU** (1T1R AC + BT combo). These share one HAL (`src/HalModule`,
`src/RtlJaguarDevice`) with chip-family branches.
**RTL8821AU** (1T1R AC + BT combo). These share one HAL (`src/jaguar1/`,
e.g. `HalModule`, `RtlJaguarDevice`) with chip-family branches.

**Jaguar3.** A second, self-contained HAL under `src/jaguar3/` covering two PHY
generations that share one core: **rtl8822c** (RTL8812CU / RTL8822CU, `0bda:c812`)
Expand Down Expand Up @@ -52,11 +52,22 @@ cmake -S . -B build
cmake --build build -j
```

Per-chip build options select which drivers are compiled in — all default ON,
so the command above builds every chip. Turn off the ones you don't need to
drop their firmware blobs + PHY tables (an 8812AU-only `WiFiDriverDemo` is
~1.0 MB vs ~2.6 MB): `DEVOURER_JAGUAR1` (8812/8811/8821), `DEVOURER_8814`
(requires JAGUAR1), `DEVOURER_JAGUAR3_8822C`, `DEVOURER_JAGUAR3_8822E`.
Configure fails on no-chip-selected or 8814-without-JAGUAR1. Each group exports
a PUBLIC `DEVOURER_HAVE_*` define; sites referencing a dropped group are behind
`#if defined(DEVOURER_HAVE_*)`, and the factory returns `nullptr` (logs) for a
chip whose support isn't built.

libusb-1.0 is required: `pkg-config` on Linux/macOS, vcpkg on Windows
(`VCPKG_ROOT` must be set so the toolchain file resolves). CI matrix builds
across GCC/Clang/MSVC on Ubuntu/macOS/Windows, plus a separate `build-mingw`
job (mingw-w64 via MSYS2, libusb from pkg-config) covering the Windows-GCC
toolchain the MSVC matrix cell doesn't
toolchain the MSVC matrix cell doesn't; the `build-configs` matrix builds each
per-chip subset and `reject-bad-configs` asserts the invalid option combos fail
(`.github/workflows/cmake-multi-platform.yml`). `ctest` runs in every CI job;
the one registered test (`stream_stdin_binary`) round-trips the stream demos'
binary-stdin framing (`txdemo/stream_stdin.h`) headlessly, so a Windows
Expand Down Expand Up @@ -251,7 +262,20 @@ C8822E). Each orchestrator drives bring-up, RX, and TX through its own HAL.
`RtlJaguarDevice` was previously named `Rtl8812aDevice` — a deprecated alias
still exists for one release cycle.

Module layout in `src/`:
Generation-agnostic core in `src/` (always compiled; depends on neither
generation's HAL):

- `WiFiDriver` — the factory (`CreateRtlDevice`).
- `RtlUsbAdapter` — libusb wrapper (vendor control + bulk transfers).
- `Radiotap.c` — radiotap header iterator. TX buffers passed to
`send_packet` **must** begin with a radiotap header; rate / MCS / VHT /
STBC / LDPC / SGI / bandwidth are read from it.
- `RateDefinitions.h` (`MGN_RATE`), `RxPacket.h` (`Packet` + RX descriptor
types), `TxDescBits.h` (`SET_BITS_TO_LE_4BYTE` + LE bit-field helpers) —
the symbols both generations' parsers build on, kept neutral so neither
generation's header pulls in the other's.

The Jaguar1 HAL lives under `src/jaguar1/`:

- `RtlJaguarDevice` — top-level orchestrator (Init / InitWrite / send_packet).
- `HalModule` — chip bring-up, power sequencing, BB/AGC/RF table application,
Expand All @@ -266,12 +290,8 @@ Module layout in `src/`:
- `PhyTableLoader` — runtime walker for Realtek's phydm-format register
tables (opcode-encoded conditional blocks). Replicates upstream phydm's
`check_positive` + state machine **without pulling in phydm itself**.
- `RtlUsbAdapter` — libusb wrapper (vendor control + bulk transfers).
- `FrameParser` — RX parsing and TX descriptor layout (`SET_TX_DESC_*_8812`
macros are shared across the Jaguar family).
- `Radiotap.c` — radiotap header iterator. TX buffers passed to
`send_packet` **must** begin with a radiotap header; rate / MCS / VHT /
STBC / LDPC / SGI / bandwidth are read from it.

The Jaguar3 HAL is a parallel, self-contained set under `src/jaguar3/`. The core
uses generation-neutral names; per-generation behaviour is behind two strategy
Expand All @@ -295,8 +315,8 @@ interfaces selected by `ChipVariant`:
**generated** from the upstream aircrack-ng/rtl8814au source by
`tools/extract_8814a_phy_tables.py`; the Jaguar3 tables under
`hal/phydm/rtl8822{c,e}/` by `tools/extract_8822c_phy_tables.py`. Edit the
generators, not the output. The runtime parser is `src/PhyTableLoader` (Jaguar1)
/ `src/jaguar3/PhyTableLoaderJaguar3`, not the upstream phydm parser.
generators, not the output. The runtime parser is `src/jaguar1/PhyTableLoader`
(Jaguar1) / `src/jaguar3/PhyTableLoaderJaguar3`, not the upstream phydm parser.

## Hardware gotchas

Expand Down
204 changes: 108 additions & 96 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,94 +10,55 @@ project(Rtl8812auNet)

enable_testing()

# ---------------------------------------------------------------------------
# Per-chip build options. All default ON (backward compatible: a plain
# `cmake -S . -B build` builds every chip). Turn off the ones you don't need to
# drop their firmware blobs + generated PHY tables and shrink the binary — e.g.
# an 8812AU-only build:
# cmake -S . -B build -DDEVOURER_8814=OFF \
# -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF
# ---------------------------------------------------------------------------
option(DEVOURER_JAGUAR1 "RTL8812AU / 8811AU / 8821AU (Jaguar1 core)" ON)
option(DEVOURER_8814 "RTL8814AU (4T4R; requires DEVOURER_JAGUAR1)" ON)
option(DEVOURER_JAGUAR3_8822C "RTL8812CU / 8822CU (Jaguar3, rtl8822c)" ON)
option(DEVOURER_JAGUAR3_8822E "RTL8812EU / 8822EU (Jaguar3, rtl8822e)" ON)

if(DEVOURER_8814 AND NOT DEVOURER_JAGUAR1)
message(FATAL_ERROR
"DEVOURER_8814=ON requires DEVOURER_JAGUAR1=ON (8814 reuses the Jaguar1 HAL).")
endif()
if(NOT DEVOURER_JAGUAR1 AND NOT DEVOURER_JAGUAR3_8822C AND NOT DEVOURER_JAGUAR3_8822E)
message(FATAL_ERROR
"No chip support selected. Enable at least one of DEVOURER_JAGUAR1 / "
"DEVOURER_JAGUAR3_8822C / DEVOURER_JAGUAR3_8822E.")
endif()
# Convenience: the shared Jaguar3 port compiles when either variant is on.
if(DEVOURER_JAGUAR3_8822C OR DEVOURER_JAGUAR3_8822E)
set(DEVOURER_JAGUAR3 ON)
else()
set(DEVOURER_JAGUAR3 OFF)
endif()

# Find pkg-config and then use it to locate libusb.
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)

add_library(WiFiDriver
# --- generation-agnostic core (always compiled) ---
src/logger.h

hal/Hal8812PhyReg.h
hal/Hal8812PwrSeq.c
hal/Hal8812PwrSeq.h
hal/Hal8812a_TxPwrTrack.cpp
hal/Hal8812a_TxPwrTrack.h
hal/Hal8814PwrSeq.c
hal/Hal8814PwrSeq.h
hal/Hal8821APwrSeq.c
hal/Hal8821APwrSeq.h
hal/Hal8821PhyReg.h
hal/basic_types.h
hal/hal8812a_fw.c
hal/hal8812a_fw.h
hal/hal8814a_fw.c
hal/hal8814a_fw.h
hal/hal8821a_fw.c
hal/hal8821a_fw.h
hal/hal8822c_fw.c
hal/hal8822c_fw.h
hal/hal8822e_fw.c
hal/hal8822e_fw.h
hal/hal_com_reg.h
hal/rtl8812a_hal.h
hal/rtl8812a_recv.h
hal/rtl8812a_spec.h

# RTL8814AU phydm-format register tables. The .c file is generated from the
# aircrack-ng/rtl8814au tree by tools/extract_8814a_phy_tables.py; the
# loader at src/PhyTableLoader walks the same conditional encoding the
# upstream phydm parser does, without pulling in phydm itself.
hal/phydm/rtl8814a/Hal8814_PhyTables.c
hal/phydm/rtl8814a/Hal8814_PhyTables.h

# RTL8822C (Jaguar3) phydm-format BB/AGC/RF tables. Generated from the
# rtl88x2cu tree by tools/extract_8822c_phy_tables.py. These use the newer
# "halbb" conditional encoding — walked by src/jaguar3/PhyTableLoaderJaguar3,
# NOT src/PhyTableLoader (which handles the Jaguar1 check_positive format).
hal/phydm/rtl8822c/Hal8822c_PhyTables.c
hal/phydm/rtl8822c/Hal8822c_PhyTables.h
hal/phydm/rtl8822c/Hal8822c_IqkNctl.c
hal/phydm/rtl8822c/Hal8822c_IqkNctl.h

# RTL8822E (Jaguar3 EU) phydm-format BB/AGC/RF tables. Generated from the
# rtl88x2eu tree by `tools/extract_8822c_phy_tables.py --chip 8822e`. Same
# halbb encoding as 8822c (one walker), different DATA.
hal/phydm/rtl8822e/Hal8822e_PhyTables.c
hal/phydm/rtl8822e/Hal8822e_PhyTables.h

src/ieee80211_radiotap.h
src/BbDbgportReader.cpp
src/BbDbgportReader.h
src/EepromManager.cpp
src/EepromManager.h
src/Firmware.h
src/FirmwareManager.cpp
src/FirmwareManager.h
src/FrameParser.cpp
src/FrameParser.h
src/HalModule.cpp
src/HalModule.h
src/Iqk8812a.cpp
src/Iqk8812a.h
src/Iqk8814a.cpp
src/Iqk8814a.h
src/PhydmWatchdog.cpp
src/PhydmWatchdog.h
src/ParsedRadioPacket.cpp
src/PhyTableLoader.cpp
src/PhyTableLoader.h
src/PowerTracking8812a.cpp
src/PowerTracking8812a.h
src/RadioManagementModule.cpp
src/RadioManagementModule.h
src/Radiotap.c
src/RadiotapBuilder.cpp
src/RadiotapBuilder.h
src/RtlJaguarDevice.cpp
src/RtlJaguarDevice.h
src/RtlUsbAdapter.cpp
src/RtlUsbAdapter.h
src/SelectedChannel.h
src/RateDefinitions.h
src/RxPacket.h
src/TxDescBits.h
src/TxMode.cpp
src/TxMode.h
src/IRtlDevice.h
Expand All @@ -106,30 +67,6 @@ add_library(WiFiDriver
src/WiFiDriver.cpp
src/WiFiDriver.h
src/registry_priv.h

# Jaguar3 (RTL8822CU / RTL8812EU / RTL8822EU) port.
src/jaguar3/Jaguar3PhyTables.h
src/jaguar3/Jaguar3Calibration.h
src/jaguar3/Phy8822cTables.cpp
src/jaguar3/Phy8822eTables.cpp
src/jaguar3/RtlJaguar3Device.cpp
src/jaguar3/RtlJaguar3Device.h
src/jaguar3/HalJaguar3.cpp
src/jaguar3/HalJaguar3.h
src/jaguar3/HalmacJaguar3Fw.cpp
src/jaguar3/HalmacJaguar3Fw.h
src/jaguar3/HalmacJaguar3MacInit.cpp
src/jaguar3/HalmacJaguar3MacInit.h
src/jaguar3/Halrf8822c.cpp
src/jaguar3/Halrf8822c.h
src/jaguar3/Halrf8822e.cpp
src/jaguar3/Halrf8822e.h
src/jaguar3/HalmacJaguar3Regs.h
src/jaguar3/RadioManagementJaguar3.cpp
src/jaguar3/RadioManagementJaguar3.h
src/jaguar3/FrameParserJaguar3.h
src/jaguar3/PhyTableLoaderJaguar3.cpp
src/jaguar3/PhyTableLoaderJaguar3.h
)

target_compile_features(WiFiDriver PUBLIC cxx_std_20)
Expand All @@ -143,6 +80,81 @@ target_include_directories(WiFiDriver PUBLIC hal/phydm/rtl8822c)
target_include_directories(WiFiDriver PUBLIC hal/phydm/rtl8822e)
target_include_directories(WiFiDriver PUBLIC src)

# --- per-chip source groups (see options above) ---
if(DEVOURER_JAGUAR1)
target_sources(WiFiDriver PRIVATE
hal/hal8812a_fw.c hal/hal8812a_fw.h
hal/hal8821a_fw.c hal/hal8821a_fw.h
hal/Hal8812PwrSeq.c hal/Hal8812PwrSeq.h
hal/Hal8821APwrSeq.c hal/Hal8821APwrSeq.h
hal/Hal8812a_TxPwrTrack.cpp hal/Hal8812a_TxPwrTrack.h
hal/Hal8812PhyReg.h hal/Hal8821PhyReg.h
hal/rtl8812a_hal.h hal/rtl8812a_recv.h hal/rtl8812a_spec.h
src/Firmware.h
src/jaguar1/RtlJaguarDevice.cpp src/jaguar1/RtlJaguarDevice.h
src/jaguar1/HalModule.cpp src/jaguar1/HalModule.h
src/jaguar1/RadioManagementModule.cpp src/jaguar1/RadioManagementModule.h
src/jaguar1/EepromManager.cpp src/jaguar1/EepromManager.h
src/jaguar1/FirmwareManager.cpp src/jaguar1/FirmwareManager.h
src/jaguar1/Iqk8812a.cpp src/jaguar1/Iqk8812a.h
src/jaguar1/PhydmWatchdog.cpp src/jaguar1/PhydmWatchdog.h
src/jaguar1/PhyTableLoader.cpp src/jaguar1/PhyTableLoader.h
src/jaguar1/PowerTracking8812a.cpp src/jaguar1/PowerTracking8812a.h
src/jaguar1/FrameParser.cpp src/jaguar1/FrameParser.h
src/jaguar1/BbDbgportReader.cpp src/jaguar1/BbDbgportReader.h)
target_compile_definitions(WiFiDriver PUBLIC DEVOURER_HAVE_JAGUAR1=1)
endif()

if(DEVOURER_8814)
target_sources(WiFiDriver PRIVATE
hal/hal8814a_fw.c hal/hal8814a_fw.h
hal/Hal8814PwrSeq.c hal/Hal8814PwrSeq.h
hal/Hal8814PhyReg.h
hal/phydm/rtl8814a/Hal8814_PhyTables.c
hal/phydm/rtl8814a/Hal8814_PhyTables.h
src/jaguar1/Iqk8814a.cpp src/jaguar1/Iqk8814a.h)
target_compile_definitions(WiFiDriver PUBLIC DEVOURER_HAVE_8814=1)
endif()

if(DEVOURER_JAGUAR3)
# Shared Jaguar3 port (compiled when either variant is on). Phy8822cTables.cpp
# and Halrf8822c.cpp also carry the make_jaguar3_*() dispatchers, so they are
# always built when Jaguar3 is on; their per-variant bodies are #if-guarded.
target_sources(WiFiDriver PRIVATE
src/jaguar3/Jaguar3PhyTables.h
src/jaguar3/Jaguar3Calibration.h
src/jaguar3/ChipVariant.h
src/jaguar3/RtlJaguar3Device.cpp src/jaguar3/RtlJaguar3Device.h
src/jaguar3/HalJaguar3.cpp src/jaguar3/HalJaguar3.h
src/jaguar3/HalmacJaguar3Fw.cpp src/jaguar3/HalmacJaguar3Fw.h
src/jaguar3/HalmacJaguar3MacInit.cpp src/jaguar3/HalmacJaguar3MacInit.h
src/jaguar3/HalmacJaguar3Regs.h
src/jaguar3/RadioManagementJaguar3.cpp src/jaguar3/RadioManagementJaguar3.h
src/jaguar3/FrameParserJaguar3.h
src/jaguar3/PhyTableLoaderJaguar3.cpp src/jaguar3/PhyTableLoaderJaguar3.h
src/jaguar3/Phy8822cTables.cpp
src/jaguar3/Halrf8822c.cpp src/jaguar3/Halrf8822c.h)
target_compile_definitions(WiFiDriver PUBLIC DEVOURER_HAVE_JAGUAR3=1)
endif()

if(DEVOURER_JAGUAR3_8822C)
target_sources(WiFiDriver PRIVATE
hal/hal8822c_fw.c hal/hal8822c_fw.h
hal/phydm/rtl8822c/Hal8822c_PhyTables.c hal/phydm/rtl8822c/Hal8822c_PhyTables.h
hal/phydm/rtl8822c/Hal8822c_IqkNctl.c hal/phydm/rtl8822c/Hal8822c_IqkNctl.h)
target_compile_definitions(WiFiDriver PUBLIC DEVOURER_HAVE_JAGUAR3_8822C=1)
endif()

if(DEVOURER_JAGUAR3_8822E)
target_sources(WiFiDriver PRIVATE
hal/hal8822e_fw.c hal/hal8822e_fw.h
hal/phydm/rtl8822e/Hal8822e_PhyTables.c hal/phydm/rtl8822e/Hal8822e_PhyTables.h
src/jaguar3/Phy8822eTables.cpp
src/jaguar3/Halrf8822e.cpp src/jaguar3/Halrf8822e.h)
target_compile_definitions(WiFiDriver PUBLIC DEVOURER_HAVE_JAGUAR3_8822E=1)
endif()


add_executable(WiFiDriverDemo
demo/main.cpp
)
Expand Down
Loading
Loading