summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2014-06-11 16:18:12 +0530
committerJohn W. Linville <linville@tuxdriver.com>2014-06-19 15:49:20 -0400
commitec70abe1f62099f8cdd5453e20098e15435706bd (patch)
treeef5044f13f4d0d839682f7a1cbdf0d5a90a759b0 /drivers
parent7414863ed3dfa407006c92616c1e0efda481738c (diff)
ath9k: Handle beacon miss on multi channel context
Signed-off-by: Felix Fietkau <nbd@openwrt.org> Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/channel.c30
2 files changed, 27 insertions, 4 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index a5afd4a7df9f..ee00ddb8885d 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -368,6 +368,7 @@ struct ath_chanctx_sched {
bool beacon_pending;
bool offchannel_pending;
enum ath_chanctx_state state;
+ u8 beacon_miss;
u32 next_tbtt;
u32 switch_start_time;
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 3d9776c4c909..55165d5a7ed1 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -478,6 +478,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
struct ath_vif *avp = NULL;
struct ath_chanctx *ctx;
u32 tsf_time;
+ u32 beacon_int;
bool noa_changed = false;
if (vif)
@@ -516,9 +517,10 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER);
cur_conf = &sc->cur_chan->beacon;
+ beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
+
/* defer channel switch by a quarter beacon interval */
- tsf_time = TU_TO_USEC(cur_conf->beacon_interval);
- tsf_time = sc->sched.next_tbtt + tsf_time / 4;
+ tsf_time = sc->sched.next_tbtt + beacon_int / 4;
sc->sched.switch_start_time = tsf_time;
sc->cur_chan->last_beacon = sc->sched.next_tbtt;
@@ -538,6 +540,13 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
noa_changed = true;
}
+ /* If at least two consecutive beacons were missed on the STA
+ * chanctx, stay on the STA channel for one extra beacon period,
+ * to resync the timer properly.
+ */
+ if (ctx->active && sc->sched.beacon_miss >= 2)
+ sc->sched.offchannel_duration = 3 * beacon_int / 2;
+
if (sc->sched.offchannel_duration) {
noa_changed = true;
avp->offchannel_start = tsf_time;
@@ -565,6 +574,10 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
break;
+ if (!sc->cur_chan->switch_after_beacon &&
+ sc->sched.beacon_pending)
+ sc->sched.beacon_miss++;
+
sc->sched.state = ATH_CHANCTX_STATE_SWITCH;
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
break;
@@ -574,6 +587,8 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
break;
ath_chanctx_adjust_tbtt_delta(sc);
+ sc->sched.beacon_pending = false;
+ sc->sched.beacon_miss = 0;
break;
case ATH_CHANCTX_EVENT_ASSOC:
if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE ||
@@ -596,13 +611,20 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
cur_conf = &sc->cur_chan->beacon;
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
- tsf_time = ath9k_hw_gettsf32(sc->sc_ah);
- tsf_time += TU_TO_USEC(cur_conf->beacon_interval) / 2;
+
+ tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2;
+ if (sc->sched.beacon_miss >= 2) {
+ sc->sched.beacon_miss = 0;
+ tsf_time *= 3;
+ }
+
tsf_time -= sc->sched.channel_switch_time;
+ tsf_time += ath9k_hw_gettsf32(sc->sc_ah);
sc->sched.switch_start_time = tsf_time;
ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer,
tsf_time, 1000000);
+ sc->sched.beacon_pending = true;
break;
case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
if (sc->cur_chan == &sc->offchannel.chan ||