Merge tag 'mac80211-for-davem-2018-09-03' of git://git.kernel.org/pub/scm/linux/kerne...
[muen/linux.git] / net / mac80211 / mlme.c
index b046bf95eb3c60fc58165f8cd76101dec294993a..3dbecae4be73cb1aae05752807b717a329b80980 100644 (file)
@@ -149,6 +149,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
                             struct ieee80211_channel *channel,
                             const struct ieee80211_ht_operation *ht_oper,
                             const struct ieee80211_vht_operation *vht_oper,
+                            const struct ieee80211_he_operation *he_oper,
                             struct cfg80211_chan_def *chandef, bool tracking)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -207,7 +208,27 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
        }
 
        vht_chandef = *chandef;
-       if (!ieee80211_chandef_vht_oper(vht_oper, &vht_chandef)) {
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && he_oper &&
+           (le32_to_cpu(he_oper->he_oper_params) &
+            IEEE80211_HE_OPERATION_VHT_OPER_INFO)) {
+               struct ieee80211_vht_operation he_oper_vht_cap;
+
+               /*
+                * Set only first 3 bytes (other 2 aren't used in
+                * ieee80211_chandef_vht_oper() anyway)
+                */
+               memcpy(&he_oper_vht_cap, he_oper->optional, 3);
+               he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0);
+
+               if (!ieee80211_chandef_vht_oper(&he_oper_vht_cap,
+                                               &vht_chandef)) {
+                       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
+                               sdata_info(sdata,
+                                          "HE AP VHT information is invalid, disable HE\n");
+                       ret = IEEE80211_STA_DISABLE_HE;
+                       goto out;
+               }
+       } else if (!ieee80211_chandef_vht_oper(vht_oper, &vht_chandef)) {
                if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
                        sdata_info(sdata,
                                   "AP VHT information is invalid, disable VHT\n");
@@ -300,12 +321,14 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
                               const struct ieee80211_ht_cap *ht_cap,
                               const struct ieee80211_ht_operation *ht_oper,
                               const struct ieee80211_vht_operation *vht_oper,
+                              const struct ieee80211_he_operation *he_oper,
                               const u8 *bssid, u32 *changed)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_supported_band *sband;
-       struct ieee80211_channel *chan;
+       struct ieee80211_channel *chan = sdata->vif.bss_conf.chandef.chan;
+       struct ieee80211_supported_band *sband =
+               local->hw.wiphy->bands[chan->band];
        struct cfg80211_chan_def chandef;
        u16 ht_opmode;
        u32 flags;
@@ -320,6 +343,11 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
                vht_oper = NULL;
 
+       /* don't check HE if we associated as non-HE station */
+       if (ifmgd->flags & IEEE80211_STA_DISABLE_HE ||
+           !ieee80211_get_he_sta_cap(sband))
+               he_oper = NULL;
+
        if (WARN_ON_ONCE(!sta))
                return -EINVAL;
 
@@ -333,12 +361,9 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
                sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
        }
 
-       chan = sdata->vif.bss_conf.chandef.chan;
-       sband = local->hw.wiphy->bands[chan->band];
-
-       /* calculate new channel (type) based on HT/VHT operation IEs */
+       /* calculate new channel (type) based on HT/VHT/HE operation IEs */
        flags = ieee80211_determine_chantype(sdata, sband, chan,
-                                            ht_oper, vht_oper,
+                                            ht_oper, vht_oper, he_oper,
                                             &chandef, true);
 
        /*
@@ -582,6 +607,34 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
        ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
 }
 
+/* This function determines HE capability flags for the association
+ * and builds the IE.
+ */
+static void ieee80211_add_he_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 = NULL;
+       u8 he_cap_size;
+
+       he_cap = ieee80211_get_he_sta_cap(sband);
+       if (!he_cap)
+               return;
+
+       /*
+        * TODO: the 1 added is because this temporarily is under the EXTENSION
+        * IE. Get rid of it when it moves.
+        */
+       he_cap_size =
+               2 + 1 + sizeof(he_cap->he_cap_elem) +
+               ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) +
+               ieee80211_he_ppe_size(he_cap->ppe_thres[0],
+                                     he_cap->he_cap_elem.phy_cap_info);
+       pos = skb_put(skb, he_cap_size);
+       ieee80211_ie_build_he_cap(pos, he_cap, pos + he_cap_size);
+}
+
 static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
