You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
anxwifipi: 32 posted reads (static 100M BPS) and ~1% inbound frames dropped as UnknownTypesReceived #89
anxwifipi reports a static info->BPS = 100000000 (third_party/wifipi/src/unit.c:1488, fork 1750732). It is not a queried rate.
ami_sana2_rx_ladder (src/sana2/sana2_rx.c:2566) maps bps <= 100,000,000 to AMI_SANA2_RX_DEPTH_LAN = 32 (src/sana2/sana2_internal.h:88-97). Above that rung is the ring, AMI_SANA2_RX_MAX_DEPTH = 128.
Live planner inputs on the A1200 CM4 (pool 4106, 3 interfaces, no IPREQUESTS) compute exactly 32 IPv4 reads for wifiN.
A frame that finds no queued CMD_READ is an orphan: UnknownTypesReceived++ (third_party/wifipi/src/packet.c:1832), offered to S2_READORPHAN listeners, else dropped. baddata and overruns do not count it.
Observed correlation (measured; causation NOT proven):
IN (real iperf 2.2.1 playhouse3 -> Amiga): 17.80 Mbit/s, then 17.95 in the diagnostic run. OUT: 58.6 Mbit/s (the Pi4 figures were 35.5-35.8 IN / 58.4 OUT).
UnknownTypesReceived delta per IN run: +151 and +153, against ~5 at idle over the same span. Sender retransmits in the diagnostic run: 149 segs (0.92%), a ~1:1 match.
Sender ss: cwnd 25 = ssthresh, srtt 14.8 ms, minrtt 5.07; 25 x 1460 B / 14.8 ms = 19.7 Mbit/s.
pcap: advertised window median 804,768 B (about 20x BDP), so not rwnd-limited. The Amiga sent 77 dupACKs.
Drain bursts reach up to 10 SDIO frames at ~5 packets per read, which can exceed 32.
Unproven: that the 32-read depth causes the orphans, and that a larger read count fixes IN. The one causal trial (IPREQUESTS=64 on wifiN) was aborted: see the stall issue. Reporting a "negotiated PHY rate" in BPS needs a real rate query first. The current value is static.
Related: the S2_OFFLINE stall hazard and the S2_OFFLINE reads issue (cross-linked below).
Cross-links: #90 (the unbounded S2_OFFLINE DoIO stall hazard, which aborted the IPREQUESTS=64 causal trial) and #91 (S2_OFFLINE does not return queued CMD_READs).
Focused CM4 hardware A/B (2026-09-27). Same A1200 beta4, anxwifipi, external ant2, playhouse3 sender, real iperf2, one 10-second inbound arm each. Stock config (computed 32 IPv4 reads): 19.68 Mbit/s; UnknownTypesReceived +243 over +18,154 received packets. IPREQUESTS=64 after an A1200-only warm reboot: 35.09 Mbit/s; UnknownTypesReceived +177 over +32,077 received packets. Same peer, port and duration; no FORCE removal or broad matrix. The 64-read setting improved throughput about 78% and lowered the orphan fraction, but did not eliminate unmatched reads. RSSI was not sampled during both arms and the reboot is a possible confound. The original interface file was restored byte-for-byte (MD5 0db6fbe2239524632a660565bd3ea5ce), followed by a second warm reboot; wifipi is back online with the default config. Leave this issue open while residual drops are understood; 64 reads is supported as a mitigation, not a complete fix.
Update 2026-09-27: read depth, counters arm, and what is still unlocalized. Issue stays OPEN.
Setup, the same for every arm below. A1200 CM4, beta4 stack, dtparam=ant2 external antenna, RhodeIsland5 5 GHz ch 46. Real iperf2 from playhouse3, one 10 s inbound arm each.
IPv4 read depth.
reads
IN Mbit/s
UnknownTypes
per MB
32 (computed default)
17.8-19.7
+151 to +243
~7
64 (IPREQUESTS)
35.1
+177 / 32,077 pkts
—
128 (IPREQUESTS)
59.0
+82 / 53,483 pkts
1.1
256 is not reachable: IPREQUESTS is clamped at AMI_SANA2_RX_MAX_DEPTH = 128.
OUT is 58.6 Mbit/s.
UnknownTypesReceived is a no-queued-read orphan counter (packet.c:1797-1832), not a radio error counter. It is reported independently of sender retransmits; the two are not subtracted.
Counters arm (128 reads). Artifact: WiFiPi zz9k/89-rx-counters-beta4 @ 39e2e2c, which is the shipped 97ea7faa plus receive counters only. anxwifipi.device sha256 ad0ecbb8f27f8ecf06cc4a3436acd77c3de1b272fb4a96b80d981a4e8d7dd4f7, built in the v1.0.0-beta4 tree. That tree reproduces the shipped driver byte-identical (sha256 947c2081…dca4). The artifact was loaded from RAM: only and restored by warm reboot, with the on-disk hashes re-verified.
Result: 73.9 MB / 10.30 s = 60.2 Mbit/s, lost 0.
Receive path, over 8,692 SDPCM frames and +53,328 SANA packets:
rx CMD53 failures, all phases: 0
repeated headers (stale-buffer replay): 0
garbage headers: 0
glom subframe errors: 0
rx sequence mismatches, all bins including behind-by-1: 0
mailbox NAK / firmware halt / flow control: 0
flow-control mask changes: 0
longest drain: 43 frames
For this arm only, that rules out rx CMD53 failures, stale-header replay and SDPCM sequence gaps. It does not rule them out in general.
netstat -s: ip sent +1,507 = wifipi +1,305, eth0 +203, genet +1.
dropped on send 0; transmit errors 0 on every interface.
UDP and ICMP sent +0.
WiFiPi frames sent +1,306. This counter increments when the receiver dequeues the write (packet.c:1177), before the void SendPKT. It means dequeued/marked sent, not confirmed on the air.
sdio_sendpkt ignores CMD53 failure, and SendGlomDataPacket replies success unconditionally. So a TX CMD53 failure is invisible to every current counter.
playhouse3 (ens18, 0 kernel drops) captured 801 frames from .137 plus 1 ARP. The sender's ss shows segs_in 791 at its last sample.
Gap: about 500 fewer frames were observed at playhouse3 than the Amiga dequeued and marked sent. The stage is unknown: it could be the chip, firmware, air, AP or bridge. RF loss is not claimed.
Only pre-allocation ACK failures in the stack are unmeasured. Post-bookkeeping IP drops were 0.
Next. A counters-only design, no hardware arm yet:
TX CMD53 fail/phase in sdio_sendpkt
glom versus single TX
firmware counters (txfail / txretry / txnoack) and bus:txglom before/after
orphan split by EtherType (IPv4 / ARP / IPv6 / other) and unicast/multicast
Related.
anxwifipi: S2_OFFLINE does not return queued CMD_READs #91: zz9k/91-reads-on-beta4 @ b675d50 is 97ea7faa plus the queued-read/offline fix only. It passes its lifecycle test 25/25 under Amiberry, and it is not yet reviewed or installed.
A 2644f42-based build failed WirelessManager 1.3 ("Failed to initialize driver interface"). 96269bd's size-only NSCMD_DEVICEQUERY refuses WirelessManager's full-size request, which carries only io_Data.
The 2026-09-27 counters-only run on the A1200 (wifipi 7a4a7f2) stopped at the first firmware counter read and returned no measurement. A probable cause of the missing reply was found in source afterwards. It is not proven, and it is not a proven root cause of the #89 throughput gap.
What was sent:
FwCountersSnapshot called PacketGetVar("counters", buf, 2048).
PacketGetVar sets c_Length = 2048 and transmits the full request buffer, so about 2,076 bytes of SDPCM + BCDC went to the firmware.
What Linux does: Linux brcmfmac caps host→firmware BCDC ioctl input at BRCMF_TX_IOCTL_MAX_MSG_SIZE = ETH_FRAME_LEN + ETH_FCS_LEN (1,518 bytes). The constant is in core.h, lines 34-37. bcdc.c (lines 100-127) sends at most that many bytes of cmd+data, and keeps the requested output length separately.
What WiFiPi has sent before (shipped 97ea7faa):
The largest control frame in normal use is the CLM upload: PacketSetVar("clmload", UploadHeader + MAX_CHUNK_LEN 1400) at packet.c:2709 and :2763, about 1.45 KB with headers.
The largest GET is "ver" at 128 bytes (unit.c:1575).
So the counters read was the first control frame above the Linux cap that this firmware received from WiFiPi.
Consequence: WiFiPi's wait for the reply had no deadline and held wu_Lock (#93), so the stack's reads queued behind it and Wi-Fi stopped until the rollback.
Next steps, emulator only; the oversized run is not to be repeated:
Separate the transmitted input length (capped at 1,518) from the requested output length, as bcdc.c does.
Test a minimal bounded GetVar that sends only the name and requests 2,048 bytes of output, in the emulator, before any hardware run.
Frames from the Amiga MAC: 786 (785 TCP ACKs plus 1 ARP).
Frames to the Amiga: 13462.
Reading
Of the 1293 frames the driver handed over, 507 were not seen on the wire. Every one of the 1293 CMD53 writes completed without error.
TX CMD53 failure and over-credit gloms are therefore excluded as the cause.
This does not prove 507 frames were lost:
A successful CMD53 proves neither firmware acceptance nor RF delivery.
The capture filter was host 192.168.1.137, so any IPv6 traffic from the Amiga would not appear. 507 such frames in 10 s is implausible, but it is not excluded.
Next
Localizing further needs the firmware tx counters (txfail / txnobuf / txerror). That means a bounded counters GET.
WiFiPi currently transmits the full ~2064-byte BCDC request for a 2048-byte GET. Linux bcdc.c caps the transmitted header+data at 1518 (BRCMF_TX_IOCTL_MAX_MSG_SIZE) and still advertises the full length.
That cap is being implemented on a separate branch. It is a plausible cause of the earlier hung diagnostic, not a proven throughput root cause.
20c61ef7: a control frame is capped at 1518 BCDC bytes, as Linux bcdc does (Cap a control frame at 1518 BCDC bytes (AmiNetXDuo #89) WiFiPi.device#13). A counters GET of 2048 now sends 1518 bytes, not 2064, and still gets the whole reply. An oversized SET is refused with PACKET_CTRL_TOOBIG instead of being sent.
A1200 A/B tests. RAM-only, codex GO for each, rolled back to stock afterwards. Metric: IPv4 IDs from .137 that never appear on the wire.
run
WiFiPi
Mbit/s IN
missing IDs
08:21 A
stock 97ea7faa
58.5
37.2%
08:22 B
stock + chain pad
62.9
8.5%
08:44 A
stock + chain pad
61.0
7.6%
08:45 B2
+ single-frame Linux header
59.4
9.7%
The chain pad removes about 78% of the loss.
The single-frame header change had no effect, so it is parked.
The remaining ~8% happens after a successful CMD53: in the firmware, on the air, or at the AP. Missing IDs are a proxy, not proof of ACK loss.
Next: read the firmware tx counters (txfail/txnobuf/txnoack) with the now-capped counters GET, under its own GO.
Update 2026-09-27 14:22Z. Correction: the IPv4-ID gap metric does not measure Wi-Fi loss. The issue stays OPEN.
AmiNetXDuo has one NX_IP (src/netstack/netstack.c), and AMINETXDUO_IP_ID_RANDOMIZATION defaults off, so one IPv4 ID counter is shared by every interface. IDs spent on eth0 (.219) and genet (.158) during a run show up as holes in a Wi-Fi-MAC-only capture.
figure
status
37.2% -> 8.5% missing IDs (glom chain-pad A/B)
upper bounds on Wi-Fi loss, confounded by other-interface traffic
arm F 13:15Z: 113 of 1328 IDs missing (8.5%)
upper bound; the capture filtered the Wi-Fi MAC only, so it cannot be re-derived
throughput A/B (the chain-pad fix)
still a measurement
arm F counters: driver frames 1312, firmware txframe 1216
aggregate only; different populations and windows, no attribution
No attribution or release claim rests on ID gaps.
Next: a per-frame trace in a diagnostic build only.
Record points:
A: requests accepted by this driver, with their IPv4 key.
B: frames submitted over SDIO, with glom position and credit.
G: the SDIO completion of each write.
Capture: all three Amiga MACs, with 0 kernel drops.
Classes: only B-submitted keys can count as Wi-Fi loss. They are split into driver drop, SDIO-reported failure, and post-SDIO. Post-SDIO covers the chip, air and AP, and cannot be split per frame.
Status: the design is reviewed (deepseek-v4, claudecode, codex). Emulator validation comes before any hardware arm.
Correction (2026-09-27): the IPv4-ID gap figures are upper bounds, not measured Wi-Fi loss.
AmiNetXDuo has one NX_IP (src/netstack/netstack.c), so a single nx_ip_packet_id is shared by every interface, and AMINETXDUO_IP_ID_RANDOMIZATION is off by default. IDs spent on eth0 and genet, including replies to HTTP polls and a web shell session during the runs, appear as holes in a Wi-Fi-only capture.
Figure
Status
37.2% -> 8.5% missing IDs (A/B, glom pad)
Upper bounds, confounded by eth0/genet traffic
113 of 1,328 missing IDs (firmware-counter run)
Upper bound; other-interface IDs were not captured, so it cannot be corrected
Inbound throughput 58.5 -> 62.9 Mbit/s
Measured; unaffected
Driver sent 1,312 vs firmware txframe 1,216
Aggregate discrepancy only; different frame populations and windows, not a loss location
No attribution or release claim rests on ID gaps. The next step is the per-frame trace (design v1.5): per-frame keys at driver accept and SDIO submission, a capture of all three interfaces, and loss counted only for frames submitted to Wi-Fi.
Update 2026-09-28 — #89 stays OPEN. This supersedes the beta4-only status in the issue description and the earlier IPv4-ID interpretation.
Inbound/read depth.#107 landed on main at 95e3983a: WiFiPi interfaces now ask for 128 posted IPv4 reads when IPREQUESTS is absent; explicit values and the pool cap still apply. The original 32-read state is historical. Focused A1200 arms with explicit 128 reads reached about 59–60 Mbit/s versus about 18–20 Mbit/s with the old 32-read default, with fewer inbound orphans. The generated-NI/default-selection path passed emulator tests. Final beta7 archive hardware validation is still pending.
Separate outbound discrepancy. In a qualified RAM-only trace on the beta4 stack plus reviewed WiFiPi maintenance driver, 3,193 host-submitted IPv4 frames were joined to peer capture: 2,969 matched and 224 were absent after successful host CMD53 completion; none were classified as lost before CMD53. No bad TCP checksums, reported TX CMD53 failures, ring drops, or capture drops in that arm. CMD53 success does not prove firmware transmission or RF delivery. Firmware TX counters and peer capture totals are unmatched aggregates, so this does not distinguish firmware, air, AP/bridge, or capture. This is not a beta6-stack loss rate. IPv4-ID holes in older WiFi-only pcaps are only confounded upper bounds because the stack shares one ID counter across WiFi, eth0, and genet.
Content and timing of absent frames. Many are pure TCP ACKs later superseded by newer ACKs. A harmful subset is a pair of flight-final ACKs absent at the peer; the sender sends one new, sequence-contiguous 1,460-byte probe about 53 ms later, and a delayed ACK on our side makes roughly 0.15–0.24 s gaps. The beta6 stack has a 50,176-byte ACK threshold and GRO of up to 16 frames. Host-side trace analysis finds a roughly 67 KB median positive ACK advance, making those flight-final ACKs sparse. This is a plausible stack-side amplification of downstream ACK disappearance, not proof the stack caused the disappearance or that the radio is defective. AmiTCP_NG 4.1.7 exhibited a different ACK/traffic pattern during a beta6 comparison, but the runs are not equivalent enough for a root-cause claim.
Next discriminator. A beta6-only 8-MSS ACK-ceiling variant (11,680 bytes; GRO maximum unchanged) is under offline build/rehearsal. Measure actual ACK cadence and the harmful gap class before any merge or release claim. No hardware result from it yet. WiFiPi multi-frame F2 chain padding remains a measured throughput improvement (58.5 -> 62.9 Mbit/s in a focused A/B), not a validated loss percentage. Beta6 is public and unchanged; beta7 is unreleased and still gated on residual #89 and final archive verification.
Update 2026-09-28 06:43Z — hw28, GRO 16 vs GRO 8 on frozen main 7fd931de; #89 remains OPEN.
One 5 GHz leg per arm, 240 s inbound, TCPACKMAX=11680 + TCPGROWRTT=2, same interface file. The two libraries differ in one functional immediate: the GRO run cap, cmpiw #15 → #7 at text 0x21771 (sana2_rx.c:1417); the other 6 differing bytes are the version tag.
GRO 16 (ad50a25c)
GRO 8 (6f024baf)
Throughput
51.3 Mbit/s
52.0 Mbit/s
Median advancing-ACK advance
23,360 B
21,900 B
Hole repairs
432 / 1.448 MB
481 / 1.588 MB
Gaps > 150 ms
0
0
The predeclared criterion (median advance ≤ ~11,680 B) failed, and the repair difference is inside run-to-run variation. GRO 8 is not merged.
The Amiga's wstat out counter counts every submitted Ethernet frame, while the peer figure counts ACKs of the test flow only. The two cannot be subtracted, so no ACK-loss rate is inferred from them.
Stock rollback on the A1200 passed after both legs.
The residual hole-repair class with a grown window remains unattributed.
The flag-off build is byte-identical to the beta7 release driver.
The flag-on $VER is anxwifipi.device 1.0.0-beta7 (27.9.2026) AmiNetXDuo 7fd931d-dirty any.
Overhead (flag on, against the base):
Code: text +2,644 B. No new data or bss.
Static RAM: +4 B (s_Ring in struct SDIO).
Ring: 2^22 records × 16 B, allocated once at receiver start. It halves on AllocMem failure down to 4,096 records.
Stack deltas per frame: receiver +60, poller +28, SendGlomDataPacket +24, CtrlTransact +28 (caller's task). The receiver stack is 64 KB and the poller's 8 KB.
Records (16 B, CLO 1 MHz timestamp):
every SDIO header read, including empty looks, with its glom frame count and bytes;
each TX glom, with count, credit and TXSeq/MaxTXSeq;
TX credit: the initial state and every zero/non-zero change;
every firmware event frame;
S2_GETNETWORKS, escan and scan-done;
every synchronous control transaction's entry, send, reply and exit;
alignment pings.
Loss semantics.
The ring overwrites its oldest records. The dump header carries the total written, the first sequence number, the count, lost and the capacity.
All writers run in tasks under Forbid, and the dump copies under Forbid, so records are never torn. The host test detects tearing when the lock is removed.
Read-out is private device command 0xF089, via the ringdump tool. It writes only the used records: 32 + 16 × count bytes, at most 64 MiB.
Clock alignment. Five pings at leg start and five at leg end are seen at both ends.
The offset and drift come from the tightest ping at each end.
The error bound is max(δ) / 2, measured on each run.
CLO wrap (about every 71.6 min) is unwrapped in write order.
Host test.tests/host_ringtrace.c: 31/31 pass on macOS clang and on Linux gcc 14. It covers layout, wrap, multi-lap overwrite, short dumps, a dump during concurrent writes, and sequence numbers near 2^32.
Coverage rule. A candidate cause is refuted only when lost=0, or when the first dumped record comes before the start pings.
The alignment is sound: 99.4 % of the 43094 Amiga frames at the peer have a ring TX record 0–20 ms before them (p50 2.3 ms).
Correction. The first per-gap reading was off by 4.127 s: gap times were taken from the first pcap packet, not the first data frame. Every in-gap TX and RX claim based on those windows is retracted.
Corrected windows, the four gaps in which the peer saw no Amiga frame:
Gap start (epoch)
Length
RX frames in ring READ gloms: start edge / interior
IP-ID advance across the gap
1790587601.789
188.9 ms
12 / 0
0
1790587661.719
211.2 ms
18 / 15
0
1790587690.902
156.1 ms
0 / 17
0
1790587721.682
152.5 ms
54 / 32
0
The start edge is the first 6.466 ms of each window. Inside these gaps the peer sent no data except the one exit frame.
The ring records no scan, control transaction or firmware event inside these gaps.
TX credit never reached zero.
The driver woke at least every 10.4 ms throughout.
The ring records carry no packet identity, so neither a host ACK hold nor an AP, air or chip RX hold is established.
Next (source only): a per-frame RX identity record at the host receive-to-stack handoff, used with the existing per-frame TX identity record to follow each packet end to end. No hardware run is authorised yet.
hw31: one 2.4 GHz leg (2437 MHz), RX/TX identity ring trace, at 7fd931d + debug driver a3c995a (RAM only). LEG_VALID, ring lost=0, clock bound ±3.310 ms.
gap
direction
edge
1
data
unavailable (no host ACK arrived at the peer in the gap)
2
data
unavailable (trigger read ambiguous: a retransmitted copy)
3
data
unavailable (no host ACK arrived at the peer in the gap)
4
ACK
read_delay 337.002 ±3.310 ms, ack_production 0.938 ±0.001 ms
5
ACK
read_delay 348.447 ±3.310 ms, ack_production 1.067 ±0.001 ms
Gaps 1+4 and 3+5 are the same two stall events. In both, the driver polled the chip at least every 10.4 ms and got nothing (64 and 34 empty reads). There was no scan, no credit starvation and no late tick, and the held frames reached the SDIO read together.
Reading: in the two attributable stalls, the delay is before the host's SDIO read of the frame (air, AP or chip firmware). It is not host ACK production. This leg cannot separate those three. Three of the five gaps have no attributable edge. No root cause is claimed.
Next: source-only debug firmware RX-counter snapshot (wl_cnt v10) before/after a leg. No hardware until reviewed.
Evidence: playhouse2 ~/anxd-evidence/89-trace/hw31-ring24/G. Repro: python3 tools/ringtrace.py rxjoin T-ring.bin tr_T.pcap START END --times tr_T.times (WiFiPi.device 5579af5), with START/END from T-gaps.txt.
hw32: one 2.4 GHz leg (2437 MHz), 180 s, 7fd931d + RAM debug driver f3998bff (ring trace + firmware counters snapshots, taken before and after the leg only). LEG_VALID, ring lost=0, clock bound ±4.636 ms, runtime driver proof ok. 27.3 Mbit/s.
Release-ACK edges (10 gaps; data 2-5 and ACK 7-10 are the same four events)
gaps
read_delay (peer TX → host read)
ack_production (host read → host ACK TX)
larger
2 / 7
154.712 ±4.636 ms
1.161 ms
upstream_delivery
3 / 8
272.696 ±4.636 ms
1.961 ms
upstream_delivery
4 / 9
133.982 ±4.636 ms
0.721 ms
upstream_delivery
10
88.176 ±4.636 ms
1.806 ms
upstream_delivery
6
2.411 ±4.636 ms
131.227 ms
host_ack_production
1, 5
unavailable
Gap 1 had 35 non-advancing ACKs and no advancing one. Gap 5 had no host ACK inside it; its ACK half is gap 10.
Gap 6 is about 5.4 s into the leg, during ramp-up and settling, and is a single case (n=1). The 131 ms falls between the driver's handoff and the ACK reaching the driver's TX path.
Host TX during gaps 2 and 4
The host's ACKs written during the gap were held too. Every one the peer saw arrived at the gap END, in one burst: seen delays run 162→7 ms (gap 2) and 165→22 ms (gap 4).
The unseen ones (15 of 26 and 13 of 20) are pure ACKs with write=ok. Leg-wide, 3831 of 31863 host frames were unseen at the peer (20.6/s, all pure ACKs), so in-gap unseen density is about 4x that rate.
Unseen does not locate the loss beyond "after the SDIO write". A superseded ACK dropped inside that burst would look the same.
The header defines neither whether these count all RX or only frames to us, nor how they overlap. No baseline exists for any band. None of these become a loss percentage or a per-gap attribution.
Causality limits
In the attributable stalls, both directions were held and released together at the gap end, before the host's SDIO read (RX) and after its SDIO write (TX): air, AP or chip firmware. This leg cannot separate those three.
Host ACK production is not the stall in 4 of the 5 attributable events. The exception is the single startup gap 6.
No root cause is claimed.
Evidence: playhouse2 ~/anxd-evidence/89-trace/hw32-ring24/G. Repro: python3 tools/ringtrace.py rxjoin|txjoin T-ring.bin tr_T.pcap START END --times tr_T.times (WiFiPi.device 5579af5), with START/END from T-gaps.txt; python3 tools/wlcnt.py delta T-wlcnt-PRE.bin T-wlcnt-POST.bin (bdf1e2d3).
Correction to the hw32 comment above: the in-gap unseen ACKs are not excess loss. In gaps 2 and 4, all of them (15/15 and 13/13) are superseded by a later ACK on the same flow with a higher ack that the peer did see. The seen ACKs reached the peer in one burst at the gap end: 11 within 2.9 ms, and 7 within 0.1 ms. This is the benign superseded-ACK thinning class, not 28 harmful losses. Where they were dropped is still unknown. The rest of the comment stands.
Throughput: 26.5 Mbit/s, 3 data gaps >150 ms (201.0 / 198.7 / 178.3 ms). The earlier references are hw31 29.3 and hw32 27.3, so the effect of the sampler on throughput is unknown.
Sampler: 3502 samples, 0 lost, 0 late, 0 skipped during traffic. Cadence mean 53.6 ms, max 73.7 ms. 32.8 KB/s over SDIO. Every hold had 2-4 eligible windows, i.e. windows lying wholly inside the hold with valid endpoints.
Inside the holds (7 gaps: 3 data plus 4 ACK-direction, overlapping; 20 eligible windows)
Counter
Reading
txframe, txretrans, txnoack
0 in every in-hold window; only the release window of gap 6 has txframe=2
rxbeaconmbss
equal to tbtt in every window, about 1 per 70 ms, the same rate as outside holds
rxframe
0, except the first windows of data gap 1 (103, 48) and the release windows
rxnobuf
0 in every in-hold window
rxcrsglitch
25-67 per ~60-70 ms window, against a leg average of 22424 / 191 s ≈ 117/s
Ring cross-check (data gap 1, 201 ms): inside the window the host made 191 RX reads and wrote 18 ACKs to the chip (write=ok, which means SDIO transfer only). Meanwhile the sampled windows show no chip TX attempt. 12 of the 18 were later superseded.
Edges: gap 4: read_delay 104.8 vs ack_production 0.7 ms; gap 6: 360.8 vs 1.5 ms (both ±5.9 ms, upstream_delivery). Gap 5: 150.7 vs 162.9 ms, a single frame within about 2× the bound, so unresolved. Gaps 1-3 and 7 have no attributable edge.
Limits
Sampler windows do not prove a continuous zero across the unsampled intervals between them.
The readings are descriptive associations only.
CCA/air, AP, and an internal TX block in the chip all remain unresolved.
No root cause is claimed, and no btc_mode claim is made.
The rollback completed: stock hashes, RAM empty, all doors up.
Evidence: playhouse2 ~/anxd-evidence/89-trace/hw33-ring24/G (ring1.out has the full wlsample.py samples output). Repro:
Update 2026-09-28 15:25Z: hw34, a btc_mode bracket at 2.4 GHz. #89 remains OPEN.
Setup: one run in one test boot, three 120 s T legs B1 / Z / B2, at 7fd931d with RAM debug driver d7f67cff (WiFiPi.device debug/89-btc 792bc57a).
Only change between legs:btc_mode 1 → 0 → 1, set at runtime.
Instrumentation (as hw33): ring trace, pre/post firmware counters, 50 ms in-leg counter sampler.
Validity
LEG_VALID: ring lost=0 (2 696 862 records); runtime proof 0xF089/0xF08A/0xF08B/0xF08D.
Sampler: 7523 valid samples, 0 lost, 0 late.
btc_mode readbacks:
ORIG = 1;
SET 0, read back 0, before Z;
SET 1, read back 1, before B2;
final 1.
All 14 ops returned rc=0, and the ring records match the tool.
btc_flags=0 and btc_dos_status=1 before and after every leg.
Gaps: 38 raw, of which 4 between-leg idle gaps were excluded, leaving 34 in-leg.
RING24_P0_PASS: stock restored.
Leg
btc_mode
Data holds >150 ms
Mbit/s
Sampled in-hold windows with txframe flat
B1
1
4
26.2
8 of 9
Z
0
2 (180.0, 184.8 ms)
24.8
4 of 5
B2
1
9
24.2
20 of 24
Reading. The rule was declared before the run, drift first: |B1 − B2| = 5 ≥ 3, so the result is INCONCLUSIVE, whatever Z shows. With n this small, the drift between the two btc_mode=1 legs exceeds any difference Z could show. The GET readback proves the iovar value; live application is unproven. Counters are sampled only; a flat sampled window is not a continuous zero. No causal or coexistence claim is made.
Update 2026-09-28 16:05Z: hw35, a single 2.4 GHz leg. #89 remains OPEN.
Setup: one 180 s T leg (2437 MHz), 7fd931d, RAM debug driver 258a4a9d (WiFiPi.device debug/89-btc bfbe2ec0). Decoded with wlsample.py 6ae38baa (bafbdfa2).
Instrumentation: the sampler now reads 22 firmware counters every ~54 ms.
btc_mode: untouched.
Validity:
LEG_VALID; ring lost=0 (1 338 146 records); runtime proof 0xF089/0xF08A/0xF08B.
The decode was reproduced independently by a second reviewer.
RTS in the holds. Counter semantics come from WHD whd_wlioctl.h:2061-2062: txrts = dot11RTSSuccessCount, txnocts = dot11RTSFailureCount. So RTS attempts = txrts + txnocts. Attempts count retries, not distinct frames.
Controls: duration-matched out-of-hold windows in the same leg, about 3300 per hold. 97.5-98.7% of them show attempts. Median about 147 attempts/s, of which about 110/s are answered.
Expected = control median rate × sampled in-hold time (not the gap length).
Data hold
Sampled in-hold ms
RTS attempts
Expected at control rate
Answered
Unanswered
g0
105.6
2
15.8
0
2
g2
176.0
6
25.9
0
6
g4
109.5
6
16.1
1
5
g6
112.6
26
16.6
16
10 (38.5 %)
g8
110.8
15
16.3
1
14
g12
174.7
20
25.7
0
20
g1, g3, g5, g7, g9, g10, g11
105-200 each
0
~15-30 each
-
-
6 of the 13 data holds show sampled RTS attempts. Four of them (g0, g2, g4, g12) are below the control-rate expectation, and two (g6, g8) are at or above it.
7 of the 13 show no attempt in the sampled windows. That does not establish zero attempts throughout the hold.
Beacons from other BSSs continue to be counted in every hold, so the chip is still receiving on the channel.
Limits: descriptive association only. An unanswered RTS means no CTS arrived in time; it is not frame loss and not an attribution to the AP. There is no AP, firmware or CCA attribution and no fix claim. Internal hold versus CCA deferral remains unresolved.
Evidence: playhouse2 ~/anxd-evidence/89-trace/hw35-wide-leg/G. The full re-decode is ~/anxd-89-wlcnt/hw35-wlsample-bafbdfa.txt. Repro:
Update 2026-09-28 17:02Z — #89 remains OPEN. hw35 is the latest hardware evidence; its sampled RTS counters do not distinguish DUT-side CCA/backoff, chip-internal TX hold, or AP/air behavior. The next discriminator is a passive channel-6 802.11 capture, but no non-disruptive monitor radio is currently available: the lab hosts have no radio; playhouse2 is an LXC container (a USB radio must attach to its Proxmox host); this Mac’s en0 is the live uplink and has no active wired alternative. No further diagnostic hardware leg is scheduled until a capture path is demonstrated. A future capture must first show target BSSID/STA and adequate out-of-hold coverage; seeing a frame proves it aired, but not seeing one, or seeing apparently idle air, cannot alone prove chip suppression or rule out DUT CCA. No root cause, fix, merge, or release claim follows from this equipment blocker. Beta7 remains unpublished.
Reviewed #89 disposition on frozen main 7fd931d (2026-09-28): the original 32-posted-read WiFiPi condition is addressed by merged 95e3983. In that code, an absent IPREQUESTS selects 128 for anxwifipi.device/wifipi.device, while explicit settings and the pool budget still apply. The hw35 NI readback (a69505f0) has no IPREQUESTS override. I independently decoded hw35 T-ring.bin (SHA-256 b8dad38dc28fc8b98ca36373b07d6fb6277b7dd01eeb9b44af4095d4d19932e0): 36,300 RT_RXQ records, 36,200 with two openers; total posted reads p50=130, p90=138, max=139. This supports runtime operation around the new default, without another boot. RT_RXQ counts all openers and packet types, so it is not a direct IPv4-only count.
The remaining 2.4 GHz >150 ms holds are real but separate from the original 32-read state. Delay is predominantly upstream of host read/ACK production in attributable hw31-35 events, not universally: hw32 startup gap 6 had 131 ms of host ACK production, and hw33 gap 5 was unresolved with read/ACK delays close together. The traces do not distinguish DUT CCA/backoff, chip-internal hold, or AP/air behavior, and do not prove a new NetX defect or a firmware/RF cause. GRO8 and selective ACK-glom drafts did not meet their criteria and remain unmerged. I classify this as a documented open residual follow-up, not a supported beta7 code-fix blocker; passive 802.11 capture is deferred until a non-disruptive monitor path exists. This is a release disposition, not a root-cause or fix claim. Beta7 remains unpublished pending the exact-final archive and WirelessManager gate review.
Activity
tinic commentedon Sep 26, 2026
Cross-links: #90 (the unbounded S2_OFFLINE DoIO stall hazard, which aborted the IPREQUESTS=64 causal trial) and #91 (S2_OFFLINE does not return queued CMD_READs).
tinic commentedon Sep 27, 2026
Focused CM4 hardware A/B (2026-09-27). Same A1200 beta4, anxwifipi, external ant2, playhouse3 sender, real iperf2, one 10-second inbound arm each. Stock config (computed 32 IPv4 reads): 19.68 Mbit/s; UnknownTypesReceived +243 over +18,154 received packets. IPREQUESTS=64 after an A1200-only warm reboot: 35.09 Mbit/s; UnknownTypesReceived +177 over +32,077 received packets. Same peer, port and duration; no FORCE removal or broad matrix. The 64-read setting improved throughput about 78% and lowered the orphan fraction, but did not eliminate unmatched reads. RSSI was not sampled during both arms and the reboot is a possible confound. The original interface file was restored byte-for-byte (MD5 0db6fbe2239524632a660565bd3ea5ce), followed by a second warm reboot; wifipi is back online with the default config. Leave this issue open while residual drops are understood; 64 reads is supported as a mitigation, not a complete fix.
tinic commentedon Sep 27, 2026
Update 2026-09-27: read depth, counters arm, and what is still unlocalized. Issue stays OPEN.
Setup, the same for every arm below. A1200 CM4, beta4 stack,
dtparam=ant2external antenna, RhodeIsland5 5 GHz ch 46. Real iperf2 from playhouse3, one 10 s inbound arm each.IPv4 read depth.
IPREQUESTSis clamped atAMI_SANA2_RX_MAX_DEPTH= 128.packet.c:1797-1832), not a radio error counter. It is reported independently of sender retransmits; the two are not subtracted.Counters arm (128 reads). Artifact: WiFiPi
zz9k/89-rx-counters-beta4@ 39e2e2c, which is the shipped 97ea7faa plus receive counters only. anxwifipi.device sha256ad0ecbb8f27f8ecf06cc4a3436acd77c3de1b272fb4a96b80d981a4e8d7dd4f7, built in the v1.0.0-beta4 tree. That tree reproduces the shipped driver byte-identical (sha256947c2081…dca4). The artifact was loaded from RAM: only and restored by warm reboot, with the on-disk hashes re-verified.TX and the unlocalized gap.
netstat -s: ip sent +1,507 = wifipi +1,305, eth0 +203, genet +1.frames sent+1,306. This counter increments when the receiver dequeues the write (packet.c:1177), before the voidSendPKT. It means dequeued/marked sent, not confirmed on the air.sdio_sendpktignores CMD53 failure, andSendGlomDataPacketreplies success unconditionally. So a TX CMD53 failure is invisible to every current counter.ens18, 0 kernel drops) captured 801 frames from .137 plus 1 ARP. The sender'sssshowssegs_in791 at its last sample.Next. A counters-only design, no hardware arm yet:
sdio_sendpktcounters(txfail / txretry / txnoack) andbus:txglombefore/afterRelated.
zz9k/91-reads-on-beta4@ b675d50 is 97ea7faa plus the queued-read/offline fix only. It passes its lifecycle test 25/25 under Amiberry, and it is not yet reviewed or installed.NSCMD_DEVICEQUERYrefuses WirelessManager's full-size request, which carries onlyio_Data.Evidence:
~/anxd-evidence/89-cnt-b4(pcap md58a9d21f4…) and~/anxd-evidence/89-ipr128.tinic commentedon Sep 27, 2026
The 2026-09-27 counters-only run on the A1200 (wifipi
7a4a7f2) stopped at the first firmware counter read and returned no measurement. A probable cause of the missing reply was found in source afterwards. It is not proven, and it is not a proven root cause of the #89 throughput gap.What was sent:
FwCountersSnapshotcalledPacketGetVar("counters", buf, 2048).PacketGetVarsetsc_Length= 2048 and transmits the full request buffer, so about 2,076 bytes of SDPCM + BCDC went to the firmware.What Linux does: Linux brcmfmac caps host→firmware BCDC ioctl input at
BRCMF_TX_IOCTL_MAX_MSG_SIZE=ETH_FRAME_LEN + ETH_FCS_LEN(1,518 bytes). The constant is incore.h, lines 34-37.bcdc.c(lines 100-127) sends at most that many bytes of cmd+data, and keeps the requested output length separately.What WiFiPi has sent before (shipped 97ea7faa):
PacketSetVar("clmload", UploadHeader + MAX_CHUNK_LEN 1400)atpacket.c:2709and:2763, about 1.45 KB with headers."ver"at 128 bytes (unit.c:1575).So the counters read was the first control frame above the Linux cap that this firmware received from WiFiPi.
Consequence: WiFiPi's wait for the reply had no deadline and held
wu_Lock(#93), so the stack's reads queued behind it and Wi-Fi stopped until the rollback.Next steps, emulator only; the oversized run is not to be repeated:
bcdc.cdoes.tinic commentedon Sep 27, 2026
Update 2026-09-27 07:43–07:46Z: one RAM-only arm with the no-firmware counters build. The issue stays OPEN.
Setup
38aa181(device sha2566eaeb3b8…).ant2/ RhodeIsland5 setup, IPREQUESTS 128.Driver deltas across the transfer
Wire (playhouse3 tcpdump, 0 kernel drops)
Reading
host 192.168.1.137, so any IPv6 traffic from the Amiga would not appear. 507 such frames in 10 s is implausible, but it is not excluded.Next
txfail/txnobuf/txerror). That means a boundedcountersGET.bcdc.ccaps the transmitted header+data at 1518 (BRCMF_TX_IOCTL_MAX_MSG_SIZE) and still advertises the full length.Evidence:
~/anxd-evidence/89-nofw-arm(s2–s7 logs, in5.pcap/ss/iperf, wstat_delta.txt).tinic commentedon Sep 27, 2026
Update 2026-09-27 09:05Z. The issue stays OPEN: about 8% loss remains, and the beta7 gitlink has not been bumped yet.
Merged into WiFiPi
maint/beta4countersGET of 2048 now sends 1518 bytes, not 2064, and still gets the whole reply. An oversized SET is refused withPACKET_CTRL_TOOBIGinstead of being sent.A1200 A/B tests. RAM-only, codex GO for each, rolled back to stock afterwards. Metric: IPv4 IDs from .137 that never appear on the wire.
countersGET, under its own GO.Evidence:
~/anxd-evidence/89-ab,~/anxd-evidence/89-ab2.wifipi: move to maint/beta4 c0782c24 for beta7 (#102)
tinic commentedon Sep 27, 2026
Update 2026-09-27 14:22Z. Correction: the IPv4-ID gap metric does not measure Wi-Fi loss. The issue stays OPEN.
AmiNetXDuo has one NX_IP (
src/netstack/netstack.c), andAMINETXDUO_IP_ID_RANDOMIZATIONdefaults off, so one IPv4 ID counter is shared by every interface. IDs spent on eth0 (.219) and genet (.158) during a run show up as holes in a Wi-Fi-MAC-only capture.No attribution or release claim rests on ID gaps.
Next: a per-frame trace in a diagnostic build only.
tinic commentedon Sep 27, 2026
Correction (2026-09-27): the IPv4-ID gap figures are upper bounds, not measured Wi-Fi loss.
AmiNetXDuo has one
NX_IP(src/netstack/netstack.c), so a singlenx_ip_packet_idis shared by every interface, andAMINETXDUO_IP_ID_RANDOMIZATIONis off by default. IDs spent on eth0 and genet, including replies to HTTP polls and a web shell session during the runs, appear as holes in a Wi-Fi-only capture.No attribution or release claim rests on ID gaps. The next step is the per-frame trace (design v1.5): per-frame keys at driver accept and SDIO submission, a capture of all three interfaces, and loss counted only for frames submitted to Wi-Fi.
tests: a chunked padded ACK and an unpadded option ACK (#89)
ci: HOST_TESTS_EXPECTED 473 for sana2_copy_netx (#89)
sana2: the deferred TCP checksum excludes the Ethernet pad (#106)
sana2: WiFiPi defaults to 128 posted IPv4 reads (#107)
tinic commentedon Sep 28, 2026
Update 2026-09-28 — #89 stays OPEN. This supersedes the beta4-only status in the issue description and the earlier IPv4-ID interpretation.
Inbound/read depth. #107 landed on main at
95e3983a: WiFiPi interfaces now ask for 128 posted IPv4 reads whenIPREQUESTSis absent; explicit values and the pool cap still apply. The original 32-read state is historical. Focused A1200 arms with explicit 128 reads reached about 59–60 Mbit/s versus about 18–20 Mbit/s with the old 32-read default, with fewer inbound orphans. The generated-NI/default-selection path passed emulator tests. Final beta7 archive hardware validation is still pending.Separate outbound discrepancy. In a qualified RAM-only trace on the beta4 stack plus reviewed WiFiPi maintenance driver, 3,193 host-submitted IPv4 frames were joined to peer capture: 2,969 matched and 224 were absent after successful host CMD53 completion; none were classified as lost before CMD53. No bad TCP checksums, reported TX CMD53 failures, ring drops, or capture drops in that arm. CMD53 success does not prove firmware transmission or RF delivery. Firmware TX counters and peer capture totals are unmatched aggregates, so this does not distinguish firmware, air, AP/bridge, or capture. This is not a beta6-stack loss rate. IPv4-ID holes in older WiFi-only pcaps are only confounded upper bounds because the stack shares one ID counter across WiFi, eth0, and genet.
Content and timing of absent frames. Many are pure TCP ACKs later superseded by newer ACKs. A harmful subset is a pair of flight-final ACKs absent at the peer; the sender sends one new, sequence-contiguous 1,460-byte probe about 53 ms later, and a delayed ACK on our side makes roughly 0.15–0.24 s gaps. The beta6 stack has a 50,176-byte ACK threshold and GRO of up to 16 frames. Host-side trace analysis finds a roughly 67 KB median positive ACK advance, making those flight-final ACKs sparse. This is a plausible stack-side amplification of downstream ACK disappearance, not proof the stack caused the disappearance or that the radio is defective. AmiTCP_NG 4.1.7 exhibited a different ACK/traffic pattern during a beta6 comparison, but the runs are not equivalent enough for a root-cause claim.
Next discriminator. A beta6-only 8-MSS ACK-ceiling variant (11,680 bytes; GRO maximum unchanged) is under offline build/rehearsal. Measure actual ACK cadence and the harmful gap class before any merge or release claim. No hardware result from it yet. WiFiPi multi-frame F2 chain padding remains a measured throughput improvement (58.5 -> 62.9 Mbit/s in a focused A/B), not a validated loss percentage. Beta6 is public and unchanged; beta7 is unreleased and still gated on residual #89 and final archive verification.
tcp: ACK ceiling comment: stall mechanism is the leading model, n=1 (#89
8 remaining items
tinic commentedon Sep 28, 2026
Update 2026-09-28 06:43Z — hw28, GRO 16 vs GRO 8 on frozen main
7fd931de; #89 remains OPEN.One 5 GHz leg per arm, 240 s inbound,
TCPACKMAX=11680+TCPGROWRTT=2, same interface file. The two libraries differ in one functional immediate: the GRO run cap,cmpiw #15→#7at text 0x21771 (sana2_rx.c:1417); the other 6 differing bytes are the version tag.ad50a25c)6f024baf)outcounter counts every submitted Ethernet frame, while the peer figure counts ACKs of the test flow only. The two cannot be subtracted, so no ACK-loss rate is inferred from them.wifipi: send a same-flow pure ACK heading a TX glom on its own (#89)
wifipi: send same-flow pure ACK runs in pairs (#89)
Cap the settled TCP receive window per interface: TCPWINDOWMAX (#89)
tinic commentedon Sep 28, 2026
Debug ring-trace review package (#89, 2.4 GHz ACK-less stalls). Diagnostic only; not for merge or release.
Source. WiFiPi.device branch
debug/89-ringtrace, headb10df171dd6670fc820d048bc802d8522cbe40d8, based on the pinned maint commitc0782c2: tinic/WiFiPi.device@maint/beta4...debug/89-ringtrace1d78eb2: all the driver code, behind#ifdef WIFIPI_RINGTRACE, which is off by default.1190db7andb10df17: tests andtools/ringtrace.pyonly.Build. AmiNetXDuo 7fd931d, preset
default, pinned toolchain3869b4427396(GCC 16.2.0b).9112bc1fabee8e91aa8e5c13200234ae7af8bac6a426131760845d10621c5d3f-DWIFIPI_RINGTRACE)8dbabbdc6f1cf7bf22bbfb1749501cdaf0614d15c2354791ce38966467119e2cringdumpc9c67965a03473775d0ca09565523ced4b33bb02b507613087fcc99cc722d7c0$VERisanxwifipi.device 1.0.0-beta7 (27.9.2026) AmiNetXDuo 7fd931d-dirty any.Overhead (flag on, against the base):
s_Ringinstruct SDIO).Records (16 B, CLO 1 MHz timestamp):
Loss semantics.
lostand the capacity.ringdumptool. It writes only the used records: 32 + 16 × count bytes, at most 64 MiB.Clock alignment. Five pings at leg start and five at leg end are seen at both ends.
Host test.
tests/host_ringtrace.c: 31/31 pass on macOS clang and on Linux gcc 14. It covers layout, wrap, multi-lap overwrite, short dumps, a dump during concurrent writes, and sequence numbers near 2^32.Coverage rule. A candidate cause is refuted only when
lost=0, or when the first dumped record comes before the start pings.Run identities.
bsdsocket-7fd-b7grw-8a0f624c.library(8a0f624cc60342ad805018a46e78420be46ec994caabddbc262f957f32984d9f, TOKEN b7grw)a69505f0…: grow2 withDEVICE=RAM:anxwifipi.device9b7e56f6…, driver773f5ad1…, ANIb56154cd…, NI2cb939fc…, User-Startup5dc5e2d7…, ENVARC prefs5e9636cb…ackab-115.sh34cbfaa6…ring24-v2d5156525…(zz9k: NO BLOCKER)ring1-v4cc50a098…Leg. One leg on 2.4 GHz channel 6,
DUR=240.tinic commentedon Sep 28, 2026
Update 2026-09-28 10:05Z: hw30, 2.4 GHz ring-trace leg (correction); #89 remains OPEN.
hw30 ran one 240 s inbound leg on 2.4 GHz channel 6 with
TCPACKMAX=11680andTCPGROWRTT=2.8dbabbdc…(WiFiPidebug/89-ringtrace1d78eb2)8a0f624c…lost=0The alignment is sound: 99.4 % of the 43094 Amiga frames at the peer have a ring TX record 0–20 ms before them (p50 2.3 ms).
Correction. The first per-gap reading was off by 4.127 s: gap times were taken from the first pcap packet, not the first data frame. Every in-gap TX and RX claim based on those windows is retracted.
Corrected windows, the four gaps in which the peer saw no Amiga frame:
The start edge is the first 6.466 ms of each window. Inside these gaps the peer sent no data except the one exit frame.
Next (source only): a per-frame RX identity record at the host receive-to-stack handoff, used with the existing per-frame TX identity record to follow each packet end to end. No hardware run is authorised yet.
Evidence on playhouse2:
~/anxd-evidence/89-trace/hw29-ring24/G/(ring dump, pcap, run.log)~/claudecode-hw30-analysis/(marks.tsv,align.txt,win2-*.txt)tinic commentedon Sep 28, 2026
hw31: one 2.4 GHz leg (2437 MHz), RX/TX identity ring trace, at 7fd931d + debug driver a3c995a (RAM only). LEG_VALID, ring lost=0, clock bound ±3.310 ms.
Gaps 1+4 and 3+5 are the same two stall events. In both, the driver polled the chip at least every 10.4 ms and got nothing (64 and 34 empty reads). There was no scan, no credit starvation and no late tick, and the held frames reached the SDIO read together.
Reading: in the two attributable stalls, the delay is before the host's SDIO read of the frame (air, AP or chip firmware). It is not host ACK production. This leg cannot separate those three. Three of the five gaps have no attributable edge. No root cause is claimed.
Next: source-only debug firmware RX-counter snapshot (wl_cnt v10) before/after a leg. No hardware until reviewed.
Evidence: playhouse2
~/anxd-evidence/89-trace/hw31-ring24/G. Repro:python3 tools/ringtrace.py rxjoin T-ring.bin tr_T.pcap START END --times tr_T.times(WiFiPi.device 5579af5), with START/END fromT-gaps.txt.tinic commentedon Sep 28, 2026
hw32: one 2.4 GHz leg (2437 MHz), 180 s, 7fd931d + RAM debug driver f3998bff (ring trace + firmware
counterssnapshots, taken before and after the leg only). LEG_VALID, ring lost=0, clock bound ±4.636 ms, runtime driver proof ok. 27.3 Mbit/s.Release-ACK edges (10 gaps; data 2-5 and ACK 7-10 are the same four events)
Host TX during gaps 2 and 4
Firmware counters (leg-wide only; wl_cnt v10/848; 188.0 s; rxframe +423775)
Causality limits
Evidence: playhouse2
~/anxd-evidence/89-trace/hw32-ring24/G. Repro:python3 tools/ringtrace.py rxjoin|txjoin T-ring.bin tr_T.pcap START END --times tr_T.times(WiFiPi.device 5579af5), with START/END fromT-gaps.txt;python3 tools/wlcnt.py delta T-wlcnt-PRE.bin T-wlcnt-POST.bin(bdf1e2d3).tinic commentedon Sep 28, 2026
Correction to the hw32 comment above: the in-gap unseen ACKs are not excess loss. In gaps 2 and 4, all of them (15/15 and 13/13) are superseded by a later ACK on the same flow with a higher ack that the peer did see. The seen ACKs reached the peer in one burst at the gap end: 11 within 2.9 ms, and 7 within 0.1 ms. This is the benign superseded-ACK thinning class, not 28 harmful losses. Where they were dropped is still unknown. The rest of the comment stands.
tinic commentedon Sep 28, 2026
hw33: one 2.4 GHz T leg (2437 MHz), 180 s, 7fd931d + RAM debug driver d3d2ccfe (WiFiPi.device debug/89-wlsample 0b585b68).
counters, and an in-leg 50 mscounterssampler.Sampler: 3502 samples, 0 lost, 0 late, 0 skipped during traffic. Cadence mean 53.6 ms, max 73.7 ms. 32.8 KB/s over SDIO. Every hold had 2-4 eligible windows, i.e. windows lying wholly inside the hold with valid endpoints.
Inside the holds (7 gaps: 3 data plus 4 ACK-direction, overlapping; 20 eligible windows)
txframe,txretrans,txnoacktxframe=2rxbeaconmbsstbttin every window, about 1 per 70 ms, the same rate as outside holdsrxframerxnobufrxcrsglitchRing cross-check (data gap 1, 201 ms): inside the window the host made 191 RX reads and wrote 18 ACKs to the chip (write=ok, which means SDIO transfer only). Meanwhile the sampled windows show no chip TX attempt. 12 of the 18 were later superseded.
Edges: gap 4: read_delay 104.8 vs ack_production 0.7 ms; gap 6: 360.8 vs 1.5 ms (both ±5.9 ms, upstream_delivery). Gap 5: 150.7 vs 162.9 ms, a single frame within about 2× the bound, so unresolved. Gaps 1-3 and 7 have no attributable edge.
Limits
btc_modeclaim is made.Evidence: playhouse2
~/anxd-evidence/89-trace/hw33-ring24/G(ring1.outhas the fullwlsample.py samplesoutput). Repro:tinic commentedon Sep 28, 2026
Update 2026-09-28 15:25Z: hw34, a btc_mode bracket at 2.4 GHz. #89 remains OPEN.
btc_mode1 → 0 → 1, set at runtime.Validity
btc_modereadbacks:btc_flags=0 andbtc_dos_status=1 before and after every leg.Reading. The rule was declared before the run, drift first: |B1 − B2| = 5 ≥ 3, so the result is INCONCLUSIVE, whatever Z shows. With n this small, the drift between the two btc_mode=1 legs exceeds any difference Z could show. The GET readback proves the iovar value; live application is unproven. Counters are sampled only; a flat sampled window is not a continuous zero. No causal or coexistence claim is made.
Evidence: playhouse2
~/anxd-evidence/89-trace/hw34-btc-bracket/G(ring1.out,T-btc-ops.txt,T-btc-ring.seq,T-btc-reads.txt).tinic commentedon Sep 28, 2026
Update 2026-09-28 16:05Z: hw35, a single 2.4 GHz leg. #89 remains OPEN.
Validity:
RTS in the holds. Counter semantics come from WHD
whd_wlioctl.h:2061-2062:txrts= dot11RTSSuccessCount,txnocts= dot11RTSFailureCount. So RTS attempts =txrts + txnocts. Attempts count retries, not distinct frames.Limits: descriptive association only. An unanswered RTS means no CTS arrived in time; it is not frame loss and not an attribution to the AP. There is no AP, firmware or CCA attribution and no fix claim. Internal hold versus CCA deferral remains unresolved.
Evidence: playhouse2
~/anxd-evidence/89-trace/hw35-wide-leg/G. The full re-decode is~/anxd-89-wlcnt/hw35-wlsample-bafbdfa.txt. Repro:tinic commentedon Sep 28, 2026
Update 2026-09-28 17:02Z — #89 remains OPEN. hw35 is the latest hardware evidence; its sampled RTS counters do not distinguish DUT-side CCA/backoff, chip-internal TX hold, or AP/air behavior. The next discriminator is a passive channel-6 802.11 capture, but no non-disruptive monitor radio is currently available: the lab hosts have no radio; playhouse2 is an LXC container (a USB radio must attach to its Proxmox host); this Mac’s en0 is the live uplink and has no active wired alternative. No further diagnostic hardware leg is scheduled until a capture path is demonstrated. A future capture must first show target BSSID/STA and adequate out-of-hold coverage; seeing a frame proves it aired, but not seeing one, or seeing apparently idle air, cannot alone prove chip suppression or rule out DUT CCA. No root cause, fix, merge, or release claim follows from this equipment blocker. Beta7 remains unpublished.
tinic commentedon Sep 28, 2026
Reviewed #89 disposition on frozen main 7fd931d (2026-09-28): the original 32-posted-read WiFiPi condition is addressed by merged 95e3983. In that code, an absent IPREQUESTS selects 128 for anxwifipi.device/wifipi.device, while explicit settings and the pool budget still apply. The hw35 NI readback (a69505f0) has no IPREQUESTS override. I independently decoded hw35 T-ring.bin (SHA-256 b8dad38dc28fc8b98ca36373b07d6fb6277b7dd01eeb9b44af4095d4d19932e0): 36,300 RT_RXQ records, 36,200 with two openers; total posted reads p50=130, p90=138, max=139. This supports runtime operation around the new default, without another boot. RT_RXQ counts all openers and packet types, so it is not a direct IPv4-only count.
The remaining 2.4 GHz >150 ms holds are real but separate from the original 32-read state. Delay is predominantly upstream of host read/ACK production in attributable hw31-35 events, not universally: hw32 startup gap 6 had 131 ms of host ACK production, and hw33 gap 5 was unresolved with read/ACK delays close together. The traces do not distinguish DUT CCA/backoff, chip-internal hold, or AP/air behavior, and do not prove a new NetX defect or a firmware/RF cause. GRO8 and selective ACK-glom drafts did not meet their criteria and remain unmerged. I classify this as a documented open residual follow-up, not a supported beta7 code-fix blocker; passive 802.11 capture is deferred until a non-disruptive monitor path exists. This is a release disposition, not a root-cause or fix claim. Beta7 remains unpublished pending the exact-final archive and WirelessManager gate review.