Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,15 @@ target_link_libraries(ToneMaskSelftest PRIVATE devourer)

add_test(NAME tone_mask_math COMMAND ToneMaskSelftest)

# Headless guard for the active idle-noise-floor math (src/NoiseFloorMath.h): the
# phydm sign/pwdb helpers the Jaguar1 0x0FA0 debug-port measurement uses. Pure
# math, header-only (issue #202).
add_executable(NoiseFloorMathSelftest tests/noise_floor_math_selftest.cpp)
target_include_directories(NoiseFloorMathSelftest PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src)

add_test(NAME noise_floor_math COMMAND NoiseFloorMathSelftest)

# Headless round-trip guard for the 802.11ax HE TX radiotap path
# (src/RadiotapBuilder.cpp build_he + the shared Radiotap.c field-23 size entry
# + src/ieee80211_radiotap.h HE masks): parse an HE spec -> build -> iterate ->
Expand Down
4 changes: 2 additions & 2 deletions docs/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ Emitters: L = library, RX/TX/... = demo. Optional fields in [brackets];
| `rx.path` | RX (`DEVOURER_RX_ALLPATHS`) | seq, rssi[4], snr[4], evm[4] |
| `rx.path_mask` | L (toggle spec) | t, mask "0xNN" |
| `rx.scrambler` | RX (`DEVOURER_DUMP_SCRAMBLER`) | seed "0xNN", rate, hits, len |
| `rx.energy` | RX (`DEVOURER_RX_ENERGY_MS` / sweep) | t, [ch], cca_ofdm\|null, cca_cck\|null, fa_ofdm\|null, fa_cck\|null, igi\|null, [retune_us], frames, frames_ldpc, frames_stbc, rssi_mean, rssi_max, snr_mean, snr_min, evm_mean |
| `rx.energy` | RX (`DEVOURER_RX_ENERGY_MS` / sweep) | t, [ch], cca_ofdm\|null, cca_cck\|null, fa_ofdm\|null, fa_cck\|null, igi\|null, abs_noise_floor_dbm\|null, [retune_us], frames, frames_ldpc, frames_stbc, rssi_mean, rssi_max, snr_mean, snr_min, evm_mean |
| `rx.nhm` | RX | [ch], peak, busy, dur, hist[12] |
| `rx.quality` | RX (`DEVOURER_RXQUALITY`) | verdict, frames, rssi_mean_dbm, rssi_max_dbm, snr_mean_db, snr_min_db, evm_db\|null, noise_floor_dbm\|null, igi |
| `rx.quality` | RX (`DEVOURER_RXQUALITY`) | verdict, frames, rssi_mean_dbm, rssi_max_dbm, snr_mean_db, snr_min_db, evm_db\|null, noise_floor_dbm\|null, abs_noise_floor_dbm\|null, igi |
| `adapter.rxpaths` | RX (`DEVOURER_RXQUALITY`) | active_mask "0xNN", n_active, n_chains, frames, rssi_dbm[] — GetActiveRxPaths live per-chain activity (the caps rx_chains companion) |
| `link.health` | RX (`DEVOURER_LINKHEALTH`) | verdict, rssi_dbm, snr_db, evm_db\|null, frames, fa_ofdm\|null, igi\|null, [igi_floor], [igi_ceil], cause, fix |
| `fw.c2h` | RX, duplex (`DEVOURER_TX_STATUS`) | len, bytes hex |
Expand Down
34 changes: 34 additions & 0 deletions docs/rx-spectrum-sensing.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,40 @@ interference localisation is possible through a different mechanism entirely —
the self-sounding beamforming report (see `docs/beamforming-self-sounding.md`),
whose per-tone SNR / V-angle variance localises an interferer to ~1 MHz.

## Noise floor — passive vs active (absolute dBm)

Two noise floors are exposed on `GetRxQuality()`:

- **Passive** (`noise_floor_dbm`, always on) — the per-frame `rssi_dbm − snr_db`,
averaged over the window. It updates only when a wanted frame arrives, but that
is exactly the self-jamming signal (raising TX power on a near-field link drops
SNR while RSSI holds, so this rises). Works on every generation.
- **Active / frame-free absolute** (`abs_noise_floor_dbm`, opt-in
`DEVOURER_RX_NOISE_FLOOR`) — the vendor idle-noise monitor, a true idle-channel
floor measured with **no wanted signal** (site survey, channel selection). It
adds ~10 ms of USB round-trips, so it is off by default. The vendor active
measurement can **wedge a live RX**, so devourer only ever runs it RX-idle:
- **Jaguar1 (8812A/8821A)** — the debug-port sampling path (fix IGI, stop
CK320/CK88, read the RX I/Q at `0x0FA0`, `pwdb = 10·log10(I²+Q²)`, average
5 idle samples) stops clocks and resets BB/PMAC/CCK, which wedges concurrent
bulk-IN DMA. devourer runs it **once at bring-up, before `StartRxLoop`** —
RX-idle by construction, wedge-free — and caches the value; on-air 0/6 wedged
runs vs the live-poll's 2/6. Re-measure by re-`Init`. The 8814A is excluded
(different vendor path).
- **Jaguar2 (8822B/8821C)** — the HW idle-noise report at `0x0FF0` (freeze
`0x9E4[30]`, `noise = −110 + IGI + report`) has no clock-stop, so it is read
live. It is only intermittently populated in monitor bring-up (it reads the
`0x80`/`0x00` sentinels between idle gaps), so it returns a value on some
reads and null on others — poll until valid. When valid it cross-matches the
Jaguar1 floor within a few dB on the same channel.
- **Jaguar3 (8822C/8822E)** — no vendor idle-noise path (the report dispatch
excludes the 8822C), so `abs_noise_floor_dbm` is always null; the passive
floor is J3's only floor.

Validation: `tests/rx_noise_floor_active_onair.sh` (anti-wedge + sanity /
cross-chip agreement — a B210 injected-noise sweep isn't used because the bench
SDR is too weakly coupled to move the RTL floor above the measurement variance).

## `DEVOURER_RX_ENERGY_MS` — the energy sensor

