From 628962d0efbdf80af469058a0096541d4a15f611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Gonz=C3=A1lez?= Date: Thu, 15 Oct 2026 17:50:52 -0300 Subject: [PATCH 1/3] kestrel: 8852C TX path selection from P-MAC (dead ACK bring-ups) Some 8852C bring-ups armed the ACK responder, read the entry back, and answered no ACK for the whole session (#490). The chip built the ACK, but the TX path selection the MAC attached to it was garbage in those bring-ups: it enabled a path this 2T2R die does not have, or mapped path A off the signal, so no antenna sent it. The selection comes from MAC state nothing initialises, which is why it changed per bring-up. The halbb glue now follows the T-MAC path routing at bring-up with halbb_ctrl_tx_path_pmac_8852c(RF_PATH_AB): the BB takes the path enable and the path A/B maps from its own P-MAC registers (0x09a4 [4:2]), paths A+B with A on IFFT 0 and B on IFFT 1, plus the vendor's per-path TSSI reset pulse. 8852C only; the switch applies to every PPDU. - ack_responder_ok is true on the 8852C as well (still an explicit list of measured variants). - docs/kestrel-ack-responder.md: the dead-bring-up section now gives the cause, how it was found (the TX-info debug port, decoded with the BB's INTF_TXINFO layout), what was ruled out, and the fix's numbers. Measured (TX50UH responder, TX20U Nano soliciting, ch161, MCS1, 8 s per session): before, 3 of 12, 6 of 15 and 4 of 30 sessions dead (the last on master with #491); the map-A-1 selection died 10 of 10 times. After, 0 dead of 123 armed sessions across the register-level override and this vendor call (30 of them on this exact code), 15 of them with the bad selection, all 96.8-100%; solicitor across a live disarm 3791/3845 then 0/1602 and 3829/3843 then 0/1639; never armed 0/1591. Second bench: 0 of 20 on ch6 and ch161, both units. One-stream injection unchanged (MCS1 and 6M ~1900/2000 to the 8852B, ch6 and ch161 with carrier sense on). Two- stream injection from the 8852C does not decode with or without this, so its effect there is not measured. --- docs/kestrel-ack-responder.md | 99 +++++++++++++++++-------------- hal/halbb/g6/kestrel_halbb_glue.c | 13 +++- hal/halbb/g6/kestrel_halbb_glue.h | 6 +- src/AdapterCaps.h | 19 +++--- src/kestrel/HalKestrel.cpp | 3 +- src/kestrel/RtlKestrelDevice.cpp | 12 ++-- src/kestrel/RtlKestrelDevice.h | 9 ++- 7 files changed, 90 insertions(+), 71 deletions(-) diff --git a/docs/kestrel-ack-responder.md b/docs/kestrel-ack-responder.md index 0b559ffc..16c05273 100644 --- a/docs/kestrel-ack-responder.md +++ b/docs/kestrel-ack-responder.md @@ -95,49 +95,56 @@ read-backs). Measured on its own: before it (a dead bring-up also reads 0), so the live-disarm row in `AdapterCaps.h` solicits across the disarm. -## 8852C: bring-ups that answer no ACKs - -Some 8852C bring-ups answer no ACK at all: the responder arms, the entry reads -back, and the adapter answers nothing for the whole session, where a good -session answers >= 97.5%. How often varies and no cause is established. On -the first bench it looked tied to uptime: over a day of back-to-back sessions -it rose from about one in ten to three in four (10 of 83 overall), and after a -long power-off the first 12 sessions answered 97.5-99.4% before 3 of the next -12 came up dead. A second bench (two TX50UH, an 8812CU soliciting) does not -support that reading: dead sessions came interleaved with good ones -(dead/good/dead/good/dead in one series), one came on the second session after -a 3 s VBUS cut, a VBUS cold cycle before every session still left 1 of 13 -dead, and the RF thermal meter sat at delta 2-3 throughout. Back on the first -bench, with the entry at hardware entry 63, 6 of 15 sessions were dead, in -runs (3 dead, 3 good, 2 dead, 4 good). It is a property of the bring-up, not -of the arm: in one failed session three -separate injector processes in a row all saw 0, and no session has ever -recovered or failed partway. Measured in failed sessions: - -- Every frame addressed to the responder still matches the entry in the RX - descriptor (ADDR_CAM_VLD, macid 63), and the adapter airs its own frames - normally: only the SIFS response is missing. -- The response abort counters (0xCC14 NAV, 0xCC18 CCA/EDCCA/scheduler) stay at - 0: the hardware does not attempt the response and abort it. -- Every MAC configuration register read (0xC000, 0xC300, 0xC400, 0xC600, - 0xC800, 0xCA00, 0xCC00, 0xCE00 and the BB path-com block at 0xD800) is - identical to a good session's; only counters differ. -- What does differ: `R_AX_WMAC_NAV_UP_INFO` (0xCC84) records intra-BSS NAV - updates and `R_AX_PS_RXINFO` (0xCEA0) flags port-0 data, i.e. the MAC - classifies the soliciting frames as its own BSS's traffic, which it does not - in a good session. -- Ruled out by measurement: the response NAV checks (`R_AX_RSP_CHK_SIG` bits - 19-21 cleared, still fails), the BSSID CAM (all zero in good and failed - sessions alike; writing entry 1 valid, still fails), the injector, the - host's per-frame RX cost, and the arm's timing relative to the RX loop. - The slot is not ruled out: development tested hardware entry 126 only, and - on a second bench entry 126 gave more dead sessions than entry 63 (8 of 21 - against 1 of 16), but entry 63 gave 6 of 15 on the first, so the slot does - not explain it. - -What decides it is not found. The 8852B shows the same shape far more rarely -(about one bring-up in 55). Because a dead session reads back like a good one, -`GetAdapterCaps` reports `ack_responder_ok` false on the 8852C (true on the -8852B), while `SetAckResponder` still arms it. A caller that needs the ACK on -the 8852C should treat a soliciting peer's retries as the tell and bring the -adapter up again. +## 8852C: bring-ups that answered no ACKs + +**In short.** Some 8852C bring-ups armed, read the entry back, and answered no +ACK for the whole session. The chip did build the ACK, but every frame also +carries a selection of which transmit paths to use, and the selection the MAC +attached to the ACK was garbage in those bring-ups: it enabled a path this +2T2R die does not have, or mapped path A off the signal, so no antenna sent the +ACK. That selection comes from MAC state nothing initialises, which is why it +changed from one bring-up to the next. The fix makes the baseband take the +path selection from its own registers (P-MAC) instead of from the MAC. + +**How it was found.** The TX-info debug port (`R_AX_WMAC_TX_INFO_DEBUG_SEL` +0xCAE4 select 0, read at 0xCAE8 with `R_AX_TCR1` bit 26 set) returns the TX +info the MAC hands the baseband, in the baseband's `INTF_TXINFO_*` layout +(`halbb_cr_info_8852c.h`, 0x1800): PATH_EN is bits [15:12], PATH_MAP_A..D bits +[23:16]. It reads 0 until the first response and then holds one value for the +session: + +| TX-info word | paths enabled | path A mapped to | sessions without the fix | +|---|---|---|---| +| 0x10e41012 | A | 0 | all answered | +| 0x10??f012, map A = 3 | A, B, C, D | 3 | all answered | +| 0x10??f012 / 0x10??b012 / 0x10??7012, map A = 1 | several | 1 | 10 of 10 dead | +| 0x10914012 (second bench) | C only | 1 | dead | + +The selection is not taken from the per-macid CMAC table (searched for in all +128 entries, every session), the MAC TX power tables (a good session's tables +written before every arm still gave dead sessions), the firmware version (the +Windows driver's 0.29.123.4 gave dead sessions too), the slot, temperature, or +the 5 GHz EDCCA level (#491). Where the MAC takes it from is still not found. + +**The fix.** Right after the T-MAC path routing at bring-up, the halbb glue +calls `halbb_ctrl_tx_path_pmac_8852c(RF_PATH_AB)`: 0x09a4 bits [4:2] select +P-MAC as the source of the path enable and the path A/B maps, set to paths A+B +with A on IFFT 0 and B on IFFT 1, plus the vendor's per-path TSSI reset pulse. +It applies to every PPDU, not only responses. + +**Measured.** Before: 3 of 12, 6 of 15 and 4 of 30 sessions dead on this bench +(the last on master with #491), up to about 85% on a second bench. After, on +this bench (TX50UH responder, TX20U Nano soliciting, ch161, MCS1, 8 s per +session): 0 dead of 123 armed sessions across the register-level override and +the vendor call, including 15 with the map-A-1 selection, every session +96.8-100%; the selection registers held the P-MAC values at the end of every +session. On the second bench (two TX50UH, an 8812CU soliciting): 0 dead of 20 +on ch6 and ch161, both units. Injection from the 8852C is unchanged for one +stream (MCS1 and 6M ~1900/2000 to the 8852B on ch6 and on ch161 with carrier +sense on). Two-stream injection from the 8852C does not decode with or without +the fix (MCS12, VHT2SS_MCS1: 0/2000, while the 8852B's are received by the +8852C), so what the fix does to two-stream TX is not measured. + +The 8852B showed the same shape far more rarely (about one bring-up in 55, not +reproduced since); it routes TX paths per station through its CMAC antenna +model and is not changed. diff --git a/hal/halbb/g6/kestrel_halbb_glue.c b/hal/halbb/g6/kestrel_halbb_glue.c index 36941e54..c4ef304b 100644 --- a/hal/halbb/g6/kestrel_halbb_glue.c +++ b/hal/halbb/g6/kestrel_halbb_glue.c @@ -277,8 +277,19 @@ void kestrel_halbb_ctrl_tx_path(struct kestrel_halbb_ctx *ctx) { /* 8852C-only: the 8852B routes TX antennas per-STA via the CMAC antenna * model (TXWD/cctl), there is no path-com block to program. */ #ifdef BB_8852C_SUPPORT - if (ctx && ctx->bb.ic_type == BB_RTL8852C) + if (ctx && ctx->bb.ic_type == BB_RTL8852C) { halbb_ctrl_tx_path_tmac_8852c(&ctx->bb, RF_PATH_A, false); + /* Then take the TX path enable and the path A/B maps from the BB's P-MAC + * registers (0x09a4[4:2]) instead of the TX info the MAC sends with each + * PPDU: paths A+B, A on IFFT 0, B on IFFT 1, with the vendor's per-path + * TSSI reset pulse. The path selection the MAC attaches to a hardware + * response (ACK) comes from state nothing initialises on this die: in + * some bring-ups it enables only a path the die does not have, or maps + * path A off the signal, and the response is composed but never radiated + * (docs/kestrel-ack-responder.md). The switch applies to every PPDU; + * injection was measured unchanged for 1SS only. */ + halbb_ctrl_tx_path_pmac_8852c(&ctx->bb, RF_PATH_AB, false); + } #else (void)ctx; #endif diff --git a/hal/halbb/g6/kestrel_halbb_glue.h b/hal/halbb/g6/kestrel_halbb_glue.h index 4f6f5785..df85f30d 100644 --- a/hal/halbb/g6/kestrel_halbb_glue.h +++ b/hal/halbb/g6/kestrel_halbb_glue.h @@ -63,8 +63,10 @@ void kestrel_halbb_ctrl_bw_ch(struct kestrel_halbb_ctx *ctx, unsigned char pri_c /* T-MAC TX path-com routing (halbb_ctrl_tx_path_tmac_8852c): the 0xD800.. * 0xD820 path-com block (per-cut, via the MAC pwr-reg plane) + the 0x9a4 path * enable + both-path TSSI reset. Connects the BB IFFT output to the TX chain. - * 8852C-only — the 8852B selects its TX antenna per-STA via the CMAC antenna - * model, so this is a no-op there. Single-PHY, RF_PATH_A. */ + * Then the path enable / path A-B maps from P-MAC (halbb_ctrl_tx_path_pmac_ + * 8852c, RF_PATH_AB), so a response's path selection does not come from the + * MAC (see the definition). 8852C-only — the 8852B selects its TX antenna + * per-STA via the CMAC antenna model, so this is a no-op there. Single-PHY. */ void kestrel_halbb_ctrl_tx_path(struct kestrel_halbb_ctx *ctx); /* 6 GHz EDCCA threshold after a 6 GHz tune (the vendor's unlinked * EDCCA_NORMAL_MODE level — see the definition); bw is the halbb diff --git a/src/AdapterCaps.h b/src/AdapterCaps.h index 93f88424..21bd7808 100644 --- a/src/AdapterCaps.h +++ b/src/AdapterCaps.h @@ -209,14 +209,14 @@ struct AdapterCaps { * session, 0/5993 at the old address, 0/6062 never armed. At hardware * entry 63, the solicitor running across a live disarm in one session: * 3812/3837 then 0/1616, 3846/3848 then 0/1623 (a third cell was a dead - * session and says nothing); 15 armed sessions back to back, 9 answering - * 97.5-99.9% and 6 dead. Some bring-ups answer no ACK at all, at any - * entry tried: 3 of 12 and 6 of 15 here, 8 of 21 and 1 of 16 on a second - * bench, interleaved with good sessions). The 8852C is measured but reports - * FALSE: a dead session reads its entry back like a good one, so the flag - * could not tell a caller the loop closes; SetAckResponder still arms it, - * and a caller that wants it there judges by the soliciting peer's - * retries. + * session and says nothing). Some bring-ups used to answer no ACK at all + * (3 of 12, 6 of 15 and 4 of 30 here, up to ~85% on a second bench): the + * TX path selection the MAC attached to the ACK was uninitialised. With + * the path selection taken from the BB's P-MAC registers + * (docs/kestrel-ack-responder.md): 0 dead of 123 armed sessions here, + * every one 96.8-100%, 0 of 20 on the second bench; solicitor across a + * live disarm 3791/3845 then 0/1602 and 3829/3843 then 0/1639, never + * armed 0/1591). * * The `on`, `retarget`, and legacy `off` rows establish arming, retargeting, * and a never-armed control. A backend-owned `disarmed` row supports only @@ -241,8 +241,7 @@ struct AdapterCaps { * generation — the AP-mode work proved the gate where it was measured. * 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 the Kestrel 8852C - * (measured, see above). + * the recipe but no 8821CU/CE cell has run. * tx_retry_limit_ok: DEVOURER_TX_RETRY_LIMIT drives hardware autonomous * retransmission (measured 12/0/12 A/B: 8821AU, 8812BU, 8822CU; the 8733B * by airtime dose-response instead, 0/3/12 -> 1.00/4.00/12.32–12.33 airings per diff --git a/src/kestrel/HalKestrel.cpp b/src/kestrel/HalKestrel.cpp index 74d0d835..62fc34f7 100644 --- a/src/kestrel/HalKestrel.cpp +++ b/src/kestrel/HalKestrel.cpp @@ -2281,7 +2281,8 @@ void HalKestrel::vnd_bb_set_gain(uint8_t channel, uint8_t band_type) { void HalKestrel::vnd_bb_ctrl_tx_path() { if (_halbb_ctx && _variant == ChipVariant::C8852C) { kestrel_halbb_ctrl_tx_path(_halbb_ctx); - _logger->info("Kestrel PHY: T-MAC TX path-com routed (vendored, 8852C)"); + _logger->info("Kestrel PHY: T-MAC TX path-com routed (vendored, 8852C), " + "TX path enable/map from P-MAC (A+B)"); } } diff --git a/src/kestrel/RtlKestrelDevice.cpp b/src/kestrel/RtlKestrelDevice.cpp index 59d39eba..aa5655f8 100644 --- a/src/kestrel/RtlKestrelDevice.cpp +++ b/src/kestrel/RtlKestrelDevice.cpp @@ -1170,17 +1170,17 @@ devourer::AdapterCaps RtlKestrelDevice::GetAdapterCaps() { /* Hardware ARQ: SetAckResponder arms on both dies, verified by CAM read-back * -- the 8852B through a firmware-committed INFRA station entry * (HalKestrel::register_infra_sta), the 8852C through a host-written one - * (HalKestrel::write_addr_cam_sta), its firmware ignoring the H2C. The flag - * is the 8852B's only: on the 8852C some bring-ups arm, read the entry back - * and answer no ACK for the whole session, and nothing on the host tells - * those apart (SetAckResponder), so a caller cannot rely on the loop - * closing there. SetAckResponder still arms the 8852C. The retry knob IS wired + * (HalKestrel::write_addr_cam_sta), its firmware ignoring the H2C. Both dies + * are measured; the 8852C has answered in every measured session since the + * bring-up takes the TX path selection from P-MAC + * (kestrel_halbb_ctrl_tx_path). Any other variant does not inherit it. The retry knob IS wired * (WD DATA_TXCNT_LMT per frame, attempts-semantics folded to the N-retries * contract in send_packet; witness-measured on the 8832CU: limits {0,2} -> * modal {1,3} on-air copies exactly, limit 8 -> an 8/9 near-tie consistent * with ~90% witness capture of a 9-copy truth — obedient, no wedge). */ c.tx_retry_limit_ok = true; - c.ack_responder_ok = _variant == kestrel::ChipVariant::C8852B; + c.ack_responder_ok = _variant == kestrel::ChipVariant::C8852B || + _variant == kestrel::ChipVariant::C8852C; 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) */ diff --git a/src/kestrel/RtlKestrelDevice.h b/src/kestrel/RtlKestrelDevice.h index 5eacc702..e0bfefb2 100644 --- a/src/kestrel/RtlKestrelDevice.h +++ b/src/kestrel/RtlKestrelDevice.h @@ -105,11 +105,10 @@ class RtlKestrelDevice : public IRtlRadio { * entry has read back empty for 50 ms; an address still held after a failed * clear (or a failed retarget, which checks the old address too) stays * recorded, so a later ClearAckResponder retries it. Arm and clear are - * serialized. On the 8852C a true return is not a guarantee: some bring-ups - * read the entry back and answer no ACK for the whole session - * (docs/kestrel-ack-responder.md); nothing on - * the host was found to detect it, so GetAdapterCaps reports - * ack_responder_ok false on that die. */ + * serialized. On the 8852C the response also depends on the bring-up + * taking the TX path selection from the BB's P-MAC registers + * (kestrel_halbb_ctrl_tx_path); without it some bring-ups armed and + * answered no ACK (docs/kestrel-ack-responder.md). */ bool SetAckResponder(const devourer::MacAddr &mac) override; void ClearAckResponder() override; /* rxdemo's live-disarm measurement hook (DEVOURER_ACK_DISARM_AFTER_MS): the From 756918bb64578ae30ded4c3996e84b0f09bb3472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Gonz=C3=A1lez?= Date: Thu, 15 Oct 2026 18:01:55 -0300 Subject: [PATCH 2/3] kestrel: date the 8852C responder measurements, scope the injection check Review follow-up on the #490 requirements: - AdapterCaps.h and the doc give the date and time window of every responder series (the dead rate drifts through a day, #490 item 4). The 4-of-30 series without the fix ran between series with it, so the difference is not the time of day. - The doc states that #490's suggested experiments (firmware RAM dump of dead against good sessions, vendor-driver A/B on the same unit) were not run, and what the attribution rests on instead. - The injection numbers are scoped as a before/after check of this change on the same build, not an absolute 5 GHz TX figure; the vendor-driver injection A/B of #490 item 2 was not run. --- docs/kestrel-ack-responder.md | 40 ++++++++++++++++++++++++++--------- src/AdapterCaps.h | 19 ++++++++++------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/docs/kestrel-ack-responder.md b/docs/kestrel-ack-responder.md index 16c05273..bdcd246f 100644 --- a/docs/kestrel-ack-responder.md +++ b/docs/kestrel-ack-responder.md @@ -125,6 +125,12 @@ The selection is not taken from the per-macid CMAC table (searched for in all written before every arm still gave dead sessions), the firmware version (the Windows driver's 0.29.123.4 gave dead sessions too), the slot, temperature, or the 5 GHz EDCCA level (#491). Where the MAC takes it from is still not found. +The two experiments #490 proposed for finding the cause were not run: a +firmware RAM dump of dead against good sessions, and a vendor-driver A/B on the +same unit. The attribution rests instead on the TX-info word predicting every +outcome seen (the map-A-1 selection dead 10 of 10 times, every other value +alive) and on the fix removing the dead sessions, including those with that +selection. **The fix.** Right after the T-MAC path routing at bring-up, the halbb glue calls `halbb_ctrl_tx_path_pmac_8852c(RF_PATH_AB)`: 0x09a4 bits [4:2] select @@ -132,16 +138,30 @@ P-MAC as the source of the path enable and the path A/B maps, set to paths A+B with A on IFFT 0 and B on IFFT 1, plus the vendor's per-path TSSI reset pulse. It applies to every PPDU, not only responses. -**Measured.** Before: 3 of 12, 6 of 15 and 4 of 30 sessions dead on this bench -(the last on master with #491), up to about 85% on a second bench. After, on -this bench (TX50UH responder, TX20U Nano soliciting, ch161, MCS1, 8 s per -session): 0 dead of 123 armed sessions across the register-level override and -the vendor call, including 15 with the map-A-1 selection, every session -96.8-100%; the selection registers held the P-MAC values at the end of every -session. On the second bench (two TX50UH, an 8812CU soliciting): 0 dead of 20 -on ch6 and ch161, both units. Injection from the 8852C is unchanged for one -stream (MCS1 and 6M ~1900/2000 to the 8852B on ch6 and on ch161 with carrier -sense on). Two-stream injection from the 8852C does not decode with or without +**Measured.** This bench: TX50UH responder, TX20U Nano soliciting, ch161, +MCS1, 8 s per session, all on 2026-10-15 (local time, UTC-3), since the dead +rate drifts through a day (#490): + +| when | build | dead sessions | +|---|---|---| +| 10:12-10:19 | without the fix | 6 of 15 | +| 12:51-14:45 | without the fix | 3 of 21, 3 of 15, 2 of 25, 1 of 31, 1 of 12 | +| 14:47-16:04 | with the fix (register-level, then the PoC) | 0 of 93 | +| 16:15-16:31 | without the fix (master with #491) | 4 of 30 | +| 17:27-17:43 | with the fix (this commit) | 0 of 30 | + +With the fix, 15 sessions had the map-A-1 selection; every session answered +96.8-100% and the selection registers held the P-MAC values at the end of every +session. The 4-of-30 series without the fix ran between series with it, so the +difference is not the time of day. An earlier day's matrix had 3 of 12 dead +(time not recorded). Second bench (two TX50UH, an 8812CU soliciting): up to +about 85% dead in the afternoon, then 0 dead of 20 on ch6 and ch161, both +units, an hour later with the fix. Injection from the 8852C, compared with the +same build without the change: unchanged for one stream (MCS1 and 6M +~1900/2000 to the 8852B on ch6 and on ch161 with carrier sense on). That is a +before/after check of this change only, not an absolute 5 GHz TX figure; the +vendor-driver injection A/B that #490 asks for before trusting 5 GHz Kestrel TX +numbers was not run. Two-stream injection from the 8852C does not decode with or without the fix (MCS12, VHT2SS_MCS1: 0/2000, while the 8852B's are received by the 8852C), so what the fix does to two-stream TX is not measured. diff --git a/src/AdapterCaps.h b/src/AdapterCaps.h index 21bd7808..15f8087b 100644 --- a/src/AdapterCaps.h +++ b/src/AdapterCaps.h @@ -209,14 +209,17 @@ struct AdapterCaps { * session, 0/5993 at the old address, 0/6062 never armed. At hardware * entry 63, the solicitor running across a live disarm in one session: * 3812/3837 then 0/1616, 3846/3848 then 0/1623 (a third cell was a dead - * session and says nothing). Some bring-ups used to answer no ACK at all - * (3 of 12, 6 of 15 and 4 of 30 here, up to ~85% on a second bench): the - * TX path selection the MAC attached to the ACK was uninitialised. With - * the path selection taken from the BB's P-MAC registers - * (docs/kestrel-ack-responder.md): 0 dead of 123 armed sessions here, - * every one 96.8-100%, 0 of 20 on the second bench; solicitor across a - * live disarm 3791/3845 then 0/1602 and 3829/3843 then 0/1639, never - * armed 0/1591). + * session and says nothing). Some bring-ups used to answer no ACK at all: + * the TX path selection the MAC attached to the ACK was uninitialised. The + * dead rate drifts through a day, so with times (2026-10-15, UTC-3): + * without the fix 6 of 15 at 10:12-10:19, 10 of 104 at 12:51-14:45 and + * 4 of 30 at 16:15-16:31 (plus 3 of 12 on an earlier day, time not + * recorded; up to ~85% on a second bench in the afternoon); with the path selection taken from the BB's + * P-MAC registers (docs/kestrel-ack-responder.md) 0 dead of 93 at + * 14:47-16:04 and 0 of 30 at 17:27-17:43, every session 96.8-100%, and 0 + * of 20 on the second bench an hour after its ~85% series; solicitor + * across a live disarm 3791/3845 then 0/1602 and 3829/3843 then 0/1639, + * never armed 0/1591, at 17:44-17:46). * * The `on`, `retarget`, and legacy `off` rows establish arming, retargeting, * and a never-armed control. A backend-owned `disarmed` row supports only From ea0552d80f46da2b0877db4f5d4a9ce05ee8ba9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Gonz=C3=A1lez?= Date: Fri, 16 Oct 2026 00:37:56 -0300 Subject: [PATCH 3/3] kestrel: firmware RAM and vendor-driver results for the 8852C dead ACKs The two experiments #490 proposed, run after the cause was found: - Firmware RAM: the firmware's writable data memory (about 590 KB, read through the indirect window at the CPU's own addresses) dumped in 26 sessions and grouped by the TX-info path word. No bit separates the groups, and the firmware's own few-valued words take each value in both, so the path selection is not recorded in firmware memory. - Vendor driver: the same TX50UH on the Realtek Windows driver, associated to an AP on ch161, through 45 full driver restarts: 0 dead, 88.6-98.3% ACKed, against 4 dead of 30 for devourer without the fix on the same unit. The vendor bring-up initialises the response's path selection; which write does it is still not found. --- docs/kestrel-ack-responder.md | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/docs/kestrel-ack-responder.md b/docs/kestrel-ack-responder.md index bdcd246f..d5c36c89 100644 --- a/docs/kestrel-ack-responder.md +++ b/docs/kestrel-ack-responder.md @@ -125,12 +125,35 @@ The selection is not taken from the per-macid CMAC table (searched for in all written before every arm still gave dead sessions), the firmware version (the Windows driver's 0.29.123.4 gave dead sessions too), the slot, temperature, or the 5 GHz EDCCA level (#491). Where the MAC takes it from is still not found. -The two experiments #490 proposed for finding the cause were not run: a -firmware RAM dump of dead against good sessions, and a vendor-driver A/B on the -same unit. The attribution rests instead on the TX-info word predicting every -outcome seen (the map-A-1 selection dead 10 of 10 times, every other value -alive) and on the fix removing the dead sessions, including those with that -selection. +The two experiments #490 proposed were run after the cause was found: + +- **Firmware RAM is not it.** The firmware's data memory reads back through + the indirect window at the CPU's own addresses (0x20100000 up: the code + section reads back byte-identical to the image). Its writable parts, + 0x20161000-0x20180000, 0x2020c000-0x20278000 and 0x20300000-0x20308000 + (about 590 KB), were dumped 8 s after the arm in 26 sessions (2026-10-15, + 21:18-21:49, UTC-3) and grouped by the TX-info word (that build had the + fix, so every session answered): 15 sessions with 0x10e41012 against 11 + with the 0x10??f012 family. No bit is constant within each group and + different between them (the same test with shuffled labels also finds + none), and the 160 words the firmware keeps with only 2-4 values across + sessions take each value in sessions of both groups. Only one session had + the map-A-1 word, and it shares the common values. The path selection is + not something the firmware's memory records. +- **The vendor driver does not show it.** The same TX50UH on the Realtek + Windows driver (5001.19.128.1, firmware 0.29.123.4), associated as a + station to an AP on ch161, was restarted 45 times (`pnputil + /restart-device`, a full driver bring-up each time; 2026-10-16 + 00:15-00:33) and solicited for 8 s after each reconnect by the TX20U Nano + (MCS1, one attempt per frame): 0 dead of 45, every one 88.6-98.3% + (associated, it also carries the AP's traffic). Without the fix, devourer + on the same unit had 4 dead of 30 earlier that day. So the vendor + bring-up initialises the response's path selection where devourer's does + not; which write does it is still not found. + +The attribution rests on the TX-info word predicting every outcome seen +(the map-A-1 selection dead 10 of 10 times, every other value alive) and on +the fix removing the dead sessions, including those with that selection. **The fix.** Right after the T-MAC path routing at bring-up, the halbb glue calls `halbb_ctrl_tx_path_pmac_8852c(RF_PATH_AB)`: 0x09a4 bits [4:2] select