tests: station client over IRadio + src/sta, and its on-air cell - #464
Conversation
|
@josephnef PR6 of the station series, #461's third library item: the It is one commit on dea68d4, so the Realtek arm from #463 is already underneath it. Everything new sits under On air (
Two things to know before running it:
On the Realtek side: Review before opening: two Flash reviews and an Opus check on the first cut, then one Flash review of the delta after the rebase. The findings and fixes are in the description's review record. |
PR Summary by QodoAdd an IRadio station client with headless and on-air tests
AI Description
Diagram
High-Level Assessment
Files changed (8)
|
Code Review by Qodo
1.
|
e17e6b6 to
b33421b
Compare
josephnef
left a comment
There was a problem hiding this comment.
Reviewed b33421b hunk by hunk against src/IRadio.h, src/sta/*, src/mt7612u/Mt7612uRadio.cpp and the env/usb helpers, and reproduced the on-air cell independently on this rig.
Hardware
tests/mt7612u_sta_onair.sh (MT7612U at 10-1.3, hostapd on a T3U 8812BU under rtw88 at 10-2, ch6, near field): 16 passed, 0 failed, 0 inconclusive, every adapter handed back (rtw88 unloaded, MT7612U back on mt76x2u, no netns/TAP/hostapd left).
| cell | result |
|---|---|
| open | associated, ping 0% loss, ledger plaintext 21 / encrypted 0, armed attempt 1/5, clear ran |
| wpa2 | four-way, group + pairwise rekey, ping 0% before/after, associations=1 answered=3 PTK=2 MIC=0, armed, clear ran, no retry_limit=0 warning |
| noarm | no arm / no clear ran; link 0% loss |
| retry0 | arm-time warning fired before the armed line; clear ran; link 0% loss |
Rig note: a first run was 4× INCONCLUSIVE because this host ships mt7662*.bin.zst; the harness scored it correctly as rig/bring-up and the library's error message says what to do. Worth one line in the script header (FW_DIR must hold decompressed blobs).
Build: -DDEVOURER_MT7612U=ON -DDEVOURER_REQUIRE_STA_CRYPTO_TESTS=ON, ctest 83/83.
Blocking (the harness misreports its own outcome)
-
A caught throw exits 0 and is scored INCONCLUSIVE "raise SECS". Every guard (RX loop thread,
on_rx, main loop, TAP reader start) setsg_stop = 1andmainthen returns 0. The cell's rule is "afterup:, status 0 = ran out of SECS → INCONCLUSIVE; anything else = FAIL", so a receive-path or RX-loop exception is reported as a timeout and the real cause is lost. Keep ag_faultflag besideg_stopand return nonzero from it (or have the cell grep the log forthrew). -
SIGINT is installed after bring-up, and the station is a background job of a non-interactive script. bash starts async jobs with SIGINT ignored; the handler at
sta_client.cpp:1062only takes over afterInitWrite, the TAP open and both thread starts.cleanup()ends the station withsta_pid_kill sta INT, andsta_pid_killdoes a barewaitbefore its 10 s poll. Ctrl-C the script while the station is still in firmware load /InitWriteand the INT is ignored: the trap path blocks inwait(or, past it, gives up, skipssta_dut_handbackand still releases the lock). Install the handlers beforeInitWrite(the handler is one atomic store and nothing readsg_stopbefore the loop anyway) and/or letcleanupescalate INT → KILL the waysta_stopdoes.
Non-blocking
limit_from_envisgetenv() != nullptr, butenv_config.cppapplies the value only viaenv_long_strict.DEVOURER_TX_RETRY_LIMIT=(empty) or non-numeric leaves the library default 0, skips the station default 7, and the banner attributes the 0 to the env var. Decide from the parsed value.- The TAP reader returns silently on a fatal
read()(got <= 0), no log, nog_stop, no counter:ip link del dvsta0mid-run leaves a station reporting a healthy link while every host frame vanishes. - Beacons without a DS Parameter Set are tagged with
g_tuned, which the loop stores only afterSetMonitorChannelreturns; a frame still queued from the previous channel during aDEVOURER_STA_SCAN_CHANNELSsweep lands with the new channel andsupervise()joins on it. Latent on hostapd (it always sends DS Params);RxAtrib's channel would close it. test_forged_mic_is_refused:goodis built and(void)ed, so the "the real AP's next frame still gets through" half of the property is never fed or asserted (andap.tx_pnwas consumed building it).
Everything else held: lock ordering (g_mu → g_q_mu, device calls outside g_mu), arm after StartRxLoop and outside the mutex, clear on every arm path, replay-window restarts keyed on install generations across both rekeys, the FCS trim (Mt7612uRadio.cpp clears fcs_present), bounds on every header read before the CCMP key-id read, the teardown order, and the cell's ledger regexes against report()'s format strings.
tests/sta_client.cpp is the in-tree caller of the station core and of IRadio::SetStationIdentity: scan, authenticate, associate, the WPA2-PSK four-way and CCMP over src/sta/, a TAP data plane for the host. It arms the station identity only when AdapterCaps::station_mode_ok is true (otherwise it refuses with exit 2; DEVOURER_STA_ARM=0 runs unarmed), arms after StartRxLoop and outside the mutex the RX callback takes, and clears on the way out whenever an arm was attempted, printing whether the clear verified. Unicast requests an ACK and the hardware retry limit defaults to 7 unless DEVOURER_TX_RETRY_LIMIT is set, so DEVOURER_TX_RETRY_LIMIT=0 is how a run asks for the single-shot uplink the library warns about. The data plane uses the core's DupDetector (one transmitter: the joined AP), delivers a non-Key EAPOL packet that on_decrypted_msdu returns unconsumed, refuses a ccmp_encrypted_len overflow, and trims the FCS by RxAtrib.fcs_present. ctest sta_client_headless (StaClientSelftest, Linux + OpenSSL) runs the headless cells in tests/sta_client_selftest.inc against a fixture that plays the authenticator: scan and sweep, re-join policy, key selection by key id, replay and duplicate windows, PTK/GTK rekeys, refusals and the ledger's identities. No device. tests/mt7612u_sta_onair.sh associates the MT7612U against hostapd in a network namespace: cells open, wpa2 (with group and pairwise rekeys), noarm (DEVOURER_STA_ARM=0 control) and retry0 (the tx.retry_limit=0 arm-time warning). Exit 0 pass, 1 fail, 2 inconclusive, 3 interrupted. docs/station-client.md describes the client; docs/station-core.md and src/sta/CLAUDE.md now name it as the core's caller. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VNC8xhn1rNCi5t6uLvE6M3
b33421b to
5acbbb3
Compare
|
@josephnef thanks, and for the independent run. All six are fixed in 5acbbb3 (still one commit on dea68d4), plus the FW_DIR line. 1. A caught throw no longer exits 0.
2. Ctrl-C during bring-up.
3.
4. A fatal TAP poll or read is a fault. POLLERR/HUP/NVAL, a read error or EOF is logged, counted ( 5. Beacons during a retune.
6. FW_DIR. The script header and the doc say it must hold the decompressed blobs. Beyond your list. I also ran a sweep for more instances of the same defect classes, which found these, all fixed in the same commit:
The same sweep over the already-merged MT7612U harnesses turned up some defects of the same kinds there. These are outside this PR; I'll bring them as a separate cleanup PR:
Re-run on 5acbbb3 (MT7612U station, hostapd on an 8812BU under rtw88, ch6): 16 passed, 0 failed, 0 inconclusive, and every adapter was handed back. The 8812CU on ctest is 83/83 with |
josephnef
left a comment
There was a problem hiding this comment.
Re-reviewed 5acbbb3 against the b33421b diff. Every item from the first round is fixed as described, and I checked the two blocking ones on hardware rather than by reading.
Verified on this rig
- Fault exit.
fault()→g_fault→ exit 3, every guard routed through it,fault=leads the ledger, the cell scores 3 as FAIL with the cause.test_a_fault_exits_nonzeropins it. - Interrupt during bring-up. Handlers now first in
main;sta_pid_killpolls a zombie-aware liveness check and reaps only after exit;cleanup()goes throughsta_stop's INT→KILL escalation. Measured: TERM to the harness the momentsta_clientappeared, beforeup:printed. Harness exited 3 in 6.1 s, the station still left and printed its ledger, nosta_client/hostapd/netns/TAP left, DUT back on mt76x2u. - The retry-limit parse now shares
env_long_strict(empty /5x→ station default 7, with the library's own warning), the TAP reader faults on error/HUP, the retune guard drops DS-less beacons for 50 ms instead of mis-tagging them, andtest_forged_mic_is_refusedfeeds and asserts the genuine frame. The strict duration/channel/ms parsing and the extra fault paths from the author's own sweep read correctly. - Build
-DDEVOURER_MT7612U=ON -DDEVOURER_REQUIRE_STA_CRYPTO_TESTS=ON: ctest 83/83. CI 23/23.
On air (tests/mt7612u_sta_onair.sh, MT7612U station, hostapd on an 8812BU under rtw88, ch6): first full run 14 passed, 0 failed, 1 inconclusive, then retry0 alone 2/2, then the interrupt check's open 5/5. Scored outcomes identical to the first round.
Non-blocking, from the inconclusive
Back-to-back hostapd restarts race the interface. cell_end returns as soon as the hostapd process exits and ap_up launches the next one immediately. The retry0 hostapd started 30 ms after the noarm one logged AP-DISABLED and died with Could not read interface wlp13s0u2 flags: No such device / nl80211 driver initialization failed. One in four restarts here; the author's runs and my first-round run did not hit it. A wait in ap_up for the netdev to be present and type managed before launching (or one retry on that failure) would close it. Correctly scored INCONCLUSIVE either way.
Observations, both on unscored INFO lines: noarm ping 33% loss on the first run and retry0's single-shot uplink 16.7% on the rerun, both 0% on other runs. Six pings at near field is a sample, not a measurement, and the cell says so by not scoring them.
Approving. The AP-restart wait can ride the cleanup PR the author already mentioned for the sibling MT7612U harnesses.
… of #465 (#466) Follow-ups and fixes collected from merged PRs: the maintainer's non-blocking review notes, a probable cause for #461's one-entry loss, part of the #465 harness defect sweep, and two txdemo teardown fixes we found ourselves. Fifteen commits, one per item or per harness, so any can be dropped or reordered on review, plus three follow-ups from the qodo review of this PR. Refs #461, Refs #465 (partial). ## What changed ### Maintainer review notes | Commit | Change | |---|---| | `0fee106` | #452: `txs_parse_long` carried two comment blocks back to back. The first one documents `txs_retry_limit_env`, and it now sits there. | | `4d862ff` | #453: TxBeaconGuard retries StopBeacon when an armed beacon's disable is refused. After a successful StartBeacon, the Jaguar2/3 StopBeacon returns false only for a refused disable, and `_bcn_hw_touched` makes a retry re-run it. A clean false after a refused StartBeacon still ends the loop. | | `294a671` | #453: the dated, attributed bench paragraph in `docs/jaguar3-tx-ring.md` becomes plain measurement. `IRtlRadio::GetTxDmaStatus` now says bit 13 (`BIT_PAYLOAD_OVF_8822C`) **can** latch at max duty on USB2, not that it does. | | `ddd8467` | The older bring-up gates in `src/mt7612u/tools/bringup.cpp` now call `mt_mac_stop()` when `mt_mac_start()` fails. `mt_mac_start` sets `ENABLE_TX` before its WPDMA poll, so a failed start could leave TX enabled. Each gate keeps its own teardown order (`rx_teardown()` first where a ring was draining EP 4). | | `2cc5330` | #463 review: Jaguar1/2/3 `Stop()` clears `_station_ready` in the block that clears the arm, under the station lock. A `SetStationIdentity` after `Stop()` is refused until the next bring-up. | | `907a5b5` | #463 review: `tests/realtek_station_onair.sh`'s submission floor is scaled to the span the arm actually aired, not to SECS, which also holds the peer's bring-up. The span runs from the first submit to the final `tx.stats` (`fcdec63`, below). | | `da8cd6d` | #463 review: the same script runs its whole AP guard in the preflight, before the DOWN half spends its minute, and again at the start of UP. | ### #461: the txs gate's lost status entry | Commit | Change | |---|---| | `2e7a3de` | `gate_txs` claims an arm's first status entry back when a stale `MT_TX_STAT_FIFO_EXT` word mislabelled it with the previous arm's pktid. Details under Why. | ### #465 (partial): the harness defect sweep | Commit | Harness | Change | |---|---|---| | `53477e0` | `mt7612u_ap_onair.sh` | An interrupt stops the run instead of carrying on into the next cell. Before re-enumerating `AP_SYSFS`, cleanup waits for its children to exit (10 s bound, zombie-aware via the lib's `sta_pid_alive`), and skips the re-enumeration if it had to KILL one. The stop cell FAILs when PHASE 2 never appeared, rather than scoring "beacon gone". The ping-loss figure is quoted whole ("66.6667%", not "6667%"). | | `05bdaf7` | `mt7612u_sta_autoack.sh` | DUT hand-back only after its process is confirmed gone, with a CLEANED guard so the EXIT pass after an INT cannot undo that refusal. | | `8a2ab14` | `mt7612u_sta_uplink.sh` | The same hand-back rule. The DUT's `mt7612uprobe txs` runs under `timeout -s INT -k 10`, bounded by the gate's own worst case. | | `bf9c0dd` | `mt7612u_sta_identity.sh` | `AP_REENUM` is set before hostapd starts, and hostapd is killed unconditionally. The BSSID gate is bounded. The monitor vif is probed in the preflight. Exit codes are 0/1/2/3, with rig refusals as 2 and the INT/TERM trap as 3. | | `d473027` | `mt7612u_sta_onair.sh` | The hostapd restart race. Before each launch, `ap_up` waits (10 s bound, inside the netns) for the AP netdev, forces it back to `type managed`, and brings it up. | Not in this PR, from #465: the generic Realtek DUT take, the reconnect cell and the CCMP soak. Those come in a separate PR. ### Our own finding: txdemo thread joins | Commit | Change | |---|---| | `64ebce3` | `examples/tx/main.cpp` joins the optional IN drainers (`DEVOURER_DRAIN_BULK_IN`, `DEVOURER_POLL_INTR_IN`) on every exit. | | `6b31d5f` | It also joins the RX and USB event threads on every exit. | In both cases an early return used to destroy a still-joinable `std::thread`, which calls `std::terminate`. Small scope guards now join the threads in the normal teardown's order, before `session.close()`. The legacy fork child is covered by `18f0a20`, below. ### qodo review of this PR | Commit | Change | |---|---| | `fcdec63` | `realtek_station_onair.sh`: the aired span started at the first `tx.report`, so a transmitter that started, stalled and then burst 50 reports in its last second passed both floors. txdemo's `tx.frame` now carries `t` (the `tx.report` timebase; `docs/logging.md`). The span runs from the first submit, and liveness also refuses a first report more than `MAX_GAP_MS` after it (`lead_ms`). | | `92dfa1d` | `mt7612u_ap_onair.sh`: if a between-cell cleanup could not reset the AP (a process outlived TERM), the remaining cells are recorded as NOT RUN and the run exits 2, instead of being scored on an unreset adapter that may still be beaconing. | | `18f0a20` | txdemo: the `DEVOURER_TX_WITH_RX` fork child leaves through `std::_Exit` after flushing stdio, with `Init` in a try. No destructor runs in it, so its copies of the IN-drainer threads no longer terminate it. | ## Why - **#461 (the txs entry loss).** The FIFO read is two USB control transfers: EXT first, then the main word, which pops the entry. - If an entry is filed between the two reads, the pop is paired with the previous entry's EXT word. - Inside an arm that is harmless, because every entry carries the same pktid. - On an arm's first entry it carries the previous arm's pktid. That entry is counted late, the arm stays one short, and every per-frame wait then times out. These are the ~6 fps N-1/N rows. - `docs/mt7612u-tx-retry.md`'s own table rules out the alternative, "status posted only on the next TX". At limit 0, receiver ON, arm f lags with its late entry in its own row, after arm e settled and owed nothing. Arm g after it shows no late entry. - **#465.** Each item was a way for a harness to score a dead or wedged device as a result, hang without bound, or re-enumerate an adapter under a process still in its de-init. - **The txdemo threads.** A joinable `std::thread` destructor terminates the process, so an otherwise clean early exit aborted. ## Measured - **#461, on the author's unit:** `gate_txs` with the stale-EXT claim settled 16/16 arms at retry limits 5 and 0 (recorded on #461). - The claim's current form is keyed to the last arm that actually sent a frame, so an arm whose every submit failed is skipped. That refinement has not been run on hardware. - **The uplink harness** (`tests/mt7612u_sta_uplink.sh`) is INCONCLUSIVE both on this branch (48/53 status entries) and on master (57/60). It predates the branch. - That loss is several entries per arm. It is **not** the race above, which accounts for at most one entry per arm, and it is still being investigated. - **Submission floor (`907a5b5`), checked on synthetic JSONL through the script's own `summarize`/`usable`:** | Fixture | Old floor | New floor | |---|---|---| | Slow bring-up, 327 frames in 2.0 s | refused at 500 | usable, floor 101 | | Start, stall, 60 reports in the last second | refused at 500 | refused: lead_ms 8800 (live=0), and 60 < floor 490 | | Rate stall, 110 frames over 10 s | — | refused, floor 500 | | Hard stall | — | refused, live=0 | | No `tx.frame` with `t` (an older txdemo) | — | refused, live=0 | ## What it can't do - The stale-EXT claim recovers at most one entry per arm, and only when the previous sending arm settled. A deficit larger than one entry is not this race. - **The INT/TERM exit code is not uniform across the harnesses:** - realtek_station_onair, mt7612u_sta_onair and now mt7612u_sta_identity exit 3, as their headers document; - autoack, uplink and ap_onair still exit 130. - Unifying them is left for later. ## Follow-ups - Find the uplink harness's multi-entry status loss. - Hardware-run the "last arm that sent" form of the txs claim. - Unify the interrupt exit code across the station harnesses. - From #465: the generic Realtek DUT take, the reconnect cell and the CCMP soak (separate PR). ## Verification - `ctest`: 83/83, both plain and under `-DDEVOURER_SANITIZE=address+undefined`, built with `-DDEVOURER_MT7612U=ON -DDEVOURER_REQUIRE_STA_CRYPTO_TESTS=ON`. The two reference-submodule tests are skipped without `reference/`. - `make -C src/mt7612u check` passes. - `bash -n` (and `sh -n` for the `/bin/sh` scripts) is clean on every touched script. `shellcheck -x` is clean on all of them except `tests/mt7612u_ap_onair.sh`, whose remaining SC2046/SC2015/SC2012 notes predate this branch. - **Hardware, on this head (6b31d5f):** MT7612U at 1-1, RTL8812CU at 5-1, RTL8812BU at 8-1 (rtw88), ch6, near field. Every adapter was handed back after each harness, and no hostapd, netns or monitor interface was left. | Harness | Result | |---|---| | realtek_station_onair | 5 passed, 0 failed, 0 inconclusive, both ways (8812CU and 8812BU as the station) | | mt7612u_sta_onair | 16/16 | | mt7612u_sta_identity | rc 0 (STAID 12/12; STAACK reports its own A/B as non-discriminating on this rig, and only arm C is scored, as before) | | mt7612u_ap_onair | 14/14 | | mt7612u_sta_autoack | 3/3 | - **The three qodo follow-ups (`fcdec63`, `92dfa1d`, `18f0a20`), re-run on 18f0a20:** `realtek_station_onair` 5 passed, 0 failed, 0 inconclusive, both ways, under the floor that now starts at the first submit. `mt7612u_ap_onair` 14/14. Every adapter was handed back. - The same harnesses also ran on the pre-squash head, with the same results. `mt7612u_ap_onair` showed one flaky open-cell ping run there, which passed on three repeats. `mt7612u_sta_uplink` was INCONCLUSIVE there and on master alike. That is the multi-entry loss under #461, which this PR doesn't address. - **Review record:** qodo-gate flagged three threads on 6b31d5f. All three held, and are fixed in `fcdec63`, `92dfa1d` and `18f0a20`. ## Blast radius - **Library:** the three Jaguar `Stop()`s only. Each clears `_station_ready`, and only the `SetStationIdentity` gate reads it. Every bring-up already clears it on entry and sets it again at the end. - **Everything else** is `src/mt7612u/tools/` (the bring-up tool), `examples/tx/` (txdemo), `tests/` and `docs/`. - **One event-schema addition:** txdemo's `tx.frame` gains a trailing `t`. Every in-tree consumer either matches on `"n"`/`"rc"` or parses the JSON, so none of them changes. - **`src/IRtlRadio.h` and `src/StationArm.h`:** comment-only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01VNC8xhn1rNCi5t6uLvE6M3 --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
What changed
tests/sta_client.cpp— a station client overIRadio+src/sta/: scan, authenticate, associate, the WPA2-PSK four-way and CCMP, and a TAP device for the host. It is the in-tree caller ofIRadio::SetStationIdentity:AdapterCaps::station_mode_okis true; otherwise refused at start-up with exit 2 (DEVOURER_STA_ARM=0runs unarmed);StartRxLoopand outside the mutex the RX callback takes;ClearStationIdentityon the way out whenever an arm was attempted, with the result printed (restored (verified)/NOT VERIFIED; trivially true on MT7612U, whose arm writes nothing).tx.retry_limitdefaults to 7 unlessDEVOURER_TX_RETRY_LIMITis set, soDEVOURER_TX_RETRY_LIMIT=0is how a run asks for the single-shot uplink the library warns about.rx.enable_with_tx). Jaguar3'sInitWritekeeps the RX path only when that is set, and enabling RX after a TX-only bring-up is not reliable there. Without it, an 8822C station could join nothing. The MT7612U and Jaguar2 backends don't read the flag.DupDetector(one transmitter, the joined AP), a non-Key EAPOL packet returned unconsumed byon_decrypted_msduis delivered to the host, accmp_encrypted_lenoverflow is refused, and the FCS is trimmed byRxAtrib.fcs_present.tests/sta_client_selftest.inc+ cteststa_client_headless(StaClientSelftest, Linux + OpenSSL, collected by theselftestsaggregate) — headless cells against a fixture that plays the authenticator: scan selection and sweep, re-join policy, key selection by key id, replay and duplicate windows, PTK/GTK rekeys, refusal of plaintext/fragments/A-MSDU, the FCS trim, the ledger's identities.tests/mt7612u_sta_onair.sh— the MT7612U joins hostapd running in a network namespace (so the ping crosses the air; the route is asserted first). Cellsopen,wpa2(group + pairwise rekeys),noarm(control) andretry0(thetx.retry_limit == 0warning). Exit 0 pass / 1 fail / 2 inconclusive / 3 interrupted. Usestests/mt7612u_sta_lib.shfor the private OUT, the run lock, PID-recorded kills and the DUT hand-back.docs/station-client.md;docs/station-core.mdandsrc/sta/CLAUDE.mdnow name the client as the core's caller.Why
Nothing in-tree called
SetStationIdentity. This harness exercises the arm-then-measure path end to end — arm, associate, key, carry traffic, clear — and is where thetx.retry_limit == 0warning gets bench-checked. It also givesDupDetectorand the MSDU<->Ethernet helpers their first caller.What is measured
On-air (
tests/mt7612u_sta_onair.shon this head: MT7612U station, hostapd on an RTL8812BU under the in-kernel rtw88 driver, ch6, near field): 16 passed, 0 failed, 0 inconclusive. Every adapter was handed back, with no hostapd, monitor interface or namespace left.openwpa2tx.retry_limit=0warning (retry limit 7, the station default)noarmretry0tx.retry_limit=0warning appeared (scored); clear ran; the link is reported: four-way completed, ping 0% loss with single-shot unicastThe first cut of this commit ran the same 16/16 on the same rig before the review fixes, and again after them, after the rebase onto #463, after the qodo fixes and after the maintainer review (this head); the
wpa2cell also passed alone.Interrupt check on this head: SIGINT to the harness one second after
sta_clientstarted, i.e. inside its bring-up. The harness was gone 7 s later, every adapter was back on its kernel driver, and nosta_client, hostapd or netns was left. The maintainer's independent run on b33421b (MT7612U and a T3U 8812BU under rtw88) was also 16/16.Against it:
rtl88x2cudriver cannot be the AP, because its phy cannot change network namespace (iw phy set netnsreturns -95). The cell refuses such an AP before taking the DUT (exit 2), and that was checked on this rig.noarmpassing with a working link is what the MT7612U predicts, since its arm writes no register. It does not show what the arm buys on this chip.retry0's link held at near field. A single-shot uplink is fragile by construction, and the cell scores the warning, not the link.StopRxLooponly sets a stop flag, the loop's exit stops the DIG/phydm workers and nothing on the TX side, andsend_packetthere is a synchronous bounded bulk-OUT.What it can't do
sta_dut_take) until a generic DUT take/hand-back exists. The Realtek arm landed in jaguar1/2/3: the Realtek station arm for IRadio::SetStationIdentity #463 (8822C / 8822B reportstation_mode_oktrue), so the client arms those dies whenDEVOURER_VID/DEVOURER_PIDselect one, but no on-air cell here covers it. A die that reportsstation_mode_okfalse is refused cleanly (exit 2);DEVOURER_STA_ARM=0runs it unarmed.noarmcontrol cannot separate "the arm made it work" on MT7612U: there the arm writes no register (it checksMT_MAC_ADDRandMT_AUTO_RSP_EN), so it scores only that no arm/clear ran and reports the link.Follow-ups
Review record
Two Flash reviews and an Opus check on the first cut; all fixed in this commit:
test_the_dup_cache_spans_a_rekey_not_an_association, which fails both with the reset at the rekey and with no reset);sta_client up:is INCONCLUSIVE (rig/bring-up), not FAIL; a hung station is escalated to SIGKILL and the DUT is not re-enumerated under it; the clear is scored as having run (its MT7612U result is trivially true); the AP phy must listset_wiphy_netns; traps installed before the DUT is taken; the AP interface's UP state restored;g_sent; the ledger is described as printed oncesta_client up:has printed;sta_client_headlessnamed in the OpenSSL-absent configure messages.After the rebase onto #463, a check against the merged Realtek arm and one more Flash review of the delta:
rx.enable_with_tx), which a Jaguar3 station needs;StopRxLoop()are guarded like the rest, so a throw there can no longer skip leave, clear and ledger;std::exceptionthrows.Maintainer review (josephnef, CHANGES_REQUESTED at b33421b), all fixed:
g_fault, exit 3,fault=1in the ledger; the on-air cell scores exit 3 as FAIL with the cause named;main(undoing an inherited SIG_IGN); the cell's cleanup escalates INT -> KILL, andsta_pid_kill(shared lib) polls before it reaps instead of a bare blockingwait;DEVOURER_TX_RETRY_LIMIT, not from the variable being set;test_forged_mic_is_refusednow feeds the real AP's frame at the forged PN and checks delivery and the replay window;FW_DIRmust hold decompressed blobs.Verification
cmake -DDEVOURER_MT7612U=ON -DDEVOURER_REQUIRE_STA_CRYPTO_TESTS=ON: build clean,ctest83/83 (with jaguar1/2/3: the Realtek station arm for IRadio::SetStationIdentity #463'sstation_arm).-DDEVOURER_SANITIZE=address+undefined:ctest83/83.ctest72/72), Jaguar3-only (ctest72/72,sta_client_headlessandstation_armpass).bash -nandshellcheck -xclean ontests/mt7612u_sta_onair.shandtests/mt7612u_sta_lib.sh.sudo DUT_SYSFS=<mt7612u> AP_SYSFS=<rtw88 adapter> CH=6 tests/mt7612u_sta_onair.sh(one cell: appendopen,wpa2,noarmorretry0).🤖 Generated with Claude Code
https://claude.ai/code/session_01VNC8xhn1rNCi5t6uLvE6M3