mirror of https://github.com/hak5/openwrt-owl.git
ath9k: implement extended channel utilization statistics via survey
SVN-Revision: 23350owl
parent
cd771e9bc5
commit
bbf2a906b6
|
@ -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;
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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);
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue