Skip to content

jaguar1: restore port identity when disarming 8812 ACKs - #411

Open
snokvist wants to merge 1 commit into
OpenIPC:masterfrom
snokvist:fix/jaguar1-8812-ack-disarm
Open

jaguar1: restore port identity when disarming 8812 ACKs#411
snokvist wants to merge 1 commit into
OpenIPC:masterfrom
snokvist:fix/jaguar1-8812-ack-disarm

Conversation

@snokvist

@snokvist snokvist commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Problem

Jaguar1's existing ClearAckResponder() only wrote net_type=NoLink. On the
reference RTL8812AU (0bda:8812) that write read back successfully, but the
port continued sending SIFS ACKs for the old responder MAC: 1946/1946
soliciting CCX reports were still ACKed.

This is not the RTL8733B gate model: a never-armed RTL8812AU did not answer on
its own MAC (0/1074), but arm-to-own-MAC followed by the old gate-only clear
did (1120/1120). The clear therefore needs both NoLink and a move away from the
armed MAC.

Change

  • On CHIP_8812, snapshot the live port-0 MACID/BSSID before the first arm and
    restore/readback-verify that exact identity during clear and failed-arm
    rollback.
  • Reject zero/group rollback identities and a responder equal to the captured
    MACID; either case would make a later retarget unsafe or ineffective.
  • Verify successful RTL8812 arms, and roll back if the arm readback fails or a
    later Init/InitWrite step throws.
  • Bind delayed disarm and Init/InitWrite rollback to the exact completed
    ACK arm. Clearing or replacing that arm invalidates its token, so stale
    cleanup cannot disarm a newer responder.
  • Serialize ACK/beacon port-0 transactions. A beacon claims the port before
    its first register mutation, including a partially failed setup, and ACK
    arming is refused until StopBeacon() verifies the beacon-function,
    TX-beacon, and net-type stop controls and releases it. An unverifiable stop
    retains ownership and recovery state for retry.
  • Extend the backend-owned live-disarm hook and fail-closed CCX matrix to the
    measured 0bda:8812 responder.

Hardware evidence

Final tree, RTL8812AU 0bda:8812 responder, 0bda:c812 solicitor, channel 36,
MCS3, retry limit 12, 12 seconds per phase:

phase submitted / reports ACK rate mean retries coverage retry-12 pin rate
armed MAC1 1150 / 1100 1.00 0.03 0.957 n/a
armed MAC2 1173 / 1123 1.00 0.08 0.957 n/a
never armed 1164 / 1069 0.00 12.0 0.918 1.00
arm MAC1, same-process zero-ms clear 1204 / 997 0.00 12.0 0.828 1.00

The zero-ms countdown is created by the Jaguar1 backend only after completed
bring-up and verified arm; it cannot win a race against Init(). A separate
2000-ms run also produced 0/1077 ACKed with retries pinned at 12 after clear.
The final own-MAC negative test refuses the arm before the responder becomes
active.

The stale-timer race was also exercised directly on this tree: arm A scheduled
an 8000-ms disarm, A was explicitly cleared and replaced by arm B after four
seconds, and A's old timer reported a stale/no-op at its deadline. Arm B then
ACKed 377/377 reports (ACK rate 1.00, mean retries 0.04, coverage 0.883) before
its own verified clear. The normal matching-timer path is covered by the
0/997 same-process disarm cell above.

The ACK-rate result establishes MACID's behavioral role. BSSID is restored and
read back as defensive port-state cleanup; this PR does not claim BSSID gates
ACK behavior.

The all-die port-ownership guard was separately exercised on the RTL8812AU:
StartBeacon() refused while the ACK responder owned port 0, succeeded after
verified ACK clear, and a subsequent ClearAckResponder() while the beacon
owned the port left it airing. An independent RTL8822CU witness captured 48
valid hardware beacons after that clear. No RTL8821AU/RTL8814AU was connected
for this rerun; their ownership path is the same backend state and lock, while
their existing gate-only responder clear remains unchanged.

