From 9d2aa5f66f037a462888027eec13855ddff61c60 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 2 Aug 2010 00:08:55 +0000 Subject: [PATCH] ath9k: improve stuck beacon recovery and noise floor handling. significantly improves stability under strong interference in ap mode SVN-Revision: 22460 --- .../patches/540-ath9k_bstuck_debug.patch | 43 ++++++ .../patches/541-ath9k_nf_validate.patch | 101 ++++++++++++++ .../542-ath9k_bstuck_nf_calibrate.patch | 127 ++++++++++++++++++ .../543-ath9k_interference_nf_cal.patch | 35 +++++ 4 files changed, 306 insertions(+) create mode 100644 package/mac80211/patches/540-ath9k_bstuck_debug.patch create mode 100644 package/mac80211/patches/541-ath9k_nf_validate.patch create mode 100644 package/mac80211/patches/542-ath9k_bstuck_nf_calibrate.patch create mode 100644 package/mac80211/patches/543-ath9k_interference_nf_cal.patch diff --git a/package/mac80211/patches/540-ath9k_bstuck_debug.patch b/package/mac80211/patches/540-ath9k_bstuck_debug.patch new file mode 100644 index 0000000000..b2d44c5bb0 --- /dev/null +++ b/package/mac80211/patches/540-ath9k_bstuck_debug.patch @@ -0,0 +1,43 @@ +--- a/drivers/net/wireless/ath/debug.h ++++ b/drivers/net/wireless/ath/debug.h +@@ -36,6 +36,7 @@ + * @ATH_DBG_PS: power save processing + * @ATH_DBG_HWTIMER: hardware timer handling + * @ATH_DBG_BTCOEX: bluetooth coexistance ++ * @ATH_DBG_BSTUCK: stuck beacons + * @ATH_DBG_ANY: enable all debugging + * + * The debug level is used to control the amount and type of debugging output +@@ -60,6 +61,7 @@ enum ATH_DEBUG { + ATH_DBG_HWTIMER = 0x00001000, + ATH_DBG_BTCOEX = 0x00002000, + ATH_DBG_WMI = 0x00004000, ++ ATH_DBG_BSTUCK = 0x00008000, + ATH_DBG_ANY = 0xffffffff + }; + +--- a/drivers/net/wireless/ath/ath9k/beacon.c ++++ b/drivers/net/wireless/ath/ath9k/beacon.c +@@ -359,11 +359,11 @@ void ath_beacon_tasklet(unsigned long da + sc->beacon.bmisscnt++; + + if (sc->beacon.bmisscnt < BSTUCK_THRESH) { +- ath_print(common, ATH_DBG_BEACON, ++ ath_print(common, ATH_DBG_BSTUCK, + "missed %u consecutive beacons\n", + sc->beacon.bmisscnt); + } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { +- ath_print(common, ATH_DBG_BEACON, ++ ath_print(common, ATH_DBG_BSTUCK, + "beacon is officially stuck\n"); + sc->sc_flags |= SC_OP_TSF_RESET; + ath_reset(sc, false); +@@ -373,7 +373,7 @@ void ath_beacon_tasklet(unsigned long da + } + + if (sc->beacon.bmisscnt != 0) { +- ath_print(common, ATH_DBG_BEACON, ++ ath_print(common, ATH_DBG_BSTUCK, + "resume beacon xmit after %u misses\n", + sc->beacon.bmisscnt); + sc->beacon.bmisscnt = 0; diff --git a/package/mac80211/patches/541-ath9k_nf_validate.patch b/package/mac80211/patches/541-ath9k_nf_validate.patch new file mode 100644 index 0000000000..7e3cb350fe --- /dev/null +++ b/package/mac80211/patches/541-ath9k_nf_validate.patch @@ -0,0 +1,101 @@ +--- a/drivers/net/wireless/ath/ath9k/calib.c ++++ b/drivers/net/wireless/ath/ath9k/calib.c +@@ -19,8 +19,7 @@ + + /* Common calibration code */ + +-/* We can tune this as we go by monitoring really low values */ +-#define ATH9K_NF_TOO_LOW -60 ++#define ATH9K_NF_TOO_HIGH -60 + + static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) + { +@@ -45,11 +44,35 @@ static int16_t ath9k_hw_get_nf_hist_mid( + return nfval; + } + +-static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h, ++static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah, ++ struct ath9k_channel *chan) ++{ ++ struct ath_nf_limits *limit; ++ ++ if (!chan || IS_CHAN_2GHZ(chan)) ++ limit = &ah->nf_2g; ++ else ++ limit = &ah->nf_5g; ++ ++ return limit; ++} ++ ++static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, ++ struct ath9k_channel *chan) ++{ ++ return ath9k_hw_get_nf_limits(ah, chan)->nominal; ++} ++ ++ ++static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, ++ struct ath9k_nfcal_hist *h, + int16_t *nfarray) + { ++ struct ath_nf_limits *limit; + int i; + ++ limit = ath9k_hw_get_nf_limits(ah, ah->curchan); ++ + for (i = 0; i < NUM_NF_READINGS; i++) { + h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; + +@@ -63,6 +86,9 @@ static void ath9k_hw_update_nfcal_hist_b + h[i].privNF = + ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); + } ++ ++ if (h[i].privNF > limit->max) ++ h[i].privNF = limit->max; + } + } + +@@ -104,19 +130,6 @@ void ath9k_hw_reset_calibration(struct a + ah->cal_samples = 0; + } + +-static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, +- struct ath9k_channel *chan) +-{ +- struct ath_nf_limits *limit; +- +- if (!chan || IS_CHAN_2GHZ(chan)) +- limit = &ah->nf_2g; +- else +- limit = &ah->nf_5g; +- +- return limit->nominal; +-} +- + /* This is done for the currently configured channel */ + bool ath9k_hw_reset_calvalid(struct ath_hw *ah) + { +@@ -277,10 +290,10 @@ static void ath9k_hw_nf_sanitize(struct + "NF calibrated [%s] [chain %d] is %d\n", + (i >= 3 ? "ext" : "ctl"), i % 3, nf[i]); + +- if (nf[i] > limit->max) { ++ if (nf[i] > ATH9K_NF_TOO_HIGH) { + ath_print(common, ATH_DBG_CALIBRATE, + "NF[%d] (%d) > MAX (%d), correcting to MAX", +- i, nf[i], limit->max); ++ i, nf[i], ATH9K_NF_TOO_HIGH); + nf[i] = limit->max; + } else if (nf[i] < limit->min) { + ath_print(common, ATH_DBG_CALIBRATE, +@@ -326,7 +339,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, s + + h = caldata->nfCalHist; + caldata->nfcal_pending = false; +- ath9k_hw_update_nfcal_hist_buffer(h, nfarray); ++ ath9k_hw_update_nfcal_hist_buffer(ah, h, nfarray); + caldata->rawNoiseFloor = h[0].privNF; + return true; + } diff --git a/package/mac80211/patches/542-ath9k_bstuck_nf_calibrate.patch b/package/mac80211/patches/542-ath9k_bstuck_nf_calibrate.patch new file mode 100644 index 0000000000..35db2cba37 --- /dev/null +++ b/package/mac80211/patches/542-ath9k_bstuck_nf_calibrate.patch @@ -0,0 +1,127 @@ +--- a/drivers/net/wireless/ath/ath9k/beacon.c ++++ b/drivers/net/wireless/ath/ath9k/beacon.c +@@ -362,6 +362,7 @@ void ath_beacon_tasklet(unsigned long da + ath_print(common, ATH_DBG_BSTUCK, + "missed %u consecutive beacons\n", + sc->beacon.bmisscnt); ++ ath9k_hw_bstuck_nfcal(ah); + } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { + ath_print(common, ATH_DBG_BSTUCK, + "beacon is officially stuck\n"); +--- a/drivers/net/wireless/ath/ath9k/calib.c ++++ b/drivers/net/wireless/ath/ath9k/calib.c +@@ -65,12 +65,16 @@ static s16 ath9k_hw_get_default_nf(struc + + + static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, +- struct ath9k_nfcal_hist *h, ++ struct ath9k_hw_cal_data *cal, + int16_t *nfarray) + { ++ struct ath_common *common = ath9k_hw_common(ah); + struct ath_nf_limits *limit; ++ struct ath9k_nfcal_hist *h; ++ bool high_nf_mid = false; + int i; + ++ h = cal->nfCalHist; + limit = ath9k_hw_get_nf_limits(ah, ah->curchan); + + for (i = 0; i < NUM_NF_READINGS; i++) { +@@ -87,9 +91,38 @@ static void ath9k_hw_update_nfcal_hist_b + ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); + } + +- if (h[i].privNF > limit->max) +- h[i].privNF = limit->max; ++ if (!h[i].privNF) ++ continue; ++ ++ if (h[i].privNF > limit->max) { ++ high_nf_mid = true; ++ ++ ath_print(common, ATH_DBG_CALIBRATE, ++ "NFmid[%d] (%d) > MAX (%d), %s\n", ++ i, h[i].privNF, limit->max, ++ (cal->nfcal_interference ? ++ "not corrected (due to interference)" : ++ "correcting to MAX")); ++ ++ /* ++ * Normally we limit the average noise floor by the ++ * hardware specific maximum here. However if we have ++ * encountered stuck beacons because of interference, ++ * we bypass this limit here in order to better deal ++ * with our environment. ++ */ ++ if (!cal->nfcal_interference) ++ h[i].privNF = limit->max; ++ } + } ++ ++ /* ++ * If the noise floor seems normal for all chains, assume that ++ * there is no significant interference in the environment anymore. ++ * Re-enable the enforcement of the NF maximum again. ++ */ ++ if (!high_nf_mid) ++ cal->nfcal_interference = false; + } + + static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, +@@ -339,7 +372,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, s + + h = caldata->nfCalHist; + caldata->nfcal_pending = false; +- ath9k_hw_update_nfcal_hist_buffer(ah, h, nfarray); ++ ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); + caldata->rawNoiseFloor = h[0].privNF; + return true; + } +@@ -374,3 +407,26 @@ s16 ath9k_hw_getchan_noise(struct ath_hw + return ah->caldata->rawNoiseFloor; + } + EXPORT_SYMBOL(ath9k_hw_getchan_noise); ++ ++void ath9k_hw_bstuck_nfcal(struct ath_hw *ah) ++{ ++ struct ath9k_hw_cal_data *caldata = ah->caldata; ++ ++ if (unlikely(!caldata)) ++ return; ++ ++ /* ++ * If beacons are stuck, the most likely cause is interference. ++ * Triggering a noise floor calibration at this point helps the ++ * hardware adapt to a noisy environment much faster. ++ * To ensure that we recover from stuck beacons quickly, let ++ * the baseband update the internal NF value itself, similar to ++ * what is being done after a full reset. ++ */ ++ if (!caldata->nfcal_pending) ++ ath9k_hw_start_nfcal(ah, true); ++ ++ caldata->nfcal_interference = true; ++} ++EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal); ++ +--- a/drivers/net/wireless/ath/ath9k/calib.h ++++ b/drivers/net/wireless/ath/ath9k/calib.h +@@ -113,6 +113,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, + bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan); + void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, + struct ath9k_channel *chan); ++void ath9k_hw_bstuck_nfcal(struct ath_hw *ah); + s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); + void ath9k_hw_reset_calibration(struct ath_hw *ah, + struct ath9k_cal_list *currCal); +--- a/drivers/net/wireless/ath/ath9k/hw.h ++++ b/drivers/net/wireless/ath/ath9k/hw.h +@@ -355,6 +355,7 @@ struct ath9k_hw_cal_data { + int16_t rawNoiseFloor; + bool paprd_done; + bool nfcal_pending; ++ bool nfcal_interference; + u16 small_signal_gain[AR9300_MAX_CHAINS]; + u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ]; + struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; diff --git a/package/mac80211/patches/543-ath9k_interference_nf_cal.patch b/package/mac80211/patches/543-ath9k_interference_nf_cal.patch new file mode 100644 index 0000000000..74bee3e149 --- /dev/null +++ b/package/mac80211/patches/543-ath9k_interference_nf_cal.patch @@ -0,0 +1,35 @@ +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -423,6 +423,7 @@ int ath_beaconq_config(struct ath_softc + #define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */ + #define ATH_ANI_POLLINTERVAL_OLD 100 /* 100 ms */ + #define ATH_ANI_POLLINTERVAL_NEW 1000 /* 1000 ms */ ++#define ATH_LONG_CALINTERVAL_INT 1000 /* 1000 ms */ + #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ + #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ + +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -396,7 +396,12 @@ void ath_ani_calibrate(unsigned long dat + bool shortcal = false; + bool aniflag = false; + unsigned int timestamp = jiffies_to_msecs(jiffies); +- u32 cal_interval, short_cal_interval; ++ u32 cal_interval, short_cal_interval, long_cal_interval; ++ ++ if (ah->caldata && ah->caldata->nfcal_interference) ++ long_cal_interval = ATH_LONG_CALINTERVAL_INT; ++ else ++ long_cal_interval = ATH_LONG_CALINTERVAL; + + short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? + ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; +@@ -408,7 +413,7 @@ void ath_ani_calibrate(unsigned long dat + ath9k_ps_wakeup(sc); + + /* Long calibration runs independently of short calibration. */ +- if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { ++ if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { + longcal = true; + ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); + common->ani.longcal_timer = timestamp; -- 2.30.2