@@ -643,6 +696,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                        2 + 2 * sband->n_channels + /* supported channels */
                        2 + sizeof(struct ieee80211_ht_cap) + /* HT */
                        2 + sizeof(struct ieee80211_vht_cap) + /* VHT */
+                       2 + 1 + sizeof(struct ieee80211_he_cap_elem) + /* HE */
+                               sizeof(struct ieee80211_he_mcs_nss_supp) +
+                               IEEE80211_HE_PPE_THRES_MAX_LEN +
                        assoc_data->ie_len + /* extra IEs */
                        (assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) +
                        9, /* WMM */
@@ -827,11 +883,41 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                offset = noffset;
        }
 
+       /* if present, add any custom IEs that go before HE */
+       if (assoc_data->ie_len) {
+               static const u8 before_he[] = {
+                       /*
+                        * no need to list the ones split off before VHT
+                        * or generated here
+                        */
+                       WLAN_EID_OPMODE_NOTIF,
+                       WLAN_EID_EXTENSION, WLAN_EID_EXT_FUTURE_CHAN_GUIDANCE,
+                       /* 11ai elements */
+                       WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_SESSION,
+                       WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_PUBLIC_KEY,
+                       WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_KEY_CONFIRM,
+                       WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_HLP_CONTAINER,
+                       WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN,
+                       /* TODO: add 11ah/11aj/11ak elements */
+               };
+
+               /* RIC already taken above, so no need to handle here anymore */
+               noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
+                                            before_he, ARRAY_SIZE(before_he),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, assoc_data->ie + offset, noffset - offset);
+               offset = noffset;
+       }
+
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
                ieee80211_add_vht_ie(sdata, skb, sband,
                                     &assoc_data->ap_vht_cap);
 
-       /* if present, add any custom non-vendor IEs that go after HT */
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
+               ieee80211_add_he_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,
                                                    assoc_data->ie_len,
@@ -898,6 +984,11 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
        struct ieee80211_hdr_3addr *nullfunc;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
+       /* Don't send NDPs when STA is connected HE */
+       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+           !(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
+               return;
+
        skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif,
                !ieee80211_hw_check(&local->hw, DOESNT_SUPPORT_QOS_NDP));
        if (!skb)
@@ -929,6 +1020,10 @@ static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
        if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
                return;
 
+       /* Don't send NDPs when connected HE */
+       if (!(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE))
+               return;
+
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30);
        if (!skb)
                return;
@@ -1763,9 +1858,11 @@ static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
 }
 
 /* MLME */
-static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
-                                    struct ieee80211_sub_if_data *sdata,
-                                    const u8 *wmm_param, size_t wmm_param_len)
+static bool
+ieee80211_sta_wmm_params(struct ieee80211_local *local,
+                        struct ieee80211_sub_if_data *sdata,
+                        const u8 *wmm_param, size_t wmm_param_len,
+                        const struct ieee80211_mu_edca_param_set *mu_edca)
 {
        struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1812,6 +1909,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
                                uapsd = true;
+                       params[ac].mu_edca = !!mu_edca;
+                       if (mu_edca)
+                               params[ac].mu_edca_param_rec = mu_edca->ac_bk;
                        break;
                case 2: /* AC_VI */
                        ac = IEEE80211_AC_VI;
@@ -1819,6 +1919,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
                                uapsd = true;
+                       params[ac].mu_edca = !!mu_edca;
+                       if (mu_edca)
+                               params[ac].mu_edca_param_rec = mu_edca->ac_vi;
                        break;
                case 3: /* AC_VO */
                        ac = IEEE80211_AC_VO;
@@ -1826,6 +1929,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
                                uapsd = true;
+                       params[ac].mu_edca = !!mu_edca;
+                       if (mu_edca)
+                               params[ac].mu_edca_param_rec = mu_edca->ac_vo;
                        break;
                case 0: /* AC_BE */
                default:
@@ -1834,6 +1940,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
                                uapsd = true;
+                       params[ac].mu_edca = !!mu_edca;
+                       if (mu_edca)
+                               params[ac].mu_edca_param_rec = mu_edca->ac_be;
                        break;
                }
 
@@ -2282,6 +2391,20 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
                ieee80211_sta_reset_conn_monitor(sdata);
 }
 
+static void ieee80211_mlme_send_probe_req(struct ieee80211_sub_if_data *sdata,
+                                         const u8 *src, const u8 *dst,
+                                         const u8 *ssid, size_t ssid_len,
+                                         struct ieee80211_channel *channel)
+{
+       struct sk_buff *skb;
+
+       skb = ieee80211_build_probe_req(sdata, src, dst, (u32)-1, channel,
+                                       ssid, ssid_len, NULL, 0,
+                                       IEEE80211_PROBE_FLAG_DIRECTED);
+       if (skb)
+               ieee80211_tx_skb(sdata, skb);
+}
+
 static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -2328,10 +2451,9 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
                else
                        ssid_len = ssid[1];
 
-               ieee80211_send_probe_req(sdata, sdata->vif.addr, dst,
-                                        ssid + 2, ssid_len, NULL,
-                                        0, (u32) -1, true, 0,
-                                        ifmgd->associated->channel, false);
+               ieee80211_mlme_send_probe_req(sdata, sdata->vif.addr, dst,
+                                             ssid + 2, ssid_len,
+                                             ifmgd->associated->channel);
                rcu_read_unlock();
        }
 
@@ -2433,7 +2555,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
        skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid,
                                        (u32) -1, cbss->channel,
                                        ssid + 2, ssid_len,
-                                       NULL, 0, true);
+                                       NULL, 0, IEEE80211_PROBE_FLAG_DIRECTED);
        rcu_read_unlock();
 
        return skb;
@@ -3074,6 +3196,25 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
+       /*
+        * If AP doesn't support HT, or it doesn't have HE mandatory IEs, mark
+        * HE 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) ||
+           (!elems.he_cap && !elems.he_operation))
+               ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+
+       if (!(ifmgd->flags & IEEE80211_STA_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;
+       }
+
        /* Set up internal HT/VHT capabilities */
        if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
                ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
@@ -3083,6 +3224,48 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
                                                    elems.vht_cap_elem, sta);
 
