These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / net / mac80211 / mlme.c
index 26053bf..83097c3 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright (C) 2015 Intel Deutschland GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -19,7 +20,6 @@
 #include <linux/etherdevice.h>
 #include <linux/moduleparam.h>
 #include <linux/rtnetlink.h>
-#include <linux/pm_qos.h>
 #include <linux/crc32.h>
 #include <linux/slab.h>
 #include <linux/export.h>
@@ -80,13 +80,6 @@ MODULE_PARM_DESC(probe_wait_ms,
                 "Maximum time(ms) to wait for probe response"
                 " before disconnecting (reason 4).");
 
-/*
- * Weight given to the latest Beacon frame when calculating average signal
- * strength for Beacon frames received in the current BSS. This must be
- * between 1 and 15.
- */
-#define IEEE80211_SIGNAL_AVE_WEIGHT    3
-
 /*
  * How many Beacon frames need to have been used in average signal strength
  * before starting to indicate signal change events.
@@ -118,7 +111,7 @@ void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
        if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
                return;
 
-       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+       if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
                return;
 
        mod_timer(&sdata->u.mgd.bcn_mon_timer,
@@ -134,7 +127,7 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
 
        ifmgd->probe_send_count = 0;
 
-       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+       if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
                return;
 
        mod_timer(&sdata->u.mgd.conn_mon_timer,
@@ -538,11 +531,16 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
        ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
 }
 
+/* This function determines vht capability flags for the association
+ * and builds the IE.
+ * Note - the function may set the owner of the MU-MIMO capability
+ */
 static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
                                 struct sk_buff *skb,
                                 struct ieee80211_supported_band *sband,
                                 struct ieee80211_vht_cap *ap_vht_cap)
 {
+       struct ieee80211_local *local = sdata->local;
        u8 *pos;
        u32 cap;
        struct ieee80211_sta_vht_cap vht_cap;
@@ -576,7 +574,34 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
         */
        if (!(ap_vht_cap->vht_cap_info &
                        cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
-               cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+               cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+                        IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+       else if (!(ap_vht_cap->vht_cap_info &
+                       cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)))
+               cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+       /*
+        * If some other vif is using the MU-MIMO capablity we cannot associate
+        * using MU-MIMO - this will lead to contradictions in the group-id
+        * mechanism.
+        * Ownership is defined since association request, in order to avoid
+        * simultaneous associations with MU-MIMO.
+        */
+       if (cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) {
+               bool disable_mu_mimo = false;
+               struct ieee80211_sub_if_data *other;
+
+               list_for_each_entry_rcu(other, &local->interfaces, list) {
+                       if (other->flags & IEEE80211_SDATA_MU_MIMO_OWNER) {
+                               disable_mu_mimo = true;
+                               break;
+                       }
+               }
+               if (disable_mu_mimo)
+                       cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+               else
+                       sdata->flags |= IEEE80211_SDATA_MU_MIMO_OWNER;
+       }
 
        mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
 
@@ -669,17 +694,15 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        capab = WLAN_CAPABILITY_ESS;
 
        if (sband->band == IEEE80211_BAND_2GHZ) {
-               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
-                       capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
-               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
-                       capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+               capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+               capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
        }
 
        if (assoc_data->capability & WLAN_CAPABILITY_PRIVACY)
                capab |= WLAN_CAPABILITY_PRIVACY;
 
        if ((assoc_data->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
-           (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
+           ieee80211_hw_check(&local->hw, SPECTRUM_MGMT))
                capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
 
        if (ifmgd->flags & IEEE80211_STA_ENABLE_RRM)
@@ -887,7 +910,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        drv_mgd_prepare_tx(local, sdata);
 
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+       if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
                                                IEEE80211_TX_INTFL_MLME_CONN_TX;
        ieee80211_tx_skb(sdata, skb);
@@ -912,7 +935,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
 
 void ieee80211_send_nullfunc(struct ieee80211_local *local,
                             struct ieee80211_sub_if_data *sdata,
-                            int powersave)
+                            bool powersave)
 {
        struct sk_buff *skb;
        struct ieee80211_hdr_3addr *nullfunc;
@@ -929,7 +952,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
                                        IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
 
-       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+       if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
 
        if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
@@ -1161,6 +1184,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
+       /*
+        * Drop all TDLS peers - either we disconnect or move to a different
+        * channel from this point on. There's no telling what our peer will do.
+        * The TDLS WIDER_BW scenario is also problematic, as peers might now
+        * have an incompatible wider chandef.
+        */
+       ieee80211_teardown_tdls_peers(sdata);
+
        mutex_lock(&local->mtx);
        mutex_lock(&local->chanctx_mtx);
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
@@ -1174,7 +1205,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        chanctx = container_of(conf, struct ieee80211_chanctx, conf);
 
        if (local->use_chanctx &&
-           !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
+           !ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA)) {
                sdata_info(sdata,
                           "driver doesn't support chan-switch with channel contexts\n");
                goto drop_connection;
@@ -1348,21 +1379,26 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
         */
        if (has_80211h_pwr &&
            (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
+               new_ap_level = pwr_level_80211h;
+
+               if (sdata->ap_power_level == new_ap_level)
+                       return 0;
+
                sdata_dbg(sdata,
                          "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
                          pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
                          sdata->u.mgd.bssid);
-               new_ap_level = pwr_level_80211h;
        } else {  /* has_cisco_pwr is always true here. */
+               new_ap_level = pwr_level_cisco;
+
+               if (sdata->ap_power_level == new_ap_level)
+                       return 0;
+
                sdata_dbg(sdata,
                          "Limiting TX power to %d dBm as advertised by %pM\n",
                          pwr_level_cisco, sdata->u.mgd.bssid);
-               new_ap_level = pwr_level_cisco;
        }
 
-       if (sdata->ap_power_level == new_ap_level)
-               return 0;
-
        sdata->ap_power_level = new_ap_level;
        if (__ieee80211_recalc_txpower(sdata))
                return BSS_CHANGED_TXPOWER;
@@ -1383,15 +1419,15 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
                return;
 
        if (conf->dynamic_ps_timeout > 0 &&
-           !(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
+           !ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) {
                mod_timer(&local->dynamic_ps_timer, jiffies +
                          msecs_to_jiffies(conf->dynamic_ps_timeout));
        } else {
-               if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
-                       ieee80211_send_nullfunc(local, sdata, 1);
+               if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK))
+                       ieee80211_send_nullfunc(local, sdata, true);
 
-               if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
-                   (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
+               if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
+                   ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
                        return;
 
                conf->flags |= IEEE80211_CONF_PS;
@@ -1444,13 +1480,13 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
 }
 
 /* need to hold RTNL or interface lock */
-void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
+void ieee80211_recalc_ps(struct ieee80211_local *local)
 {
        struct ieee80211_sub_if_data *sdata, *found = NULL;
        int count = 0;
        int timeout;
 
-       if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
+       if (!ieee80211_hw_check(&local->hw, SUPPORTS_PS)) {
                local->ps_sdata = NULL;
                return;
        }
@@ -1473,48 +1509,23 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
        }
 
        if (count == 1 && ieee80211_powersave_allowed(found)) {
+               u8 dtimper = found->u.mgd.dtim_period;
                s32 beaconint_us;
 
-               if (latency < 0)
-                       latency = pm_qos_request(PM_QOS_NETWORK_LATENCY);
-
                beaconint_us = ieee80211_tu_to_usec(
                                        found->vif.bss_conf.beacon_int);
 
                timeout = local->dynamic_ps_forced_timeout;
-               if (timeout < 0) {
-                       /*
-                        * Go to full PSM if the user configures a very low
-                        * latency requirement.
-                        * The 2000 second value is there for compatibility
-                        * until the PM_QOS_NETWORK_LATENCY is configured
-                        * with real values.
-                        */
-                       if (latency > (1900 * USEC_PER_MSEC) &&
-                           latency != (2000 * USEC_PER_SEC))
-                               timeout = 0;
-                       else
-                               timeout = 100;
-               }
+               if (timeout < 0)
+                       timeout = 100;
                local->hw.conf.dynamic_ps_timeout = timeout;
 
-               if (beaconint_us > latency) {
-                       local->ps_sdata = NULL;
-               } else {
-                       int maxslp = 1;
-                       u8 dtimper = found->u.mgd.dtim_period;
-
-                       /* If the TIM IE is invalid, pretend the value is 1 */
-                       if (!dtimper)
-                               dtimper = 1;
-                       else if (dtimper > 1)
-                               maxslp = min_t(int, dtimper,
-                                                   latency / beaconint_us);
-
-                       local->hw.conf.max_sleep_period = maxslp;
-                       local->hw.conf.ps_dtim_period = dtimper;
-                       local->ps_sdata = found;
-               }
+               /* If the TIM IE is invalid, pretend the value is 1 */
+               if (!dtimper)
+                       dtimper = 1;
+
+               local->hw.conf.ps_dtim_period = dtimper;
+               local->ps_sdata = found;
        } else {
                local->ps_sdata = NULL;
        }
@@ -1596,21 +1607,21 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
        }
 
-       if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
+       if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
            !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
                if (drv_tx_frames_pending(local)) {
                        mod_timer(&local->dynamic_ps_timer, jiffies +
                                  msecs_to_jiffies(
                                  local->hw.conf.dynamic_ps_timeout));
                } else {
-                       ieee80211_send_nullfunc(local, sdata, 1);
+                       ieee80211_send_nullfunc(local, sdata, true);
                        /* Flush to get the tx status of nullfunc frame */
                        ieee80211_flush_queues(local, sdata, false);
                }
        }
 
-       if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
-             (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) ||
+       if (!(ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS) &&
+             ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) ||
            (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
                ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
                local->hw.conf.flags |= IEEE80211_CONF_PS;
@@ -1738,10 +1749,10 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata,
                                     const u8 *wmm_param, size_t wmm_param_len)
 {
-       struct ieee80211_tx_queue_params params;
+       struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        size_t left;
-       int count;
+       int count, ac;
        const u8 *pos;
        u8 uapsd_queues = 0;
 
@@ -1775,25 +1786,24 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                int aci = (pos[0] >> 5) & 0x03;
                int acm = (pos[0] >> 4) & 0x01;
                bool uapsd = false;
-               int queue;
 
                switch (aci) {
                case 1: /* AC_BK */
-                       queue = 3;
+                       ac = IEEE80211_AC_BK;
                        if (acm)
                                sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
                                uapsd = true;
                        break;
                case 2: /* AC_VI */
-                       queue = 1;
+                       ac = IEEE80211_AC_VI;
                        if (acm)
                                sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
                                uapsd = true;
                        break;
                case 3: /* AC_VO */
-                       queue = 0;
+                       ac = IEEE80211_AC_VO;
                        if (acm)
                                sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
@@ -1801,7 +1811,7 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                        break;
                case 0: /* AC_BE */
                default:
-                       queue = 2;
+                       ac = IEEE80211_AC_BE;
                        if (acm)
                                sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
@@ -1809,25 +1819,41 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                        break;
                }
 
-               params.aifs = pos[0] & 0x0f;
-               params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
-               params.cw_min = ecw2cw(pos[1] & 0x0f);
-               params.txop = get_unaligned_le16(pos + 2);
-               params.acm = acm;
-               params.uapsd = uapsd;
+               params[ac].aifs = pos[0] & 0x0f;
+
+               if (params[ac].aifs < 2) {
+                       sdata_info(sdata,
+                                  "AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n",
+                                  params[ac].aifs, aci);
+                       params[ac].aifs = 2;
+               }
+               params[ac].cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
+               params[ac].cw_min = ecw2cw(pos[1] & 0x0f);
+               params[ac].txop = get_unaligned_le16(pos + 2);
+               params[ac].acm = acm;
+               params[ac].uapsd = uapsd;
+
+               if (params[ac].cw_min > params[ac].cw_max) {
+                       sdata_info(sdata,
+                                  "AP has invalid WMM params (CWmin/max=%d/%d for ACI %d), using defaults\n",
+                                  params[ac].cw_min, params[ac].cw_max, aci);
+                       return false;
+               }
+       }
 
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                mlme_dbg(sdata,
-                        "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
-                        queue, aci, acm,
-                        params.aifs, params.cw_min, params.cw_max,
-                        params.txop, params.uapsd,
-                        ifmgd->tx_tspec[queue].downgraded);
-               sdata->tx_conf[queue] = params;
-               if (!ifmgd->tx_tspec[queue].downgraded &&
-                   drv_conf_tx(local, sdata, queue, &params))
+                        "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
+                        ac, params[ac].acm,
+                        params[ac].aifs, params[ac].cw_min, params[ac].cw_max,
+                        params[ac].txop, params[ac].uapsd,
+                        ifmgd->tx_tspec[ac].downgraded);
+               sdata->tx_conf[ac] = params[ac];
+               if (!ifmgd->tx_tspec[ac].downgraded &&
+                   drv_conf_tx(local, sdata, ac, &params[ac]))
                        sdata_err(sdata,
-                                 "failed to set TX queue parameters for queue %d\n",
-                                 queue);
+                                 "failed to set TX queue parameters for AC %d\n",
+                                 ac);
        }
 
        /* enable WMM or activate new settings */
@@ -1965,7 +1991,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
        mutex_lock(&local->iflist_mtx);
-       ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_ps(local);
        mutex_unlock(&local->iflist_mtx);
 
        ieee80211_recalc_smps(sdata);
@@ -2052,6 +2078,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
        memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
        memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
+       sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
 
        sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
 
@@ -2070,7 +2097,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_bss_info_change_notify(sdata, changed);
 
        /* disassociated - set to defaults now */
-       ieee80211_set_wmm_default(sdata, false);
+       ieee80211_set_wmm_default(sdata, false, false);
 
        del_timer_sync(&sdata->u.mgd.conn_mon_timer);
        del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
@@ -2132,10 +2159,10 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
        __ieee80211_stop_poll(sdata);
 
        mutex_lock(&local->iflist_mtx);
-       ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_ps(local);
        mutex_unlock(&local->iflist_mtx);
 
-       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+       if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
                goto out;
 
        /*
@@ -2233,9 +2260,9 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
         */
        ifmgd->probe_send_count++;
 
-       if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
+       if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
                ifmgd->nullfunc_failed = false;
-               ieee80211_send_nullfunc(sdata->local, sdata, 0);
+               ieee80211_send_nullfunc(sdata->local, sdata, false);
        } else {
                int ssid_len;
 
@@ -2308,7 +2335,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
                goto out;
 
        mutex_lock(&sdata->local->iflist_mtx);
-       ieee80211_recalc_ps(sdata->local, -1);
+       ieee80211_recalc_ps(sdata->local);
        mutex_unlock(&sdata->local->iflist_mtx);
 
        ifmgd->probe_send_count = 0;
@@ -2413,15 +2440,9 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data,
                             u.mgd.beacon_connection_loss_work);
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct sta_info *sta;
 
