ath9k: Fix Notice of Absence issues
authorSujith Manoharan <c_manoha@qca.qualcomm.com>
Wed, 10 Sep 2014 13:45:57 +0000 (19:15 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 11 Sep 2014 19:27:39 +0000 (15:27 -0400)
* The index has to incremented only when advertising
  a new NoA schedule.

* Switch to non-periodic NoA when starting a scan operation
  and multiple channel contexts are active.

* Make sure that periodic NoA is advertised again when
  scan ends. Since the offchannel timer moves the offchannel
  state to IDLE after the GO operating channel becomes
  active, use a flag "force_noa_update" to update the
  NoA contents.

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

index 0c071b094010c4932a595d2a313e9b6c28e4887d..305db1a6889a1e64853c7cea5b6d4babee3f9ff2 100644 (file)
@@ -379,6 +379,7 @@ struct ath_chanctx_sched {
        bool beacon_pending;
        bool offchannel_pending;
        bool wait_switch;
+       bool force_noa_update;
        enum ath_chanctx_state state;
        u8 beacon_miss;
 
@@ -595,8 +596,10 @@ struct ath_vif {
        u32 offchannel_start;
        u32 offchannel_duration;
 
-       u32 periodic_noa_start;
-       u32 periodic_noa_duration;
+       /* These are used for both periodic and one-shot */
+       u32 noa_start;
+       u32 noa_duration;
+       bool periodic_noa;
 };
 
 struct ath9k_vif_iter_data {
index ae575cb44e5a5c13729bb7c0760c980eddf82e2e..ab3d29d2ee9f1a4a4661282849d964293b3d29c9 100644 (file)
@@ -310,7 +310,6 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
        struct ath_chanctx *ctx;
        u32 tsf_time;
        u32 beacon_int;
-       bool noa_changed = false;
 
        if (vif)
                avp = (struct ath_vif *) vif->drv_priv;
@@ -372,22 +371,6 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
                sc->sched.switch_start_time = tsf_time;
                sc->cur_chan->last_beacon = sc->sched.next_tbtt;
 
-               /* Prevent wrap-around issues */
-               if (avp->periodic_noa_duration &&
-                   tsf_time - avp->periodic_noa_start > BIT(30))
-                       avp->periodic_noa_duration = 0;
-
-               if (ctx->active) {
-                       avp->periodic_noa_start = tsf_time;
-                       avp->periodic_noa_duration =
-                               TU_TO_USEC(cur_conf->beacon_interval) / 2 -
-                               sc->sched.channel_switch_time;
-                       noa_changed = true;
-               } else if (!ctx->active) {
-                       avp->periodic_noa_duration = 0;
-                       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.
@@ -395,21 +378,65 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
                if (ctx->active && sc->sched.beacon_miss >= 2)
                        sc->sched.offchannel_duration = 3 * beacon_int / 2;
 
-               if (sc->sched.offchannel_duration) {
-                       noa_changed = true;
+               /*
+                * If an offchannel switch is scheduled to happen after
+                * a beacon transmission, update the NoA with one-shot
+                * values and increment the index.
+                */
+               if (sc->next_chan == &sc->offchannel.chan) {
+                       avp->noa_index++;
                        avp->offchannel_start = tsf_time;
-                       avp->offchannel_duration =
-                               sc->sched.offchannel_duration;
+                       avp->offchannel_duration = sc->sched.offchannel_duration;
+
+                       ath_dbg(common, CHAN_CTX,
+                               "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n",
+                               avp->offchannel_duration,
+                               avp->offchannel_start,
+                               avp->noa_index);
+
+                       /*
+                        * When multiple contexts are active, the NoA
+                        * has to be recalculated and advertised after
+                        * an offchannel operation.
+                        */
+                       if (ctx->active && avp->noa_duration)
+                               avp->noa_duration = 0;
+
+                       break;
                }
 
-               if (noa_changed)
+               /* Prevent wrap-around issues */
+               if (avp->noa_duration && tsf_time - avp->noa_start > BIT(30))
+                       avp->noa_duration = 0;
+
+               /*
+                * If multiple contexts are active, start periodic
+                * NoA and increment the index for the first
+                * announcement.
+                */
+               if (ctx->active &&
+                   (!avp->noa_duration || sc->sched.force_noa_update)) {
                        avp->noa_index++;
+                       avp->noa_start = tsf_time;
+                       avp->noa_duration =
+                               TU_TO_USEC(cur_conf->beacon_interval) / 2 -
+                               sc->sched.channel_switch_time;
 
-               ath_dbg(common, CHAN_CTX,
-                       "periodic_noa_duration: %d, periodic_noa_start: %d, noa_index: %d\n",
-                       avp->periodic_noa_duration,
-                       avp->periodic_noa_start,
-                       avp->noa_index);
+                       if (test_bit(ATH_OP_SCANNING, &common->op_flags))
+                               avp->periodic_noa = false;
+                       else
+                               avp->periodic_noa = true;
+
+                       ath_dbg(common, CHAN_CTX,
+                               "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
+                               avp->noa_duration,
+                               avp->noa_start,
+                               avp->noa_index,
+                               avp->periodic_noa);
+               }
+
+               if (ctx->active && sc->sched.force_noa_update)
+                       sc->sched.force_noa_update = false;
 
                break;
        case ATH_CHANCTX_EVENT_BEACON_SENT:
@@ -736,6 +763,10 @@ void ath_scan_complete(struct ath_softc *sc, bool abort)
        sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
        ieee80211_scan_completed(sc->hw, abort);
        clear_bit(ATH_OP_SCANNING, &common->op_flags);
+       spin_lock_bh(&sc->chan_lock);
+       if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+               sc->sched.force_noa_update = true;
+       spin_unlock_bh(&sc->chan_lock);
        ath_offchannel_next(sc);
        ath9k_ps_restore(sc);
 }
@@ -1218,10 +1249,10 @@ void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
        int noa_len, noa_desc, i = 0;
        u8 *hdr;
 
-       if (!avp->offchannel_duration && !avp->periodic_noa_duration)
+       if (!avp->offchannel_duration && !avp->noa_duration)
                return;
 
-       noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration;
+       noa_desc = !!avp->offchannel_duration + !!avp->noa_duration;
        noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc;
 
        hdr = skb_put(skb, sizeof(noa_ie_hdr));
@@ -1235,13 +1266,17 @@ void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
        noa->index = avp->noa_index;
        noa->oppps_ctwindow = ath9k_get_ctwin(sc, avp);
 
-       if (avp->periodic_noa_duration) {
-               u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
+       if (avp->noa_duration) {
+               if (avp->periodic_noa) {
+                       u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
+                       noa->desc[i].count = 255;
+                       noa->desc[i].interval = cpu_to_le32(interval);
+               } else {
+                       noa->desc[i].count = 1;
+               }
 
-               noa->desc[i].count = 255;
-               noa->desc[i].start_time = cpu_to_le32(avp->periodic_noa_start);
-               noa->desc[i].duration = cpu_to_le32(avp->periodic_noa_duration);
-               noa->desc[i].interval = cpu_to_le32(interval);
+               noa->desc[i].start_time = cpu_to_le32(avp->noa_start);
+               noa->desc[i].duration = cpu_to_le32(avp->noa_duration);
                i++;
        }