+       if (elems.he_operation && !(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
+           elems.he_cap) {
+               ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
+                                                 elems.he_cap,
+                                                 elems.he_cap_len,
+                                                 sta);
+
+               bss_conf->he_support = sta->sta.he_cap.has_he;
+       } else {
+               bss_conf->he_support = false;
+       }
+
+       if (bss_conf->he_support) {
+               u32 he_oper_params =
+                       le32_to_cpu(elems.he_operation->he_oper_params);
+
+               bss_conf->bss_color = he_oper_params &
+                                     IEEE80211_HE_OPERATION_BSS_COLOR_MASK;
+               bss_conf->htc_trig_based_pkt_ext =
+                       (he_oper_params &
+                        IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK) <<
+                       IEEE80211_HE_OPERATION_DFLT_PE_DURATION_OFFSET;
+               bss_conf->frame_time_rts_th =
+                       (he_oper_params &
+                        IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK) <<
+                       IEEE80211_HE_OPERATION_RTS_THRESHOLD_OFFSET;
+
+               bss_conf->multi_sta_back_32bit =
+                       sta->sta.he_cap.he_cap_elem.mac_cap_info[2] &
+                       IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP;
+
+               bss_conf->ack_enabled =
+                       sta->sta.he_cap.he_cap_elem.mac_cap_info[2] &
+                       IEEE80211_HE_MAC_CAP2_ACK_EN;
+
+               bss_conf->uora_exists = !!elems.uora_element;
+               if (elems.uora_element)
+                       bss_conf->uora_ocw_range = elems.uora_element[0];
+
+               /* TODO: OPEN: what happens if BSS color disable is set? */
+       }
+
        /*
         * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data
         * in their association response, so ignore that data for our own
@@ -3142,7 +3325,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
                ieee80211_set_wmm_default(sdata, false, false);
        } else if (!ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
-                                            elems.wmm_param_len)) {
+                                            elems.wmm_param_len,
+                                            elems.mu_edca_param_set)) {
                /* still enable QoS since we might have HT/VHT */
                ieee80211_set_wmm_default(sdata, false, true);
                /* set the disable-WMM flag in this case to disable
@@ -3656,7 +3840,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
            ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
-                                    elems.wmm_param_len))
+                                    elems.wmm_param_len,
+                                    elems.mu_edca_param_set))
                changed |= BSS_CHANGED_QOS;
 
        /*
@@ -3695,7 +3880,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        if (ieee80211_config_bw(sdata, sta,
                                elems.ht_cap_elem, elems.ht_operation,
-                               elems.vht_operation, bssid, &changed)) {
+                               elems.vht_operation, elems.he_operation,
+                               bssid, &changed)) {
                mutex_unlock(&local->sta_mtx);
                sdata_info(sdata,
                           "failed to follow AP %pM bandwidth change, disconnect\n",
@@ -4332,6 +4518,68 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
        return chains;
 }
 
+static bool
+ieee80211_verify_sta_he_mcs_support(struct ieee80211_supported_band *sband,
+                                   const struct ieee80211_he_operation *he_op)
+{
+       const struct ieee80211_sta_he_cap *sta_he_cap =
+               ieee80211_get_he_sta_cap(sband);
+       u16 ap_min_req_set;
+       int i;
+
+       if (!sta_he_cap || !he_op)
+               return false;
+
+       ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set);
+
+       /* 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;
+
+               /*
+                * 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 (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED)
+                               continue;
+
+                       /*
+                        * Make sure the HE AP doesn't require MCSs that aren't
+                        * supported by the client
+                        */
+                       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;
+                       }
+               }
+
+               if (verified)
+                       return true;
+       }
+
+       /* If here, STA doesn't meet AP's HE min requirements */
+       return false;
+}
+
 static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
                                  struct cfg80211_bss *cbss)
 {
@@ -4340,6 +4588,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
        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;
        struct ieee80211_supported_band *sband;
        struct cfg80211_chan_def chandef;
        int ret;
@@ -4395,6 +4644,24 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
                }
        }
 
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
+           ieee80211_get_he_sta_cap(sband)) {
+               const struct cfg80211_bss_ies *ies;
+               const u8 *he_oper_ie;
+
+               ies = rcu_dereference(cbss->ies);
+               he_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_OPERATION,
+                                                 ies->data, ies->len);
+               if (he_oper_ie &&
+                   he_oper_ie[1] == ieee80211_he_oper_size(&he_oper_ie[3]))
+                       he_oper = (void *)(he_oper_ie + 3);
+               else
+                       he_oper = NULL;
+
+               if (!ieee80211_verify_sta_he_mcs_support(sband, he_oper))
+                       ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+       }
+
        /* Allow VHT if at least one channel on the sband supports 80 MHz */
        have_80mhz = false;
        for (i = 0; i < sband->n_channels; i++) {
@@ -4411,7 +4678,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
 
        ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
                                                     cbss->channel,
-                                                    ht_oper, vht_oper,
+                                                    ht_oper, vht_oper, he_oper,
                                                     &chandef, false);
 
        sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
@@ -4817,8 +5084,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
                        ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
                        ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+                       ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
                        netdev_info(sdata->dev,
-                                   "disabling HT/VHT due to WEP/TKIP use\n");
+                                   "disabling HE/HT/VHT due to WEP/TKIP use\n");
                }
        }