-       if (ifmgd->associated) {
-               rcu_read_lock();
-               sta = sta_info_get(sdata, ifmgd->bssid);
-               if (sta)
-                       sta->beacon_loss_count++;
-               rcu_read_unlock();
-       }
+       if (ifmgd->associated)
+               ifmgd->beacon_loss_count++;
 
        if (ifmgd->connection_loss) {
                sdata_info(sdata, "Connection to AP %pM lost\n",
@@ -2495,6 +2516,35 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
        sdata->u.mgd.auth_data = NULL;
 }
 
+static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
+                                        bool assoc)
+{
+       struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
+
+       sdata_assert_lock(sdata);
+
+       if (!assoc) {
+               /*
+                * we are not associated yet, the only timer that could be
+                * running is the timeout for the association response which
+                * which is not relevant anymore.
+                */
+               del_timer_sync(&sdata->u.mgd.timer);
+               sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
+
+               eth_zero_addr(sdata->u.mgd.bssid);
+               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
+               sdata->u.mgd.flags = 0;
+               sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
+               mutex_lock(&sdata->local->mtx);
+               ieee80211_vif_release_channel(sdata);
+               mutex_unlock(&sdata->local->mtx);
+       }
+
+       kfree(assoc_data);
+       sdata->u.mgd.assoc_data = NULL;
+}
+
 static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_mgmt *mgmt, size_t len)
 {
@@ -2510,7 +2560,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
                return;
        auth_data->expected_transaction = 4;
        drv_mgd_prepare_tx(sdata->local, sdata);
-       if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+       if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
                tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
                           IEEE80211_TX_INTFL_MLME_CONN_TX;
        ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0,
@@ -2687,28 +2737,42 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_mgmt *mgmt, size_t len)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       const u8 *bssid = NULL;
-       u16 reason_code;
+       u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
 
        sdata_assert_lock(sdata);
 
        if (len < 24 + 2)
                return;
 
-       if (!ifmgd->associated ||
-           !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
-               return;
+       if (ifmgd->associated &&
+           ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) {
+               const u8 *bssid = ifmgd->associated->bssid;
 
-       bssid = ifmgd->associated->bssid;
+               sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n",
+                          bssid, reason_code,
+                          ieee80211_get_reason_code_string(reason_code));
 
-       reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
+               ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n",
-                  bssid, reason_code, ieee80211_get_reason_code_string(reason_code));
+               ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false,
+                                           reason_code);
+               return;
+       }
 
-       ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
+       if (ifmgd->assoc_data &&
+           ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
+               const u8 *bssid = ifmgd->assoc_data->bss->bssid;
 
-       ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
+               sdata_info(sdata,
+                          "deauthenticated from %pM while associating (Reason: %u=%s)\n",
+                          bssid, reason_code,
+                          ieee80211_get_reason_code_string(reason_code));
+
+               ieee80211_destroy_assoc_data(sdata, false);
+
+               cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+               return;
+       }
 }
 
 
@@ -2788,34 +2852,6 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
        }
 }
 
-static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
-                                        bool assoc)
-{
-       struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
-
-       sdata_assert_lock(sdata);
-
-       if (!assoc) {
-               /*
-                * we are not associated yet, the only timer that could be
-                * running is the timeout for the association response which
-                * which is not relevant anymore.
-                */
-               del_timer_sync(&sdata->u.mgd.timer);
-               sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
-
-               eth_zero_addr(sdata->u.mgd.bssid);
-               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
-               sdata->u.mgd.flags = 0;
-               mutex_lock(&sdata->local->mtx);
-               ieee80211_vif_release_channel(sdata);
-               mutex_unlock(&sdata->local->mtx);
-       }
-
-       kfree(assoc_data);
-       sdata->u.mgd.assoc_data = NULL;
-}
-
 static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                                    struct cfg80211_bss *cbss,
                                    struct ieee80211_mgmt *mgmt, size_t len)
@@ -3028,11 +3064,21 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
         */
        ifmgd->wmm_last_param_set = -1;
 
-       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)
-               ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
-                                        elems.wmm_param_len);
-       else
-               ieee80211_set_wmm_default(sdata, false);
+       if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
+               ieee80211_set_wmm_default(sdata, false, false);
+       } else if (!ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
+                                            elems.wmm_param_len)) {
+               /* still enable QoS since we might have HT/VHT */
+               ieee80211_set_wmm_default(sdata, false, true);
+               /* set the disable-WMM flag in this case to disable
+                * tracking WMM parameter changes in the beacon if
+                * the parameters weren't actually valid. Doing so
+                * avoids changing parameters very strangely when
+                * the AP is going back and forth between valid and
+                * invalid parameters.
+                */
+               ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
+       }
        changed |= BSS_CHANGED_QOS;
 
        /* set AID and assoc capability,
@@ -3211,16 +3257,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->associated &&
            ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
                ieee80211_reset_ap_probe(sdata);
-
-       if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies &&
-           ether_addr_equal(mgmt->bssid, ifmgd->auth_data->bss->bssid)) {
-               /* got probe response, continue with auth */
-               sdata_info(sdata, "direct probe responded\n");
-               ifmgd->auth_data->tries = 0;
-               ifmgd->auth_data->timeout = jiffies;
-               ifmgd->auth_data->timeout_started = true;
-               run_again(sdata, ifmgd->auth_data->timeout);
-       }
 }
 
 /*
@@ -3299,7 +3335,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                }
                ifmgd->have_beacon = true;
                ifmgd->assoc_data->need_beacon = false;
-               if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
+               if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
                        sdata->vif.bss_conf.sync_tsf =
                                le64_to_cpu(mgmt->u.beacon.timestamp);
                        sdata->vif.bss_conf.sync_device_ts =
@@ -3323,24 +3359,21 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        bssid = ifmgd->associated->bssid;
 
        /* Track average RSSI from the Beacon frames of the current AP */
-       ifmgd->last_beacon_signal = rx_status->signal;
        if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) {
                ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE;
-               ifmgd->ave_beacon_signal = rx_status->signal * 16;
+               ewma_beacon_signal_init(&ifmgd->ave_beacon_signal);
                ifmgd->last_cqm_event_signal = 0;
                ifmgd->count_beacon_signal = 1;
                ifmgd->last_ave_beacon_signal = 0;
        } else {
-               ifmgd->ave_beacon_signal =
-                       (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
-                        (16 - IEEE80211_SIGNAL_AVE_WEIGHT) *
-                        ifmgd->ave_beacon_signal) / 16;
                ifmgd->count_beacon_signal++;
        }
 
+       ewma_beacon_signal_add(&ifmgd->ave_beacon_signal, -rx_status->signal);
+
        if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold &&
            ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
-               int sig = ifmgd->ave_beacon_signal;
+               int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal);
                int last_sig = ifmgd->last_ave_beacon_signal;
                struct ieee80211_event event = {
                        .type = RSSI_EVENT,
@@ -3367,10 +3400,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (bss_conf->cqm_rssi_thold &&
            ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT &&
            !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) {
-               int sig = ifmgd->ave_beacon_signal / 16;
+               int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal);
                int last_event = ifmgd->last_cqm_event_signal;
                int thold = bss_conf->cqm_rssi_thold;
                int hyst = bss_conf->cqm_rssi_hyst;
+
                if (sig < thold &&
                    (last_event == 0 || sig < last_event - hyst)) {
                        ifmgd->last_cqm_event_signal = sig;
@@ -3405,31 +3439,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                          len - baselen, false, &elems,
                                          care_about_ies, ncrc);
 
-       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
-               bool directed_tim = ieee80211_check_tim(elems.tim,
-                                                       elems.tim_len,
-                                                       ifmgd->aid);
-               if (directed_tim) {
-                       if (local->hw.conf.dynamic_ps_timeout > 0) {
-                               if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-                                       local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-                                       ieee80211_hw_config(local,
-                                                           IEEE80211_CONF_CHANGE_PS);
-                               }
-                               ieee80211_send_nullfunc(local, sdata, 0);
-                       } else if (!local->pspolling && sdata->u.mgd.powersave) {
-                               local->pspolling = true;
-
-                               /*
-                                * Here is assumed that the driver will be
-                                * able to send ps-poll frame and receive a
-                                * response even though power save mode is
-                                * enabled, but some drivers might require
-                                * to disable power save here. This needs
-                                * to be investigated.
-                                */
-                               ieee80211_send_pspoll(local, sdata);
+       if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
+           ieee80211_check_tim(elems.tim, elems.tim_len, ifmgd->aid)) {
+               if (local->hw.conf.dynamic_ps_timeout > 0) {
+                       if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+                               local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+                               ieee80211_hw_config(local,
+                                                   IEEE80211_CONF_CHANGE_PS);
                        }
+                       ieee80211_send_nullfunc(local, sdata, false);
+               } else if (!local->pspolling && sdata->u.mgd.powersave) {
+                       local->pspolling = true;
+
+                       /*
+                        * Here is assumed that the driver will be
+                        * able to send ps-poll frame and receive a
+                        * response even though power save mode is
+                        * enabled, but some drivers might require
+                        * to disable power save here. This needs
+                        * to be investigated.
+                        */
+                       ieee80211_send_pspoll(local, sdata);
                }
        }
 
@@ -3473,7 +3503,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
         * the driver will use them. The synchronized view is currently
         * guaranteed only in certain callbacks.
         */
-       if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
+       if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
                sdata->vif.bss_conf.sync_tsf =
                        le64_to_cpu(mgmt->u.beacon.timestamp);
                sdata->vif.bss_conf.sync_device_ts =
@@ -3516,7 +3546,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ifmgd->have_beacon = true;
 
                mutex_lock(&local->iflist_mtx);
-               ieee80211_recalc_ps(local, -1);
+               ieee80211_recalc_ps(local);
                mutex_unlock(&local->iflist_mtx);
 
                ieee80211_recalc_ps_vif(sdata);
@@ -3550,7 +3580,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        if (sta && elems.opmode_notif)
                ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif,
-                                           rx_status->band, true);
+                                           rx_status->band);
        mutex_unlock(&local->sta_mtx);
 
        changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt,
@@ -3666,12 +3696,14 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
                                    reason);
 }
 
-static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
+static int ieee80211_auth(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
        u32 tx_flags = 0;
+       u16 trans = 1;
+       u16 status = 0;
 
        sdata_assert_lock(sdata);
 
@@ -3695,54 +3727,27 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
 
        drv_mgd_prepare_tx(local, sdata);
 
-       if (auth_data->bss->proberesp_ies) {
-               u16 trans = 1;
-               u16 status = 0;
-
-               sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
-                          auth_data->bss->bssid, auth_data->tries,
-                          IEEE80211_AUTH_MAX_TRIES);
+       sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
+                  auth_data->bss->bssid, auth_data->tries,
+                  IEEE80211_AUTH_MAX_TRIES);
 
-               auth_data->expected_transaction = 2;
+       auth_data->expected_transaction = 2;
 
-               if (auth_data->algorithm == WLAN_AUTH_SAE) {
-                       trans = auth_data->sae_trans;
-                       status = auth_data->sae_status;
-                       auth_data->expected_transaction = trans;
-               }
-
-               if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
-                       tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
-                                  IEEE80211_TX_INTFL_MLME_CONN_TX;
-
-               ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
-                                   auth_data->data, auth_data->data_len,
-                                   auth_data->bss->bssid,
-                                   auth_data->bss->bssid, NULL, 0, 0,
-                                   tx_flags);
-       } else {
-               const u8 *ssidie;
+       if (auth_data->algorithm == WLAN_AUTH_SAE) {
+               trans = auth_data->sae_trans;
+               status = auth_data->sae_status;
+               auth_data->expected_transaction = trans;
+       }
 
-               sdata_info(sdata, "direct probe to %pM (try %d/%i)\n",
-                          auth_data->bss->bssid, auth_data->tries,
-                          IEEE80211_AUTH_MAX_TRIES);
+       if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
+               tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+                          IEEE80211_TX_INTFL_MLME_CONN_TX;
 
-               rcu_read_lock();
-               ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
-               if (!ssidie) {
-                       rcu_read_unlock();
-                       return -EINVAL;
-               }
-               /*
-                * Direct probe is sent to broadcast address as some APs
-                * will not answer to direct packet in unassociated state.
-                */
-               ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
-                                        ssidie + 2, ssidie[1],
-                                        NULL, 0, (u32) -1, true, 0,
-                                        auth_data->bss->channel, false);
-               rcu_read_unlock();
-       }
+       ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
+                           auth_data->data, auth_data->data_len,
+                           auth_data->bss->bssid,
+                           auth_data->bss->bssid, NULL, 0, 0,
+                           tx_flags);
 
        if (tx_flags == 0) {
                auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@@ -3784,7 +3789,7 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
                   IEEE80211_ASSOC_MAX_TRIES);
        ieee80211_send_assoc(sdata);
 
-       if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+       if (!ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
                assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
                assoc_data->timeout_started = true;
                run_again(sdata, assoc_data->timeout);
@@ -3823,8 +3828,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                bool status_acked = ifmgd->status_acked;
 
                ifmgd->status_received = false;
-               if (ifmgd->auth_data &&
-                   (ieee80211_is_probe_req(fc) || ieee80211_is_auth(fc))) {
+               if (ifmgd->auth_data && ieee80211_is_auth(fc)) {
                        if (status_acked) {
                                ifmgd->auth_data->timeout =
                                        jiffies + IEEE80211_AUTH_TIMEOUT_SHORT;
@@ -3855,7 +3859,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                         * so let's just kill the auth data
                         */
                        ieee80211_destroy_auth_data(sdata, false);
-               } else if (ieee80211_probe_auth(sdata)) {
+               } else if (ieee80211_auth(sdata)) {
                        u8 bssid[ETH_ALEN];
                        struct ieee80211_event event = {
                                .type = MLME_EVENT,
@@ -3898,7 +3902,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
 
                memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
 
-               if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+               if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
                        max_tries = max_nullfunc_tries;
                else
                        max_tries = max_probe_tries;
@@ -3923,7 +3927,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                        }
                } else if (time_is_after_jiffies(ifmgd->probe_timeout))
                        run_again(sdata, ifmgd->probe_timeout);
-               else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
+               else if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
                        mlme_dbg(sdata,
                                 "Failed to send nullfunc to AP %pM after %dms, disconnecting\n",
                                 bssid, probe_wait_ms);
@@ -3992,18 +3996,13 @@ static void ieee80211_sta_monitor_work(struct work_struct *work)
 
 static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
 {
-       u32 flags;
-
        if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                __ieee80211_stop_poll(sdata);
 
                /* let's probe the connection once */
-               flags = sdata->local->hw.flags;
-               if (!(flags & IEEE80211_HW_CONNECTION_MONITOR))
+               if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
                        ieee80211_queue_work(&sdata->local->hw,
                                             &sdata->u.mgd.monitor_work);
-               /* and do all the other regular work too */
-               ieee80211_queue_work(&sdata->local->hw, &sdata->work);
        }
 }
 
@@ -4149,21 +4148,6 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
        rcu_read_unlock();
 }
 
-int ieee80211_max_network_latency(struct notifier_block *nb,
-                                 unsigned long data, void *dummy)
-{
-       s32 latency_usec = (s32) data;
-       struct ieee80211_local *local =
-               container_of(nb, struct ieee80211_local,
-                            network_latency_notifier);
-
-       mutex_lock(&local->iflist_mtx);
-       ieee80211_recalc_ps(local, latency_usec);
-       mutex_unlock(&local->iflist_mtx);
-
-       return NOTIFY_OK;
-}
-
 static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
                                     struct cfg80211_bss *cbss)
 {
@@ -4219,6 +4203,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_supported_band *sband;
        struct cfg80211_chan_def chandef;
        int ret;
+       u32 i;
+       bool have_80mhz;
 
        sband = local->hw.wiphy->bands[cbss->channel->band];
 
@@ -4269,6 +4255,20 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
                }
        }
 
