wifi: mac80211: mlme: shift some code around
authorJohannes Berg <johannes.berg@intel.com>
Tue, 12 Jul 2022 09:13:56 +0000 (11:13 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 15 Jul 2022 09:43:22 +0000 (11:43 +0200)
We'll need ieee80211_prep_channel() in other code for MLO
later, so move the code up - unchanged for now - to avoid
forward declarations in the future.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/mlme.c

index 0d43b20..43b64ec 100644 (file)
@@ -3546,2195 +3546,2211 @@ static int ieee80211_mgd_setup_link_sta(struct ieee80211_link_data *link,
        return 0;
 }
 
-static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
-                                   struct cfg80211_bss *cbss,
-                                   struct ieee80211_mgmt *mgmt, size_t len,
-                                   struct ieee802_11_elems *elems)
+static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link,
+                                 struct cfg80211_bss *cbss)
 {
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_supported_band *sband;
-       struct link_sta_info *link_sta;
-       struct sta_info *sta;
-       u16 capab_info, aid;
-       struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
-       const struct cfg80211_bss_ies *bss_ies = NULL;
-       struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
-       bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
-       bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
-       struct ieee80211_link_data *link = &sdata->deflink;
-       u32 changed = 0;
-       int err;
-       bool ret;
+       struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp;
+       const struct element *ht_cap_elem, *vht_cap_elem;
+       const struct cfg80211_bss_ies *ies;
+       const struct ieee80211_ht_cap *ht_cap;
+       const struct ieee80211_vht_cap *vht_cap;
+       const struct ieee80211_he_cap_elem *he_cap;
+       const struct element *he_cap_elem;
+       u16 mcs_80_map, mcs_160_map;
+       int i, mcs_nss_size;
+       bool support_160;
+       u8 chains = 1;
 
-       capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
+       if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)
+               return chains;
 
-       if (elems->aid_resp)
-               aid = le16_to_cpu(elems->aid_resp->aid);
-       else if (is_s1g)
-               aid = 0; /* TODO */
-       else
-               aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+       ht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_HT_CAPABILITY);
+       if (ht_cap_elem && ht_cap_elem->datalen >= sizeof(*ht_cap)) {
+               ht_cap = (void *)ht_cap_elem->data;
+               chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
+               /*
+                * TODO: use "Tx Maximum Number Spatial Streams Supported" and
+                *       "Tx Unequal Modulation Supported" fields.
+                */
+       }
 
-       /*
-        * The 5 MSB of the AID field are reserved
-        * (802.11-2016 9.4.1.8 AID field)
-        */
-       aid &= 0x7ff;
+       if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)
+               return chains;
 
-       ifmgd->broken_ap = false;
+       vht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_VHT_CAPABILITY);
+       if (vht_cap_elem && vht_cap_elem->datalen >= sizeof(*vht_cap)) {
+               u8 nss;
+               u16 tx_mcs_map;
 
-       if (aid == 0 || aid > IEEE80211_MAX_AID) {
-               sdata_info(sdata, "invalid AID value %d (out of range), turn off PS\n",
-                          aid);
-               aid = 0;
-               ifmgd->broken_ap = true;
+               vht_cap = (void *)vht_cap_elem->data;
+               tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
+               for (nss = 8; nss > 0; nss--) {
+                       if (((tx_mcs_map >> (2 * (nss - 1))) & 3) !=
+                                       IEEE80211_VHT_MCS_NOT_SUPPORTED)
+                               break;
+               }
+               /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */
+               chains = max(chains, nss);
        }
 
-       if (!is_s1g && !elems->supp_rates) {
-               sdata_info(sdata, "no SuppRates element in AssocResp\n");
-               ret = false;
-               goto out;
-       }
+       if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE)
+               return chains;
 
-       sdata->vif.cfg.aid = aid;
-       sdata->deflink.u.mgd.tdls_chan_switch_prohibited =
-               elems->ext_capab && elems->ext_capab_len >= 5 &&
-               (elems->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
+       ies = rcu_dereference(cbss->ies);
+       he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
+                                            ies->data, ies->len);
 
-       /*
-        * Some APs are erroneously not including some information in their
-        * (re)association response frames. Try to recover by using the data
-        * from the beacon or probe response. This seems to afflict mobile
-        * 2G/3G/4G wifi routers, reported models include the "Onda PN51T",
-        * "Vodafone PocketWiFi 2", "ZTE MF60" and a similar T-Mobile device.
-        */
-       if (!is_6ghz &&
-           ((assoc_data->wmm && !elems->wmm_param) ||
-            (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) &&
-             (!elems->ht_cap_elem || !elems->ht_operation)) ||
-            (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) &&
-             (!elems->vht_cap_elem || !elems->vht_operation)))) {
-               const struct cfg80211_bss_ies *ies;
-               struct ieee802_11_elems *bss_elems;
+       if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap))
+               return chains;
 
-               rcu_read_lock();
-               ies = rcu_dereference(cbss->ies);
-               if (ies)
-                       bss_ies = kmemdup(ies, sizeof(*ies) + ies->len,
-                                         GFP_ATOMIC);
-               rcu_read_unlock();
-               if (!bss_ies) {
-                       ret = false;
-                       goto out;
-               }
+       /* skip one byte ext_tag_id */
+       he_cap = (void *)(he_cap_elem->data + 1);
+       mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap);
 
-               bss_elems = ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
-                                                  false, assoc_data->bss);
-               if (!bss_elems) {
-                       ret = false;
-                       goto out;
-               }
+       /* invalid HE IE */
+       if (he_cap_elem->datalen < 1 + mcs_nss_size + sizeof(*he_cap))
+               return chains;
 
-               if (assoc_data->wmm &&
-                   !elems->wmm_param && bss_elems->wmm_param) {
-                       elems->wmm_param = bss_elems->wmm_param;
-                       sdata_info(sdata,
-                                  "AP bug: WMM param missing from AssocResp\n");
-               }
+       /* mcs_nss is right after he_cap info */
+       he_mcs_nss_supp = (void *)(he_cap + 1);
 
-               /*
-                * Also check if we requested HT/VHT, otherwise the AP doesn't
-                * have to include the IEs in the (re)association response.
-                */
-               if (!elems->ht_cap_elem && bss_elems->ht_cap_elem &&
-                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) {
-                       elems->ht_cap_elem = bss_elems->ht_cap_elem;
-                       sdata_info(sdata,
-                                  "AP bug: HT capability missing from AssocResp\n");
-               }
-               if (!elems->ht_operation && bss_elems->ht_operation &&
-                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) {
-                       elems->ht_operation = bss_elems->ht_operation;
-                       sdata_info(sdata,
-                                  "AP bug: HT operation missing from AssocResp\n");
-               }
-               if (!elems->vht_cap_elem && bss_elems->vht_cap_elem &&
-                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) {
-                       elems->vht_cap_elem = bss_elems->vht_cap_elem;
-                       sdata_info(sdata,
-                                  "AP bug: VHT capa missing from AssocResp\n");
-               }
-               if (!elems->vht_operation && bss_elems->vht_operation &&
-                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) {
-                       elems->vht_operation = bss_elems->vht_operation;
-                       sdata_info(sdata,
-                                  "AP bug: VHT operation missing from AssocResp\n");
-               }
+       mcs_80_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80);
 
-               kfree(bss_elems);
+       for (i = 7; i >= 0; i--) {
+               u8 mcs_80 = mcs_80_map >> (2 * i) & 3;
+
+               if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+                       chains = max_t(u8, chains, i + 1);
+                       break;
+               }
        }
 
-       /*
-        * We previously checked these in the beacon/probe response, so
-        * they should be present here. This is just a safety net.
-        */
-       if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) &&
-           (!elems->wmm_param || !elems->ht_cap_elem || !elems->ht_operation)) {
-               sdata_info(sdata,
-                          "HT AP is missing WMM params or HT capability/operation\n");
-               ret = false;
-               goto out;
+       support_160 = he_cap->phy_cap_info[0] &
+                     IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+       if (!support_160)
+               return chains;
+
+       mcs_160_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_160);
+       for (i = 7; i >= 0; i--) {
+               u8 mcs_160 = mcs_160_map >> (2 * i) & 3;
+
+               if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+                       chains = max_t(u8, chains, i + 1);
+                       break;
+               }
        }
 
-       if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) &&
-           (!elems->vht_cap_elem || !elems->vht_operation)) {
+       return chains;
+}
+
+static bool
+ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata,
+                                    const struct cfg80211_bss_ies *ies,
+                                    const struct ieee80211_he_operation *he_op)
+{
+       const struct element *he_cap_elem;
+       const struct ieee80211_he_cap_elem *he_cap;
+       struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp;
+       u16 mcs_80_map_tx, mcs_80_map_rx;
+       u16 ap_min_req_set;
+       int mcs_nss_size;
+       int nss;
+
+       he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
+                                            ies->data, ies->len);
+
+       /* invalid HE IE */
+       if (!he_cap_elem || he_cap_elem->datalen < 1 + sizeof(*he_cap)) {
                sdata_info(sdata,
-                          "VHT AP is missing VHT capability/operation\n");
-               ret = false;
-               goto out;
+                          "Invalid HE elem, Disable HE\n");
+               return false;
        }
 
-       if (is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
-           !elems->he_6ghz_capa) {
+       /* skip one byte ext_tag_id */
+       he_cap = (void *)(he_cap_elem->data + 1);
+       mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap);
+
+       /* invalid HE IE */
+       if (he_cap_elem->datalen < 1 + sizeof(*he_cap) + mcs_nss_size) {
                sdata_info(sdata,
-                          "HE 6 GHz AP is missing HE 6 GHz band capability\n");
-               ret = false;
-               goto out;
+                          "Invalid HE elem with nss size, Disable HE\n");
+               return false;
        }
 
-       mutex_lock(&sdata->local->sta_mtx);
-       /*
-        * station info was already allocated and inserted before
-        * the association and should be available to us
-        */
-       sta = sta_info_get(sdata, cbss->bssid);
-       if (WARN_ON(!sta)) {
-               mutex_unlock(&sdata->local->sta_mtx);
-               ret = false;
-               goto out;
-       }
+       /* mcs_nss is right after he_cap info */
+       he_mcs_nss_supp = (void *)(he_cap + 1);
 
-       link_sta = rcu_dereference_protected(sta->link[link->link_id],
-                                            lockdep_is_held(&local->sta_mtx));
-       if (WARN_ON(!link_sta)) {
-               mutex_unlock(&sdata->local->sta_mtx);
-               ret = false;
-               goto out;
-       }
-
-       sband = ieee80211_get_link_sband(link);
-       if (!sband) {
-               mutex_unlock(&sdata->local->sta_mtx);
-               ret = false;
-               goto out;
-       }
+       mcs_80_map_tx = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80);
+       mcs_80_map_rx = le16_to_cpu(he_mcs_nss_supp->rx_mcs_80);
 
-       if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
-           (!elems->he_cap || !elems->he_operation)) {
-               mutex_unlock(&sdata->local->sta_mtx);
+       /* P802.11-REVme/D0.3
+        * 27.1.1 Introduction to the HE PHY
+        * ...
+        * An HE STA shall support the following features:
+        * ...
+        * Single spatial stream HE-MCSs 0 to 7 (transmit and receive) in all
+        * supported channel widths for HE SU PPDUs
+        */
+       if ((mcs_80_map_tx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED ||
+           (mcs_80_map_rx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED) {
                sdata_info(sdata,
-                          "HE AP is missing HE capability/operation\n");
-               ret = false;
-               goto out;
+                          "Missing mandatory rates for 1 Nss, rx 0x%x, tx 0x%x, disable HE\n",
+                          mcs_80_map_tx, mcs_80_map_rx);
+               return false;
        }
 
-       /* Set up internal HT/VHT capabilities */
-       if (elems->ht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT))
-               ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
-                                                 elems->ht_cap_elem,
-                                                 link_sta);
+       if (!he_op)
+               return true;
 
-       if (elems->vht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT))
-               ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
-                                                   elems->vht_cap_elem,
-                                                   link_sta);
+       ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set);
 
-       if (elems->he_operation && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
-           elems->he_cap) {
-               ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
-                                                 elems->he_cap,
-                                                 elems->he_cap_len,
-                                                 elems->he_6ghz_capa,
-                                                 link_sta);
+       /*
+        * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all
+        * zeroes, which is nonsense, and completely inconsistent with itself
+        * (it doesn't have 8 streams). Accept the settings in this case anyway.
+        */
+       if (!ap_min_req_set)
+               return true;
 
-               bss_conf->he_support = link_sta->pub->he_cap.has_he;
-               if (elems->rsnx && elems->rsnx_len &&
-                   (elems->rsnx[0] & WLAN_RSNX_CAPA_PROTECTED_TWT) &&
-                   wiphy_ext_feature_isset(local->hw.wiphy,
-                                           NL80211_EXT_FEATURE_PROTECTED_TWT))
-                       bss_conf->twt_protected = true;
-               else
-                       bss_conf->twt_protected = false;
+       /* make sure the AP is consistent with itself
+        *
+        * P802.11-REVme/D0.3
+        * 26.17.1 Basic HE BSS operation
+        *
+        * A STA that is operating in an HE BSS shall be able to receive and
+        * transmit at each of the <HE-MCS, NSS> tuple values indicated by the
+        * Basic HE-MCS And NSS Set field of the HE Operation parameter of the
+        * MLME-START.request primitive and shall be able to receive at each of
+        * the <HE-MCS, NSS> tuple values indicated by the Supported HE-MCS and
+        * NSS Set field in the HE Capabilities parameter of the MLMESTART.request
+        * primitive
+        */
+       for (nss = 8; nss > 0; nss--) {
+               u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3;
+               u8 ap_rx_val;
+               u8 ap_tx_val;
 
-               changed |= ieee80211_recalc_twt_req(link, link_sta, elems);
+               if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED)
+                       continue;
 
-               if (elems->eht_operation && elems->eht_cap &&
-                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) {
-                       ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband,
-                                                           elems->he_cap,
-                                                           elems->he_cap_len,
-                                                           elems->eht_cap,
-                                                           elems->eht_cap_len,
-                                                           link_sta);
+               ap_rx_val = (mcs_80_map_rx >> (2 * (nss - 1))) & 3;
+               ap_tx_val = (mcs_80_map_tx >> (2 * (nss - 1))) & 3;
 
-                       bss_conf->eht_support = link_sta->pub->eht_cap.has_eht;
-               } else {
-                       bss_conf->eht_support = false;
+               if (ap_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
+                   ap_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
+                   ap_rx_val < ap_op_val || ap_tx_val < ap_op_val) {
+                       sdata_info(sdata,
+                                  "Invalid rates for %d Nss, rx %d, tx %d oper %d, disable HE\n",
+                                  nss, ap_rx_val, ap_rx_val, ap_op_val);
+                       return false;
                }
-       } else {
-               bss_conf->he_support = false;
-               bss_conf->twt_requester = false;
-               bss_conf->twt_protected = false;
-               bss_conf->eht_support = false;
        }
 
-       bss_conf->twt_broadcast =
-               ieee80211_twt_bcast_support(sdata, bss_conf, sband, link_sta);
-
-       if (bss_conf->he_support) {
-               bss_conf->he_bss_color.color =
-                       le32_get_bits(elems->he_operation->he_oper_params,
-                                     IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
-               bss_conf->he_bss_color.partial =
-                       le32_get_bits(elems->he_operation->he_oper_params,
-                                     IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR);
-               bss_conf->he_bss_color.enabled =
-                       !le32_get_bits(elems->he_operation->he_oper_params,
-                                      IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED);
-
-               if (bss_conf->he_bss_color.enabled)
-                       changed |= BSS_CHANGED_HE_BSS_COLOR;
-
-               bss_conf->htc_trig_based_pkt_ext =
-                       le32_get_bits(elems->he_operation->he_oper_params,
-                             IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK);
-               bss_conf->frame_time_rts_th =
-                       le32_get_bits(elems->he_operation->he_oper_params,
-                             IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
+       return true;
+}
 
-               bss_conf->uora_exists = !!elems->uora_element;
-               if (elems->uora_element)
-                       bss_conf->uora_ocw_range = elems->uora_element[0];
+static bool
+ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata,
+                                   struct ieee80211_supported_band *sband,
+                                   const struct ieee80211_he_operation *he_op)
+{
+       const struct ieee80211_sta_he_cap *sta_he_cap =
+               ieee80211_get_he_iftype_cap(sband,
+                                           ieee80211_vif_type_p2p(&sdata->vif));
+       u16 ap_min_req_set;
+       int i;
 
-               ieee80211_he_op_ie_to_bss_conf(&sdata->vif, elems->he_operation);
-               ieee80211_he_spr_ie_to_bss_conf(&sdata->vif, elems->he_spr);
-               /* TODO: OPEN: what happens if BSS color disable is set? */
-       }
+       if (!sta_he_cap || !he_op)
+               return false;
 
-       if (cbss->transmitted_bss) {
-               bss_conf->nontransmitted = true;
-               ether_addr_copy(bss_conf->transmitter_bssid,
-                               cbss->transmitted_bss->bssid);
-               bss_conf->bssid_indicator = cbss->max_bssid_indicator;
-               bss_conf->bssid_index = cbss->bssid_index;
-       } else {
-               bss_conf->nontransmitted = false;
-               memset(bss_conf->transmitter_bssid, 0,
-                      sizeof(bss_conf->transmitter_bssid));
-               bss_conf->bssid_indicator = 0;
-               bss_conf->bssid_index = 0;
-       }
+       ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set);
 
        /*
-        * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data
-        * in their association response, so ignore that data for our own
-        * configuration. If it changed since the last beacon, we'll get the
-        * next beacon and update then.
+        * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all
+        * zeroes, which is nonsense, and completely inconsistent with itself
+        * (it doesn't have 8 streams). Accept the settings in this case anyway.
         */
+       if (!ap_min_req_set)
+               return true;
 
