diff options
author | Felix Fietkau <nbd@openwrt.org> | 2014-06-11 06:48:10 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-06-19 15:49:20 -0400 |
commit | 73fa2f26d35a37034fdff9fd702887909e138926 (patch) | |
tree | fd000e8a11a3b78596628f6b49b415826d4288e4 /drivers/net/wireless | |
parent | 6036c2845650d26a15b44498f8fb8f8f4518847a (diff) |
ath9k: Add multi-channel scheduling support
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/net/wireless')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ath9k.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/channel.c | 95 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 3 |
3 files changed, 99 insertions, 4 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 7947909918bd..0bc63bd4ec26 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h | |||
@@ -350,6 +350,10 @@ enum ath_chanctx_event { | |||
350 | ATH_CHANCTX_EVENT_BEACON_SENT, | 350 | ATH_CHANCTX_EVENT_BEACON_SENT, |
351 | ATH_CHANCTX_EVENT_TSF_TIMER, | 351 | ATH_CHANCTX_EVENT_TSF_TIMER, |
352 | ATH_CHANCTX_EVENT_BEACON_RECEIVED, | 352 | ATH_CHANCTX_EVENT_BEACON_RECEIVED, |
353 | ATH_CHANCTX_EVENT_ASSOC, | ||
354 | ATH_CHANCTX_EVENT_SWITCH, | ||
355 | ATH_CHANCTX_EVENT_UNASSIGN, | ||
356 | ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL, | ||
353 | }; | 357 | }; |
354 | 358 | ||
355 | enum ath_chanctx_state { | 359 | enum ath_chanctx_state { |
@@ -362,6 +366,7 @@ enum ath_chanctx_state { | |||
362 | 366 | ||
363 | struct ath_chanctx_sched { | 367 | struct ath_chanctx_sched { |
364 | bool beacon_pending; | 368 | bool beacon_pending; |
369 | bool offchannel_pending; | ||
365 | enum ath_chanctx_state state; | 370 | enum ath_chanctx_state state; |
366 | 371 | ||
367 | u32 next_tbtt; | 372 | u32 next_tbtt; |
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 8d56b7961d7b..1cb2909a114c 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c | |||
@@ -180,10 +180,13 @@ void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) | |||
180 | n_active++; | 180 | n_active++; |
181 | } | 181 | } |
182 | 182 | ||
183 | if (n_active > 1) | 183 | if (n_active <= 1) { |
184 | set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); | ||
185 | else | ||
186 | clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); | 184 | clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags); |
185 | return; | ||
186 | } | ||
187 | if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) | ||
188 | return; | ||
189 | ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL); | ||
187 | } | 190 | } |
188 | 191 | ||
189 | static bool | 192 | static bool |
@@ -282,6 +285,7 @@ static void ath_chanctx_set_next(struct ath_softc *sc, bool force) | |||
282 | ath_chanctx_send_ps_frame(sc, false); | 285 | ath_chanctx_send_ps_frame(sc, false); |
283 | 286 | ||
284 | ath_offchannel_channel_change(sc); | 287 | ath_offchannel_channel_change(sc); |
288 | ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH); | ||
285 | } | 289 | } |
286 | 290 | ||
287 | void ath_chanctx_work(struct work_struct *work) | 291 | void ath_chanctx_work(struct work_struct *work) |
@@ -357,8 +361,17 @@ void ath9k_chanctx_force_active(struct ieee80211_hw *hw, | |||
357 | void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, | 361 | void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, |
358 | struct cfg80211_chan_def *chandef) | 362 | struct cfg80211_chan_def *chandef) |
359 | { | 363 | { |
364 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||
360 | 365 | ||
361 | spin_lock_bh(&sc->chan_lock); | 366 | spin_lock_bh(&sc->chan_lock); |
367 | |||
368 | if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) && | ||
369 | (sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) { | ||
370 | sc->sched.offchannel_pending = true; | ||
371 | spin_unlock_bh(&sc->chan_lock); | ||
372 | return; | ||
373 | } | ||
374 | |||
362 | sc->next_chan = ctx; | 375 | sc->next_chan = ctx; |
363 | if (chandef) | 376 | if (chandef) |
364 | ctx->chandef = *chandef; | 377 | ctx->chandef = *chandef; |
@@ -462,6 +475,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, | |||
462 | struct ath_hw *ah = sc->sc_ah; | 475 | struct ath_hw *ah = sc->sc_ah; |
463 | struct ath_common *common = ath9k_hw_common(ah); | 476 | struct ath_common *common = ath9k_hw_common(ah); |
464 | struct ath_vif *avp = NULL; | 477 | struct ath_vif *avp = NULL; |
478 | struct ath_chanctx *ctx; | ||
465 | u32 tsf_time; | 479 | u32 tsf_time; |
466 | bool noa_changed = false; | 480 | bool noa_changed = false; |
467 | 481 | ||
@@ -475,6 +489,25 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, | |||
475 | if (avp->offchannel_duration) | 489 | if (avp->offchannel_duration) |
476 | avp->offchannel_duration = 0; | 490 | avp->offchannel_duration = 0; |
477 | 491 | ||
492 | if (avp->chanctx != sc->cur_chan) | ||
493 | break; | ||
494 | |||
495 | if (sc->sched.offchannel_pending) { | ||
496 | sc->sched.offchannel_pending = false; | ||
497 | sc->next_chan = &sc->offchannel.chan; | ||
498 | sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; | ||
499 | } | ||
500 | |||
501 | ctx = ath_chanctx_get_next(sc, sc->cur_chan); | ||
502 | if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) { | ||
503 | sc->next_chan = ctx; | ||
504 | sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; | ||
505 | } | ||
506 | |||
507 | /* if the timer missed its window, use the next interval */ | ||
508 | if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER) | ||
509 | sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON; | ||
510 | |||
478 | if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) | 511 | if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) |
479 | break; | 512 | break; |
480 | 513 | ||
@@ -518,11 +551,65 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, | |||
518 | ieee80211_queue_work(sc->hw, &sc->chanctx_work); | 551 | ieee80211_queue_work(sc->hw, &sc->chanctx_work); |
519 | break; | 552 | break; |
520 | case ATH_CHANCTX_EVENT_BEACON_RECEIVED: | 553 | case ATH_CHANCTX_EVENT_BEACON_RECEIVED: |
521 | if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) | 554 | if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || |
555 | sc->cur_chan == &sc->offchannel.chan) | ||
522 | break; | 556 | break; |
523 | 557 | ||
524 | ath_chanctx_adjust_tbtt_delta(sc); | 558 | ath_chanctx_adjust_tbtt_delta(sc); |
525 | break; | 559 | break; |
560 | case ATH_CHANCTX_EVENT_ASSOC: | ||
561 | if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE || | ||
562 | avp->chanctx != sc->cur_chan) | ||
563 | break; | ||
564 | |||
565 | sc->sched.state = ATH_CHANCTX_STATE_IDLE; | ||
566 | /* fall through */ | ||
567 | case ATH_CHANCTX_EVENT_SWITCH: | ||
568 | if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || | ||
569 | sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE || | ||
570 | sc->cur_chan->switch_after_beacon || | ||
571 | sc->cur_chan == &sc->offchannel.chan) | ||
572 | break; | ||
573 | |||
574 | /* If this is a station chanctx, stay active for a half | ||
575 | * beacon period (minus channel switch time) | ||
576 | */ | ||
577 | sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); | ||
578 | |||
579 | sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER; | ||
580 | tsf_time = ath9k_hw_gettsf32(sc->sc_ah); | ||
581 | tsf_time += | ||
582 | TU_TO_USEC(sc->cur_chan->beacon.beacon_interval) / 2; | ||
583 | tsf_time -= sc->sched.channel_switch_time; | ||
584 | sc->sched.switch_start_time = tsf_time; | ||
585 | |||
586 | ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, | ||
587 | tsf_time, 1000000); | ||
588 | break; | ||
589 | case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL: | ||
590 | if (sc->cur_chan == &sc->offchannel.chan || | ||
591 | sc->cur_chan->switch_after_beacon) | ||
592 | break; | ||
593 | |||
594 | sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan); | ||
595 | ieee80211_queue_work(sc->hw, &sc->chanctx_work); | ||
596 | break; | ||
597 | case ATH_CHANCTX_EVENT_UNASSIGN: | ||
598 | if (sc->cur_chan->assigned) { | ||
599 | if (sc->next_chan && !sc->next_chan->assigned && | ||
600 | sc->next_chan != &sc->offchannel.chan) | ||
601 | sc->sched.state = ATH_CHANCTX_STATE_IDLE; | ||
602 | break; | ||
603 | } | ||
604 | |||
605 | ctx = ath_chanctx_get_next(sc, sc->cur_chan); | ||
606 | sc->sched.state = ATH_CHANCTX_STATE_IDLE; | ||
607 | if (!ctx->assigned) | ||
608 | break; | ||
609 | |||
610 | sc->next_chan = ctx; | ||
611 | ieee80211_queue_work(sc->hw, &sc->chanctx_work); | ||
612 | break; | ||
526 | } | 613 | } |
527 | 614 | ||
528 | spin_unlock_bh(&sc->chan_lock); | 615 | spin_unlock_bh(&sc->chan_lock); |
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index b8975f0700bf..f7d8ddae216e 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c | |||
@@ -1747,6 +1747,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, | |||
1747 | bss_conf->bssid, bss_conf->assoc); | 1747 | bss_conf->bssid, bss_conf->assoc); |
1748 | 1748 | ||
1749 | ath9k_calculate_summary_state(sc, avp->chanctx); | 1749 | ath9k_calculate_summary_state(sc, avp->chanctx); |
1750 | if (bss_conf->assoc) | ||
1751 | ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_ASSOC); | ||
1750 | } | 1752 | } |
1751 | 1753 | ||
1752 | if (changed & BSS_CHANGED_IBSS) { | 1754 | if (changed & BSS_CHANGED_IBSS) { |
@@ -2492,6 +2494,7 @@ static void ath9k_remove_chanctx(struct ieee80211_hw *hw, | |||
2492 | 2494 | ||
2493 | mutex_lock(&sc->mutex); | 2495 | mutex_lock(&sc->mutex); |
2494 | ctx->assigned = false; | 2496 | ctx->assigned = false; |
2497 | ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN); | ||
2495 | mutex_unlock(&sc->mutex); | 2498 | mutex_unlock(&sc->mutex); |
2496 | } | 2499 | } |
2497 | 2500 | ||