Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / ht.c
index a71d891794a40ab96134ec16eef5568e8b5b3eca..af8cee06e4f37475ed17c764e55cf75c070ef9f2 100644 (file)
@@ -37,12 +37,8 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
        int i;
 
-       if (sdata->vif.type != NL80211_IFTYPE_STATION) {
-               /* AP interfaces call this code when adding new stations,
-                * so just silently ignore non station interfaces.
-                */
+       if (!ht_cap->ht_supported)
                return;
-       }
 
        /* NOTE:  If you add more over-rides here, update register_hw
         * ht_capa_mod_msk logic in main.c as well.
@@ -62,6 +58,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
        __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
        __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40);
 
+       /* Allow user to disable SGI-20 (SGI-40 is handled above) */
+       __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20);
+
        /* Allow user to disable the max-AMSDU bit. */
        __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU);
 
@@ -86,22 +85,36 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
 }
 
 
-void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
+bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
                                       struct ieee80211_supported_band *sband,
-                                      struct ieee80211_ht_cap *ht_cap_ie,
-                                      struct ieee80211_sta_ht_cap *ht_cap)
+                                      const struct ieee80211_ht_cap *ht_cap_ie,
+                                      struct sta_info *sta)
 {
+       struct ieee80211_sta_ht_cap ht_cap, own_cap;
        u8 ampdu_info, tx_mcs_set_cap;
        int i, max_tx_streams;
+       bool changed;
+       enum ieee80211_sta_rx_bandwidth bw;
+       enum ieee80211_smps_mode smps_mode;
 
-       BUG_ON(!ht_cap);
-
-       memset(ht_cap, 0, sizeof(*ht_cap));
+       memset(&ht_cap, 0, sizeof(ht_cap));
 
        if (!ht_cap_ie || !sband->ht_cap.ht_supported)
-               return;
+               goto apply;
 
-       ht_cap->ht_supported = true;
+       ht_cap.ht_supported = true;
+
+       own_cap = sband->ht_cap;
+
+       /*
+        * If user has specified capability over-rides, take care
+        * of that if the station we're setting up is the AP that
+        * we advertised a restricted capability set to. Override
+        * our own capabilities and then use those below.
+        */
+       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+           !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               ieee80211_apply_htcap_overrides(sdata, &own_cap);
 
        /*
         * The bits listed in this expression should be
@@ -109,38 +122,38 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
         * advertises more then we can't use those thus
         * we mask them out.
         */
-       ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) &
-               (sband->ht_cap.cap |
-                ~(IEEE80211_HT_CAP_LDPC_CODING |
-                  IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
-                  IEEE80211_HT_CAP_GRN_FLD |
-                  IEEE80211_HT_CAP_SGI_20 |
-                  IEEE80211_HT_CAP_SGI_40 |
-                  IEEE80211_HT_CAP_DSSSCCK40));
+       ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &
+               (own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING |
+                                IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+                                IEEE80211_HT_CAP_GRN_FLD |
+                                IEEE80211_HT_CAP_SGI_20 |
+                                IEEE80211_HT_CAP_SGI_40 |
+                                IEEE80211_HT_CAP_DSSSCCK40));
+
        /*
         * The STBC bits are asymmetric -- if we don't have
         * TX then mask out the peer's RX and vice versa.
         */
-       if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
-               ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC;
-       if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC))
-               ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC;
+       if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC))
+               ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;
+       if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC))
+               ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
 
        ampdu_info = ht_cap_ie->ampdu_params_info;
-       ht_cap->ampdu_factor =
+       ht_cap.ampdu_factor =
                ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
-       ht_cap->ampdu_density =
+       ht_cap.ampdu_density =
                (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
 
        /* own MCS TX capabilities */
-       tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
+       tx_mcs_set_cap = own_cap.mcs.tx_params;
 
        /* Copy peer MCS TX capabilities, the driver might need them. */
-       ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params;
+       ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;
 
        /* can we TX with MCS rates? */
        if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
-               return;
+               goto apply;
 
        /* Counting from 0, therefore +1 */
        if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
@@ -158,37 +171,84 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
         * - remainder are multiple spatial streams using unequal modulation
         */
        for (i = 0; i < max_tx_streams; i++)
-               ht_cap->mcs.rx_mask[i] =
-                       sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
+               ht_cap.mcs.rx_mask[i] =
+                       own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
 
        if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
                for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
                     i < IEEE80211_HT_MCS_MASK_LEN; i++)
-                       ht_cap->mcs.rx_mask[i] =
-                               sband->ht_cap.mcs.rx_mask[i] &
+                       ht_cap.mcs.rx_mask[i] =
+                               own_cap.mcs.rx_mask[i] &
                                        ht_cap_ie->mcs.rx_mask[i];
 
        /* handle MCS rate 32 too */
-       if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
-               ht_cap->mcs.rx_mask[32/8] |= 1;
+       if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
+               ht_cap.mcs.rx_mask[32/8] |= 1;
 
-       /*
-        * If user has specified capability over-rides, take care
-        * of that here.
-        */
-       ieee80211_apply_htcap_overrides(sdata, ht_cap);
+ apply:
+       changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
+
+       memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
+
+       switch (sdata->vif.bss_conf.chandef.width) {
+       default:
+               WARN_ON_ONCE(1);
+               /* fall through */
+       case NL80211_CHAN_WIDTH_20_NOHT:
+       case NL80211_CHAN_WIDTH_20:
+               bw = IEEE80211_STA_RX_BW_20;
+               break;
+       case NL80211_CHAN_WIDTH_40:
+       case NL80211_CHAN_WIDTH_80:
+       case NL80211_CHAN_WIDTH_80P80:
+       case NL80211_CHAN_WIDTH_160:
+               bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
+                               IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
+               break;
+       }
+
+       if (bw != sta->sta.bandwidth)
+               changed = true;
+       sta->sta.bandwidth = bw;
+
+       sta->cur_max_bandwidth =
+               ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
+                               IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
+
+       switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS)
+                       >> IEEE80211_HT_CAP_SM_PS_SHIFT) {
+       case WLAN_HT_CAP_SM_PS_INVALID:
+       case WLAN_HT_CAP_SM_PS_STATIC:
+               smps_mode = IEEE80211_SMPS_STATIC;
+               break;
+       case WLAN_HT_CAP_SM_PS_DYNAMIC:
+               smps_mode = IEEE80211_SMPS_DYNAMIC;
+               break;
+       case WLAN_HT_CAP_SM_PS_DISABLED:
+               smps_mode = IEEE80211_SMPS_OFF;
+               break;
+       }
+
+       if (smps_mode != sta->sta.smps_mode)
+               changed = true;
+       sta->sta.smps_mode = smps_mode;
+
+       return changed;
 }
 
-void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx)
+void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
+                                        enum ieee80211_agg_stop_reason reason)
 {
        int i;
 
        cancel_work_sync(&sta->ampdu_mlme.work);
 
        for (i = 0; i <  IEEE80211_NUM_TIDS; i++) {
-               __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx);
+               __ieee80211_stop_tx_ba_session(sta, i, reason);
                __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
-                                              WLAN_REASON_QSTA_LEAVE_QBSS, tx);
+                                              WLAN_REASON_QSTA_LEAVE_QBSS,
+                                              reason != AGG_STOP_DESTROY_STA &&
+                                              reason != AGG_STOP_PEER_REQUEST);
        }
 }
 
@@ -245,8 +305,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
                if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
                                                 &tid_tx->state))
                        ___ieee80211_stop_tx_ba_session(sta, tid,
-                                                       WLAN_BACK_INITIATOR,
-                                                       true);
+                                                       AGG_STOP_LOCAL_REQUEST);
        }
        mutex_unlock(&sta->ampdu_mlme.mtx);
 }
@@ -314,8 +373,7 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
                __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0,
                                               true);
        else
-               __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
-                                              true);
+               __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST);
 }
 
 int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
@@ -387,6 +445,9 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
        if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
+       if (sdata->u.mgd.driver_smps_mode == smps_mode)
+               return;
+
        sdata->u.mgd.driver_smps_mode = smps_mode;
 
        ieee80211_queue_work(&sdata->local->hw,