-       /*
-        * If an operating mode notification IE is present, override the
-        * NSS calculation (that would be done in rate_control_rate_init())
-        * and use the # of streams from that element.
-        */
-       if (elems->opmode_notif &&
-           !(*elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) {
+       /* Need to go over for 80MHz, 160MHz and for 80+80 */
+       for (i = 0; i < 3; i++) {
+               const struct ieee80211_he_mcs_nss_supp *sta_mcs_nss_supp =
+                       &sta_he_cap->he_mcs_nss_supp;
+               u16 sta_mcs_map_rx =
+                       le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i]);
+               u16 sta_mcs_map_tx =
+                       le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i + 1]);
                u8 nss;
+               bool verified = true;
 
-               nss = *elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
-               nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
-               nss += 1;
-               link_sta->pub->rx_nss = nss;
-       }
-
-       rate_control_rate_init(sta);
+               /*
+                * For each band there is a maximum of 8 spatial streams
+                * possible. Each of the sta_mcs_map_* is a 16-bit struct built
+                * of 2 bits per NSS (1-8), with the values defined in enum
+                * ieee80211_he_mcs_support. Need to make sure STA TX and RX
+                * capabilities aren't less than the AP's minimum requirements
+                * for this HE BSS per SS.
+                * It is enough to find one such band that meets the reqs.
+                */
+               for (nss = 8; nss > 0; nss--) {
+                       u8 sta_rx_val = (sta_mcs_map_rx >> (2 * (nss - 1))) & 3;
+                       u8 sta_tx_val = (sta_mcs_map_tx >> (2 * (nss - 1))) & 3;
+                       u8 ap_val = (ap_min_req_set >> (2 * (nss - 1))) & 3;
 
-       if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
-               set_sta_flag(sta, WLAN_STA_MFP);
-               sta->sta.mfp = true;
-       } else {
-               sta->sta.mfp = false;
-       }
+                       if (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED)
+                               continue;
 
-       sta->sta.wme = (elems->wmm_param || elems->s1g_capab) &&
-                      local->hw.queues >= IEEE80211_NUM_ACS;
+                       /*
+                        * Make sure the HE AP doesn't require MCSs that aren't
+                        * supported by the client as required by spec
+                        *
+                        * P802.11-REVme/D0.3
+                        * 26.17.1 Basic HE BSS operation
+                        *
+                        * An HE STA shall not attempt to join * (MLME-JOIN.request primitive)
+                        * a BSS, unless it supports (i.e., is able to both transmit and
+                        * receive using) all of the <HE-MCS, NSS> tuples in the basic
+                        * HE-MCS and NSS set.
+                        */
+                       if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
+                           sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
+                           (ap_val > sta_rx_val) || (ap_val > sta_tx_val)) {
+                               verified = false;
+                               break;
+                       }
+               }
 
-       err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
-       if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
-               err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
-       if (err) {
-               sdata_info(sdata,
-                          "failed to move station %pM to desired state\n",
-                          sta->sta.addr);
-               WARN_ON(__sta_info_destroy(sta));
-               mutex_unlock(&sdata->local->sta_mtx);
-               ret = false;
-               goto out;
+               if (verified)
+                       return true;
        }
 
-       if (sdata->wdev.use_4addr)
-               drv_sta_set_4addr(local, sdata, &sta->sta, true);
-
-       mutex_unlock(&sdata->local->sta_mtx);
+       /* If here, STA doesn't meet AP's HE min requirements */
+       return false;
+}
 
-       /*
-        * Always handle WMM once after association regardless
-        * of the first value the AP uses. Setting -1 here has
-        * that effect because the AP values is an unsigned
-        * 4-bit value.
-        */
-       link->u.mgd.wmm_last_param_set = -1;
-       link->u.mgd.mu_edca_last_param_set = -1;
+static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
+                                 struct ieee80211_link_data *link,
+                                 struct cfg80211_bss *cbss)
+{
+       struct ieee80211_local *local = sdata->local;
+       const struct ieee80211_ht_cap *ht_cap = NULL;
+       const struct ieee80211_ht_operation *ht_oper = NULL;
+       const struct ieee80211_vht_operation *vht_oper = NULL;
+       const struct ieee80211_he_operation *he_oper = NULL;
+       const struct ieee80211_eht_operation *eht_oper = NULL;
+       const struct ieee80211_s1g_oper_ie *s1g_oper = NULL;
+       struct ieee80211_supported_band *sband;
+       struct cfg80211_chan_def chandef;
+       bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
+       bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ;
+       struct ieee80211_bss *bss = (void *)cbss->priv;
+       struct ieee802_11_elems *elems;
+       const struct cfg80211_bss_ies *ies;
+       int ret;
+       u32 i;
+       bool have_80mhz;
 
-       if (link->u.mgd.disable_wmm_tracking) {
-               ieee80211_set_wmm_default(link, false, false);
-       } else if (!ieee80211_sta_wmm_params(local, link, elems->wmm_param,
-                                            elems->wmm_param_len,
-                                            elems->mu_edca_param_set)) {
-               /* still enable QoS since we might have HT/VHT */
-               ieee80211_set_wmm_default(link, false, true);
-               /* disable WMM tracking 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.
-                */
-               link->u.mgd.disable_wmm_tracking = true;
+       rcu_read_lock();
+
+       ies = rcu_dereference(cbss->ies);
+       elems = ieee802_11_parse_elems(ies->data, ies->len, false, cbss);
+       if (!elems) {
+               rcu_read_unlock();
+               return -ENOMEM;
        }
-       changed |= BSS_CHANGED_QOS;
 
-       if (elems->max_idle_period_ie) {
-               bss_conf->max_idle_period =
-                       le16_to_cpu(elems->max_idle_period_ie->max_idle_period);
-               bss_conf->protected_keep_alive =
-                       !!(elems->max_idle_period_ie->idle_options &
-                          WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE);
-               changed |= BSS_CHANGED_KEEP_ALIVE;
-       } else {
-               bss_conf->max_idle_period = 0;
-               bss_conf->protected_keep_alive = false;
+       sband = local->hw.wiphy->bands[cbss->channel->band];
+
+       link->u.mgd.conn_flags &= ~(IEEE80211_CONN_DISABLE_40MHZ |
+                                            IEEE80211_CONN_DISABLE_80P80MHZ |
+                                            IEEE80211_CONN_DISABLE_160MHZ);
+
+       /* disable HT/VHT/HE if we don't support them */
+       if (!sband->ht_cap.ht_supported && !is_6ghz) {
+               mlme_dbg(sdata, "HT not supported, disabling HT/VHT/HE/EHT\n");
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HT;
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE;
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
        }
 
-       /* set assoc capability (AID was already set earlier),
-        * ieee80211_set_associated() will tell the driver */
-       bss_conf->assoc_capability = capab_info;
-       ieee80211_set_associated(sdata, cbss, changed);
+       if (!sband->vht_cap.vht_supported && is_5ghz) {
+               mlme_dbg(sdata, "VHT not supported, disabling VHT/HE/EHT\n");
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE;
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
+       }
 
-       /*
-        * If we're using 4-addr mode, let the AP know that we're
-        * doing so, so that it can create the STA VLAN on its side
-        */
-       if (ifmgd->use_4addr)
-               ieee80211_send_4addr_nullfunc(local, sdata);
+       if (!ieee80211_get_he_iftype_cap(sband,
+                                        ieee80211_vif_type_p2p(&sdata->vif))) {
+               mlme_dbg(sdata, "HE not supported, disabling HE and EHT\n");
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE;
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
+       }
 
-       /*
-        * Start timer to probe the connection to the AP now.
-        * Also start the timer that will detect beacon loss.
-        */
-       ieee80211_sta_reset_beacon_monitor(sdata);
-       ieee80211_sta_reset_conn_monitor(sdata);
+       if (!ieee80211_get_eht_iftype_cap(sband,
+                                         ieee80211_vif_type_p2p(&sdata->vif))) {
+               mlme_dbg(sdata, "EHT not supported, disabling EHT\n");
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
+       }
 
-       ret = true;
- out:
-       kfree(bss_ies);
-       return ret;
-}
+       if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) && !is_6ghz) {
+               ht_oper = elems->ht_operation;
+               ht_cap = elems->ht_cap_elem;
 
-static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
-                                        struct ieee80211_mgmt *mgmt,
-                                        size_t len)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
-       u16 capab_info, status_code, aid;
-       struct ieee802_11_elems *elems;
-       int ac;
-       u8 *pos;
-       bool reassoc;
-       struct cfg80211_bss *cbss;
-       struct ieee80211_event event = {
-               .type = MLME_EVENT,
-               .u.mlme.data = ASSOC_EVENT,
-       };
-       struct ieee80211_prep_tx_info info = {};
-       struct cfg80211_rx_assoc_resp resp = {
-               .uapsd_queues = -1,
-       };
+               if (!ht_cap) {
+                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HT;
+                       ht_oper = NULL;
+               }
+       }
 
-       sdata_assert_lock(sdata);
+       if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) && !is_6ghz) {
+               vht_oper = elems->vht_operation;
+               if (vht_oper && !ht_oper) {
+                       vht_oper = NULL;
+                       sdata_info(sdata,
+                                  "AP advertised VHT without HT, disabling HT/VHT/HE\n");
+                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HT;
+                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
+                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE;
+                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
+               }
 
-       if (!assoc_data)
-               return;
+               if (!elems->vht_cap_elem) {
+                       sdata_info(sdata,
+                                  "bad VHT capabilities, disabling VHT\n");
+                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
+                       vht_oper = NULL;
+               }
+       }
 
-       if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid))
-               return;
+       if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE)) {
+               he_oper = elems->he_operation;
 
-       cbss = assoc_data->bss;
+               if (is_6ghz) {
+                       struct ieee80211_bss_conf *bss_conf;
+                       u8 j = 0;
 
-       /*
-        * AssocResp and ReassocResp have identical structure, so process both
-        * of them in this function.
-        */
+                       bss_conf = link->conf;
 
-       if (len < 24 + 6)
-               return;
+                       if (elems->pwr_constr_elem)
+                               bss_conf->pwr_reduction = *elems->pwr_constr_elem;
 
-       reassoc = ieee80211_is_reassoc_resp(mgmt->frame_control);
-       capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
-       status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
-       pos = mgmt->u.assoc_resp.variable;
-       aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
-       if (cbss->channel->band == NL80211_BAND_S1GHZ) {
-               pos = (u8 *) mgmt->u.s1g_assoc_resp.variable;
-               aid = 0; /* TODO */
+                       BUILD_BUG_ON(ARRAY_SIZE(bss_conf->tx_pwr_env) !=
+                                    ARRAY_SIZE(elems->tx_pwr_env));
+
+                       for (i = 0; i < elems->tx_pwr_env_num; i++) {
+                               if (elems->tx_pwr_env_len[i] >
+                                   sizeof(bss_conf->tx_pwr_env[j]))
+                                       continue;
+
+                               bss_conf->tx_pwr_env_num++;
+                               memcpy(&bss_conf->tx_pwr_env[j], elems->tx_pwr_env[i],
+                                      elems->tx_pwr_env_len[i]);
+                               j++;
+                       }
+               }
+
+               if (!ieee80211_verify_peer_he_mcs_support(sdata, ies, he_oper) ||
+                   !ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper))
+                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE |
+                                                 IEEE80211_CONN_DISABLE_EHT;
        }
 
        /*
-        * Note: this may not be perfect, AP might misbehave - if
-        * anyone needs to rely on perfect complete notification
-        * with the exact right subtype, then we need to track what
-        * we actually transmitted.
+        * EHT requires HE to be supported as well. Specifically for 6 GHz
+        * channels, the operation channel information can only be deduced from
+        * both the 6 GHz operation information (from the HE operation IE) and
+        * EHT operation.
         */
-       info.subtype = reassoc ? IEEE80211_STYPE_REASSOC_REQ :
-                                IEEE80211_STYPE_ASSOC_REQ;
-
-       sdata_info(sdata,
-                  "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n",
-                  reassoc ? "Rea" : "A", mgmt->sa,
-                  capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
+       if (!(link->u.mgd.conn_flags &
+                       (IEEE80211_CONN_DISABLE_HE |
+                        IEEE80211_CONN_DISABLE_EHT)) &&
+           he_oper) {
+               const struct cfg80211_bss_ies *cbss_ies;
+               const u8 *eht_oper_ie;
 
-       if (assoc_data->fils_kek_len &&
-           fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0)
-               return;
+               cbss_ies = rcu_dereference(cbss->ies);
+               eht_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_EHT_OPERATION,
+                                                  cbss_ies->data, cbss_ies->len);
+               if (eht_oper_ie && eht_oper_ie[1] >=
+                   1 + sizeof(struct ieee80211_eht_operation))
+                       eht_oper = (void *)(eht_oper_ie + 3);
+               else
+                       eht_oper = NULL;
+       }
 
-       elems = ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false,
-                                      assoc_data->bss);
-       if (!elems)
-               goto notify_driver;
+       /* 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;
 
-       if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
-           elems->timeout_int &&
-           elems->timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
-               u32 tu, ms;
+               have_80mhz = true;
+               break;
+       }
 
-               cfg80211_assoc_comeback(sdata->dev, assoc_data->bss->bssid,
-                                       le32_to_cpu(elems->timeout_int->value));
+       if (!have_80mhz) {
+               sdata_info(sdata, "80 MHz not supported, disabling VHT\n");
+               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
+       }
 
-               tu = le32_to_cpu(elems->timeout_int->value);
-               ms = tu * 1024 / 1000;
-               sdata_info(sdata,
-                          "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
-                          mgmt->sa, tu, ms);
-               assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
-               assoc_data->timeout_started = true;
-               if (ms > IEEE80211_ASSOC_TIMEOUT)
-                       run_again(sdata, assoc_data->timeout);
-               goto notify_driver;
+       if (sband->band == NL80211_BAND_S1GHZ) {
+               s1g_oper = elems->s1g_oper;
+               if (!s1g_oper)
+                       sdata_info(sdata,
+                                  "AP missing S1G operation element?\n");
        }
 
-       if (status_code != WLAN_STATUS_SUCCESS) {
-               sdata_info(sdata, "%pM denied association (code=%d)\n",
-                          mgmt->sa, status_code);
-               ieee80211_destroy_assoc_data(sdata, ASSOC_REJECTED);
-               event.u.mlme.status = MLME_DENIED;
-               event.u.mlme.reason = status_code;
-               drv_event_callback(sdata->local, sdata, &event);
-       } else {
-               if (!ieee80211_assoc_success(sdata, cbss, mgmt, len, elems)) {
-                       /* oops -- internal error -- send timeout for now */
-                       ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT);
-                       goto notify_driver;
-               }
-               event.u.mlme.status = MLME_SUCCESS;
-               drv_event_callback(sdata->local, sdata, &event);
-               sdata_info(sdata, "associated\n");
+       link->u.mgd.conn_flags |=
+               ieee80211_determine_chantype(link, sband,
+                                            cbss->channel,
+                                            bss->vht_cap_info,
+                                            ht_oper, vht_oper,
+                                            he_oper, eht_oper,
+                                            s1g_oper,
+                                            &chandef, false);
 
-               /*
-                * destroy assoc_data afterwards, as otherwise an idle
-                * recalc after assoc_data is NULL but before associated
-                * is set can cause the interface to go idle
-                */
-               ieee80211_destroy_assoc_data(sdata, ASSOC_SUCCESS);
+       link->needed_rx_chains =
+               min(ieee80211_max_rx_chains(link, cbss), local->rx_chains);
 
-               /* get uapsd queues configuration */
-               resp.uapsd_queues = 0;
-               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-                       if (sdata->deflink.tx_conf[ac].uapsd)
-                               resp.uapsd_queues |= ieee80211_ac_to_qos_mask[ac];
+       rcu_read_unlock();
+       /* the element data was RCU protected so no longer valid anyway */
+       kfree(elems);
+       elems = NULL;
 
-               info.success = 1;
+       if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE && is_6ghz) {
+               sdata_info(sdata, "Rejecting non-HE 6/7 GHz connection");
+               return -EINVAL;
        }
 
-       resp.links[0].bss = cbss;
-       resp.buf = (u8 *)mgmt;
-       resp.len = len;
-       resp.req_ies = ifmgd->assoc_req_ies;
-       resp.req_ies_len = ifmgd->assoc_req_ies_len;
-       cfg80211_rx_assoc_resp(sdata->dev, &resp);
-notify_driver:
-       drv_mgd_complete_tx(sdata->local, sdata, &info);
-       kfree(elems);
-}
-
-static void ieee80211_rx_bss_info(struct ieee80211_link_data *link,
-                                 struct ieee80211_mgmt *mgmt, size_t len,
-                                 struct ieee80211_rx_status *rx_status)
-{
-       struct ieee80211_sub_if_data *sdata = link->sdata;
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_bss *bss;
-       struct ieee80211_channel *channel;
+       /* will change later if needed */
+       link->smps_mode = IEEE80211_SMPS_OFF;
 
-       sdata_assert_lock(sdata);
+       mutex_lock(&local->mtx);
+       /*
+        * If this fails (possibly due to channel context sharing
+        * on incompatible channels, e.g. 80+80 and 160 sharing the
+        * same control channel) try to use a smaller bandwidth.
+        */
+       ret = ieee80211_link_use_channel(link, &chandef,
+                                        IEEE80211_CHANCTX_SHARED);
 
-       channel = ieee80211_get_channel_khz(local->hw.wiphy,
-                                       ieee80211_rx_status_to_khz(rx_status));
-       if (!channel)
-               return;
+       /* don't downgrade for 5 and 10 MHz channels, though. */
+       if (chandef.width == NL80211_CHAN_WIDTH_5 ||
+           chandef.width == NL80211_CHAN_WIDTH_10)
+               goto out;
 
-       bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel);
-       if (bss) {
-               link->conf->beacon_rate = bss->beacon_rate;
-               ieee80211_rx_bss_put(local, bss);
+       while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
+               link->u.mgd.conn_flags |=
+                       ieee80211_chandef_downgrade(&chandef);
+               ret = ieee80211_link_use_channel(link, &chandef,
+                                                IEEE80211_CHANCTX_SHARED);
        }
+ out:
+       mutex_unlock(&local->mtx);
+       return ret;
 }
 
-
-static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_link_data *link,
-                                        struct sk_buff *skb)
+static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
+                                   struct cfg80211_bss *cbss,
+                                   struct ieee80211_mgmt *mgmt, size_t len,
+                                   struct ieee802_11_elems *elems)
 {
-       struct ieee80211_sub_if_data *sdata = link->sdata;
-       struct ieee80211_mgmt *mgmt = (void *)skb->data;
-       struct ieee80211_if_managed *ifmgd;
-       struct ieee80211_rx_status *rx_status = (void *) skb->cb;
-       struct ieee80211_channel *channel;
-       size_t baselen, len = skb->len;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_supported_band *sband;
+       struct link_sta_info *link_sta;
+       struct sta_info *sta;
+       u16 capab_info, aid;
+       struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
+       const struct cfg80211_bss_ies *bss_ies = NULL;
+       struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
+       bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
+       bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
+       struct ieee80211_link_data *link = &sdata->deflink;
+       u32 changed = 0;
+       int err;
+       bool ret;
 
-       ifmgd = &sdata->u.mgd;
+       capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
 
-       sdata_assert_lock(sdata);
+       if (elems->aid_resp)
+               aid = le16_to_cpu(elems->aid_resp->aid);
+       else if (is_s1g)
+               aid = 0; /* TODO */
+       else
+               aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
 
        /*
-        * According to Draft P802.11ax D6.0 clause 26.17.2.3.2:
-        * "If a 6 GHz AP receives a Probe Request frame  and responds with
-        * a Probe Response frame [..], the Address 1 field of the Probe
-        * Response frame shall be set to the broadcast address [..]"
-        * So, on 6GHz band we should also accept broadcast responses.
+        * The 5 MSB of the AID field are reserved
+        * (802.11-2016 9.4.1.8 AID field)
         */
-       channel = ieee80211_get_channel(sdata->local->hw.wiphy,
-                                       rx_status->freq);
-       if (!channel)
-               return;
+       aid &= 0x7ff;
 
-       if (!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
-           (channel->band != NL80211_BAND_6GHZ ||
-            !is_broadcast_ether_addr(mgmt->da)))
-               return; /* ignore ProbeResp to foreign address */
+       ifmgd->broken_ap = false;
 
-       baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
-       if (baselen > len)
-               return;
+       if (aid == 0 || aid > IEEE80211_MAX_AID) {
+               sdata_info(sdata, "invalid AID value %d (out of range), turn off PS\n",
+                          aid);
+               aid = 0;
+               ifmgd->broken_ap = true;
+       }
 
-       ieee80211_rx_bss_info(link, mgmt, len, rx_status);
+       if (!is_s1g && !elems->supp_rates) {
+               sdata_info(sdata, "no SuppRates element in AssocResp\n");
+               ret = false;
+               goto out;
+       }
 
-       if (ifmgd->associated &&
-           ether_addr_equal(mgmt->bssid, link->u.mgd.bssid))
-               ieee80211_reset_ap_probe(sdata);
-}
+       sdata->vif.cfg.aid = aid;
+       sdata->deflink.u.mgd.tdls_chan_switch_prohibited =
+               elems->ext_capab && elems->ext_capab_len >= 5 &&
+               (elems->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
 
-/*
- * This is the canonical list of information elements we care about,
- * the filter code also gives us all changes to the Microsoft OUI
- * (00:50:F2) vendor IE which is used for WMM which we need to track,
- * as well as the DTPC IE (part of the Cisco OUI) used for signaling
- * changes to requested client power.
- *
- * We implement beacon filtering in software since that means we can
- * avoid processing the frame here and in cfg80211, and userspace
- * will not be able to tell whether the hardware supports it or not.
- *
- * XXX: This list needs to be dynamic -- userspace needs to be able to
- *     add items it requires. It also needs to be able to tell us to
- *     look out for other vendor IEs.
- */
-static const u64 care_about_ies =
-       (1ULL << WLAN_EID_COUNTRY) |
-       (1ULL << WLAN_EID_ERP_INFO) |
-       (1ULL << WLAN_EID_CHANNEL_SWITCH) |
-       (1ULL << WLAN_EID_PWR_CONSTRAINT) |
-       (1ULL << WLAN_EID_HT_CAPABILITY) |
-       (1ULL << WLAN_EID_HT_OPERATION) |
-       (1ULL << WLAN_EID_EXT_CHANSWITCH_ANN);
+       /*
+        * Some APs are erroneously not including some information in their
+        * (re)association response frames. Try to recover by using the data
+        * from the beacon or probe response. This seems to afflict mobile
+        * 2G/3G/4G wifi routers, reported models include the "Onda PN51T",
+        * "Vodafone PocketWiFi 2", "ZTE MF60" and a similar T-Mobile device.
+        */
+       if (!is_6ghz &&
+           ((assoc_data->wmm && !elems->wmm_param) ||
+            (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) &&
+             (!elems->ht_cap_elem || !elems->ht_operation)) ||
+            (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) &&
+             (!elems->vht_cap_elem || !elems->vht_operation)))) {
+               const struct cfg80211_bss_ies *ies;
+               struct ieee802_11_elems *bss_elems;
 
-static void ieee80211_handle_beacon_sig(struct ieee80211_link_data *link,
-                                       struct ieee80211_if_managed *ifmgd,
-                                       struct ieee80211_bss_conf *bss_conf,
-                                       struct ieee80211_local *local,
-                                       struct ieee80211_rx_status *rx_status)
-{
-       struct ieee80211_sub_if_data *sdata = link->sdata;
+               rcu_read_lock();
+               ies = rcu_dereference(cbss->ies);
+               if (ies)
+                       bss_ies = kmemdup(ies, sizeof(*ies) + ies->len,
+                                         GFP_ATOMIC);
+               rcu_read_unlock();
+               if (!bss_ies) {
+                       ret = false;
+                       goto out;
+               }
 
-       /* Track average RSSI from the Beacon frames of the current AP */
+               bss_elems = ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
+                                                  false, assoc_data->bss);
+               if (!bss_elems) {
+                       ret = false;
+                       goto out;
+               }
 
-       if (!link->u.mgd.tracking_signal_avg) {
-               link->u.mgd.tracking_signal_avg = true;
-               ewma_beacon_signal_init(&link->u.mgd.ave_beacon_signal);
-               link->u.mgd.last_cqm_event_signal = 0;
-               link->u.mgd.count_beacon_signal = 1;
-               link->u.mgd.last_ave_beacon_signal = 0;
-       } else {
-               link->u.mgd.count_beacon_signal++;
-       }
-
-       ewma_beacon_signal_add(&link->u.mgd.ave_beacon_signal,
-                              -rx_status->signal);
-
-       if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold &&
-           link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
-               int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal);
-               int last_sig = link->u.mgd.last_ave_beacon_signal;
-               struct ieee80211_event event = {
-                       .type = RSSI_EVENT,
-               };
+               if (assoc_data->wmm &&
+                   !elems->wmm_param && bss_elems->wmm_param) {
+                       elems->wmm_param = bss_elems->wmm_param;
+                       sdata_info(sdata,
+                                  "AP bug: WMM param missing from AssocResp\n");
+               }
 
                /*
-                * if signal crosses either of the boundaries, invoke callback
-                * with appropriate parameters
+                * Also check if we requested HT/VHT, otherwise the AP doesn't
+                * have to include the IEs in the (re)association response.
                 */
-               if (sig > ifmgd->rssi_max_thold &&
-                   (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
-                       link->u.mgd.last_ave_beacon_signal = sig;
-                       event.u.rssi.data = RSSI_EVENT_HIGH;
-                       drv_event_callback(local, sdata, &event);
-               } else if (sig < ifmgd->rssi_min_thold &&
-                          (last_sig >= ifmgd->rssi_max_thold ||
-                          last_sig == 0)) {
-                       link->u.mgd.last_ave_beacon_signal = sig;
-                       event.u.rssi.data = RSSI_EVENT_LOW;
-                       drv_event_callback(local, sdata, &event);
+               if (!elems->ht_cap_elem && bss_elems->ht_cap_elem &&
+                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) {
+                       elems->ht_cap_elem = bss_elems->ht_cap_elem;
+                       sdata_info(sdata,
+                                  "AP bug: HT capability missing from AssocResp\n");
                }
-       }
-
-       if (bss_conf->cqm_rssi_thold &&
-           link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT &&
-           !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) {
-               int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal);
-               int last_event = link->u.mgd.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)) {
-                       link->u.mgd.last_cqm_event_signal = sig;
-                       ieee80211_cqm_rssi_notify(
-                               &sdata->vif,
-                               NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
-                               sig, GFP_KERNEL);
-               } else if (sig > thold &&
-                          (last_event == 0 || sig > last_event + hyst)) {
-                       link->u.mgd.last_cqm_event_signal = sig;
-                       ieee80211_cqm_rssi_notify(
-                               &sdata->vif,
-                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
-                               sig, GFP_KERNEL);
+               if (!elems->ht_operation && bss_elems->ht_operation &&
+                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) {
+                       elems->ht_operation = bss_elems->ht_operation;
+                       sdata_info(sdata,
+                                  "AP bug: HT operation missing from AssocResp\n");
                }
-       }
-
-       if (bss_conf->cqm_rssi_low &&
-           link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
-               int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal);
-               int last_event = link->u.mgd.last_cqm_event_signal;
-               int low = bss_conf->cqm_rssi_low;
-               int high = bss_conf->cqm_rssi_high;
-
-               if (sig < low &&
-                   (last_event == 0 || last_event >= low)) {
-                       link->u.mgd.last_cqm_event_signal = sig;
-                       ieee80211_cqm_rssi_notify(
-                               &sdata->vif,
-                               NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
-                               sig, GFP_KERNEL);
-               } else if (sig > high &&
-                          (last_event == 0 || last_event <= high)) {
-                       link->u.mgd.last_cqm_event_signal = sig;
-                       ieee80211_cqm_rssi_notify(
-                               &sdata->vif,
-                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
-                               sig, GFP_KERNEL);
+               if (!elems->vht_cap_elem && bss_elems->vht_cap_elem &&
+                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) {
+                       elems->vht_cap_elem = bss_elems->vht_cap_elem;
+                       sdata_info(sdata,
+                                  "AP bug: VHT capa missing from AssocResp\n");
+               }
+               if (!elems->vht_operation && bss_elems->vht_operation &&
+                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) {
+                       elems->vht_operation = bss_elems->vht_operation;
+                       sdata_info(sdata,
+                                  "AP bug: VHT operation missing from AssocResp\n");
                }
-       }
-}
-
-static bool ieee80211_rx_our_beacon(const u8 *tx_bssid,
-                                   struct cfg80211_bss *bss)
-{
-       if (ether_addr_equal(tx_bssid, bss->bssid))
-               return true;
-       if (!bss->transmitted_bss)
-               return false;
-       return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid);
-}
-
-static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
-                                    struct ieee80211_hdr *hdr, size_t len,
-                                    struct ieee80211_rx_status *rx_status)
-{
-       struct ieee80211_sub_if_data *sdata = link->sdata;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
-       struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg;
-       struct ieee80211_mgmt *mgmt = (void *) hdr;
-       size_t baselen;
-       struct ieee802_11_elems *elems;
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       struct ieee80211_channel *chan;
-       struct link_sta_info *link_sta;
-       struct sta_info *sta;
-       u32 changed = 0;
-       bool erp_valid;
-       u8 erp_value = 0;
-       u32 ncrc = 0;
-       u8 *bssid, *variable = mgmt->u.beacon.variable;
-       u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
-
-       sdata_assert_lock(sdata);
-
-       /* Process beacon from the current BSS */
-       bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type);
-       if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
-               struct ieee80211_ext *ext = (void *) mgmt;
 
-               if (ieee80211_is_s1g_short_beacon(ext->frame_control))
-                       variable = ext->u.s1g_short_beacon.variable;
-               else
-                       variable = ext->u.s1g_beacon.variable;
+               kfree(bss_elems);
        }
 
-       baselen = (u8 *) variable - (u8 *) mgmt;
-       if (baselen > len)
-               return;
-
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
-       if (!chanctx_conf) {
-               rcu_read_unlock();
-               return;
+       /*
+        * We previously checked these in the beacon/probe response, so
+        * they should be present here. This is just a safety net.
+        */
+       if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) &&
+           (!elems->wmm_param || !elems->ht_cap_elem || !elems->ht_operation)) {
+               sdata_info(sdata,
+                          "HT AP is missing WMM params or HT capability/operation\n");
+               ret = false;
+               goto out;
        }
 
-       if (ieee80211_rx_status_to_khz(rx_status) !=
-           ieee80211_channel_to_khz(chanctx_conf->def.chan)) {
-               rcu_read_unlock();
-               return;
+       if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) &&
+           (!elems->vht_cap_elem || !elems->vht_operation)) {
+               sdata_info(sdata,
+                          "VHT AP is missing VHT capability/operation\n");
+               ret = false;
+               goto out;
        }
-       chan = chanctx_conf->def.chan;
-       rcu_read_unlock();
-
-       if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
-           ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->bss)) {
-               elems = ieee802_11_parse_elems(variable, len - baselen, false,
-                                              ifmgd->assoc_data->bss);
-               if (!elems)
-                       return;
 
-               ieee80211_rx_bss_info(link, mgmt, len, rx_status);
+       if (is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
+           !elems->he_6ghz_capa) {
+               sdata_info(sdata,
+                          "HE 6 GHz AP is missing HE 6 GHz band capability\n");
+               ret = false;
+               goto out;
+       }
 
-               if (elems->dtim_period)
-                       link->u.mgd.dtim_period = elems->dtim_period;
-               link->u.mgd.have_beacon = true;
-               ifmgd->assoc_data->need_beacon = false;
-               if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
-                       link->conf->sync_tsf =
-                               le64_to_cpu(mgmt->u.beacon.timestamp);
-                       link->conf->sync_device_ts =
-                               rx_status->device_timestamp;
-                       link->conf->sync_dtim_count = elems->dtim_count;
-               }
+       mutex_lock(&sdata->local->sta_mtx);
+       /*
+        * station info was already allocated and inserted before
+        * the association and should be available to us
+        */
+       sta = sta_info_get(sdata, cbss->bssid);
+       if (WARN_ON(!sta)) {
+               mutex_unlock(&sdata->local->sta_mtx);
+               ret = false;
+               goto out;
+       }
 
-               if (elems->mbssid_config_ie)
-                       bss_conf->profile_periodicity =
-                               elems->mbssid_config_ie->profile_periodicity;
-               else
-                       bss_conf->profile_periodicity = 0;
+       link_sta = rcu_dereference_protected(sta->link[link->link_id],
+                                            lockdep_is_held(&local->sta_mtx));
+       if (WARN_ON(!link_sta)) {
+               mutex_unlock(&sdata->local->sta_mtx);
+               ret = false;
+               goto out;
+       }
 
-               if (elems->ext_capab_len >= 11 &&
-                   (elems->ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
-                       bss_conf->ema_ap = true;
-               else
-                       bss_conf->ema_ap = false;
+       sband = ieee80211_get_link_sband(link);
+       if (!sband) {
+               mutex_unlock(&sdata->local->sta_mtx);
+               ret = false;
+               goto out;
+       }
 
-               /* continue assoc process */
-               ifmgd->assoc_data->timeout = jiffies;
-               ifmgd->assoc_data->timeout_started = true;
-               run_again(sdata, ifmgd->assoc_data->timeout);
-               kfree(elems);
-               return;
+       if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
+           (!elems->he_cap || !elems->he_operation)) {
+               mutex_unlock(&sdata->local->sta_mtx);
+               sdata_info(sdata,
+                          "HE AP is missing HE capability/operation\n");
+               ret = false;
+               goto out;
        }
 
-       if (!ifmgd->associated ||
-           !ieee80211_rx_our_beacon(bssid, link->u.mgd.bss))
-               return;
-       bssid = link->u.mgd.bssid;
+       /* Set up internal HT/VHT capabilities */
+       if (elems->ht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT))
+               ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
+                                                 elems->ht_cap_elem,
+                                                 link_sta);
 
-       if (!(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL))
-               ieee80211_handle_beacon_sig(link, ifmgd, bss_conf,
-                                           local, rx_status);
+       if (elems->vht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT))
+               ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+                                                   elems->vht_cap_elem,
+                                                   link_sta);
 
-       if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
-               mlme_dbg_ratelimited(sdata,
-                                    "cancelling AP probe due to a received beacon\n");
-               ieee80211_reset_ap_probe(sdata);
-       }
+       if (elems->he_operation && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
+           elems->he_cap) {
+               ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
+                                                 elems->he_cap,
+                                                 elems->he_cap_len,
+                                                 elems->he_6ghz_capa,
+                                                 link_sta);
 
-       /*
-        * Push the beacon loss detection into the future since
-        * we are processing a beacon from the AP just now.
-        */
-       ieee80211_sta_reset_beacon_monitor(sdata);
+               bss_conf->he_support = link_sta->pub->he_cap.has_he;
+               if (elems->rsnx && elems->rsnx_len &&
+                   (elems->rsnx[0] & WLAN_RSNX_CAPA_PROTECTED_TWT) &&
+                   wiphy_ext_feature_isset(local->hw.wiphy,
+                                           NL80211_EXT_FEATURE_PROTECTED_TWT))
+                       bss_conf->twt_protected = true;
+               else
+                       bss_conf->twt_protected = false;
 
-       /* TODO: CRC urrently not calculated on S1G Beacon Compatibility
-        * element (which carries the beacon interval). Don't forget to add a
-        * bit to care_about_ies[] above if mac80211 is interested in a
-        * changing S1G element.
-        */
-       if (!ieee80211_is_s1g_beacon(hdr->frame_control))
-               ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
-       elems = ieee802_11_parse_elems_crc(variable, len - baselen,
-                                          false, care_about_ies, ncrc,
-                                          link->u.mgd.bss);
-       if (!elems)
-               return;
-       ncrc = elems->crc;
+               changed |= ieee80211_recalc_twt_req(link, link_sta, elems);
 