+       /* Allow VHT if at least one channel on the sband supports 80 MHz */
+       have_80mhz = false;
+       for (i = 0; i < sband->n_channels; i++) {
+               if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
+                                               IEEE80211_CHAN_NO_80MHZ))
+                       continue;
+
+               have_80mhz = true;
+               break;
+       }
+
+       if (!have_80mhz)
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+
        ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
                                                     cbss->channel,
                                                     ht_cap, ht_oper, vht_oper,
@@ -4307,15 +4307,15 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
 }
 
 static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
-                                    struct cfg80211_bss *cbss, bool assoc)
+                                    struct cfg80211_bss *cbss, bool assoc,
+                                    bool override)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_bss *bss = (void *)cbss->priv;
        struct sta_info *new_sta = NULL;
        struct ieee80211_supported_band *sband;
-       struct ieee80211_sta_ht_cap sta_ht_cap;
-       bool have_sta = false, is_override = false;
+       bool have_sta = false;
        int err;
 
        sband = local->hw.wiphy->bands[cbss->channel->band];
@@ -4335,14 +4335,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                        return -ENOMEM;
        }
 
-       memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
-       ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
-
-       is_override = (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) !=
-                     (sband->ht_cap.cap &
-                      IEEE80211_HT_CAP_SUP_WIDTH_20_40);
-
-       if (new_sta || is_override) {
+       if (new_sta || override) {
                err = ieee80211_prep_channel(sdata, cbss);
                if (err) {
                        if (new_sta)
@@ -4419,8 +4412,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                                sdata->vif.bss_conf.sync_dtim_count = tim_ie[2];
                        else
                                sdata->vif.bss_conf.sync_dtim_count = 0;
-               } else if (!(local->hw.flags &
-                                       IEEE80211_HW_TIMING_BEACON_ONLY)) {
+               } else if (!ieee80211_hw_check(&sdata->local->hw,
+                                              TIMING_BEACON_ONLY)) {
                        ies = rcu_dereference(cbss->proberesp_ies);
                        /* must be non-NULL since beacon IEs were NULL */
                        sdata->vif.bss_conf.sync_tsf = ies->tsf;
@@ -4552,11 +4545,11 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
 
        sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
 
-       err = ieee80211_prep_connection(sdata, req->bss, false);
+       err = ieee80211_prep_connection(sdata, req->bss, false, false);
        if (err)
                goto err_clear;
 
-       err = ieee80211_probe_auth(sdata);
+       err = ieee80211_auth(sdata);
        if (err) {
                sta_info_destroy_addr(sdata, req->bss->bssid);
                goto err_clear;
@@ -4570,49 +4563,14 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        eth_zero_addr(ifmgd->bssid);
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
        ifmgd->auth_data = NULL;
+       mutex_lock(&sdata->local->mtx);
+       ieee80211_vif_release_channel(sdata);
+       mutex_unlock(&sdata->local->mtx);
  err_free:
        kfree(auth_data);
        return err;
 }
 
-static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata,
-                                       const u8 *wmm_param, int len)
-{
-       const u8 *pos;
-       size_t left;
-
-       if (len < 8)
-               return false;
-
-       if (wmm_param[5] != 1 /* version */)
-               return false;
-
-       pos = wmm_param + 8;
-       left = len - 8;
-
-       for (; left >= 4; left -= 4, pos += 4) {
-               u8 aifsn = pos[0] & 0x0f;
-               u8 ecwmin = pos[1] & 0x0f;
-               u8 ecwmax = (pos[1] & 0xf0) >> 4;
-               int aci = (pos[0] >> 5) & 0x03;
-
-               if (aifsn < 2) {
-                       sdata_info(sdata,
-                                  "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n",
-                                  aifsn, aci);
-                       return false;
-               }
-               if (ecwmin > ecwmax) {
-                       sdata_info(sdata,
-                                  "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n",
-                                  ecwmin, ecwmax, aci);
-                       return false;
-               }
-       }
-
-       return true;
-}
-
 int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_assoc_request *req)
 {
@@ -4624,6 +4582,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_supported_band *sband;
        const u8 *ssidie, *ht_ie, *vht_ie;
        int i, err;
+       bool override = false;
 
        assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
        if (!assoc_data)
@@ -4676,39 +4635,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
        assoc_data->wmm = bss->wmm_used &&
                          (local->hw.queues >= IEEE80211_NUM_ACS);
-       if (assoc_data->wmm) {
-               /* try to check validity of WMM params IE */
-               const struct cfg80211_bss_ies *ies;
-               const u8 *wp, *start, *end;
-
-               rcu_read_lock();
-               ies = rcu_dereference(req->bss->ies);
-               start = ies->data;
-               end = start + ies->len;
-
-               while (true) {
-                       wp = cfg80211_find_vendor_ie(
-                               WLAN_OUI_MICROSOFT,
-                               WLAN_OUI_TYPE_MICROSOFT_WMM,
-                               start, end - start);
-                       if (!wp)
-                               break;
-                       start = wp + wp[1] + 2;
-                       /* if this IE is too short, try the next */
-                       if (wp[1] <= 4)
-                               continue;
-                       /* if this IE is WMM params, we found what we wanted */
-                       if (wp[6] == 1)
-                               break;
-               }
-
-               if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2,
-                                                       wp[1] - 2)) {
-                       assoc_data->wmm = false;
-                       ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
-               }
-               rcu_read_unlock();
-       }
 
        /*
         * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
@@ -4728,14 +4654,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       if (req->flags & ASSOC_REQ_DISABLE_HT) {
-               ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
-               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
-       }
-
-       if (req->flags & ASSOC_REQ_DISABLE_VHT)
-               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
-
        /* Also disable HT if we don't support it or the AP doesn't use WMM */
        sband = local->hw.wiphy->bands[req->bss->channel->band];
        if (!sband->ht_cap.ht_supported ||
@@ -4802,7 +4720,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        rcu_read_unlock();
 
        if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) &&
-                (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
+                ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK),
             "U-APSD not supported with HW_PS_NULLFUNC_STACK\n"))
                sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
 
@@ -4847,14 +4765,43 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        ifmgd->dtim_period = 0;
        ifmgd->have_beacon = false;
 
-       err = ieee80211_prep_connection(sdata, req->bss, true);
+       /* override HT/VHT configuration only if the AP and we support it */
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
+               struct ieee80211_sta_ht_cap sta_ht_cap;
+
+               if (req->flags & ASSOC_REQ_DISABLE_HT)
+                       override = true;
+
+               memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+               ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
+               /* check for 40 MHz disable override */
+               if (!(ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ) &&
+                   sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
+                   !(sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+                       override = true;
+
+               if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
+                   req->flags & ASSOC_REQ_DISABLE_VHT)
+                       override = true;
+       }
+
+       if (req->flags & ASSOC_REQ_DISABLE_HT) {
+               ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+       }
+
+       if (req->flags & ASSOC_REQ_DISABLE_VHT)
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+
+       err = ieee80211_prep_connection(sdata, req->bss, true, override);
        if (err)
                goto err_clear;
 
        rcu_read_lock();
        beacon_ies = rcu_dereference(req->bss->beacon_ies);
 
-       if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC &&
+       if (ieee80211_hw_check(&sdata->local->hw, NEED_DTIM_BEFORE_ASSOC) &&
            !beacon_ies) {
                /*
                 * Wait up to one beacon interval ...
@@ -4881,7 +4828,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                assoc_data->timeout = jiffies;
                assoc_data->timeout_started = true;
 
-               if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
+               if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
                        sdata->vif.bss_conf.sync_tsf = beacon_ies->tsf;
                        sdata->vif.bss_conf.sync_device_ts =
                                bss->device_ts_beacon;
@@ -4946,6 +4893,25 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                return 0;
        }
 
+       if (ifmgd->assoc_data &&
+           ether_addr_equal(ifmgd->assoc_data->bss->bssid, req->bssid)) {
+               sdata_info(sdata,
+                          "aborting association with %pM by local choice (Reason: %u=%s)\n",
+                          req->bssid, req->reason_code,
+                          ieee80211_get_reason_code_string(req->reason_code));
+
+               drv_mgd_prepare_tx(sdata->local, sdata);
+               ieee80211_send_deauth_disassoc(sdata, req->bssid,
+                                              IEEE80211_STYPE_DEAUTH,
+                                              req->reason_code, tx,
+                                              frame_buf);
+               ieee80211_destroy_assoc_data(sdata, false);
+               ieee80211_report_disconnect(sdata, frame_buf,
+                                           sizeof(frame_buf), true,
+                                           req->reason_code);
+               return 0;
+       }
+
        if (ifmgd->associated &&
            ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
                sdata_info(sdata,