`rxdemo` with `DEVOURER_RX_ENERGY_MS=N` emits one `rx.energy`
Expand Down
1 change: 1 addition & 0 deletions examples/common/env_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ devourer::DeviceConfig devourer_config_from_env() {
if (env_long("DEVOURER_RX_URBS", &v))
cfg.rx.urbs = static_cast<int>(v);
cfg.rx.phy_status_8821c = !env_flag("DEVOURER_8821C_NO_PHYST");
cfg.rx.abs_noise_floor = env_flag("DEVOURER_RX_NOISE_FLOOR");
if (env_long("DEVOURER_IGI", &v))
cfg.rx.igi = static_cast<uint8_t>(v & 0x7f);
if (const char *e = env_str("DEVOURER_ACK_RESPONDER"))
Expand Down
11 changes: 11 additions & 0 deletions examples/rx/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,10 @@ int main() {
ev.f("igi", e.igi);
else
ev.f("igi", nullptr);
if (e.valid_noise_floor) /* active absolute floor, #202 (opt-in) */
ev.f("abs_noise_floor_dbm", e.abs_noise_floor_dbm);
else
ev.f("abs_noise_floor_dbm", nullptr);
ev.f("frames", agg.n)
.f("frames_ldpc", agg.n_ldpc)
.f("frames_stbc", agg.n_stbc)
Expand Down Expand Up @@ -1288,6 +1292,13 @@ int main() {
ev.f("noise_floor_dbm", q.noise_floor_dbm);
else
ev.f("noise_floor_dbm", nullptr);
/* Active/frame-free absolute floor (DEVOURER_RX_NOISE_FLOOR): the
* companion to the passive floor above. Null unless opted-in AND the
* generation supports it (Jaguar2 live / Jaguar1 8812A CAL). #202. */
if (q.abs_nf_valid)
ev.f("abs_noise_floor_dbm", q.abs_noise_floor_dbm);
else
ev.f("abs_noise_floor_dbm", nullptr);
ev.f("igi", q.igi_valid ? q.igi : -1);

/* Live per-chain RX-path activity (GetActiveRxPaths) — which
Expand Down
7 changes: 7 additions & 0 deletions src/DeviceConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ struct DeviceConfig {
* PHY-status to RX frames (per-frame RSSI/SNR/EVM). Disable only for the
* leanest possible RX path. */
bool phy_status_8821c = true;
/* env: DEVOURER_RX_NOISE_FLOOR — opt-in active/frame-free ABSOLUTE noise
* floor (dBm) in GetRxEnergy/GetRxQuality (RxEnergy.abs_noise_floor_dbm).
* OFF by default: the vendor idle-noise measurement adds ~10 ms of USB
* round-trips. Jaguar2 measures it live (wedge-free HW idle-noise report);
* Jaguar1 8812A/8821A measure it RX-idle (a CAL bracket); Jaguar3 and others
* leave it invalid (no vendor path). Issue #202. */
bool abs_noise_floor = false;
/* env: DEVOURER_IGI — Jaguar2 fixed initial-gain index override, 7 bits
* (unset = 0x40, the FA-rate-validated default). */
std::optional<uint8_t> igi;
Expand Down
49 changes: 49 additions & 0 deletions src/NoiseFloorMath.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* Verbatim ports of the two phydm_math_lib helpers the active idle-noise-floor
* measurement needs (reference/rtl8812au/hal/phydm/phydm_math_lib.c). Kept in a
* standalone header so the per-generation noise-floor code and a selftest share
* one copy. See docs/rx-spectrum-sensing.md and issue #202.
*
* These drive the Jaguar1 (8812A/8821A) active-sampling path: the 0x0FA0 debug
* port packs rx I/Q as 10-bit signed (sign_conversion), and the per-sample power
* is 10*log10(I^2 + Q^2) approximated as 3*log2 via the leading set bit
* (pwdb_conversion). */
#ifndef DEVOURER_NOISE_FLOOR_MATH_H
#define DEVOURER_NOISE_FLOOR_MATH_H

#include <cstdint>

namespace devourer {
namespace nf {

/* odm_pwdb_conversion: Y = 10*log10(X) ~= 3*log2(X), read off the leading set
* bit. `total_bit` bounds the bit search; `decimal_bit` is the fixed-point
* fractional width of X. The measurement forms S(20,18) = I^2 + Q^2, so it is
* called with (X, 20, 18). The next-lower bit adds a coarse ~2 dB decimal. */
inline int32_t pwdb_conversion(int32_t X, uint32_t total_bit,
uint32_t decimal_bit) {
int32_t integer = 0, decimal = 0;
if (X == 0)
X = 1; /* log2(x): x can't be 0 */
for (uint32_t i = (total_bit - 1); i > 0; i--) {
if (X & (int32_t{1} << i)) {
integer = static_cast<int32_t>(i);
decimal = (X & (int32_t{1} << (i - 1))) ? 2 : 0;
break;
}
}
return 3 * (integer - static_cast<int32_t>(decimal_bit)) + decimal;
}

/* odm_sign_conversion: interpret the low `total_bit` bits of `value` as a
* two's-complement signed integer (rxi/rxq are 10-bit signed on the debug
* port). */
inline int32_t sign_conversion(int32_t value, uint32_t total_bit) {
if (value & (int32_t{1} << (total_bit - 1)))
value -= (int32_t{1} << total_bit);
return value;
}

} // namespace nf
} // namespace devourer

#endif /* DEVOURER_NOISE_FLOOR_MATH_H */
10 changes: 10 additions & 0 deletions src/RxQuality.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ struct RxQuality {
double noise_floor_dbm = 0.0;
bool nf_valid = false;

/* Active/frame-free ABSOLUTE noise floor (dBm) — the companion to the passive
* floor above: the vendor idle-noise monitor, measured with no wanted signal
* (site survey / channel selection). Present only when the caller opted in
* (DEVOURER_RX_NOISE_FLOOR) and the generation supports it (Jaguar2 live,
* Jaguar1 8812A/8821A RX-idle CAL; false elsewhere). Issue #202. */
int8_t abs_noise_floor_dbm = 0;
bool abs_nf_valid = false;

/* Frame-free energy (from GetRxEnergy — GetRxQuality subsumes it). */
bool energy_valid = false;
uint32_t fa_ofdm = 0;
Expand Down Expand Up @@ -174,6 +182,8 @@ inline RxQuality build_rx_quality(const RxQualitySnapshot &s, const RxEnergy &e,
q.evm_mean_db = s.evm_mean_raw / 2.0;
q.noise_floor_dbm = s.nf_mean_dbm;
q.nf_valid = s.nf_valid;
q.abs_noise_floor_dbm = e.abs_noise_floor_dbm; /* active floor rides RxEnergy */
q.abs_nf_valid = e.valid_noise_floor;
q.energy_valid = e.valid_fa;
q.fa_ofdm = e.fa_ofdm;
q.cca_ofdm = e.cca_ofdm;
Expand Down
9 changes: 9 additions & 0 deletions src/RxSense.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ struct RxEnergy {
bool valid_nhm = false;
uint8_t nhm[12] = {};
uint16_t nhm_duration = 0;

/* Active/frame-free ABSOLUTE noise floor (dBm) — the vendor idle-noise
* monitor, distinct from the passive rssi-snr floor in RxQuality. Heavy
* (~10 ms of USB round-trips), so it is only filled when the caller opted in
* (DEVOURER_RX_NOISE_FLOOR). Jaguar2 fills it live (HW idle-noise report,
* wedge-free); Jaguar1 8812A/8821A fill it from an RX-idle CAL measurement;
* Jaguar3 and others leave it invalid (no vendor path). Issue #202. */
bool valid_noise_floor = false;
int8_t abs_noise_floor_dbm = 0;
};

#endif /* RX_SENSE_H */
74 changes: 74 additions & 0 deletions src/jaguar1/RtlJaguarDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "EepromManager.h"
#include "Hal8812PhyReg.h"
#include "NhmReader.h"
#include "NoiseFloorMath.h" /* active idle-noise-floor sign/pwdb helpers (#202) */
#include "RadioManagementModule.h"
#include "AckResponder.h" /* hardware ACK responder recipe */
#include "RadiotapPeek.h" /* send_packets batch pre-parse */
Expand Down Expand Up @@ -299,9 +300,75 @@ RxEnergy RtlJaguarDevice::GetRxEnergy() {
_device.phy_set_bb_reg(a, m, v);
},
e);

/* Active absolute floor (#202): the debug-port measurement wedges live RX, so
* it is NOT re-run here — GetRxEnergy just surfaces the RX-idle CAL taken at
* bring-up (measure_idle_noise_floor). Left invalid on the 8814A / when off. */
if (_cfg.rx.abs_noise_floor) {
e.abs_noise_floor_dbm = _abs_noise_floor;
e.valid_noise_floor = _abs_nf_valid;
}
return e;
}

/* Vendor odm_inband_noise_monitor_ac (8812/8821 active-sampling path). MUST run
* RX-idle: the clock-stop (0x8B4[6]) + BB/PMAC/CCK resets collide with a live
* bulk-IN RX DMA (#202). Reads the path-A RX I/Q off debug port 0x0FA0, forms
* pwdb = 10*log10(I^2+Q^2), averages VALID_CNT idle samples in [-27,0) dB, and
* offsets to dBm: noise = avg + 12(ADC backoff) + 0x1C(IGI ref) - 110 - 3. */
void RtlJaguarDevice::measure_idle_noise_floor() {
auto bbset = [this](uint16_t a, uint32_t m, uint32_t v) {
_device.phy_set_bb_reg(a, m, v);
};
const uint8_t igi_save =
static_cast<uint8_t>(_device.rtw_read8(0x0C50) & 0x7f);
int sum = 0, valid = 0, invalid = 0;
while (valid < 5 && invalid < 10) {
bbset(0x0C50, 0x7f, 0x1C); /* fix IGI = 0x1C */
bbset(0x08B4, 1u << 6, 1); /* stop CK320 & CK88 */
bbset(0x08FC, 0xFFFFFFFF, 0x200); /* debug port -> RX I/Q buffer (path A) */
const uint32_t v =
_radioManagement->phy_query_bb_reg_public(0x0FA0, 0xFFFFFFFF);
const bool pd = (v >> 31) & 1u;
int rxi = static_cast<int>((v & 0xFFC00) >> 10); /* [19:10] */
int rxq = static_cast<int>(v & 0x3FF); /* [9:0] */
bool have = false;
int pwdb = 0;
if (!pd || rxi != 0x200) { /* not in packet-detect / Tx state */
rxi = devourer::nf::sign_conversion(rxi, 10);
rxq = devourer::nf::sign_conversion(rxq, 10);
pwdb = devourer::nf::pwdb_conversion(rxi * rxi + rxq * rxq, 20, 18);
have = true;
}
bbset(0x08B4, 1u << 6, 0); /* restart clocks */
_device.rtw_write8(0x0002, _device.rtw_read8(0x0002) & ~1u); /* BB reset */
_device.rtw_write8(0x0002, _device.rtw_read8(0x0002) | 1u);
_device.rtw_write8(0x0B03, _device.rtw_read8(0x0B03) & ~1u); /* PMAC reset */
_device.rtw_write8(0x0B03, _device.rtw_read8(0x0B03) | 1u);
if (_device.rtw_read8(0x080B) & (1u << 4)) { /* CCK reset (if enabled) */
_device.rtw_write8(0x080B, _device.rtw_read8(0x080B) & ~(1u << 4));
_device.rtw_write8(0x080B, _device.rtw_read8(0x080B) | (1u << 4));
}
if (have && pwdb < 0 && pwdb >= -27) {
sum += pwdb;
++valid;
} else {
++invalid;
}
}
bbset(0x0C50, 0x7f, igi_save); /* restore IGI */
if (valid > 0) {
const int noise = sum / valid + 12 + 0x1C - 110 - 3;
_abs_noise_floor = static_cast<int8_t>(noise);
_abs_nf_valid = true;
_logger->info("Jaguar1: idle noise floor = {} dBm ({}/5 samples)", noise,
valid);
} else {
_abs_nf_valid = false;
_logger->warn("Jaguar1: idle noise-floor CAL got no valid samples");
}
}

SelectedChannel RtlJaguarDevice::GetSelectedChannel() { return _channel; }

uint64_t RtlJaguarDevice::ReadTsf() {
Expand Down Expand Up @@ -1211,6 +1278,13 @@ void RtlJaguarDevice::Init(Action_ParsedRadioPacket packetProcessor,
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

/* DEVOURER_RX_NOISE_FLOOR — measure the absolute idle floor RX-idle here,
* before StartRxLoop starts the bulk-IN DMA (the active-sampling path wedges a
* live RX). 8812A/8821A only; the 8814A uses a different vendor path (#202). */
if (_cfg.rx.abs_noise_floor &&
_eepromManager->version_id.ICType != CHIP_8814A)
measure_idle_noise_floor();

StartRxLoop(std::move(packetProcessor));
}

Expand Down
10 changes: 10 additions & 0 deletions src/jaguar1/RtlJaguarDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,16 @@ class RtlJaguarDevice : public IRtlDevice {
void SetRxPathMask(uint8_t mask);
int GetRxPathMask();

/* Active idle-noise-floor CAL (#202, 8812A/8821A only). The vendor
* odm_inband_noise_monitor_ac debug-port path stops CK320/CK88 and resets
* BB/PMAC/CCK, which wedges a live bulk-IN RX DMA — so it MUST run RX-idle.
* Called at bring-up (before StartRxLoop); result cached and surfaced via
* GetRxEnergy. Left invalid on the 8814A (different vendor path) and when
* DEVOURER_RX_NOISE_FLOOR is off. */
void measure_idle_noise_floor();
int8_t _abs_noise_floor = 0;
bool _abs_nf_valid = false;

bool should_stop = false;

/* Per-queue free-page snapshot read from REG_FIFOPAGE_INFO_1..5
Expand Down
52 changes: 52 additions & 0 deletions src/jaguar2/RtlJaguar2Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,58 @@ RxEnergy RtlJaguar2Device::GetRxEnergy() {
_device.phy_set_bb_reg(a, m, v);
},
e);

/* DEVOURER_RX_NOISE_FLOOR — active/frame-free absolute floor (#202). The
* vendor phydm_idle_noise_measure_ac: the BB maintains an idle-time power
* report at 0x0FF0 (path-A [7:0], path-B [15:8], sval = byte>>1); freeze it
* (0x9E4[30]=1) for a clean read, then noise = -110 + IGI(0xC50/0xE50) + mean.
* Wedge-free: no clock-stop and no BB/PMAC/CCK reset (unlike the 8812A/8821A
* debug-port path), so it runs safely under live RX. Serialized on _reg_mu.
*
* BEST-EFFORT on the tested 8812BU (1x1 8822B cut): the report is only rarely
* populated in devourer's monitor bring-up — most reads return the 0x80/0x00
* "no idle sample" sentinels. Investigated (issue #202): there is no vendor
* enable step for it (the report is HW-automatic and the vendor runs the
* measurement from a full associated init, not monitor mode); the sentinel
* rate is channel-independent, oversampling barely helps, and pausing DIG
* (fixing IGI) makes it worse. So this oversamples and GUARDS: 0x80/0x00 bytes
* are dropped and >=3 live samples in a plausible band are required, else
* valid_noise_floor stays false (never a fake dBm) — accuracy over valid-rate.
* When it does read, it cross-matches the Jaguar1 floor within a few dB on the
* same channel. A 2T2R 8822BU or a fuller init may populate it reliably. */
if (_cfg.rx.abs_noise_floor) {
std::lock_guard<std::mutex> lk(_reg_mu);
const int paths = _hal.chip_version().rf_2t2r ? 2 : 1;
int sum[2] = {0, 0}, cnt[2] = {0, 0};
bool any_nonzero = false;
for (int s = 0; s < 48; ++s) {
_device.phy_set_bb_reg(0x09E4, 1u << 30, 1); /* freeze idle-power report */
const uint32_t tmp = _device.rtw_read<uint32_t>(0x0FF0);
_device.phy_set_bb_reg(0x09E4, 1u << 30, 0); /* resume 5-us updates */
for (int p = 0; p < paths; ++p) {
const uint8_t byte = (tmp >> (8 * p)) & 0xff;
if (byte == 0x80 || byte == 0x00) /* "no idle sample" sentinels */
continue;
any_nonzero = true;
sum[p] += static_cast<int8_t>(byte) >> 1;
++cnt[p];
}
}
const bool ok = any_nonzero && cnt[0] >= 3 && (paths == 1 || cnt[1] >= 3);
if (ok) {
int noise = -110 + (_device.rtw_read8(0x0C50) & 0x7f) + sum[0] / cnt[0];
if (paths == 2)
noise = (noise + (-110 + (_device.rtw_read8(0x0E50) & 0x7f) +
sum[1] / cnt[1])) /
2;
if (noise <= -70 && noise >= -105) { /* plausible idle-floor band; a read
outside it is interference or a bad
report sample, not the idle floor */
e.abs_noise_floor_dbm = static_cast<int8_t>(noise);
e.valid_noise_floor = true;
}
}
}
return e;
}

Expand Down
6 changes: 6 additions & 0 deletions src/jaguar3/RtlJaguar3Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,12 @@ RxEnergy RtlJaguar3Device::GetRxEnergy() {
_device.phy_set_bb_reg(0x1eb4, 1u << 25, 0x1);
_device.phy_set_bb_reg(0x1eb4, 1u << 25, 0x0);
_device.phy_set_bb_reg(0x1d2c, 1u << 31, 0x1);

/* No active absolute noise floor on Jaguar3 (#202): the vendor 8822C driver
* has no idle-noise path (phydm_noisemonitor.c dispatches the report only to
* 8822B/8821C and returns 0 for the 8822C), so e.valid_noise_floor stays
* false. The passive rssi-snr floor (RxQuality.noise_floor_dbm) is J3's only
* floor. */
return e;
}

Expand Down
Loading
Loading