The AR9271 firmware tests AR_CRCErr before AR_PHYErr when it fills in the RX status, so a descriptor carrying both bits reaches the host as a CRC error with no PHY error bit. ath9k had the same order and fixed it in commit 3a325565c7fa ("ath9k: reorder error codes for spectral"), because spectral samples under interference "seem to happen quite often" with a bad CRC. The firmware never got that fix, and the host only hands PHY errors to ath_cmn_process_fft(), so once the channel is busy enough the scan keeps producing samples and the host drops every one of them as a CRC error. Traced on a field receiver: 640 samples a second arriving with rs_status 0x01, each ending in SPECTRAL_SCAN_BITMASK, CRC ERR climbing at exactly the sample rate while PHY ERR stood still. When a scan is active, give a CRC error whose length matches an FFT report to the parser as well, with the PHY error code it expects. The parser only accepts the frame if its magnitude fields agree with the bins, so a genuine CRC-damaged frame of that size is rejected and falls through to the normal path. The firmware order is fixed separately, but the firmware in linux-firmware is 1.4.0 from 2015, so the host has to cope with what the card sends. Fixes: 83fb287ecd8a ("ath9k_htc: process rx spectral packets") Signed-off-by: Nerijus Bendžiūnas --- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index bed7ea2425a0..f21cfc03426b 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -969,6 +969,32 @@ static void rx_status_htc_to_ath(struct ath_rx_status *rx_stats, convert_htc_flag(rx_stats, rxstatus); } +/* + * The firmware reports a frame that failed its CRC as a CRC error even when + * the PHY error bit is set as well, so under interference spectral samples + * reach the host as CRC errors. A sample is recognisable by its size: the + * FFT report length for the channel width, plus or minus the two bytes the + * MAC may add or drop. + */ +static bool ath9k_htc_is_spectral_sample_len(struct ath9k_htc_priv *priv, + u16 len) +{ + enum nl80211_channel_type chan_type; + u16 fft_len; + + if (priv->spec_priv.spectral_mode == SPECTRAL_DISABLED) + return false; + + chan_type = cfg80211_get_chandef_type(&priv->hw->conf.chandef); + if (chan_type == NL80211_CHAN_HT40MINUS || + chan_type == NL80211_CHAN_HT40PLUS) + fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; + else + fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; + + return len + 1 >= fft_len && len <= fft_len + 2; +} + static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, struct ath9k_htc_rxbuf *rxbuf, struct ieee80211_rx_status *rx_status) @@ -1052,6 +1078,18 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, goto rx_next; } + /* + * Let the FFT parser decide whether a CRC error of sample size is a + * sample; it validates the contents and returns 0 for anything else. + */ + if (unlikely(rx_stats.rs_status & ATH9K_RXERR_CRC) && + ath9k_htc_is_spectral_sample_len(priv, rs_datalen)) { + rx_stats.rs_phyerr = ATH9K_PHYERR_RADAR; + if (ath_cmn_process_fft(&priv->spec_priv, hdr, &rx_stats, + rx_status->mactime)) + goto rx_next; + } + if (!ath9k_cmn_rx_accept(common, hdr, rx_status, &rx_stats, &decrypt_error, priv->rxfilter)) goto rx_next; base-commit: ca800a9302764c445de0da0e84d2252400a770ee -- 2.55.0