The final verified-stop tree was then exercised in the opposite direction.
The RTL8812AU aired 55 valid beacons before StopBeacon() returned true; the
RTL8822CU witness saw zero beacons after a 250-ms drain window. In the same
RTL8812AU process, ACK ownership was acquired immediately afterward and the
RTL8822CU solicitor measured 372/372 successful reports (ACK rate 1.00, mean
retries 0.02, coverage 0.882). The responder then completed verified
MACID/BSSID restoration. This hardware result covers the normal stop path;
the transport-failure branches are register-model failure-injection tests.

Scope and validation

  • Measured on RTL8812AU only. The implementation follows the shared
    CHIP_8812 path (including the RTL8811AU cut), but RTL8811AU was not
    separately measured.
  • RTL8814A and RTL8821A retain their existing gate-only clear; HalMAC and
    RTL8733B behavior is unchanged.
  • This proves normal immediate-ACK disarm only. It makes no BlockAck-disarm
    claim.
  • Default build and 55/55 CTest pass; Jaguar1-only, RTL8733B-only, and
    Jaguar3-only builds pass; ACK analyzer selftests, AckResponderSelftest,
    ShellCheck, bash syntax, and git diff --check pass.
  • Headless tests cover exact MACID/BSSID snapshot/restore and readback,
    zero/group/equal rollback rejection, false-return and thrown-write failure
    injection, all-four-register restore attempts, independent attempts of all
    three beacon stop operations, fail-closed readback, and false-but-landed
    transfer handling.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Restore RTL8812 port identity when disarming Jaguar1 ACKs

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Restore pre-arm MACID/BSSID when disarming RTL8812 ACK responders.
• Verify arms and roll back failed initialization or register operations.
• Serialize beacon and ACK ownership; extend hardware disarm validation to RTL8812AU.
Diagram

graph TD
  Init["Jaguar1 Init"] --> Arm["Verified ACK Arm"] --> Lock["Port 0 Lock"] --> Regs["MACID BSSID"]
  Arm --> Rollback["Failure Rollback"] --> Restore["Identity Restore"] --> Lock
  Hook["Disarm Hook"] --> Restore
  Beacon["Beacon Operations"] --> Lock
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Restore EFUSE own MAC only
  • ➕ Simpler state model with fewer captured registers
  • ➕ Matches the existing RTL8733B retarget strategy
  • ➖ May overwrite live identity established by earlier port users
  • ➖ Does not restore BSSID or the exact pre-arm state
2. Retarget to a synthetic safe MAC
  • ➕ Avoids storing a complete port snapshot
  • ➕ Can move ACK matching away from the responder address
  • ➖ Leaves port identity mutated after clear
  • ➖ Requires choosing an address that cannot conflict or disrupt transmission
  • ➖ Provides weaker cleanup guarantees than exact restoration
3. Use only the NoLink gate
  • ➕ Preserves the previous minimal clear implementation
  • ➕ Avoids identity register writes
  • ➖ Hardware measurements show RTL8812AU continues ACKing the armed MAC
  • ➖ Cannot safely disarm the measured device

Recommendation: Keep the PR's exact pre-arm snapshot and verified restoration. It directly addresses the measured RTL8812AU behavior, preserves prior port state, and fails closed when restoration would be unsafe; static or synthetic retargeting provides weaker state fidelity.

Files changed (10) +465 / -68

Bug fix (3) +329 / -23
AckResponder.hAdd exact port identity snapshot and restoration helpers +92/-5

Add exact port identity snapshot and restoration helpers

• Introduces packed MACID/BSSID capture, safe-identity validation, complete restore attempts, and readback verification. Documents that CHIP_8812 requires identity restoration in addition to closing net_type.

src/AckResponder.h

RtlJaguarDevice.cppImplement verified RTL8812 identity restoration and port ownership +207/-18

Implement verified RTL8812 identity restoration and port ownership

• Captures and validates the pre-arm port identity, verifies responder arms, and restores MACID/BSSID during clear or rollback. It also serializes ACK and beacon transactions, rolls back initialization failures, and adds backend-owned timed disarm support.

src/jaguar1/RtlJaguarDevice.cpp

RtlJaguarDevice.hTrack Jaguar1 rollback identity and synchronized port state +30/-0

Track Jaguar1 rollback identity and synchronized port state

• Adds the CHIP_8812 disarm scheduler, captured identity state, recursive port-0 mutex, shared disarm helper, and persistent beacon ownership flag.

src/jaguar1/RtlJaguarDevice.h

Tests (2) +75 / -11
ack_responder_selftest.cppTest identity restoration and transport failure handling +64/-2

Test identity restoration and transport failure handling

• Covers exact MACID/BSSID snapshot and restore, readback checks, unsafe identity rejection, same-address rejection, and all-register attempts for false-returning or throwing writes.

tests/ack_responder_selftest.cpp

ack_txreport_matrix.shRun live-disarm matrix for reference RTL8812AU +11/-9

Run live-disarm matrix for reference RTL8812AU

• Adds USB ID 0bda:8812 to backend-owned disarm testing and recognizes Jaguar1 restoration success and failure diagnostics.

tests/ack_txreport_matrix.sh

Documentation (4) +39 / -18
aggregation.mdDocument RTL8812 identity-based ACK disarm behavior +7/-4

Document RTL8812 identity-based ACK disarm behavior

• Explains that RTL8812AU continues responding after NoLink and requires restoration of its pre-arm MACID. Clarifies that BSSID restoration is defensive cleanup rather than a proven ACK gate.

docs/aggregation.md

scheduled-mac.mdAdd RTL8812AU to measured live-disarm documentation +15/-6

Add RTL8812AU to measured live-disarm documentation

• Extends the backend-owned disarm workflow to the reference RTL8812AU and records the relevant hardware evidence and scope limitations.

docs/scheduled-mac.md

AdapterCaps.hRecord RTL8812AU disarm evidence and applicability +12/-3

Record RTL8812AU disarm evidence and applicability

• Documents the gate-only failure, successful captured-MACID restoration, same-address hazard, and the measured versus inherited chipset scope.

src/AdapterCaps.h

IRtlDevice.hClarify identity-aware ACK clear semantics +5/-5

Clarify identity-aware ACK clear semantics

• Updates the device contract to cover backends where clearing net_type alone does not end measured ACK response behavior.

src/IRtlDevice.h

Other (1) +22 / -16
main.cppRoute test disarm scheduling through supported backends +22/-16

Route test disarm scheduling through supported backends

• Allows the live-disarm environment hook to use Jaguar1 CHIP_8812 as well as RTL8733B. Unsupported generations are rejected to prevent race-prone measurements.

examples/rx/main.cpp

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Unverified beacon claim release 🐞 Bug ☼ Reliability
Description
StopBeacon ignores the status of its hardware-disable writes and unconditionally clears
_port0_beacon_claimed. A failed write can therefore leave the autonomous beacon active while
allowing SetAckResponder to claim and overwrite the same port registers.
Code

src/jaguar1/RtlJaguarDevice.cpp[657]

+  _port0_beacon_claimed = false;
Evidence
The newly added ownership flag is cleared unconditionally after three register writes whose boolean
results are ignored. The adapter API explicitly returns each transport write's success, while the
interface documents that an armed beacon continues autonomously until hardware shutdown, so
releasing the claim after a false write permits concurrent ACK reconfiguration of a potentially live
beacon port.

src/jaguar1/RtlJaguarDevice.cpp[644-658]
src/RtlAdapter.h[180-195]
src/IRtlDevice.h[424-430]
src/jaguar1/RtlJaguarDevice.cpp[819-823]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`StopBeacon()` releases the port-0 beacon claim without checking whether the register writes that disable the autonomous beacon succeeded. Retain ownership and return false whenever shutdown cannot be verified, so ACK responder setup cannot reuse an active port.
## Issue Context
`RtlAdapter::rtw_write()` returns the transport write status. The device contract notes that hardware beaconing continues autonomously until explicitly stopped.
## Fix Focus Areas
- src/jaguar1/RtlJaguarDevice.cpp[644-659]
- src/RtlAdapter.h[180-195]
- src/IRtlDevice.h[424-430]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Port ownership skips other dies 🐞 Bug ≡ Correctness
Description
SetAckResponder checks the new beacon ownership flag only inside the CHIP_8812 branch, so
supported 8821A/8814A devices can still arm an ACK responder over an active beacon and overwrite its
shared MACID/BSSID registers. Conversely, StartBeacon can overwrite an active ACK responder on
those devices, defeating the claimed port-0 serialization.
Code

