jaguar3: gate PHY-status parsing on the RX-desc PHYST bit (DW0 bit 26) - #409
jaguar3: gate PHY-status parsing on the RX-desc PHYST bit (DW0 bit 26)#409gilankpam wants to merge 1 commit into
Conversation
1ee3244 to
f83c2b0
Compare
The jgr3 RX loop parsed the drvinfo area as a PHY-status report whenever it was long enough (>= 28 bytes). But drvinfo space is reserved on *every* frame — RX_DRVINFO_SZ is a global register — while the PHY writes a report only into the frames whose descriptor PHYST bit is set. On all-but-one subframe of an A-MPDU the area therefore holds stale bytes, and the page nibble in byte0 can alias a valid page number (0 = CCK, 1 = OFDM type1), so the parse silently folded garbage RSSI/SNR/EVM into the RF EMAs behind GetRxQuality() / GetActiveRxPaths() / the rx.path event. Decode DW0 bit 26 into Rx8822cFrame.physt and require it before parsing. This brings jaguar3 in line with jaguar1 (FrameParser.cpp:88) and rtl8733b (FrameParser8733b.h:77), which already decode the same bit into rx_pkt_attrib::physt — jaguar3 was the generation leaving it unset. parse_phy_sts_jgr3 now returns whether it recognised the page layout (page 0 CCK or page 1 OFDM type1) and actually filled the attrib's signal fields, rather than returning void after silently ignoring an unknown page number. The internal EMA folds (_rxq, _rxpaths, _cfo) gate on that result, so an unparsed page no longer contributes a zero-valued sample. tests/rx_physt_selftest.cpp is a headless guard on the bit position and the plumbing (ctest: rx_physt_bit, built with DEVOURER_JAGUAR3) so a descriptor-layout regression fails the suite instead of quietly poisoning the RF averages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019FN9qETmHFJr6ekfkViZv8
f83c2b0 to
51d743b
Compare
PR Summary by QodoGate Jaguar3 PHY-status parsing on RX descriptor PHYST
AI Description
Diagram
High-Level Assessment
Files changed (4)
|
Code Review by Qodo
1. PHYST semantics diverge by chipset
|
| if (!is_c2h && f.physt && f.drvinfo_size >= 28) | ||
| p.RxAtrib.physt = jaguar3::parse_phy_sts_jgr3( | ||
| data + off + jaguar3::RXDESC_SIZE_8822C, f.drvinfo_size, | ||
| p.RxAtrib); |
There was a problem hiding this comment.
1. Physt semantics diverge by chipset 🐞 Bug ≡ Correctness
Jaguar3 now reports RxAtrib.physt=false when the descriptor bit is set but the report is short or uses an unsupported page, while Jaguar1 and RTL8733B expose the raw descriptor bit. Consumers of the shared packet callback can therefore no longer use physt consistently to identify which A-MPDU subframe carried PHY status.
Agent Prompt
## Issue description
Jaguar3 assigns PHY parse success to the shared `RxAtrib.physt` field, whereas other generations assign the raw RX-descriptor PHYST bit. Preserve the descriptor-bit meaning and use a separate local parse-success flag to gate RF EMAs.
## Issue Context
`RxAtrib.physt` is exposed through the generation-neutral `Packet` callback. `parse_phy_sts_jgr3` may return false even when the descriptor PHYST bit is set, including for unsupported pages, so these two facts must remain separate.
## Fix Focus Areas
- src/jaguar3/RtlJaguar3Device.cpp[297-340]
- src/jaguar3/FrameParserJaguar3.h[278-317]
- src/jaguar1/FrameParser.cpp[72-89]
- src/rtl8733b/Rtl8733bDevice.cpp[353-380]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Problem
The Jaguar3 RX loop parsed the drvinfo area as a PHY-status report whenever it was long enough:
But
RX_DRVINFO_SZis a global register — the drvinfo space is reserved on every frame, while the PHY writes a report only into frames whose RX-descriptor PHYST bit (DW0 bit 26) is set. On all-but-one subframe of an A-MPDU the area therefore holds stale bytes from a previous frame, and the page nibble inphysts[0] & 0x0fcan alias a valid page number (0= CCK,1= OFDM type1).The parse then succeeded on garbage and folded it into the RF EMAs behind
GetRxQuality(),GetActiveRxPaths()and therx.pathevent — contaminated RSSI/SNR/EVM tails that track A-MPDU density rather than the link.Change
Rx8822cFrame.phystand require it before parsing the report.parse_phy_sts_jgr3now returnsbool— whether it recognised the page layout (page 0 CCK, or page 1 OFDM type1) and actually filled the attrib's signal fields. Previously it returnedvoidafter silently ignoring an unknown page number, leaving the caller unable to tell a filled attrib from an untouched one._rxq,_rxpaths,_cfo), so an unparsed page no longer contributes a zero-valued sample.tests/rx_physt_selftest.cpp— headless guard on the bit position and the plumbing (ctest: rx_physt_bit, built underDEVOURER_JAGUAR3), so a descriptor-layout regression fails the suite instead of quietly poisoning the averages.Consistency with the other generations
rx_pkt_attrib::phystalready exists and is already populated on two generations that decode the same bit — Jaguar3 was the one leaving it unset:src/jaguar1/FrameParser.cpp:88GET_RX_STATUS_DESC_PHY_STATUS_8812src/rtl8733b/FrameParser8733b.h:77rx_bits(buf + 0x00, 26, 1)GET_RX_DESC_PHYST_8822COpen question for reviewers
This PR sets
RxAtrib.phystto the parse result (report written and page understood and fields filled), whereas Jaguar1 and RTL8733B set it to the raw descriptor bit. The parse-result semantics is what the internal EMA gate wants, but it means a caller readingRxAtrib.phystgets a slightly different meaning per generation.The alternative is to assign
p.RxAtrib.physt = f.phystunconditionally (matching the other two) and keep the parse-succeeded flag in a local for the gate. Happy to switch to that if you'd rather the field keep one cross-generation meaning — the on-air behaviour is identical either way.Testing
ctest: 54 passed, 1 skipped (la_csi_math, needs numpy), 0 failures — including the newrx_physt_bit.Not included
Split out of the working branch deliberately — the same branch also carried an unrelated
usb.rx_zerocopydefault flip (intermittent zero-frame delivery on an xhci host). That is a separate concern and will come as its own PR if wanted.🤖 Generated with Claude Code
https://claude.ai/code/session_019FN9qETmHFJr6ekfkViZv8