diff options
| author | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2010-08-02 00:08:55 +0000 | 
|---|---|---|
| committer | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2010-08-02 00:08:55 +0000 | 
| commit | 8132fc94cb5005ec2b777a9b7301e8755ed7b7dc (patch) | |
| tree | a6eefeb1323f44f86c8ee70c24abc019ba7c42fa | |
| parent | 5ef70c6dac645535d60bd29ff0d6813b0429a6ae (diff) | |
ath9k: improve stuck beacon recovery and noise floor handling. significantly improves stability under strong interference in ap mode
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@22460 3c298f89-4303-0410-b956-a3cf2f4a3e73
4 files changed, 306 insertions, 0 deletions
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 000000000..b2d44c5bb --- /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 000000000..7e3cb350f --- /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 000000000..35db2cba3 --- /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 000000000..74bee3e14 --- /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;  | 