src/jaguar1/RtlJaguarDevice.cpp[R820-822]

+  if (_eepromManager->version_id.ICType == CHIP_8812) {
+    const bool had_restore_identity = _ack_restore_identity.has_value();
+    if (_port0_beacon_claimed) {
Evidence
The added ownership check is nested under ICType == CHIP_8812, while ack::enable writes MACID at
0x610, BSSID at 0x618, and net type at 0x102 for every caller. StartBeacon writes those same
registers but rejects an armed responder only for CHIP_8812, and Jaguar1 advertises ACK-responder
support across all its supported dies.

src/jaguar1/RtlJaguarDevice.cpp[806-853]
src/jaguar1/RtlJaguarDevice.cpp[528-558]
src/AckResponder.h[75-100]
src/jaguar1/RtlJaguarDevice.cpp[2023-2027]
src/jaguar1/RtlJaguarDevice.h[431-434]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new port-0 ownership checks apply only to `CHIP_8812`, although every supported Jaguar1 ACK responder and beacon uses the same MACID, BSSID, and net-type registers. Track ACK ownership independently of the 8812 restore snapshot and reject conflicting beacon/ACK operations on every Jaguar1 IC type.
## Issue Context
The exact identity snapshot and restore should remain specific to CHIP_8812, but mutual exclusion of active logical owners is required for all chips sharing port 0. Ensure clear operations also cannot disable a beacon-owned net type.
## Fix Focus Areas
- src/jaguar1/RtlJaguarDevice.cpp[528-548]
- src/jaguar1/RtlJaguarDevice.cpp[806-850]
- src/jaguar1/RtlJaguarDevice.cpp[881-908]
- src/jaguar1/RtlJaguarDevice.h[74-87]
- src/jaguar1/RtlJaguarDevice.h[431-434]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. A stale timer clears a new responder 🐞 Bug ≡ Correctness
Description
The delayed-disarm lambda always invokes ClearAckResponder() and carries no identity for the
responder arm that created it. If an operator clears and re-arms the responder before the deadline,
the callback sees the replacement’s current claim and disables that replacement rather than only the
measured arm.
Code

src/jaguar1/RtlJaguarDevice.cpp[R1612-1616]

+          _logger->info(
+              "DEVOURER_ACK_DISARM_AFTER_MS: disarming Jaguar1/CHIP_8812 "
+              "ACK responder {} ms after completed bring-up",
+              delay_ms);
+          ClearAckResponder();
Evidence
The added worker captures only this and delay_ms before unconditionally clearing the current
responder. The clear path tests only the current ownership boolean, while every later successful arm
sets that same boolean, so it cannot distinguish the originally scheduled arm from a replacement.

src/jaguar1/RtlJaguarDevice.cpp[1594-1617]
src/jaguar1/RtlJaguarDevice.cpp[860-891]
src/jaguar1/RtlJaguarDevice.cpp[894-955]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The delayed test-disarm callback can clear an ACK responder that was armed after the timer was created. Associate the delayed request with the specific successful arm it is meant to clear, and make the callback a no-op when that arm has since been cleared or replaced.
## Issue Context
`ScheduleAckResponderDisarmForTest()` records a request before `Init()`. The worker subsequently captures only the delay and clears whichever responder currently owns port 0 at expiry.
## Fix Focus Areas
- src/jaguar1/RtlJaguarDevice.h[74-91]
- src/jaguar1/RtlJaguarDevice.cpp[815-891]
- src/jaguar1/RtlJaguarDevice.cpp[1589-1617]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: 🧠 Deep: This is a large, behaviorally dense hardware/backend change spanning ACK disarming, beacon ownership, rollback, concurrency, timers, and multiple register paths, with several plausible independent defects and high operational impact.

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit 401dbf9 🧠 Deep

Results up to commit 0941ec0


🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. Unverified beacon claim release 🐞 Bug ☼ Reliability
Description
StopBeacon ignores the status of its hardware-disable writes and unconditionally clears
_port0_beacon_claimed. A failed write can therefore leave the autonomous beacon active while
allowing SetAckResponder to claim and overwrite the same port registers.
Code

src/jaguar1/RtlJaguarDevice.cpp[657]

+  _port0_beacon_claimed = false;
Evidence
The newly added ownership flag is cleared unconditionally after three register writes whose boolean
results are ignored. The adapter API explicitly returns each transport write's success, while the
interface documents that an armed beacon continues autonomously until hardware shutdown, so
releasing the claim after a false write permits concurrent ACK reconfiguration of a potentially live
beacon port.

src/jaguar1/RtlJaguarDevice.cpp[644-658]
src/RtlAdapter.h[180-195]
src/IRtlDevice.h[424-430]
src/jaguar1/RtlJaguarDevice.cpp[819-823]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`StopBeacon()` releases the port-0 beacon claim without checking whether the register writes that disable the autonomous beacon succeeded. Retain ownership and return false whenever shutdown cannot be verified, so ACK responder setup cannot reuse an active port.
## Issue Context
`RtlAdapter::rtw_write()` returns the transport write status. The device contract notes that hardware beaconing continues autonomously until explicitly stopped.
## Fix Focus Areas
- src/jaguar1/RtlJaguarDevice.cpp[644-659]
- src/RtlAdapter.h[180-195]
- src/IRtlDevice.h[424-430]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Port ownership skips other dies 🐞 Bug ≡ Correctness
Description
SetAckResponder checks the new beacon ownership flag only inside the CHIP_8812 branch, so
supported 8821A/8814A devices can still arm an ACK responder over an active beacon and overwrite its
shared MACID/BSSID registers. Conversely, StartBeacon can overwrite an active ACK responder on
those devices, defeating the claimed port-0 serialization.
Code

src/jaguar1/RtlJaguarDevice.cpp[R820-822]

+  if (_eepromManager->version_id.ICType == CHIP_8812) {
+    const bool had_restore_identity = _ack_restore_identity.has_value();
+    if (_port0_beacon_claimed) {
Evidence
The added ownership check is nested under ICType == CHIP_8812, while ack::enable writes MACID at
0x610, BSSID at 0x618, and net type at 0x102 for every caller. StartBeacon writes those same
registers but rejects an armed responder only for CHIP_8812, and Jaguar1 advertises ACK-responder
support across all its supported dies.

src/jaguar1/RtlJaguarDevice.cpp[806-853]
src/jaguar1/RtlJaguarDevice.cpp[528-558]
src/AckResponder.h[75-100]
src/jaguar1/RtlJaguarDevice.cpp[2023-2027]
src/jaguar1/RtlJaguarDevice.h[431-434]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new port-0 ownership checks apply only to `CHIP_8812`, although every supported Jaguar1 ACK responder and beacon uses the same MACID, BSSID, and net-type registers. Track ACK ownership independently of the 8812 restore snapshot and reject conflicting beacon/ACK operations on every Jaguar1 IC type.
## Issue Context
The exact identity snapshot and restore should remain specific to CHIP_8812, but mutual exclusion of active logical owners is required for all chips sharing port 0. Ensure clear operations also cannot disable a beacon-owned net type.
## Fix Focus Areas
- src/jaguar1/RtlJaguarDevice.cpp[528-548]
- src/jaguar1/RtlJaguarDevice.cpp[806-850]
- src/jaguar1/RtlJaguarDevice.cpp[881-908]
- src/jaguar1/RtlJaguarDevice.h[74-87]
- src/jaguar1/RtlJaguarDevice.h[431-434]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
3. A stale timer clears a new responder 🐞 Bug ≡ Correctness ⭐ New
Description
The delayed-disarm lambda always invokes ClearAckResponder() and carries no identity for the
responder arm that created it. If an operator clears and re-arms the responder before the deadline,
the callback sees the replacement’s current claim and disables that replacement rather than only the
measured arm.
Code

src/jaguar1/RtlJaguarDevice.cpp[R1612-1616]

+          _logger->info(
+              "DEVOURER_ACK_DISARM_AFTER_MS: disarming Jaguar1/CHIP_8812 "
+              "ACK responder {} ms after completed bring-up",
+              delay_ms);
+          ClearAckResponder();
Evidence
The added worker captures only this and delay_ms before unconditionally clearing the current
responder. The clear path tests only the current ownership boolean, while every later successful arm
sets that same boolean, so it cannot distinguish the originally scheduled arm from a replacement.

src/jaguar1/RtlJaguarDevice.cpp[1594-1617]
src/jaguar1/RtlJaguarDevice.cpp[860-891]
src/jaguar1/RtlJaguarDevice.cpp[894-955]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The delayed test-disarm callback can clear an ACK responder that was armed after the timer was created. Associate the delayed request with the specific successful arm it is meant to clear, and make the callback a no-op when that arm has since been cleared or replaced.

## Issue Context
`ScheduleAckResponderDisarmForTest()` records a request before `Init()`. The worker subsequently captures only the delay and clears whichever responder currently owns port 0 at expiry.

## Fix Focus Areas
- src/jaguar1/RtlJaguarDevice.h[74-91]
- src/jaguar1/RtlJaguarDevice.cpp[815-891]
- src/jaguar1/RtlJaguarDevice.cpp[1589-1617]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Context sources
Review mode: 🧠 Deep: This is a large, bug-dense hardware/runtime change spanning ACK disarm semantics, rollback, beacon ownership, concurrency, multiple chip paths, and test/harness integration, with several independent failure modes that merit redundant review.
Results up to commit 716d1b2


🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. Unverified beacon claim release 🐞 Bug ☼ Reliability ⭐ New
Description
StopBeacon ignores the status of its hardware-disable writes and unconditionally clears
_port0_beacon_claimed. A failed write can therefore leave the autonomous beacon active while
allowing SetAckResponder to claim and overwrite the same port registers.
Code

src/jaguar1/RtlJaguarDevice.cpp[657]

+  _port0_beacon_claimed = false;
Evidence
The newly added ownership flag is cleared unconditionally after three register writes whose boolean
results are ignored. The adapter API explicitly returns each transport write's success, while the
interface documents that an armed beacon continues autonomously until hardware shutdown, so
releasing the claim after a false write permits concurrent ACK reconfiguration of a potentially live
beacon port.

src/jaguar1/RtlJaguarDevice.cpp[644-658]
src/RtlAdapter.h[180-195]
src/IRtlDevice.h[424-430]
src/jaguar1/RtlJaguarDevice.cpp[819-823]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`StopBeacon()` releases the port-0 beacon claim without checking whether the register writes that disable the autonomous beacon succeeded. Retain ownership and return false whenever shutdown cannot be verified, so ACK responder setup cannot reuse an active port.

## Issue Context
`RtlAdapter::rtw_write()` returns the transport write status. The device contract notes that hardware beaconing continues autonomously until explicitly stopped.

## Fix Focus Areas
- src/jaguar1/RtlJaguarDevice.cpp[644-659]
- src/RtlAdapter.h[180-195]
- src/IRtlDevice.h[424-430]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Port ownership skips other dies 🐞 Bug ≡ Correctness
Description
SetAckResponder checks the new beacon ownership flag only inside the CHIP_8812 branch, so
supported 8821A/8814A devices can still arm an ACK responder over an active beacon and overwrite its
shared MACID/BSSID registers. Conversely, StartBeacon can overwrite an active ACK responder on
those devices, defeating the claimed port-0 serialization.
Code

src/jaguar1/RtlJaguarDevice.cpp[R820-822]

+  if (_eepromManager->version_id.ICType == CHIP_8812) {
+    const bool had_restore_identity = _ack_restore_identity.has_value();
+    if (_port0_beacon_claimed) {
Evidence
The added ownership check is nested under ICType == CHIP_8812, while ack::enable writes MACID at
0x610, BSSID at 0x618, and net type at 0x102 for every caller. StartBeacon writes those same
registers but rejects an armed responder only for CHIP_8812, and Jaguar1 advertises ACK-responder
support across all its supported dies.

src/jaguar1/RtlJaguarDevice.cpp[806-853]
src/jaguar1/RtlJaguarDevice.cpp[528-558]
src/AckResponder.h[75-100]
src/jaguar1/RtlJaguarDevice.cpp[2023-2027]
src/jaguar1/RtlJaguarDevice.h[431-434]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new port-0 ownership checks apply only to `CHIP_8812`, although every supported Jaguar1 ACK responder and beacon uses the same MACID, BSSID, and net-type registers. Track ACK ownership independently of the 8812 restore snapshot and reject conflicting beacon/ACK operations on every Jaguar1 IC type.
## Issue Context
The exact identity snapshot and restore should remain specific to CHIP_8812, but mutual exclusion of active logical owners is required for all chips sharing port 0. Ensure clear operations also cannot disable a beacon-owned net type.
## Fix Focus Areas
- src/jaguar1/RtlJaguarDevice.cpp[528-548]
- src/jaguar1/RtlJaguarDevice.cpp[806-850]
- src/jaguar1/RtlJaguarDevice.cpp[881-908]
- src/jaguar1/RtlJaguarDevice.h[74-87]
- src/jaguar1/RtlJaguarDevice.h[431-434]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 3d85cca


🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)


Action required
1. Port ownership skips other dies 🐞 Bug ≡ Correctness
Description
SetAckResponder checks the new beacon ownership flag only inside the CHIP_8812 branch, so
supported 8821A/8814A devices can still arm an ACK responder over an active beacon and overwrite its
shared MACID/BSSID registers. Conversely, StartBeacon can overwrite an active ACK responder on
those devices, defeating the claimed port-0 serialization.
Code

src/jaguar1/RtlJaguarDevice.cpp[R820-822]

+  if (_eepromManager->version_id.ICType == CHIP_8812) {
+    const bool had_restore_identity = _ack_restore_identity.has_value();
+    if (_port0_beacon_claimed) {
Evidence
The added ownership check is nested under ICType == CHIP_8812, while ack::enable writes MACID at
0x610, BSSID at 0x618, and net type at 0x102 for every caller. StartBeacon writes those same
registers but rejects an armed responder only for CHIP_8812, and Jaguar1 advertises ACK-responder
support across all its supported dies.

src/jaguar1/RtlJaguarDevice.cpp[806-853]
src/jaguar1/RtlJaguarDevice.cpp[528-558]
src/AckResponder.h[75-100]
src/jaguar1/RtlJaguarDevice.cpp[2023-2027]
src/jaguar1/RtlJaguarDevice.h[431-434]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new port-0 ownership checks apply only to `CHIP_8812`, although every supported Jaguar1 ACK responder and beacon uses the same MACID, BSSID, and net-type registers. Track ACK ownership independently of the 8812 restore snapshot and reject conflicting beacon/ACK operations on every Jaguar1 IC type.

## Issue Context
The exact identity snapshot and restore should remain specific to CHIP_8812, but mutual exclusion of active logical owners is required for all chips sharing port 0. Ensure clear operations also cannot disable a beacon-owned net type.

## Fix Focus Areas
- src/jaguar1/RtlJaguarDevice.cpp[528-548]
- src/jaguar1/RtlJaguarDevice.cpp[806-850]
- src/jaguar1/RtlJaguarDevice.cpp[881-908]
- src/jaguar1/RtlJaguarDevice.h[74-87]
- src/jaguar1/RtlJaguarDevice.h[431-434]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/jaguar1/RtlJaguarDevice.cpp Outdated
@snokvist
snokvist force-pushed the fix/jaguar1-8812-ack-disarm branch from 3d85cca to 716d1b2 Compare September 6, 2026 08:05
@snokvist

snokvist commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

/review

Comment thread src/jaguar1/RtlJaguarDevice.cpp
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 716d1b2

@snokvist
snokvist force-pushed the fix/jaguar1-8812-ack-disarm branch from 716d1b2 to 0941ec0 Compare September 6, 2026 08:25
@snokvist

snokvist commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

/review

Comment thread src/jaguar1/RtlJaguarDevice.cpp Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 0941ec0

@snokvist
snokvist force-pushed the fix/jaguar1-8812-ack-disarm branch from 0941ec0 to 401dbf9 Compare September 6, 2026 08:41
@snokvist

snokvist commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

/review\n\nPlease review the current 401dbf9 head. In particular, please re-check delayed-disarm/rollback token ownership and failed-clear semantics; the exact stale-timer replacement race and normal matching-timer disarm were both rerun on RTL8812AU hardware before this update.

@snokvist

snokvist commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

/review

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 401dbf9

@josephnef josephnef left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 401dbf9. No correctness findings; the three earlier Qodo items (unverified beacon release, ownership only on CHIP_8812, stale timer clearing a new arm) are all closed at this head by the verified stop, the all-die claim flags and the arm-generation token. Two doc nits inline, neither blocking.

What I checked:

  • ASan+UBSan build of the branch, ctest 55/55.
  • Token/rollback ordering in Init: the disarm jthread is declared after the scope-exit guard, so it is stopped and joined before the rollback runs, and a fired timer has already invalidated the token the rollback checks. A failed re-arm invalidates the previous token before its first write, so no stale timer can reach the replacement. Claim flags are retained on every unverified path.
  • Hardware regression on the non-8812 Jaguar1 path, since the PR touches every die's arm path. No RTL8812AU was on the bench, so the 8812 cell itself is not reproduced here; an RTL8821AU responder (0bda:a81a) against an RTL8822CU solicitor, channel 36 / MCS3 / retry limit 12:
phase reports ACK rate mean retries
on 1569 1.00 0.05
retarget 1549 1.00 0.04
off (never armed) 1526 0.00 12.0 (pin rate 1.00)

The disarmed phase reports SKIP on the 8821A as designed.

One behaviour change worth a line in the PR body: on CHIP_8812 an adapter whose MACID reads zero or group at arm time is now refused outright (has_safe_restore_mac), where previously the arm went ahead. Correct, since restoring zero stops TX scheduling, but it is a new failure mode a caller can hit.

Comment thread src/IRtlDevice.h
* airing and contaminated the next test's witness) — so any beaconing
* session that ends without a device power-cycle must call this. Idempotent;
* returns false when no beacon was active. */
* returns false when no beacon was active or shutdown could not be verified.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This base-contract sentence now promises a verified stop, but only Jaguar1 verifies: RtlJaguar2Device::StopBeacon and RtlJaguar3Device::StopBeacon still return true unconditionally after their writes. Either scope the sentence to Jaguar1 ("Jaguar1 additionally reads back the three stop controls and returns false if any is still active") or leave the base contract as it was.

Comment thread src/AdapterCaps.h
* the live-disarm claim for the responder used in that run. On the reference
* RTL8812AU, that cell found that the old gate-only clear left 1946/1946
* soliciting reports ACKed. Restoring the captured pre-arm MACID produced
* 0/1052 ACKed with retries pinned at 12; the never-armed counterpart was

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three different count sets now describe the same claim: this header says 0/1052 and 0/1033, the PR body says 0/997 and 0/1077, and docs/scheduled-mac.md points here as the owner of "the exact counts". They are different runs and all agree, but a caps header comment is turning into a measurement log. Keep one set here (or point at the doc that carries the table) so the next edit does not have to reconcile them.

"the current port identity could not be read");
return false;
}
if (!devourer::ack::has_safe_restore_mac(identity)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behaviour change to state in the PR body: before this PR an 8812 with a zero/group MACID at arm time still armed; now the arm is refused. The refusal is right (restoring zero stops TX scheduling), but it is a new failure mode for a caller with an unprogrammed EFUSE MAC.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants