diff --git a/sys/dev/iwx/if_iwx.c b/sys/dev/iwx/if_iwx.c --- a/sys/dev/iwx/if_iwx.c +++ b/sys/dev/iwx/if_iwx.c @@ -432,7 +432,7 @@ 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 *, @@ -4208,6 +4208,17 @@ 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) @@ -4226,6 +4237,26 @@ 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) @@ -4253,12 +4284,18 @@ } /* - * 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++) { @@ -4270,7 +4307,14 @@ } /* 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 @@ -4666,9 +4710,8 @@ 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; @@ -4685,9 +4728,19 @@ 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; @@ -9129,11 +9182,16 @@ 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; } @@ -10574,6 +10632,9 @@ 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 --- a/sys/dev/iwx/if_iwxreg.h +++ b/sys/dev/iwx/if_iwxreg.h @@ -7902,6 +7902,23 @@ #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))