-       if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
-           ieee80211_check_tim(elems->tim, elems->tim_len, vif_cfg->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;
+               if (elems->eht_operation && elems->eht_cap &&
+                   !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) {
+                       ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband,
+                                                           elems->he_cap,
+                                                           elems->he_cap_len,
+                                                           elems->eht_cap,
+                                                           elems->eht_cap_len,
+                                                           link_sta);
 
-                       /*
-                        * 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);
+                       bss_conf->eht_support = link_sta->pub->eht_cap.has_eht;
+               } else {
+                       bss_conf->eht_support = false;
                }
+       } else {
+               bss_conf->he_support = false;
+               bss_conf->twt_requester = false;
+               bss_conf->twt_protected = false;
+               bss_conf->eht_support = false;
        }
 
-       if (sdata->vif.p2p ||
-           sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
-               struct ieee80211_p2p_noa_attr noa = {};
-               int ret;
+       bss_conf->twt_broadcast =
+               ieee80211_twt_bcast_support(sdata, bss_conf, sband, link_sta);
 
-               ret = cfg80211_get_p2p_attr(variable,
-                                           len - baselen,
-                                           IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
-                                           (u8 *) &noa, sizeof(noa));
-               if (ret >= 2) {
-                       if (link->u.mgd.p2p_noa_index != noa.index) {
-                               /* valid noa_attr and index changed */
-                               link->u.mgd.p2p_noa_index = noa.index;
-                               memcpy(&bss_conf->p2p_noa_attr, &noa, sizeof(noa));
-                               changed |= BSS_CHANGED_P2P_PS;
-                               /*
-                                * make sure we update all information, the CRC
-                                * mechanism doesn't look at P2P attributes.
-                                */
-                               link->u.mgd.beacon_crc_valid = false;
-                       }
-               } else if (link->u.mgd.p2p_noa_index != -1) {
-                       /* noa_attr not found and we had valid noa_attr before */
-                       link->u.mgd.p2p_noa_index = -1;
-                       memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr));
-                       changed |= BSS_CHANGED_P2P_PS;
-                       link->u.mgd.beacon_crc_valid = false;
-               }
-       }
+       if (bss_conf->he_support) {
+               bss_conf->he_bss_color.color =
+                       le32_get_bits(elems->he_operation->he_oper_params,
+                                     IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
+               bss_conf->he_bss_color.partial =
+                       le32_get_bits(elems->he_operation->he_oper_params,
+                                     IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR);
+               bss_conf->he_bss_color.enabled =
+                       !le32_get_bits(elems->he_operation->he_oper_params,
+                                      IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED);
 
-       if (link->u.mgd.csa_waiting_bcn)
-               ieee80211_chswitch_post_beacon(link);
+               if (bss_conf->he_bss_color.enabled)
+                       changed |= BSS_CHANGED_HE_BSS_COLOR;
 
-       /*
-        * Update beacon timing and dtim count on every beacon appearance. This
-        * will allow the driver to use the most updated values. Do it before
-        * comparing this one with last received beacon.
-        * IMPORTANT: These parameters would possibly be out of sync by the time
-        * the driver will use them. The synchronized view is currently
-        * guaranteed only in certain callbacks.
-        */
-       if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY) &&
-           !ieee80211_is_s1g_beacon(hdr->frame_control)) {
-               link->conf->sync_tsf =
-                       le64_to_cpu(mgmt->u.beacon.timestamp);
-               link->conf->sync_device_ts =
-                       rx_status->device_timestamp;
-               link->conf->sync_dtim_count = elems->dtim_count;
-       }
+               bss_conf->htc_trig_based_pkt_ext =
+                       le32_get_bits(elems->he_operation->he_oper_params,
+                             IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK);
+               bss_conf->frame_time_rts_th =
+                       le32_get_bits(elems->he_operation->he_oper_params,
+                             IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
 
-       if ((ncrc == link->u.mgd.beacon_crc && link->u.mgd.beacon_crc_valid) ||
-           ieee80211_is_s1g_short_beacon(mgmt->frame_control))
-               goto free;
-       link->u.mgd.beacon_crc = ncrc;
-       link->u.mgd.beacon_crc_valid = true;
+               bss_conf->uora_exists = !!elems->uora_element;
+               if (elems->uora_element)
+                       bss_conf->uora_ocw_range = elems->uora_element[0];
 
-       ieee80211_rx_bss_info(link, mgmt, len, rx_status);
+               ieee80211_he_op_ie_to_bss_conf(&sdata->vif, elems->he_operation);
+               ieee80211_he_spr_ie_to_bss_conf(&sdata->vif, elems->he_spr);
+               /* TODO: OPEN: what happens if BSS color disable is set? */
+       }
 
-       ieee80211_sta_process_chanswitch(link, rx_status->mactime,
-                                        rx_status->device_timestamp,
-                                        elems, true);
+       if (cbss->transmitted_bss) {
+               bss_conf->nontransmitted = true;
+               ether_addr_copy(bss_conf->transmitter_bssid,
+                               cbss->transmitted_bss->bssid);
+               bss_conf->bssid_indicator = cbss->max_bssid_indicator;
+               bss_conf->bssid_index = cbss->bssid_index;
+       } else {
+               bss_conf->nontransmitted = false;
+               memset(bss_conf->transmitter_bssid, 0,
+                      sizeof(bss_conf->transmitter_bssid));
+               bss_conf->bssid_indicator = 0;
+               bss_conf->bssid_index = 0;
+       }
 
-       if (!link->u.mgd.disable_wmm_tracking &&
-           ieee80211_sta_wmm_params(local, link, elems->wmm_param,
-                                    elems->wmm_param_len,
-                                    elems->mu_edca_param_set))
-               changed |= BSS_CHANGED_QOS;
+       /*
+        * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data
+        * in their association response, so ignore that data for our own
+        * configuration. If it changed since the last beacon, we'll get the
+        * next beacon and update then.
+        */
 
        /*
-        * If we haven't had a beacon before, tell the driver about the
-        * DTIM period (and beacon timing if desired) now.
+        * If an operating mode notification IE is present, override the
+        * NSS calculation (that would be done in rate_control_rate_init())
+        * and use the # of streams from that element.
         */
-       if (!link->u.mgd.have_beacon) {
-               /* a few bogus AP send dtim_period = 0 or no TIM IE */
-               bss_conf->dtim_period = elems->dtim_period ?: 1;
+       if (elems->opmode_notif &&
+           !(*elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) {
+               u8 nss;
 
-               changed |= BSS_CHANGED_BEACON_INFO;
-               link->u.mgd.have_beacon = true;
+               nss = *elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
+               nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
+               nss += 1;
+               link_sta->pub->rx_nss = nss;
+       }
 
-               mutex_lock(&local->iflist_mtx);
-               ieee80211_recalc_ps(local);
-               mutex_unlock(&local->iflist_mtx);
+       rate_control_rate_init(sta);
 
-               ieee80211_recalc_ps_vif(sdata);
+       if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
+               set_sta_flag(sta, WLAN_STA_MFP);
+               sta->sta.mfp = true;
+       } else {
+               sta->sta.mfp = false;
        }
 
-       if (elems->erp_info) {
-               erp_valid = true;
-               erp_value = elems->erp_info[0];
-       } else {
-               erp_valid = false;
+       sta->sta.wme = (elems->wmm_param || elems->s1g_capab) &&
+                      local->hw.queues >= IEEE80211_NUM_ACS;
+
+       err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+       if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
+               err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+       if (err) {
+               sdata_info(sdata,
+                          "failed to move station %pM to desired state\n",
+                          sta->sta.addr);
+               WARN_ON(__sta_info_destroy(sta));
+               mutex_unlock(&sdata->local->sta_mtx);
+               ret = false;
+               goto out;
        }
 
-       if (!ieee80211_is_s1g_beacon(hdr->frame_control))
-               changed |= ieee80211_handle_bss_capability(link,
-                               le16_to_cpu(mgmt->u.beacon.capab_info),
-                               erp_valid, erp_value);
+       if (sdata->wdev.use_4addr)
+               drv_sta_set_4addr(local, sdata, &sta->sta, true);
 
-       mutex_lock(&local->sta_mtx);
-       sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
-       if (WARN_ON(!sta))
-               goto free;
-       link_sta = rcu_dereference_protected(sta->link[link->link_id],
-                                            lockdep_is_held(&local->sta_mtx));
-       if (WARN_ON(!link_sta))
-               goto free;
+       mutex_unlock(&sdata->local->sta_mtx);
 
-       changed |= ieee80211_recalc_twt_req(link, link_sta, elems);
+       /*
+        * Always handle WMM once after association regardless
+        * of the first value the AP uses. Setting -1 here has
+        * that effect because the AP values is an unsigned
+        * 4-bit value.
+        */
+       link->u.mgd.wmm_last_param_set = -1;
+       link->u.mgd.mu_edca_last_param_set = -1;
 
-       if (ieee80211_config_bw(link, elems->ht_cap_elem,
-                               elems->vht_cap_elem, elems->ht_operation,
-                               elems->vht_operation, elems->he_operation,
-                               elems->eht_operation,
-                               elems->s1g_oper, bssid, &changed)) {
-               mutex_unlock(&local->sta_mtx);
-               sdata_info(sdata,
-                          "failed to follow AP %pM bandwidth change, disconnect\n",
-                          bssid);
-               ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
-                                      WLAN_REASON_DEAUTH_LEAVING,
-                                      true, deauth_buf);
-               ieee80211_report_disconnect(sdata, deauth_buf,
-                                           sizeof(deauth_buf), true,
-                                           WLAN_REASON_DEAUTH_LEAVING,
-                                           false);
-               goto free;
+       if (link->u.mgd.disable_wmm_tracking) {
+               ieee80211_set_wmm_default(link, false, false);
+       } else if (!ieee80211_sta_wmm_params(local, link, elems->wmm_param,
+                                            elems->wmm_param_len,
+                                            elems->mu_edca_param_set)) {
+               /* still enable QoS since we might have HT/VHT */
+               ieee80211_set_wmm_default(link, false, true);
+               /* disable WMM tracking 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.
+                */
+               link->u.mgd.disable_wmm_tracking = true;
        }
+       changed |= BSS_CHANGED_QOS;
 
-       if (sta && elems->opmode_notif)
-               ieee80211_vht_handle_opmode(sdata, link_sta,
-                                           *elems->opmode_notif,
-                                           rx_status->band);
-       mutex_unlock(&local->sta_mtx);
-
-       changed |= ieee80211_handle_pwr_constr(link, chan, mgmt,
-                                              elems->country_elem,
-                                              elems->country_elem_len,
-                                              elems->pwr_constr_elem,
-                                              elems->cisco_dtpc_elem);
+       if (elems->max_idle_period_ie) {
+               bss_conf->max_idle_period =
+                       le16_to_cpu(elems->max_idle_period_ie->max_idle_period);
+               bss_conf->protected_keep_alive =
+                       !!(elems->max_idle_period_ie->idle_options &
+                          WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE);
+               changed |= BSS_CHANGED_KEEP_ALIVE;
+       } else {
+               bss_conf->max_idle_period = 0;
+               bss_conf->protected_keep_alive = false;
+       }
 
-       ieee80211_link_info_change_notify(sdata, link, changed);
-free:
-       kfree(elems);
-}
+       /* set assoc capability (AID was already set earlier),
+        * ieee80211_set_associated() will tell the driver */
+       bss_conf->assoc_capability = capab_info;
+       ieee80211_set_associated(sdata, cbss, changed);
 
-void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata,
-                                struct sk_buff *skb)
-{
-       struct ieee80211_link_data *link = &sdata->deflink;
-       struct ieee80211_rx_status *rx_status;
-       struct ieee80211_hdr *hdr;
-       u16 fc;
+       /*
+        * If we're using 4-addr mode, let the AP know that we're
+        * doing so, so that it can create the STA VLAN on its side
+        */
+       if (ifmgd->use_4addr)
+               ieee80211_send_4addr_nullfunc(local, sdata);
 
-       rx_status = (struct ieee80211_rx_status *) skb->cb;
-       hdr = (struct ieee80211_hdr *) skb->data;
-       fc = le16_to_cpu(hdr->frame_control);
+       /*
+        * Start timer to probe the connection to the AP now.
+        * Also start the timer that will detect beacon loss.
+        */
+       ieee80211_sta_reset_beacon_monitor(sdata);
+       ieee80211_sta_reset_conn_monitor(sdata);
 
-       sdata_lock(sdata);
-       switch (fc & IEEE80211_FCTL_STYPE) {
-       case IEEE80211_STYPE_S1G_BEACON:
-               ieee80211_rx_mgmt_beacon(link, hdr, skb->len, rx_status);
-               break;
-       }
-       sdata_unlock(sdata);
+       ret = true;
+ out:
+       kfree(bss_ies);
+       return ret;
 }
 
-void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
-                                 struct sk_buff *skb)
+static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
+                                        struct ieee80211_mgmt *mgmt,
+                                        size_t len)
 {
-       struct ieee80211_link_data *link = &sdata->deflink;
-       struct ieee80211_rx_status *rx_status;
-       struct ieee80211_mgmt *mgmt;
-       u16 fc;
-       int ies_len;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
+       u16 capab_info, status_code, aid;
+       struct ieee802_11_elems *elems;
+       int ac;
+       u8 *pos;
+       bool reassoc;
+       struct cfg80211_bss *cbss;
+       struct ieee80211_event event = {
+               .type = MLME_EVENT,
+               .u.mlme.data = ASSOC_EVENT,
+       };
+       struct ieee80211_prep_tx_info info = {};
+       struct cfg80211_rx_assoc_resp resp = {
+               .uapsd_queues = -1,
+       };
 
-       rx_status = (struct ieee80211_rx_status *) skb->cb;
-       mgmt = (struct ieee80211_mgmt *) skb->data;
-       fc = le16_to_cpu(mgmt->frame_control);
+       sdata_assert_lock(sdata);
 
-       sdata_lock(sdata);
+       if (!assoc_data)
+               return;
 
-       switch (fc & IEEE80211_FCTL_STYPE) {
-       case IEEE80211_STYPE_BEACON:
-               ieee80211_rx_mgmt_beacon(link, (void *)mgmt,
-                                        skb->len, rx_status);
-               break;
-       case IEEE80211_STYPE_PROBE_RESP:
-               ieee80211_rx_mgmt_probe_resp(link, skb);
-               break;
-       case IEEE80211_STYPE_AUTH:
-               ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
-               break;
-       case IEEE80211_STYPE_DEAUTH:
-               ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
-               break;
-       case IEEE80211_STYPE_DISASSOC:
-               ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
-               break;
-       case IEEE80211_STYPE_ASSOC_RESP:
-       case IEEE80211_STYPE_REASSOC_RESP:
-               ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len);
-               break;
-       case IEEE80211_STYPE_ACTION:
-               if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
-                       struct ieee802_11_elems *elems;
+       if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid))
+               return;
 
-                       ies_len = skb->len -
-                                 offsetof(struct ieee80211_mgmt,
-                                          u.action.u.chan_switch.variable);
+       cbss = assoc_data->bss;
 
-                       if (ies_len < 0)
-                               break;
+       /*
+        * AssocResp and ReassocResp have identical structure, so process both
+        * of them in this function.
+        */
 
-                       /* CSA IE cannot be overridden, no need for BSSID */
-                       elems = ieee802_11_parse_elems(
-                                       mgmt->u.action.u.chan_switch.variable,
-                                       ies_len, true, NULL);
+       if (len < 24 + 6)
+               return;
 
-                       if (elems && !elems->parse_error)
-                               ieee80211_sta_process_chanswitch(link,
-                                                                rx_status->mactime,
-                                                                rx_status->device_timestamp,
-                                                                elems, false);
-                       kfree(elems);
-               } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
-                       struct ieee802_11_elems *elems;
+       reassoc = ieee80211_is_reassoc_resp(mgmt->frame_control);
+       capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
+       status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+       pos = mgmt->u.assoc_resp.variable;
+       aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+       if (cbss->channel->band == NL80211_BAND_S1GHZ) {
+               pos = (u8 *) mgmt->u.s1g_assoc_resp.variable;
+               aid = 0; /* TODO */
+       }
 
-                       ies_len = skb->len -
-                                 offsetof(struct ieee80211_mgmt,
-                                          u.action.u.ext_chan_switch.variable);
+       /*
+        * Note: this may not be perfect, AP might misbehave - if
+        * anyone needs to rely on perfect complete notification
+        * with the exact right subtype, then we need to track what
+        * we actually transmitted.
+        */
+       info.subtype = reassoc ? IEEE80211_STYPE_REASSOC_REQ :
+                                IEEE80211_STYPE_ASSOC_REQ;
 
-                       if (ies_len < 0)
-                               break;
+       sdata_info(sdata,
+                  "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n",
+                  reassoc ? "Rea" : "A", mgmt->sa,
+                  capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
 
-                       /*
-                        * extended CSA IE can't be overridden, no need for
-                        * BSSID
-                        */
-                       elems = ieee802_11_parse_elems(
-                                       mgmt->u.action.u.ext_chan_switch.variable,
-                                       ies_len, true, NULL);
+       if (assoc_data->fils_kek_len &&
+           fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0)
+               return;
 
-                       if (elems && !elems->parse_error) {
-                               /* for the handling code pretend it was an IE */
-                               elems->ext_chansw_ie =
-                                       &mgmt->u.action.u.ext_chan_switch.data;
+       elems = ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false,
+                                      assoc_data->bss);
+       if (!elems)
+               goto notify_driver;
 
-                               ieee80211_sta_process_chanswitch(link,
-                                                                rx_status->mactime,
-                                                                rx_status->device_timestamp,
-                                                                elems, false);
-                       }
+       if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
+           elems->timeout_int &&
+           elems->timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
+               u32 tu, ms;
 
-                       kfree(elems);
-               }
-               break;
-       }
-       sdata_unlock(sdata);
-}
+               cfg80211_assoc_comeback(sdata->dev, assoc_data->bss->bssid,
+                                       le32_to_cpu(elems->timeout_int->value));
 
