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
13 changes: 7 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ construction from the `SYS_CFG2` chip-id (see **Architecture**):
rides the 8812 path), RTL8814AU (4T4R RF / 3-SS baseband — host-pushed TX
requires the on-chip 3081 MCU booted during FW download; a failed FW-boot
poll means dead TX while RX still works), RTL8821AU (1T1R + BT). **5/10 MHz
narrowband on the 8812 die** (8812AU/8811AU): the 8812A shares the Jaguar2
`0x8ac` ADC/DAC clock-divider block, so the same re-clock trick works even
though the vendor never wired it — TX+RX bench-characterized. The 8821A is
excluded (dividing its DAC clock starves the 1T1R TX DMA/USB path) and the
8814A uses a different BW block; both fall back to 20 MHz. See
`docs/narrowband.md`.
narrowband on the 8812AU/8811AU and the 8814AU**: both share the Jaguar2
`0x8ac` ADC/DAC clock-divider block, so the re-clock trick works even though
the vendor never wired it — TX+RX bench-characterized. The two dies use
different field encodings of the same register (the 8812A divides via
`[9:8]`/`[21:20]`, the 8814A via the 8822B's `[9:8]+[16]`/`[21:20]+[28]`).
The 8821A is excluded (dividing its DAC clock starves the 1T1R TX DMA/USB
path) and falls back to 20 MHz. See `docs/narrowband.md`.
- **Jaguar2** (`src/jaguar2/`): RTL8822BU / RTL8812BU (chip-id `0x0a`) and
RTL8811CU / RTL8821CU (chip 8821C, chip-id `0x09`). A hybrid: HalMAC FW
download / MAC init / power sequencing like Jaguar3, phydm `check_positive`
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ long-range digital video links.
- **Frequency hopping at FHSS speed.** A channel hop costs ~0.5–2.5 ms
depending on chip — fast enough to hop on every packet
([how](docs/frequency-hopping.md)).
- **Narrowband modes the kernel can't do.** 5 and 10 MHz channels — including
on the decade-old RTL8812AU the vendor never gave narrowband — half/quarter
the bandwidth, more range from the same power ([how](docs/narrowband.md)).
- **Narrowband modes the kernel can't do.** 5 and 10 MHz channels on every
supported generation — including the decade-old RTL8812AU and RTL8814AU the
vendor never gave narrowband — half/quarter the bandwidth, more range from
the same power ([how](docs/narrowband.md)).
- **A radio lab in a dongle.** Channel sounding, per-antenna signal quality,
beamforming report capture (enough to do
[motion sensing](docs/beamforming-victim-sensing.md)), spectrum sweeps,
Expand All @@ -51,7 +52,7 @@ Bandwidth cells are devourer's measured on-air TX throughput (Mbps, HT MCS7,
| ----------------------------- | ----------------- | ------------- | ------------- | ---------------- | ------------------------------------------- |
| **RTL8812AU** | 2T2R | 56 | 52 | 52 | [CHANEVE CHW50L](https://www.aliexpress.com/item/4000762461362.html) (`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](docs/measuring-spatial-diversity.md) |
| **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](docs/measuring-spatial-diversity.md). 5/10 MHz capable |
| **RTL8821AU** | 1T1R AC + 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 |
Expand Down
30 changes: 23 additions & 7 deletions docs/narrowband.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ retune** on an already-tuned channel: bring the chip up at 20 MHz, then re-clock
All three generations do the same thing — divide the ADC and DAC clocks and tell
the baseband it is "small BW" — but the register block differs.

### Jaguar1 (RTL8812AU / RTL8811AU) — the shared `0x8ac` block
### Jaguar1 (RTL8812AU / RTL8811AU / RTL8814AU) — the shared `0x8ac` block

The 8812A drives its bandwidth through BB register `0x8ac` (`rRFMOD_Jaguar`)
under mask `0x003003C3`, whose fields are:
Expand Down Expand Up @@ -70,6 +70,19 @@ to the emission, while at 10 MHz an ADC of 1 receives roughly 2.5× better than
The codes are overridable via `DEVOURER_NB_ADC` / `DEVOURER_NB_DAC` for
uncharacterized cuts.

The **RTL8814AU (4T4R)** reaches the same block but with a different field
encoding, and it is instructive. Its `phy_SetBwRegAdc_8814A` only pokes
`0x8ac[1:0]` (a 20/40/80 mode selector with no sub-20 value), so the divider
looks absent — but the vendor's own comment documents the full field set as
`[28, 21:20, 16, 9:6, 1:0]`, which is the **Jaguar2 (8822B) layout**: the ADC
clock is `[9:8]+[16]` and the DAC clock is `[21:20]+[28]`, with the `[16]`/`[28]`
high bits dominating (a divide touching only `[9:8]`/`[21:20]` does nothing on
the 8814). Clear those high bits and write the 8821C/8822B divide codes verbatim
(10 MHz → 3/3, 5 MHz → 2/2, small-BW 2/1) and the 8814 narrows exactly as an
8822B does — TX and RX. The lesson: the same register block can carry the same
fields with a *different* value encoding per die; a divide that reads dead may
just be landing in the wrong sub-field.

### Jaguar2 (RTL8822BU / RTL8821CU) — `0x8ac` packed, plus an RF re-latch edge

Jaguar2 uses the same `0x8ac` register with the small-BW field at `[7:6]` and
Expand Down Expand Up @@ -139,10 +152,13 @@ chip-specific traps. The ones this port paid for, current-state:
a driver bug; the durable fix is a crystal-cap trim lever (tracked
separately).

4. **8814A uses a different bandwidth mechanism.** The 4T4R 8814A configures
bandwidth through `0x8ac[1:0]` as a mode selector plus a separate ADC/AGC
function, not the `[9:8]` divider the 8812A exposes. The 8812AU divide does
not transplant; 8814 narrowband is an unported research effort.
4. **The same die-family can encode the divider differently.** The 8814A's
bandwidth register looked like a mode selector (`0x8ac[1:0]`) with no
narrowband value, and the 8812A divide (which writes `[9:8]`/`[21:20]`)
narrows nothing on it. The trap is assuming "no divide field" when the field
is there under a different encoding — the 8814 uses the 8822B's `[16]`/`[28]`
high bits (see the Jaguar1 section). Read the vendor's field comments, not
just the code it executes.

## Validation methodology

Expand Down Expand Up @@ -184,8 +200,8 @@ was characterized (the TX failure rate versus divide depth).
it as `CHANNEL_WIDTH_5` / `CHANNEL_WIDTH_10` on `SelectedChannel`, and
`IRtlDevice::GetAdapterCaps().narrowband_ok` reports whether the running chip
supports it. Support today: **Jaguar2 (8822B/8821C) and Jaguar3 (8822C/8822E)**
fully; **Jaguar1 on the 8812 die (8812AU/8811AU)** as a characterized addition;
8821A and 8814A excluded (see the walls above).
fully, and **Jaguar1 on the 8812AU/8811AU and the 8814AU** — every generation.
The 8821A is the one exclusion (its DAC-clock divide starves TX; see the walls).

Test scripts: `tests/jaguar2_narrowband_sdr.sh` and
`tests/jaguar1_nb_divide_sweep.sh` (SDR occupied-bandwidth), and
Expand Down
32 changes: 32 additions & 0 deletions src/jaguar1/RadioManagementModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,8 @@ void RadioManagementModule::phy_SetBwRegAdc_8814A(BandType Band,
(void)Band;
uint32_t val;
switch (bw) {
case ChannelWidth_t::CHANNEL_WIDTH_5: /* SPIKE: narrowband -> 20M mode */
case ChannelWidth_t::CHANNEL_WIDTH_10:
case ChannelWidth_t::CHANNEL_WIDTH_20:
val = 0x0;
break;
Expand All @@ -1193,6 +1195,8 @@ void RadioManagementModule::phy_SetBwRegAgc_8814A(BandType Band,
ChannelWidth_t bw) {
uint32_t agc;
switch (bw) {
case ChannelWidth_t::CHANNEL_WIDTH_5: /* SPIKE: narrowband -> 20M AGC */
case ChannelWidth_t::CHANNEL_WIDTH_10:
case ChannelWidth_t::CHANNEL_WIDTH_20:
agc = 6;
break;
Expand Down Expand Up @@ -1782,6 +1786,34 @@ void RadioManagementModule::phy_PostSetBwMode8814A() {
case ChannelWidth_t::CHANNEL_WIDTH_20:
/* No extra writes for 20 MHz on 8814. */
break;
case ChannelWidth_t::CHANNEL_WIDTH_5:
case ChannelWidth_t::CHANNEL_WIDTH_10: {
/* 8814AU narrowband. The 8814 sets its BW ADC clock via 0x8ac[1:0]
* (a 20/40/80 mode selector; phy_SetBwRegAdc_8814A), which has no sub-20
* MHz value — BUT its phy_SetBwRegAdc comment documents the full field set
* as [28, 21:20, 16, 9:6, 1:0], i.e. the 8814 shares the 8822B 0x8ac
* layout (ADC clock [9:8]+[16], DAC clock [21:20]+[28]) rather than the
* 8812A's ([9:8]/[21:20] only — dividing those alone did nothing here; the
* [16]/[28] high bits dominate). Clear the [16]/[28] high bits and set the
* low divide fields + small-BW; the codes are the 8821C/8822B narrowband
* codes verbatim (10 MHz -> 3/3, 5 MHz -> 2/2). Bench-verified TX+RX both
* directions. Sweepable via DEVOURER_NB_ADC / DEVOURER_NB_DAC. */
const bool is5 = (_currentChannelBw == CHANNEL_WIDTH_5);
uint32_t adc = is5 ? 2u : 3u;
uint32_t dac = is5 ? 2u : 3u;
const uint32_t smallbw = is5 ? 1u : 2u;
if (_tuning.nb_adc)
adc = *_tuning.nb_adc & 0x3;
if (_tuning.nb_dac)
dac = *_tuning.nb_dac & 0x3;
/* mask [28]+[21:20]+[16]+[9:6]+[1:0]; clears the [28]/[16] clock high
* bits, keeps [1:0]=0 (20M mode set above). */
_device.phy_set_bb_reg(rRFMOD_Jaguar, 0x103103C3,
(dac << 20) | (adc << 8) | (smallbw << 6));
_logger->info("SPIKE 8814AU narrowband: {} MHz (adc={} dac={} smallbw={})",
is5 ? 5 : 10, adc, dac, smallbw);
break;
}
case ChannelWidth_t::CHANNEL_WIDTH_40:
_device.phy_set_bb_reg(rRFMOD_Jaguar, 0x3C, SubChnlNum);
if (SubChnlNum ==
Expand Down
23 changes: 13 additions & 10 deletions src/jaguar1/RtlJaguarDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -868,16 +868,19 @@ devourer::AdapterCaps RtlJaguarDevice::GetAdapterCaps() {
c.rx_chains = chains;
c.per_chain_rssi = chains >= 2;
c.bw_mask = devourer::bw_mask_for_generation(c.generation);
/* SPIKE: 5/10 MHz narrowband on the 8812 die only (8812AU/8811AU). The
* 8812A shares the 8822B/8821C 0x8ac baseband clock-divider block — the
* divide codes are bench-characterized (tests/jaguar1_nb_divide_sweep.sh),
* TX+RX both directions, both widths. EXCLUDED: the 8821A — its 1T1R clock
* tree couples the DAC clock to the TX DMA/USB path, so dividing it starves
* TX (bulk-out submission failures scale with divide depth: dac=2 ~35% fail,
* dac=1 ~72%, vs 0% at full clock; 20 MHz is unaffected). The 8814A has its
* own phy_PostSetBwMode8814A path (0x8ac[1:0] mode selector) that the
* divide does not transplant to. Experimental (5 MHz@5 GHz is CFO-limited). */
if (_eepromManager->version_id.ICType == CHIP_8812) {
/* 5/10 MHz narrowband on the 8812 die (8812AU/8811AU) and the 8814AU. Both
* share the Jaguar2 0x8ac baseband clock-divider block; the codes are
* bench-characterized, TX+RX both directions, both widths
* (tests/jaguar1_nb_divide_sweep.sh, docs/narrowband.md). The two dies use
* DIFFERENT field encodings of the same register: the 8812A divides via
* [9:8]/[21:20], while the 8814A shares the 8822B layout (ADC [9:8]+[16],
* DAC [21:20]+[28]) and reuses the identical 8821C/8822B divide codes.
* EXCLUDED: the 8821A — its 1T1R clock tree couples the DAC clock to the TX
* DMA/USB path, so dividing it starves TX (bulk-out submission failures
* scale with divide depth: dac=2 ~35% fail, dac=1 ~72%, vs 0% at full clock;
* 20 MHz unaffected). Experimental (5 MHz@5 GHz is CFO-limited). */
if (_eepromManager->version_id.ICType == CHIP_8812 ||
_eepromManager->version_id.ICType == CHIP_8814A) {
c.bw_mask |= devourer::kBw5 | devourer::kBw10;
c.narrowband_ok = true;
}
Expand Down
Loading