Introduce a debugfs option to manually override the noise floor, ignoring the automatically tuned noise floor of the driver/hw. In my tests with a AR9580 based module and a tx99 5 MHz interferer, I could tune the noisefloor to -95 dBm or above to allow communication again. The automatic noise floor calibration sometimes could adapt to the situation as well, but not reliably and permanently. I would consider this "feature" experimental and interesting for people debugging the noise floor calibration or other effects of the hardware. Signed-off-by: Simon Wunderlich <sw@xxxxxxxxxxxxxxxxxx> Signed-off-by: Mathias Kretschmer <mathias.kretschmer@xxxxxxxxxxxxxxxxx> --- drivers/net/wireless/ath/ath9k/calib.c | 5 ++- drivers/net/wireless/ath/ath9k/debug.c | 62 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/hw.h | 1 + 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 0f71146b781d..13ab6bc46775 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -254,7 +254,9 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) continue; - if (h) + if (ah->nf_override) + nfval = ah->nf_override; + else if (h) nfval = h[i].privNF; else nfval = default_nf; @@ -348,6 +350,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) return 0; } +EXPORT_SYMBOL(ath9k_hw_loadnf); static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 43930c336987..2e64977a8ab6 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1191,6 +1191,65 @@ static const struct file_operations fops_tpc = { .llseek = default_llseek, }; +static ssize_t read_file_nf_override(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + char buf[32]; + unsigned int len; + + if (ah->nf_override == 0) + len = sprintf(buf, "off\n"); + else + len = sprintf(buf, "%d\n", ah->nf_override); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_nf_override(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (strncmp("off", buf, 3) == 0) + val = 0; + else if (kstrtol(buf, 0, &val)) + return -EINVAL; + + if (val > 0) + return -EINVAL; + + if (val < -120) + return -EINVAL; + + ah->nf_override = val; + + if (ah->curchan) + ath9k_hw_loadnf(ah, ah->curchan); + + return count; +} + +static const struct file_operations fops_nf_override = { + .read = read_file_nf_override, + .write = write_file_nf_override, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" @@ -1402,5 +1461,8 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_u16("airtime_flags", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->airtime_flags); + debugfs_create_file("nf_override", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, &fops_nf_override); + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 2166f644599d..f06e02d1c342 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -807,6 +807,7 @@ struct ath_hw { u32 rfkill_gpio; u32 rfkill_polarity; u32 ah_flags; + s16 nf_override; bool reset_power_on; bool htc_reset_init; -- 2.11.0