Merge tag 'wireless-next-2022-03-11' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / net / mac80211 / mlme.c
index 950be0f..476b213 100644 (file)
@@ -150,6 +150,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                             const struct ieee80211_ht_operation *ht_oper,
                             const struct ieee80211_vht_operation *vht_oper,
                             const struct ieee80211_he_operation *he_oper,
+                            const struct ieee80211_eht_operation *eht_oper,
                             const struct ieee80211_s1g_oper_ie *s1g_oper,
                             struct cfg80211_chan_def *chandef, bool tracking)
 {
@@ -165,12 +166,14 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        chandef->freq1_offset = channel->freq_offset;
 
        if (channel->band == NL80211_BAND_6GHZ) {
-               if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, chandef)) {
+               if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, eht_oper,
+                                                   chandef)) {
                        mlme_dbg(sdata,
-                                "bad 6 GHz operation, disabling HT/VHT/HE\n");
+                                "bad 6 GHz operation, disabling HT/VHT/HE/EHT\n");
                        ret = IEEE80211_STA_DISABLE_HT |
                              IEEE80211_STA_DISABLE_VHT |
-                             IEEE80211_STA_DISABLE_HE;
+                             IEEE80211_STA_DISABLE_HE |
+                             IEEE80211_STA_DISABLE_EHT;
                } else {
                        ret = 0;
                }
@@ -197,7 +200,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                mlme_dbg(sdata, "HT operation missing / HT not supported\n");
                ret = IEEE80211_STA_DISABLE_HT |
                      IEEE80211_STA_DISABLE_VHT |
-                     IEEE80211_STA_DISABLE_HE;
+                     IEEE80211_STA_DISABLE_HE |
+                     IEEE80211_STA_DISABLE_EHT;
                goto out;
        }
 
@@ -220,7 +224,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                           ht_oper->primary_chan, channel->band);
                ret = IEEE80211_STA_DISABLE_HT |
                      IEEE80211_STA_DISABLE_VHT |
-                     IEEE80211_STA_DISABLE_HE;
+                     IEEE80211_STA_DISABLE_HE |
+                     IEEE80211_STA_DISABLE_EHT;
                goto out;
        }
 
@@ -261,7 +266,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
                                sdata_info(sdata,
                                           "HE AP VHT information is invalid, disabling HE\n");
-                       ret = IEEE80211_STA_DISABLE_HE;
+                       ret = IEEE80211_STA_DISABLE_HE | IEEE80211_STA_DISABLE_EHT;
                        goto out;
                }
        } else if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
@@ -341,7 +346,8 @@ out:
                if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
                        ret = IEEE80211_STA_DISABLE_HT |
                              IEEE80211_STA_DISABLE_VHT |
-                             IEEE80211_STA_DISABLE_HE;
+                             IEEE80211_STA_DISABLE_HE |
+                             IEEE80211_STA_DISABLE_EHT;
                        break;
                }
 
@@ -350,7 +356,11 @@ out:
 
        if (!he_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef,
                                                 IEEE80211_CHAN_NO_HE))
-               ret |= IEEE80211_STA_DISABLE_HE;
+               ret |= IEEE80211_STA_DISABLE_HE | IEEE80211_STA_DISABLE_EHT;
+
+       if (!eht_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef,
+                                                 IEEE80211_CHAN_NO_EHT))
+               ret |= IEEE80211_STA_DISABLE_EHT;
 
        if (chandef->width != vht_chandef.width && !tracking)
                sdata_info(sdata,
@@ -367,6 +377,7 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
                               const struct ieee80211_ht_operation *ht_oper,
                               const struct ieee80211_vht_operation *vht_oper,
                               const struct ieee80211_he_operation *he_oper,
+                              const struct ieee80211_eht_operation *eht_oper,
                               const struct ieee80211_s1g_oper_ie *s1g_oper,
                               const u8 *bssid, u32 *changed)
 {
@@ -392,9 +403,16 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
        /* don't check HE if we associated as non-HE station */
        if (ifmgd->flags & IEEE80211_STA_DISABLE_HE ||
            !ieee80211_get_he_iftype_cap(sband,
-                                        ieee80211_vif_type_p2p(&sdata->vif)))
-
+                                        ieee80211_vif_type_p2p(&sdata->vif))) {
                he_oper = NULL;
+               eht_oper = NULL;
+       }
+
+       /* don't check EHT if we associated as non-EHT station */
+       if (ifmgd->flags & IEEE80211_STA_DISABLE_EHT ||
+           !ieee80211_get_eht_iftype_cap(sband,
+                                        ieee80211_vif_type_p2p(&sdata->vif)))
+               eht_oper = NULL;
 
        if (WARN_ON_ONCE(!sta))
                return -EINVAL;
@@ -414,7 +432,8 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
 
        /* calculate new channel (type) based on HT/VHT/HE operation IEs */
        flags = ieee80211_determine_chantype(sdata, sband, chan, vht_cap_info,
-                                            ht_oper, vht_oper, he_oper,
+                                            ht_oper, vht_oper,
+                                            he_oper, eht_oper,
                                             s1g_oper, &chandef, true);
 
        /*
@@ -448,9 +467,11 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
        if (flags != (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
                                      IEEE80211_STA_DISABLE_VHT |
                                      IEEE80211_STA_DISABLE_HE |
+                                     IEEE80211_STA_DISABLE_EHT |
                                      IEEE80211_STA_DISABLE_40MHZ |
                                      IEEE80211_STA_DISABLE_80P80MHZ |
-                                     IEEE80211_STA_DISABLE_160MHZ)) ||
+                                     IEEE80211_STA_DISABLE_160MHZ |
+                                     IEEE80211_STA_DISABLE_320MHZ)) ||
            !cfg80211_chandef_valid(&chandef)) {
                sdata_info(sdata,
                           "AP %pM changed caps/bw in a way we can't support (0x%x/0x%x) - disconnect\n",
@@ -672,6 +693,48 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata,
        ieee80211_ie_build_he_6ghz_cap(sdata, skb);
 }
 
+static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata,
+                                struct sk_buff *skb,
+                                struct ieee80211_supported_band *sband)
+{
+       u8 *pos;
+       const struct ieee80211_sta_he_cap *he_cap;
+       const struct ieee80211_sta_eht_cap *eht_cap;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       u8 eht_cap_size;
+       bool reg_cap = false;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (!WARN_ON_ONCE(!chanctx_conf))
+               reg_cap = cfg80211_chandef_usable(sdata->wdev.wiphy,
+                                                 &chanctx_conf->def,
+                                                 IEEE80211_CHAN_NO_HE |
+                                                 IEEE80211_CHAN_NO_EHT);
+       rcu_read_unlock();
+
+       he_cap = ieee80211_get_he_iftype_cap(sband,
+                                            ieee80211_vif_type_p2p(&sdata->vif));
+       eht_cap = ieee80211_get_eht_iftype_cap(sband,
+                                              ieee80211_vif_type_p2p(&sdata->vif));
+
+       /*
+        * EHT capabilities element is only added if the HE capabilities element
+        * was added so assume that 'he_cap' is valid and don't check it.
+        */
+       if (WARN_ON(!he_cap || !eht_cap || !reg_cap))
+               return;
+
+       eht_cap_size =
+               2 + 1 + sizeof(eht_cap->eht_cap_elem) +
+               ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
+                                          &eht_cap->eht_cap_elem) +
+               ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
+                                      eht_cap->eht_cap_elem.phy_cap_info);
+       pos = skb_put(skb, eht_cap_size);
+       ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + eht_cap_size);
+}
+
 static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
