mac80211: Handle the CSA counters correctly
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / tx.c
index 509456e5722d1df4a8cfbfda20740efc70e061c5..5214686d9fd1ec9ab4bc1e2a466532bd3c829c10 100644 (file)
@@ -2416,13 +2416,14 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
-                                struct beacon_data *beacon)
+static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
+                             struct beacon_data *beacon)
 {
        struct probe_resp *resp;
        u8 *beacon_data;
        size_t beacon_data_len;
        int i;
+       u8 count = sdata->csa_current_counter;
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
@@ -2450,16 +2451,7 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
                        if (WARN_ON(counter_offset_beacon >= beacon_data_len))
                                return;
 
-                       /* Warn if the driver did not check for/react to csa
-                        * completeness.  A beacon with CSA counter set to 0
-                        * should never occur, because a counter of 1 means
-                        * switch just before the next beacon.
-                        */
-                       if (WARN_ON(beacon_data[counter_offset_beacon] == 1))
-                               return;
-
-                       beacon_data[counter_offset_beacon] =
-                               sdata->csa_current_counter - 1;
+                       beacon_data[counter_offset_beacon] = count;
                }
 
                if (sdata->vif.type == NL80211_IFTYPE_AP &&
@@ -2474,14 +2466,24 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
                                rcu_read_unlock();
                                return;
                        }
-                       resp->data[counter_offset_presp] =
-                               sdata->csa_current_counter - 1;
+                       resp->data[counter_offset_presp] = count;
                        rcu_read_unlock();
                }
        }
+}
+
+u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 
        sdata->csa_current_counter--;
+
+       /* the counter should never reach 0 */
+       WARN_ON(!sdata->csa_current_counter);
+
+       return sdata->csa_current_counter;
 }
+EXPORT_SYMBOL(ieee80211_csa_update_counter);
 
 bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
 {
@@ -2552,6 +2554,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
        enum ieee80211_band band;
        struct ieee80211_tx_rate_control txrc;
        struct ieee80211_chanctx_conf *chanctx_conf;
+       int csa_off_base = 0;
 
        rcu_read_lock();
 
@@ -2569,8 +2572,12 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                struct beacon_data *beacon = rcu_dereference(ap->beacon);
 
                if (beacon) {
-                       if (sdata->vif.csa_active)
-                               ieee80211_update_csa(sdata, beacon);
+                       if (sdata->vif.csa_active) {
+                               if (!is_template)
+                                       ieee80211_csa_update_counter(vif);
+
+                               ieee80211_set_csa(sdata, beacon);
+                       }
 
                        /*
                         * headroom, head length,
@@ -2593,6 +2600,9 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                        if (offs) {
                                offs->tim_offset = beacon->head_len;
                                offs->tim_length = skb->len - beacon->head_len;
+
+                               /* for AP the csa offsets are from tail */
+                               csa_off_base = skb->len;
                        }
 
                        if (beacon->tail)
@@ -2608,9 +2618,12 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                if (!presp)
                        goto out;
 
-               if (sdata->vif.csa_active)
-                       ieee80211_update_csa(sdata, presp);
+               if (sdata->vif.csa_active) {
+                       if (!is_template)
+                               ieee80211_csa_update_counter(vif);
 
+                       ieee80211_set_csa(sdata, presp);
+               }
 
                skb = dev_alloc_skb(local->tx_headroom + presp->head_len +
                                    local->hw.extra_beacon_tailroom);
@@ -2630,8 +2643,17 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                if (!bcn)
                        goto out;
 
-               if (sdata->vif.csa_active)
-                       ieee80211_update_csa(sdata, bcn);
+               if (sdata->vif.csa_active) {
+                       if (!is_template)
+                               /* TODO: For mesh csa_counter is in TU, so
+                                * decrementing it by one isn't correct, but
+                                * for now we leave it consistent with overall
+                                * mac80211's behavior.
+                                */
+                               ieee80211_csa_update_counter(vif);
+
+                       ieee80211_set_csa(sdata, bcn);
+               }
 
                if (ifmsh->sync_ops)
                        ifmsh->sync_ops->adjust_tbtt(sdata, bcn);
@@ -2658,6 +2680,20 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                goto out;
        }
 
+       /* CSA offsets */
+       if (offs) {
+               int i;
+
+               for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) {
+                       u16 csa_off = sdata->csa_counter_offset_beacon[i];
+
+                       if (!csa_off)
+                               continue;
+
+                       offs->csa_counter_offs[i] = csa_off_base + csa_off;
+               }
+       }
+
        band = chanctx_conf->def.chan->band;
 
        info = IEEE80211_SKB_CB(skb);