Commit ec70abe1 authored by Felix Fietkau's avatar Felix Fietkau Committed by John W. Linville

ath9k: Handle beacon miss on multi channel context

Signed-off-by: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarRajkumar Manoharan <rmanohar@qti.qualcomm.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 7414863e
...@@ -368,6 +368,7 @@ struct ath_chanctx_sched { ...@@ -368,6 +368,7 @@ struct ath_chanctx_sched {
bool beacon_pending; bool beacon_pending;
bool offchannel_pending; bool offchannel_pending;
enum ath_chanctx_state state; enum ath_chanctx_state state;
u8 beacon_miss;
u32 next_tbtt; u32 next_tbtt;
u32 switch_start_time; u32 switch_start_time;
......
...@@ -478,6 +478,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, ...@@ -478,6 +478,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
struct ath_vif *avp = NULL; struct ath_vif *avp = NULL;
struct ath_chanctx *ctx; struct ath_chanctx *ctx;
u32 tsf_time; u32 tsf_time;
u32 beacon_int;
bool noa_changed = false; bool noa_changed = false;
if (vif) if (vif)
...@@ -516,9 +517,10 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *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); sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER);
cur_conf = &sc->cur_chan->beacon; cur_conf = &sc->cur_chan->beacon;
beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
/* defer channel switch by a quarter 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 + beacon_int / 4;
tsf_time = sc->sched.next_tbtt + tsf_time / 4;
sc->sched.switch_start_time = tsf_time; sc->sched.switch_start_time = tsf_time;
sc->cur_chan->last_beacon = sc->sched.next_tbtt; 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, ...@@ -538,6 +540,13 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
noa_changed = true; 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) { if (sc->sched.offchannel_duration) {
noa_changed = true; noa_changed = true;
avp->offchannel_start = tsf_time; avp->offchannel_start = tsf_time;
...@@ -565,6 +574,10 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, ...@@ -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) if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
break; break;
if (!sc->cur_chan->switch_after_beacon &&
sc->sched.beacon_pending)
sc->sched.beacon_miss++;
sc->sched.state = ATH_CHANCTX_STATE_SWITCH; sc->sched.state = ATH_CHANCTX_STATE_SWITCH;
ieee80211_queue_work(sc->hw, &sc->chanctx_work); ieee80211_queue_work(sc->hw, &sc->chanctx_work);
break; break;
...@@ -574,6 +587,8 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, ...@@ -574,6 +587,8 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
break; break;
ath_chanctx_adjust_tbtt_delta(sc); ath_chanctx_adjust_tbtt_delta(sc);
sc->sched.beacon_pending = false;
sc->sched.beacon_miss = 0;
break; break;
case ATH_CHANCTX_EVENT_ASSOC: case ATH_CHANCTX_EVENT_ASSOC:
if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || 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, ...@@ -596,13 +611,20 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
cur_conf = &sc->cur_chan->beacon; cur_conf = &sc->cur_chan->beacon;
sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; 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 -= sc->sched.channel_switch_time;
tsf_time += ath9k_hw_gettsf32(sc->sc_ah);
sc->sched.switch_start_time = tsf_time; sc->sched.switch_start_time = tsf_time;
ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer,
tsf_time, 1000000); tsf_time, 1000000);
sc->sched.beacon_pending = true;
break; break;
case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL: case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
if (sc->cur_chan == &sc->offchannel.chan || if (sc->cur_chan == &sc->offchannel.chan ||
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment