These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / mac80211 / cfg.c
index f06d422..c12f348 100644 (file)
@@ -2,7 +2,7 @@
  * mac80211 configuration hooks for cfg80211
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright 2013-2015  Intel Mobile Communications GmbH
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -17,7 +17,6 @@
 #include <net/cfg80211.h>
 #include "ieee80211_i.h"
 #include "driver-ops.h"
-#include "cfg.h"
 #include "rate.h"
 #include "mesh.h"
 #include "wme.h"
@@ -137,6 +136,9 @@ static int ieee80211_set_noack_map(struct wiphy *wiphy,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
        sdata->noack_map = noack_map;
+
+       ieee80211_check_fast_xmit_iface(sdata);
+
        return 0;
 }
 
@@ -309,6 +311,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        u32 iv32;
        u16 iv16;
        int err = -ENOENT;
+       struct ieee80211_key_seq kseq = {};
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -339,10 +342,12 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                iv32 = key->u.tkip.tx.iv32;
                iv16 = key->u.tkip.tx.iv16;
 
-               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
-                       drv_get_tkip_seq(sdata->local,
-                                        key->conf.hw_key_idx,
-                                        &iv32, &iv16);
+               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
+                   !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
+                       drv_get_key_seq(sdata->local, key, &kseq);
+                       iv32 = kseq.tkip.iv32;
+                       iv16 = kseq.tkip.iv16;
+               }
 
                seq[0] = iv16 & 0xff;
                seq[1] = (iv16 >> 8) & 0xff;
@@ -355,52 +360,44 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                break;
        case WLAN_CIPHER_SUITE_CCMP:
        case WLAN_CIPHER_SUITE_CCMP_256:
-               pn64 = atomic64_read(&key->u.ccmp.tx_pn);
-               seq[0] = pn64;
-               seq[1] = pn64 >> 8;
-               seq[2] = pn64 >> 16;
-               seq[3] = pn64 >> 24;
-               seq[4] = pn64 >> 32;
-               seq[5] = pn64 >> 40;
-               params.seq = seq;
-               params.seq_len = 6;
-               break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
        case WLAN_CIPHER_SUITE_BIP_CMAC_256:
-               pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
-               seq[0] = pn64;
-               seq[1] = pn64 >> 8;
-               seq[2] = pn64 >> 16;
-               seq[3] = pn64 >> 24;
-               seq[4] = pn64 >> 32;
-               seq[5] = pn64 >> 40;
-               params.seq = seq;
-               params.seq_len = 6;
-               break;
+               BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
+                            offsetof(typeof(kseq), aes_cmac));
        case WLAN_CIPHER_SUITE_BIP_GMAC_128:
        case WLAN_CIPHER_SUITE_BIP_GMAC_256:
-               pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
-               seq[0] = pn64;
-               seq[1] = pn64 >> 8;
-               seq[2] = pn64 >> 16;
-               seq[3] = pn64 >> 24;
-               seq[4] = pn64 >> 32;
-               seq[5] = pn64 >> 40;
-               params.seq = seq;
-               params.seq_len = 6;
-               break;
+               BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
+                            offsetof(typeof(kseq), aes_gmac));
        case WLAN_CIPHER_SUITE_GCMP:
        case WLAN_CIPHER_SUITE_GCMP_256:
-               pn64 = atomic64_read(&key->u.gcmp.tx_pn);
-               seq[0] = pn64;
-               seq[1] = pn64 >> 8;
-               seq[2] = pn64 >> 16;
-               seq[3] = pn64 >> 24;
-               seq[4] = pn64 >> 32;
-               seq[5] = pn64 >> 40;
+               BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
+                            offsetof(typeof(kseq), gcmp));
+
+               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
+                   !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
+                       drv_get_key_seq(sdata->local, key, &kseq);
+                       memcpy(seq, kseq.ccmp.pn, 6);
+               } else {
+                       pn64 = atomic64_read(&key->conf.tx_pn);
+                       seq[0] = pn64;
+                       seq[1] = pn64 >> 8;
+                       seq[2] = pn64 >> 16;
+                       seq[3] = pn64 >> 24;
+                       seq[4] = pn64 >> 32;
+                       seq[5] = pn64 >> 40;
+               }
                params.seq = seq;
                params.seq_len = 6;
                break;
+       default:
+               if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+                       break;
+               if (WARN_ON(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
+                       break;
+               drv_get_key_seq(sdata->local, key, &kseq);
+               params.seq = kseq.hw.seq;
+               params.seq_len = kseq.hw.seq_len;
+               break;
        }
 
        params.key = key->conf.key;
@@ -471,45 +468,6 @@ void sta_set_rate_info_tx(struct sta_info *sta,
                rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
 }
 
-void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
-{
-       rinfo->flags = 0;
-
-       if (sta->last_rx_rate_flag & RX_FLAG_HT) {
-               rinfo->flags |= RATE_INFO_FLAGS_MCS;
-               rinfo->mcs = sta->last_rx_rate_idx;
-       } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
-               rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
-               rinfo->nss = sta->last_rx_rate_vht_nss;
-               rinfo->mcs = sta->last_rx_rate_idx;
-       } else {
-               struct ieee80211_supported_band *sband;
-               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
-               u16 brate;
-
-               sband = sta->local->hw.wiphy->bands[
-                               ieee80211_get_sdata_band(sta->sdata)];
-               brate = sband->bitrates[sta->last_rx_rate_idx].bitrate;
-               rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
-       }
-
-       if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
-               rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
-
-       if (sta->last_rx_rate_flag & RX_FLAG_5MHZ)
-               rinfo->bw = RATE_INFO_BW_5;
-       else if (sta->last_rx_rate_flag & RX_FLAG_10MHZ)
-               rinfo->bw = RATE_INFO_BW_10;
-       else if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
-               rinfo->bw = RATE_INFO_BW_40;
-       else if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)
-               rinfo->bw = RATE_INFO_BW_80;
-       else if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)
-               rinfo->bw = RATE_INFO_BW_160;
-       else
-               rinfo->bw = RATE_INFO_BW_20;
-}
-
 static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
                                  int idx, u8 *mac, struct station_info *sinfo)
 {
@@ -983,7 +941,7 @@ static int sta_apply_auth_flags(struct ieee80211_local *local,
                 * well. Some drivers require rate control initialized
                 * before drv_sta_state() is called.
                 */
-               if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               if (!test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
                        rate_control_rate_init(sta);
 
                ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
@@ -1021,6 +979,65 @@ static int sta_apply_auth_flags(struct ieee80211_local *local,
        return 0;
 }
 
+static void sta_apply_mesh_params(struct ieee80211_local *local,
+                                 struct sta_info *sta,
+                                 struct station_parameters *params)
+{
+#ifdef CONFIG_MAC80211_MESH
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       u32 changed = 0;
+
+       if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
+               switch (params->plink_state) {
+               case NL80211_PLINK_ESTAB:
+                       if (sta->mesh->plink_state != NL80211_PLINK_ESTAB)
+                               changed = mesh_plink_inc_estab_count(sdata);
+                       sta->mesh->plink_state = params->plink_state;
+
+                       ieee80211_mps_sta_status_update(sta);
+                       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                     sdata->u.mesh.mshcfg.power_mode);
+                       break;
+               case NL80211_PLINK_LISTEN:
+               case NL80211_PLINK_BLOCKED:
+               case NL80211_PLINK_OPN_SNT:
+               case NL80211_PLINK_OPN_RCVD:
+               case NL80211_PLINK_CNF_RCVD:
+               case NL80211_PLINK_HOLDING:
+                       if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
+                               changed = mesh_plink_dec_estab_count(sdata);
+                       sta->mesh->plink_state = params->plink_state;
+
+                       ieee80211_mps_sta_status_update(sta);
+                       changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                       NL80211_MESH_POWER_UNKNOWN);
+                       break;
+               default:
+                       /*  nothing  */
+                       break;
+               }
+       }
+
+       switch (params->plink_action) {
+       case NL80211_PLINK_ACTION_NO_ACTION:
+               /* nothing */
+               break;
+       case NL80211_PLINK_ACTION_OPEN:
+               changed |= mesh_plink_open(sta);
+               break;
+       case NL80211_PLINK_ACTION_BLOCK:
+               changed |= mesh_plink_block(sta);
+               break;
+       }
+
+       if (params->local_pm)
+               changed |= ieee80211_mps_set_sta_local_pm(sta,
+                                                         params->local_pm);
+
+       ieee80211_mbss_info_change_notify(sdata, changed);
+#endif
+}
+
 static int sta_apply_parameters(struct ieee80211_local *local,
                                struct sta_info *sta,
                                struct station_parameters *params)
@@ -1063,8 +1080,11 @@ static int sta_apply_parameters(struct ieee80211_local *local,
            local->hw.queues >= IEEE80211_NUM_ACS)
                sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
 
-       /* auth flags will be set later for TDLS stations */
-       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+       /* auth flags will be set later for TDLS,
+        * and for unassociated stations that move to assocaited */
+       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           !((mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
+             (set & BIT(NL80211_STA_FLAG_ASSOCIATED)))) {
                ret = sta_apply_auth_flags(local, sta, mask, set);
                if (ret)
                        return ret;
@@ -1099,6 +1119,13 @@ static int sta_apply_parameters(struct ieee80211_local *local,
            params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
                set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
 
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           !sdata->u.mgd.tdls_wider_bw_prohibited &&
+           ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
+           params->ext_capab_len >= 8 &&
+           params->ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED)
+               set_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW);
+
        if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
                sta->sta.uapsd_queues = params->uapsd_queues;
                sta->sta.max_sp = params->max_sp;
@@ -1142,69 +1169,15 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                 * rc isn't initialized here yet, so ignore it
                 */
                __ieee80211_vht_handle_opmode(sdata, sta,
-                                             params->opmode_notif,
-                                             band, false);
+                                             params->opmode_notif, band);
        }
 
-       if (ieee80211_vif_is_mesh(&sdata->vif)) {
-#ifdef CONFIG_MAC80211_MESH
-               u32 changed = 0;
-
-               if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
-                       switch (params->plink_state) {
-                       case NL80211_PLINK_ESTAB:
-                               if (sta->plink_state != NL80211_PLINK_ESTAB)
-                                       changed = mesh_plink_inc_estab_count(
-                                                       sdata);
-                               sta->plink_state = params->plink_state;
-
-                               ieee80211_mps_sta_status_update(sta);
-                               changed |= ieee80211_mps_set_sta_local_pm(sta,
-                                             sdata->u.mesh.mshcfg.power_mode);
-                               break;
-                       case NL80211_PLINK_LISTEN:
-                       case NL80211_PLINK_BLOCKED:
-                       case NL80211_PLINK_OPN_SNT:
-                       case NL80211_PLINK_OPN_RCVD:
-                       case NL80211_PLINK_CNF_RCVD:
-                       case NL80211_PLINK_HOLDING:
-                               if (sta->plink_state == NL80211_PLINK_ESTAB)
-                                       changed = mesh_plink_dec_estab_count(
-                                                       sdata);
-                               sta->plink_state = params->plink_state;
-
-                               ieee80211_mps_sta_status_update(sta);
-                               changed |= ieee80211_mps_set_sta_local_pm(sta,
-                                               NL80211_MESH_POWER_UNKNOWN);
-                               break;
-                       default:
-                               /*  nothing  */
-                               break;
-                       }
-               }
-
-               switch (params->plink_action) {
-               case NL80211_PLINK_ACTION_NO_ACTION:
-                       /* nothing */
-                       break;
-               case NL80211_PLINK_ACTION_OPEN:
-                       changed |= mesh_plink_open(sta);
-                       break;
-               case NL80211_PLINK_ACTION_BLOCK:
-                       changed |= mesh_plink_block(sta);
-                       break;
-               }
-
-               if (params->local_pm)
-                       changed |=
-                             ieee80211_mps_set_sta_local_pm(sta,
-                                                            params->local_pm);
-               ieee80211_mbss_info_change_notify(sdata, changed);
-#endif
-       }
+       if (ieee80211_vif_is_mesh(&sdata->vif))
+               sta_apply_mesh_params(local, sta, params);
 
        /* set the STA state after all sta info from usermode has been set */
-       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
+           set & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
                ret = sta_apply_auth_flags(local, sta, mask, set);
                if (ret)
                        return ret;
@@ -1246,12 +1219,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
         * defaults -- if userspace wants something else we'll
         * change it accordingly in sta_apply_parameters()
         */
-       if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {
+       if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
+           !(params->sta_flags_set & (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                                       BIT(NL80211_STA_FLAG_ASSOCIATED)))) {
                sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
                sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
-       } else {
-               sta->sta.tdls = true;
        }
+       if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               sta->sta.tdls = true;
 
        err = sta_apply_parameters(local, sta, params);
        if (err) {
@@ -1260,10 +1235,12 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        }
 
        /*
-        * for TDLS, rate control should be initialized only when
-        * rates are known and station is marked authorized
+        * for TDLS and for unassociated station, rate control should be
+        * initialized only when rates are known and station is marked
+        * authorized/associated
         */
-       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+           test_sta_flag(sta, WLAN_STA_ASSOC))
                rate_control_rate_init(sta);
 
        layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
@@ -1338,7 +1315,10 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                break;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
-               statype = CFG80211_STA_AP_CLIENT;
+               if (test_sta_flag(sta, WLAN_STA_ASSOC))
+                       statype = CFG80211_STA_AP_CLIENT;
+               else
+                       statype = CFG80211_STA_AP_CLIENT_UNASSOC;
                break;
        default:
                err = -EOPNOTSUPP;
@@ -1372,6 +1352,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                }
 
                sta->sdata = vlansdata;
+               ieee80211_check_fast_xmit(sta);
 
                if (sta->sta_state == IEEE80211_STA_AUTHORIZED &&
                    prev_4addr != new_4addr) {
@@ -1406,7 +1387,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
            params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
-               ieee80211_recalc_ps(local, -1);
+               ieee80211_recalc_ps(local);
                ieee80211_recalc_ps_vif(sdata);
        }
 
@@ -1764,7 +1745,7 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
                /* our RSSI threshold implementation is supported only for
                 * devices that report signal in dBm.
                 */
-               if (!(sdata->local->hw.flags & IEEE80211_HW_SIGNAL_DBM))
+               if (!ieee80211_hw_check(&sdata->local->hw, SIGNAL_DBM))
                        return -ENOTSUPP;
                conf->rssi_threshold = nconf->rssi_threshold;
        }
@@ -2028,12 +2009,12 @@ ieee80211_sched_scan_start(struct wiphy *wiphy,
 static int
 ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = wiphy_priv(wiphy);
 
-       if (!sdata->local->ops->sched_scan_stop)
+       if (!local->ops->sched_scan_stop)
                return -EOPNOTSUPP;
 
-       return ieee80211_request_sched_scan_stop(sdata);
+       return ieee80211_request_sched_scan_stop(local);
 }
 
 static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
@@ -2099,10 +2080,14 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
        int err;
 
        if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+               ieee80211_check_fast_xmit_all(local);
+
                err = drv_set_frag_threshold(local, wiphy->frag_threshold);
 
-               if (err)
+               if (err) {
+                       ieee80211_check_fast_xmit_all(local);
                        return err;
+               }
        }
 
        if ((changed & WIPHY_PARAM_COVERAGE_CLASS) ||
@@ -2355,6 +2340,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
        const u8 *ap;
        enum ieee80211_smps_mode old_req;
        int err;
+       struct sta_info *sta;
+       bool tdls_peer_found = false;
 
        lockdep_assert_held(&sdata->wdev.mtx);
 
@@ -2379,11 +2366,22 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
 
        ap = sdata->u.mgd.associated->bssid;
 
+       rcu_read_lock();
+       list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
+               if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
+                   !test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       continue;
+
+               tdls_peer_found = true;
+               break;
+       }
+       rcu_read_unlock();
+
        if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
-               if (sdata->u.mgd.powersave)
-                       smps_mode = IEEE80211_SMPS_DYNAMIC;
-               else
+               if (tdls_peer_found || !sdata->u.mgd.powersave)
                        smps_mode = IEEE80211_SMPS_OFF;
+               else
+                       smps_mode = IEEE80211_SMPS_DYNAMIC;
        }
 
        /* send SM PS frame to AP */
@@ -2391,6 +2389,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
                                         ap, ap);
        if (err)
                sdata->u.mgd.req_smps = old_req;
+       else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
+               ieee80211_teardown_tdls_peers(sdata);
 
        return err;
 }
@@ -2404,7 +2404,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
        if (sdata->vif.type != NL80211_IFTYPE_STATION)
                return -EOPNOTSUPP;
 
-       if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
+       if (!ieee80211_hw_check(&local->hw, SUPPORTS_PS))
                return -EOPNOTSUPP;
 
        if (enabled == sdata->u.mgd.powersave &&
@@ -2419,10 +2419,10 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
        __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
        sdata_unlock(sdata);
 
-       if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
+       if (ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS))
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
 
-       ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_ps(local);
        ieee80211_recalc_ps_vif(sdata);
 
        return 0;
@@ -2440,8 +2440,13 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
            rssi_hyst == bss_conf->cqm_rssi_hyst)
                return 0;
 
+       if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER &&
+           !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
+               return -EOPNOTSUPP;
+
        bss_conf->cqm_rssi_thold = rssi_thold;
        bss_conf->cqm_rssi_hyst = rssi_hyst;
+       sdata->u.mgd.last_cqm_event_signal = 0;
 
        /* tell the driver upon association, unless already associated */
        if (sdata->u.mgd.associated &&
@@ -2463,7 +2468,7 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
        if (!ieee80211_sdata_running(sdata))
                return -ENETDOWN;
 
-       if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) {
+       if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
                ret = drv_set_bitrate_mask(local, sdata, mask);
                if (ret)
                        return ret;
@@ -2476,16 +2481,28 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
                sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
                memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
                       sizeof(mask->control[i].ht_mcs));
+               memcpy(sdata->rc_rateidx_vht_mcs_mask[i],
+                      mask->control[i].vht_mcs,
+                      sizeof(mask->control[i].vht_mcs));
 
                sdata->rc_has_mcs_mask[i] = false;
+               sdata->rc_has_vht_mcs_mask[i] = false;
                if (!sband)
                        continue;
 
-               for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++)
+               for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) {
                        if (~sdata->rc_rateidx_mcs_mask[i][j]) {
                                sdata->rc_has_mcs_mask[i] = true;
                                break;
                        }
+               }
+
+               for (j = 0; j < NL80211_VHT_NSS_MAX; j++) {
+                       if (~sdata->rc_rateidx_vht_mcs_mask[i][j]) {
+                               sdata->rc_has_vht_mcs_mask[i] = true;
+                               break;
+                       }
+               }
        }
 
        return 0;
@@ -2514,6 +2531,19 @@ static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
        return true;
 }
 
+static u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
+{
+       lockdep_assert_held(&local->mtx);
+
+       local->roc_cookie_counter++;
+
+       /* wow, you wrapped 64 bits ... more likely a bug */
+       if (WARN_ON(local->roc_cookie_counter == 0))
+               local->roc_cookie_counter++;
+
+       return local->roc_cookie_counter;
+}
+
 static int ieee80211_start_roc_work(struct ieee80211_local *local,
                                    struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_channel *channel,
@@ -2551,7 +2581,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        roc->req_duration = duration;
        roc->frame = txskb;
        roc->type = type;
-       roc->mgmt_tx_cookie = (unsigned long)txskb;
        roc->sdata = sdata;
        INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
        INIT_LIST_HEAD(&roc->dependents);
@@ -2561,17 +2590,10 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
         * or the SKB (for mgmt TX)
         */
        if (!txskb) {
-               /* local->mtx protects this */
-               local->roc_cookie_counter++;
-               roc->cookie = local->roc_cookie_counter;
-               /* wow, you wrapped 64 bits ... more likely a bug */
-               if (WARN_ON(roc->cookie == 0)) {
-                       roc->cookie = 1;
-                       local->roc_cookie_counter++;
-               }
+               roc->cookie = ieee80211_mgmt_tx_cookie(local);
                *cookie = roc->cookie;
        } else {
-               *cookie = (unsigned long)txskb;
+               roc->mgmt_tx_cookie = *cookie;
        }
 
        /* if there's one pending or we're scanning, queue this one */
@@ -3244,13 +3266,43 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        return err;
 }
 
+static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
+                                             struct sk_buff *skb, u64 *cookie,
+                                             gfp_t gfp)
+{
+       unsigned long spin_flags;
+       struct sk_buff *ack_skb;
+       int id;
+
+       ack_skb = skb_copy(skb, gfp);
+       if (!ack_skb)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock_irqsave(&local->ack_status_lock, spin_flags);
+       id = idr_alloc(&local->ack_status_frames, ack_skb,
+                      1, 0x10000, GFP_ATOMIC);
+       spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
+
+       if (id < 0) {
+               kfree_skb(ack_skb);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       IEEE80211_SKB_CB(skb)->ack_frame_id = id;
+
+       *cookie = ieee80211_mgmt_tx_cookie(local);
+       IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
+
+       return ack_skb;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct cfg80211_mgmt_tx_params *params,
                             u64 *cookie)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *ack_skb;
        struct sta_info *sta;
        const struct ieee80211_mgmt *mgmt = (void *)params->buf;
        bool need_offchan = false;
@@ -3299,8 +3351,14 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                break;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
-               if (!sdata->u.mgd.associated)
+               sdata_lock(sdata);
+               if (!sdata->u.mgd.associated ||
+                   (params->offchan && params->wait &&
+                    local->ops->remain_on_channel &&
+                    memcmp(sdata->u.mgd.associated->bssid,
+                           mgmt->bssid, ETH_ALEN)))
                        need_offchan = true;
+               sdata_unlock(sdata);
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                need_offchan = true;
@@ -3383,8 +3441,27 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
        skb->dev = sdata->dev;
 
+       if (!params->dont_wait_for_ack) {
+               /* make a copy to preserve the frame contents
+                * in case of encryption.
+                */
+               ack_skb = ieee80211_make_ack_skb(local, skb, cookie,
+                                                GFP_KERNEL);
+               if (IS_ERR(ack_skb)) {
+                       ret = PTR_ERR(ack_skb);
+                       kfree_skb(skb);
+                       goto out_unlock;
+               }
+       } else {
+               /* Assign a dummy non-zero cookie, it's not sent to
+                * userspace in this case but we rely on its value
+                * internally in the need_offchan case to distinguish
+                * mgmt-tx from remain-on-channel.
+                */
+               *cookie = 0xffffffff;
+       }
+
        if (!need_offchan) {
-               *cookie = (unsigned long) skb;
                ieee80211_tx_skb(sdata, skb);
                ret = 0;
                goto out_unlock;
@@ -3392,7 +3469,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
                                        IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
-       if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
+       if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
                IEEE80211_SKB_CB(skb)->hw_queue =
                        local->hw.offchannel_tx_hw_queue;
 
@@ -3421,18 +3498,32 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
                                          u16 frame_type, bool reg)
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
 
        switch (frame_type) {
        case IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ:
-               if (reg)
+               if (reg) {
                        local->probe_req_reg++;
-               else
-                       local->probe_req_reg--;
+                       sdata->vif.probe_req_reg++;
+               } else {
+                       if (local->probe_req_reg)
+                               local->probe_req_reg--;
+
+                       if (sdata->vif.probe_req_reg)
+                               sdata->vif.probe_req_reg--;
+               }
 
                if (!local->open_count)
                        break;
 
-               ieee80211_queue_work(&local->hw, &local->reconfig_filter);
+               if (sdata->vif.probe_req_reg == 1)
+                       drv_config_iface_filter(local, sdata, FIF_PROBE_REQ,
+                                               FIF_PROBE_REQ);
+               else if (sdata->vif.probe_req_reg == 0)
+                       drv_config_iface_filter(local, sdata, 0,
+                                               FIF_PROBE_REQ);
+
+               ieee80211_configure_filter(local);
                break;
        default:
                break;
@@ -3477,7 +3568,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_qos_hdr *nullfunc;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *ack_skb;
        int size = sizeof(*nullfunc);
        __le16 fc;
        bool qos;
@@ -3485,20 +3576,24 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        enum ieee80211_band band;
+       int ret;
+
+       /* the lock is needed to assign the cookie later */
+       mutex_lock(&local->mtx);
 
        rcu_read_lock();
        chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
        if (WARN_ON(!chanctx_conf)) {
-               rcu_read_unlock();
-               return -EINVAL;
+               ret = -EINVAL;
+               goto unlock;
        }
        band = chanctx_conf->def.chan->band;
        sta = sta_info_get_bss(sdata, peer);
        if (sta) {
                qos = sta->sta.wme;
        } else {
-               rcu_read_unlock();
-               return -ENOLINK;
+               ret = -ENOLINK;
+               goto unlock;
        }
 
        if (qos) {
@@ -3514,8 +3609,8 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
        if (!skb) {
-               rcu_read_unlock();
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto unlock;
        }
 
        skb->dev = dev;
@@ -3541,13 +3636,23 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        if (qos)
                nullfunc->qos_ctrl = cpu_to_le16(7);
 
+       ack_skb = ieee80211_make_ack_skb(local, skb, cookie, GFP_ATOMIC);
+       if (IS_ERR(ack_skb)) {
+               kfree_skb(skb);
+               ret = PTR_ERR(ack_skb);
+               goto unlock;
+       }
+
        local_bh_disable();
        ieee80211_xmit(sdata, sta, skb);
        local_bh_enable();
+
+       ret = 0;
+unlock:
        rcu_read_unlock();
+       mutex_unlock(&local->mtx);
 
-       *cookie = (unsigned long) skb;
-       return 0;
+       return ret;
 }
 
 static int ieee80211_cfg_get_channel(struct wiphy *wiphy,