-static void ieee80211_sta_timer(struct timer_list *t)
-{
-       struct ieee80211_sub_if_data *sdata =
-               from_timer(sdata, t, u.mgd.timer);
+               tu = le32_to_cpu(elems->timeout_int->value);
+               ms = tu * 1024 / 1000;
+               sdata_info(sdata,
+                          "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
+                          mgmt->sa, tu, ms);
+               assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
+               assoc_data->timeout_started = true;
+               if (ms > IEEE80211_ASSOC_TIMEOUT)
+                       run_again(sdata, assoc_data->timeout);
+               goto notify_driver;
+       }
 
-       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
-}
+       if (status_code != WLAN_STATUS_SUCCESS) {
+               sdata_info(sdata, "%pM denied association (code=%d)\n",
+                          mgmt->sa, status_code);
+               ieee80211_destroy_assoc_data(sdata, ASSOC_REJECTED);
+               event.u.mlme.status = MLME_DENIED;
+               event.u.mlme.reason = status_code;
+               drv_event_callback(sdata->local, sdata, &event);
+       } else {
+               if (!ieee80211_assoc_success(sdata, cbss, mgmt, len, elems)) {
+                       /* oops -- internal error -- send timeout for now */
+                       ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT);
+                       goto notify_driver;
+               }
+               event.u.mlme.status = MLME_SUCCESS;
+               drv_event_callback(sdata->local, sdata, &event);
+               sdata_info(sdata, "associated\n");
 
-void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
-                                  u8 reason, bool tx)
-{
-       u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+               /*
+                * destroy assoc_data afterwards, as otherwise an idle
+                * recalc after assoc_data is NULL but before associated
+                * is set can cause the interface to go idle
+                */
+               ieee80211_destroy_assoc_data(sdata, ASSOC_SUCCESS);
 
-       ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
-                              tx, frame_buf);
+               /* get uapsd queues configuration */
+               resp.uapsd_queues = 0;
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       if (sdata->deflink.tx_conf[ac].uapsd)
+                               resp.uapsd_queues |= ieee80211_ac_to_qos_mask[ac];
 
-       ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
-                                   reason, false);
+               info.success = 1;
+       }
+
+       resp.links[0].bss = cbss;
+       resp.buf = (u8 *)mgmt;
+       resp.len = len;
+       resp.req_ies = ifmgd->assoc_req_ies;
+       resp.req_ies_len = ifmgd->assoc_req_ies_len;
+       cfg80211_rx_assoc_resp(sdata->dev, &resp);
+notify_driver:
+       drv_mgd_complete_tx(sdata->local, sdata, &info);
+       kfree(elems);
 }
 
-static int ieee80211_auth(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_rx_bss_info(struct ieee80211_link_data *link,
+                                 struct ieee80211_mgmt *mgmt, size_t len,
+                                 struct ieee80211_rx_status *rx_status)
 {
+       struct ieee80211_sub_if_data *sdata = link->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;
-       struct ieee80211_prep_tx_info info = {
-               .subtype = IEEE80211_STYPE_AUTH,
-       };
+       struct ieee80211_bss *bss;
+       struct ieee80211_channel *channel;
 
        sdata_assert_lock(sdata);
 
-       if (WARN_ON_ONCE(!auth_data))
-               return -EINVAL;
-
-       auth_data->tries++;
-
-       if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
-               sdata_info(sdata, "authentication with %pM timed out\n",
-                          auth_data->bss->bssid);
-
-               /*
-                * Most likely AP is not in the range so remove the
-                * bss struct for that AP.
-                */
-               cfg80211_unlink_bss(local->hw.wiphy, auth_data->bss);
+       channel = ieee80211_get_channel_khz(local->hw.wiphy,
+                                       ieee80211_rx_status_to_khz(rx_status));
+       if (!channel)
+               return;
 
-               return -ETIMEDOUT;
+       bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel);
+       if (bss) {
+               link->conf->beacon_rate = bss->beacon_rate;
+               ieee80211_rx_bss_put(local, bss);
        }
+}
 
-       if (auth_data->algorithm == WLAN_AUTH_SAE)
-               info.duration = jiffies_to_msecs(IEEE80211_AUTH_TIMEOUT_SAE);
-
-       drv_mgd_prepare_tx(local, sdata, &info);
 
-       sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
-                  auth_data->bss->bssid, auth_data->tries,
-                  IEEE80211_AUTH_MAX_TRIES);
+static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_link_data *link,
+                                        struct sk_buff *skb)
+{
+       struct ieee80211_sub_if_data *sdata = link->sdata;
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
+       struct ieee80211_if_managed *ifmgd;
+       struct ieee80211_rx_status *rx_status = (void *) skb->cb;
+       struct ieee80211_channel *channel;
+       size_t baselen, len = skb->len;
 
-       auth_data->expected_transaction = 2;
+       ifmgd = &sdata->u.mgd;
 
-       if (auth_data->algorithm == WLAN_AUTH_SAE) {
-               trans = auth_data->sae_trans;
-               status = auth_data->sae_status;
-               auth_data->expected_transaction = trans;
-       }
+       sdata_assert_lock(sdata);
 
-       if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
-               tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
-                          IEEE80211_TX_INTFL_MLME_CONN_TX;
+       /*
+        * According to Draft P802.11ax D6.0 clause 26.17.2.3.2:
+        * "If a 6 GHz AP receives a Probe Request frame  and responds with
+        * a Probe Response frame [..], the Address 1 field of the Probe
+        * Response frame shall be set to the broadcast address [..]"
+        * So, on 6GHz band we should also accept broadcast responses.
+        */
+       channel = ieee80211_get_channel(sdata->local->hw.wiphy,
+                                       rx_status->freq);
+       if (!channel)
+               return;
 
-       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 (!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
+           (channel->band != NL80211_BAND_6GHZ ||
+            !is_broadcast_ether_addr(mgmt->da)))
+               return; /* ignore ProbeResp to foreign address */
 
-       if (tx_flags == 0) {
-               if (auth_data->algorithm == WLAN_AUTH_SAE)
-                       auth_data->timeout = jiffies +
-                               IEEE80211_AUTH_TIMEOUT_SAE;
-               else
-                       auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
-       } else {
-               auth_data->timeout =
-                       round_jiffies_up(jiffies + IEEE80211_AUTH_TIMEOUT_LONG);
-       }
+       baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
+       if (baselen > len)
+               return;
 
-       auth_data->timeout_started = true;
-       run_again(sdata, auth_data->timeout);
+       ieee80211_rx_bss_info(link, mgmt, len, rx_status);
 
-       return 0;
+       if (ifmgd->associated &&
+           ether_addr_equal(mgmt->bssid, link->u.mgd.bssid))
+               ieee80211_reset_ap_probe(sdata);
 }
 
-static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
+/*
+ * This is the canonical list of information elements we care about,
+ * the filter code also gives us all changes to the Microsoft OUI
+ * (00:50:F2) vendor IE which is used for WMM which we need to track,
+ * as well as the DTPC IE (part of the Cisco OUI) used for signaling
+ * changes to requested client power.
+ *
+ * We implement beacon filtering in software since that means we can
+ * avoid processing the frame here and in cfg80211, and userspace
+ * will not be able to tell whether the hardware supports it or not.
+ *
+ * XXX: This list needs to be dynamic -- userspace needs to be able to
+ *     add items it requires. It also needs to be able to tell us to
+ *     look out for other vendor IEs.
+ */
+static const u64 care_about_ies =
+       (1ULL << WLAN_EID_COUNTRY) |
+       (1ULL << WLAN_EID_ERP_INFO) |
+       (1ULL << WLAN_EID_CHANNEL_SWITCH) |
+       (1ULL << WLAN_EID_PWR_CONSTRAINT) |
+       (1ULL << WLAN_EID_HT_CAPABILITY) |
+       (1ULL << WLAN_EID_HT_OPERATION) |
+       (1ULL << WLAN_EID_EXT_CHANSWITCH_ANN);
+
+static void ieee80211_handle_beacon_sig(struct ieee80211_link_data *link,
+                                       struct ieee80211_if_managed *ifmgd,
+                                       struct ieee80211_bss_conf *bss_conf,
+                                       struct ieee80211_local *local,
+                                       struct ieee80211_rx_status *rx_status)
 {
-       struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
-       struct ieee80211_local *local = sdata->local;
-       int ret;
+       struct ieee80211_sub_if_data *sdata = link->sdata;
 
-       sdata_assert_lock(sdata);
+       /* Track average RSSI from the Beacon frames of the current AP */
 
-       assoc_data->tries++;
-       if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
-               sdata_info(sdata, "association with %pM timed out\n",
-                          assoc_data->bss->bssid);
+       if (!link->u.mgd.tracking_signal_avg) {
+               link->u.mgd.tracking_signal_avg = true;
+               ewma_beacon_signal_init(&link->u.mgd.ave_beacon_signal);
+               link->u.mgd.last_cqm_event_signal = 0;
+               link->u.mgd.count_beacon_signal = 1;
+               link->u.mgd.last_ave_beacon_signal = 0;
+       } else {
+               link->u.mgd.count_beacon_signal++;
+       }
+
+       ewma_beacon_signal_add(&link->u.mgd.ave_beacon_signal,
+                              -rx_status->signal);
+
+       if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold &&
+           link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
+               int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal);
+               int last_sig = link->u.mgd.last_ave_beacon_signal;
+               struct ieee80211_event event = {
+                       .type = RSSI_EVENT,
+               };
 
                /*
-                * Most likely AP is not in the range so remove the
-                * bss struct for that AP.
+                * if signal crosses either of the boundaries, invoke callback
+                * with appropriate parameters
                 */
-               cfg80211_unlink_bss(local->hw.wiphy, assoc_data->bss);
+               if (sig > ifmgd->rssi_max_thold &&
+                   (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
+                       link->u.mgd.last_ave_beacon_signal = sig;
+                       event.u.rssi.data = RSSI_EVENT_HIGH;
+                       drv_event_callback(local, sdata, &event);
+               } else if (sig < ifmgd->rssi_min_thold &&
+                          (last_sig >= ifmgd->rssi_max_thold ||
+                          last_sig == 0)) {
+                       link->u.mgd.last_ave_beacon_signal = sig;
+                       event.u.rssi.data = RSSI_EVENT_LOW;
+                       drv_event_callback(local, sdata, &event);
+               }
+       }
 
-               return -ETIMEDOUT;
+       if (bss_conf->cqm_rssi_thold &&
+           link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT &&
+           !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) {
+               int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal);
+               int last_event = link->u.mgd.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)) {
+                       link->u.mgd.last_cqm_event_signal = sig;
+                       ieee80211_cqm_rssi_notify(
+                               &sdata->vif,
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+                               sig, GFP_KERNEL);
+               } else if (sig > thold &&
+                          (last_event == 0 || sig > last_event + hyst)) {
+                       link->u.mgd.last_cqm_event_signal = sig;
+                       ieee80211_cqm_rssi_notify(
+                               &sdata->vif,
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                               sig, GFP_KERNEL);
+               }
        }
 
-       sdata_info(sdata, "associate with %pM (try %d/%d)\n",
-                  assoc_data->bss->bssid, assoc_data->tries,
-                  IEEE80211_ASSOC_MAX_TRIES);
-       ret = ieee80211_send_assoc(sdata);
-       if (ret)
-               return ret;
+       if (bss_conf->cqm_rssi_low &&
+           link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
+               int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal);
+               int last_event = link->u.mgd.last_cqm_event_signal;
+               int low = bss_conf->cqm_rssi_low;
+               int high = bss_conf->cqm_rssi_high;
 
-       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);
-       } else {
-               assoc_data->timeout =
-                       round_jiffies_up(jiffies +
-                                        IEEE80211_ASSOC_TIMEOUT_LONG);
-               assoc_data->timeout_started = true;
-               run_again(sdata, assoc_data->timeout);
+               if (sig < low &&
+                   (last_event == 0 || last_event >= low)) {
+                       link->u.mgd.last_cqm_event_signal = sig;
+                       ieee80211_cqm_rssi_notify(
+                               &sdata->vif,
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+                               sig, GFP_KERNEL);
+               } else if (sig > high &&
+                          (last_event == 0 || last_event <= high)) {
+                       link->u.mgd.last_cqm_event_signal = sig;
+                       ieee80211_cqm_rssi_notify(
+                               &sdata->vif,
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                               sig, GFP_KERNEL);
+               }
        }
-
-       return 0;
 }
 
-void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
-                                 __le16 fc, bool acked)
+static bool ieee80211_rx_our_beacon(const u8 *tx_bssid,
+                                   struct cfg80211_bss *bss)
 {
-       struct ieee80211_local *local = sdata->local;
-
-       sdata->u.mgd.status_fc = fc;
-       sdata->u.mgd.status_acked = acked;
-       sdata->u.mgd.status_received = true;
-
-       ieee80211_queue_work(&local->hw, &sdata->work);
+       if (ether_addr_equal(tx_bssid, bss->bssid))
+               return true;
+       if (!bss->transmitted_bss)
+               return false;
+       return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid);
 }
 
-void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
+                                    struct ieee80211_hdr *hdr, size_t len,
+                                    struct ieee80211_rx_status *rx_status)
 {
-       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
+       struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg;
+       struct ieee80211_mgmt *mgmt = (void *) hdr;
+       size_t baselen;
+       struct ieee802_11_elems *elems;
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_channel *chan;
+       struct link_sta_info *link_sta;
+       struct sta_info *sta;
+       u32 changed = 0;
+       bool erp_valid;
+       u8 erp_value = 0;
+       u32 ncrc = 0;
+       u8 *bssid, *variable = mgmt->u.beacon.variable;
+       u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
-       sdata_lock(sdata);
+       sdata_assert_lock(sdata);
 
-       if (ifmgd->status_received) {
-               __le16 fc = ifmgd->status_fc;
-               bool status_acked = ifmgd->status_acked;
+       /* Process beacon from the current BSS */
+       bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type);
+       if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
+               struct ieee80211_ext *ext = (void *) mgmt;
 
-               ifmgd->status_received = false;
-               if (ifmgd->auth_data && ieee80211_is_auth(fc)) {
-                       if (status_acked) {
-                               if (ifmgd->auth_data->algorithm ==
-                                   WLAN_AUTH_SAE)
-                                       ifmgd->auth_data->timeout =
-                                               jiffies +
-                                               IEEE80211_AUTH_TIMEOUT_SAE;
-                               else
-                                       ifmgd->auth_data->timeout =
-                                               jiffies +
-                                               IEEE80211_AUTH_TIMEOUT_SHORT;
-                               run_again(sdata, ifmgd->auth_data->timeout);
-                       } else {
-                               ifmgd->auth_data->timeout = jiffies - 1;
-                       }
-                       ifmgd->auth_data->timeout_started = true;
-               } else if (ifmgd->assoc_data &&
-                          (ieee80211_is_assoc_req(fc) ||
-                           ieee80211_is_reassoc_req(fc))) {
-                       if (status_acked) {
-                               ifmgd->assoc_data->timeout =
-                                       jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
-                               run_again(sdata, ifmgd->assoc_data->timeout);
-                       } else {
-                               ifmgd->assoc_data->timeout = jiffies - 1;
-                       }
-                       ifmgd->assoc_data->timeout_started = true;
-               }
+               if (ieee80211_is_s1g_short_beacon(ext->frame_control))
+                       variable = ext->u.s1g_short_beacon.variable;
+               else
+                       variable = ext->u.s1g_beacon.variable;
        }
 
-       if (ifmgd->auth_data && ifmgd->auth_data->timeout_started &&
-           time_after(jiffies, ifmgd->auth_data->timeout)) {
-               if (ifmgd->auth_data->done || ifmgd->auth_data->waiting) {
-                       /*
-                        * ok ... we waited for assoc or continuation but
-                        * userspace didn't do it, so kill the auth data
-                        */
-                       ieee80211_destroy_auth_data(sdata, false);
-               } else if (ieee80211_auth(sdata)) {
-                       u8 bssid[ETH_ALEN];
-                       struct ieee80211_event event = {
-                               .type = MLME_EVENT,
-                               .u.mlme.data = AUTH_EVENT,
-                               .u.mlme.status = MLME_TIMEOUT,
-                       };
+       baselen = (u8 *) variable - (u8 *) mgmt;
+       if (baselen > len)
+               return;
 
-                       memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
+       if (!chanctx_conf) {
+               rcu_read_unlock();
+               return;
+       }
 
-                       ieee80211_destroy_auth_data(sdata, false);
+       if (ieee80211_rx_status_to_khz(rx_status) !=
+           ieee80211_channel_to_khz(chanctx_conf->def.chan)) {
+               rcu_read_unlock();
+               return;
+       }
+       chan = chanctx_conf->def.chan;
+       rcu_read_unlock();
 
-                       cfg80211_auth_timeout(sdata->dev, bssid);
-                       drv_event_callback(sdata->local, sdata, &event);
-               }
-       } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
-               run_again(sdata, ifmgd->auth_data->timeout);
+       if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
+           ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->bss)) {
+               elems = ieee802_11_parse_elems(variable, len - baselen, false,
+                                              ifmgd->assoc_data->bss);
+               if (!elems)
+                       return;
 
-       if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
-           time_after(jiffies, ifmgd->assoc_data->timeout)) {
-               if ((ifmgd->assoc_data->need_beacon &&
-                    !sdata->deflink.u.mgd.have_beacon) ||
-                   ieee80211_do_assoc(sdata)) {
-                       struct ieee80211_event event = {
-                               .type = MLME_EVENT,
-                               .u.mlme.data = ASSOC_EVENT,
-                               .u.mlme.status = MLME_TIMEOUT,
-                       };
+               ieee80211_rx_bss_info(link, mgmt, len, rx_status);
 
-                       ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT);
-                       drv_event_callback(sdata->local, sdata, &event);
+               if (elems->dtim_period)
+                       link->u.mgd.dtim_period = elems->dtim_period;
+               link->u.mgd.have_beacon = true;
+               ifmgd->assoc_data->need_beacon = false;
+               if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
+                       link->conf->sync_tsf =
+                               le64_to_cpu(mgmt->u.beacon.timestamp);
+                       link->conf->sync_device_ts =
+                               rx_status->device_timestamp;
+                       link->conf->sync_dtim_count = elems->dtim_count;
                }
-       } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
-               run_again(sdata, ifmgd->assoc_data->timeout);
 
-       if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
-           ifmgd->associated) {
-               u8 *bssid = sdata->deflink.u.mgd.bssid;
-               int max_tries;
+               if (elems->mbssid_config_ie)
+                       bss_conf->profile_periodicity =
+                               elems->mbssid_config_ie->profile_periodicity;
+               else
+                       bss_conf->profile_periodicity = 0;
 
-               if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
-                       max_tries = max_nullfunc_tries;
+               if (elems->ext_capab_len >= 11 &&
+                   (elems->ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
+                       bss_conf->ema_ap = true;
                else
-                       max_tries = max_probe_tries;
+                       bss_conf->ema_ap = false;
+
+               /* continue assoc process */
+               ifmgd->assoc_data->timeout = jiffies;
+               ifmgd->assoc_data->timeout_started = true;
+               run_again(sdata, ifmgd->assoc_data->timeout);
+               kfree(elems);
+               return;
+       }
+
+       if (!ifmgd->associated ||
+           !ieee80211_rx_our_beacon(bssid, link->u.mgd.bss))
+               return;
+       bssid = link->u.mgd.bssid;
+
+       if (!(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL))
+               ieee80211_handle_beacon_sig(link, ifmgd, bss_conf,
+                                           local, rx_status);
+
+       if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
+               mlme_dbg_ratelimited(sdata,
+                                    "cancelling AP probe due to a received beacon\n");
+               ieee80211_reset_ap_probe(sdata);
+       }
+
+       /*
+        * Push the beacon loss detection into the future since
+        * we are processing a beacon from the AP just now.
+        */
+       ieee80211_sta_reset_beacon_monitor(sdata);
+
+       /* TODO: CRC urrently not calculated on S1G Beacon Compatibility
+        * element (which carries the beacon interval). Don't forget to add a
+        * bit to care_about_ies[] above if mac80211 is interested in a
+        * changing S1G element.
+        */
+       if (!ieee80211_is_s1g_beacon(hdr->frame_control))
+               ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
+       elems = ieee802_11_parse_elems_crc(variable, len - baselen,
+                                          false, care_about_ies, ncrc,
+                                          link->u.mgd.bss);
+       if (!elems)
+               return;
+       ncrc = elems->crc;
+
+       if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
+           ieee80211_check_tim(elems->tim, elems->tim_len, vif_cfg->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;
 
-               /* ACK received for nullfunc probing frame */
-               if (!ifmgd->probe_send_count)
-                       ieee80211_reset_ap_probe(sdata);
-               else if (ifmgd->nullfunc_failed) {
-                       if (ifmgd->probe_send_count < max_tries) {
-                               mlme_dbg(sdata,
-                                        "No ack for nullfunc frame to AP %pM, try %d/%i\n",
-                                        bssid, ifmgd->probe_send_count,
-                                        max_tries);
-                               ieee80211_mgd_probe_ap_send(sdata);
-                       } else {
-                               mlme_dbg(sdata,
-                                        "No ack for nullfunc frame to AP %pM, disconnecting.\n",
-                                        bssid);
-                               ieee80211_sta_connection_lost(sdata,
-                                       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
-                                       false);
-                       }
-               } else if (time_is_after_jiffies(ifmgd->probe_timeout))
-                       run_again(sdata, ifmgd->probe_timeout);
-               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);
-                       ieee80211_sta_connection_lost(sdata,
-                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
-               } else if (ifmgd->probe_send_count < max_tries) {
-                       mlme_dbg(sdata,
-                                "No probe response from AP %pM after %dms, try %d/%i\n",
-                                bssid, probe_wait_ms,
-                                ifmgd->probe_send_count, max_tries);
-                       ieee80211_mgd_probe_ap_send(sdata);
-               } else {
                        /*
-                        * We actually lost the connection ... or did we?
-                        * Let's make sure!
+                        * 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.
                         */
-                       mlme_dbg(sdata,
-                                "No probe response from AP %pM after %dms, disconnecting.\n",
-                                bssid, probe_wait_ms);
-
-                       ieee80211_sta_connection_lost(sdata,
-                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
+                       ieee80211_send_pspoll(local, sdata);
                }
        }
 
-       sdata_unlock(sdata);
-}
-
-static void ieee80211_sta_bcn_mon_timer(struct timer_list *t)
-{
-       struct ieee80211_sub_if_data *sdata =
-               from_timer(sdata, t, u.mgd.bcn_mon_timer);
-
-       if (WARN_ON(sdata->vif.valid_links))
-               return;
-
-       if (sdata->vif.bss_conf.csa_active &&
-           !sdata->deflink.u.mgd.csa_waiting_bcn)
-               return;
+       if (sdata->vif.p2p ||
+           sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
+               struct ieee80211_p2p_noa_attr noa = {};
+               int ret;
 
-       if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
-               return;
+               ret = cfg80211_get_p2p_attr(variable,
+                                           len - baselen,
+                                           IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+                                           (u8 *) &noa, sizeof(noa));
+               if (ret >= 2) {
+                       if (link->u.mgd.p2p_noa_index != noa.index) {
+                               /* valid noa_attr and index changed */
+                               link->u.mgd.p2p_noa_index = noa.index;
+                               memcpy(&bss_conf->p2p_noa_attr, &noa, sizeof(noa));
+                               changed |= BSS_CHANGED_P2P_PS;
+                               /*
+                                * make sure we update all information, the CRC
+                                * mechanism doesn't look at P2P attributes.
+                                */
+                               link->u.mgd.beacon_crc_valid = false;
+                       }
+               } else if (link->u.mgd.p2p_noa_index != -1) {
+                       /* noa_attr not found and we had valid noa_attr before */
+                       link->u.mgd.p2p_noa_index = -1;
+                       memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr));
+                       changed |= BSS_CHANGED_P2P_PS;
+                       link->u.mgd.beacon_crc_valid = false;
+               }
+       }
 
-       sdata->u.mgd.connection_loss = false;
-       ieee80211_queue_work(&sdata->local->hw,
-                            &sdata->u.mgd.beacon_connection_loss_work);
-}
+       if (link->u.mgd.csa_waiting_bcn)
+               ieee80211_chswitch_post_beacon(link);
 
-static void ieee80211_sta_conn_mon_timer(struct timer_list *t)
-{
-       struct ieee80211_sub_if_data *sdata =
-               from_timer(sdata, t, u.mgd.conn_mon_timer);
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_local *local = sdata->local;
-       struct sta_info *sta;
-       unsigned long timeout;
+       /*
+        * Update beacon timing and dtim count on every beacon appearance. This
+        * will allow the driver to use the most updated values. Do it before
+        * comparing this one with last received beacon.
+        * IMPORTANT: These parameters would possibly be out of sync by the time
+        * the driver will use them. The synchronized view is currently
+        * guaranteed only in certain callbacks.
+        */
+       if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY) &&
+           !ieee80211_is_s1g_beacon(hdr->frame_control)) {
+               link->conf->sync_tsf =
+                       le64_to_cpu(mgmt->u.beacon.timestamp);
+               link->conf->sync_device_ts =
+                       rx_status->device_timestamp;
+               link->conf->sync_dtim_count = elems->dtim_count;
+       }
 
-       if (WARN_ON(sdata->vif.valid_links))
-               return;
+       if ((ncrc == link->u.mgd.beacon_crc && link->u.mgd.beacon_crc_valid) ||
+           ieee80211_is_s1g_short_beacon(mgmt->frame_control))
+               goto free;
+       link->u.mgd.beacon_crc = ncrc;
+       link->u.mgd.beacon_crc_valid = true;
 
-       if (sdata->vif.bss_conf.csa_active &&
-           !sdata->deflink.u.mgd.csa_waiting_bcn)
-               return;
+       ieee80211_rx_bss_info(link, mgmt, len, rx_status);
 
-       sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
-       if (!sta)
-               return;
+       ieee80211_sta_process_chanswitch(link, rx_status->mactime,
+                                        rx_status->device_timestamp,
+                                        elems, true);
 
-       timeout = sta->deflink.status_stats.last_ack;
-       if (time_before(sta->deflink.status_stats.last_ack, sta->deflink.rx_stats.last_rx))
-               timeout = sta->deflink.rx_stats.last_rx;
-       timeout += IEEE80211_CONNECTION_IDLE_TIME;
+       if (!link->u.mgd.disable_wmm_tracking &&
+           ieee80211_sta_wmm_params(local, link, elems->wmm_param,
+                                    elems->wmm_param_len,
+                                    elems->mu_edca_param_set))
+               changed |= BSS_CHANGED_QOS;
 
-       /* If timeout is after now, then update timer to fire at
-        * the later date, but do not actually probe at this time.
+       /*
+        * If we haven't had a beacon before, tell the driver about the
+        * DTIM period (and beacon timing if desired) now.
         */
-       if (time_is_after_jiffies(timeout)) {
-               mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(timeout));
-               return;
-       }
-
-       ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
-}
+       if (!link->u.mgd.have_beacon) {
+               /* a few bogus AP send dtim_period = 0 or no TIM IE */
+               bss_conf->dtim_period = elems->dtim_period ?: 1;
 
-static void ieee80211_sta_monitor_work(struct work_struct *work)
-{
-       struct ieee80211_sub_if_data *sdata =
-               container_of(work, struct ieee80211_sub_if_data,
-                            u.mgd.monitor_work);
+               changed |= BSS_CHANGED_BEACON_INFO;
+               link->u.mgd.have_beacon = true;
 
-       ieee80211_mgd_probe_ap(sdata, false);
-}
+               mutex_lock(&local->iflist_mtx);
+               ieee80211_recalc_ps(local);
+               mutex_unlock(&local->iflist_mtx);
 
-static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
-{
-       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               __ieee80211_stop_poll(sdata);
+               ieee80211_recalc_ps_vif(sdata);
+       }
 
-               /* let's probe the connection once */
-               if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
-                       ieee80211_queue_work(&sdata->local->hw,
-                                            &sdata->u.mgd.monitor_work);
+       if (elems->erp_info) {
+               erp_valid = true;
+               erp_value = elems->erp_info[0];
+       } else {
+               erp_valid = false;
        }
-}
 
-#ifdef CONFIG_PM
-void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+       if (!ieee80211_is_s1g_beacon(hdr->frame_control))
+               changed |= ieee80211_handle_bss_capability(link,
+                               le16_to_cpu(mgmt->u.beacon.capab_info),
+                               erp_valid, erp_value);
 
-       sdata_lock(sdata);
+       mutex_lock(&local->sta_mtx);
+       sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
+       if (WARN_ON(!sta))
+               goto free;
+       link_sta = rcu_dereference_protected(sta->link[link->link_id],
+                                            lockdep_is_held(&local->sta_mtx));
+       if (WARN_ON(!link_sta))
+               goto free;
 
-       if (ifmgd->auth_data || ifmgd->assoc_data) {
-               const u8 *bssid = ifmgd->auth_data ?
-                               ifmgd->auth_data->bss->bssid :
-                               ifmgd->assoc_data->bss->bssid;
+       changed |= ieee80211_recalc_twt_req(link, link_sta, elems);
 
-               /*
-                * If we are trying to authenticate / associate while suspending,
-                * cfg80211 won't know and won't actually abort those attempts,
-                * thus we need to do that ourselves.
-                */
-               ieee80211_send_deauth_disassoc(sdata, bssid, bssid,
-                                              IEEE80211_STYPE_DEAUTH,
-                                              WLAN_REASON_DEAUTH_LEAVING,
-                                              false, frame_buf);
-               if (ifmgd->assoc_data)
-                       ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON);
-               if (ifmgd->auth_data)
-                       ieee80211_destroy_auth_data(sdata, false);
-               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-                                     IEEE80211_DEAUTH_FRAME_LEN,
-                                     false);
+       if (ieee80211_config_bw(link, elems->ht_cap_elem,
+                               elems->vht_cap_elem, elems->ht_operation,
+                               elems->vht_operation, elems->he_operation,
+                               elems->eht_operation,
+                               elems->s1g_oper, bssid, &changed)) {
+               mutex_unlock(&local->sta_mtx);
+               sdata_info(sdata,
+                          "failed to follow AP %pM bandwidth change, disconnect\n",
+                          bssid);
+               ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+                                      WLAN_REASON_DEAUTH_LEAVING,
+                                      true, deauth_buf);
+               ieee80211_report_disconnect(sdata, deauth_buf,
+                                           sizeof(deauth_buf), true,
+                                           WLAN_REASON_DEAUTH_LEAVING,
+                                           false);
+               goto free;
        }
 
-       /* This is a bit of a hack - we should find a better and more generic
-        * solution to this. Normally when suspending, cfg80211 will in fact
-        * deauthenticate. However, it doesn't (and cannot) stop an ongoing
-        * auth (not so important) or assoc (this is the problem) process.
-        *
-        * As a consequence, it can happen that we are in the process of both
-        * associating and suspending, and receive an association response
-        * after cfg80211 has checked if it needs to disconnect, but before
-        * we actually set the flag to drop incoming frames. This will then
-        * cause the workqueue flush to process the association response in
-        * the suspend, resulting in a successful association just before it
-        * tries to remove the interface from the driver, which now though
-        * has a channel context assigned ... this results in issues.
-        *
-        * To work around this (for now) simply deauth here again if we're
-        * now connected.
-        */
-       if (ifmgd->associated && !sdata->local->wowlan) {
-               u8 bssid[ETH_ALEN];
-               struct cfg80211_deauth_request req = {
-                       .reason_code = WLAN_REASON_DEAUTH_LEAVING,
-                       .bssid = bssid,
-               };
+       if (sta && elems->opmode_notif)
+               ieee80211_vht_handle_opmode(sdata, link_sta,
+                                           *elems->opmode_notif,
+                                           rx_status->band);
+       mutex_unlock(&local->sta_mtx);
 
-               memcpy(bssid, sdata->vif.cfg.ap_addr, ETH_ALEN);
-               ieee80211_mgd_deauth(sdata, &req);
-       }
+       changed |= ieee80211_handle_pwr_constr(link, chan, mgmt,
+                                              elems->country_elem,
+                                              elems->country_elem_len,
+                                              elems->pwr_constr_elem,
+                                              elems->cisco_dtpc_elem);
 
-       sdata_unlock(sdata);
+       ieee80211_link_info_change_notify(sdata, link, changed);
+free:
+       kfree(elems);
 }
-#endif
 
-void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
+void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata,
+                                struct sk_buff *skb)
 {
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
-       sdata_lock(sdata);
-       if (!ifmgd->associated) {
-               sdata_unlock(sdata);
-               return;
-       }
+       struct ieee80211_link_data *link = &sdata->deflink;
+       struct ieee80211_rx_status *rx_status;
+       struct ieee80211_hdr *hdr;
+       u16 fc;
 
-       if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
-               sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
-               mlme_dbg(sdata, "driver requested disconnect after resume\n");
-               ieee80211_sta_connection_lost(sdata,
-                                             WLAN_REASON_UNSPECIFIED,
-                                             true);
-               sdata_unlock(sdata);
-               return;
-       }
+       rx_status = (struct ieee80211_rx_status *) skb->cb;
+       hdr = (struct ieee80211_hdr *) skb->data;
+       fc = le16_to_cpu(hdr->frame_control);
 
-       if (sdata->flags & IEEE80211_SDATA_DISCONNECT_HW_RESTART) {
-               sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_HW_RESTART;
-               mlme_dbg(sdata, "driver requested disconnect after hardware restart\n");
-               ieee80211_sta_connection_lost(sdata,
-                                             WLAN_REASON_UNSPECIFIED,
-                                             true);
-               sdata_unlock(sdata);
-               return;
+       sdata_lock(sdata);
+       switch (fc & IEEE80211_FCTL_STYPE) {
+       case IEEE80211_STYPE_S1G_BEACON:
+               ieee80211_rx_mgmt_beacon(link, hdr, skb->len, rx_status);
+               break;
        }
-
        sdata_unlock(sdata);
 }
 
-static void ieee80211_request_smps_mgd_work(struct work_struct *work)
+void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
+                                 struct sk_buff *skb)
 {
-       struct ieee80211_link_data *link =
-               container_of(work, struct ieee80211_link_data,
-                            u.mgd.request_smps_work);
+       struct ieee80211_link_data *link = &sdata->deflink;
+       struct ieee80211_rx_status *rx_status;
+       struct ieee80211_mgmt *mgmt;
+       u16 fc;
+       int ies_len;
 
-       sdata_lock(link->sdata);
-       __ieee80211_request_smps_mgd(link->sdata, link,
-                                    link->u.mgd.driver_smps_mode);
-       sdata_unlock(link->sdata);
-}
+       rx_status = (struct ieee80211_rx_status *) skb->cb;
+       mgmt = (struct ieee80211_mgmt *) skb->data;
+       fc = le16_to_cpu(mgmt->frame_control);
 
-/* interface setup */
-void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       sdata_lock(sdata);
 
-       INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
-       INIT_WORK(&ifmgd->beacon_connection_loss_work,
-                 ieee80211_beacon_connection_loss_work);
-       INIT_WORK(&ifmgd->csa_connection_drop_work,
-                 ieee80211_csa_connection_drop_work);
-       INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
-                         ieee80211_tdls_peer_del_work);
-       timer_setup(&ifmgd->timer, ieee80211_sta_timer, 0);
-       timer_setup(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 0);
-       timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0);
-       INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
-                         ieee80211_sta_handle_tspec_ac_params_wk);
+       switch (fc & IEEE80211_FCTL_STYPE) {
+       case IEEE80211_STYPE_BEACON:
+               ieee80211_rx_mgmt_beacon(link, (void *)mgmt,
+                                        skb->len, rx_status);
+               break;
+       case IEEE80211_STYPE_PROBE_RESP:
+               ieee80211_rx_mgmt_probe_resp(link, skb);
+               break;
+       case IEEE80211_STYPE_AUTH:
+               ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
+               break;
+       case IEEE80211_STYPE_DEAUTH:
+               ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
+               break;
+       case IEEE80211_STYPE_DISASSOC:
+               ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
+               break;
+       case IEEE80211_STYPE_ASSOC_RESP:
+       case IEEE80211_STYPE_REASSOC_RESP:
+               ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len);
+               break;
+       case IEEE80211_STYPE_ACTION:
+               if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
+                       struct ieee802_11_elems *elems;
 
-       ifmgd->flags = 0;
-       ifmgd->powersave = sdata->wdev.ps;
-       ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues;
-       ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len;
-       /* Setup TDLS data */
-       spin_lock_init(&ifmgd->teardown_lock);
-       ifmgd->teardown_skb = NULL;
-       ifmgd->orig_teardown_skb = NULL;
-}
+                       ies_len = skb->len -
+                                 offsetof(struct ieee80211_mgmt,
+                                          u.action.u.chan_switch.variable);
 
-void ieee80211_mgd_setup_link(struct ieee80211_link_data *link)
-{
-       struct ieee80211_local *local = link->sdata->local;
+                       if (ies_len < 0)
+                               break;
 
-       link->u.mgd.p2p_noa_index = -1;
-       link->u.mgd.conn_flags = 0;
-       link->conf->bssid = link->u.mgd.bssid;
+                       /* CSA IE cannot be overridden, no need for BSSID */
+                       elems = ieee802_11_parse_elems(
+                                       mgmt->u.action.u.chan_switch.variable,
+                                       ies_len, true, NULL);
 
-       INIT_WORK(&link->u.mgd.request_smps_work,
-                 ieee80211_request_smps_mgd_work);
-       if (local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS)
-               link->u.mgd.req_smps = IEEE80211_SMPS_AUTOMATIC;
-       else
-               link->u.mgd.req_smps = IEEE80211_SMPS_OFF;
+                       if (elems && !elems->parse_error)
+                               ieee80211_sta_process_chanswitch(link,
+                                                                rx_status->mactime,
+                                                                rx_status->device_timestamp,
+                                                                elems, false);
+                       kfree(elems);
+               } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
+                       struct ieee802_11_elems *elems;
 
-       INIT_WORK(&link->u.mgd.chswitch_work, ieee80211_chswitch_work);
-       timer_setup(&link->u.mgd.chswitch_timer, ieee80211_chswitch_timer, 0);
-}
+                       ies_len = skb->len -
+                                 offsetof(struct ieee80211_mgmt,
+                                          u.action.u.ext_chan_switch.variable);
 
-/* scan finished notification */
-void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
-{
-       struct ieee80211_sub_if_data *sdata;
+                       if (ies_len < 0)
+                               break;
 
-       /* Restart STA timers */
-       rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               if (ieee80211_sdata_running(sdata))
-                       ieee80211_restart_sta_timer(sdata);
+                       /*
+                        * extended CSA IE can't be overridden, no need for
+                        * BSSID
+                        */
+                       elems = ieee802_11_parse_elems(
+                                       mgmt->u.action.u.ext_chan_switch.variable,
+                                       ies_len, true, NULL);
+
+                       if (elems && !elems->parse_error) {
+                               /* for the handling code pretend it was an IE */
+                               elems->ext_chansw_ie =
+                                       &mgmt->u.action.u.ext_chan_switch.data;
+
+                               ieee80211_sta_process_chanswitch(link,
+                                                                rx_status->mactime,
+                                                                rx_status->device_timestamp,
+                                                                elems, false);
+                       }
+
+                       kfree(elems);
+               }
+               break;
        }
-       rcu_read_unlock();
+       sdata_unlock(sdata);
 }
 
-static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link,
-                                 struct cfg80211_bss *cbss)
+static void ieee80211_sta_timer(struct timer_list *t)
 {
-       struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp;
-       const struct element *ht_cap_elem, *vht_cap_elem;
-       const struct cfg80211_bss_ies *ies;
-       const struct ieee80211_ht_cap *ht_cap;
-       const struct ieee80211_vht_cap *vht_cap;
-       const struct ieee80211_he_cap_elem *he_cap;
-       const struct element *he_cap_elem;
-       u16 mcs_80_map, mcs_160_map;
-       int i, mcs_nss_size;
-       bool support_160;
-       u8 chains = 1;
+       struct ieee80211_sub_if_data *sdata =
+               from_timer(sdata, t, u.mgd.timer);
 
-       if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)
-               return chains;
+       ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+}
 
-       ht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_HT_CAPABILITY);
-       if (ht_cap_elem && ht_cap_elem->datalen >= sizeof(*ht_cap)) {
-               ht_cap = (void *)ht_cap_elem->data;
-               chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
-               /*
-                * TODO: use "Tx Maximum Number Spatial Streams Supported" and
-                *       "Tx Unequal Modulation Supported" fields.
-                */
-       }
+void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
+                                  u8 reason, bool tx)
+{
+       u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
-       if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)
-               return chains;
+       ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
+                              tx, frame_buf);
 
-       vht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_VHT_CAPABILITY);
-       if (vht_cap_elem && vht_cap_elem->datalen >= sizeof(*vht_cap)) {
-               u8 nss;
-               u16 tx_mcs_map;
+       ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+                                   reason, false);
+}
 
-               vht_cap = (void *)vht_cap_elem->data;
-               tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
-               for (nss = 8; nss > 0; nss--) {
-                       if (((tx_mcs_map >> (2 * (nss - 1))) & 3) !=
-                                       IEEE80211_VHT_MCS_NOT_SUPPORTED)
-                               break;
-               }
-               /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */
-               chains = max(chains, nss);
-       }
+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;
+       struct ieee80211_prep_tx_info info = {
+               .subtype = IEEE80211_STYPE_AUTH,
+       };
+
+       sdata_assert_lock(sdata);
 
-       if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE)
-               return chains;
+       if (WARN_ON_ONCE(!auth_data))
+               return -EINVAL;
 
-       ies = rcu_dereference(cbss->ies);
-       he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
-                                            ies->data, ies->len);
+       auth_data->tries++;
 
-       if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap))
-               return chains;
+       if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
+               sdata_info(sdata, "authentication with %pM timed out\n",
+                          auth_data->bss->bssid);
 
-       /* skip one byte ext_tag_id */
-       he_cap = (void *)(he_cap_elem->data + 1);
-       mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap);
+               /*
+                * Most likely AP is not in the range so remove the
+                * bss struct for that AP.
+                */
+               cfg80211_unlink_bss(local->hw.wiphy, auth_data->bss);
 
-       /* invalid HE IE */
-       if (he_cap_elem->datalen < 1 + mcs_nss_size + sizeof(*he_cap))
-               return chains;
+               return -ETIMEDOUT;
+       }
 
-       /* mcs_nss is right after he_cap info */
-       he_mcs_nss_supp = (void *)(he_cap + 1);
+       if (auth_data->algorithm == WLAN_AUTH_SAE)
+               info.duration = jiffies_to_msecs(IEEE80211_AUTH_TIMEOUT_SAE);
 
-       mcs_80_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80);
+       drv_mgd_prepare_tx(local, sdata, &info);
 
-       for (i = 7; i >= 0; i--) {
-               u8 mcs_80 = mcs_80_map >> (2 * i) & 3;
+       sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
+                  auth_data->bss->bssid, auth_data->tries,
+                  IEEE80211_AUTH_MAX_TRIES);
 
-               if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
-                       chains = max_t(u8, chains, i + 1);
-                       break;
-               }
-       }
+       auth_data->expected_transaction = 2;
 
-       support_160 = he_cap->phy_cap_info[0] &
-                     IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+       if (auth_data->algorithm == WLAN_AUTH_SAE) {
+               trans = auth_data->sae_trans;
+               status = auth_data->sae_status;
+               auth_data->expected_transaction = trans;
+       }
 
-       if (!support_160)
-               return chains;
+       if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
+               tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+                          IEEE80211_TX_INTFL_MLME_CONN_TX;
 
-       mcs_160_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_160);
-       for (i = 7; i >= 0; i--) {
-               u8 mcs_160 = mcs_160_map >> (2 * i) & 3;
+       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 (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
-                       chains = max_t(u8, chains, i + 1);
-                       break;
-               }
+       if (tx_flags == 0) {
+               if (auth_data->algorithm == WLAN_AUTH_SAE)
+                       auth_data->timeout = jiffies +
+                               IEEE80211_AUTH_TIMEOUT_SAE;
+               else
+                       auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+       } else {
+               auth_data->timeout =
+                       round_jiffies_up(jiffies + IEEE80211_AUTH_TIMEOUT_LONG);
        }
 
-       return chains;
+       auth_data->timeout_started = true;
+       run_again(sdata, auth_data->timeout);
+
+       return 0;
 }
 
-static bool
-ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata,
-                                    const struct cfg80211_bss_ies *ies,
-                                    const struct ieee80211_he_operation *he_op)
+static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
 {
-       const struct element *he_cap_elem;
-       const struct ieee80211_he_cap_elem *he_cap;
-       struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp;
-       u16 mcs_80_map_tx, mcs_80_map_rx;
-       u16 ap_min_req_set;
-       int mcs_nss_size;
-       int nss;
+       struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
+       struct ieee80211_local *local = sdata->local;
+       int ret;
 
-       he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
-                                            ies->data, ies->len);
+       sdata_assert_lock(sdata);
 
-       /* invalid HE IE */
-       if (!he_cap_elem || he_cap_elem->datalen < 1 + sizeof(*he_cap)) {
-               sdata_info(sdata,
-                          "Invalid HE elem, Disable HE\n");
-               return false;
-       }
+       assoc_data->tries++;
+       if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
+               sdata_info(sdata, "association with %pM timed out\n",
+                          assoc_data->bss->bssid);
 
-       /* skip one byte ext_tag_id */
-       he_cap = (void *)(he_cap_elem->data + 1);
-       mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap);
+               /*
+                * Most likely AP is not in the range so remove the
+                * bss struct for that AP.
+                */
+               cfg80211_unlink_bss(local->hw.wiphy, assoc_data->bss);
 
-       /* invalid HE IE */
-       if (he_cap_elem->datalen < 1 + sizeof(*he_cap) + mcs_nss_size) {
-               sdata_info(sdata,
-                          "Invalid HE elem with nss size, Disable HE\n");
-               return false;
+               return -ETIMEDOUT;
        }
 
-       /* mcs_nss is right after he_cap info */
-       he_mcs_nss_supp = (void *)(he_cap + 1);
-
-       mcs_80_map_tx = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80);
-       mcs_80_map_rx = le16_to_cpu(he_mcs_nss_supp->rx_mcs_80);
+       sdata_info(sdata, "associate with %pM (try %d/%d)\n",
+                  assoc_data->bss->bssid, assoc_data->tries,
+                  IEEE80211_ASSOC_MAX_TRIES);
+       ret = ieee80211_send_assoc(sdata);
+       if (ret)
+               return ret;
 
-       /* P802.11-REVme/D0.3
-        * 27.1.1 Introduction to the HE PHY
-        * ...
-        * An HE STA shall support the following features:
-        * ...
-        * Single spatial stream HE-MCSs 0 to 7 (transmit and receive) in all
-        * supported channel widths for HE SU PPDUs
-        */
-       if ((mcs_80_map_tx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED ||
-           (mcs_80_map_rx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED) {
-               sdata_info(sdata,
-                          "Missing mandatory rates for 1 Nss, rx 0x%x, tx 0x%x, disable HE\n",
-                          mcs_80_map_tx, mcs_80_map_rx);
-               return false;
+       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);
+       } else {
+               assoc_data->timeout =
+                       round_jiffies_up(jiffies +
+                                        IEEE80211_ASSOC_TIMEOUT_LONG);
+               assoc_data->timeout_started = true;
+               run_again(sdata, assoc_data->timeout);
        }
 
-       if (!he_op)
-               return true;
+       return 0;
+}
 
-       ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set);
+void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
+                                 __le16 fc, bool acked)
+{
+       struct ieee80211_local *local = sdata->local;
 
-       /* make sure the AP is consistent with itself
-        *
-        * P802.11-REVme/D0.3
-        * 26.17.1 Basic HE BSS operation
-        *
-        * A STA that is operating in an HE BSS shall be able to receive and
-        * transmit at each of the <HE-MCS, NSS> tuple values indicated by the
-        * Basic HE-MCS And NSS Set field of the HE Operation parameter of the
-        * MLME-START.request primitive and shall be able to receive at each of
-        * the <HE-MCS, NSS> tuple values indicated by the Supported HE-MCS and
-        * NSS Set field in the HE Capabilities parameter of the MLMESTART.request
-        * primitive
-        */
-       for (nss = 8; nss > 0; nss--) {
-               u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3;
-               u8 ap_rx_val;
-               u8 ap_tx_val;
+       sdata->u.mgd.status_fc = fc;
+       sdata->u.mgd.status_acked = acked;
+       sdata->u.mgd.status_received = true;
 
-               if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED)
-                       continue;
+       ieee80211_queue_work(&local->hw, &sdata->work);
+}
 
-               ap_rx_val = (mcs_80_map_rx >> (2 * (nss - 1))) & 3;
-               ap_tx_val = (mcs_80_map_tx >> (2 * (nss - 1))) & 3;
+void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-               if (ap_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
-                   ap_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
-                   ap_rx_val < ap_op_val || ap_tx_val < ap_op_val) {
-                       sdata_info(sdata,
-                                  "Invalid rates for %d Nss, rx %d, tx %d oper %d, disable HE\n",
-                                  nss, ap_rx_val, ap_rx_val, ap_op_val);
-                       return false;
+       sdata_lock(sdata);
+
+       if (ifmgd->status_received) {
+               __le16 fc = ifmgd->status_fc;
+               bool status_acked = ifmgd->status_acked;
+
+               ifmgd->status_received = false;
+               if (ifmgd->auth_data && ieee80211_is_auth(fc)) {
+                       if (status_acked) {
+                               if (ifmgd->auth_data->algorithm ==
+                                   WLAN_AUTH_SAE)
+                                       ifmgd->auth_data->timeout =
+                                               jiffies +
+                                               IEEE80211_AUTH_TIMEOUT_SAE;
+                               else
+                                       ifmgd->auth_data->timeout =
+                                               jiffies +
+                                               IEEE80211_AUTH_TIMEOUT_SHORT;
+                               run_again(sdata, ifmgd->auth_data->timeout);
+                       } else {
+                               ifmgd->auth_data->timeout = jiffies - 1;
+                       }
+                       ifmgd->auth_data->timeout_started = true;
+               } else if (ifmgd->assoc_data &&
+                          (ieee80211_is_assoc_req(fc) ||
+                           ieee80211_is_reassoc_req(fc))) {
+                       if (status_acked) {
+                               ifmgd->assoc_data->timeout =
+                                       jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
+                               run_again(sdata, ifmgd->assoc_data->timeout);
+                       } else {
+                               ifmgd->assoc_data->timeout = jiffies - 1;
+                       }
+                       ifmgd->assoc_data->timeout_started = true;
                }
        }
 
-       return true;
-}
+       if (ifmgd->auth_data && ifmgd->auth_data->timeout_started &&
+           time_after(jiffies, ifmgd->auth_data->timeout)) {
+               if (ifmgd->auth_data->done || ifmgd->auth_data->waiting) {
+                       /*
+                        * ok ... we waited for assoc or continuation but
+                        * userspace didn't do it, so kill the auth data
+                        */
+                       ieee80211_destroy_auth_data(sdata, false);
+               } else if (ieee80211_auth(sdata)) {
+                       u8 bssid[ETH_ALEN];
+                       struct ieee80211_event event = {
+                               .type = MLME_EVENT,
+                               .u.mlme.data = AUTH_EVENT,
+                               .u.mlme.status = MLME_TIMEOUT,
+                       };
+
+                       memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
 
-static bool
-ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata,
-                                   struct ieee80211_supported_band *sband,
-                                   const struct ieee80211_he_operation *he_op)
-{
-       const struct ieee80211_sta_he_cap *sta_he_cap =
-               ieee80211_get_he_iftype_cap(sband,
-                                           ieee80211_vif_type_p2p(&sdata->vif));
-       u16 ap_min_req_set;
-       int i;
+                       ieee80211_destroy_auth_data(sdata, false);
 
-       if (!sta_he_cap || !he_op)
-               return false;
+                       cfg80211_auth_timeout(sdata->dev, bssid);
+                       drv_event_callback(sdata->local, sdata, &event);
+               }
+       } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
+               run_again(sdata, ifmgd->auth_data->timeout);
 
-       ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set);
+       if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
+           time_after(jiffies, ifmgd->assoc_data->timeout)) {
+               if ((ifmgd->assoc_data->need_beacon &&
+                    !sdata->deflink.u.mgd.have_beacon) ||
+                   ieee80211_do_assoc(sdata)) {
+                       struct ieee80211_event event = {
+                               .type = MLME_EVENT,
+                               .u.mlme.data = ASSOC_EVENT,
+                               .u.mlme.status = MLME_TIMEOUT,
+                       };
 
-       /* Need to go over for 80MHz, 160MHz and for 80+80 */
-       for (i = 0; i < 3; i++) {
-               const struct ieee80211_he_mcs_nss_supp *sta_mcs_nss_supp =
-                       &sta_he_cap->he_mcs_nss_supp;
-               u16 sta_mcs_map_rx =
-                       le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i]);
-               u16 sta_mcs_map_tx =
-                       le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i + 1]);
-               u8 nss;
-               bool verified = true;
+                       ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT);
+                       drv_event_callback(sdata->local, sdata, &event);
+               }
+       } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
+               run_again(sdata, ifmgd->assoc_data->timeout);
 
