From fd1409cbfc5a5e07e536f191e4094bdaed83b7de Mon Sep 17 00:00:00 2001 From: Joseph <162703152+josephnef@users.noreply.github.com> Date: Tue, 4 Aug 2026 08:14:25 +0300 Subject: [PATCH 1/2] AdapterCaps: hardware-ARQ capability flags from the measured responder/retry matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ack_responder_ok and tx_retry_limit_ok, bench-derived like the ldpc_rx_* trio (the truth table is doc-commented at the declarations; the measured matrix is docs/scheduled-mac.md): responders measured working on the 8812A/ 8814A/8822B/8822C/8822E dies, measured BROKEN on the 8821A (an armed 8821AU never closed the loop across three independent runs), by-recipe true on the 8811A/8821C, and false on Kestrel where SetAckResponder is not implemented (matrix-measured 0% closure). The retry knob is inert on the 8814A die (the vendor DATA_RETRY_LIMIT=0 carve-out is kept) and on Kestrel (fw-level retry). Callers stop discovering either fact in the field. Carried in the adapter.caps event (ack_responder, tx_retry_limit); docs/logging.md row updated. Validated on air: all six plugged dies report exactly the matrix — 8822E/8822C/8822B 1/1, 8814A 1/0, 8821A 0/1, 8852C 0/0. ctest 49/49. Co-Authored-By: Claude Opus 4.8 --- docs/logging.md | 2 +- examples/common/caps_event.h | 2 ++ src/AdapterCaps.h | 17 +++++++++++++++++ src/jaguar1/RtlJaguarDevice.cpp | 5 +++++ src/jaguar2/RtlJaguar2Device.cpp | 4 ++++ src/jaguar3/RtlJaguar3Device.cpp | 4 ++++ src/kestrel/RtlKestrelDevice.cpp | 3 +++ 7 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/logging.md b/docs/logging.md index 7db607a..84ec911 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -77,7 +77,7 @@ Emitters: L = library, RX/TX/... = demo. Optional fields in [brackets]; | ev | emitter | fields | |---|---|---| | `init.timing` | L (`src/InitTimer.h`) + demos | stage ("scope.stage", e.g. "demo.first_rx_frame", "txdemo.first_tx_submit"), ms | -| `adapter.caps` | RX, TX, doctor, txpower (`examples/common/caps_event.h`) | supported, chip, names, chip_id "0x..", gen, variant, transport, tx_chains, rx_chains, n_ss, stbc, ldpc, sgi, bw_max, bw[] (MHz), txpwr_max, txpwr_step_qdb, txpwr_step_measured, txpwr_min_qdb, txpwr_max_qdb, txpwr_rate_diffs, txpwr_rate_diffs_hw, txpwr_rate_diffs_measured, tune_2g4[]\|null, tune_5g[]\|null, char_2g4[]\|null, char_5g[]\|null, ldpc_rx_ht, ldpc_rx_vht, ldpc_rx_flag, per_pkt_txpwr, narrowband, fastretune, he_er_su, per_chain_rssi | +| `adapter.caps` | RX, TX, doctor, txpower (`examples/common/caps_event.h`) | supported, chip, names, chip_id "0x..", gen, variant, transport, tx_chains, rx_chains, n_ss, stbc, ldpc, sgi, bw_max, bw[] (MHz), txpwr_max, txpwr_step_qdb, txpwr_step_measured, txpwr_min_qdb, txpwr_max_qdb, txpwr_rate_diffs, txpwr_rate_diffs_hw, txpwr_rate_diffs_measured, tune_2g4[]\|null, tune_5g[]\|null, char_2g4[]\|null, char_5g[]\|null, ldpc_rx_ht, ldpc_rx_vht, ldpc_rx_flag, per_pkt_txpwr, narrowband, fastretune, ack_responder, tx_retry_limit, he_er_su, per_chain_rssi | | `debug.wreg` | L (`DEVOURER_LOG_WRITES`) | addr "0x0nnn", width, val "0x…" | | `hop.prof` | L (`DEVOURER_HOP_PROF`) | gen, ch, `_us`…, total_us | | `tx.fail` | L (send failure; regress.py keys on it) | {status, actual_len, timeout} or {rc, timeout} | diff --git a/examples/common/caps_event.h b/examples/common/caps_event.h index d8244a8..f7ed055 100644 --- a/examples/common/caps_event.h +++ b/examples/common/caps_event.h @@ -81,6 +81,8 @@ inline void emit_adapter_caps(EventSink &sink, IRtlDevice *dev) { .f("per_pkt_txpwr_measured", c.per_pkt_txpwr_measured ? 1 : 0) .f("narrowband", c.narrowband_ok ? 1 : 0) .f("fastretune", c.fastretune_ok ? 1 : 0) + .f("ack_responder", c.ack_responder_ok ? 1 : 0) + .f("tx_retry_limit", c.tx_retry_limit_ok ? 1 : 0) .f("he_er_su", c.he_er_su_ok ? 1 : 0) .f("per_chain_rssi", c.per_chain_rssi ? 1 : 0) .f("hw_rx_tsf", c.hw_rx_timestamp ? 1 : 0) diff --git a/src/AdapterCaps.h b/src/AdapterCaps.h index af8c740..c33e707 100644 --- a/src/AdapterCaps.h +++ b/src/AdapterCaps.h @@ -159,6 +159,23 @@ struct AdapterCaps { * strong-link, close-range mode, the opposite of a range mode. */ bool vht_2g4_ok = false; + /* --- hardware-ARQ capability (bench-derived truth table, on-air responder + * matrix + retry-knob A/B; the measured contract is docs/scheduled-mac.md). + * ack_responder_ok: SetAckResponder measurably closes a hardware-ARQ loop + * as the RESPONDER (SIFS ACKs that a soliciting TX's CCX reports confirm). + * Measured true: 8812A (works, degraded — intermittent SIFS ACKs), 8814A, + * 8822B, 8812C/8822C, 8812E/8822E. Measured FALSE: the 8821A die — an + * armed 8821AU never closed the loop across three independent runs. + * By-recipe true (shared MAC recipe, not separately measured): 8811A + * (8812 die cut), 8821C. FALSE on Kestrel: SetAckResponder is not + * implemented on the AX generation. + * tx_retry_limit_ok: DEVOURER_TX_RETRY_LIMIT drives hardware autonomous + * retransmission (measured 12/0/12 A/B on J1/J2/J3). FALSE on the 8814A + * die (the vendor DATA_RETRY_LIMIT=0 carve-out is kept — knob inert) and + * on Kestrel (retry is firmware-level there). */ + bool ack_responder_ok = false; + bool tx_retry_limit_ok = false; + /* --- feature flags --- */ /* Per-packet TX power: a per-frame power trim driven by radiotap * DBM_TX_POWER (dB delta vs the calibrated table / session base) or a diff --git a/src/jaguar1/RtlJaguarDevice.cpp b/src/jaguar1/RtlJaguarDevice.cpp index c028aae..348ca21 100644 --- a/src/jaguar1/RtlJaguarDevice.cpp +++ b/src/jaguar1/RtlJaguarDevice.cpp @@ -1725,6 +1725,11 @@ devourer::AdapterCaps RtlJaguarDevice::GetAdapterCaps() { c.narrowband_ok = true; } c.fastretune_ok = true; /* phy_SwChnl8812_fast (8812/8821) + full-path fallback */ + /* Hardware ARQ (truth table at the AdapterCaps declarations): the 8821A + * die never closes the responder loop; the 8814A die keeps the vendor + * retry carve-out (knob inert). */ + c.ack_responder_ok = _eepromManager->version_id.ICType != CHIP_8821; + c.tx_retry_limit_ok = _eepromManager->version_id.ICType != CHIP_8814A; /* Per-packet TX power: 8814A only — its dword5 [30:28] descriptor LUT (the * 8822B TXPWR_OFSET position; vendor-defined, vendor-unused). measured * stays false until tests/txpkt_pwr_ofset_onair.sh proves it moves on-air diff --git a/src/jaguar2/RtlJaguar2Device.cpp b/src/jaguar2/RtlJaguar2Device.cpp index 7608eaa..d16e99b 100644 --- a/src/jaguar2/RtlJaguar2Device.cpp +++ b/src/jaguar2/RtlJaguar2Device.cpp @@ -1124,6 +1124,10 @@ devourer::AdapterCaps RtlJaguar2Device::GetAdapterCaps() { * RF18 re-latch edge after the re-clock (see the set_channel_bw narrowband * branch). */ c.narrowband_ok = true; + /* Hardware ARQ (truth table at the AdapterCaps declarations): 8822B + * responder + retry knob measured; the 8821C shares the recipe. */ + c.ack_responder_ok = true; + c.tx_retry_limit_ok = true; c.hw_rx_timestamp = true; /* FrameParserJaguar2 fills RxAtrib.tsfl */ c.hw_beacon_txtsf = true; /* StartBeacon: MAC inserts the egress TSF into beacons */ c.xtal_cap_max = 0x3f; /* 6-bit AFE crystal-cap trim (0x24/0x28) */ diff --git a/src/jaguar3/RtlJaguar3Device.cpp b/src/jaguar3/RtlJaguar3Device.cpp index dee1c90..123f4bd 100644 --- a/src/jaguar3/RtlJaguar3Device.cpp +++ b/src/jaguar3/RtlJaguar3Device.cpp @@ -1527,6 +1527,10 @@ devourer::AdapterCaps RtlJaguar3Device::GetAdapterCaps() { c.tx_chains = 2; /* 8822C/8822E are 2T2R */ c.rx_chains = 2; c.per_chain_rssi = true; + /* Hardware ARQ (truth table at the AdapterCaps declarations): both dies + * measured — responder matrix + retry-knob A/B + the arq_e2e ledgers. */ + c.ack_responder_ok = true; + c.tx_retry_limit_ok = true; /* Per-packet TX power: the TXPWR_OFSET_TYPE bank selector + programmable * 0x1e70 offset banks (SetTxPacketPowerOffsetQdb / radiotap DBM_TX_POWER; * TxPktPwrBanks.h). Continuous in step_qdb units, ±63/-64 index travel, 2 diff --git a/src/kestrel/RtlKestrelDevice.cpp b/src/kestrel/RtlKestrelDevice.cpp index 36bd7cd..f18db78 100644 --- a/src/kestrel/RtlKestrelDevice.cpp +++ b/src/kestrel/RtlKestrelDevice.cpp @@ -783,6 +783,9 @@ devourer::AdapterCaps RtlKestrelDevice::GetAdapterCaps() { c.tx_chains = 2; /* 8852B/8852C are 2T2R */ c.rx_chains = 2; c.per_chain_rssi = true; /* per-path RSSI from the PPDU-status physts header */ + /* Hardware ARQ: SetAckResponder is not implemented on the AX generation + * (matrix-measured 0% closure) and retry is firmware-level here, so the + * DEVOURER_TX_RETRY_LIMIT knob is inert — both flags stay false. */ c.bw_mask = devourer::bw_mask_for_generation(c.generation); if (_variant == kestrel::ChipVariant::C8852C) c.bw_mask |= devourer::kBw160; /* 8852C-only (vendor bw_sup BW_CAP_160M) */ From c81dca437bb3cb95591d105098784e55df4eb0f0 Mon Sep 17 00:00:00 2001 From: Joseph <162703152+josephnef@users.noreply.github.com> Date: Tue, 4 Aug 2026 08:21:56 +0300 Subject: [PATCH 2/2] caps-flags review round: publish the responder matrix; 8821C false-as-unmeasured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The six-cell responder matrix (with its single-shot caveat) lands in docs/scheduled-mac.md next to the existing responder notes, and the 8821C variant's flags follow the repo's false-means-unmeasured reading (the vht_2g4_ok convention) instead of by-recipe true — a capability gate should only assert what a bench cell proved, and the false row is what motivates running the 8821CU/8821CE cell. J2 gates both flags on the C8822B variant; the 8812BU re-validated 1/1 on air. Co-Authored-By: Claude Opus 4.8 --- docs/scheduled-mac.md | 19 +++++++++++++++++++ src/AdapterCaps.h | 18 ++++++++++-------- src/jaguar2/RtlJaguar2Device.cpp | 9 +++++---- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/docs/scheduled-mac.md b/docs/scheduled-mac.md index 2658dd2..0b5477f 100644 --- a/docs/scheduled-mac.md +++ b/docs/scheduled-mac.md @@ -208,6 +208,25 @@ its SIFS ACKs only land intermittently); **8821AU never closed the loop** (TX retries stayed pinned with it armed); the 8812BU responder was separately proven (`tests/ack_responder_check.sh`). +The full responder matrix (six cells, ch36, MCS3 unicast; run with +`DEVOURER_TX_RETRY_LIMIT` at its 0 default, so delivered% is the +**single-shot** ACK rate and capability is the on-vs-off delta — pin a +nonzero limit for absolute numbers): + +| responder | on | off | verdict | +|---|---|---|---| +| 8814AU | 79% | 0% | works | +| 8812BU | 98% | 0% | works | +| 8821AU | 0% | 0% | broken (third independent confirmation) | +| 8812EU | 98% | 0% | works | +| 8812CU | 69% | 0% | works | +| 8852CU (Kestrel) | 0% | 0% | not implemented (SetAckResponder is J1/2/3-only) | + +Unmeasured for lack of plugged hardware: 8821CU / PCIe 8821CE (recipe-shared +with the 8822B; their `AdapterCaps.ack_responder_ok` stays false-as-unmeasured +until a cell runs). The `ack_responder_ok` / `tx_retry_limit_ok` caps flags +carry this table per die. + ### The contract 1. **Per-frame delivery detection is GO on all three generations**: the OFF diff --git a/src/AdapterCaps.h b/src/AdapterCaps.h index c33e707..3bcc5e5 100644 --- a/src/AdapterCaps.h +++ b/src/AdapterCaps.h @@ -164,15 +164,17 @@ struct AdapterCaps { * ack_responder_ok: SetAckResponder measurably closes a hardware-ARQ loop * as the RESPONDER (SIFS ACKs that a soliciting TX's CCX reports confirm). * Measured true: 8812A (works, degraded — intermittent SIFS ACKs), 8814A, - * 8822B, 8812C/8822C, 8812E/8822E. Measured FALSE: the 8821A die — an - * armed 8821AU never closed the loop across three independent runs. - * By-recipe true (shared MAC recipe, not separately measured): 8811A - * (8812 die cut), 8821C. FALSE on Kestrel: SetAckResponder is not - * implemented on the AX generation. + * 8822B, 8812C/8822C, 8812E/8822E (the 8811A rides the 8812 die path and + * inherits its row). Measured FALSE: the 8821A die — an armed 8821AU never + * closed the loop across three independent runs. False-as-unmeasured (the + * vht_2g4_ok reading: unmeasured, not incapable): the 8821C — it shares + * the recipe but no 8821CU/CE cell has run. FALSE on Kestrel: + * SetAckResponder is not implemented on the AX generation. * tx_retry_limit_ok: DEVOURER_TX_RETRY_LIMIT drives hardware autonomous - * retransmission (measured 12/0/12 A/B on J1/J2/J3). FALSE on the 8814A - * die (the vendor DATA_RETRY_LIMIT=0 carve-out is kept — knob inert) and - * on Kestrel (retry is firmware-level there). */ + * retransmission (measured 12/0/12 A/B: 8821AU, 8812BU, 8822CU). FALSE on + * the 8814A die (the vendor DATA_RETRY_LIMIT=0 carve-out is kept — knob + * inert), false-as-unmeasured on the 8821C, and FALSE on Kestrel (retry is + * firmware-level there). */ bool ack_responder_ok = false; bool tx_retry_limit_ok = false; diff --git a/src/jaguar2/RtlJaguar2Device.cpp b/src/jaguar2/RtlJaguar2Device.cpp index d16e99b..5219776 100644 --- a/src/jaguar2/RtlJaguar2Device.cpp +++ b/src/jaguar2/RtlJaguar2Device.cpp @@ -1124,10 +1124,11 @@ devourer::AdapterCaps RtlJaguar2Device::GetAdapterCaps() { * RF18 re-latch edge after the re-clock (see the set_channel_bw narrowband * branch). */ c.narrowband_ok = true; - /* Hardware ARQ (truth table at the AdapterCaps declarations): 8822B - * responder + retry knob measured; the 8821C shares the recipe. */ - c.ack_responder_ok = true; - c.tx_retry_limit_ok = true; + /* Hardware ARQ (truth table at the AdapterCaps declarations): the 8822B + * die is measured on both counts; the 8821C shares the recipe but has no + * bench cell yet, so its flags stay false-as-unmeasured. */ + c.ack_responder_ok = _variant == jaguar2::ChipVariant::C8822B; + c.tx_retry_limit_ok = _variant == jaguar2::ChipVariant::C8822B; c.hw_rx_timestamp = true; /* FrameParserJaguar2 fills RxAtrib.tsfl */ c.hw_beacon_txtsf = true; /* StartBeacon: MAC inserts the egress TSF into beacons */ c.xtal_cap_max = 0x3f; /* 6-bit AFE crystal-cap trim (0x24/0x28) */