Skip to content

Follow-ups left open by the 8852C ACK responder review (#489) #490

Open

Description

@josephnef

Left open by the review and bisect of #489 (8852C hardware ACK responder). Bench: TX50UH (RTL8832CU) x2, 8812CU soliciting with CCX tx.report, near field.

1. 8852C bring-ups that never answer an ACK: cause unknown

The responder arms, the entry reads back, injection works, and the chip answers no ACK for the whole session. Measured rate on this bench: ~1/3 of soft re-runs in the morning, ~85% by the afternoon, on ch161 and ch6, on both units; one 8852B-side report of ~1 in 55 from the author.

Excluded by measurement (details in the #489 review thread):

  • temperature (meter flat; dead at the coldest reading after 120 s VBUS-offs) and retained chip state (120 s VBUS cuts);
  • the TX chain (99% of host-injected frames witnessed in a dead session) and the soliciting side (an independent witness sees the ACKs vanish);
  • every host-readable register and SRAM read at the correct 0x20 stride: ADDR_CAM, BSSID CAM, DMAC/CMAC tables (uninitialised in RX-only sessions, no separating bit), MAC 0x8000/0xC000/0xD800 blocks, BB 0x10000-0x17FFF, RF path 0/1, GNT;
  • levers: host-written minimal and vendor-default CMAC entries, the CMAC H2C, forced WiFi-only GNT with and without the vendor coex init, a host-written BSSID CAM entry, the CAM slot.

The one lead: R_AX_WMAC_TX_INFO0_DEBUG (0xCAE8) reads 0x10914012 in every dead session and 0x10915012 in good ones (bit 12), so the TMAC does issue the response, and the RF path-0 TX registers show it never reaches the RF. Next experiments: a firmware RAM dump dead vs good, and a vendor rtl8852cu A/B on the same unit with a real station answering, to learn whether the vendor bring-up ever shows the state at all.

2. Kestrel host injection on ch161 nearly silent

Both TX50UH units, txdemo TX-only at 6M, 20 ms gap, ch161: 6-10 of ~290 frames witnessed, by either an 8852C or the 8812CU, in good and dead ACK sessions alike. Same setup on ch6: 97-99%. Earlier 8852C -> 8812CU injection A/Bs (~97%) were on ch6. Needs a 5 GHz injection A/B against the vendor driver on this unit before any 5 GHz Kestrel TX number is trusted.

3. 8852C coex init on the C die is a no-op

HalKestrel::coex_mac_init sets ENBT for the 8852B only, so on the 8852C both write_lte calls time out ("Kestrel coex: LTE interface not ready" twice per session) and the GNT stance is skipped. The vendor's 8852C path (coex_8852c.c) does not use the LTE window (mac_write_lte_8852c returns MACNOTSUP); it writes R_AX_GNT_SW_CTRL and R_AX_BTC_CFG / R_AX_BT_CNT_CFG / R_AX_CCA_CFG_0 directly. Cold state read on this unit: GNT_SW_CTRL 0x175775, GNT_VAL 0x118 (BT granted, WL not), BTC_CFG bit 31 clear. Forcing the WiFi-only grant did not change item 1, so this is a correctness cleanup, not a fix.

4. Bench note

The dead rate in item 1 rose through a day of back-to-back sessions on both units and no power cycle reset it. Anyone measuring the 8852C responder late in a session day will see a different rate from the morning; the matrix in AdapterCaps.h should say when in the day its rows were taken.

Activity

  1. josephnef commented on Oct 15, 2026

    @josephnef
    CollaboratorAuthor

    Investigation results, same bench (two TX50UH / 8832CU, 8812CU witness, LibreSDR).

    Item 2, Kestrel injection on ch161: root cause found, fix measured

    It is the MAC EDCCA TX gate latching busy on the receiver's own floor, the mechanism the 6 GHz path already parks. Sweep from the bus-3 unit at a 20 ms gap, 8812CU witness, default configuration: ch6 205/432, ch36 192/433, ch44 211/440, ch52 226/439, ch60 92/439, ch64 12/432, ch100 / 149 / 157 / 161 0 of ~435. SDR airtime on ch161 with a max-duty flood: 0.0% (ch44: +4% over idle), and the injector stalls on bulk-OUT timeouts. The fixed TX-power knob (8 / 14 / 20 dBm) changes nothing. DEVOURER_CCA_GATES=0,1 (EDCCA gate alone cleared) restores ch64 / 100 / 161 to 220-226 of ~435; 1,0 (primary alone) restores nothing. The tune log already said it: EDCCA band1 pwdB fb=-67 ... busy=1 on every 5 GHz tune of that unit.

    Fix on branch kestrel-edcca-5g (local, not pushed): kestrel_halbb_edcca_6g becomes kestrel_halbb_edcca_park(band_type, bw) and the channel set calls it for 5 GHz as well; comments and src/kestrel/CLAUDE.md updated with the measurements. After the change, default configuration: ch6 196/423, ch44 215/434, ch100 227/440, ch161 224/437. Deferral still works on ch161: max-duty 6M injector, 8 s, 19063 frames quiet; under a co-channel max-duty MCS1 flood 7327 sent / 7086 witnessed with gates on vs 15676 / 10639 with DEVOURER_DIS_CCA=1. Linux build + ctest 83/83. The consequence the 6 GHz note already states now covers 5 GHz: the ETSI adaptivity energy-detect level is not enforced; the caller owns compliance.

    New: the bus-9 TX50UH does not host-inject at all

    The second unit answers hardware ACKs and receives normally, but host injection delivers nothing on any channel (ch6 included), in TX-only and TX+RX sessions, with the gates off, after a real VBUS cycle: exactly 213 frames are accepted per session and then the bulk-OUT stalls. After the session its R_AX_TX_PTK_CNT (0x9BEC) reads 0 where the working unit reads 1499, the CMAC TX counters and R_AX_WMAC_TX_INFO0_DEBUG never move, while the host-dispatcher words do. So the frames reach the host interface and are dropped before the TX packet controller. Both units' MAC / BB / RF maps are identical after bring-up except counters and one DLE empty flag (0x8434 bit 16, C2H queue non-empty on the bad unit at bring-up; draining it with the RX thread does not help). Same USB descriptors, same speed, same detected USB mode and DLE quota; firmware download and H2Cs work on it. Not resolved: next is a usbmon capture of the bulk-OUT traffic on bus 9 versus bus 3, or the same unit on another port. Until then, any Kestrel TX number from the bus-9 unit is void.

    Item 1, dead ACK bring-ups: additional negatives

    Tested as levers during this round, no change to the dead rate (8-11 sessions each): host-written minimal and vendor-default CMAC control entries for the responder macid, the CMAC H2C (the C firmware ignores it, the entry stays random), forced WiFi-only GNT with and without the vendor coex init, a host-written BSSID CAM entry, hardware entry 63 vs 126. Also learned on the way: on this die the per-macid CMAC / DMAC tables and ADDR_CAM entry 0 are all uninitialised SRAM after bring-up and none of them matters for host TX either (host-written vendor-default entries change nothing on the working unit).

    Item 3, coex init

    Confirmed as described; forcing the vendor's WiFi-only grant made no measurable difference to either TX path, so it stays a cleanup.

  2. josephnef commented on Oct 15, 2026

    @josephnef
    CollaboratorAuthor

    Item 2 is fixed on master by #491 (5b12c86): the EDCCA level is parked on 5 GHz as on 6 GHz, and the 2.4 GHz table level is restored after a parked band. Still open here: item 1 (dead ACK bring-ups), the bus-9 TX50UH host-TX fault, item 3 (coex init cleanup), item 4 (bench note).

  3. agustinglez commented on Oct 15, 2026

    @agustinglez
    Contributor

    Dead ACK bring-ups: cause found, and a workaround that removes them

    In short. The chip has two transmit antenna paths (A and B). Every frame it sends, including the automatic ACK, carries a small instruction saying which paths to use. In a dead session, the instruction the MAC attaches to the ACK is garbage: it either asks for a path the chip does not have, or wires the ACK to the wrong signal. So the ACK is built but no antenna sends it. The garbage comes from somewhere in the MAC that is never initialised, which is why some bring-ups are fine and others are not, at random.

    The workaround. The baseband (the part that drives the antennas) can ignore the MAC's path instruction and use its own fixed setting instead. Telling it to always use its own setting removed the dead sessions: 0 of 93 sessions died, including 13 sessions that had the bad instruction and would have died before (6 of 6 did without the workaround).

    What is still open.

    • I have not found where in the MAC the bad instruction comes from, so the real fix (initialising it properly) is not done yet. The workaround overrides the path choice for everything the chip sends, not only the ACK.
    • Normal one-antenna injection is unaffected by the workaround. Two-antenna (2-stream) injection could not be checked, because it does not work from the 8852C even without the workaround (a separate, pre-existing problem).

    PoC to try. Branch kestrel-8852c-resp-txpath on my fork, one commit on top of #489. It changes only HalKestrel::vnd_bb_ctrl_tx_path() (8852C only): right after the existing path routing at bring-up, three BB writes switch the path choice to the baseband's own fixed setting (both paths, A on the first signal, B on the second). No environment variable or flag; check out the branch and run the usual armed sessions.

    git fetch https://github.com/agustinglez/devourer.git kestrel-8852c-resp-txpath
    git checkout FETCH_HEAD

    This also explains your dead session: its instruction enabled only path C, which this chip does not have.

    Details: registers, measurements and the patch

    Reading the instruction. 0xCAE8 is the read side of the TX-info debug port (R_AX_WMAC_TX_INFO_DEBUG_SEL 0xCAE4 select 0, with TCR1 B_AX_TCR_FORCE_READ_TXDFIFO set). It returns the TX info the MAC hands the BB, in the BB's INTF_TXINFO_* layout (0x1800, halbb_cr_info_8852c.h): PATH_EN is bits [15:12], PATH_MAP_A..D bits [23:16]. Every value seen so far:

    TX-info word outcome paths enabled path A mapped to
    0x10e41012 good (45/45) A only 0
    0x10??f012 with map_A = 3 good (14/14) A, B, C, D 3
    0x10??f012 with map_A = 1 dead (6/6) A, B, C, D 1
    your 0x10915012 good A and C 1
    your 0x10914012 dead C only 1

    The word varies per bring-up and is 0 until the first response. It is not taken from the per-macid CMAC table: I searched all 128 x 8 words at 0x18840000 in every session and found no consistent location.

    The workaround. BB 0x09a4 bits [4:2] = 7 makes the BB take path_en / path_map_a / path_map_b from its P-MAC registers. Then P-MAC path_en 0x458C [31:28] and maps 0x45B4 [20:17], as halbb_ctrl_tx_path_pmac_8852c does:

    • RF_PATH_A form (path_en 1, maps 0): 0 dead of 31, all 97.2-99.6% ACKed.
    • RF_PATH_AB form (path_en 3, maps 4): 0 dead of 31, all 96.8-100%.
    • The PoC commit (RF_PATH_AB, set at bring-up instead of at arm): 0 dead of 31, all 97.1-100%, including 3 sessions with the map_A = 1 word; the three registers still held the P-MAC values at the end of every session. Injection to the 8852B on ch6: MCS1 and 6M ~1900/2000, as without it.

    Injection. 8852C txdemo with the override, 8852B rxdemo counting hits, ch6, 2000 frames, carrier sense off, two runs per cell: MCS1 ~1900/2000 with and without the override. MCS12 and VHT2SS_MCS1 from the 8852C: 0/2000 with and without it, and also with the vendor's RF_PATH_AB path-com values written (0xD800 = 0x19999980, 0xD804 = 0x33333333, 0xD808 = 0x01209333). In the reverse direction (8852B transmitting, 8852C receiving) the same rates decode ~1700-1800/2000.

    Bench: TX50UH responder, TX20U Nano soliciting, ch161, MCS1, 8 s per session, entry at hardware index 63.

    The diagnostic dump used to read the TX-info word (env-gated, not for merge): issue490-pmacpath.patch.

  4. josephnef commented on Oct 15, 2026

    @josephnef
    CollaboratorAuthor

    Ran 9efa6ac here, on the bench where ~85% of armed sessions were dead an hour earlier (same units, same day, 8812CU soliciting with CCX tx.report, 8 s per session):

    cell sessions result
    bus-3 TX50UH, ch6 10 10 alive, 98.1-99.2%
    bus-3 TX50UH, ch161 6 6 alive, 100% each
    bus-9 TX50UH, ch6 4 4 alive, 97.3-98.7%
    live disarm (solicitor across it), both units 3 clean step each (~40% of the first 40% of reports, then 0)
    never armed 3 0

    0 dead of 20 armed sessions. Injection from the bus-3 unit is unchanged (ch6 207/432, ch161 224/437 with DEVOURER_DIS_CCA=1, since the branch predates #491). The bus-9 unit's host-injection fault is unchanged by it (213 frames then stall, 0 witnessed), so that one stays separate.

    Your reading of the word matches mine: 0x10914012 = path C only, 0x10915012 = A + C.

    Two notes for the PR: halbb_ctrl_tx_path_pmac_8852c also pulses 0x58dc / 0x78dc [31:30] 1-then-3 per path after the maps, which the commit leaves out; worth a sentence on why, or including them. And the override applies to every PPDU, so the commit message's "injected frames are not affected" should stay scoped to 1SS as it is. Ready for a PR from my side; I will re-run these cells on the PR head.

  5. josephnef commented on Oct 16, 2026

    @josephnef
    CollaboratorAuthor

    Item 1 is fixed on master by #492 (8da7af2): the 8852C takes its TX path selection from the BB's P-MAC registers, so the response's path bits no longer come from uninitialised MAC state. Re-validated here on the PR head: 0 dead of 18 armed sessions across both units and both bands. Still open: the bus-9 TX50UH host-TX fault, item 3 (coex init cleanup), item 4 (bench note), and the 2SS-from-8852C injection gap noted in the PR. Separate from this issue: the build-mingw CI job currently fails on every run with an MSYS2 package miss (target not found: mingw-w64-x86_64-libusb), on this PR's head twice; not a required check, but it will stay red until the workflow's package name is updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions