Follow-up to the RTL8733B bring-up (upstream PR OpenIPC/devourer#388). Investigate here on the fork, PR upstream once measured.
docs/rtl8733b-upstream-plan.md deliberately deferred this — Stage 8's audit says "Narrowband, SGI, LDPC, STBC, VHT/HE, ACK responder, beaconing, runtime TX-power knobs, energy sensing, and advanced scheduled-MAC features are not claimed", and the scope note lists "generalized TX-power controls … remain later gates". So this is planned work, not an oversight. What is new is a consumer-side cost that argues for doing it, and for one small change even if the actuator itself waits.
What a consumer hits
Rtl8733bDevice overrides none of the IRtlDevice runtime-power family — not SetTxPowerOffsetQdb, GetTxPowerCaps, SetTxPowerIndexOverride, GetTxPowerState or ReApplyTxPower. Only jaguar1/2/3 and kestrel do. Every call therefore lands on src/IRtlDevice.h:134, body (void)qdb; return 0;.
Measured 2026-08-14 on a CV610 board (0bda:f72b) transmitting 5805/20, received by an RTL8812AU:
| commanded offset |
ground RSSI (mean of 5) |
ground EVM |
| −24 qdb |
−28.0 |
−22 |
| −48 qdb |
−28.4 |
−22 |
| −96 qdb |
−26.8 |
−22 |
| −24 qdb |
−27.6 |
−22 |
18 dB commanded, nothing aired. A matched positive control on the same receiver at the same bench geometry minutes earlier — an 8822EU over the identical rungs — moved 5.6 dB of air for a 6 dB command and then railed with saturated_low. So the receiver reads a working actuator fine at that range; the 8733BU simply has none.
That part is expected. The problem is that return 0 is indistinguishable from a successful zero-offset apply, and the consumer's own reported state came out as:
{"override_active":true,"qdb":-24,"applied_qdb":0,
"saturated_low":false,"saturated_high":false}
Both the value and the saturation flags read like a healthy actuator with travel remaining.
Two pieces, and the cheap one is worth doing first
1. GetTxPowerCaps() — one override, no RF work. The base class already returns supported=false, so a caller can discriminate today; but a caller reading it gets an all-zero struct that says nothing about which knob is missing. An explicit 8733B override returning {supported=false} alongside the honest per-knob bits makes the refusal deliberate and greppable, matches how the other four backends state their answer, and costs nothing. This alone would have let the consumer refuse rather than report success.
2. The actuator itself. The silicon and this backend already have what the API needs:
Phy8733b::set_flat_tx_power(index) writes the TXAGC refs at BB 0x4308 and the per-rate diffs at 0x3a00..0x3a10, and read_txagc_state() reads them back — a verified write path already exists.
- On a TSSI-offset PG unit,
configure_tx_power drives enable_tssi_tracking(..., kSafeTssiTargetQdbm8733b) with a fixed 64 qdBm = 16 dBm target. That is an absolute qdBm knob the closed loop already honours.
So SetTxPowerOffsetQdb(qdb) maps naturally onto re-running the TSSI target at kSafeTssiTargetQdbm8733b + qdb, clamped, with the flat-index path as the fallback for a non-TSSI unit. TxPowerCaps already reserves index_max = 0 for exactly this shape — the comment reads "0 = dBm model" — so no new capability vocabulary is needed.
Two things to measure rather than assume, both flagged by docs/rtl8733b.md:
- Settling. The doc records the loop needing time: alternating CCK/OFDM at ~9 ms per frame left CCK ~5 dB above its settled level, and 86 ms pacing landed on it. Any offset sweep has to pace or it will misread its own steps.
- The floor is real.
kSafeTxAgcIndex8733b was measured too cold to carry HT at all (MCS7, 300/300 submitted, 0 captured, twice). Negative offsets have somewhere unusable to go, so saturated_low needs to mean something concrete here rather than being wired to false.
Suggested order
- Override
GetTxPowerCaps() with the honest false set — mergeable on its own, no hardware.
- Implement
SetTxPowerOffsetQdb on the TSSI-target path; report saturated_low/high from the clamp.
- Wire
GetTxPowerState() from read_txagc_state() plus the TSSI target, so the state snapshot is chip truth rather than a shadow.
- Measure a paced offset sweep against a witness receiver; only then flip
supported=true.
- PR upstream, citing the bench evidence.
Consumer-side evidence and the full measurement write-up: snokvist/waybeam-link #188, issue #185.
Follow-up to the RTL8733B bring-up (upstream PR OpenIPC/devourer#388). Investigate here on the fork, PR upstream once measured.
docs/rtl8733b-upstream-plan.mddeliberately deferred this — Stage 8's audit says "Narrowband, SGI, LDPC, STBC, VHT/HE, ACK responder, beaconing, runtime TX-power knobs, energy sensing, and advanced scheduled-MAC features are not claimed", and the scope note lists "generalized TX-power controls … remain later gates". So this is planned work, not an oversight. What is new is a consumer-side cost that argues for doing it, and for one small change even if the actuator itself waits.What a consumer hits
Rtl8733bDeviceoverrides none of theIRtlDeviceruntime-power family — notSetTxPowerOffsetQdb,GetTxPowerCaps,SetTxPowerIndexOverride,GetTxPowerStateorReApplyTxPower. Onlyjaguar1/2/3andkestreldo. Every call therefore lands onsrc/IRtlDevice.h:134, body(void)qdb; return 0;.Measured 2026-08-14 on a CV610 board (
0bda:f72b) transmitting 5805/20, received by an RTL8812AU:18 dB commanded, nothing aired. A matched positive control on the same receiver at the same bench geometry minutes earlier — an 8822EU over the identical rungs — moved 5.6 dB of air for a 6 dB command and then railed with
saturated_low. So the receiver reads a working actuator fine at that range; the 8733BU simply has none.That part is expected. The problem is that
return 0is indistinguishable from a successful zero-offset apply, and the consumer's own reported state came out as:Both the value and the saturation flags read like a healthy actuator with travel remaining.
Two pieces, and the cheap one is worth doing first
1.
GetTxPowerCaps()— one override, no RF work. The base class already returnssupported=false, so a caller can discriminate today; but a caller reading it gets an all-zero struct that says nothing about which knob is missing. An explicit 8733B override returning{supported=false}alongside the honest per-knob bits makes the refusal deliberate and greppable, matches how the other four backends state their answer, and costs nothing. This alone would have let the consumer refuse rather than report success.2. The actuator itself. The silicon and this backend already have what the API needs:
Phy8733b::set_flat_tx_power(index)writes the TXAGC refs at BB0x4308and the per-rate diffs at0x3a00..0x3a10, andread_txagc_state()reads them back — a verified write path already exists.configure_tx_powerdrivesenable_tssi_tracking(..., kSafeTssiTargetQdbm8733b)with a fixed 64 qdBm = 16 dBm target. That is an absolute qdBm knob the closed loop already honours.So
SetTxPowerOffsetQdb(qdb)maps naturally onto re-running the TSSI target atkSafeTssiTargetQdbm8733b + qdb, clamped, with the flat-index path as the fallback for a non-TSSI unit.TxPowerCapsalready reservesindex_max = 0for exactly this shape — the comment reads "0 = dBm model" — so no new capability vocabulary is needed.Two things to measure rather than assume, both flagged by
docs/rtl8733b.md:kSafeTxAgcIndex8733bwas measured too cold to carry HT at all (MCS7, 300/300 submitted, 0 captured, twice). Negative offsets have somewhere unusable to go, sosaturated_lowneeds to mean something concrete here rather than being wired tofalse.Suggested order
GetTxPowerCaps()with the honest false set — mergeable on its own, no hardware.SetTxPowerOffsetQdbon the TSSI-target path; reportsaturated_low/highfrom the clamp.GetTxPowerState()fromread_txagc_state()plus the TSSI target, so the state snapshot is chip truth rather than a shadow.supported=true.Consumer-side evidence and the full measurement write-up:
snokvist/waybeam-link#188, issue #185.