-               /*
-                * For each band there is a maximum of 8 spatial streams
-                * possible. Each of the sta_mcs_map_* is a 16-bit struct built
-                * of 2 bits per NSS (1-8), with the values defined in enum
-                * ieee80211_he_mcs_support. Need to make sure STA TX and RX
-                * capabilities aren't less than the AP's minimum requirements
-                * for this HE BSS per SS.
-                * It is enough to find one such band that meets the reqs.
-                */
-               for (nss = 8; nss > 0; nss--) {
-                       u8 sta_rx_val = (sta_mcs_map_rx >> (2 * (nss - 1))) & 3;
-                       u8 sta_tx_val = (sta_mcs_map_tx >> (2 * (nss - 1))) & 3;
-                       u8 ap_val = (ap_min_req_set >> (2 * (nss - 1))) & 3;
+       if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
+           ifmgd->associated) {
+               u8 *bssid = sdata->deflink.u.mgd.bssid;
+               int max_tries;
 
-                       if (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED)
-                               continue;
+               if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
+                       max_tries = max_nullfunc_tries;
+               else
+                       max_tries = max_probe_tries;
 
+               /* ACK received for nullfunc probing frame */
+               if (!ifmgd->probe_send_count)
+                       ieee80211_reset_ap_probe(sdata);
+               else if (ifmgd->nullfunc_failed) {
+                       if (ifmgd->probe_send_count < max_tries) {
+                               mlme_dbg(sdata,
+                                        "No ack for nullfunc frame to AP %pM, try %d/%i\n",
+                                        bssid, ifmgd->probe_send_count,
+                                        max_tries);
+                               ieee80211_mgd_probe_ap_send(sdata);
+                       } else {
+                               mlme_dbg(sdata,
+                                        "No ack for nullfunc frame to AP %pM, disconnecting.\n",
+                                        bssid);
+                               ieee80211_sta_connection_lost(sdata,
+                                       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
+                                       false);
+                       }
+               } else if (time_is_after_jiffies(ifmgd->probe_timeout))
+                       run_again(sdata, ifmgd->probe_timeout);
+               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);
+                       ieee80211_sta_connection_lost(sdata,
+                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
+               } else if (ifmgd->probe_send_count < max_tries) {
+                       mlme_dbg(sdata,
+                                "No probe response from AP %pM after %dms, try %d/%i\n",
+                                bssid, probe_wait_ms,
+                                ifmgd->probe_send_count, max_tries);
+                       ieee80211_mgd_probe_ap_send(sdata);
+               } else {
                        /*
-                        * Make sure the HE AP doesn't require MCSs that aren't
-                        * supported by the client as required by spec
-                        *
-                        * P802.11-REVme/D0.3
-                        * 26.17.1 Basic HE BSS operation
-                        *
-                        * An HE STA shall not attempt to join * (MLME-JOIN.request primitive)
-                        * a BSS, unless it supports (i.e., is able to both transmit and
-                        * receive using) all of the <HE-MCS, NSS> tuples in the basic
-                        * HE-MCS and NSS set.
+                        * We actually lost the connection ... or did we?
+                        * Let's make sure!
                         */
-                       if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
-                           sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
-                           (ap_val > sta_rx_val) || (ap_val > sta_tx_val)) {
-                               verified = false;
-                               break;
-                       }
-               }
+                       mlme_dbg(sdata,
+                                "No probe response from AP %pM after %dms, disconnecting.\n",
+                                bssid, probe_wait_ms);
 
-               if (verified)
-                       return true;
+                       ieee80211_sta_connection_lost(sdata,
+                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
+               }
        }
 
-       /* If here, STA doesn't meet AP's HE min requirements */
-       return false;
+       sdata_unlock(sdata);
 }
 
-static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
-                                 struct ieee80211_link_data *link,
-                                 struct cfg80211_bss *cbss)
+static void ieee80211_sta_bcn_mon_timer(struct timer_list *t)
 {
-       struct ieee80211_local *local = sdata->local;
-       const struct ieee80211_ht_cap *ht_cap = NULL;
-       const struct ieee80211_ht_operation *ht_oper = NULL;
-       const struct ieee80211_vht_operation *vht_oper = NULL;
-       const struct ieee80211_he_operation *he_oper = NULL;
-       const struct ieee80211_eht_operation *eht_oper = NULL;
-       const struct ieee80211_s1g_oper_ie *s1g_oper = NULL;
-       struct ieee80211_supported_band *sband;
-       struct cfg80211_chan_def chandef;
-       bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
-       bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ;
-       struct ieee80211_bss *bss = (void *)cbss->priv;
-       struct ieee802_11_elems *elems;
-       const struct cfg80211_bss_ies *ies;
-       int ret;
-       u32 i;
-       bool have_80mhz;
-
-       rcu_read_lock();
-
-       ies = rcu_dereference(cbss->ies);
-       elems = ieee802_11_parse_elems(ies->data, ies->len, false, cbss);
-       if (!elems) {
-               rcu_read_unlock();
-               return -ENOMEM;
-       }
+       struct ieee80211_sub_if_data *sdata =
+               from_timer(sdata, t, u.mgd.bcn_mon_timer);
 
-       sband = local->hw.wiphy->bands[cbss->channel->band];
+       if (WARN_ON(sdata->vif.valid_links))
+               return;
 
-       link->u.mgd.conn_flags &= ~(IEEE80211_CONN_DISABLE_40MHZ |
-                                   IEEE80211_CONN_DISABLE_80P80MHZ |
-                                   IEEE80211_CONN_DISABLE_160MHZ);
+       if (sdata->vif.bss_conf.csa_active &&
+           !sdata->deflink.u.mgd.csa_waiting_bcn)
+               return;
 
-       /* disable HT/VHT/HE if we don't support them */
-       if (!sband->ht_cap.ht_supported && !is_6ghz) {
-               mlme_dbg(sdata, "HT not supported, disabling HT/VHT/HE/EHT\n");
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HT;
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE;
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
-       }
+       if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
+               return;
 
-       if (!sband->vht_cap.vht_supported && is_5ghz) {
-               mlme_dbg(sdata, "VHT not supported, disabling VHT/HE/EHT\n");
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE;
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
-       }
+       sdata->u.mgd.connection_loss = false;
+       ieee80211_queue_work(&sdata->local->hw,
+                            &sdata->u.mgd.beacon_connection_loss_work);
+}
 
-       if (!ieee80211_get_he_iftype_cap(sband,
-                                        ieee80211_vif_type_p2p(&sdata->vif))) {
-               mlme_dbg(sdata, "HE not supported, disabling HE and EHT\n");
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE;
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
-       }
+static void ieee80211_sta_conn_mon_timer(struct timer_list *t)
+{
+       struct ieee80211_sub_if_data *sdata =
+               from_timer(sdata, t, u.mgd.conn_mon_timer);
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_local *local = sdata->local;
+       struct sta_info *sta;
+       unsigned long timeout;
 
-       if (!ieee80211_get_eht_iftype_cap(sband,
-                                         ieee80211_vif_type_p2p(&sdata->vif))) {
-               mlme_dbg(sdata, "EHT not supported, disabling EHT\n");
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
-       }
+       if (WARN_ON(sdata->vif.valid_links))
+               return;
 
-       if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) && !is_6ghz) {
-               ht_oper = elems->ht_operation;
-               ht_cap = elems->ht_cap_elem;
+       if (sdata->vif.bss_conf.csa_active &&
+           !sdata->deflink.u.mgd.csa_waiting_bcn)
+               return;
 
-               if (!ht_cap) {
-                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HT;
-                       ht_oper = NULL;
-               }
-       }
+       sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
+       if (!sta)
+               return;
 
-       if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) && !is_6ghz) {
-               vht_oper = elems->vht_operation;
-               if (vht_oper && !ht_oper) {
-                       vht_oper = NULL;
-                       sdata_info(sdata,
-                                  "AP advertised VHT without HT, disabling HT/VHT/HE\n");
-                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HT;
-                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
-                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE;
-                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_EHT;
-               }
+       timeout = sta->deflink.status_stats.last_ack;
+       if (time_before(sta->deflink.status_stats.last_ack, sta->deflink.rx_stats.last_rx))
+               timeout = sta->deflink.rx_stats.last_rx;
+       timeout += IEEE80211_CONNECTION_IDLE_TIME;
 
-               if (!elems->vht_cap_elem) {
-                       sdata_info(sdata,
-                                  "bad VHT capabilities, disabling VHT\n");
-                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
-                       vht_oper = NULL;
-               }
+       /* If timeout is after now, then update timer to fire at
+        * the later date, but do not actually probe at this time.
+        */
+       if (time_is_after_jiffies(timeout)) {
+               mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(timeout));
+               return;
        }
 
-       if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE)) {
-               he_oper = elems->he_operation;
+       ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
+}
 
-               if (is_6ghz) {
-                       struct ieee80211_bss_conf *bss_conf;
-                       u8 i, j = 0;
+static void ieee80211_sta_monitor_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            u.mgd.monitor_work);
 
-                       bss_conf = link->conf;
+       ieee80211_mgd_probe_ap(sdata, false);
+}
 
-                       if (elems->pwr_constr_elem)
-                               bss_conf->pwr_reduction = *elems->pwr_constr_elem;
+static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
+{
+       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+               __ieee80211_stop_poll(sdata);
 
-                       BUILD_BUG_ON(ARRAY_SIZE(bss_conf->tx_pwr_env) !=
-                                    ARRAY_SIZE(elems->tx_pwr_env));
+               /* let's probe the connection once */
+               if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
+                       ieee80211_queue_work(&sdata->local->hw,
+                                            &sdata->u.mgd.monitor_work);
+       }
+}
 
-                       for (i = 0; i < elems->tx_pwr_env_num; i++) {
-                               if (elems->tx_pwr_env_len[i] >
-                                   sizeof(bss_conf->tx_pwr_env[j]))
-                                       continue;
+#ifdef CONFIG_PM
+void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
-                               bss_conf->tx_pwr_env_num++;
-                               memcpy(&bss_conf->tx_pwr_env[j], elems->tx_pwr_env[i],
-                                      elems->tx_pwr_env_len[i]);
-                               j++;
-                       }
-               }
+       sdata_lock(sdata);
 
-               if (!ieee80211_verify_peer_he_mcs_support(sdata, ies, he_oper) ||
-                   !ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper))
-                       link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_HE |
-                                                 IEEE80211_CONN_DISABLE_EHT;
+       if (ifmgd->auth_data || ifmgd->assoc_data) {
+               const u8 *bssid = ifmgd->auth_data ?
+                               ifmgd->auth_data->bss->bssid :
+                               ifmgd->assoc_data->bss->bssid;
+
+               /*
+                * If we are trying to authenticate / associate while suspending,
+                * cfg80211 won't know and won't actually abort those attempts,
+                * thus we need to do that ourselves.
+                */
+               ieee80211_send_deauth_disassoc(sdata, bssid, bssid,
+                                              IEEE80211_STYPE_DEAUTH,
+                                              WLAN_REASON_DEAUTH_LEAVING,
+                                              false, frame_buf);
+               if (ifmgd->assoc_data)
+                       ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON);
+               if (ifmgd->auth_data)
+                       ieee80211_destroy_auth_data(sdata, false);
+               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
+                                     IEEE80211_DEAUTH_FRAME_LEN,
+                                     false);
        }
 
-       /*
-        * EHT requires HE to be supported as well. Specifically for 6 GHz
-        * channels, the operation channel information can only be deduced from
-        * both the 6 GHz operation information (from the HE operation IE) and
-        * EHT operation.
+       /* This is a bit of a hack - we should find a better and more generic
+        * solution to this. Normally when suspending, cfg80211 will in fact
+        * deauthenticate. However, it doesn't (and cannot) stop an ongoing
+        * auth (not so important) or assoc (this is the problem) process.
+        *
+        * As a consequence, it can happen that we are in the process of both
+        * associating and suspending, and receive an association response
+        * after cfg80211 has checked if it needs to disconnect, but before
+        * we actually set the flag to drop incoming frames. This will then
+        * cause the workqueue flush to process the association response in
+        * the suspend, resulting in a successful association just before it
+        * tries to remove the interface from the driver, which now though
+        * has a channel context assigned ... this results in issues.
+        *
+        * To work around this (for now) simply deauth here again if we're
+        * now connected.
         */
-       if (!(link->u.mgd.conn_flags &
-                       (IEEE80211_CONN_DISABLE_HE |
-                        IEEE80211_CONN_DISABLE_EHT)) &&
-           he_oper) {
-               const struct cfg80211_bss_ies *ies;
-               const u8 *eht_oper_ie;
+       if (ifmgd->associated && !sdata->local->wowlan) {
+               u8 bssid[ETH_ALEN];
+               struct cfg80211_deauth_request req = {
+                       .reason_code = WLAN_REASON_DEAUTH_LEAVING,
+                       .bssid = bssid,
+               };
 
-               ies = rcu_dereference(cbss->ies);
-               eht_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_EHT_OPERATION,
-                                                  ies->data, ies->len);
-               if (eht_oper_ie && eht_oper_ie[1] >=
-                   1 + sizeof(struct ieee80211_eht_operation))
-                       eht_oper = (void *)(eht_oper_ie + 3);
-               else
-                       eht_oper = NULL;
+               memcpy(bssid, sdata->vif.cfg.ap_addr, ETH_ALEN);
+               ieee80211_mgd_deauth(sdata, &req);
        }
 
-       /* 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;
+       sdata_unlock(sdata);
+}
+#endif
 
-               have_80mhz = true;
-               break;
+void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+       sdata_lock(sdata);
+       if (!ifmgd->associated) {
+               sdata_unlock(sdata);
+               return;
        }
 
-       if (!have_80mhz) {
-               sdata_info(sdata, "80 MHz not supported, disabling VHT\n");
-               link->u.mgd.conn_flags |= IEEE80211_CONN_DISABLE_VHT;
+       if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
+               sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
+               mlme_dbg(sdata, "driver requested disconnect after resume\n");
+               ieee80211_sta_connection_lost(sdata,
+                                             WLAN_REASON_UNSPECIFIED,
+                                             true);
+               sdata_unlock(sdata);
+               return;
        }
 
-       if (sband->band == NL80211_BAND_S1GHZ) {
-               s1g_oper = elems->s1g_oper;
-               if (!s1g_oper)
-                       sdata_info(sdata,
-                                  "AP missing S1G operation element?\n");
+       if (sdata->flags & IEEE80211_SDATA_DISCONNECT_HW_RESTART) {
+               sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_HW_RESTART;
+               mlme_dbg(sdata, "driver requested disconnect after hardware restart\n");
+               ieee80211_sta_connection_lost(sdata,
+                                             WLAN_REASON_UNSPECIFIED,
+                                             true);
+               sdata_unlock(sdata);
+               return;
        }
 
-       link->u.mgd.conn_flags |=
-               ieee80211_determine_chantype(link, sband,
-                                            cbss->channel,
-                                            bss->vht_cap_info,
-                                            ht_oper, vht_oper,
-                                            he_oper, eht_oper,
-                                            s1g_oper,
-                                            &chandef, false);
+       sdata_unlock(sdata);
+}
 
-       link->needed_rx_chains =
-               min(ieee80211_max_rx_chains(link, cbss), local->rx_chains);
+static void ieee80211_request_smps_mgd_work(struct work_struct *work)
+{
+       struct ieee80211_link_data *link =
+               container_of(work, struct ieee80211_link_data,
+                            u.mgd.request_smps_work);
 
-       rcu_read_unlock();
-       /* the element data was RCU protected so no longer valid anyway */
-       kfree(elems);
-       elems = NULL;
+       sdata_lock(link->sdata);
+       __ieee80211_request_smps_mgd(link->sdata, link,
+                                    link->u.mgd.driver_smps_mode);
+       sdata_unlock(link->sdata);
+}
 
-       if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE && is_6ghz) {
-               sdata_info(sdata, "Rejecting non-HE 6/7 GHz connection");
-               return -EINVAL;
-       }
+/* interface setup */
+void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-       /* will change later if needed */
-       link->smps_mode = IEEE80211_SMPS_OFF;
+       INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
+       INIT_WORK(&ifmgd->beacon_connection_loss_work,
+                 ieee80211_beacon_connection_loss_work);
+       INIT_WORK(&ifmgd->csa_connection_drop_work,
+                 ieee80211_csa_connection_drop_work);
+       INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
+                         ieee80211_tdls_peer_del_work);
+       timer_setup(&ifmgd->timer, ieee80211_sta_timer, 0);
+       timer_setup(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 0);
+       timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0);
+       INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
+                         ieee80211_sta_handle_tspec_ac_params_wk);
 
-       mutex_lock(&local->mtx);
-       /*
-        * If this fails (possibly due to channel context sharing
-        * on incompatible channels, e.g. 80+80 and 160 sharing the
-        * same control channel) try to use a smaller bandwidth.
-        */
-       ret = ieee80211_link_use_channel(link, &chandef,
-                                        IEEE80211_CHANCTX_SHARED);
+       ifmgd->flags = 0;
+       ifmgd->powersave = sdata->wdev.ps;
+       ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues;
+       ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len;
+       /* Setup TDLS data */
+       spin_lock_init(&ifmgd->teardown_lock);
+       ifmgd->teardown_skb = NULL;
+       ifmgd->orig_teardown_skb = NULL;
+}
 
-       /* don't downgrade for 5 and 10 MHz channels, though. */
-       if (chandef.width == NL80211_CHAN_WIDTH_5 ||
-           chandef.width == NL80211_CHAN_WIDTH_10)
-               goto out;
+void ieee80211_mgd_setup_link(struct ieee80211_link_data *link)
+{
+       struct ieee80211_local *local = link->sdata->local;
 
-       while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
-               link->u.mgd.conn_flags |=
-                       ieee80211_chandef_downgrade(&chandef);
-               ret = ieee80211_link_use_channel(link, &chandef,
-                                                IEEE80211_CHANCTX_SHARED);
+       link->u.mgd.p2p_noa_index = -1;
+       link->u.mgd.conn_flags = 0;
+       link->conf->bssid = link->u.mgd.bssid;
+
+       INIT_WORK(&link->u.mgd.request_smps_work,
+                 ieee80211_request_smps_mgd_work);
+       if (local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS)
+               link->u.mgd.req_smps = IEEE80211_SMPS_AUTOMATIC;
+       else
+               link->u.mgd.req_smps = IEEE80211_SMPS_OFF;
+
+       INIT_WORK(&link->u.mgd.chswitch_work, ieee80211_chswitch_work);
+       timer_setup(&link->u.mgd.chswitch_timer, ieee80211_chswitch_timer, 0);
+}
+
+/* scan finished notification */
+void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       /* Restart STA timers */
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               if (ieee80211_sdata_running(sdata))
+                       ieee80211_restart_sta_timer(sdata);
        }
- out:
-       mutex_unlock(&local->mtx);
-       return ret;
+       rcu_read_unlock();
 }
 
 static bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies,