ath9k: Check for active GO in mgd_prepare_tx()
authorSujith Manoharan <c_manoha@qca.qualcomm.com>
Fri, 17 Oct 2014 02:10:22 +0000 (07:40 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 27 Oct 2014 18:16:16 +0000 (14:16 -0400)
If a GO interface is active when we receive a
mgd_prepare_tx() call, then we need to send
out a new NoA before switching to a new context.

Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/channel.c
drivers/net/wireless/ath/ath9k/main.c

index 8f6f46c..b7abb4f 100644 (file)
@@ -385,6 +385,7 @@ struct ath_chanctx_sched {
        bool wait_switch;
        bool force_noa_update;
        bool extend_absence;
+       bool mgd_prepare_tx;
        enum ath_chanctx_state state;
        u8 beacon_miss;
 
@@ -977,6 +978,7 @@ struct ath_softc {
        struct ath_chanctx_sched sched;
        struct ath_offchannel offchannel;
        struct ath_chanctx *next_chan;
+       struct completion go_beacon;
 #endif
 
        unsigned long driver_data;
index 2066b09..45d2c48 100644 (file)
@@ -421,6 +421,9 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
                                "Move chanctx state from WAIT_FOR_TIMER to WAIT_FOR_BEACON\n");
                }
 
+               if (sc->sched.mgd_prepare_tx)
+                       sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+
                /*
                 * When a context becomes inactive, for example,
                 * disassociation of a station context, the NoA
@@ -547,6 +550,15 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
                }
 
                sc->sched.beacon_pending = false;
+
+               if (sc->sched.mgd_prepare_tx) {
+                       sc->sched.mgd_prepare_tx = false;
+                       complete(&sc->go_beacon);
+                       ath_dbg(common, CHAN_CTX,
+                               "Beacon sent, complete go_beacon\n");
+                       break;
+               }
+
                if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
                        break;
 
@@ -1263,6 +1275,8 @@ void ath9k_init_channel_context(struct ath_softc *sc)
                    (unsigned long)sc);
        setup_timer(&sc->sched.timer, ath_chanctx_timer,
                    (unsigned long)sc);
+
+       init_completion(&sc->go_beacon);
 }
 
 void ath9k_deinit_channel_context(struct ath_softc *sc)
index c291d54..f58781a 100644 (file)
@@ -2474,7 +2474,11 @@ static void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw,
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_vif *avp = (struct ath_vif *) vif->drv_priv;
+       struct ath_beacon_config *cur_conf;
+       struct ath_chanctx *go_ctx;
+       unsigned long timeout;
        bool changed = false;
+       u32 beacon_int;
 
        if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
                return;
@@ -2485,19 +2489,46 @@ static void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw,
        mutex_lock(&sc->mutex);
 
        spin_lock_bh(&sc->chan_lock);
-       if (sc->next_chan || (sc->cur_chan != avp->chanctx)) {
-               sc->next_chan = avp->chanctx;
+       if (sc->next_chan || (sc->cur_chan != avp->chanctx))
                changed = true;
+       spin_unlock_bh(&sc->chan_lock);
+
+       if (!changed)
+               goto out;
+
+       go_ctx = ath_is_go_chanctx_present(sc);
+
+       if (go_ctx) {
+               /*
+                * Wait till the GO interface gets a chance
+                * to send out an NoA.
+                */
+               spin_lock_bh(&sc->chan_lock);
+               sc->sched.mgd_prepare_tx = true;
+               cur_conf = &go_ctx->beacon;
+               beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
+               spin_unlock_bh(&sc->chan_lock);
+
+               timeout = usecs_to_jiffies(beacon_int);
+               init_completion(&sc->go_beacon);
+
+               if (wait_for_completion_timeout(&sc->go_beacon,
+                                               timeout) == 0)
+                       ath_dbg(common, CHAN_CTX,
+                               "Failed to send new NoA\n");
        }
+
        ath_dbg(common, CHAN_CTX,
-               "%s: Set chanctx state to FORCE_ACTIVE, changed: %d\n",
-               __func__, changed);
+               "%s: Set chanctx state to FORCE_ACTIVE for vif: %pM\n",
+               __func__, vif->addr);
+
+       spin_lock_bh(&sc->chan_lock);
+       sc->next_chan = avp->chanctx;
        sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE;
        spin_unlock_bh(&sc->chan_lock);
 
-       if (changed)
-               ath_chanctx_set_next(sc, true);
-
+       ath_chanctx_set_next(sc, true);
+out:
        mutex_unlock(&sc->mutex);
 }