Skip to content

Latest commit

 

History

History
262 lines (221 loc) · 14.2 KB

File metadata and controls

262 lines (221 loc) · 14.2 KB

RTL8731BU / RTL8733BU support

Devourer's RTL8733B backend supports the Wi-Fi function of the 1T1R, dual-band RTL8731BU/RTL8733BU family. It is based on the vendor's HALMAC 87xx path rather than any Jaguar or Kestrel implementation.

The supported USB identities are:

  • 0bda:f72b, the Wi-Fi-only identity used by the tested RTL8731BU module.
  • 0bda:b733, the combo-module Wi-Fi function listed by the vendor driver.

An opened device must also report RTL8733B chip ID 0x16; a matching PID is not accepted as sufficient identity.

Advertised capability

The backend advertises one spatial stream, 2.4 and 5 GHz, legacy OFDM and HT MCS0–7 at 20 or 40 MHz. It also accepts all four long-preamble CCK rates on 2.4 GHz at 20 MHz; CCK is rejected on 5 GHz and at 40 MHz. Raw HT TX is deliberately forced to BCC. SGI, STBC, LDPC, VHT, 80 MHz, CCK short preamble, Bluetooth/coexistence controls and narrowband are not advertised.

That conservative surface follows both the hardware results and the vendor HAL. The pinned 2023 HAL declares 802.11b/g/n, one TX and RX spatial stream, and only BW_CAP_20M | BW_CAP_40M. It sets TX-LDPC true but RX-LDPC false; devourer still reports both false because independently witnessed raw HT TX only became reliable after clearing the global HT/VHT LDPC enable bits. The newer 2024 vendor tree has the same 1SS, HT and 20/40 MHz declarations, and its Fix VHT flags commit removes the accidentally enabled VHT build flag.

Hardware validation

Every claim below rests on one physical unit: a bare unbranded 1T1R RTL8731BU module, 0bda:f72b, RTL8733B chip ID 0x16, cut D, on USB high speed. Independent witness: an RTL8812AU in monitor mode. Nothing here was measured on an RTL8733BU — that chip is recognized from the vendor ID table and rides the same code path, but no 0bda:b733 combo module was available.

The following was exercised through the normal WiFiDriver / IRtlDevice factory path:

  • USB discovery and endpoint selection; card enable and disable; firmware download, checksum and ready handshakes; HALMAC/MAC, EFUSE, BB and RF setup.
  • Stable physical EFUSE reads, permanent MAC extraction, firmware-health and thermal telemetry.
  • Ambient monitor RX on channels 6 and 36, including descriptor aggregation, PHY status, CRC reporting and clean shutdown.
  • Independently captured raw TX on 2.4 and 5 GHz: legacy OFDM and HT MCS0–7, 20 MHz and HT 40 MHz. BCC was used throughout. On 2.4 GHz, an RTL8812AU witness also decoded long-preamble 1, 2, 5.5 and 11 Mbps CCK with valid FCS.
  • Repeated warm initialization, concurrent RX/TX, bounded sustained TX, teardown during active RX/TX, malformed-input rejection and post-test adapter-doctor checks.

Per-rate delivery, witnessed by an RTL8812AU (0bda:8812) in monitor mode on channel 6, 300 submitted frames per rate. Counted per frame from the witness's own RX events, so these are exact rather than sampled:

requested delivered decoded as FCS errors
1 Mbps CCK 298 / 300 1 Mbps 0
2 Mbps CCK 300 / 300 2 Mbps 0
5.5 Mbps CCK 300 / 300 5.5 Mbps 0
11 Mbps CCK 294 / 300 11 Mbps 0
6 Mbps OFDM 300 / 300 6 Mbps 0
HT MCS0 270 / 300 MCS0 0
HT MCS7 277 / 300 MCS7 0

Every frame decoded at the rate it was requested at, and no captured frame carried an FCS error at any rate. These are single probes, so per this repo's own guidance they are worth about +/-3 points each.

The two HT readings sitting below the legacy ones are not a transmitter effect, and the witness was qualified rather than assumed (tests/ground_station_qualify.sh exists for exactly this question). Sweeping the RTL8812AU witness across the HT ladder, 300 frames each:

rate delivered witness RSSI
MCS0 270 / 300 57.8
MCS1 300 / 300 65.0
MCS3 300 / 300 65.3
MCS5 300 / 300 65.2
MCS6 296 / 300 65.1
MCS7 277 / 300 60.3

