diff options
Diffstat (limited to 'package')
7 files changed, 935 insertions, 0 deletions
| diff --git a/package/mac80211/patches/520-ath9k_common_clockrate.patch b/package/mac80211/patches/520-ath9k_common_clockrate.patch new file mode 100644 index 000000000..3266e0e10 --- /dev/null +++ b/package/mac80211/patches/520-ath9k_common_clockrate.patch @@ -0,0 +1,116 @@ +--- a/drivers/net/wireless/ath/ath.h ++++ b/drivers/net/wireless/ath/ath.h +@@ -145,6 +145,8 @@ struct ath_common { + 	DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX); + 	enum ath_crypt_caps crypt_caps; +  ++	unsigned int clockrate; ++ + 	struct ath_regulatory regulatory; + 	const struct ath_ops *ops; + 	const struct ath_bus_ops *bus_ops; +--- a/drivers/net/wireless/ath/ath9k/hw.c ++++ b/drivers/net/wireless/ath/ath9k/hw.c +@@ -91,29 +91,32 @@ static void ath9k_hw_ani_cache_ini_regs( + /* Helper Functions */ + /********************/ +  +-static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs) ++static void ath9k_hw_set_clockrate(struct ath_hw *ah) + { + 	struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; ++	struct ath_common *common = ath9k_hw_common(ah); ++	unsigned int clockrate; +  + 	if (!ah->curchan) /* should really check for CCK instead */ +-		return usecs *ATH9K_CLOCK_RATE_CCK; +-	if (conf->channel->band == IEEE80211_BAND_2GHZ) +-		return usecs *ATH9K_CLOCK_RATE_2GHZ_OFDM; +- +-	if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK) +-		return usecs * ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; ++		clockrate = ATH9K_CLOCK_RATE_CCK; ++	else if (conf->channel->band == IEEE80211_BAND_2GHZ) ++		clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM; ++	else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK) ++		clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; + 	else +-		return usecs * ATH9K_CLOCK_RATE_5GHZ_OFDM; ++		clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM; ++ ++	if (conf_is_ht40(conf)) ++		clockrate *= 2; ++ ++	common->clockrate = clockrate; + } +  + static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs) + { +-	struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; ++	struct ath_common *common = ath9k_hw_common(ah); +  +-	if (conf_is_ht40(conf)) +-		return ath9k_hw_mac_clks(ah, usecs) * 2; +-	else +-		return ath9k_hw_mac_clks(ah, usecs); ++	return usecs * common->clockrate; + } +  + bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout) +@@ -1168,6 +1171,7 @@ static bool ath9k_hw_channel_change(stru + 			  "Failed to set channel\n"); + 		return false; + 	} ++	ath9k_hw_set_clockrate(ah); +  + 	ah->eep_ops->set_txpower(ah, chan, + 			     ath9k_regd_get_ctl(regulatory, chan), +@@ -1380,6 +1384,8 @@ int ath9k_hw_reset(struct ath_hw *ah, st + 	if (r) + 		return r; +  ++	ath9k_hw_set_clockrate(ah); ++ + 	ENABLE_REGWRITE_BUFFER(ah); +  + 	for (i = 0; i < AR_NUM_DCU; i++) +--- a/drivers/net/wireless/ath/ath9k/ani.c ++++ b/drivers/net/wireless/ath/ath9k/ani.c +@@ -465,35 +465,13 @@ static void ath9k_hw_ani_lower_immunity( + 		ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1); + } +  +-static u8 ath9k_hw_chan_2_clockrate_mhz(struct ath_hw *ah) +-{ +-	struct ath9k_channel *chan = ah->curchan; +-	struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; +-	u8 clockrate; /* in MHz */ +- +-	if (!ah->curchan) /* should really check for CCK instead */ +-		clockrate = ATH9K_CLOCK_RATE_CCK; +-	else if (conf->channel->band == IEEE80211_BAND_2GHZ) +-		clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM; +-	else if (IS_CHAN_A_FAST_CLOCK(ah, chan)) +-		clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; +-	else +-		clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM; +- +-	if (conf_is_ht40(conf)) +-		return clockrate * 2; +- +-	return clockrate; +-} +- + static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah) + { ++	struct ath_common *common = ath9k_hw_common(ah); + 	int32_t listen_time; +-	int32_t clock_rate; +  + 	ath9k_hw_update_cycle_counters(ah); +-	clock_rate = ath9k_hw_chan_2_clockrate_mhz(ah) * 1000; +-	listen_time = ah->listen_time / clock_rate; ++	listen_time = ah->listen_time / (common->clockrate * 1000); + 	ah->listen_time = 0; +  + 	return listen_time; diff --git a/package/mac80211/patches/521-ath5k_common_clockrate.patch b/package/mac80211/patches/521-ath5k_common_clockrate.patch new file mode 100644 index 000000000..08c18342e --- /dev/null +++ b/package/mac80211/patches/521-ath5k_common_clockrate.patch @@ -0,0 +1,65 @@ +--- a/drivers/net/wireless/ath/ath5k/pcu.c ++++ b/drivers/net/wireless/ath/ath5k/pcu.c +@@ -207,7 +207,8 @@ static int ath5k_hw_set_cts_timeout(stru +  */ + unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec) + { +-	return usec * ath5k_hw_get_clockrate(ah); ++	struct ath_common *common = ath5k_hw_common(ah); ++	return usec * common->clockrate; + } +  + /** +@@ -216,17 +217,19 @@ unsigned int ath5k_hw_htoclock(struct at +  */ + unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock) + { +-	return clock / ath5k_hw_get_clockrate(ah); ++	struct ath_common *common = ath5k_hw_common(ah); ++	return clock / common->clockrate; + } +  + /** +- * ath5k_hw_get_clockrate - Get the clock rate for current mode ++ * ath5k_hw_set_clockrate - Set common->clockrate for the current channel +  * +  * @ah: The &struct ath5k_hw +  */ +-unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah) ++void ath5k_hw_set_clockrate(struct ath5k_hw *ah) + { + 	struct ieee80211_channel *channel = ah->ah_current_channel; ++	struct ath_common *common = ath5k_hw_common(ah); + 	int clock; +  + 	if (channel->hw_value & CHANNEL_5GHZ) +@@ -240,7 +243,7 @@ unsigned int ath5k_hw_get_clockrate(stru + 	if (channel->hw_value & CHANNEL_TURBO) + 		clock *= 2; +  +-	return clock; ++	common->clockrate = clock; + } +  + /** +--- a/drivers/net/wireless/ath/ath5k/ath5k.h ++++ b/drivers/net/wireless/ath/ath5k/ath5k.h +@@ -1201,7 +1201,7 @@ void ath5k_hw_set_ack_bitrate_high(struc + /* Clock rate related functions */ + unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec); + unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock); +-unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah); ++void ath5k_hw_set_clockrate(struct ath5k_hw *ah); +  + /* Queue Control Unit, DFS Control Unit Functions */ + int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, +--- a/drivers/net/wireless/ath/ath5k/phy.c ++++ b/drivers/net/wireless/ath/ath5k/phy.c +@@ -1093,6 +1093,7 @@ int ath5k_hw_channel(struct ath5k_hw *ah +  + 	ah->ah_current_channel = channel; + 	ah->ah_turbo = channel->hw_value == CHANNEL_T ? true : false; ++	ath5k_hw_set_clockrate(ah); +  + 	return 0; + } diff --git a/package/mac80211/patches/522-ath_common_counters.patch b/package/mac80211/patches/522-ath_common_counters.patch new file mode 100644 index 000000000..f71957526 --- /dev/null +++ b/package/mac80211/patches/522-ath_common_counters.patch @@ -0,0 +1,347 @@ +--- a/drivers/net/wireless/ath/ath.h ++++ b/drivers/net/wireless/ath/ath.h +@@ -19,6 +19,7 @@ +  + #include <linux/skbuff.h> + #include <linux/if_ether.h> ++#include <linux/spinlock.h> + #include <net/mac80211.h> +  + /* +@@ -42,6 +43,13 @@ struct ath_ani { + 	struct timer_list timer; + }; +  ++struct ath_cycle_counters { ++	u32 cycles; ++	u32 rx_busy; ++	u32 rx_frame; ++	u32 tx_frame; ++}; ++ + enum ath_device_state { + 	ATH_HW_UNAVAILABLE, + 	ATH_HW_INITIALIZED, +@@ -147,6 +155,10 @@ struct ath_common { +  + 	unsigned int clockrate; +  ++	spinlock_t cc_lock; ++	struct ath_cycle_counters cc_ani; ++	struct ath_cycle_counters cc_survey; ++ + 	struct ath_regulatory regulatory; + 	const struct ath_ops *ops; + 	const struct ath_bus_ops *bus_ops; +@@ -163,5 +175,7 @@ int ath_key_config(struct ath_common *co + 			  struct ieee80211_sta *sta, + 			  struct ieee80211_key_conf *key); + bool ath_hw_keyreset(struct ath_common *common, u16 entry); ++void ath_hw_cycle_counters_update(struct ath_common *common); ++int32_t ath_hw_get_listen_time(struct ath_common *common); +  + #endif /* ATH_H */ +--- a/drivers/net/wireless/ath/ath9k/ani.c ++++ b/drivers/net/wireless/ath/ath9k/ani.c +@@ -465,18 +465,6 @@ static void ath9k_hw_ani_lower_immunity( + 		ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1); + } +  +-static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah) +-{ +-	struct ath_common *common = ath9k_hw_common(ah); +-	int32_t listen_time; +- +-	ath9k_hw_update_cycle_counters(ah); +-	listen_time = ah->listen_time / (common->clockrate * 1000); +-	ah->listen_time = 0; +- +-	return listen_time; +-} +- + static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) + { + 	struct ar5416AniState *aniState; +@@ -655,7 +643,9 @@ static void ath9k_hw_ani_read_counters(s + 	u32 phyCnt1, phyCnt2; + 	int32_t listenTime; +  +-	listenTime = ath9k_hw_ani_get_listen_time(ah); ++	ath_hw_cycle_counters_update(common); ++	listenTime = ath_hw_get_listen_time(common); ++ + 	if (listenTime < 0) { + 		ah->stats.ast_ani_lneg++; + 		ath9k_ani_restart(ah); +@@ -796,54 +786,6 @@ void ath9k_hw_disable_mib_counters(struc + } + EXPORT_SYMBOL(ath9k_hw_disable_mib_counters); +  +-void ath9k_hw_update_cycle_counters(struct ath_hw *ah) +-{ +-	struct ath_cycle_counters cc; +-	bool clear; +- +-	memcpy(&cc, &ah->cc, sizeof(cc)); +- +-	/* freeze counters */ +-	REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC); +- +-	ah->cc.cycles = REG_READ(ah, AR_CCCNT); +-	if (ah->cc.cycles < cc.cycles) { +-		clear = true; +-		goto skip; +-	} +- +-	ah->cc.rx_clear = REG_READ(ah, AR_RCCNT); +-	ah->cc.rx_frame = REG_READ(ah, AR_RFCNT); +-	ah->cc.tx_frame = REG_READ(ah, AR_TFCNT); +- +-	/* prevent wraparound */ +-	if (ah->cc.cycles & BIT(31)) +-		clear = true; +- +-#define CC_DELTA(_field, _reg) ah->cc_delta._field += ah->cc._field - cc._field +-	CC_DELTA(cycles, AR_CCCNT); +-	CC_DELTA(rx_frame, AR_RFCNT); +-	CC_DELTA(rx_clear, AR_RCCNT); +-	CC_DELTA(tx_frame, AR_TFCNT); +-#undef CC_DELTA +- +-	ah->listen_time += (ah->cc.cycles - cc.cycles) - +-		 ((ah->cc.rx_frame - cc.rx_frame) + +-		  (ah->cc.tx_frame - cc.tx_frame)); +- +-skip: +-	if (clear) { +-		REG_WRITE(ah, AR_CCCNT, 0); +-		REG_WRITE(ah, AR_RFCNT, 0); +-		REG_WRITE(ah, AR_RCCNT, 0); +-		REG_WRITE(ah, AR_TFCNT, 0); +-		memset(&ah->cc, 0, sizeof(ah->cc)); +-	} +- +-	/* unfreeze counters */ +-	REG_WRITE(ah, AR_MIBC, 0); +-} +- + /* +  * Process a MIB interrupt.  We may potentially be invoked because +  * any of the MIB counters overflow/trigger so don't assume we're +--- a/drivers/net/wireless/ath/ath9k/ani.h ++++ b/drivers/net/wireless/ath/ath9k/ani.h +@@ -93,13 +93,6 @@ struct ath9k_mib_stats { + 	u32 beacons; + }; +  +-struct ath_cycle_counters { +-	u32 cycles; +-	u32 rx_frame; +-	u32 rx_clear; +-	u32 tx_frame; +-}; +- + /* INI default values for ANI registers */ + struct ath9k_ani_default { + 	u16 m1ThreshLow; +@@ -164,7 +157,6 @@ struct ar5416Stats { +  + void ath9k_enable_mib_counters(struct ath_hw *ah); + void ath9k_hw_disable_mib_counters(struct ath_hw *ah); +-void ath9k_hw_update_cycle_counters(struct ath_hw *ah); + void ath9k_hw_ani_setup(struct ath_hw *ah); + void ath9k_hw_ani_init(struct ath_hw *ah); + int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah, +--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c ++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c +@@ -1254,13 +1254,12 @@ void ar9003_hw_bb_watchdog_dbg_info(stru + 		  "** BB mode: BB_gen_controls=0x%08x **\n", + 		  REG_READ(ah, AR_PHY_GEN_CTRL)); +  +-	ath9k_hw_update_cycle_counters(ah); +-#define PCT(_field) (ah->cc_delta._field * 100 / ah->cc_delta.cycles) +-	if (ah->cc_delta.cycles) ++#define PCT(_field) (common->cc_survey._field * 100 / common->cc_survey.cycles) ++	if (common->cc_survey.cycles) + 		ath_print(common, ATH_DBG_RESET, + 			  "** BB busy times: rx_clear=%d%%, " + 			  "rx_frame=%d%%, tx_frame=%d%% **\n", +-			  PCT(rx_clear), PCT(rx_frame), PCT(tx_frame)); ++			  PCT(rx_busy), PCT(rx_frame), PCT(tx_frame)); +  + 	ath_print(common, ATH_DBG_RESET, + 		  "==== BB update: done ====\n\n"); +--- a/drivers/net/wireless/ath/ath9k/hw.h ++++ b/drivers/net/wireless/ath/ath9k/hw.h +@@ -740,8 +740,6 @@ struct ath_hw { + 	int coarse_low[5]; + 	int firpwr[5]; + 	enum ath9k_ani_cmd ani_function; +-	struct ath_cycle_counters cc, cc_delta; +-	int32_t listen_time; +  + 	/* Bluetooth coexistance */ + 	struct ath_btcoex_hw btcoex_hw; +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -400,6 +400,7 @@ void ath_ani_calibrate(unsigned long dat + 	bool aniflag = false; + 	unsigned int timestamp = jiffies_to_msecs(jiffies); + 	u32 cal_interval, short_cal_interval, long_cal_interval; ++	unsigned long flags; +  + 	if (ah->caldata && ah->caldata->nfcal_interference) + 		long_cal_interval = ATH_LONG_CALINTERVAL_INT; +@@ -450,8 +451,11 @@ void ath_ani_calibrate(unsigned long dat + 	/* Skip all processing if there's nothing to do. */ + 	if (longcal || shortcal || aniflag) { + 		/* Call ANI routine if necessary */ +-		if (aniflag) ++		if (aniflag) { ++			spin_lock_irqsave(&common->cc_lock, flags); + 			ath9k_hw_ani_monitor(ah, ah->curchan); ++			spin_unlock_irqrestore(&common->cc_lock, flags); ++		} +  + 		/* Perform calibration if necessary */ + 		if (longcal || shortcal) { +@@ -636,6 +640,7 @@ irqreturn_t ath_isr(int irq, void *dev) +  + 	struct ath_softc *sc = dev; + 	struct ath_hw *ah = sc->sc_ah; ++	struct ath_common *common = ath9k_hw_common(ah); + 	enum ath9k_int status; + 	bool sched = false; +  +@@ -685,7 +690,12 @@ irqreturn_t ath_isr(int irq, void *dev) +  + 	if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && + 	    (status & ATH9K_INT_BB_WATCHDOG)) { ++ ++		spin_lock(&common->cc_lock); ++		ath_hw_cycle_counters_update(common); + 		ar9003_hw_bb_watchdog_dbg_info(ah); ++		spin_unlock(&common->cc_lock); ++ + 		goto chip_reset; + 	} +  +--- a/drivers/net/wireless/ath/ath9k/reg.h ++++ b/drivers/net/wireless/ath/ath9k/reg.h +@@ -107,12 +107,6 @@ + #define AR_RXCFG_DMASZ_256B  6 + #define AR_RXCFG_DMASZ_512B  7 +  +-#define AR_MIBC              0x0040 +-#define AR_MIBC_COW          0x00000001 +-#define AR_MIBC_FMC          0x00000002 +-#define AR_MIBC_CMC          0x00000004 +-#define AR_MIBC_MCS          0x00000008 +- + #define AR_TOPS              0x0044 + #define AR_TOPS_MASK         0x0000FFFF +  +@@ -1524,11 +1518,6 @@ enum { + #define AR_TPC_CHIRP           0x003f0000 + #define AR_TPC_CHIRP_S         0x16 +  +-#define AR_TFCNT           0x80ec +-#define AR_RFCNT           0x80f0 +-#define AR_RCCNT           0x80f4 +-#define AR_CCCNT           0x80f8 +- + #define AR_QUIET1          0x80fc + #define AR_QUIET1_NEXT_QUIET_S         0 + #define AR_QUIET1_NEXT_QUIET_M         0x0000ffff +--- a/drivers/net/wireless/ath/hw.c ++++ b/drivers/net/wireless/ath/hw.c +@@ -124,3 +124,62 @@ void ath_hw_setbssidmask(struct ath_comm + 	REG_WRITE(ah, get_unaligned_le16(common->bssidmask + 4), AR_BSSMSKU); + } + EXPORT_SYMBOL(ath_hw_setbssidmask); ++ ++ ++/** ++ * ath_hw_cycle_counters_update - common function to update cycle counters ++ * ++ * @common: the ath_common struct for the device. ++ * ++ * This function is used to update all cycle counters in one place. ++ * It has to be called while holding common->cc_lock! ++ */ ++void ath_hw_cycle_counters_update(struct ath_common *common) ++{ ++	u32 cycles, busy, rx, tx; ++	void *ah = common->ah; ++ ++	/* freeze */ ++	REG_WRITE(ah, AR_MIBC_FMC, AR_MIBC); ++ ++	/* read */ ++	cycles = REG_READ(ah, AR_CCCNT); ++	busy = REG_READ(ah, AR_RCCNT); ++	rx = REG_READ(ah, AR_RFCNT); ++	tx = REG_READ(ah, AR_TFCNT); ++ ++	/* clear */ ++	REG_WRITE(ah, 0, AR_CCCNT); ++	REG_WRITE(ah, 0, AR_RFCNT); ++	REG_WRITE(ah, 0, AR_RCCNT); ++	REG_WRITE(ah, 0, AR_TFCNT); ++ ++	/* unfreeze */ ++	REG_WRITE(ah, 0, AR_MIBC); ++ ++	/* update all cycle counters here */ ++	common->cc_ani.cycles += cycles; ++	common->cc_ani.rx_busy += busy; ++	common->cc_ani.rx_frame += rx; ++	common->cc_ani.tx_frame += tx; ++ ++	common->cc_survey.cycles += cycles; ++	common->cc_survey.rx_busy += busy; ++	common->cc_survey.rx_frame += rx; ++	common->cc_survey.tx_frame += tx; ++} ++EXPORT_SYMBOL(ath_hw_cycle_counters_update); ++ ++int32_t ath_hw_get_listen_time(struct ath_common *common) ++{ ++	struct ath_cycle_counters *cc = &common->cc_ani; ++	int32_t listen_time; ++ ++	listen_time = (cc->cycles - cc->rx_frame - cc->tx_frame) / ++		      (common->clockrate * 1000); ++ ++	memset(cc, 0, sizeof(*cc)); ++ ++	return listen_time; ++} ++EXPORT_SYMBOL(ath_hw_get_listen_time); +--- a/drivers/net/wireless/ath/reg.h ++++ b/drivers/net/wireless/ath/reg.h +@@ -17,6 +17,12 @@ + #ifndef ATH_REGISTERS_H + #define ATH_REGISTERS_H +  ++#define AR_MIBC			0x0040 ++#define AR_MIBC_COW		0x00000001 ++#define AR_MIBC_FMC		0x00000002 ++#define AR_MIBC_CMC		0x00000004 ++#define AR_MIBC_MCS		0x00000008 ++ + /* +  * BSSID mask registers. See ath_hw_set_bssid_mask() +  * for detailed documentation about these registers. +@@ -24,6 +30,11 @@ + #define AR_BSSMSKL		0x80e0 + #define AR_BSSMSKU		0x80e4 +  ++#define AR_TFCNT		0x80ec ++#define AR_RFCNT		0x80f0 ++#define AR_RCCNT		0x80f4 ++#define AR_CCCNT		0x80f8 ++ + #define AR_KEYTABLE_0           0x8800 + #define AR_KEYTABLE(_n)         (AR_KEYTABLE_0 + ((_n)*32)) + #define AR_KEY_CACHE_SIZE       128 diff --git a/package/mac80211/patches/523-ath5k_use_common_counters.patch b/package/mac80211/patches/523-ath5k_use_common_counters.patch new file mode 100644 index 000000000..1899f8d46 --- /dev/null +++ b/package/mac80211/patches/523-ath5k_use_common_counters.patch @@ -0,0 +1,104 @@ +--- a/drivers/net/wireless/ath/ath5k/ani.c ++++ b/drivers/net/wireless/ath/ath5k/ani.c +@@ -355,41 +355,28 @@ ath5k_ani_lower_immunity(struct ath5k_hw +  +  + /** +- * ath5k_hw_ani_get_listen_time() - Calculate time spent listening ++ * ath5k_hw_ani_get_listen_time() - Update counters and return listening time +  * +  * Return an approximation of the time spent "listening" in milliseconds (ms) +- * since the last call of this function by deducting the cycles spent +- * transmitting and receiving from the total cycle count. +- * Save profile count values for debugging/statistics and because we might want +- * to use them later. +- * +- * We assume no one else clears these registers! ++ * since the last call of this function. ++ * Save a snapshot of the counter values for debugging/statistics. +  */ + static int + ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as) + { ++	struct ath_common *common = ath5k_hw_common(ah); + 	int listen; +  +-	/* freeze */ +-	ath5k_hw_reg_write(ah, AR5K_MIBC_FMC, AR5K_MIBC); +-	/* read */ +-	as->pfc_cycles = ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE); +-	as->pfc_busy = ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR); +-	as->pfc_tx = ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX); +-	as->pfc_rx = ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX); +-	/* clear */ +-	ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_TX); +-	ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RX); +-	ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR); +-	ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE); +-	/* un-freeze */ +-	ath5k_hw_reg_write(ah, 0, AR5K_MIBC); ++	spin_lock_bh(&common->cc_lock); +  +-	/* TODO: where does 44000 come from? (11g clock rate?) */ +-	listen = (as->pfc_cycles - as->pfc_rx - as->pfc_tx) / 44000; ++	ath_hw_cycle_counters_update(common); ++	memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc)); ++ ++	/* clears common->cc_ani */ ++	listen = ath_hw_get_listen_time(common); ++ ++	spin_unlock_bh(&common->cc_lock); +  +-	if (as->pfc_cycles == 0 || listen < 0) +-		return 0; + 	return listen; + } +  +--- a/drivers/net/wireless/ath/ath5k/ani.h ++++ b/drivers/net/wireless/ath/ath5k/ani.h +@@ -75,10 +75,7 @@ struct ath5k_ani_state { + 	unsigned int		cck_errors; +  + 	/* debug/statistics only: numbers from last ANI calibration */ +-	unsigned int		pfc_tx; +-	unsigned int		pfc_rx; +-	unsigned int		pfc_busy; +-	unsigned int		pfc_cycles; ++	struct ath_cycle_counters last_cc; + 	unsigned int		last_listen; + 	unsigned int		last_ofdm_errors; + 	unsigned int		last_cck_errors; +--- a/drivers/net/wireless/ath/ath5k/debug.c ++++ b/drivers/net/wireless/ath/ath5k/debug.c +@@ -715,20 +715,21 @@ static ssize_t read_file_ani(struct file + 	len += snprintf(buf+len, sizeof(buf)-len, + 			"beacon RSSI average:\t%d\n", + 			sc->ah->ah_beacon_rssi_avg.avg); ++ ++#define CC_PRINT(_struct, _field) \ ++	_struct._field, \ ++	_struct.cycles > 0 ? \ ++	_struct._field*100/_struct.cycles : 0 ++ + 	len += snprintf(buf+len, sizeof(buf)-len, "profcnt tx\t\t%u\t(%d%%)\n", +-			as->pfc_tx, +-			as->pfc_cycles > 0 ? +-			as->pfc_tx*100/as->pfc_cycles : 0); ++			CC_PRINT(as->last_cc, tx_frame)); + 	len += snprintf(buf+len, sizeof(buf)-len, "profcnt rx\t\t%u\t(%d%%)\n", +-			as->pfc_rx, +-			as->pfc_cycles > 0 ? +-			as->pfc_rx*100/as->pfc_cycles : 0); ++			CC_PRINT(as->last_cc, rx_frame)); + 	len += snprintf(buf+len, sizeof(buf)-len, "profcnt busy\t\t%u\t(%d%%)\n", +-			as->pfc_busy, +-			as->pfc_cycles > 0 ? +-			as->pfc_busy*100/as->pfc_cycles : 0); ++			CC_PRINT(as->last_cc, rx_busy)); ++#undef CC_PRINT + 	len += snprintf(buf+len, sizeof(buf)-len, "profcnt cycles\t\t%u\n", +-			as->pfc_cycles); ++			as->last_cc.cycles); + 	len += snprintf(buf+len, sizeof(buf)-len, + 			"listen time\t\t%d\tlast: %d\n", + 			as->listen_time, as->last_listen); diff --git a/package/mac80211/patches/524-mac80211_survey_channel_stats.patch b/package/mac80211/patches/524-mac80211_survey_channel_stats.patch new file mode 100644 index 000000000..b05484f76 --- /dev/null +++ b/package/mac80211/patches/524-mac80211_survey_channel_stats.patch @@ -0,0 +1,105 @@ +--- a/include/linux/nl80211.h ++++ b/include/linux/nl80211.h +@@ -1413,6 +1413,16 @@ enum nl80211_reg_rule_flags { +  * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel +  * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) +  * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used ++ * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio ++ *	spent on this channel ++ * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary ++ *	channel was sensed busy (either due to activity or energy detect) ++ * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension ++ *	channel was sensed busy ++ * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent ++ *	receiving data ++ * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent ++ *	transmitting data +  * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number +  *	currently defined +  * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use +@@ -1422,6 +1432,11 @@ enum nl80211_survey_info { + 	NL80211_SURVEY_INFO_FREQUENCY, + 	NL80211_SURVEY_INFO_NOISE, + 	NL80211_SURVEY_INFO_IN_USE, ++	NL80211_SURVEY_INFO_CHANNEL_TIME, ++	NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, ++	NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, ++	NL80211_SURVEY_INFO_CHANNEL_TIME_RX, ++	NL80211_SURVEY_INFO_CHANNEL_TIME_TX, +  + 	/* keep last */ + 	__NL80211_SURVEY_INFO_AFTER_LAST, +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -295,6 +295,11 @@ struct key_params { +  * +  * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in +  * @SURVEY_INFO_IN_USE: channel is currently being used ++ * @SURVEY_INFO_CHANNEL_TIME: channel active time (in ms) was filled in ++ * @SURVEY_INFO_CHANNEL_TIME_BUSY: channel busy time was filled in ++ * @SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: extension channel busy time was filled in ++ * @SURVEY_INFO_CHANNEL_TIME_RX: channel receive time was filled in ++ * @SURVEY_INFO_CHANNEL_TIME_TX: channel transmit time was filled in +  * +  * Used by the driver to indicate which info in &struct survey_info +  * it has filled in during the get_survey(). +@@ -302,6 +307,11 @@ struct key_params { + enum survey_info_flags { + 	SURVEY_INFO_NOISE_DBM = 1<<0, + 	SURVEY_INFO_IN_USE = 1<<1, ++	SURVEY_INFO_CHANNEL_TIME = 1<<2, ++	SURVEY_INFO_CHANNEL_TIME_BUSY = 1<<3, ++	SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 1<<4, ++	SURVEY_INFO_CHANNEL_TIME_RX = 1<<5, ++	SURVEY_INFO_CHANNEL_TIME_TX = 1<<6, + }; +  + /** +@@ -311,6 +321,11 @@ enum survey_info_flags { +  * @filled: bitflag of flags from &enum survey_info_flags +  * @noise: channel noise in dBm. This and all following fields are +  *     optional ++ * @channel_time: amount of time in ms the radio spent on the channel ++ * @channel_time_busy: amount of time the primary channel was sensed busy ++ * @channel_time_ext_busy: amount of time the extension channel was sensed busy ++ * @channel_time_rx: amount of time the radio spent receiving data ++ * @channel_time_tx: amount of time the radio spent transmitting data +  * +  * Used by dump_survey() to report back per-channel survey information. +  * +@@ -319,6 +334,11 @@ enum survey_info_flags { +  */ + struct survey_info { + 	struct ieee80211_channel *channel; ++	u64 channel_time; ++	u64 channel_time_busy; ++	u64 channel_time_ext_busy; ++	u64 channel_time_rx; ++	u64 channel_time_tx; + 	u32 filled; + 	s8 noise; + }; +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -3176,6 +3176,21 @@ static int nl80211_send_survey(struct sk + 			    survey->noise); + 	if (survey->filled & SURVEY_INFO_IN_USE) + 		NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE); ++	if (survey->filled & SURVEY_INFO_CHANNEL_TIME) ++		NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME, ++			    survey->channel_time); ++	if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) ++		NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, ++			    survey->channel_time_busy); ++	if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) ++		NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, ++			    survey->channel_time_ext_busy); ++	if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) ++		NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, ++			    survey->channel_time_rx); ++	if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) ++		NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, ++			    survey->channel_time_tx); +  + 	nla_nest_end(msg, infoattr); +  diff --git a/package/mac80211/patches/525-ath9k_channel_count_check.patch b/package/mac80211/patches/525-ath9k_channel_count_check.patch new file mode 100644 index 000000000..ae743934b --- /dev/null +++ b/package/mac80211/patches/525-ath9k_channel_count_check.patch @@ -0,0 +1,33 @@ +--- a/drivers/net/wireless/ath/ath9k/hw.h ++++ b/drivers/net/wireless/ath/ath9k/hw.h +@@ -61,6 +61,8 @@ +  + #define ATH9K_RSSI_BAD			-128 +  ++#define ATH9K_NUM_CHANNELS	38 ++ + /* Register read/write primitives */ + #define REG_WRITE(_ah, _reg, _val) \ + 	ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg)) +@@ -618,7 +620,7 @@ struct ath_hw { + 	struct ath9k_hw_version hw_version; + 	struct ath9k_ops_config config; + 	struct ath9k_hw_capabilities caps; +-	struct ath9k_channel channels[38]; ++	struct ath9k_channel channels[ATH9K_NUM_CHANNELS]; + 	struct ath9k_channel *curchan; +  + 	union { +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -482,6 +482,10 @@ static int ath9k_init_channels_rates(str + { + 	void *channels; +  ++	BUILD_BUG_ON(ARRAY_SIZE(ath9k_2ghz_chantable) + ++		     ARRAY_SIZE(ath9k_5ghz_chantable) != ++		     ATH9K_NUM_CHANNELS); ++ + 	if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) { + 		channels = kmemdup(ath9k_2ghz_chantable, + 			sizeof(ath9k_2ghz_chantable), GFP_KERNEL); diff --git a/package/mac80211/patches/526-ath9k_survey_channel_stats.patch b/package/mac80211/patches/526-ath9k_survey_channel_stats.patch new file mode 100644 index 000000000..842dc812a --- /dev/null +++ b/package/mac80211/patches/526-ath9k_survey_channel_stats.patch @@ -0,0 +1,165 @@ +--- a/drivers/net/wireless/ath/ath9k/ath9k.h ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h +@@ -594,6 +594,8 @@ struct ath_softc { + 	struct delayed_work wiphy_work; + 	unsigned long wiphy_scheduler_int; + 	int wiphy_scheduler_index; ++	struct survey_info *cur_survey; ++	struct survey_info survey[ATH9K_NUM_CHANNELS]; +  + 	struct tasklet_struct intr_tq; + 	struct tasklet_struct bcon_tasklet; +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -176,6 +176,49 @@ static void ath_start_ani(struct ath_com + 			msecs_to_jiffies((u32)ah->config.ani_poll_interval)); + } +  ++static void ath_update_survey_nf(struct ath_softc *sc, int channel) ++{ ++	struct ath_hw *ah = sc->sc_ah; ++	struct ath9k_channel *chan = &ah->channels[channel]; ++	struct survey_info *survey = &sc->survey[channel]; ++ ++	if (chan->noisefloor) { ++		survey->filled |= SURVEY_INFO_NOISE_DBM; ++		survey->noise = chan->noisefloor; ++	} ++} ++ ++static void ath_update_survey_stats(struct ath_softc *sc) ++{ ++	struct ath_hw *ah = sc->sc_ah; ++	struct ath_common *common = ath9k_hw_common(ah); ++	int pos = ah->curchan - &ah->channels[0]; ++	struct survey_info *survey = &sc->survey[pos]; ++	struct ath_cycle_counters *cc = &common->cc_survey; ++	unsigned long flags; ++	unsigned int div = common->clockrate * 1000; ++ ++	spin_lock_irqsave(&common->cc_lock, flags); ++ ++	ath_hw_cycle_counters_update(common); ++ ++	if (cc->cycles > 0) { ++		survey->filled |= SURVEY_INFO_CHANNEL_TIME | ++			SURVEY_INFO_CHANNEL_TIME_BUSY | ++			SURVEY_INFO_CHANNEL_TIME_RX | ++			SURVEY_INFO_CHANNEL_TIME_TX; ++		survey->channel_time += cc->cycles / div; ++		survey->channel_time_busy += cc->rx_busy / div; ++		survey->channel_time_rx += cc->rx_frame / div; ++		survey->channel_time_tx += cc->tx_frame / div; ++	} ++	memset(cc, 0, sizeof(*cc)); ++ ++	ath_update_survey_nf(sc, pos); ++ ++	spin_unlock_irqrestore(&common->cc_lock, flags); ++} ++ + /* +  * Set/change channels.  If the channel is really being changed, it's done +  * by reseting the chip.  To accomplish this we must first cleanup any pending +@@ -1533,7 +1576,8 @@ static int ath9k_config(struct ieee80211 + { + 	struct ath_wiphy *aphy = hw->priv; + 	struct ath_softc *sc = aphy->sc; +-	struct ath_common *common = ath9k_hw_common(sc->sc_ah); ++	struct ath_hw *ah = sc->sc_ah; ++	struct ath_common *common = ath9k_hw_common(ah); + 	struct ieee80211_conf *conf = &hw->conf; + 	bool disable_radio; +  +@@ -1599,6 +1643,10 @@ static int ath9k_config(struct ieee80211 + 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + 		struct ieee80211_channel *curchan = hw->conf.channel; + 		int pos = curchan->hw_value; ++		int old_pos = -1; ++ ++		if (ah->curchan) ++			old_pos = ah->curchan - &ah->channels[0]; +  + 		aphy->chan_idx = pos; + 		aphy->chan_is_ht = conf_is_ht(conf); +@@ -1626,12 +1674,43 @@ static int ath9k_config(struct ieee80211 +  + 		ath_update_chainmask(sc, conf_is_ht(conf)); +  ++		/* update survey stats for the old channel before switching */ ++		ath_update_survey_stats(sc); ++ ++		/* ++		 * If the operating channel changes, change the survey in-use flags ++		 * along with it. ++		 * Reset the survey data for the new channel, unless we're switching ++		 * back to the operating channel from an off-channel operation. ++		 */ ++		if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && ++		    sc->cur_survey != &sc->survey[pos]) { ++ ++			if (sc->cur_survey) ++				sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; ++ ++			sc->cur_survey = &sc->survey[pos]; ++ ++			memset(sc->cur_survey, 0, sizeof(struct survey_info)); ++			sc->cur_survey->filled |= SURVEY_INFO_IN_USE; ++		} else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { ++			memset(&sc->survey[pos], 0, sizeof(struct survey_info)); ++		} ++ + 		if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { + 			ath_print(common, ATH_DBG_FATAL, + 				  "Unable to set channel\n"); + 			mutex_unlock(&sc->mutex); + 			return -EINVAL; + 		} ++ ++		/* ++		 * The most recent snapshot of channel->noisefloor for the old ++		 * channel is only available after the hardware reset. Copy it to ++		 * the survey stats now. ++		 */ ++		if (old_pos >= 0) ++			ath_update_survey_nf(sc, old_pos); + 	} +  + skip_chan_change: +@@ -2001,9 +2080,12 @@ static int ath9k_get_survey(struct ieee8 + { + 	struct ath_wiphy *aphy = hw->priv; + 	struct ath_softc *sc = aphy->sc; +-	struct ath_hw *ah = sc->sc_ah; + 	struct ieee80211_supported_band *sband; +-	struct ath9k_channel *chan; ++	struct ieee80211_channel *chan; ++	int pos; ++ ++	if (idx == 0) ++		ath_update_survey_stats(sc); +  + 	sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + 	if (sband && idx >= sband->n_channels) { +@@ -2017,17 +2099,10 @@ static int ath9k_get_survey(struct ieee8 + 	if (!sband || idx >= sband->n_channels) + 	    return -ENOENT; +  +-	survey->channel = &sband->channels[idx]; +-	chan = &ah->channels[survey->channel->hw_value]; +-	survey->filled = 0; +- +-	if (chan == ah->curchan) +-		survey->filled |= SURVEY_INFO_IN_USE; +- +-	if (chan->noisefloor) { +-		survey->filled |= SURVEY_INFO_NOISE_DBM; +-		survey->noise = chan->noisefloor; +-	} ++	chan = &sband->channels[idx]; ++	pos = chan->hw_value; ++	memcpy(survey, &sc->survey[pos], sizeof(*survey)); ++	survey->channel = chan; +  + 	return 0; + } | 