@@ -992,17 +1055,22 @@ skip_rates:
                                     &assoc_data->ap_vht_cap);
 
        /*
-        * If AP doesn't support HT, mark HE as disabled.
+        * If AP doesn't support HT, mark HE and EHT as disabled.
         * If on the 5GHz band, make sure it supports VHT.
         */
        if (ifmgd->flags & IEEE80211_STA_DISABLE_HT ||
            (sband->band == NL80211_BAND_5GHZ &&
             ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
-               ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_HE |
+                               IEEE80211_STA_DISABLE_EHT;
 
-       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) {
                ieee80211_add_he_ie(sdata, skb, sband);
 
+               if (!(ifmgd->flags & IEEE80211_STA_DISABLE_EHT))
+                       ieee80211_add_eht_ie(sdata, skb, sband);
+       }
+
        /* if present, add any custom non-vendor IEs that go after HE */
        if (assoc_data->ie_len) {
                noffset = ieee80211_ie_split_vendor(assoc_data->ie,
@@ -3529,10 +3597,25 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                        bss_conf->twt_protected = false;
 
                changed |= ieee80211_recalc_twt_req(sdata, sta, elems);
+
+               if (elems->eht_operation && elems->eht_cap &&
+                   !(ifmgd->flags & IEEE80211_STA_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,
+                                                           sta);
+
+                       bss_conf->eht_support = sta->sta.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;
        }
 
        bss_conf->twt_broadcast =
@@ -4276,6 +4359,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (ieee80211_config_bw(sdata, sta, 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,
@@ -4871,6 +4955,18 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
                sdata_unlock(sdata);
                return;
        }
+
+       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,
+                                             ifmgd->associated->bssid,
+                                             WLAN_REASON_UNSPECIFIED,
+                                             true);
+               sdata_unlock(sdata);
+               return;
+       }
+
        sdata_unlock(sdata);
 }
 #endif
@@ -5206,6 +5302,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
        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;
@@ -5236,22 +5333,31 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
 
        /* 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\n");
+               mlme_dbg(sdata, "HT not supported, disabling HT/VHT/HE/EHT\n");
                ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
                ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_EHT;
        }
 
        if (!sband->vht_cap.vht_supported && is_5ghz) {
-               mlme_dbg(sdata, "VHT not supported, disabling VHT/HE\n");
+               mlme_dbg(sdata, "VHT not supported, disabling VHT/HE/EHT\n");
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
                ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_EHT;
        }
 
        if (!ieee80211_get_he_iftype_cap(sband,
                                         ieee80211_vif_type_p2p(&sdata->vif))) {
-               mlme_dbg(sdata, "HE not supported, disabling it\n");
+               mlme_dbg(sdata, "HE not supported, disabling HE and EHT\n");
                ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_EHT;
+       }
+
+       if (!ieee80211_get_eht_iftype_cap(sband,
+                                         ieee80211_vif_type_p2p(&sdata->vif))) {
+               mlme_dbg(sdata, "EHT not supported, disabling EHT\n");
+               ifmgd->flags |= IEEE80211_STA_DISABLE_EHT;
        }
 
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && !is_6ghz) {
@@ -5273,6 +5379,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
                        ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
                        ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
                        ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+                       ifmgd->flags |= IEEE80211_STA_DISABLE_EHT;
                }
 
                if (!elems->vht_cap_elem) {
@@ -5312,7 +5419,29 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
 
                if (!ieee80211_verify_peer_he_mcs_support(sdata, ies, he_oper) ||
                    !ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper))
-                       ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+                       ifmgd->flags |= IEEE80211_STA_DISABLE_HE |
+                                       IEEE80211_STA_DISABLE_EHT;
+       }
+
+       /*
+        * 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.
+        */
+       if (!(ifmgd->flags & (IEEE80211_STA_DISABLE_HE |
+                             IEEE80211_STA_DISABLE_EHT)) && he_oper) {
+               const struct cfg80211_bss_ies *ies;
+               const u8 *eht_oper_ie;
+
+               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;
        }
 
        /* Allow VHT if at least one channel on the sband supports 80 MHz */
@@ -5341,7 +5470,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
        ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
                                                     cbss->channel,
                                                     bss->vht_cap_info,
-                                                    ht_oper, vht_oper, he_oper,
+                                                    ht_oper, vht_oper,
+                                                    he_oper, eht_oper,
                                                     s1g_oper,
                                                     &chandef, false);
 
@@ -5832,6 +5962,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
                        ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
                        ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+                       ifmgd->flags |= IEEE80211_STA_DISABLE_EHT;
                        netdev_info(sdata->dev,
                                    "disabling HT/VHT/HE due to WEP/TKIP use\n");
                }
@@ -5839,11 +5970,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
        sband = local->hw.wiphy->bands[req->bss->channel->band];
 
-       /* also disable HT/VHT/HE if the AP doesn't use WMM */
+       /* also disable HT/VHT/HE/EHT if the AP doesn't use WMM */
        if (!bss->wmm_used) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
                ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_EHT;
                netdev_info(sdata->dev,
                            "disabling HT/VHT/HE as WMM/QoS is not supported by the AP\n");
        }
@@ -5897,9 +6029,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                memcpy(&assoc_data->ap_vht_cap, vht_elem->data,
                       sizeof(struct ieee80211_vht_cap));
        } else if (is_5ghz) {
-               sdata_info(sdata, "VHT capa missing/short, disabling VHT/HE\n");
+               sdata_info(sdata,
+                          "VHT capa missing/short, disabling VHT/HE/EHT\n");
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT |
-                               IEEE80211_STA_DISABLE_HE;
+                               IEEE80211_STA_DISABLE_HE |
+                               IEEE80211_STA_DISABLE_EHT;
        }
        rcu_read_unlock();
 
@@ -5978,6 +6112,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
                ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
                ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_EHT;
        }
 
        if (req->flags & ASSOC_REQ_DISABLE_VHT) {
@@ -5986,8 +6121,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        }
 
        if (req->flags & ASSOC_REQ_DISABLE_HE) {
-               mlme_dbg(sdata, "HE disabled by flag, disabling VHT\n");
+               mlme_dbg(sdata, "HE disabled by flag, disabling HE/EHT\n");
                ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_EHT;
        }
 
        err = ieee80211_prep_connection(sdata, req->bss, true, override);