git: e56f7becc7ef - main - iwx: clean up / document noise floor and RSSI fetching
Date: Tue, 19 May 2026 05:43:37 UTC
The branch main has been updated by adrian:
URL: https://cgit.FreeBSD.org/src/commit/?id=e56f7becc7efe05acbb08162961dca574b152720
commit e56f7becc7efe05acbb08162961dca574b152720
Author: Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2025-11-16 03:13:59 +0000
Commit: Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-05-19 05:27:59 +0000
iwx: clean up / document noise floor and RSSI fetching
* Document what iwx_rxmq_get_signal_strength() is doing in a comment,
noting what the firmware returns and what math is being done on it
to turn it into a dBm value.
* Document what iwx_get_noise() is supposed to do, that we can't just
go do math with log numbers like we're doing, but also that we're
seeing zeros in this firmware (AX210), which may mean we're decoding
using the wrong structs.
* Swizzle around the RSSI calculation as a function, add min/max RSSI
values, and calculate RSSI against the noise floor.
* And handle the lowest noise floor value - it can't be -127dBm as
that will throw things off. Cap it at -100dBm which is a little
lower than the thermal noise floor at 20MHz (-98dBm), but it matches
IWX_MIN_DBM.
Differential Revision: https://reviews.freebsd.org/D53780
---
sys/dev/iwx/if_iwx.c | 83 ++++++++++++++++++++++++++++++++++++++++++-------
sys/dev/iwx/if_iwxreg.h | 17 ++++++++++
2 files changed, 89 insertions(+), 11 deletions(-)
diff --git a/sys/dev/iwx/if_iwx.c b/sys/dev/iwx/if_iwx.c
index 03ef775205e7..372fa440466c 100644
--- a/sys/dev/iwx/if_iwx.c
+++ b/sys/dev/iwx/if_iwx.c
@@ -433,7 +433,7 @@ static int iwx_rx_addbuf(struct iwx_softc *, int, int);
static int iwx_rxmq_get_signal_strength(struct iwx_softc *, struct iwx_rx_mpdu_desc *);
static void iwx_rx_rx_phy_cmd(struct iwx_softc *, struct iwx_rx_packet *,
struct iwx_rx_data *);
-static int iwx_get_noise(const struct iwx_statistics_rx_non_phy *);
+static int iwx_get_noise(struct iwx_softc *, const struct iwx_statistics_rx_non_phy *);
static int iwx_rx_hwdecrypt(struct iwx_softc *, struct mbuf *, uint32_t);
#if 0
int iwx_ccmp_decap(struct iwx_softc *, struct mbuf *,
@@ -4209,6 +4209,17 @@ iwx_rx_addbuf(struct iwx_softc *sc, int size, int idx)
return 0;
}
+/*
+ * @brief Return a single signal strength for the given frame.
+ *
+ * The firmware communicates up an energy field which is the negative of
+ * the dBm value. Ie, the number is positive and it increases as the
+ * signal level decreases.
+ *
+ * Fetch the two values, map 0 (inactive antenna) to -256 dBm which is a
+ * very small number, negate a non-zero value so it's mapped into a dBm
+ * value, then choose the maximum value to return.
+ */
static int
iwx_rxmq_get_signal_strength(struct iwx_softc *sc,
struct iwx_rx_mpdu_desc *desc)
@@ -4227,6 +4238,26 @@ iwx_rxmq_get_signal_strength(struct iwx_softc *sc,
return MAX(energy_a, energy_b);
}
+/**
+ * @brief Calculate an RSSI from the given signal level and noise floor.
+ *
+ * This calculates an RSSI and clamps it at IWX_RSSI_MINIMUM at the lower level
+ * and IWX_RSSI_MAXIMUM at the upper level.
+ *
+ * All units are in dBm.
+ */
+static int
+iwx_calculate_rssi(struct iwx_softc *sc, int ss, int nf)
+{
+ int rssi = (ss - nf);
+ if (rssi < IWX_RSSI_MINIMUM)
+ rssi = IWX_RSSI_MINIMUM;
+ else if (rssi > IWX_RSSI_MAXIMUM)
+ rssi = IWX_RSSI_MAXIMUM;
+
+ return (rssi);
+}
+
static int
iwx_rxmq_get_chains(struct iwx_softc *sc,
struct iwx_rx_mpdu_desc *desc)
@@ -4254,12 +4285,18 @@ iwx_rx_rx_phy_cmd(struct iwx_softc *sc, struct iwx_rx_packet *pkt,
}
/*
- * Retrieve the average noise (in dBm) among receivers.
+ * @brief Retrieve the average noise (in dBm) among receivers.
+ *
+ * Note: This routine calculates the noise floor sum incorrectly, as
+ * you can't just linearly add the logarithm based dB units together.
+ *
+ * If no noise floor is available then this routine will return -127.
*/
static int
-iwx_get_noise(const struct iwx_statistics_rx_non_phy *stats)
+iwx_get_noise(struct iwx_softc *sc,
+ const struct iwx_statistics_rx_non_phy *stats)
{
- int i, total, nbant, noise;
+ int i, total, nbant, noise, ret;
total = nbant = noise = 0;
for (i = 0; i < 3; i++) {
@@ -4271,7 +4308,14 @@ iwx_get_noise(const struct iwx_statistics_rx_non_phy *stats)
}
/* There should be at least one antenna but check anyway. */
- return (nbant == 0) ? -127 : (total / nbant) - 107;
+ if (nbant == 0)
+ ret = -127;
+ else if (total == 0)
+ ret = -127;
+ else
+ ret = (total / nbant) - 127;
+
+ return (ret);
}
#if 0
@@ -4669,9 +4713,8 @@ iwx_rx_mpdu_mq(struct iwx_softc *sc, struct mbuf *m, void *pktdata,
phy_info = le16toh(desc->phy_info);
+ /* note: RSSI here is absolute signal strength, not relative */
rssi = iwx_rxmq_get_signal_strength(sc, desc);
- rssi = (0 - IWX_MIN_DBM) + rssi; /* normalize */
- rssi = MIN(rssi, (IWX_MAX_DBM - IWX_MIN_DBM)); /* clip to max. 100% */
memset(&rxs, 0, sizeof(rxs));
rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ;
@@ -4688,9 +4731,19 @@ iwx_rx_mpdu_mq(struct iwx_softc *sc, struct mbuf *m, void *pktdata,
if (rxs.c_chain != 0)
rxs.r_flags |= IEEE80211_R_C_CHAIN;
- /* rssi is in 1/2db units */
- rxs.c_rssi = rssi * 2;
- rxs.c_nf = sc->sc_noise;
+ /* noise floor is in 1dB units */
+ if (sc->sc_noise < IWX_DEFAULT_NF)
+ /*
+ * For now choose /a/ default, net80211 expects nf to be passed
+ * in various places and older drivers fake NF values where
+ * needed.
+ */
+ rxs.c_nf = IWX_DEFAULT_NF;
+ else
+ rxs.c_nf = sc->sc_noise;
+
+ /* rssi is in 1/2db units relative to the noise floor */
+ rxs.c_rssi = iwx_calculate_rssi(sc, rssi, rxs.c_nf) * 2;
if (pad) {
rxs.c_pktflags |= IEEE80211_RX_F_DECRYPTED;
@@ -9142,11 +9195,16 @@ iwx_rx_pkt(struct iwx_softc *sc, struct iwx_rx_data *data, struct mbuf *ml)
break;
}
+ /*
+ * TODO: is this the right struct to use? Look at what
+ * mvm is doing for statistics notification (eg
+ * iwl_mvm_handle_rx_statistics() .
+ */
case IWX_STATISTICS_NOTIFICATION: {
struct iwx_notif_statistics *stats;
SYNC_RESP_STRUCT(stats, pkt);
memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats));
- sc->sc_noise = iwx_get_noise(&stats->rx.general);
+ sc->sc_noise = iwx_get_noise(sc, &stats->rx.general);
break;
}
@@ -10587,6 +10645,9 @@ iwx_attach(device_t dev)
mbufq_init(&rxba->entries[j].frames, ifqmaxlen);
}
+ /* Initialize to something to have a chance to get S:N values. */
+ sc->sc_noise = IWX_DEFAULT_NF;
+
sc->sc_preinit_hook.ich_func = iwx_attach_hook;
sc->sc_preinit_hook.ich_arg = sc;
if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) {
diff --git a/sys/dev/iwx/if_iwxreg.h b/sys/dev/iwx/if_iwxreg.h
index f3d1f078b48e..3f161b627a5f 100644
--- a/sys/dev/iwx/if_iwxreg.h
+++ b/sys/dev/iwx/if_iwxreg.h
@@ -7902,6 +7902,23 @@ iwx_rx_packet_payload_len(const struct iwx_rx_packet *pkt)
#define IWX_MIN_DBM -100
#define IWX_MAX_DBM -33 /* realistic guess */
+/*
+ * NF values for various channel widths at 20C
+ *
+ * 20MHz - -98 dBm
+ * 40MHz - -94 dBm
+ * 80MHz - -92 dBm
+ * 160MHz - -88 dBm
+ * 320MHz - -85 dBm
+ */
+#define IWX_DEFAULT_NF -100
+/*
+ * Note; RSSI is for net80211, and it's calculated against the noise floor
+ * as a reference.
+ */
+#define IWX_RSSI_MINIMUM -10
+#define IWX_RSSI_MAXIMUM 60
+
#define IWX_READ(sc, reg) \
bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg))