mirror of
https://github.com/ruvnet/RuView.git
synced 2026-10-18 21:00:22 -05:00
feat(hardware): carry legacy and unknown PPDU types in MediaTek MTC1
A real MT7981 (WL-WN586X3, MT7981B + MT7976C) CSI report arrived with rx_mode = MT_PHY_TYPE_OFDM for a 2.4 GHz client. The MTC1 PpduType had no pre-HT value, so a bridge had to drop the frame. MediaTek's CSI driver treats legacy OFDM as its own tone-mask group, and association traffic is legacy-rate, so these frames are routine rather than a corner case. Add PpduType::Legacy = 6 and PpduType::Unknown = 7. Values 1-5 and the 72-byte header are unchanged. A decoder built before this change rejects 6 and 7 as UnknownPpduType (fails safe, drops the frame), so MTC1 consumers need a rebuild. ADR-267 gains Amendment 1 recording the change, its compatibility, and the observed 1x1x64 dimensions of the motivating capture. The capture is not in the tree: it holds a client transmitter address and raw CSI. Developed in whitsentry, a RuView-based project. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
1 parent
03a897938f
commit
ebb303cbb8
2 files changed
+119
No files matched your search
@@ -34,6 +34,81 @@ Define `MTC1` version 1 as a little-endian, self-delimiting envelope:
|
||||
- Flags distinguish calibrated, saturated, time-synchronized, dropped-predecessor,
|
||||
and synthetic frames. Synthetic provenance cannot be cleared by downstream code.
|
||||
|
||||
## Amendment 1 — PPDU types `Legacy` and `Unknown`
|
||||
|
||||
- **Date**: 2026-09-19
|
||||
- **Status**: proposed. Demonstrated on one WL-WN586X3 capture. Not a maintainer acceptance.
|
||||
- **Scope**: `PpduType` discriminants only. The 72-byte header layout, CRC,
|
||||
payload ordering, flags, and caps are unchanged.
|
||||
|
||||
### What changed
|
||||
|
||||
`PpduType` gains two variants:
|
||||
|
||||
| Value | Name | Meaning |
|
||||
|---|---|---|
|
||||
| 6 | `Legacy` | Pre-HT: 802.11b CCK or 802.11a/g OFDM |
|
||||
| 7 | `Unknown` | A PPDU format the producer could not classify |
|
||||
|
||||
Values 1-5 (`Ht`, `Vht`, `HeSu`, `HeMu`, `Eht`) are untouched.
|
||||
|
||||
### Why
|
||||
|
||||
The original set assumed every CSI report would be HT or later. The first real
|
||||
MT7981 capture disproved that. A Wavlink WL-WN586X3 (MT7981B + MT7976C) running
|
||||
an OpenWrt 24.10.8 image built with the patches in `firmware/openwrt-wn586x3/`
|
||||
reported `rx_mode = 1` (`MT_PHY_TYPE_OFDM`) for a client associated on 2.4 GHz
|
||||
channel 6 at 20 MHz. The bridge had no way to express it and dropped the frame.
|
||||
|
||||
That was a real data loss, not a corner case. MediaTek's CSI driver treats legacy
|
||||
OFDM as a first-class mode: `mt7915_vendor_csi_tone_mask` skips only
|
||||
`MT_PHY_TYPE_CCK`, and its `mode_map` is keyed by `MT_PHY_TYPE_OFDM`, `_HT`,
|
||||
`_VHT` and `_HE_SU`, giving OFDM its own tone-mask group
|
||||
(`1001-wifi-mt76-mt7915-csi-implement-csi-support.patch:1070-1088`). Association
|
||||
and management exchanges are legacy-rate by design, so a sensing deployment will
|
||||
see these continuously.
|
||||
|
||||
`Unknown` exists so the enum's gaps (5-7 and 12 in `mt76_phy_type`) and future
|
||||
modes are carried explicitly rather than guessed at or dropped. It also covers
|
||||
transports that omit the field: the MtkCSIdump UDP datagram carries no `rx_mode`,
|
||||
and reporting `Unknown` there is honest where defaulting to a concrete format was
|
||||
not.
|
||||
|
||||
### Compatibility
|
||||
|
||||
- **Forward, not backward.** A decoder built before this amendment rejects
|
||||
discriminants 6 and 7 with `UnknownPpduType`, because the original parser
|
||||
rejects unknown types by design. `TryFrom<u8>` returns `Err` rather than
|
||||
panicking, so an old decoder fails safely — it just drops legacy frames, and
|
||||
does so silently. **Rebuild MTC1 consumers after this codec change.**
|
||||
- **Scope is narrower than "the workspace".** `wifi-densepose-hardware` defines
|
||||
three independent `PpduType` enums — `csi_frame.rs` (ESP32),
|
||||
`qualcomm_csi.rs`, and `mediatek_csi.rs` — and this amendment touches only the
|
||||
MediaTek one. Neither of the others changes, so no other vendor bridge is
|
||||
affected. The complete set of `mediatek_csi::PpduType` consumers is:
|
||||
`wifi-densepose-hardware`'s own `mediatek-csi-sim` binary,
|
||||
`wifi-densepose-mtk-bridge`, and `wifi-densepose-sensing-server`, which renders the
|
||||
value with `{:?}` and so needed no code change. In particular the
|
||||
`ppdu_type > 3` bound in `bootstrap_baseline.rs` is fed from the **ESP32**
|
||||
enum via `CsiGridKey::from_frame(&Esp32Frame)`, not from MediaTek frames, so
|
||||
`Legacy` and `Unknown` cannot reach it.
|
||||
- Because the variants were added rather than renumbered, and Rust requires
|
||||
exhaustive matches, the compiler locates any consumer that must be updated.
|
||||
Nothing outside the files above needed one.
|
||||
- Producers must emit 6 only for frames that genuinely are pre-HT, and 7 only
|
||||
where the format is truly unclassified. Neither is a dumping ground for a
|
||||
mapping the producer has not done.
|
||||
- The mapping from `rx_mode` to `PpduType` is now infallible, so no MTC1 producer
|
||||
may drop a frame on PPDU grounds.
|
||||
- Unchanged: synthetic provenance still cannot be cleared by downstream code.
|
||||
|
||||
### Observed dimensions
|
||||
|
||||
The same capture assembled to 1x1x64. That is the client's behaviour, not a
|
||||
protocol limit: the handset sent single-stream frames to a 2.4 GHz 20 MHz AP.
|
||||
Expect 1x2 or 2x2 on 5 GHz, and size consumers for the chipset profile's
|
||||
`max_chains` rather than for what one capture happened to show.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
@@ -59,3 +134,8 @@ Define `MTC1` version 1 as a little-endian, self-delimiting envelope:
|
||||
- [ADR-266: MediaTek Filogic CSI platform](ADR-266-mediatek-filogic-csi-platform.md)
|
||||
- [ADR-018: ESP32 binary CSI framing](ADR-018-esp32-csi-frame-protocol.md)
|
||||
- [ADR-264: RTL8720F radar wire protocol](ADR-264-rtl8720f-radar-wire-protocol.md)
|
||||
- Amendment 1 implementation: `v2/crates/wifi-densepose-hardware/src/mediatek_csi.rs`
|
||||
(codec) and `v2/crates/wifi-densepose-mtk-bridge` (`ppdu_from_rx_mode`). The
|
||||
motivating capture is a lab record and is not in this tree: it contains a
|
||||
client transmitter address and raw CSI. The in-repo check is a fabricated
|
||||
legacy-OFDM record in `wifi-densepose-mtk-bridge/tests/hardware_capture.rs`.
|
||||
@@ -85,6 +85,17 @@ impl TryFrom<u8> for ElementFormat {
|
||||
}
|
||||
}
|
||||
|
||||
/// PPDU format a CSI report was measured on.
|
||||
///
|
||||
/// `Legacy` and `Unknown` were added after a real MT7981 capture (a 2.4 GHz
|
||||
/// client on channel 6) reported `rx_mode = MT_PHY_TYPE_OFDM`. MediaTek's CSI
|
||||
/// driver treats legacy OFDM as a first-class mode — its tone-mask table gives
|
||||
/// `MT_PHY_TYPE_OFDM` its own group — so a vendor bridge must be able to carry
|
||||
/// one. Without these, such a frame had to be dropped, which lost real data.
|
||||
///
|
||||
/// Wire note: a decoder older than this change rejects discriminants 6 and 7 as
|
||||
/// `UnknownPpduType`. Producers should only emit them for frames that genuinely
|
||||
/// are pre-HT or unrecognised.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[repr(u8)]
|
||||
pub enum PpduType {
|
||||
@@ -93,6 +104,11 @@ pub enum PpduType {
|
||||
HeSu = 3,
|
||||
HeMu = 4,
|
||||
Eht = 5,
|
||||
/// Pre-HT: 802.11b CCK or 802.11a/g OFDM.
|
||||
Legacy = 6,
|
||||
/// A PPDU format the producer could not classify. Carried rather than
|
||||
/// guessed, so the frame survives and the ambiguity stays visible.
|
||||
Unknown = 7,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for PpduType {
|
||||
@@ -104,6 +120,8 @@ impl TryFrom<u8> for PpduType {
|
||||
3 => Ok(Self::HeSu),
|
||||
4 => Ok(Self::HeMu),
|
||||
5 => Ok(Self::Eht),
|
||||
6 => Ok(Self::Legacy),
|
||||
7 => Ok(Self::Unknown),
|
||||
_ => Err(CsiParseError::UnknownPpduType(value)),
|
||||
}
|
||||
}
|
||||
@@ -628,6 +646,27 @@ mod tests {
|
||||
let w = f.to_bytes().unwrap();
|
||||
assert_eq!(CsiFrame::from_bytes(&w).unwrap().0, f);
|
||||
}
|
||||
#[test]
|
||||
fn legacy_and_unknown_ppdu_types_round_trip() {
|
||||
// A real MT7981 capture reported MT_PHY_TYPE_OFDM, which has no HT/VHT/HE
|
||||
// equivalent; these two discriminants exist so such a frame is carried
|
||||
// rather than dropped.
|
||||
let mut s = MediatekCsiSimulator::new(SimulatorConfig::default()).unwrap();
|
||||
for want in [PpduType::Legacy, PpduType::Unknown] {
|
||||
let mut f = s.next_frame();
|
||||
f.ppdu_type = want;
|
||||
let w = f.to_bytes().unwrap();
|
||||
let (decoded, _) = CsiFrame::from_bytes(&w).unwrap();
|
||||
assert_eq!(decoded.ppdu_type, want);
|
||||
}
|
||||
assert_eq!(PpduType::try_from(6u8).unwrap(), PpduType::Legacy);
|
||||
assert_eq!(PpduType::try_from(7u8).unwrap(), PpduType::Unknown);
|
||||
assert!(matches!(
|
||||
PpduType::try_from(8u8),
|
||||
Err(CsiParseError::UnknownPpduType(8))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crc_corruption_is_rejected() {
|
||||
let mut s = MediatekCsiSimulator::new(SimulatorConfig::default()).unwrap();
|
||||
|
||||
Reference in new issue
Block a user