The ladder is flat at 100% through MCS5, so the witness is nowhere near its modulation cliff and is fit to measure this rate range — a receiver measuring itself would fall off progressively at the top, not dip at both ends. MCS0 is the most robust rate in the set, so its being the lowest reading rules out a cliff at either end. Both low readings also came with 5-7 units less RSSI than the flat middle, which places the variation in the per-run link conditions rather than in modulation. Treat the whole HT column as one flat band around 90-100% until something moves it by more than the probe noise.

A mixed run alternating all eight rates (1/2/5.5/11 Mbps CCK, 6 Mbps OFDM, MCS0/4/7) submitted 250 frames, crossing the CCK/OFDM rate-class boundary on every frame. The witness decoded all eight rate_hw values on air with valid FCS, every frame at 20 MHz with STBC and LDPC clear, matching the forced-BCC contract.

RX aggregation is capped at 12 KiB (rtl8733b::kRxAggregateBytes8733b) because the vendor default of 20 KiB exceeds one bulk-IN URB: a 60-second high-traffic receive run had xHCI split such an aggregate, leaving a descriptor tail and its body in separate completions. The RX loop floors its URB size at the same constant, so DEVOURER_RX_URB_BYTES cannot reintroduce the straddle from the other side. Five repeated high-traffic channel-36 runs with the cap in place completed without malformed descriptors, aggregate mismatches or parser aborts. A separate ten-cycle channel-6/channel-36 warm lifecycle run passed EFUSE, firmware-ready, initialization, thermal, rollback and card-disable gates. tests/rtl8733b_lifecycle_soak.sh makes that warm test reproducible and labels it explicitly as distinct from a true VBUS cycle.

Thermal readings are telemetry, exactly as on every other generation: GetThermalStatus() exposes the RF 0x42 meter against the EFUSE baseline, InitWrite logs one bring-up snapshot, and DEVOURER_THERMAL_POLL_MS emits thermal events at the caller's cadence. Nothing in the send path reads the meter — a per-frame read would be several USB control transfers per frame, and the meter is a PA-bias tracking index rather than a calibrated junction temperature, so it is not sound input for a TX gate (docs/warm-tx-degradation.md). During the mixed CCK/OFDM witness run the raw code stayed at 32–33 against baseline 32 across 650 submitted frames; 636 were captured with valid FCS across all five requested rates. A separate 1,000-frame run alternating CCK and OFDM submitted 1,000/1,000; the witness captured 660 canonical frames across both rates, all with valid FCS, while the same 32–33 thermal range held. Both runs are rate-coverage and thermal evidence only — neither says anything about TX-power tracking, and at these deltas (+0 to +1) the two thermal-compensation curves are the same bits anyway.

TX power control

On a unit whose EFUSE is in TSSI-offset PG mode — the tested RTL8731BU is — closed-loop TSSI is the TX-power control, so it runs by default. It is not optional there: the flat fallback index (kSafeTxAgcIndex8733b) is a conservative bring-up value, and on air it runs cold enough that HT rates do not survive the link. Measured with the witness, 300 frames submitted at MCS7 with the flat index: zero captured, twice. A unit whose EFUSE carries no TSSI calibration has nothing to drive the loop and takes the flat path.

The chip keeps two thermal-compensation curves, one for CCK and one for OFDM/HT. The table is chosen once per channel set from the configured TX mode and then left alone, which is what the vendor driver does — _halrf_tssi_set_tmeter_tbl_8733b is only ever called from full TSSI setup, keyed on phydm_get_tx_rate at that instant, and never re-selected at runtime.

There is deliberately no runtime switch, and the reason is worth recording so nobody adds one back.

The two curves are bit-identical for thermal deltas 0..+17 and first differ at +18 — the swing ramp starts there, and the words either side of the baseline are zero in both tables. A five-minute max-duty MCS7 soak on the validation unit plateaued at +8 after two minutes and stopped climbing, less than half the delta needed for the tables to differ by a single entry. So on this silicon a runtime switch would spend 84 ms — 136 USB register round trips, inside send_packet, capping a mixed-rate stream near 11 fps — to install a table bit-identical to the one already loaded.

An opt-in knob for it was written, measured on air and then deleted on that evidence. Room temperature agreed too: 300 frames of 1 Mbps CCK delivered 300/300 with the OFDM table loaded and 299/300 with the CCK table, zero FCS errors either way. OpenIPC/devourer#389 has the cost breakdown and a validated in-place alternative (13.8 ms, tracking left enabled) — if a future board does reach +18, that is the implementation to revisit, not the teardown/rebuild.

A related property of the closed loop, worth knowing before reading power numbers from a fast rate-switching run: TSSI needs settling time. Alternating CCK and OFDM at roughly 9 ms per frame leaves CCK airing about 5 dB above its settled level, and the same stream paced to 86 ms per frame lands on the settled value. That is the tracking loop converging, not a descriptor or table-programming effect.

SetCcaMode(false) — the universal carrier-sense-enabled default, and the state this backend's MAC bring-up leaves programmed — succeeds as a no-op. SetCcaMode(true) (DEVOURER_DIS_CCA) throws: the HALMAC 87xx carrier-sense gate has not been located and measured here, and the backend refuses rather than silently pretending. The refusal leaves the session running.

The construction-time form of the same knob — DeviceConfig::tuning::disable_cca, which every other generation applies during bring-up — cannot be honoured either, and bring-up emits one warning saying so rather than dropping it silently or failing to initialise. Silence is the only outcome that would let a refused knob look like a granted one; a hard failure would be a harsh answer to a request a caller may be making only through an inherited environment.

Known gaps and deferred validation

These results have not been claimed:

  • No SDR was available, so occupied bandwidth, spectral mask, EVM and absolute output power were not measured.
  • The experimental 5/10 MHz sequence has register-readback and normal-path RX evidence only. Narrowband TX and decode by an independent narrowband peer are deferred; AdapterCaps::narrowband_ok remains false.
  • No physical 0bda:b733 combo module was available. Its USB identity is taken from the vendor driver, but Bluetooth and coexistence are out of scope.
  • Secure-firmware variants were not exercised. The tested cut-D device used the normal NIC image.
  • The test hub cannot switch VBUS, so repeated true power-off cold boots were not automated. Physical unplug/replug and repeated warm bring-up were tested.
  • SGI remains disabled: a descriptor with its short-GI bit set submitted successfully, but an independent RTL8812AU decoded both broadcast and unicast probes as long GI. STBC and LDPC remain rejected. ACK/BlockAck response, A-MPDU, fast retune and throughput were not independently validated and remain unadvertised or unsupported by this backend.
  • A vendor-kernel-driver cross-test was attempted with the 2024 source on Linux 6.14, but its module did not build: its Makefile omitted required include paths, and after supplying the main include path compilation stopped on an incompatible MODULE_IMPORT_NS use and a missing platform_ops.h. No vendor module was loaded, so no vendor-driver parity result is claimed.

A second RTL8731BU f72b unit — the same chip the surviving DUT is, so it would have covered the same README row — passed early identity, EFUSE and firmware bring-up checks, then stopped enumerating and overheated while attached to a custom power supply. It is excluded from all radio-validation claims, and the RTL8731BU row therefore rests on a single surviving sample: a one-unit result, not a population.

Narrowband status

The experimental 5/10 MHz path ports the later workaround in rtl8733bu-20230626: program the RF as 20 MHz, then apply the small-bandwidth BB clock fields after the RF writes. Both 5 and 10 MHz register transitions read back on the tested device. The vendor's published capability mask remains 20/40 MHz, however, and the workaround itself describes a no-RF-output failure without its final writes. Consequently the code is retained for further work but the public capability mask stays at 20/40 MHz until SDR occupied-bandwidth and independent narrowband TX/RX tests are available.

Vendor-source provenance

Firmware and parameter tables are reproducibly extracted from libc0607/rtl8733bu-20230626 commit 9e5f6845b4e3393207720fc1886668bc80b6af1c, pinned as the reference/rtl8733bu-20230626 submodule (reference/README.md). That tree is also the source of the halmac_reg2.h and power-sequence constants transcribed into src/rtl8733b/, so both the generated artifacts and the hand-carried constants are re-verifiable from a fresh checkout. The extraction tools pin the source-file hashes, output sizes and content hashes:

git submodule update --init reference/rtl8733bu-20230626
python3 tools/extract_8733b_fw.py --check
python3 tools/extract_8733b_tables.py --check

Both accept --input / --source-root to point at a tree elsewhere.

The implementation was also compared with libc0607/rtl8733bu-20240806 commit 2ec19e154cffbc2abd98d43d59278dffa6e50d49; it was not used to silently replace the firmware or tables already validated on the device. The review covered the later active-RF-path TSSI selection (already matched here), SRRC band-edge setup, WLAN/Bluetooth GPIO pull-up changes, power-saving and C2H/TX tasklet fixes. None justified replacing the pinned firmware/tables or widening the raw-monitor capability claims above.

The backend covers the USB-first HALMAC 87xx scope of OpenIPC/devourer#233. It does not adopt that issue's provisional VHT/80 MHz assumptions, because both vendor trees describe this silicon as HT-only and 20/40 MHz.