Conversation
|
@josephnef PR4 of the station series, now that #452 and #454 are in. It is one commit on 101fdc9: the IRadio station identity seam, with the MT7612U as its first implementation. It builds on #452's retry-limit semantics: a station session has to set Before opening it went through two independent review passes, each checked against the tree, and it was re-verified on an MT7612U: |
PR Summary by QodoAdd MT7612U infrastructure-station identity support to IRadio
AI Description
Diagram
High-Level Assessment
Files changed (24)
|
Code Review by Qodo
1.
|
13eb6b9 to
87aef53
Compare
|
/review |
|
Code review by qodo was updated up to the latest commit 87aef53 |
…he MT7612U IRadio gains SetStationIdentity(own, bssid) / ClearStationIdentity(): program the MAC for the STATION half of an infrastructure BSS. The contract at the declaration says why this is not SetAckResponder(bssid) (on the MT7612U that retargets the port identity a station needs left alone), requires the call after the RX loop is running, makes Clear report whether the rollback was verified, leaves a later port-0 claimant (ACK responder, beacon) to the backend, and states that transmission is not part of the arm: a station's unicast must request an ACK (build_stream_radiotap(mode, false)) and needs a nonzero tx.retry_limit, whose default of 0 sends each frame once. The not-ported defaults return false / true. StartRxLoop's doc gains the lock rule and its libusb mechanism: never make a device call while holding a lock the RX callback takes. AdapterCaps::station_mode_ok (adapter.caps "station_mode") gates callers: TRUE on the MT7612U only, FALSE (not ported) everywhere else. MT7612U: mt7612u_set_station_identity() writes NO register. It refuses unless `own` already is MT_MAC_ADDR and MT_AUTO_RSP_EN is set (both reads fail closed), and records the BSSID for the host. The decision is the pure StationIdentity.h, covered by ctest mt7612u_station_identity (built only with DEVOURER_MT7612U). After every write to MT_MAC_ADDR the arm is re-checked against what the register holds, tri-state: a verified move drops it with a WARN, an unreadable register keeps it with a WARN, and a beacon start that fails and unwinds the identity back restores the arm it dropped. Mt7612uRadio::SetStationIdentity warns when tx.retry_limit is 0. mt7612uprobe gains the staid/sta/staack/norsp/bssen gates; the APC slot is the station's (mt76 keys it on the station's own address - slot 0 for a factory address), every write is read back, and staid checks on the chip that arming and clearing write no register. The receiving gates tick the PHY once a second, turn the receiver off before the EP4 ring, read their receive filter back, and verify every restore. The harnesses (tests/mt7612u_sta_identity.sh, _autoack.sh, _uplink.sh, sharing tests/mt7612u_sta_lib.sh) write only to a private OUT, kill only recorded PIDs, hand every adapter back (the DUT to mt76x2u, the Realtek peer and the AP by a re-enumeration guarded on their recorded USB identity), guard the AP adapter, start the unicast injector only after the DUT's bring-up, and refuse a table the injector did not really feed. Measured (docs/mt7612u-station-identity.md; one DUT, channel 6, near field, driven by mt7612uprobe, not through IRadio): - Auto-ACK, asked of the transmitter (a Realtek peer's CCX reports): 100% at 0.45 mean retries over 1279 reports with nothing armed, against 0% / 12.00 retries for a destination nobody holds, the DUT absent, and MT_AUTO_RSP_EN cleared (one bit, same code path and filter). One peer, one run per arm. - MT_MAC_BSSID programmed WRONG: 5877 unicast frames vs 6250 with nothing programmed; the AP in the station's slot 0: 5878. Arm A's ~6% excess is unexplained beyond a first-arm pattern. A wrong BSSID in the station's slot with its enable bit set was still acknowledged 867/867 (one run, acknowledgement only). Earlier rows that wrote the "derived" slot used slot 1 - the AP-side rule, not the station's - and are withdrawn. On the station-slot gate (one run): A 6277, B-E 5846-5861, F (both WRONG) 6003 unicast frames - no gating, within the 4-7% first-arm spread. Two earlier runs measured nothing: inferred cause, the AP on a full-speed hub stalling under the stimulus; the harness now orders the stimulus after bring-up and refuses a starved table. - Moving MT_MAC_ADDR under the managed filter: reception 103 -> 0 frames. - Uplink (MT_TX_STAT_FIFO, peer an ACK responder): 200/200 at 0.0 retries vs a 0/200 full-ladder control that is UNSETTLED by construction and accepted only as a floor. Ran at the initvals' retry limit 15; the harness now sets that limit explicitly (re-run: 200/200 vs 0/157 settled at 16.0). Not a default library session. Against it: the BSSID gate's first run had the monitor filter installed and is withdrawn, as is the probe-response auto-ACK method, whose control cannot move. Every cell is an unassociated station; no cell armed through IRadio; and StartRxLoop installs the monitor filter, so a station driven through IRadio runs promiscuous, not under the managed filter the cells measured. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VNC8xhn1rNCi5t6uLvE6M3
87aef53 to
cc48be4
Compare
josephnef
left a comment
There was a problem hiding this comment.
Reviewed at cc48be4.
Verified here: configure with -DDEVOURER_MT7612U=ON -DDEVOURER_REQUIRE_STA_CRYPTO_TESTS=ON, build clean (only the pre-existing basic_types.h / Radiotap.c warnings), ctest 80/80 with mt7612u_station_identity registered and passing, make -C src/mt7612u check resolves 39 entry points. CI green on every job.
Not verified here: the on-air gates. The rig's CF-922AC (INVENTORY #17) is not plugged at the moment, so staid / autoack / identity / uplink could not be re-run on independent hardware. The bench record in the description is the only hardware evidence; it is honest about that (one unit, one peer, one run per arm, no arm through IRadio).
Library side: no findings. Checked the tri-state hand-off end to end (fail_post → unwind_identity → clear_ack_responder's own check at allow_restore=0 → the beacon's check at !sta_lost_before; idempotent, so the double call is correct), new mt7612u_dev{} zero-initialises sta, mt_rr_chk leaves *val untouched so the failed-read-vs-zero-address case really is caught, mt_io_restore keeps a beacon start's io-error delta honest, and the StartRxLoop thread/lock text matches what UsbTransport actually does in Async vs SpscFat mode.
One thing to fix before merge (test tool, not the library):
bringup.cppgate_sta: the arm loop'sif (g_stop) break;falls straight into the verdict block, which only testsany_beacon/any_to_us/unverified. A SIGTERM during arm B, after arm A already saw beacons and unicast, printsGATE STA: measuredand returns 0 with B–F never run.gate_txsandgate_tsfwrapreturn 3INTERRUPTED - no verdictfor this;gate_stashould too.tests/mt7612u_sta_identity.shfolds the gate's rc into its exit, so today a gate cut short by anything other than the harness's own Ctrl-C reads as a pass.
Nits, take or leave:
gate_norsp/gate_bssendiscardwait_ticking()'s return, so an interrupted 1 s dwell exits 0 with "done". Same rc-3 convention applies.gate_bssen's singlebadpath returnsrestored ? 1 : 2for both a failed register write (a defect) and the "BIT(16) would not stay set" case (genuinely inconclusive). Split them.mt7612u_sta_autoack.sh/_uplink.shcleanup:sta_pid_kill peer INTthensta_peer_handback— the peer PID was started inside a command substitution, so the lib'swaitreturns at once and theauthorizedtoggle can land while txdemo/rxdemo is still inside chip de-init. A boundedkill -0poll before re-enumerating closes it.- Same two scripts:
sta_peer_recordsetsSTA_PEER_IDbefore the peer is ever opened, so a failingsta_dut_take(exit 2) still bounces the driver of a Realtek adapter this run never touched. - All three harnesses:
[ ! -e "$ROOT/firmware" ]follows symlinks, so a dangling operator link is treated as absent, overwritten, and then deleted by cleanup as "ours".&& [ ! -L "$ROOT/firmware" ]keeps the stated rule. mt7612u_sta_lib.shlock:kill -0on a PID read from a reusedOUTcan match a recycled PID of an unrelated process and refuse the run until it exits. Failure mode is a refusal, never a wrong number, so this is cosmetic.
Interface shape looks right to me: the seam is separate from SetAckResponder for a measured reason, the not-ported defaults are the trivially-true ones, and station_mode_ok is gated on a bench cell rather than a code reading. Happy to approve once the gate_sta rc is in.
|
Hardware follow-up at cc48be4, second unit and second rig: MT7612U Comfast CF-922AC (
Two harness portability notes from this rig, both worth a line in the script header rather than code:
Neither changes the verdict above: still a comment pending the |
josephnef
left a comment
There was a problem hiding this comment.
Changing the status to changes requested: the gate_sta interrupt exit code (falls into the verdict block and reports measured, rc 0, when cut short mid-arm; convention is rc 3 INTERRUPTED) needs to land before merge. Details in my two comments above.
What changed
IRadio::SetStationIdentity(own, bssid)/ClearStationIdentity(): avendor-neutral seam for the STATION half of an infrastructure BSS. The
contract lives at the declaration in
src/IRadio.h. It covers:SetAckResponder(bssid);ClearStationIdentity()returns whether the rollback was verified;an ACK (
build_stream_radiotap(mode, false)). It also needs a nonzerotx.retry_limit: the default is 0, which sends each unicast frame once.The not-ported defaults are
falseforSetStationIdentityandtrueforClearStationIdentity.IRadio::StartRxLoopdoc: which thread runs the callback, and the lockrule with its libusb mechanism. A synchronous transfer completes only when
some thread handles libusb events. In the Async RX mode that thread is the
RX thread, which is inside the callback waiting on the caller's lock.
AdapterCaps::station_mode_ok(thestation_modefield of theadapter.capsevent). TRUE on the MT7612U, FALSE (not ported) on everyother backend.
The MT7612U station arm,
src/mt7612u/station.cpp:mt7612u_set_station_identity()writes no register. It refuses unlessownalready isMT_MAC_ADDRandMT_AUTO_RSP_ENis set, and both readsfail closed. It records the BSSID for the host.
The decision is the pure
src/mt7612u/StationIdentity.h.After every write to
MT_MAC_ADDR(ACK responder, beacon, responderclear) the arm is re-checked against what the register holds
(
mt7612u_station_identity_check), tri-state:arm it dropped.
Headless cells in
mt7612u_station_identitycover this.Mt7612uRadio::SetStationIdentitywarns whentx.retry_limitis 0.The new C entry points are
mt7612u_set_station_identity,mt7612u_clear_station_identityandmt7612u_station_bssid;api_linknow counts 39.
Hardware gates and harnesses:
mt7612uprobegainsstaid,sta,staack,norspandbssen. Three harnesses drive them and the existingtxsgate:tests/mt7612u_sta_identity.sh,tests/mt7612u_sta_autoack.shand
tests/mt7612u_sta_uplink.sh, plus the helpertests/sta_unicast_inject.py.tests/mt7612u_sta_uplink.shsets the DUT's retry limit explicitly(
RETRY_LIMIT, default 15) and checks it was read back. It refuses 0.Its header states the runtime: about 9 minutes per arm at FRAMES=200.
The harnesses share
tests/mt7612u_sta_lib.sh, and all run as root:removed first);
DUT_SYSFSthat is not 0e8d:7612;mt76x2uon exit with a VID:PID-guardedauthorizedtoggle.The identity harness takes hostapd's PID from
-P. It accepts an APonly if the adapter:
It verifies that the unicast injector actually injected.
Record:
docs/mt7612u-station-identity.md, plus a section insrc/mt7612u/CLAUDE.md.Why
A station has to receive the AP's unicast addressed to it and auto-ACK it. On
the MT7612U, the obvious way to arm that (
SetAckResponder(bssid)) is the onething that breaks it. The interface needed a separate call whose contract says
so. The backend's behaviour had to be measured, not read off registers.
station_mode_oklets a caller refuse up front instead of starting ahandshake it cannot finish.
What is measured
The source is
docs/mt7612u-station-identity.md. Every cell used one MT7612Uon channel 6, near field, driven by
mt7612uproberather than throughIRadio.RTL8812CU's own CCX
tx.report, retry limit 12.(
MT_AUTO_RSP_ENcleared): 0% at 12.00 retries each.same shape, but it was not single-variable.
MT_MAC_BSSID, and the AP's BSSID in the station's APC slot, do notgate a managed station's receive.
MT_MAC_BSSIDprogrammed WRONG: 5877 unicast frames against 6250 withnothing programmed (re-run: 5909 vs 6478).
acknowledgement: autoack arm E on 6e99f43 was acknowledged 867/867.
The DUT log reads
WRONG BSSID 02:00:00:de:ad:02 in station APC slot 0, BIT(16) SET (high reg 000102ad), other slots empty, MT_MAC_BSSID = own address (verified).AP-side rule (mt76 keys a station's slot on its own address, slot 0
here) and are withdrawn;
DUT was A 6277, B 5857, C 5861, D 5846, E 5852 and F (both WRONG, base
and slot 0) 6003. Every write was verified, BIT(16) was clear, and the
managed filter
0x00015f97was in force;did. It does not show the registers are without effect: B-F sit 4-7%
below A, mostly B-E, which is the unexplained first-arm excess every
run shows, and a few-percent effect would hide in it. One unit, one AP,
one run.
MT_MAC_ADDRunder the managed filter drops reception of theAP's unicast from 103 frames to 0.
MT_TX_STAT_FIFO, the peer's ACK responder armed):only as a floor. The first run used the initvals' retry limit of 15; the
later runs programmed 15 and confirmed it by read-back.
Withdrawn (kept in the doc): the BSSID gate's first run, which had the
monitor filter installed. Also the probe-response auto-ACK method
(
staack's verdict), whose control cannot move against hostapd.What it can't do
Mt7612uRadio::StartRxLoopinstalls the monitor filter unconditionally, so a station driven through
IRadiodoes not run the managed filter the cells measured.MT_MAC_ADDRmakes it deaf" is a managed-filter property.IRadio. The seam writes no register, so themeasured state is the state a successful arm leaves. The
arm-then-measure path is still unexercised, and no in-tree demo calls
SetStationIdentity, so the retry-limit warning is not bench-checkedeither.
duplicate detection and hardware key lookup are untested.
src/stacaller. Frame building, thesupplicant, CCMP and the per-transmitter
DupDetectorbelong to whateverdrives the seam (a later PR).
Follow-ups
SetStationIdentityon Jaguar1/2/3).IRadioover thesrc/stacore, and the condensed station doc.
Verification
Review record:
verified each finding against the tree;
ClearStationIdentitydefault, thestation_mode_okpeer sentence), 2wording;
mt7612uprobe staidnow also checks that arming and clearing write noregister, so it expects 12 passed, 0 failed (was 10).
and all 9 fixed:
src/mt7612u/CLAUDE.mdreduced to pointers;(
rx_teardown(): receiver off, then the EP4 ring;bringup.cpprx_teardowncomment,Mt7612uRadio.cppmt7612u_rx_quiesce);sta_dut_takeverifies that no driver is left bound;OUT(lock);second, which the public header requires (
mt7612u_phy_tick); they donow.
driver: txdemo/rxdemo's libusb open detaches it and the harness only
handed back the DUT. autoack and uplink now record the peer's
idVendor:idProduct:serial and re-enumerate it on exit while the path
still reports that ID (
sta_peer_record/sta_peer_handbackintests/mt7612u_sta_lib.sh). This is harness-only; the gates areunchanged.
OUT: an unsetOUTgets a freshmktemp -d(0700), and agiven one is refused if it is a symlink or not owned by root or the
invoking user;
mt_mac_start()fails,since it sets ENABLE_TX before its WPDMA poll. This is an error path
only; the measured path is unchanged;
VID:PID:serial recorded at take;
PEER_VID:PEER_PID, not be a hub, and not be theDUT's path, or the run refuses to start.
cmake -S . -B build -DDEVOURER_MT7612U=ON -DDEVOURER_REQUIRE_STA_CRYPTO_TESTS=ON && cmake --build build -j4 && ctestmt7612u_usb_ids_vs_mt76andmt7612u_initvals_generated, which skip in every configure here).mt7612u_station_identityand the station cryptocells.
Default configure: 78/78 pass.
mt7612u_station_identityis notregistered there, because it is gated on
DEVOURER_MT7612U.RTL8733B-only subset: 73/73 pass.
MT7612U-only subset: 70/70 pass, and
mt7612u_station_identityisregistered.
make -C src/mt7612u check: passes, "api_link: 39 public entry pointsresolved".
Hardware (root,
mt76x2uunbound, run from the repo root; the scriptslink
./firmwareto/lib/firmware/mediatek):mt7612uprobe staid: the contract on the chip.tests/mt7612u_sta_autoack.sh: needs a Realtek CCX peer.tests/mt7612u_sta_identity.sh: needs a hostapd-capable AP adapter.tests/mt7612u_sta_uplink.sh: needs a Realtek ACK-responder peer.Bench record.
Rig, same for both heads:
40:a5:ef:5a:32:f8;a1873ee:
staid: 10/10.staread 5.9-6.5k unicast per arm,filtr=00015f97;staackarm C "received NOTHING".6e99f43:
staid: 10/10.staidpasses, andstaackarm C "received NOTHING".stagate's bring-up then logged 8 MCU timeouts, and every arm read0-8 frames and no beacons. The gate said INCONCLUSIVE, correctly.
a1873ee run).
Adversarial sides:
staack's verdict is not evidence.b94e119 (gates in their final form), same new rig; peer RTL8812CU at 5-1:
slot 0.
staid12/12;staackarm C received nothing.staverified,to_us: A 6268, B 5871, C 5881, D 5885, E 5877,F 6018.
staack's verdict is not evidence.Confirmed with no manual hand-back on 87aef53 and again on cc48be4
(after the qodo pass-2 harness guards): autoack "3 passed" both times,
and afterwards the DUT (1-1) was on
mt76x2uand the peer (5-1) onrtl88x2cu, both with their netdevs.523a324, new rig:
staid10/10;staackarm C deaf as recorded (verdict INCONCLUSIVE bydesign).
sta"measured", with the table above.The two failing identity runs (inferred, not proven).
beacons stopped.
failure
mcu.cppdocuments.position.
speed or better, and no full-speed hub).
gate_stachanged, and no kernel driver touched the DUT mid-run.🤖 Generated with Claude Code
https://claude.ai/code/session_01VNC8xhn1rNCi5t6uLvE6M3