From bbf2a906b6bde84e2b76d220c5f30ec3b52232fc Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 9 Oct 2010 00:36:54 +0000 Subject: [PATCH] ath9k: implement extended channel utilization statistics via survey SVN-Revision: 23350 --- .../patches/520-ath9k_common_clockrate.patch | 116 ++++++ .../patches/521-ath5k_common_clockrate.patch | 65 ++++ .../patches/522-ath_common_counters.patch | 347 ++++++++++++++++++ .../523-ath5k_use_common_counters.patch | 104 ++++++ .../524-mac80211_survey_channel_stats.patch | 105 ++++++ .../525-ath9k_channel_count_check.patch | 33 ++ .../526-ath9k_survey_channel_stats.patch | 165 +++++++++ 7 files changed, 935 insertions(+) create mode 100644 package/mac80211/patches/520-ath9k_common_clockrate.patch create mode 100644 package/mac80211/patches/521-ath5k_common_clockrate.patch create mode 100644 package/mac80211/patches/522-ath_common_counters.patch create mode 100644 package/mac80211/patches/523-ath5k_use_common_counters.patch create mode 100644 package/mac80211/patches/524-mac80211_survey_channel_stats.patch create mode 100644 package/mac80211/patches/525-ath9k_channel_count_check.patch create mode 100644 package/mac80211/patches/526-ath9k_survey_channel_stats.patch 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 0000000000..3266e0e103 --- /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 0000000000..08c18342e7 --- /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 0000000000..f719575264 --- /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 + #include ++#include + #include + + /* +@@ -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 0000000000..1899f8d465 --- /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 0000000000..b05484f769 --- /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 0000000000..ae743934bd --- /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 0000000000..842dc812a0 --- /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; + }