diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index b8a8eed..95e3a72 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -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 diff --git a/.gitignore b/.gitignore index f8ef70f..ccc49dd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ __pycache__/ .venv/ .pytest_cache/ reference/ + +# Local lab notes / Claude Code state (never commit) +INVENTORY.md +.claude/ diff --git a/CLAUDE.md b/CLAUDE.md index 2f2eda8..eab9906 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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`) @@ -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 @@ -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, @@ -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 @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index e717085..e027def 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 @@ -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) @@ -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 ) diff --git a/README.md b/README.md index 2efa64e..8dbf4b0 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ Devourer targets **RTL8812AU**, **RTL8811AU**, **RTL8814AU**, and **RTL8821AU** — all members of Realtek's first-generation 802.11ac silicon family, internally codenamed **"Jaguar"**. The HAL, register-table layout, firmware-download plumbing, and -`SET_TX_DESC_*_8812` macros in `src/FrameParser.h` are shared across the -family; chip-specific EEPROM handling, firmware blobs, and RF tables are +`SET_TX_DESC_*_8812` macros in `src/jaguar1/FrameParser.h` are shared across +the family; chip-specific EEPROM handling, firmware blobs, and RF tables are layered on top. It also targets the second-generation **Jaguar3** parts through a @@ -109,6 +109,29 @@ root. `libusb_wrap_sys_device`). Forks RX into a child, TX-loops a hardcoded beacon in the parent. +### Selecting which chips to build + +All chips are compiled in by default. Turn off the ones you don't need to +drop their firmware blobs and generated PHY tables and shrink the binary — +an 8812AU-only `WiFiDriverDemo` is ~1.0 MB versus ~2.6 MB with everything on. + +| CMake option | default | chips | +|---|---|---| +| `DEVOURER_JAGUAR1` | ON | RTL8812AU / 8811AU / 8821AU | +| `DEVOURER_8814` | ON | RTL8814AU (requires `DEVOURER_JAGUAR1`) | +| `DEVOURER_JAGUAR3_8822C` | ON | RTL8812CU / 8822CU | +| `DEVOURER_JAGUAR3_8822E` | ON | RTL8812EU / 8822EU | + +```sh +# 8812AU/8811AU/8821AU only +cmake -S . -B build -DDEVOURER_8814=OFF \ + -DDEVOURER_JAGUAR3_8822C=OFF -DDEVOURER_JAGUAR3_8822E=OFF +``` + +Configure fails if no chip is selected, or if `DEVOURER_8814` is on without +`DEVOURER_JAGUAR1`. A chip whose support isn't built is rejected at runtime +(the factory returns `nullptr` and logs which). + ### Demo env vars Common to both demos: @@ -233,8 +256,17 @@ hal/ Vendor headers and tables ported from Realtek's tree Hal8812a_TxPwrTrack.[h,cpp] (phydm thermal-meter delta-swing tables) hal8822c_fw.[ch], phydm/rtl8822c/ (Jaguar3 firmware blob + BB/AGC/RF tables) src/ Driver implementation - WiFiDriver thin factory (dispatches Jaguar1 vs Jaguar3 by PID) + Generation-agnostic core (always compiled): + WiFiDriver thin factory (dispatches Jaguar1 vs Jaguar3 by chip-id) IRtlDevice chip-family-agnostic device interface + RtlUsbAdapter libusb wrapper (vendor + bulk transfers) + Radiotap.c radiotap header iterator + RateDefinitions.h MGN_* rate enum (shared by both generations) + RxPacket.h RX packet / descriptor types (shared) + TxDescBits.h little-endian TX-descriptor bit-field macros (shared) + TxMode runtime TX-mode parsing + + jaguar1/ Jaguar1 (8812/8811/8821/8814) HAL RtlJaguarDevice Jaguar1 orchestrator (RX + TX entry points) HalModule chip bring-up / power sequencing RadioManagementModule channel, bandwidth, TX power, up to 4 RF paths @@ -242,19 +274,20 @@ src/ Driver implementation FirmwareManager chip-specific firmware download PhyTableLoader applies chip-cut-conditional BB/AGC tables PowerTracking8812a phydm thermal-meter TX BB-swing compensation - Iqk8812a phydm I/Q calibration for 8812 / 8821 - Iqk8814a phydm I/Q calibration for 8814 (4-path) + Iqk8812a / Iqk8814a phydm I/Q calibration (8812/8821; 8814 4-path) PhydmWatchdog opt-in periodic DM thread (FA stats + DIG) - RtlUsbAdapter libusb wrapper (vendor + bulk transfers) FrameParser RX parsing, TX descriptor layout - Radiotap.c radiotap header iterator - jaguar3/ Jaguar3 (rtl8822c) HAL — RtlJaguar3Device, Hal8822c, + + jaguar3/ Jaguar3 (rtl8822c / rtl8822e) HAL — RtlJaguar3Device, HalMAC firmware download, halrf calibration, 5/10 MHz - narrowband, 8822C TX/RX descriptors + narrowband, per-generation PHY/RF tables + TX/RX descriptors demo/ RX example txdemo/ TX example (Android / Termux pattern) ``` +Each chip generation can be compiled out to shrink the binary — see +**Selecting which chips to build** below. + ## License GPL-2.0. See [LICENSE](LICENSE). diff --git a/demo/main.cpp b/demo/main.cpp index eb12ddf..1b4f051 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -10,8 +10,10 @@ #include -#include "FrameParser.h" -#include "RtlJaguarDevice.h" +#include "RxPacket.h" +#if defined(DEVOURER_HAVE_JAGUAR1) +#include "jaguar1/RtlJaguarDevice.h" +#endif #include "RtlUsbAdapter.h" #include "SignalStop.h" #include "WiFiDriver.h" @@ -39,7 +41,9 @@ static constexpr uint16_t kRealtekProductIds[] = { }; static int g_rx_count = 0; +#if defined(DEVOURER_HAVE_JAGUAR1) static RtlJaguarDevice *g_rtl_device = nullptr; +#endif /* Process-start reference for the init-timing lines (see src/InitTimer.h). * `init-timing: demo.first_rx_frame` is the end-to-end "ready to RX" mark: @@ -202,7 +206,9 @@ static void packetProcessor(const Packet &packet) { hits, g_rx_count, packet.Data.size()); fflush(stdout); } - /* F2: BB-dbgport sweep on the first kCsiMaxFrames canonical-SA frames. */ +#if defined(DEVOURER_HAVE_JAGUAR1) + /* F2: BB-dbgport sweep on the first kCsiMaxFrames canonical-SA frames. + * Jaguar1-only (RtlJaguarDevice); g_rtl_device is null on Jaguar3. */ if (!g_csi_selectors.empty() && g_rtl_device != nullptr && hits <= kCsiMaxFrames && !g_rtl_device->bb_dbgport_wedged()) { for (uint32_t sel : g_csi_selectors) { @@ -219,6 +225,7 @@ static void packetProcessor(const Packet &packet) { } fflush(stdout); } +#endif /* DEVOURER_DUMP_SCRAMBLER=1: print the descrambler seed the chip * recovered from this frame's SERVICE field. Consumed by * tools/precoder/seed_probe.py --mode rx to learn the seed a precoder TX @@ -409,10 +416,18 @@ int main() { WiFiDriver wifi_driver(logger); auto rtlDevice = wifi_driver.CreateRtlDevice(dev_handle, ctx); + if (!rtlDevice) { + /* The factory returns null when the plugged chip's generation wasn't + * compiled in (per-chip CMake options); it already logged which. */ + logger->error("No driver for this chip in this build — exiting"); + return 1; + } logger->info("init-timing: demo.create_device = {} ms", ms_since_start()); - /* The BB-debug-port / queue-depth research helpers are Jaguar1-only, so they - * live on RtlJaguarDevice rather than the IRtlDevice interface. dynamic_cast - * yields nullptr for a Jaguar3 device, which disables those helpers cleanly. */ + /* The BB-debug-port / queue-depth / thermal research helpers are Jaguar1-only, + * so they live on RtlJaguarDevice rather than the IRtlDevice interface. The + * whole block compiles out when Jaguar1 support isn't built; when it is, the + * dynamic_cast yields nullptr for a Jaguar3 device, disabling them cleanly. */ +#if defined(DEVOURER_HAVE_JAGUAR1) g_rtl_device = dynamic_cast(rtlDevice.get()); std::atomic qd_emitter_stop{false}; std::thread qd_emitter; @@ -468,6 +483,7 @@ int main() { } }); } +#endif /* DEVOURER_HAVE_JAGUAR1 */ /* Default channel 36 (5 GHz) for the 8812 reference. Override with * DEVOURER_CHANNEL=N env var (e.g. DEVOURER_CHANNEL=6 for busy 2.4 GHz). */ int channel = 36; diff --git a/hal/drv_types.h b/hal/drv_types.h index cce8e80..8bae4dc 100644 --- a/hal/drv_types.h +++ b/hal/drv_types.h @@ -41,6 +41,11 @@ typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; +/* basic_types.h's cpu_to_le / le_to_cpu macros expand to these; define them + * here (the typedef home) so any TU that includes basic_types.h has them, + * independent of the TX-descriptor headers. */ +typedef uint16_t __u16; +typedef uint32_t __u32; typedef void *PADAPTER; #ifndef WIN32 typedef bool BOOLEAN; diff --git a/src/Firmware.h b/src/Firmware.h index 0d9dd49..be92a7c 100644 --- a/src/Firmware.h +++ b/src/Firmware.h @@ -7,7 +7,9 @@ #include "HalVerDef.h" #define CONFIG_RTL8812A +#if defined(DEVOURER_HAVE_8814) #define CONFIG_RTL8814A +#endif #define LOAD_FW_HEADER_FROM_DRIVER #define ODM_WIN 1 #define DM_ODM_SUPPORT_TYPE ODM_WIN @@ -15,7 +17,9 @@ typedef uint8_t u8; typedef uint32_t u32; extern "C" { #include "hal8812a_fw.h" +#if defined(DEVOURER_HAVE_8814) #include "hal8814a_fw.h" +#endif /* CONFIG_RTL8821A is scoped tightly: hal/rtl8812a_spec.h has a vendor-pattern * `#ifdef CONFIG_RTL8821A #include "rtl8821a_spec.h"` block, and we don't * carry that spec header (8821AU register layout is covered by the shared @@ -36,9 +40,11 @@ struct FirmwareBlob { * IS_FW_HEADER_EXIST_8821 signature (0x2100) — the FW header dispatch in * jaguar_fw_header_present picks the right header parse based on ic_type. */ inline FirmwareBlob PickFirmwareForChip(HAL_IC_TYPE_E ic_type) { +#if defined(DEVOURER_HAVE_8814) if (ic_type == CHIP_8814A) { return {array_mp_8814a_fw_nic, sizeof(array_mp_8814a_fw_nic)}; } +#endif if (ic_type == CHIP_8821) { return {array_mp_8821a_fw_nic, sizeof(array_mp_8821a_fw_nic)}; } diff --git a/src/RateDefinitions.h b/src/RateDefinitions.h new file mode 100644 index 0000000..565cdc0 --- /dev/null +++ b/src/RateDefinitions.h @@ -0,0 +1,97 @@ +#ifndef DEVOURER_RATE_DEFINITIONS_H +#define DEVOURER_RATE_DEFINITIONS_H + +/* Realtek MGN_* rate enumeration — the driver-internal rate index used across + * both chip generations (Jaguar1 RadioManagementModule and Jaguar3 + * RadioManagementJaguar3). Kept in a neutral header so neither generation's + * files depend on the other's. */ +enum MGN_RATE { + MGN_1M = 0x02, + MGN_2M = 0x04, + MGN_5_5M = 0x0B, + MGN_6M = 0x0C, + MGN_9M = 0x12, + MGN_11M = 0x16, + MGN_12M = 0x18, + MGN_18M = 0x24, + MGN_24M = 0x30, + MGN_36M = 0x48, + MGN_48M = 0x60, + MGN_54M = 0x6C, + MGN_MCS32 = 0x7F, + MGN_MCS0, + MGN_MCS1, + MGN_MCS2, + MGN_MCS3, + MGN_MCS4, + MGN_MCS5, + MGN_MCS6, + MGN_MCS7, + MGN_MCS8, + MGN_MCS9, + MGN_MCS10, + MGN_MCS11, + MGN_MCS12, + MGN_MCS13, + MGN_MCS14, + MGN_MCS15, + MGN_MCS16, + MGN_MCS17, + MGN_MCS18, + MGN_MCS19, + MGN_MCS20, + MGN_MCS21, + MGN_MCS22, + MGN_MCS23, + MGN_MCS24, + MGN_MCS25, + MGN_MCS26, + MGN_MCS27, + MGN_MCS28, + MGN_MCS29, + MGN_MCS30, + MGN_MCS31, + MGN_VHT1SS_MCS0, + MGN_VHT1SS_MCS1, + MGN_VHT1SS_MCS2, + MGN_VHT1SS_MCS3, + MGN_VHT1SS_MCS4, + MGN_VHT1SS_MCS5, + MGN_VHT1SS_MCS6, + MGN_VHT1SS_MCS7, + MGN_VHT1SS_MCS8, + MGN_VHT1SS_MCS9, + MGN_VHT2SS_MCS0, + MGN_VHT2SS_MCS1, + MGN_VHT2SS_MCS2, + MGN_VHT2SS_MCS3, + MGN_VHT2SS_MCS4, + MGN_VHT2SS_MCS5, + MGN_VHT2SS_MCS6, + MGN_VHT2SS_MCS7, + MGN_VHT2SS_MCS8, + MGN_VHT2SS_MCS9, + MGN_VHT3SS_MCS0, + MGN_VHT3SS_MCS1, + MGN_VHT3SS_MCS2, + MGN_VHT3SS_MCS3, + MGN_VHT3SS_MCS4, + MGN_VHT3SS_MCS5, + MGN_VHT3SS_MCS6, + MGN_VHT3SS_MCS7, + MGN_VHT3SS_MCS8, + MGN_VHT3SS_MCS9, + MGN_VHT4SS_MCS0, + MGN_VHT4SS_MCS1, + MGN_VHT4SS_MCS2, + MGN_VHT4SS_MCS3, + MGN_VHT4SS_MCS4, + MGN_VHT4SS_MCS5, + MGN_VHT4SS_MCS6, + MGN_VHT4SS_MCS7, + MGN_VHT4SS_MCS8, + MGN_VHT4SS_MCS9, + MGN_UNKNOWN +}; + +#endif /* DEVOURER_RATE_DEFINITIONS_H */ diff --git a/src/RtlUsbAdapter.cpp b/src/RtlUsbAdapter.cpp index dad77ba..e946766 100644 --- a/src/RtlUsbAdapter.cpp +++ b/src/RtlUsbAdapter.cpp @@ -7,7 +7,6 @@ #else #include #endif -#include "FrameParser.h" #include "Hal8812PhyReg.h" #include "logger.h" #include @@ -118,43 +117,6 @@ RtlUsbAdapter::RtlUsbAdapter(libusb_device_handle *dev_handle, Logger_t logger, bInterval 0 */ -std::vector RtlUsbAdapter::infinite_read() { - /* Must cover one full chip-side RX aggregate: the 8814 init programs - * REG_RXDMA_AGG_PG_TH = 0x05 ("dmc agg th 20K"), and the kernel pairs - * that with MAX_RECVBUF_SZ = 32768 (rtl8814a_recv.h:25) — the threshold - * plus the in-flight frame must fit the host read. A 16 KB buffer (an - * exact multiple of wMaxPacketSize, so no short-packet terminates the - * transfer) split >16 KB aggregates and the tail bytes were then parsed - * as an RX descriptor at the head of the next transfer. */ - static constexpr int BUF_SIZE = 32 * 1024; - uint8_t buffer[BUF_SIZE] = {}; - int actual_length = 0; - int rc; - - rc = libusb_bulk_transfer(_dev_handle, _bulk_in_ep, buffer, sizeof(buffer), - &actual_length, USB_TIMEOUT * 10); - - if (rc < 0) { - /* Rate-limit the error log: a fast-failing rc (e.g. LIBUSB_ERROR_NO_DEVICE - * after the chip dropped off USB) used to spin the outer Init() loop at - * full CPU, producing multi-GB log spam in a few seconds. Log every - * Nth failure + sleep enough to keep the loop sane until the caller's - * should_stop fires. */ - static uint64_t err_count = 0; - if ((err_count++ % 100) == 0) { - _logger->error("libusb_bulk_transfer failed with error: {} (count={})", - rc, err_count); - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - - std::vector packets; - FrameParser fp{_logger}; - packets = - fp.recvbuf2recvframe(std::span{buffer, (size_t)actual_length}); - return packets; -} - bool RtlUsbAdapter::WriteBytes(uint16_t reg_num, uint8_t *ptr, size_t size) { if (libusb_control_transfer(_dev_handle, REALTEK_USB_VENQT_WRITE, 5, reg_num, 0, ptr, size, USB_TIMEOUT) == size) { @@ -386,7 +348,7 @@ void RtlUsbAdapter::InitDvObj() { * descriptor offers a different IN endpoint, so libusb's * submit_bulk_transfer to 0x81 would return "endpoint not found on any * open interface". Capture whatever IN endpoint the chip actually - * exposes and use it in infinite_read(). */ + * exposes and use it in bulk_read_raw(). */ if (is_bulk && (endPointAddr & LIBUSB_ENDPOINT_IN) && !found_bulk_in) { _bulk_in_ep = endPointAddr; found_bulk_in = true; diff --git a/src/RtlUsbAdapter.h b/src/RtlUsbAdapter.h index f390f24..a6131a9 100644 --- a/src/RtlUsbAdapter.h +++ b/src/RtlUsbAdapter.h @@ -8,7 +8,6 @@ #include #include -#include "FrameParser.h" #include "drv_types.h" #include "hal_com_reg.h" #include "logger.h" @@ -90,7 +89,6 @@ class RtlUsbAdapter { void bulk_clear_halt(uint8_t ep) { libusb_clear_halt(_dev_handle, ep); } int bulk_send_sync_ep(uint8_t ep, uint8_t *packet, size_t length, int timeout_ms); - std::vector infinite_read(); /* Raw bulk-IN read on the discovered bulk-IN endpoint, returning bytes read * (or a negative libusb error). For chip families whose RX descriptor is not * the Jaguar1 layout (e.g. Jaguar3), the caller parses the buffer itself. */ diff --git a/src/RxPacket.h b/src/RxPacket.h new file mode 100644 index 0000000..07057d2 --- /dev/null +++ b/src/RxPacket.h @@ -0,0 +1,71 @@ +#ifndef DEVOURER_RX_PACKET_H +#define DEVOURER_RX_PACKET_H + +#include +#include + +/* Parsed RX packet types, shared across chip generations. The per-generation + * frame parsers (Jaguar1 FrameParser, Jaguar3 FrameParserJaguar3) both produce + * `Packet`s; the USB adapter and demos consume them. Kept neutral so neither + * generation depends on the other's parser header. */ + +enum class RX_PACKET_TYPE +{ + NORMAL_RX, /* Normal rx packet */ + TX_REPORT1, /* CCX */ + TX_REPORT2, /* TX RPT */ + HIS_REPORT, /* USB HISR RPT */ + C2H_PACKET +}; + +struct rx_pkt_attrib +{ + uint16_t pkt_len; + bool physt; + uint8_t drvinfo_sz; + uint8_t shift_sz; + bool qos; + uint8_t priority; + bool mdata; + uint16_t seq_num; + uint8_t frag_num; + bool mfrag; + bool bdecrypted; + uint8_t encrypt; /* when 0 indicate no encrypt. when non-zero, indicate the + encrypt algorith */ + bool crc_err; + bool icv_err; + /* TSF low (4 bytes at RX-descriptor offset 20) — chip-side timestamp + * for the frame. With the seq_num just above it, a downstream layer + * can drop duplicates by seq and measure one-way latency by diffing + * the chip's TSF against its own wall clock. Populated by + * FrameParser; surfaced through demo/main.cpp's . */ + uint32_t tsfl; + uint8_t data_rate; + uint8_t bw; + uint8_t stbc; + uint8_t ldpc; + uint8_t sgi; + /* Descrambler seed the chip recovered from this frame's SERVICE field. + * Trustworthy only on RTL8814AU (see FrameParser.cpp); surfaced for the + * DEVOURER_DUMP_SCRAMBLER hook in demo/main.cpp. */ + uint8_t scrambler; + /* RSSI / SNR per RF path: A, B (Jaguar 8812/8811) plus C, D (8814AU). On + * non-8814 chips the [2..3] slots are zero — the upstream RX phy-status + * report reserves those bytes when only 2 paths are active. */ + uint8_t rssi[4]; + int8_t snr[4]; + /* Per-stream RX EVM (A,B on 8812/8811; plus C,D on 8814). Raw RX-status + * bytes — a link-quality metric, NOT a per-subcarrier or content signal. + * Surfaced for the DEVOURER_DUMP_BODY Tier-2 health diagnostic. */ + int8_t evm[4]; + RX_PACKET_TYPE pkt_rpt_type; +}; + +struct Packet +{ + rx_pkt_attrib RxAtrib; + std::span Data; +}; + +#endif /* DEVOURER_RX_PACKET_H */ diff --git a/src/TxDescBits.h b/src/TxDescBits.h new file mode 100644 index 0000000..386b79a --- /dev/null +++ b/src/TxDescBits.h @@ -0,0 +1,56 @@ +#ifndef DEVOURER_TX_DESC_BITS_H +#define DEVOURER_TX_DESC_BITS_H + +/* Little-endian TX-descriptor bit-field write helpers, shared across chip + * generations (Jaguar1 FrameParser SET_TX_DESC_*_8812 macros and Jaguar3 + * FrameParserJaguar3 SET_TX_DESC_*_8822C macros all build on + * SET_BITS_TO_LE_4BYTE). Kept neutral so neither generation depends on the + * other's parser header. The cpu_to_le and le32_to_cpu helpers used by the macro + * bodies come from hal/basic_types.h at the expansion site (as before). */ + +typedef unsigned int __u32; + +#ifndef __u16 +typedef unsigned short __u16; +#endif + +typedef signed char s8; +typedef unsigned char u8; + +typedef signed short s16; +typedef unsigned short u16; + +typedef signed int s32; +typedef unsigned int u32; + +#define WriteLE4Byte(_ptr, _val) ((*((u32*)(_ptr))) = cpu_to_le32(_val)) +#define WriteLE2Byte(_ptr, _val) ((*((u16*)(_ptr))) = cpu_to_le16(_val)) +#define WriteLE1Byte(_ptr, _val) ((*((u8*)(_ptr))) = ((u8)(_val))) + +#ifndef BIT_LEN_MASK_32 +#define BIT_LEN_MASK_32(__BitLen) ((u32)(0xFFFFFFFF >> (32 - (__BitLen)))) +#endif + +#ifndef LE_P4BYTE_TO_HOST_4BYTE +#define LE_P4BYTE_TO_HOST_4BYTE(__pStart) (le32_to_cpu(*((u32*)(__pStart)))) +#endif + +#define BIT_OFFSET_LEN_MASK_32(__BitOffset, __BitLen) ((u32)(BIT_LEN_MASK_32(__BitLen) << (__BitOffset))) + +#define LE_BITS_CLEARED_TO_4BYTE(__pStart, __BitOffset, __BitLen) \ + (LE_P4BYTE_TO_HOST_4BYTE(__pStart) & (~BIT_OFFSET_LEN_MASK_32(__BitOffset, __BitLen))) + +#define SET_BITS_TO_LE_4BYTE(__pStart, __BitOffset, __BitLen, __Value) \ + do \ + { \ + if (__BitOffset == 0 && __BitLen == 32) \ + WriteLE4Byte(__pStart, __Value); \ + else \ + { \ + WriteLE4Byte(__pStart, \ + LE_BITS_CLEARED_TO_4BYTE(__pStart, __BitOffset, __BitLen) | \ + ((((u32)__Value) & BIT_LEN_MASK_32(__BitLen)) << (__BitOffset))); \ + } \ + } while (0) + +#endif /* DEVOURER_TX_DESC_BITS_H */ diff --git a/src/TxMode.cpp b/src/TxMode.cpp index 02d1b28..a3578cf 100644 --- a/src/TxMode.cpp +++ b/src/TxMode.cpp @@ -1,6 +1,6 @@ #include "TxMode.h" -#include "RadioManagementModule.h" // enum MGN_RATE +#include "RateDefinitions.h" // enum MGN_RATE namespace devourer { diff --git a/src/WiFiDriver.cpp b/src/WiFiDriver.cpp index c18454b..0296c3d 100644 --- a/src/WiFiDriver.cpp +++ b/src/WiFiDriver.cpp @@ -5,9 +5,13 @@ #include -#include "RtlJaguarDevice.h" #include "RtlUsbAdapter.h" +#if defined(DEVOURER_HAVE_JAGUAR1) +#include "jaguar1/RtlJaguarDevice.h" +#endif +#if defined(DEVOURER_HAVE_JAGUAR3) #include "jaguar3/RtlJaguar3Device.h" +#endif namespace { @@ -44,16 +48,47 @@ WiFiDriver::CreateRtlDevice(libusb_device_handle *dev_handle, uint8_t chip_id = read_chip_id(dev_handle); if (chip_id == 0x13 || chip_id == 0x17) { +#if defined(DEVOURER_HAVE_JAGUAR3) + /* Reject the variant whose support wasn't compiled in (0x13 = 8822C, + * 0x17 = 8822E) rather than construct a device whose PHY-table / + * calibration dispatchers were built out. */ +#if !defined(DEVOURER_HAVE_JAGUAR3_8822C) + if (chip_id == 0x13) { + _logger->error("RTL8822C (chip-id 0x13) support not compiled in " + "(DEVOURER_JAGUAR3_8822C=OFF)"); + return nullptr; + } +#endif +#if !defined(DEVOURER_HAVE_JAGUAR3_8822E) + if (chip_id == 0x17) { + _logger->error("RTL8822E (chip-id 0x17) support not compiled in " + "(DEVOURER_JAGUAR3_8822E=OFF)"); + return nullptr; + } +#endif auto variant = chip_id == 0x17 ? jaguar3::ChipVariant::C8822E : jaguar3::ChipVariant::C8822C; _logger->info("Creating RtlJaguar3Device (PID 0x{:04x}, chip-id 0x{:02x})", pid, chip_id); return std::make_unique( RtlUsbAdapter(dev_handle, _logger, ctx), _logger, variant); +#else + _logger->error("Jaguar3 chip (chip-id 0x{:02x}) detected but Jaguar3 " + "support not compiled in", + chip_id); + return nullptr; +#endif } +#if defined(DEVOURER_HAVE_JAGUAR1) _logger->info("Creating RtlJaguarDevice (PID 0x{:04x}, chip-id 0x{:02x})", pid, chip_id); return std::make_unique(RtlUsbAdapter(dev_handle, _logger), _logger); +#else + _logger->error("Jaguar1 chip (PID 0x{:04x}, chip-id 0x{:02x}) detected but " + "Jaguar1 support not compiled in", + pid, chip_id); + return nullptr; +#endif } diff --git a/src/BbDbgportReader.cpp b/src/jaguar1/BbDbgportReader.cpp similarity index 100% rename from src/BbDbgportReader.cpp rename to src/jaguar1/BbDbgportReader.cpp diff --git a/src/BbDbgportReader.h b/src/jaguar1/BbDbgportReader.h similarity index 100% rename from src/BbDbgportReader.h rename to src/jaguar1/BbDbgportReader.h diff --git a/src/EepromManager.cpp b/src/jaguar1/EepromManager.cpp similarity index 100% rename from src/EepromManager.cpp rename to src/jaguar1/EepromManager.cpp diff --git a/src/EepromManager.h b/src/jaguar1/EepromManager.h similarity index 100% rename from src/EepromManager.h rename to src/jaguar1/EepromManager.h diff --git a/src/FirmwareManager.cpp b/src/jaguar1/FirmwareManager.cpp similarity index 100% rename from src/FirmwareManager.cpp rename to src/jaguar1/FirmwareManager.cpp diff --git a/src/FirmwareManager.h b/src/jaguar1/FirmwareManager.h similarity index 100% rename from src/FirmwareManager.h rename to src/jaguar1/FirmwareManager.h diff --git a/src/FrameParser.cpp b/src/jaguar1/FrameParser.cpp similarity index 100% rename from src/FrameParser.cpp rename to src/jaguar1/FrameParser.cpp diff --git a/src/FrameParser.h b/src/jaguar1/FrameParser.h similarity index 80% rename from src/FrameParser.h rename to src/jaguar1/FrameParser.h index f74f3c7..053bf4a 100644 --- a/src/FrameParser.h +++ b/src/jaguar1/FrameParser.h @@ -6,20 +6,8 @@ #include #include -typedef unsigned int __u32; - -#ifndef __u16 -typedef unsigned short __u16; -#endif - -typedef signed char s8; -typedef unsigned char u8; - -typedef signed short s16; -typedef unsigned short u16; - -typedef signed int s32; -typedef unsigned int u32; +#include "TxDescBits.h" +#include "RxPacket.h" #define ETH_ALEN 6 #define TXDESC_SIZE 40 @@ -30,31 +18,6 @@ typedef unsigned int u32; #define LSG BIT(26) #define QSEL_SHT 8 -#define WriteLE4Byte(_ptr, _val) ((*((u32*)(_ptr))) = cpu_to_le32(_val)) -#define WriteLE2Byte(_ptr, _val) ((*((u16*)(_ptr))) = cpu_to_le16(_val)) -#define WriteLE1Byte(_ptr, _val) ((*((u8*)(_ptr))) = ((u8)(_val))) - -#define BIT_LEN_MASK_32(__BitLen) ((u32)(0xFFFFFFFF >> (32 - (__BitLen)))) - -#define LE_P4BYTE_TO_HOST_4BYTE(__pStart) (le32_to_cpu(*((u32*)(__pStart)))) - -#define BIT_OFFSET_LEN_MASK_32(__BitOffset, __BitLen) ((u32)(BIT_LEN_MASK_32(__BitLen) << (__BitOffset))) - -#define LE_BITS_CLEARED_TO_4BYTE(__pStart, __BitOffset, __BitLen) \ - (LE_P4BYTE_TO_HOST_4BYTE(__pStart) & (~BIT_OFFSET_LEN_MASK_32(__BitOffset, __BitLen))) - -#define SET_BITS_TO_LE_4BYTE(__pStart, __BitOffset, __BitLen, __Value) \ - do \ - { \ - if (__BitOffset == 0 && __BitLen == 32) \ - WriteLE4Byte(__pStart, __Value); \ - else \ - { \ - WriteLE4Byte(__pStart, \ - LE_BITS_CLEARED_TO_4BYTE(__pStart, __BitOffset, __BitLen) | \ - ((((u32)__Value) & BIT_LEN_MASK_32(__BitLen)) << (__BitOffset))); \ - } \ - } while (0) #define SET_TX_DESC_TX_DESC_CHECKSUM_8812(__pTxDesc, __Value) SET_BITS_TO_LE_4BYTE(__pTxDesc + 28, 0, 16, __Value) @@ -259,64 +222,6 @@ enum _PUBLIC_ACTION ACT_PUBLIC_MAX }; -enum class RX_PACKET_TYPE -{ - NORMAL_RX, /* Normal rx packet */ - TX_REPORT1, /* CCX */ - TX_REPORT2, /* TX RPT */ - HIS_REPORT, /* USB HISR RPT */ - C2H_PACKET -}; - -struct rx_pkt_attrib -{ - uint16_t pkt_len; - bool physt; - uint8_t drvinfo_sz; - uint8_t shift_sz; - bool qos; - uint8_t priority; - bool mdata; - uint16_t seq_num; - uint8_t frag_num; - bool mfrag; - bool bdecrypted; - uint8_t encrypt; /* when 0 indicate no encrypt. when non-zero, indicate the - encrypt algorith */ - bool crc_err; - bool icv_err; - /* TSF low (4 bytes at RX-descriptor offset 20) — chip-side timestamp - * for the frame. With the seq_num just above it, a downstream layer - * can drop duplicates by seq and measure one-way latency by diffing - * the chip's TSF against its own wall clock. Populated by - * FrameParser; surfaced through demo/main.cpp's . */ - uint32_t tsfl; - uint8_t data_rate; - uint8_t bw; - uint8_t stbc; - uint8_t ldpc; - uint8_t sgi; - /* Descrambler seed the chip recovered from this frame's SERVICE field. - * Trustworthy only on RTL8814AU (see FrameParser.cpp); surfaced for the - * DEVOURER_DUMP_SCRAMBLER hook in demo/main.cpp. */ - uint8_t scrambler; - /* RSSI / SNR per RF path: A, B (Jaguar 8812/8811) plus C, D (8814AU). On - * non-8814 chips the [2..3] slots are zero — the upstream RX phy-status - * report reserves those bytes when only 2 paths are active. */ - uint8_t rssi[4]; - int8_t snr[4]; - /* Per-stream RX EVM (A,B on 8812/8811; plus C,D on 8814). Raw RX-status - * bytes — a link-quality metric, NOT a per-subcarrier or content signal. - * Surfaced for the DEVOURER_DUMP_BODY Tier-2 health diagnostic. */ - int8_t evm[4]; - RX_PACKET_TYPE pkt_rpt_type; -}; - -struct Packet -{ - rx_pkt_attrib RxAtrib; - std::span Data; -}; class FrameParser { diff --git a/src/HalModule.cpp b/src/jaguar1/HalModule.cpp similarity index 99% rename from src/HalModule.cpp rename to src/jaguar1/HalModule.cpp index 4e37309..c9c8329 100644 --- a/src/HalModule.cpp +++ b/src/jaguar1/HalModule.cpp @@ -3,7 +3,9 @@ #include "FirmwareManager.h" #include "InitTimer.h" #include "Hal8812PhyReg.h" +#if defined(DEVOURER_HAVE_8814) #include "Hal8814_PhyTables.h" +#endif #include "Hal8821PhyReg.h" #include "PhyTableLoader.h" #include "phydm_pre_define.h" @@ -667,9 +669,11 @@ bool HalModule::InitPowerOn() { * false. */ WLAN_PWR_CFG *enable_flow; switch (_eepromManager->version_id.ICType) { +#if defined(DEVOURER_HAVE_8814) case CHIP_8814A: enable_flow = rtl8814A_card_enable_flow; break; +#endif case CHIP_8821: enable_flow = Rtl8821A_NIC_ENABLE_FLOW; break; @@ -941,15 +945,18 @@ void HalModule::PHY_MACConfig8812() { } void HalModule::odm_read_and_config_mp_8814a_mac_reg() { +#if defined(DEVOURER_HAVE_8814) auto ctx = _eepromManager->GetPhyContext(); PhyTableLoader::Load(array_mp_8814a_mac_reg, array_mp_8814a_mac_reg_len, ctx, [this](uint32_t addr, uint32_t value) { _device.rtw_write8(static_cast(addr), static_cast(value)); }); +#endif } void HalModule::odm_read_and_config_mp_8814a_phy_reg() { +#if defined(DEVOURER_HAVE_8814) auto ctx = _eepromManager->GetPhyContext(); /* odm_config_bb_phy_8812a is chip-agnostic for the phydm special addresses * 0xfe/fd/fc/fb/fa/f9 (sleep/delay opcodes shared across Realtek chips) and @@ -959,14 +966,17 @@ void HalModule::odm_read_and_config_mp_8814a_phy_reg() { [this](uint32_t addr, uint32_t value) { odm_config_bb_phy_8812a(addr, 0xFFFFFFFFu, value); }); +#endif } void HalModule::odm_read_and_config_mp_8814a_agc_tab() { +#if defined(DEVOURER_HAVE_8814) auto ctx = _eepromManager->GetPhyContext(); PhyTableLoader::Load(array_mp_8814a_agc_tab, array_mp_8814a_agc_tab_len, ctx, [this](uint32_t addr, uint32_t value) { odm_config_bb_agc_8812a(addr, 0xFFFFFFFFu, value); }); +#endif } bool HalModule::phy_BB8814_Config_ParaFile() { @@ -2642,6 +2652,7 @@ void HalModule::phy_RF6052_Config_ParaFile_8814() { } void HalModule::odm_read_and_config_mp_8814a_radioa() { +#if defined(DEVOURER_HAVE_8814) auto ctx = _eepromManager->GetPhyContext(); PhyTableLoader::Load( array_mp_8814a_radioa, array_mp_8814a_radioa_len, ctx, @@ -2649,9 +2660,11 @@ void HalModule::odm_read_and_config_mp_8814a_radioa() { odm_config_rf_reg_8812a(addr, value, RfPath::RF_PATH_A, static_cast(addr)); }); +#endif } void HalModule::odm_read_and_config_mp_8814a_radiob() { +#if defined(DEVOURER_HAVE_8814) auto ctx = _eepromManager->GetPhyContext(); PhyTableLoader::Load( array_mp_8814a_radiob, array_mp_8814a_radiob_len, ctx, @@ -2659,9 +2672,11 @@ void HalModule::odm_read_and_config_mp_8814a_radiob() { odm_config_rf_reg_8812a(addr, value, RfPath::RF_PATH_B, static_cast(addr)); }); +#endif } void HalModule::odm_read_and_config_mp_8814a_radioc() { +#if defined(DEVOURER_HAVE_8814) auto ctx = _eepromManager->GetPhyContext(); PhyTableLoader::Load( array_mp_8814a_radioc, array_mp_8814a_radioc_len, ctx, @@ -2669,9 +2684,11 @@ void HalModule::odm_read_and_config_mp_8814a_radioc() { odm_config_rf_reg_8812a(addr, value, RfPath::RF_PATH_C, static_cast(addr)); }); +#endif } void HalModule::odm_read_and_config_mp_8814a_radiod() { +#if defined(DEVOURER_HAVE_8814) auto ctx = _eepromManager->GetPhyContext(); PhyTableLoader::Load( array_mp_8814a_radiod, array_mp_8814a_radiod_len, ctx, @@ -2679,6 +2696,7 @@ void HalModule::odm_read_and_config_mp_8814a_radiod() { odm_config_rf_reg_8812a(addr, value, RfPath::RF_PATH_D, static_cast(addr)); }); +#endif } void HalModule::phy_RF6052_Config_ParaFile_8812() { diff --git a/src/HalModule.h b/src/jaguar1/HalModule.h similarity index 100% rename from src/HalModule.h rename to src/jaguar1/HalModule.h diff --git a/src/Iqk8812a.cpp b/src/jaguar1/Iqk8812a.cpp similarity index 100% rename from src/Iqk8812a.cpp rename to src/jaguar1/Iqk8812a.cpp diff --git a/src/Iqk8812a.h b/src/jaguar1/Iqk8812a.h similarity index 100% rename from src/Iqk8812a.h rename to src/jaguar1/Iqk8812a.h diff --git a/src/Iqk8814a.cpp b/src/jaguar1/Iqk8814a.cpp similarity index 100% rename from src/Iqk8814a.cpp rename to src/jaguar1/Iqk8814a.cpp diff --git a/src/Iqk8814a.h b/src/jaguar1/Iqk8814a.h similarity index 100% rename from src/Iqk8814a.h rename to src/jaguar1/Iqk8814a.h diff --git a/src/PhyTableLoader.cpp b/src/jaguar1/PhyTableLoader.cpp similarity index 100% rename from src/PhyTableLoader.cpp rename to src/jaguar1/PhyTableLoader.cpp diff --git a/src/PhyTableLoader.h b/src/jaguar1/PhyTableLoader.h similarity index 100% rename from src/PhyTableLoader.h rename to src/jaguar1/PhyTableLoader.h diff --git a/src/PhydmWatchdog.cpp b/src/jaguar1/PhydmWatchdog.cpp similarity index 100% rename from src/PhydmWatchdog.cpp rename to src/jaguar1/PhydmWatchdog.cpp diff --git a/src/PhydmWatchdog.h b/src/jaguar1/PhydmWatchdog.h similarity index 100% rename from src/PhydmWatchdog.h rename to src/jaguar1/PhydmWatchdog.h diff --git a/src/PowerTracking8812a.cpp b/src/jaguar1/PowerTracking8812a.cpp similarity index 100% rename from src/PowerTracking8812a.cpp rename to src/jaguar1/PowerTracking8812a.cpp diff --git a/src/PowerTracking8812a.h b/src/jaguar1/PowerTracking8812a.h similarity index 100% rename from src/PowerTracking8812a.h rename to src/jaguar1/PowerTracking8812a.h diff --git a/src/RadioManagementModule.cpp b/src/jaguar1/RadioManagementModule.cpp similarity index 99% rename from src/RadioManagementModule.cpp rename to src/jaguar1/RadioManagementModule.cpp index 7c4537f..ae52e90 100644 --- a/src/RadioManagementModule.cpp +++ b/src/jaguar1/RadioManagementModule.cpp @@ -52,8 +52,12 @@ RadioManagementModule::RadioManagementModule( Logger_t logger) : _device{device}, _eepromManager{eepromManager}, _logger{logger}, _pwrTrk{device, eepromManager, this, logger}, - _iqk{device, eepromManager, this, logger}, - _iqk8814{device, eepromManager, this, logger} {} + _iqk{device, eepromManager, this, logger} +#if defined(DEVOURER_HAVE_8814) + , + _iqk8814{device, eepromManager, this, logger} +#endif +{} void RadioManagementModule::RunIQK() { _iqk.Calibrate(_currentChannel, current_band_type, /*is_recovery=*/false); @@ -531,10 +535,13 @@ void RadioManagementModule::phy_SwChnlAndSetBwMode8812() { if (_eepromManager->version_id.ICType == CHIP_8812) { _iqk.Calibrate(_currentChannel, current_band_type, /*is_recovery=*/false); - } else if (_eepromManager->version_id.ICType == CHIP_8814A) { + } +#if defined(DEVOURER_HAVE_8814) + else if (_eepromManager->version_id.ICType == CHIP_8814A) { _iqk8814.Calibrate(_currentChannel, current_band_type, /*is_recovery=*/false); } +#endif } _needIQK = false; timer.stage("iqk"); diff --git a/src/RadioManagementModule.h b/src/jaguar1/RadioManagementModule.h similarity index 88% rename from src/RadioManagementModule.h rename to src/jaguar1/RadioManagementModule.h index 15e4928..680cc88 100644 --- a/src/RadioManagementModule.h +++ b/src/jaguar1/RadioManagementModule.h @@ -6,7 +6,9 @@ #include "EepromManager.h" #include "Iqk8812a.h" +#if defined(DEVOURER_HAVE_8814) #include "Iqk8814a.h" +#endif #include "PowerTracking8812a.h" #include "RfPath.h" #include "RtlUsbAdapter.h" @@ -48,94 +50,7 @@ enum RATE_SECTION { RATE_SECTION_NUM, }; -enum MGN_RATE { - MGN_1M = 0x02, - MGN_2M = 0x04, - MGN_5_5M = 0x0B, - MGN_6M = 0x0C, - MGN_9M = 0x12, - MGN_11M = 0x16, - MGN_12M = 0x18, - MGN_18M = 0x24, - MGN_24M = 0x30, - MGN_36M = 0x48, - MGN_48M = 0x60, - MGN_54M = 0x6C, - MGN_MCS32 = 0x7F, - MGN_MCS0, - MGN_MCS1, - MGN_MCS2, - MGN_MCS3, - MGN_MCS4, - MGN_MCS5, - MGN_MCS6, - MGN_MCS7, - MGN_MCS8, - MGN_MCS9, - MGN_MCS10, - MGN_MCS11, - MGN_MCS12, - MGN_MCS13, - MGN_MCS14, - MGN_MCS15, - MGN_MCS16, - MGN_MCS17, - MGN_MCS18, - MGN_MCS19, - MGN_MCS20, - MGN_MCS21, - MGN_MCS22, - MGN_MCS23, - MGN_MCS24, - MGN_MCS25, - MGN_MCS26, - MGN_MCS27, - MGN_MCS28, - MGN_MCS29, - MGN_MCS30, - MGN_MCS31, - MGN_VHT1SS_MCS0, - MGN_VHT1SS_MCS1, - MGN_VHT1SS_MCS2, - MGN_VHT1SS_MCS3, - MGN_VHT1SS_MCS4, - MGN_VHT1SS_MCS5, - MGN_VHT1SS_MCS6, - MGN_VHT1SS_MCS7, - MGN_VHT1SS_MCS8, - MGN_VHT1SS_MCS9, - MGN_VHT2SS_MCS0, - MGN_VHT2SS_MCS1, - MGN_VHT2SS_MCS2, - MGN_VHT2SS_MCS3, - MGN_VHT2SS_MCS4, - MGN_VHT2SS_MCS5, - MGN_VHT2SS_MCS6, - MGN_VHT2SS_MCS7, - MGN_VHT2SS_MCS8, - MGN_VHT2SS_MCS9, - MGN_VHT3SS_MCS0, - MGN_VHT3SS_MCS1, - MGN_VHT3SS_MCS2, - MGN_VHT3SS_MCS3, - MGN_VHT3SS_MCS4, - MGN_VHT3SS_MCS5, - MGN_VHT3SS_MCS6, - MGN_VHT3SS_MCS7, - MGN_VHT3SS_MCS8, - MGN_VHT3SS_MCS9, - MGN_VHT4SS_MCS0, - MGN_VHT4SS_MCS1, - MGN_VHT4SS_MCS2, - MGN_VHT4SS_MCS3, - MGN_VHT4SS_MCS4, - MGN_VHT4SS_MCS5, - MGN_VHT4SS_MCS6, - MGN_VHT4SS_MCS7, - MGN_VHT4SS_MCS8, - MGN_VHT4SS_MCS9, - MGN_UNKNOWN -}; +#include "RateDefinitions.h" /* Read-only snapshot of the chip's thermal meter. `raw` is the live * RF[A][0x42][15:10] reading (0..63, Realtek "thermal units" — roughly @@ -212,7 +127,9 @@ class RadioManagementModule { int txpwr_override_ = -1; PowerTracking8812a _pwrTrk; Iqk8812a _iqk; +#if defined(DEVOURER_HAVE_8814) Iqk8814a _iqk8814; +#endif public: RadioManagementModule(RtlUsbAdapter device, diff --git a/src/RtlJaguarDevice.cpp b/src/jaguar1/RtlJaguarDevice.cpp similarity index 95% rename from src/RtlJaguarDevice.cpp rename to src/jaguar1/RtlJaguarDevice.cpp index ee1b71d..11ad8a7 100644 --- a/src/RtlJaguarDevice.cpp +++ b/src/jaguar1/RtlJaguarDevice.cpp @@ -375,6 +375,27 @@ bool RtlJaguarDevice::send_packet(const uint8_t *packet, size_t length) { return resp; } +std::vector RtlJaguarDevice::read_frames() { + /* Cover one full chip-side RX aggregate (the 8814 pairs a 20K DMA-agg + * threshold with a 32K host read). 32K buffer, an exact multiple of + * wMaxPacketSize so no short packet truncates the transfer. */ + static constexpr int BUF_SIZE = 32 * 1024; + uint8_t buffer[BUF_SIZE] = {}; + int n = _device.bulk_read_raw(buffer, sizeof(buffer), USB_TIMEOUT * 10); + if (n < 0) { + /* Rate-limit the error log + sleep so a fast-failing rc (e.g. NO_DEVICE + * after the chip drops off USB) can't spin the RX loop at full CPU. */ + static uint64_t err_count = 0; + if ((err_count++ % 100) == 0) + _logger->error("bulk_read_raw failed with error: {} (count={})", n, + err_count); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + n = 0; + } + FrameParser fp{_logger}; + return fp.recvbuf2recvframe(std::span{buffer, (size_t)n}); +} + void RtlJaguarDevice::Init(Action_ParsedRadioPacket packetProcessor, SelectedChannel channel) { _packetProcessor = packetProcessor; @@ -389,7 +410,7 @@ void RtlJaguarDevice::Init(Action_ParsedRadioPacket packetProcessor, * transfer there is a window between transfers where NO URB is posted; the * 8814's RX-DMA ring pauses in that gap and, under sparse traffic, * intermittently wedges after a short burst — the "RX stalls at ~10 frames" - * bug (8812/8821 tolerate it, the 8814 does not). infinite_read() is + * bug (8812/8821 tolerate it, the 8814 does not). read_frames() is * reentrant (local buffer + local FrameParser; libusb is thread-safe), so a * small worker pool restores the always-posted behaviour. Frame order across * workers is not preserved, which is fine for monitor-mode RX. Set @@ -404,7 +425,7 @@ void RtlJaguarDevice::Init(Action_ParsedRadioPacket packetProcessor, for (int i = 0; i < rx_workers; ++i) { workers.emplace_back([this, &proc_mu]() { while (!should_stop && !g_devourer_should_stop) { - auto packets = _device.infinite_read(); + auto packets = read_frames(); if (packets.empty()) continue; std::lock_guard lk(proc_mu); diff --git a/src/RtlJaguarDevice.h b/src/jaguar1/RtlJaguarDevice.h similarity index 95% rename from src/RtlJaguarDevice.h rename to src/jaguar1/RtlJaguarDevice.h index a67b0b0..7e0b0c1 100644 --- a/src/RtlJaguarDevice.h +++ b/src/jaguar1/RtlJaguarDevice.h @@ -136,6 +136,12 @@ class RtlJaguarDevice : public IRtlDevice { void StartWithMonitorMode(SelectedChannel selectedChannel); bool NetDevOpen(SelectedChannel selectedChannel); + /* One bulk-IN read + Jaguar1 RX-descriptor parse. Reentrant (local buffer + + * local FrameParser), so the RX worker pool can call it concurrently. The + * generation-agnostic USB read lives in RtlUsbAdapter::bulk_read_raw; the + * Jaguar1-specific parse stays here (Jaguar3 parses via FrameParserJaguar3). */ + std::vector read_frames(); + std::array, 5> _qd_snap{}; std::thread _qd_thread; std::atomic _qd_stop{false}; diff --git a/src/jaguar3/FrameParserJaguar3.h b/src/jaguar3/FrameParserJaguar3.h index 9f374bc..d178500 100644 --- a/src/jaguar3/FrameParserJaguar3.h +++ b/src/jaguar3/FrameParserJaguar3.h @@ -6,7 +6,8 @@ #include #include "basic_types.h" /* cpu_to_le32 / le16_to_cpu used by the LE macros */ -#include "FrameParser.h" /* SET_BITS_TO_LE_4BYTE / LE_BITS_TO_4BYTE */ +#include "TxDescBits.h" /* SET_BITS_TO_LE_4BYTE / LE_BITS_TO_4BYTE */ +#include "RxPacket.h" /* Packet / rx_pkt_attrib / RX_PACKET_TYPE */ /* Jaguar3 (8822C/8812EU/8822EU) TX/RX descriptor layout. * diff --git a/src/jaguar3/HalmacJaguar3Fw.cpp b/src/jaguar3/HalmacJaguar3Fw.cpp index 130fb8f..1cfa98d 100644 --- a/src/jaguar3/HalmacJaguar3Fw.cpp +++ b/src/jaguar3/HalmacJaguar3Fw.cpp @@ -9,8 +9,12 @@ #include "FrameParserJaguar3.h" #include "HalmacJaguar3Regs.h" +#if defined(DEVOURER_HAVE_JAGUAR3_8822C) #include "hal8822c_fw.h" /* array_mp_8822c_fw_nic[] + _len */ +#endif +#if defined(DEVOURER_HAVE_JAGUAR3_8822E) #include "hal8822e_fw.h" /* array_mp_8822e_fw_nic[] + _len */ +#endif using namespace jaguar3::halmac; @@ -35,9 +39,16 @@ HalmacJaguar3Fw::HalmacJaguar3Fw(RtlUsbAdapter device, Logger_t logger, bool HalmacJaguar3Fw::download_default_firmware() { /* The DLFW state machine is identical for 8822c/8822e (registers verified * byte-identical); only the firmware image differs. */ +#if defined(DEVOURER_HAVE_JAGUAR3_8822E) if (_variant == ChipVariant::C8822E) return download_firmware(array_mp_8822e_fw_nic, array_mp_8822e_fw_nic_len); +#endif +#if defined(DEVOURER_HAVE_JAGUAR3_8822C) return download_firmware(array_mp_8822c_fw_nic, array_mp_8822c_fw_nic_len); +#else + _logger->error("Jaguar3: no firmware image compiled for this chip variant"); + return false; +#endif } /* --- register helpers (HALMAC_REG_R/W*) --- */ diff --git a/src/jaguar3/Halrf8822c.cpp b/src/jaguar3/Halrf8822c.cpp index 24113e4..9858051 100644 --- a/src/jaguar3/Halrf8822c.cpp +++ b/src/jaguar3/Halrf8822c.cpp @@ -4,12 +4,15 @@ #include #include +#if defined(DEVOURER_HAVE_JAGUAR3_8822C) extern "C" { #include "Hal8822c_IqkNctl.h" } +#endif namespace jaguar3 { +#if defined(DEVOURER_HAVE_JAGUAR3_8822C) namespace { /* IQK type indices (halrf_iqk_8822c.h: TXIQK/RXIQK; halrf_iqk.h: trigger steps) */ constexpr int TXIQK = 0; @@ -39,20 +42,33 @@ inline uint32_t mask_shift(uint32_t mask) { Halrf8822c::Halrf8822c(RtlUsbAdapter device, Logger_t logger) : _device{device}, _logger{logger} {} +#endif /* DEVOURER_HAVE_JAGUAR3_8822C */ +#if defined(DEVOURER_HAVE_JAGUAR3_8822E) /* Defined in Halrf8822e.cpp. */ std::unique_ptr make_halrf_8822e(RtlUsbAdapter device, Logger_t logger); +#endif /* Calibration strategy factory: pick the per-generation halrf impl. */ std::unique_ptr make_jaguar3_calibration(ChipVariant variant, RtlUsbAdapter device, Logger_t logger) { +#if defined(DEVOURER_HAVE_JAGUAR3_8822E) if (variant == ChipVariant::C8822E) return make_halrf_8822e(device, logger); +#endif +#if defined(DEVOURER_HAVE_JAGUAR3_8822C) return std::make_unique(device, logger); +#else + (void)variant; + (void)device; + (void)logger; + return nullptr; +#endif } +#if defined(DEVOURER_HAVE_JAGUAR3_8822C) uint32_t Halrf8822c::bb_get(uint16_t addr, uint32_t mask) { return (_device.rtw_read32(addr) & mask) >> mask_shift(mask); } @@ -1330,5 +1346,6 @@ void Halrf8822c::coex_wlan_only_init() { force_wl_antenna(); _logger->info("Jaguar3: coex WiFi-only init applied (antenna locked to WL)"); } +#endif /* DEVOURER_HAVE_JAGUAR3_8822C */ } /* namespace jaguar3 */ diff --git a/src/jaguar3/Phy8822cTables.cpp b/src/jaguar3/Phy8822cTables.cpp index 76863f4..ac5d7c3 100644 --- a/src/jaguar3/Phy8822cTables.cpp +++ b/src/jaguar3/Phy8822cTables.cpp @@ -1,9 +1,12 @@ #include "Jaguar3PhyTables.h" +#if defined(DEVOURER_HAVE_JAGUAR3_8822C) #include "Hal8822c_PhyTables.h" /* array_mp_8822c_* + _len */ +#endif namespace jaguar3 { +#if defined(DEVOURER_HAVE_JAGUAR3_8822C) namespace { /* rtl8822c (RTL8812CU/8822CU) phydm BB/AGC/RF + cal-init tables, generated by @@ -28,14 +31,24 @@ class Phy8822cTables : public Jaguar3PhyTables { }; } /* namespace */ +#endif +#if defined(DEVOURER_HAVE_JAGUAR3_8822E) /* Defined in Phy8822eTables.cpp. */ std::unique_ptr make_phy_8822e_tables(); +#endif std::unique_ptr make_jaguar3_phy_tables(ChipVariant variant) { +#if defined(DEVOURER_HAVE_JAGUAR3_8822E) if (variant == ChipVariant::C8822E) return make_phy_8822e_tables(); +#endif +#if defined(DEVOURER_HAVE_JAGUAR3_8822C) return std::make_unique(); +#else + (void)variant; + return nullptr; +#endif } } /* namespace jaguar3 */ diff --git a/src/jaguar3/RadioManagementJaguar3.cpp b/src/jaguar3/RadioManagementJaguar3.cpp index 1d65268..a3baea5 100644 --- a/src/jaguar3/RadioManagementJaguar3.cpp +++ b/src/jaguar3/RadioManagementJaguar3.cpp @@ -3,8 +3,10 @@ #include #include -#include "RadioManagementModule.h" /* MGN_* rate enum */ +#include "RateDefinitions.h" /* MGN_* rate enum */ +#if defined(DEVOURER_HAVE_JAGUAR3_8822E) #include "Hal8822e_PhyTables.h" /* array_mp_8822e_phy_reg_pg */ +#endif extern "C" { #include "ieee80211_radiotap.h" /* MRateToHwRate */ @@ -151,6 +153,7 @@ void RadioManagementJaguar3::set_tx_power_ref(uint8_t idx, bool zero_diffs) { * CCK reference, not the OFDM/HT/VHT 0x3a00 diff table). Path-B addresses * (0xE20..) carry identical values to path A, so only the 0xC2x/0xC3x/0xC4x set * is needed to fill the (path-shared) 0x3a00 table. */ +#if defined(DEVOURER_HAVE_JAGUAR3_8822E) static bool pg_addr_to_rates(uint32_t addr, std::array &rates) { switch (addr) { case 0xC24: rates = {MGN_6M, MGN_9M, MGN_12M, MGN_18M}; return true; @@ -183,10 +186,12 @@ static bool pg_addr_to_rates(uint32_t addr, std::array &rates) { return false; /* 0xC20 = CCK, or a path-B / unknown address */ } } +#endif /* DEVOURER_HAVE_JAGUAR3_8822E */ void RadioManagementJaguar3::apply_power_by_rate_8822e(uint8_t channel, uint8_t ref_a, uint8_t ref_b) { +#if defined(DEVOURER_HAVE_JAGUAR3_8822E) /* Port of the phy_reg_pg (power-by-rate) apply that devourer's table walk * skips. The 8822e TXAGC is ref + per-rate diff: the OFDM/HT/VHT reference * lives in 0x18e8 (A)/0x41e8 (B), and the signed per-rate diff table at @@ -253,6 +258,11 @@ void RadioManagementJaguar3::apply_power_by_rate_8822e(uint8_t channel, _logger->info("Jaguar3: applied phy_reg_pg power-by-rate (band {}, {} rate " "groups, ref A=0x{:02x} B=0x{:02x}, MCS7 anchor 0x{:02x})", band, groups, ref_a, ref_b, anchor); +#else + (void)channel; + (void)ref_a; + (void)ref_b; +#endif } void RadioManagementJaguar3::set_bandwidth_dividers(ChannelWidth_t bwmode) { diff --git a/src/jaguar3/RtlJaguar3Device.cpp b/src/jaguar3/RtlJaguar3Device.cpp index 74799e6..4e3dd26 100644 --- a/src/jaguar3/RtlJaguar3Device.cpp +++ b/src/jaguar3/RtlJaguar3Device.cpp @@ -5,7 +5,7 @@ #include #include "FrameParserJaguar3.h" -#include "RadioManagementModule.h" /* MGN_* rate enum (shared across the family) */ +#include "RateDefinitions.h" /* MGN_* rate enum (shared across the family) */ #include "SignalStop.h" /* g_devourer_should_stop — set by demo signal handlers */ extern "C" { diff --git a/tests/in_process_hotplug_wrap.cpp b/tests/in_process_hotplug_wrap.cpp index f765d34..e5bf935 100644 --- a/tests/in_process_hotplug_wrap.cpp +++ b/tests/in_process_hotplug_wrap.cpp @@ -71,10 +71,9 @@ #include #include "logger.h" -#include "FrameParser.h" +#include "RxPacket.h" #include "RtlUsbAdapter.h" #include "WiFiDriver.h" -#include "RtlJaguarDevice.h" namespace { diff --git a/tests/refactor_rx_check.sh b/tests/refactor_rx_check.sh new file mode 100755 index 0000000..2c3f9b3 --- /dev/null +++ b/tests/refactor_rx_check.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# Controlled devourer-TX -> devourer-RX smoke: one adapter injects the canonical +# beacon (SA 57:42:75:05:d6:00) while each target adapter receives on the same +# channel. Confirms the RX read+parse path (and factory dispatch) works per chip +# without needing the kernel driver or ambient traffic — used to validate the +# src/jaguar1 split refactor broke no chip's RX. +# +# Usage: sudo tests/refactor_rx_check.sh [RX_PID ...] +set -u + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +RX_BIN="$ROOT/build/WiFiDriverDemo" +TX_BIN="$ROOT/build/WiFiDriverTxDemo" +DWELL=14 # seconds each RX cell listens +TX_PID="${1:?TX pid, e.g. 0x8812}" +CH="${2:?channel, e.g. 6}" +shift 2 +CANONICAL_SA_HIT="devourer-tx-hit" + +TX_PROC="" +cleanup() { + [ -n "$TX_PROC" ] && kill "$TX_PROC" 2>/dev/null + pkill -f "WiFiDriverTxDemo" 2>/dev/null + pkill -f "WiFiDriverDemo" 2>/dev/null + wait 2>/dev/null +} +trap cleanup EXIT INT TERM + +echo "== controlled RX check: TX=$TX_PID ch=$CH ==" + +for RX_PID in "$@"; do + # start the beacon source + DEVOURER_PID="$TX_PID" DEVOURER_CHANNEL="$CH" DEVOURER_TX_RATE=6M \ + "$TX_BIN" >/tmp/rxchk-tx.log 2>&1 & + TX_PROC=$! + sleep 3 # let TX init + start emitting + + RX_LOG="/tmp/rxchk-rx-${RX_PID//0x/}.log" + timeout "$DWELL" env DEVOURER_PID="$RX_PID" DEVOURER_CHANNEL="$CH" \ + "$RX_BIN" >"$RX_LOG" 2>&1 + + frames=$(grep -cE "devourer-stream|RX #|first_rx_frame" "$RX_LOG" 2>/dev/null) + hits=$(grep -cE "$CANONICAL_SA_HIT" "$RX_LOG" 2>/dev/null) + inited=$(grep -cE "monitor RX config applied|first_rx_frame|read loop|create_device" "$RX_LOG" 2>/dev/null) + if [ "$hits" -gt 0 ]; then + verdict="PASS (canonical beacon received: $hits hits)" + elif [ "$frames" -gt 0 ]; then + verdict="PASS (RX frames captured: $frames)" + elif [ "$inited" -gt 0 ]; then + verdict="INIT-OK / no-frame (init succeeded, saw no frame in ${DWELL}s)" + else + verdict="FAIL (no init)" + fi + printf " RX %-8s ch %-3s -> %s\n" "$RX_PID" "$CH" "$verdict" + + kill "$TX_PROC" 2>/dev/null; TX_PROC="" + pkill -f "WiFiDriverTxDemo" 2>/dev/null + sleep 2 +done diff --git a/txdemo/main.cpp b/txdemo/main.cpp index 775aa50..80b5efc 100644 --- a/txdemo/main.cpp +++ b/txdemo/main.cpp @@ -26,8 +26,10 @@ #include #endif -#include "FrameParser.h" -#include "RtlJaguarDevice.h" +#include "RxPacket.h" +#if defined(DEVOURER_HAVE_JAGUAR1) +#include "jaguar1/RtlJaguarDevice.h" +#endif #include "RtlUsbAdapter.h" #include "SignalStop.h" #include "WiFiDriver.h" @@ -317,13 +319,22 @@ int main(int argc, char **argv) { WiFiDriver wifi_driver{logger}; auto rtlDevice = wifi_driver.CreateRtlDevice(handle); + if (!rtlDevice) { + /* Factory returns null when this chip's generation wasn't compiled in + * (per-chip CMake options); it already logged which. */ + logger->error("No driver for this chip in this build — exiting"); + return 1; + } logger->info("init-timing: txdemo.create_device = {} ms", ms_since_start()); /* Jaguar1-only research features (TX-mode default, fast-retune hopping, * thermal telemetry, TXAGC override, BB-reg probe) are not part of the * IRtlDevice contract — reach them by downcasting. jag is null on Jaguar3, - * where those call sites are skipped. */ + * where those call sites are skipped, and compiled out entirely when Jaguar1 + * support isn't built. */ +#if defined(DEVOURER_HAVE_JAGUAR1) RtlJaguarDevice *jag = dynamic_cast(rtlDevice.get()); +#endif int channel = 161; if (const char *ch_env = std::getenv("DEVOURER_CHANNEL")) { @@ -547,7 +558,9 @@ int main(int argc, char **argv) { long pwr_next_step_ms = 0; bool txpwr_readback = std::getenv("DEVOURER_TX_PWR_READBACK") != nullptr; auto apply_txpwr = [&](int idx) { - if (!jag) /* TXAGC override is a Jaguar1 (RtlJaguarDevice) feature */ + /* TXAGC override is a Jaguar1 (RtlJaguarDevice) feature; a no-op otherwise. */ +#if defined(DEVOURER_HAVE_JAGUAR1) + if (!jag) return; jag->SetTxPowerOverride(idx); jag->ApplyTxPower(); /* SetMonitorChannel early-returns on same ch */ @@ -564,6 +577,9 @@ int main(int argc, char **argv) { ofdm6m); } fflush(stdout); +#else + (void)idx; +#endif }; if (const char *e = std::getenv("DEVOURER_TX_PWR_START")) { pwr_ramp = true; @@ -634,10 +650,14 @@ int main(int argc, char **argv) { tx_buf = build_frame_with_channel(chan_to_freq(ch), beacon_frame + 10, sizeof(beacon_frame) - 10); mode = " radiotap"; - } else if (hop_fast && jag) { + } +#if defined(DEVOURER_HAVE_JAGUAR1) + else if (hop_fast && jag) { jag->FastRetune(static_cast(ch), /*cache_rf=*/hop_fast != 2); mode = " fast"; - } else { + } +#endif + else { rtlDevice->SetMonitorChannel(SelectedChannel{ .Channel = static_cast(ch), .ChannelOffset = init_offset, @@ -662,7 +682,8 @@ int main(int argc, char **argv) { fflush(stdout); } /* Thermal telemetry is a Jaguar1 (RtlJaguarDevice) feature; skip on - * Jaguar3, where jag is null. */ + * Jaguar3, where jag is null; compiled out when Jaguar1 isn't built. */ +#if defined(DEVOURER_HAVE_JAGUAR1) if (jag && thermal_every > 0 && tx_count % thermal_every == 0) { auto t = jag->GetThermalStatus(); if (t.valid) { @@ -683,6 +704,7 @@ int main(int argc, char **argv) { } fflush(stdout); } +#endif /* DEVOURER_HAVE_JAGUAR1 */ if (rc) { consec_fail = 0; } else if (++consec_fail >= kMaxConsecFail) { diff --git a/txdemo/precoder_demo/main.cpp b/txdemo/precoder_demo/main.cpp index d633ee9..919ba2e 100644 --- a/txdemo/precoder_demo/main.cpp +++ b/txdemo/precoder_demo/main.cpp @@ -53,7 +53,6 @@ #include #endif -#include "FrameParser.h" #include "RtlUsbAdapter.h" #include "WiFiDriver.h" #include "logger.h" diff --git a/txdemo/stream_duplex_demo/main.cpp b/txdemo/stream_duplex_demo/main.cpp index d877260..b6f0e69 100644 --- a/txdemo/stream_duplex_demo/main.cpp +++ b/txdemo/stream_duplex_demo/main.cpp @@ -58,10 +58,12 @@ #include #endif -#include "FrameParser.h" +#include "RxPacket.h" #include "RadiotapBuilder.h" #include "RtlUsbAdapter.h" -#include "RtlJaguarDevice.h" +#if defined(DEVOURER_HAVE_JAGUAR1) +#include "jaguar1/RtlJaguarDevice.h" +#endif #include "WiFiDriver.h" #include "logger.h" #include "stream_stdin.h" @@ -203,11 +205,14 @@ static void tx_thread(TxArgs args) { break; uint8_t op = ctl[0]; if (op == 1 && clen >= 2) { // SET_PWR - /* TXAGC override is a Jaguar1 (RtlJaguarDevice) feature. */ + /* TXAGC override is a Jaguar1 (RtlJaguarDevice) feature; the SET_PWR + * control op is a no-op when Jaguar1 support isn't built. */ +#if defined(DEVOURER_HAVE_JAGUAR1) if (auto *jag = dynamic_cast(args.rtl)) { jag->SetTxPowerOverride(ctl[1]); jag->ApplyTxPower(); } +#endif } else if (op == 2 && clen >= 2) { // SET_RATE std::string spec(ctl.begin() + 1, ctl.end()); auto rt = devourer::build_stream_radiotap(devourer::parse_tx_mode_str(spec)); diff --git a/txdemo/stream_tx_demo/main.cpp b/txdemo/stream_tx_demo/main.cpp index 455b4cf..56607f3 100644 --- a/txdemo/stream_tx_demo/main.cpp +++ b/txdemo/stream_tx_demo/main.cpp @@ -63,10 +63,11 @@ #include #endif -#include "FrameParser.h" #include "RadiotapBuilder.h" #include "RtlUsbAdapter.h" -#include "RtlJaguarDevice.h" +#if defined(DEVOURER_HAVE_JAGUAR1) +#include "jaguar1/RtlJaguarDevice.h" +#endif #include "WiFiDriver.h" #include "logger.h" #include "stream_stdin.h" @@ -183,8 +184,11 @@ int main(int argc, char **argv) { WiFiDriver wifi_driver{logger}; auto rtlDevice = wifi_driver.CreateRtlDevice(handle); /* Jaguar1-only research features (TXAGC override, fast-retune hopping) aren't - * on the IRtlDevice contract — downcast for them; jag is null on Jaguar3. */ + * on the IRtlDevice contract — downcast for them; jag is null on Jaguar3, and + * the downcast plus its call sites compile out when Jaguar1 isn't built. */ +#if defined(DEVOURER_HAVE_JAGUAR1) RtlJaguarDevice *jag = dynamic_cast(rtlDevice.get()); +#endif int channel = 6; if (const char *ch_env = std::getenv("DEVOURER_CHANNEL")) { @@ -211,12 +215,14 @@ int main(int argc, char **argv) { * in tests/fused_fec_onair.sh). Applied once and held, unlike * WiFiDriverTxDemo's DEVOURER_TX_PWR_START ramp. Must follow InitWrite so the * channel-set has run; ApplyTxPower re-pushes the index to the registers. */ +#if defined(DEVOURER_HAVE_JAGUAR1) if (const char *o = std::getenv("DEVOURER_TX_PWR_OVERRIDE"); o && jag) { int idx = std::atoi(o); jag->SetTxPowerOverride(idx); jag->ApplyTxPower(); logger->info("DEVOURER_TX_PWR_OVERRIDE — forced absolute TXAGC index {}", idx); } +#endif /* Channel hopping for frequency diversity. DEVOURER_HOP_CHANNELS="1,6,11" * cycles the TX channel every DEVOURER_HOP_DWELL_FRAMES PSDUs (default 1 = * per-packet hop, which spreads an outer-FEC block's shards across channels @@ -311,9 +317,11 @@ int main(int argc, char **argv) { * it per packet is fine. */ if (!hop_channels.empty()) { int ch = hop_channels[(tx_count / hop_dwell) % hop_channels.size()]; +#if defined(DEVOURER_HAVE_JAGUAR1) if (hop_fast && jag) jag->FastRetune(static_cast(ch), /*cache_rf=*/hop_fast != 2); else +#endif rtlDevice->SetMonitorChannel(SelectedChannel{ .Channel = static_cast(ch), .ChannelOffset = 0, diff --git a/txdemo/svc_tx_demo/main.cpp b/txdemo/svc_tx_demo/main.cpp index 0cb4262..203306d 100644 --- a/txdemo/svc_tx_demo/main.cpp +++ b/txdemo/svc_tx_demo/main.cpp @@ -56,7 +56,6 @@ #include #endif -#include "FrameParser.h" #include "RadiotapBuilder.h" #include "RtlUsbAdapter.h" #include "WiFiDriver.h"