]> Git Repo - J-linux.git/commitdiff
Merge tag 'mac80211-for-davem-2018-09-03' of git://git.kernel.org/pub/scm/linux/kerne...
authorDavid S. Miller <[email protected]>
Tue, 4 Sep 2018 05:12:02 +0000 (22:12 -0700)
committerDavid S. Miller <[email protected]>
Tue, 4 Sep 2018 05:12:02 +0000 (22:12 -0700)
Johannes Berg says:

====================
Here are quite a large number of fixes, notably:
 * various A-MSDU building fixes (currently only affects mt76)
 * syzkaller & spectre fixes in hwsim
 * TXQ vs. teardown fix that was causing crashes
 * embed WMM info in reg rule, bad code here had been causing crashes
 * one compilation issue with fix from Arnd (rfkill-gpio includes)
 * fixes for a race and bad data during/after channel switch
 * nl80211: a validation fix, attribute type & unit fixes
along with other small fixes.
====================

Signed-off-by: David S. Miller <[email protected]>
1  2 
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/mac80211_hwsim.c
include/net/cfg80211.h
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/tx.c
net/mac80211/util.c
net/wireless/nl80211.c
net/wireless/util.c

index b4c3a957c102d2463fde9d4987dcdf3764e2a694,88121548eb9fe381295c3d07a4168e789014707c..73969dbeb5c5ff390709dea24cbd61da794d9a3a
@@@ -430,13 -430,6 +430,13 @@@ static void iwl_init_vht_hw_capab(cons
                else
                        vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
                break;
 +      case IWL_AMSDU_2K:
 +              if (cfg->mq_rx_supported)
 +                      vht_cap->cap |=
 +                              IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
 +              else
 +                      WARN(1, "RB size of 2K is not supported by this device\n");
 +              break;
        case IWL_AMSDU_4K:
                vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
                break;
        vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
  }
  
 +static struct ieee80211_sband_iftype_data iwl_he_capa = {
 +      .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP),
 +      .he_cap = {
 +              .has_he = true,
 +              .he_cap_elem = {
 +                      .mac_cap_info[0] =
 +                              IEEE80211_HE_MAC_CAP0_HTC_HE,
 +                      .mac_cap_info[1] =
 +                              IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
 +                              IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_QOS_8,
 +                      .mac_cap_info[2] =
 +                              IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP |
 +                              IEEE80211_HE_MAC_CAP2_ACK_EN,
 +                      .mac_cap_info[3] =
 +                              IEEE80211_HE_MAC_CAP3_GRP_ADDR_MULTI_STA_BA_DL_MU |
 +                              IEEE80211_HE_MAC_CAP3_MAX_A_AMPDU_LEN_EXP_VHT_2,
 +                      .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU,
 +                      .phy_cap_info[0] =
 +                              IEEE80211_HE_PHY_CAP0_DUAL_BAND |
 +                              IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
 +                              IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
 +                              IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G,
 +                      .phy_cap_info[1] =
 +                              IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
 +                              IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
 +                              IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_MAX_NSTS,
 +                      .phy_cap_info[2] =
 +                              IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
 +                              IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
 +                              IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ,
 +                      .phy_cap_info[3] =
 +                              IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK |
 +                              IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1 |
 +                              IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK |
 +                              IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1,
 +                      .phy_cap_info[4] =
 +                              IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
 +                              IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8 |
 +                              IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8,
 +                      .phy_cap_info[5] =
 +                              IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 |
 +                              IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2,
 +                      .phy_cap_info[6] =
 +                              IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT,
 +                      .phy_cap_info[7] =
 +                              IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR |
 +                              IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI |
 +                              IEEE80211_HE_PHY_CAP7_MAX_NC_7,
 +                      .phy_cap_info[8] =
 +                              IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI |
 +                              IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
 +                              IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
 +                              IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU,
 +              },
 +              /*
 +               * Set default Tx/Rx HE MCS NSS Support field. Indicate support
 +               * for up to 2 spatial streams and all MCS, without any special
 +               * cases
 +               */
 +              .he_mcs_nss_supp = {
 +                      .rx_mcs_80 = cpu_to_le16(0xfffa),
 +                      .tx_mcs_80 = cpu_to_le16(0xfffa),
 +                      .rx_mcs_160 = cpu_to_le16(0xfffa),
 +                      .tx_mcs_160 = cpu_to_le16(0xfffa),
 +                      .rx_mcs_80p80 = cpu_to_le16(0xffff),
 +                      .tx_mcs_80p80 = cpu_to_le16(0xffff),
 +              },
 +              /*
 +               * Set default PPE thresholds, with PPET16 set to 0, PPET8 set
 +               * to 7
 +               */
 +              .ppe_thres = {0x61, 0x1c, 0xc7, 0x71},
 +      },
 +};
 +
 +static void iwl_init_he_hw_capab(struct ieee80211_supported_band *sband,
 +                               u8 tx_chains, u8 rx_chains)
 +{
 +      if (sband->band == NL80211_BAND_2GHZ ||
 +          sband->band == NL80211_BAND_5GHZ)
 +              sband->iftype_data = &iwl_he_capa;
 +      else
 +              return;
 +
 +      sband->n_iftype_data = 1;
 +
 +      /* If not 2x2, we need to indicate 1x1 in the Midamble RX Max NSTS */
 +      if ((tx_chains & rx_chains) != ANT_AB) {
 +              iwl_he_capa.he_cap.he_cap_elem.phy_cap_info[1] &=
 +                      ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_MAX_NSTS;
 +              iwl_he_capa.he_cap.he_cap_elem.phy_cap_info[2] &=
 +                      ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_MAX_NSTS;
 +      }
 +}
 +
  static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
                            struct iwl_nvm_data *data,
                            const __le16 *nvm_ch_flags, u8 tx_chains,
        iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, NL80211_BAND_2GHZ,
                             tx_chains, rx_chains);
  
 +      if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
 +              iwl_init_he_hw_capab(sband, tx_chains, rx_chains);
 +
        sband = &data->bands[NL80211_BAND_5GHZ];
        sband->band = NL80211_BAND_5GHZ;
        sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
                iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap,
                                      tx_chains, rx_chains);
  
 +      if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
 +              iwl_init_he_hw_capab(sband, tx_chains, rx_chains);
 +
        if (n_channels != n_used)
                IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
                            n_used, n_channels);
@@@ -985,15 -877,12 +985,12 @@@ iwl_parse_nvm_mcc_info(struct device *d
        const u8 *nvm_chan = cfg->nvm_type == IWL_NVM_EXT ?
                             iwl_ext_nvm_channels : iwl_nvm_channels;
        struct ieee80211_regdomain *regd, *copy_rd;
-       int size_of_regd, regd_to_copy, wmms_to_copy;
-       int size_of_wmms = 0;
+       int size_of_regd, regd_to_copy;
        struct ieee80211_reg_rule *rule;
-       struct ieee80211_wmm_rule *wmm_rule, *d_wmm, *s_wmm;
        struct regdb_ptrs *regdb_ptrs;
        enum nl80211_band band;
        int center_freq, prev_center_freq = 0;
-       int valid_rules = 0, n_wmms = 0;
-       int i;
+       int valid_rules = 0;
        bool new_rule;
        int max_num_ch = cfg->nvm_type == IWL_NVM_EXT ?
                         IWL_NVM_NUM_CHANNELS_EXT : IWL_NVM_NUM_CHANNELS;
                sizeof(struct ieee80211_regdomain) +
                num_of_ch * sizeof(struct ieee80211_reg_rule);
  
-       if (geo_info & GEO_WMM_ETSI_5GHZ_INFO)
-               size_of_wmms =
-                       num_of_ch * sizeof(struct ieee80211_wmm_rule);
-       regd = kzalloc(size_of_regd + size_of_wmms, GFP_KERNEL);
+       regd = kzalloc(size_of_regd, GFP_KERNEL);
        if (!regd)
                return ERR_PTR(-ENOMEM);
  
        regd->alpha2[0] = fw_mcc >> 8;
        regd->alpha2[1] = fw_mcc & 0xff;
  
-       wmm_rule = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd);
        for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
                ch_flags = (u16)__le32_to_cpup(channels + ch_idx);
                band = (ch_idx < NUM_2GHZ_CHANNELS) ?
                    band == NL80211_BAND_2GHZ)
                        continue;
  
-               if (!reg_query_regdb_wmm(regd->alpha2, center_freq,
-                                        &regdb_ptrs[n_wmms].token, wmm_rule)) {
-                       /* Add only new rules */
-                       for (i = 0; i < n_wmms; i++) {
-                               if (regdb_ptrs[i].token ==
-                                   regdb_ptrs[n_wmms].token) {
-                                       rule->wmm_rule = regdb_ptrs[i].rule;
-                                       break;
-                               }
-                       }
-                       if (i == n_wmms) {
-                               rule->wmm_rule = wmm_rule;
-                               regdb_ptrs[n_wmms++].rule = wmm_rule;
-                               wmm_rule++;
-                       }
-               }
+               reg_query_regdb_wmm(regd->alpha2, center_freq, rule);
        }
  
        regd->n_reg_rules = valid_rules;
-       regd->n_wmm_rules = n_wmms;
  
        /*
         * Narrow down regdom for unused regulatory rules to prevent hole
        regd_to_copy = sizeof(struct ieee80211_regdomain) +
                valid_rules * sizeof(struct ieee80211_reg_rule);
  
-       wmms_to_copy = sizeof(struct ieee80211_wmm_rule) * n_wmms;
-       copy_rd = kzalloc(regd_to_copy + wmms_to_copy, GFP_KERNEL);
+       copy_rd = kzalloc(regd_to_copy, GFP_KERNEL);
        if (!copy_rd) {
                copy_rd = ERR_PTR(-ENOMEM);
                goto out;
        }
  
        memcpy(copy_rd, regd, regd_to_copy);
-       memcpy((u8 *)copy_rd + regd_to_copy, (u8 *)regd + size_of_regd,
-              wmms_to_copy);
-       d_wmm = (struct ieee80211_wmm_rule *)((u8 *)copy_rd + regd_to_copy);
-       s_wmm = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd);
-       for (i = 0; i < regd->n_reg_rules; i++) {
-               if (!regd->reg_rules[i].wmm_rule)
-                       continue;
-               copy_rd->reg_rules[i].wmm_rule = d_wmm +
-                       (regd->reg_rules[i].wmm_rule - s_wmm);
-       }
  
  out:
        kfree(regdb_ptrs);
@@@ -1401,8 -1253,6 +1361,8 @@@ struct iwl_nvm_data *iwl_get_nvm(struc
                !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AC_ENABLED);
        nvm->sku_cap_11n_enable =
                !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11N_ENABLED);
 +      nvm->sku_cap_11ax_enable =
 +              !!(mac_flags & NVM_MAC_SKU_FLAGS_802_11AX_ENABLED);
        nvm->sku_cap_band_24ghz_enable =
                !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_2_4_ENABLED);
        nvm->sku_cap_band_52ghz_enable =
index 998dfac0fcff359d3727fb5df313e257484378b6,80e2c8595c7c8d63da25cd245357d15acce4635b..1068757ec42e4784942e69c00161ae4e1ee16548
@@@ -2,7 -2,6 +2,7 @@@
   * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
   * Copyright (c) 2008, Jouni Malinen <[email protected]>
   * Copyright (c) 2011, Javier Lopez <[email protected]>
 + * Copyright (c) 2016 - 2017 Intel Deutschland GmbH
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
@@@ -34,6 -33,7 +34,7 @@@
  #include <net/net_namespace.h>
  #include <net/netns/generic.h>
  #include <linux/rhashtable.h>
+ #include <linux/nospec.h>
  #include "mac80211_hwsim.h"
  
  #define WARN_QUEUE 100
@@@ -2518,123 -2518,6 +2519,123 @@@ out_err
        nlmsg_free(mcast_skb);
  }
  
 +static const struct ieee80211_sband_iftype_data he_capa_2ghz = {
 +      /* TODO: should we support other types, e.g., P2P?*/
 +      .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP),
 +      .he_cap = {
 +              .has_he = true,
 +              .he_cap_elem = {
 +                      .mac_cap_info[0] =
 +                              IEEE80211_HE_MAC_CAP0_HTC_HE,
 +                      .mac_cap_info[1] =
 +                              IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
 +                              IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_QOS_8,
 +                      .mac_cap_info[2] =
 +                              IEEE80211_HE_MAC_CAP2_BSR |
 +                              IEEE80211_HE_MAC_CAP2_MU_CASCADING |
 +                              IEEE80211_HE_MAC_CAP2_ACK_EN,
 +                      .mac_cap_info[3] =
 +                              IEEE80211_HE_MAC_CAP3_GRP_ADDR_MULTI_STA_BA_DL_MU |
 +                              IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
 +                              IEEE80211_HE_MAC_CAP3_MAX_A_AMPDU_LEN_EXP_VHT_2,
 +                      .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU,
 +                      .phy_cap_info[0] =
 +                              IEEE80211_HE_PHY_CAP0_DUAL_BAND,
 +                      .phy_cap_info[1] =
 +                              IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
 +                              IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
 +                              IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
 +                              IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_MAX_NSTS,
 +                      .phy_cap_info[2] =
 +                              IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
 +                              IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
 +                              IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
 +                              IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
 +                              IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
 +
 +                      /* Leave all the other PHY capability bytes unset, as
 +                       * DCM, beam forming, RU and PPE threshold information
 +                       * are not supported
 +                       */
 +              },
 +              .he_mcs_nss_supp = {
 +                      .rx_mcs_80 = cpu_to_le16(0xfffa),
 +                      .tx_mcs_80 = cpu_to_le16(0xfffa),
 +                      .rx_mcs_160 = cpu_to_le16(0xffff),
 +                      .tx_mcs_160 = cpu_to_le16(0xffff),
 +                      .rx_mcs_80p80 = cpu_to_le16(0xffff),
 +                      .tx_mcs_80p80 = cpu_to_le16(0xffff),
 +              },
 +      },
 +};
 +
 +static const struct ieee80211_sband_iftype_data he_capa_5ghz = {
 +      /* TODO: should we support other types, e.g., P2P?*/
 +      .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP),
 +      .he_cap = {
 +              .has_he = true,
 +              .he_cap_elem = {
 +                      .mac_cap_info[0] =
 +                              IEEE80211_HE_MAC_CAP0_HTC_HE,
 +                      .mac_cap_info[1] =
 +                              IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
 +                              IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_QOS_8,
 +                      .mac_cap_info[2] =
 +                              IEEE80211_HE_MAC_CAP2_BSR |
 +                              IEEE80211_HE_MAC_CAP2_MU_CASCADING |
 +                              IEEE80211_HE_MAC_CAP2_ACK_EN,
 +                      .mac_cap_info[3] =
 +                              IEEE80211_HE_MAC_CAP3_GRP_ADDR_MULTI_STA_BA_DL_MU |
 +                              IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
 +                              IEEE80211_HE_MAC_CAP3_MAX_A_AMPDU_LEN_EXP_VHT_2,
 +                      .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU,
 +                      .phy_cap_info[0] =
 +                              IEEE80211_HE_PHY_CAP0_DUAL_BAND |
 +                              IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
 +                              IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
 +                              IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G,
 +                      .phy_cap_info[1] =
 +                              IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
 +                              IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
 +                              IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
 +                              IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_MAX_NSTS,
 +                      .phy_cap_info[2] =
 +                              IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
 +                              IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
 +                              IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
 +                              IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
 +                              IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
 +
 +                      /* Leave all the other PHY capability bytes unset, as
 +                       * DCM, beam forming, RU and PPE threshold information
 +                       * are not supported
 +                       */
 +              },
 +              .he_mcs_nss_supp = {
 +                      .rx_mcs_80 = cpu_to_le16(0xfffa),
 +                      .tx_mcs_80 = cpu_to_le16(0xfffa),
 +                      .rx_mcs_160 = cpu_to_le16(0xfffa),
 +                      .tx_mcs_160 = cpu_to_le16(0xfffa),
 +                      .rx_mcs_80p80 = cpu_to_le16(0xfffa),
 +                      .tx_mcs_80p80 = cpu_to_le16(0xfffa),
 +              },
 +      },
 +};
 +
 +static void mac80211_hswim_he_capab(struct ieee80211_supported_band *sband)
 +{
 +      if (sband->band == NL80211_BAND_2GHZ)
 +              sband->iftype_data =
 +                      (struct ieee80211_sband_iftype_data *)&he_capa_2ghz;
 +      else if (sband->band == NL80211_BAND_5GHZ)
 +              sband->iftype_data =
 +                      (struct ieee80211_sband_iftype_data *)&he_capa_5ghz;
 +      else
 +              return;
 +
 +      sband->n_iftype_data = 1;
 +}
 +
  static int mac80211_hwsim_new_radio(struct genl_info *info,
                                    struct hwsim_new_radio_params *param)
  {
  
        for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
                struct ieee80211_supported_band *sband = &data->bands[band];
 +
 +              sband->band = band;
 +
                switch (band) {
                case NL80211_BAND_2GHZ:
                        sband->channels = data->channels_2ghz;
                                IEEE80211_VHT_CAP_SHORT_GI_80 |
                                IEEE80211_VHT_CAP_SHORT_GI_160 |
                                IEEE80211_VHT_CAP_TXSTBC |
-                               IEEE80211_VHT_CAP_RXSTBC_1 |
-                               IEEE80211_VHT_CAP_RXSTBC_2 |
-                               IEEE80211_VHT_CAP_RXSTBC_3 |
                                IEEE80211_VHT_CAP_RXSTBC_4 |
                                IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
                        sband->vht_cap.vht_mcs.rx_mcs_map =
                sband->ht_cap.mcs.rx_mask[1] = 0xff;
                sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
  
 +              mac80211_hswim_he_capab(sband);
 +
                hw->wiphy->bands[band] = sband;
        }
  
@@@ -3317,6 -3192,11 +3315,11 @@@ static int hwsim_new_radio_nl(struct sk
        if (info->attrs[HWSIM_ATTR_CHANNELS])
                param.channels = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
  
+       if (param.channels < 1) {
+               GENL_SET_ERR_MSG(info, "must have at least one channel");
+               return -EINVAL;
+       }
        if (param.channels > CFG80211_MAX_NUM_DIFFERENT_CHANNELS) {
                GENL_SET_ERR_MSG(info, "too many channels specified");
                return -EINVAL;
                        kfree(hwname);
                        return -EINVAL;
                }
+               idx = array_index_nospec(idx,
+                                        ARRAY_SIZE(hwsim_world_regdom_custom));
                param.regd = hwsim_world_regdom_custom[idx];
        }
  
diff --combined include/net/cfg80211.h
index 9a850973e09a739aaa72d5be951436640bc9c131,7229c186d199c1b426b0b19db724a78a42eda5d9..8ebabc9873d1593b46161697b53c8a8d14242000
@@@ -285,41 -285,6 +285,41 @@@ struct ieee80211_sta_vht_cap 
        struct ieee80211_vht_mcs_info vht_mcs;
  };
  
 +#define IEEE80211_HE_PPE_THRES_MAX_LEN                25
 +
 +/**
 + * struct ieee80211_sta_he_cap - STA's HE capabilities
 + *
 + * This structure describes most essential parameters needed
 + * to describe 802.11ax HE capabilities for a STA.
 + *
 + * @has_he: true iff HE data is valid.
 + * @he_cap_elem: Fixed portion of the HE capabilities element.
 + * @he_mcs_nss_supp: The supported NSS/MCS combinations.
 + * @ppe_thres: Holds the PPE Thresholds data.
 + */
 +struct ieee80211_sta_he_cap {
 +      bool has_he;
 +      struct ieee80211_he_cap_elem he_cap_elem;
 +      struct ieee80211_he_mcs_nss_supp he_mcs_nss_supp;
 +      u8 ppe_thres[IEEE80211_HE_PPE_THRES_MAX_LEN];
 +};
 +
 +/**
 + * struct ieee80211_sband_iftype_data
 + *
 + * This structure encapsulates sband data that is relevant for the
 + * interface types defined in @types_mask.  Each type in the
 + * @types_mask must be unique across all instances of iftype_data.
 + *
 + * @types_mask: interface types mask
 + * @he_cap: holds the HE capabilities
 + */
 +struct ieee80211_sband_iftype_data {
 +      u16 types_mask;
 +      struct ieee80211_sta_he_cap he_cap;
 +};
 +
  /**
   * struct ieee80211_supported_band - frequency band definition
   *
   * @n_bitrates: Number of bitrates in @bitrates
   * @ht_cap: HT capabilities in this band
   * @vht_cap: VHT capabilities in this band
 + * @n_iftype_data: number of iftype data entries
 + * @iftype_data: interface type data entries.  Note that the bits in
 + *    @types_mask inside this structure cannot overlap (i.e. only
 + *    one occurrence of each type is allowed across all instances of
 + *    iftype_data).
   */
  struct ieee80211_supported_band {
        struct ieee80211_channel *channels;
        int n_bitrates;
        struct ieee80211_sta_ht_cap ht_cap;
        struct ieee80211_sta_vht_cap vht_cap;
 +      u16 n_iftype_data;
 +      const struct ieee80211_sband_iftype_data *iftype_data;
  };
  
 +/**
 + * ieee80211_get_sband_iftype_data - return sband data for a given iftype
 + * @sband: the sband to search for the STA on
 + * @iftype: enum nl80211_iftype
 + *
 + * Return: pointer to struct ieee80211_sband_iftype_data, or NULL is none found
 + */
 +static inline const struct ieee80211_sband_iftype_data *
 +ieee80211_get_sband_iftype_data(const struct ieee80211_supported_band *sband,
 +                              u8 iftype)
 +{
 +      int i;
 +
 +      if (WARN_ON(iftype >= NL80211_IFTYPE_MAX))
 +              return NULL;
 +
 +      for (i = 0; i < sband->n_iftype_data; i++)  {
 +              const struct ieee80211_sband_iftype_data *data =
 +                      &sband->iftype_data[i];
 +
 +              if (data->types_mask & BIT(iftype))
 +                      return data;
 +      }
 +
 +      return NULL;
 +}
 +
 +/**
 + * ieee80211_get_he_sta_cap - return HE capabilities for an sband's STA
 + * @sband: the sband to search for the STA on
 + *
 + * Return: pointer to the struct ieee80211_sta_he_cap, or NULL is none found
 + */
 +static inline const struct ieee80211_sta_he_cap *
 +ieee80211_get_he_sta_cap(const struct ieee80211_supported_band *sband)
 +{
 +      const struct ieee80211_sband_iftype_data *data =
 +              ieee80211_get_sband_iftype_data(sband, NL80211_IFTYPE_STATION);
 +
 +      if (data && data->he_cap.has_he)
 +              return &data->he_cap;
 +
 +      return NULL;
 +}
 +
  /**
   * wiphy_read_of_freq_limits - read frequency limits from device tree
   *
@@@ -986,8 -899,6 +986,8 @@@ enum station_parameters_apply_mask 
   * @opmode_notif: operating mode field from Operating Mode Notification
   * @opmode_notif_used: information if operating mode field is used
   * @support_p2p_ps: information if station supports P2P PS mechanism
 + * @he_capa: HE capabilities of station
 + * @he_capa_len: the length of the HE capabilities
   */
  struct station_parameters {
        const u8 *supported_rates;
        u8 opmode_notif;
        bool opmode_notif_used;
        int support_p2p_ps;
 +      const struct ieee80211_he_cap_elem *he_capa;
 +      u8 he_capa_len;
  };
  
  /**
@@@ -1091,14 -1000,12 +1091,14 @@@ int cfg80211_check_station_change(struc
   * @RATE_INFO_FLAGS_VHT_MCS: mcs field filled with VHT MCS
   * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
   * @RATE_INFO_FLAGS_60G: 60GHz MCS
 + * @RATE_INFO_FLAGS_HE_MCS: HE MCS information
   */
  enum rate_info_flags {
        RATE_INFO_FLAGS_MCS                     = BIT(0),
        RATE_INFO_FLAGS_VHT_MCS                 = BIT(1),
        RATE_INFO_FLAGS_SHORT_GI                = BIT(2),
        RATE_INFO_FLAGS_60G                     = BIT(3),
 +      RATE_INFO_FLAGS_HE_MCS                  = BIT(4),
  };
  
  /**
   * @RATE_INFO_BW_40: 40 MHz bandwidth
   * @RATE_INFO_BW_80: 80 MHz bandwidth
   * @RATE_INFO_BW_160: 160 MHz bandwidth
 + * @RATE_INFO_BW_HE_RU: bandwidth determined by HE RU allocation
   */
  enum rate_info_bw {
        RATE_INFO_BW_20 = 0,
        RATE_INFO_BW_40,
        RATE_INFO_BW_80,
        RATE_INFO_BW_160,
 +      RATE_INFO_BW_HE_RU,
  };
  
  /**
   * Information about a receiving or transmitting bitrate
   *
   * @flags: bitflag of flags from &enum rate_info_flags
 - * @mcs: mcs index if struct describes a 802.11n bitrate
 + * @mcs: mcs index if struct describes an HT/VHT/HE rate
   * @legacy: bitrate in 100kbit/s for 802.11abg
 - * @nss: number of streams (VHT only)
 + * @nss: number of streams (VHT & HE only)
   * @bw: bandwidth (from &enum rate_info_bw)
 + * @he_gi: HE guard interval (from &enum nl80211_he_gi)
 + * @he_dcm: HE DCM value
 + * @he_ru_alloc: HE RU allocation (from &enum nl80211_he_ru_alloc,
 + *    only valid if bw is %RATE_INFO_BW_HE_RU)
   */
  struct rate_info {
        u8 flags;
        u16 legacy;
        u8 nss;
        u8 bw;
 +      u8 he_gi;
 +      u8 he_dcm;
 +      u8 he_ru_alloc;
  };
  
  /**
@@@ -4865,8 -4763,8 +4865,8 @@@ const char *reg_initiator_name(enum nl8
   *
   * Return: 0 on success. -ENODATA.
   */
- int reg_query_regdb_wmm(char *alpha2, int freq, u32 *ptr,
-                       struct ieee80211_wmm_rule *rule);
+ int reg_query_regdb_wmm(char *alpha2, int freq,
+                       struct ieee80211_reg_rule *rule);
  
  /*
   * callbacks for asynchronous cfg80211 methods, notification
diff --combined net/mac80211/main.c
index 4fb2709cb52796c752f052a746bd5c420d6caf08,66cbddd65b47f8179a9c4e1e2001a957c66a53af..513627896204938485ef01f0f2d76606d4418af4
@@@ -3,7 -3,6 +3,7 @@@
   * Copyright 2005-2006, Devicescape Software, Inc.
   * Copyright 2006-2007        Jiri Benc <[email protected]>
   * Copyright 2013-2014  Intel Mobile Communications GmbH
 + * Copyright (C) 2017     Intel Deutschland GmbH
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
@@@ -256,8 -255,27 +256,27 @@@ static void ieee80211_restart_work(stru
  
        flush_work(&local->radar_detected_work);
        rtnl_lock();
-       list_for_each_entry(sdata, &local->interfaces, list)
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               /*
+                * XXX: there may be more work for other vif types and even
+                * for station mode: a good thing would be to run most of
+                * the iface type's dependent _stop (ieee80211_mg_stop,
+                * ieee80211_ibss_stop) etc...
+                * For now, fix only the specific bug that was seen: race
+                * between csa_connection_drop_work and us.
+                */
+               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       /*
+                        * This worker is scheduled from the iface worker that
+                        * runs on mac80211's workqueue, so we can't be
+                        * scheduling this worker after the cancel right here.
+                        * The exception is ieee80211_chswitch_done.
+                        * Then we can have a race...
+                        */
+                       cancel_work_sync(&sdata->u.mgd.csa_connection_drop_work);
+               }
                flush_delayed_work(&sdata->dec_tailroom_needed_wk);
+       }
        ieee80211_scan_cancel(local);
  
        /* make sure any new ROC will consider local->in_reconfig */
@@@ -471,10 -489,7 +490,7 @@@ static const struct ieee80211_vht_cap m
                cpu_to_le32(IEEE80211_VHT_CAP_RXLDPC |
                            IEEE80211_VHT_CAP_SHORT_GI_80 |
                            IEEE80211_VHT_CAP_SHORT_GI_160 |
-                           IEEE80211_VHT_CAP_RXSTBC_1 |
-                           IEEE80211_VHT_CAP_RXSTBC_2 |
-                           IEEE80211_VHT_CAP_RXSTBC_3 |
-                           IEEE80211_VHT_CAP_RXSTBC_4 |
+                           IEEE80211_VHT_CAP_RXSTBC_MASK |
                            IEEE80211_VHT_CAP_TXSTBC |
                            IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
                            IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
@@@ -558,19 -573,10 +574,19 @@@ struct ieee80211_hw *ieee80211_alloc_hw
        wiphy_ext_feature_set(wiphy,
                              NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211);
  
 -      if (!ops->hw_scan)
 +      if (!ops->hw_scan) {
                wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
                                   NL80211_FEATURE_AP_SCAN;
 -
 +              /*
 +               * if the driver behaves correctly using the probe request
 +               * (template) from mac80211, then both of these should be
 +               * supported even with hw scan - but let drivers opt in.
 +               */
 +              wiphy_ext_feature_set(wiphy,
 +                                    NL80211_EXT_FEATURE_SCAN_RANDOM_SN);
 +              wiphy_ext_feature_set(wiphy,
 +                                    NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT);
 +      }
  
        if (!ops->set_key)
                wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        local->hw.queues = 1;
        local->hw.max_rates = 1;
        local->hw.max_report_rates = 0;
 -      local->hw.max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
 -      local->hw.max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
 +      local->hw.max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HT;
 +      local->hw.max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HT;
        local->hw.offchannel_tx_hw_queue = IEEE80211_INVAL_HW_QUEUE;
        local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
        local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
@@@ -826,7 -832,7 +842,7 @@@ int ieee80211_register_hw(struct ieee80
        int result, i;
        enum nl80211_band band;
        int channels, max_bitrates;
 -      bool supp_ht, supp_vht;
 +      bool supp_ht, supp_vht, supp_he;
        netdev_features_t feature_whitelist;
        struct cfg80211_chan_def dflt_chandef = {};
  
        max_bitrates = 0;
        supp_ht = false;
        supp_vht = false;
 +      supp_he = false;
        for (band = 0; band < NUM_NL80211_BANDS; band++) {
                struct ieee80211_supported_band *sband;
  
                supp_ht = supp_ht || sband->ht_cap.ht_supported;
                supp_vht = supp_vht || sband->vht_cap.vht_supported;
  
 +              if (!supp_he)
 +                      supp_he = !!ieee80211_get_he_sta_cap(sband);
 +
                if (!sband->ht_cap.ht_supported)
                        continue;
  
                local->scan_ies_len +=
                        2 + sizeof(struct ieee80211_vht_cap);
  
 +      /* HE cap element is variable in size - set len to allow max size */
 +      /*
 +       * TODO: 1 is added at the end of the calculation to accommodate for
 +       *      the temporary placing of the HE capabilities IE under EXT.
 +       *      Remove it once it is placed in the final place.
 +       */
 +      if (supp_he)
 +              local->scan_ies_len +=
 +                      2 + sizeof(struct ieee80211_he_cap_elem) +
 +                      sizeof(struct ieee80211_he_mcs_nss_supp) +
 +                      IEEE80211_HE_PPE_THRES_MAX_LEN + 1;
 +
        if (!local->ops->hw_scan) {
                /* For hw_scan, driver needs to set these up. */
                local->hw.wiphy->max_scan_ssids = 4;
@@@ -1208,6 -1198,7 +1224,7 @@@ void ieee80211_unregister_hw(struct iee
  #if IS_ENABLED(CONFIG_IPV6)
        unregister_inet6addr_notifier(&local->ifa6_notifier);
  #endif
+       ieee80211_txq_teardown_flows(local);
  
        rtnl_lock();
  
        skb_queue_purge(&local->skb_queue);
        skb_queue_purge(&local->skb_queue_unreliable);
        skb_queue_purge(&local->skb_queue_tdls_chsw);
-       ieee80211_txq_teardown_flows(local);
  
        destroy_workqueue(local->workqueue);
        wiphy_unregister(local->hw.wiphy);
diff --combined net/mac80211/mlme.c
index 7fb9957359a3c1be557e577ba5b76cc4c1177105,b046bf95eb3c60fc58165f8cd76101dec294993a..3dbecae4be73cb1aae05752807b717a329b80980
@@@ -149,7 -149,6 +149,7 @@@ ieee80211_determine_chantype(struct iee
                             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;
        }
  
        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");
@@@ -321,14 -300,12 +321,14 @@@ static int ieee80211_config_bw(struct i
                               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;
        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;
  
                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);
  
        /*
@@@ -607,34 -582,6 +607,34 @@@ static void ieee80211_add_vht_ie(struc
        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;
                        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 */
                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,
@@@ -984,11 -898,6 +984,11 @@@ void ieee80211_send_nullfunc(struct iee
        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)
@@@ -1020,10 -929,6 +1020,10 @@@ static void ieee80211_send_4addr_nullfu
        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;
@@@ -1073,6 -978,10 +1073,10 @@@ static void ieee80211_chswitch_work(str
         */
  
        if (sdata->reserved_chanctx) {
+               struct ieee80211_supported_band *sband = NULL;
+               struct sta_info *mgd_sta = NULL;
+               enum ieee80211_sta_rx_bandwidth bw = IEEE80211_STA_RX_BW_20;
                /*
                 * with multi-vif csa driver may call ieee80211_csa_finish()
                 * many times while waiting for other interfaces to use their
                if (sdata->reserved_ready)
                        goto out;
  
+               if (sdata->vif.bss_conf.chandef.width !=
+                   sdata->csa_chandef.width) {
+                       /*
+                        * For managed interface, we need to also update the AP
+                        * station bandwidth and align the rate scale algorithm
+                        * on the bandwidth change. Here we only consider the
+                        * bandwidth of the new channel definition (as channel
+                        * switch flow does not have the full HT/VHT/HE
+                        * information), assuming that if additional changes are
+                        * required they would be done as part of the processing
+                        * of the next beacon from the AP.
+                        */
+                       switch (sdata->csa_chandef.width) {
+                       case NL80211_CHAN_WIDTH_20_NOHT:
+                       case NL80211_CHAN_WIDTH_20:
+                       default:
+                               bw = IEEE80211_STA_RX_BW_20;
+                               break;
+                       case NL80211_CHAN_WIDTH_40:
+                               bw = IEEE80211_STA_RX_BW_40;
+                               break;
+                       case NL80211_CHAN_WIDTH_80:
+                               bw = IEEE80211_STA_RX_BW_80;
+                               break;
+                       case NL80211_CHAN_WIDTH_80P80:
+                       case NL80211_CHAN_WIDTH_160:
+                               bw = IEEE80211_STA_RX_BW_160;
+                               break;
+                       }
+                       mgd_sta = sta_info_get(sdata, ifmgd->bssid);
+                       sband =
+                               local->hw.wiphy->bands[sdata->csa_chandef.chan->band];
+               }
+               if (sdata->vif.bss_conf.chandef.width >
+                   sdata->csa_chandef.width) {
+                       mgd_sta->sta.bandwidth = bw;
+                       rate_control_rate_update(local, sband, mgd_sta,
+                                                IEEE80211_RC_BW_CHANGED);
+               }
                ret = ieee80211_vif_use_reserved_context(sdata);
                if (ret) {
                        sdata_info(sdata,
                        goto out;
                }
  
+               if (sdata->vif.bss_conf.chandef.width <
+                   sdata->csa_chandef.width) {
+                       mgd_sta->sta.bandwidth = bw;
+                       rate_control_rate_update(local, sband, mgd_sta,
+                                                IEEE80211_RC_BW_CHANGED);
+               }
                goto out;
        }
  
@@@ -1312,6 -1270,16 +1365,16 @@@ ieee80211_sta_process_chanswitch(struc
                                         cbss->beacon_interval));
        return;
   drop_connection:
+       /*
+        * This is just so that the disconnect flow will know that
+        * we were trying to switch channel and failed. In case the
+        * mode is 1 (we are not allowed to Tx), we will know not to
+        * send a deauthentication frame. Those two fields will be
+        * reset when the disconnection worker runs.
+        */
+       sdata->vif.csa_active = true;
+       sdata->csa_block_tx = csa_ie.mode;
        ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work);
        mutex_unlock(&local->chanctx_mtx);
        mutex_unlock(&local->mtx);
@@@ -1795,11 -1763,9 +1858,11 @@@ static void ieee80211_sta_handle_tspec_
  }
  
  /* 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;
                                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;
                                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;
                                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:
                                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;
                }
  
@@@ -2328,20 -2282,6 +2391,20 @@@ void ieee80211_sta_tx_notify(struct iee
                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;
                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();
        }
  
@@@ -2492,7 -2433,7 +2555,7 @@@ struct sk_buff *ieee80211_ap_probereq_g
        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;
@@@ -2522,6 -2463,7 +2585,7 @@@ static void __ieee80211_disconnect(stru
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+       bool tx;
  
        sdata_lock(sdata);
        if (!ifmgd->associated) {
                return;
        }
  
+       tx = !sdata->csa_block_tx;
        /* AP is probably out of range (or not reachable for another reason) so
         * remove the bss struct for that AP.
         */
  
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
-                              true, frame_buf);
+                              tx, frame_buf);
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
        ifmgd->csa_waiting_bcn = false;
        }
        mutex_unlock(&local->mtx);
  
-       ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+       ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx,
                                    WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
  
        sdata_unlock(sdata);
@@@ -3130,25 -3074,6 +3196,25 @@@ static bool ieee80211_assoc_success(str
                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,
                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
        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
@@@ -3774,8 -3656,7 +3840,8 @@@ static void ieee80211_rx_mgmt_beacon(st
  
        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;
  
        /*
  
        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",
@@@ -4452,68 -4332,6 +4518,68 @@@ static u8 ieee80211_ht_vht_rx_chains(st
        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)
  {
        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;
                }
        }
  
 +      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++) {
  
        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),
@@@ -5018,9 -4817,8 +5084,9 @@@ int ieee80211_mgd_assoc(struct ieee8021
                    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");
                }
        }
  
diff --combined net/mac80211/rx.c
index 64742f2765c4846c36d3f9304314059023215d0a,3f80a5ca4050428e52d2c8d1b48a216a382ab239..96611d5dfadb0ce37effd4adcce80454bb69f285
@@@ -175,20 -175,6 +175,20 @@@ ieee80211_rx_radiotap_hdrlen(struct iee
                len += 12;
        }
  
 +      if (status->encoding == RX_ENC_HE &&
 +          status->flag & RX_FLAG_RADIOTAP_HE) {
 +              len = ALIGN(len, 2);
 +              len += 12;
 +              BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he) != 12);
 +      }
 +
 +      if (status->encoding == RX_ENC_HE &&
 +          status->flag & RX_FLAG_RADIOTAP_HE_MU) {
 +              len = ALIGN(len, 2);
 +              len += 12;
 +              BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he_mu) != 12);
 +      }
 +
        if (status->chains) {
                /* antenna and antenna signal fields */
                len += 2 * hweight8(status->chains);
@@@ -277,19 -263,6 +277,19 @@@ ieee80211_add_rx_radiotap_header(struc
        int mpdulen, chain;
        unsigned long chains = status->chains;
        struct ieee80211_vendor_radiotap rtap = {};
 +      struct ieee80211_radiotap_he he = {};
 +      struct ieee80211_radiotap_he_mu he_mu = {};
 +
 +      if (status->flag & RX_FLAG_RADIOTAP_HE) {
 +              he = *(struct ieee80211_radiotap_he *)skb->data;
 +              skb_pull(skb, sizeof(he));
 +              WARN_ON_ONCE(status->encoding != RX_ENC_HE);
 +      }
 +
 +      if (status->flag & RX_FLAG_RADIOTAP_HE_MU) {
 +              he_mu = *(struct ieee80211_radiotap_he_mu *)skb->data;
 +              skb_pull(skb, sizeof(he_mu));
 +      }
  
        if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
                rtap = *(struct ieee80211_vendor_radiotap *)skb->data;
                *pos++ = flags;
        }
  
 +      if (status->encoding == RX_ENC_HE &&
 +          status->flag & RX_FLAG_RADIOTAP_HE) {
 +#define HE_PREP(f, val)       cpu_to_le16(FIELD_PREP(IEEE80211_RADIOTAP_HE_##f, val))
 +
 +              if (status->enc_flags & RX_ENC_FLAG_STBC_MASK) {
 +                      he.data6 |= HE_PREP(DATA6_NSTS,
 +                                          FIELD_GET(RX_ENC_FLAG_STBC_MASK,
 +                                                    status->enc_flags));
 +                      he.data3 |= HE_PREP(DATA3_STBC, 1);
 +              } else {
 +                      he.data6 |= HE_PREP(DATA6_NSTS, status->nss);
 +              }
 +
 +#define CHECK_GI(s) \
 +      BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_GI_##s != \
 +                   (int)NL80211_RATE_INFO_HE_GI_##s)
 +
 +              CHECK_GI(0_8);
 +              CHECK_GI(1_6);
 +              CHECK_GI(3_2);
 +
 +              he.data3 |= HE_PREP(DATA3_DATA_MCS, status->rate_idx);
 +              he.data3 |= HE_PREP(DATA3_DATA_DCM, status->he_dcm);
 +              he.data3 |= HE_PREP(DATA3_CODING,
 +                                  !!(status->enc_flags & RX_ENC_FLAG_LDPC));
 +
 +              he.data5 |= HE_PREP(DATA5_GI, status->he_gi);
 +
 +              switch (status->bw) {
 +              case RATE_INFO_BW_20:
 +                      he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
 +                                          IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ);
 +                      break;
 +              case RATE_INFO_BW_40:
 +                      he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
 +                                          IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ);
 +                      break;
 +              case RATE_INFO_BW_80:
 +                      he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
 +                                          IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ);
 +                      break;
 +              case RATE_INFO_BW_160:
 +                      he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
 +                                          IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ);
 +                      break;
 +              case RATE_INFO_BW_HE_RU:
 +#define CHECK_RU_ALLOC(s) \
 +      BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_##s##T != \
 +                   NL80211_RATE_INFO_HE_RU_ALLOC_##s + 4)
 +
 +                      CHECK_RU_ALLOC(26);
 +                      CHECK_RU_ALLOC(52);
 +                      CHECK_RU_ALLOC(106);
 +                      CHECK_RU_ALLOC(242);
 +                      CHECK_RU_ALLOC(484);
 +                      CHECK_RU_ALLOC(996);
 +                      CHECK_RU_ALLOC(2x996);
 +
 +                      he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
 +                                          status->he_ru + 4);
 +                      break;
 +              default:
 +                      WARN_ONCE(1, "Invalid SU BW %d\n", status->bw);
 +              }
 +
 +              /* ensure 2 byte alignment */
 +              while ((pos - (u8 *)rthdr) & 1)
 +                      pos++;
 +              rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE);
 +              memcpy(pos, &he, sizeof(he));
 +              pos += sizeof(he);
 +      }
 +
 +      if (status->encoding == RX_ENC_HE &&
 +          status->flag & RX_FLAG_RADIOTAP_HE_MU) {
 +              /* ensure 2 byte alignment */
 +              while ((pos - (u8 *)rthdr) & 1)
 +                      pos++;
 +              rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE_MU);
 +              memcpy(pos, &he_mu, sizeof(he_mu));
 +              pos += sizeof(he_mu);
 +      }
 +
        for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
                *pos++ = status->chain_signal[chain];
                *pos++ = chain;
@@@ -723,12 -613,6 +723,12 @@@ ieee80211_rx_monitor(struct ieee80211_l
                rcu_dereference(local->monitor_sdata);
        bool only_monitor = false;
  
 +      if (status->flag & RX_FLAG_RADIOTAP_HE)
 +              rtap_space += sizeof(struct ieee80211_radiotap_he);
 +
 +      if (status->flag & RX_FLAG_RADIOTAP_HE_MU)
 +              rtap_space += sizeof(struct ieee80211_radiotap_he_mu);
 +
        if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
                struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
  
@@@ -1728,6 -1612,7 +1728,7 @@@ ieee80211_rx_h_sta_process(struct ieee8
         */
        if (!ieee80211_hw_check(&sta->local->hw, AP_LINK_PS) &&
            !ieee80211_has_morefrags(hdr->frame_control) &&
+           !is_multicast_ether_addr(hdr->addr1) &&
            (ieee80211_is_mgmt(hdr->frame_control) ||
             ieee80211_is_data(hdr->frame_control)) &&
            !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) &&
@@@ -3354,7 -3239,7 +3355,7 @@@ ieee80211_rx_h_action_return(struct iee
                }
  
                __ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7,
 -                                          status->band);
 +                                          status->band, 0);
        }
        dev_kfree_skb(rx->skb);
        return RX_QUEUED;
@@@ -3499,7 -3384,8 +3500,7 @@@ static void ieee80211_rx_handlers_resul
                status = IEEE80211_SKB_RXCB((rx->skb));
  
                sband = rx->local->hw.wiphy->bands[status->band];
 -              if (!(status->encoding == RX_ENC_HT) &&
 -                  !(status->encoding == RX_ENC_VHT))
 +              if (status->encoding == RX_ENC_LEGACY)
                        rate = &sband->bitrates[status->rate_idx];
  
                ieee80211_rx_cooked_monitor(rx, rate);
@@@ -4498,14 -4384,6 +4499,14 @@@ void ieee80211_rx_napi(struct ieee80211
                                      status->rate_idx, status->nss))
                                goto drop;
                        break;
 +              case RX_ENC_HE:
 +                      if (WARN_ONCE(status->rate_idx > 11 ||
 +                                    !status->nss ||
 +                                    status->nss > 8,
 +                                    "Rate marked as an HE rate but data is invalid: MCS: %d, NSS: %d\n",
 +                                    status->rate_idx, status->nss))
 +                              goto drop;
 +                      break;
                default:
                        WARN_ON_ONCE(1);
                        /* fall through */
diff --combined net/mac80211/tx.c
index cd332e3e1134bed3efb89838ac245b0402e7a604,9b3b069e418abe25639f25e6568bc0fafee0d8d6..f353d9db54bc1f049e14b20713af88bde1da3c62
@@@ -825,8 -825,6 +825,8 @@@ ieee80211_tx_h_sequence(struct ieee8021
         */
        if (!ieee80211_is_data_qos(hdr->frame_control) ||
            is_multicast_ether_addr(hdr->addr1)) {
 +              if (tx->flags & IEEE80211_TX_NO_SEQNO)
 +                      return TX_CONTINUE;
                /* driver should assign sequence number */
                info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
                /* for pure STA mode without beacons, we can do it */
@@@ -1249,7 -1247,7 +1249,7 @@@ static struct txq_info *ieee80211_get_t
            (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
                return NULL;
  
 -      if (!ieee80211_is_data(hdr->frame_control))
 +      if (!ieee80211_is_data_present(hdr->frame_control))
                return NULL;
  
        if (sta) {
@@@ -1856,7 -1854,7 +1856,7 @@@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb)
   */
  static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
                         struct sta_info *sta, struct sk_buff *skb,
 -                       bool txpending)
 +                       bool txpending, u32 txdata_flags)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_tx_data tx;
        led_len = skb->len;
        res_prepare = ieee80211_tx_prepare(sdata, &tx, sta, skb);
  
 +      tx.flags |= txdata_flags;
 +
        if (unlikely(res_prepare == TX_DROP)) {
                ieee80211_free_txskb(&local->hw, skb);
                return true;
@@@ -1937,8 -1933,7 +1937,8 @@@ static int ieee80211_skb_resize(struct 
  }
  
  void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
 -                  struct sta_info *sta, struct sk_buff *skb)
 +                  struct sta_info *sta, struct sk_buff *skb,
 +                  u32 txdata_flags)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        }
  
        ieee80211_set_qos_hdr(sdata, skb);
 -      ieee80211_tx(sdata, sta, skb, false);
 +      ieee80211_tx(sdata, sta, skb, false, txdata_flags);
  }
  
  static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
@@@ -2294,7 -2289,7 +2294,7 @@@ netdev_tx_t ieee80211_monitor_start_xmi
        if (!ieee80211_parse_tx_radiotap(local, skb))
                goto fail_rcu;
  
 -      ieee80211_xmit(sdata, NULL, skb);
 +      ieee80211_xmit(sdata, NULL, skb, 0);
        rcu_read_unlock();
  
        return NETDEV_TX_OK;
@@@ -3078,27 -3073,18 +3078,18 @@@ void ieee80211_clear_fast_xmit(struct s
  }
  
  static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
-                                       struct sk_buff *skb, int headroom,
-                                       int *subframe_len)
+                                       struct sk_buff *skb, int headroom)
  {
-       int amsdu_len = *subframe_len + sizeof(struct ethhdr);
-       int padding = (4 - amsdu_len) & 3;
-       if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
+       if (skb_headroom(skb) < headroom) {
                I802_DEBUG_INC(local->tx_expand_skb_head);
  
-               if (pskb_expand_head(skb, headroom, padding, GFP_ATOMIC)) {
+               if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
                        wiphy_debug(local->hw.wiphy,
                                    "failed to reallocate TX buffer\n");
                        return false;
                }
        }
  
-       if (padding) {
-               *subframe_len += padding;
-               skb_put_zero(skb, padding);
-       }
        return true;
  }
  
@@@ -3122,8 -3108,7 +3113,7 @@@ static bool ieee80211_amsdu_prepare_hea
        if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
                return true;
  
-       if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr),
-                                        &subframe_len))
+       if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr)))
                return false;
  
        data = skb_push(skb, sizeof(*amsdu_hdr));
@@@ -3189,7 -3174,8 +3179,8 @@@ static bool ieee80211_amsdu_aggregate(s
        void *data;
        bool ret = false;
        unsigned int orig_len;
-       int n = 1, nfrags;
+       int n = 2, nfrags, pad = 0;
+       u16 hdrlen;
  
        if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
                return false;
        if (skb->len + head->len > max_amsdu_len)
                goto out;
  
-       if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
-               goto out;
        nfrags = 1 + skb_shinfo(skb)->nr_frags;
        nfrags += 1 + skb_shinfo(head)->nr_frags;
        frag_tail = &skb_shinfo(head)->frag_list;
        if (max_frags && nfrags > max_frags)
                goto out;
  
-       if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) + 2,
-                                        &subframe_len))
+       if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
                goto out;
  
+       /*
+        * Pad out the previous subframe to a multiple of 4 by adding the
+        * padding to the next one, that's being added. Note that head->len
+        * is the length of the full A-MSDU, but that works since each time
+        * we add a new subframe we pad out the previous one to a multiple
+        * of 4 and thus it no longer matters in the next round.
+        */
+       hdrlen = fast_tx->hdr_len - sizeof(rfc1042_header);
+       if ((head->len - hdrlen) & 3)
+               pad = 4 - ((head->len - hdrlen) & 3);
+       if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) +
+                                                    2 + pad))
+               goto out_recalc;
        ret = true;
        data = skb_push(skb, ETH_ALEN + 2);
        memmove(data, data + ETH_ALEN + 2, 2 * ETH_ALEN);
        memcpy(data, &len, 2);
        memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
  
+       memset(skb_push(skb, pad), 0, pad);
        head->len += skb->len;
        head->data_len += skb->len;
        *frag_tail = skb;
  
-       flow->backlog += head->len - orig_len;
-       tin->backlog_bytes += head->len - orig_len;
-       fq_recalc_backlog(fq, tin, flow);
+ out_recalc:
+       if (head->len != orig_len) {
+               flow->backlog += head->len - orig_len;
+               tin->backlog_bytes += head->len - orig_len;
  
+               fq_recalc_backlog(fq, tin, flow);
+       }
  out:
        spin_unlock_bh(&fq->lock);
  
@@@ -3653,7 -3654,7 +3659,7 @@@ void __ieee80211_subif_start_xmit(struc
  
                ieee80211_tx_stats(dev, skb->len);
  
 -              ieee80211_xmit(sdata, sta, skb);
 +              ieee80211_xmit(sdata, sta, skb, 0);
        }
        goto out;
   out_free:
@@@ -3872,7 -3873,7 +3878,7 @@@ static bool ieee80211_tx_pending_skb(st
                        return true;
                }
                info->band = chanctx_conf->def.chan->band;
 -              result = ieee80211_tx(sdata, NULL, skb, true);
 +              result = ieee80211_tx(sdata, NULL, skb, true, 0);
        } else {
                struct sk_buff_head skbs;
  
@@@ -4788,7 -4789,7 +4794,7 @@@ EXPORT_SYMBOL(ieee80211_unreserve_tid)
  
  void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
                                 struct sk_buff *skb, int tid,
 -                               enum nl80211_band band)
 +                               enum nl80211_band band, u32 txdata_flags)
  {
        int ac = ieee80211_ac_from_tid(tid);
  
         */
        local_bh_disable();
        IEEE80211_SKB_CB(skb)->band = band;
 -      ieee80211_xmit(sdata, NULL, skb);
 +      ieee80211_xmit(sdata, NULL, skb, txdata_flags);
        local_bh_enable();
  }
  
diff --combined net/mac80211/util.c
index 88efda7c9f8a78737538a355b1b499104ab55aea,93b5bb849ad7174181d64bbadf938d1fd93aa4c3..716cd6442d86c85b6507ba1aaa8e28e56f58e65b
@@@ -1095,21 -1095,6 +1095,21 @@@ u32 ieee802_11_parse_elems_crc(const u
                        if (elen >= sizeof(*elems->max_idle_period_ie))
                                elems->max_idle_period_ie = (void *)pos;
                        break;
 +              case WLAN_EID_EXTENSION:
 +                      if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA &&
 +                          elen >= (sizeof(*elems->mu_edca_param_set) + 1)) {
 +                              elems->mu_edca_param_set = (void *)&pos[1];
 +                      } else if (pos[0] == WLAN_EID_EXT_HE_CAPABILITY) {
 +                              elems->he_cap = (void *)&pos[1];
 +                              elems->he_cap_len = elen - 1;
 +                      } else if (pos[0] == WLAN_EID_EXT_HE_OPERATION &&
 +                                 elen >= sizeof(*elems->he_operation) &&
 +                                 elen >= ieee80211_he_oper_size(&pos[1])) {
 +                              elems->he_operation = (void *)&pos[1];
 +                      } else if (pos[0] == WLAN_EID_EXT_UORA && elen >= 1) {
 +                              elems->uora_element = (void *)&pos[1];
 +                      }
 +                      break;
                default:
                        break;
                }
@@@ -1135,7 -1120,7 +1135,7 @@@ void ieee80211_regulatory_limit_wmm_par
  {
        struct ieee80211_chanctx_conf *chanctx_conf;
        const struct ieee80211_reg_rule *rrule;
-       struct ieee80211_wmm_ac *wmm_ac;
+       const struct ieee80211_wmm_ac *wmm_ac;
        u16 center_freq = 0;
  
        if (sdata->vif.type != NL80211_IFTYPE_AP &&
  
        rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq));
  
-       if (IS_ERR_OR_NULL(rrule) || !rrule->wmm_rule) {
+       if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) {
                rcu_read_unlock();
                return;
        }
  
        if (sdata->vif.type == NL80211_IFTYPE_AP)
-               wmm_ac = &rrule->wmm_rule->ap[ac];
+               wmm_ac = &rrule->wmm_rule.ap[ac];
        else
-               wmm_ac = &rrule->wmm_rule->client[ac];
+               wmm_ac = &rrule->wmm_rule.client[ac];
        qparam->cw_min = max_t(u16, qparam->cw_min, wmm_ac->cw_min);
        qparam->cw_max = max_t(u16, qparam->cw_max, wmm_ac->cw_max);
        qparam->aifs = max_t(u8, qparam->aifs, wmm_ac->aifsn);
-       qparam->txop = !qparam->txop ? wmm_ac->cot / 32 :
-               min_t(u16, qparam->txop, wmm_ac->cot / 32);
+       qparam->txop = min_t(u16, qparam->txop, wmm_ac->cot / 32);
        rcu_read_unlock();
  }
  
@@@ -1368,10 -1352,9 +1367,10 @@@ static int ieee80211_build_preq_ies_ban
                                         enum nl80211_band band,
                                         u32 rate_mask,
                                         struct cfg80211_chan_def *chandef,
 -                                       size_t *offset)
 +                                       size_t *offset, u32 flags)
  {
        struct ieee80211_supported_band *sband;
 +      const struct ieee80211_sta_he_cap *he_cap;
        u8 *pos = buffer, *end = buffer + buffer_len;
        size_t noffset;
        int supp_rates_len, i;
                                chandef->chan->center_freq);
        }
  
 +      if (flags & IEEE80211_PROBE_FLAG_MIN_CONTENT)
 +              goto done;
 +
        /* insert custom IEs that go before HT */
        if (ie && ie_len) {
                static const u8 before_ht[] = {
                                                sband->ht_cap.cap);
        }
  
 -      /*
 -       * If adding more here, adjust code in main.c
 -       * that calculates local->scan_ies_len.
 -       */
 -
        /* insert custom IEs that go before VHT */
        if (ie && ie_len) {
                static const u8 before_vht[] = {
                                                 sband->vht_cap.cap);
        }
  
 +      /* insert custom IEs that go before HE */
 +      if (ie && ie_len) {
 +              static const u8 before_he[] = {
 +                      /*
 +                       * no need to list the ones split off before VHT
 +                       * or generated here
 +                       */
 +                      WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS,
 +                      WLAN_EID_AP_CSN,
 +                      /* TODO: add 11ah/11aj/11ak elements */
 +              };
 +              noffset = ieee80211_ie_split(ie, ie_len,
 +                                           before_he, ARRAY_SIZE(before_he),
 +                                           *offset);
 +              if (end - pos < noffset - *offset)
 +                      goto out_err;
 +              memcpy(pos, ie + *offset, noffset - *offset);
 +              pos += noffset - *offset;
 +              *offset = noffset;
 +      }
 +
 +      he_cap = ieee80211_get_he_sta_cap(sband);
 +      if (he_cap) {
 +              pos = ieee80211_ie_build_he_cap(pos, he_cap, end);
 +              if (!pos)
 +                      goto out_err;
 +      }
 +
 +      /*
 +       * If adding more here, adjust code in main.c
 +       * that calculates local->scan_ies_len.
 +       */
 +
        return pos - buffer;
   out_err:
        WARN_ONCE(1, "not enough space for preq IEs\n");
 + done:
        return pos - buffer;
  }
  
@@@ -1566,8 -1517,7 +1565,8 @@@ int ieee80211_build_preq_ies(struct iee
                             struct ieee80211_scan_ies *ie_desc,
                             const u8 *ie, size_t ie_len,
                             u8 bands_used, u32 *rate_masks,
 -                           struct cfg80211_chan_def *chandef)
 +                           struct cfg80211_chan_def *chandef,
 +                           u32 flags)
  {
        size_t pos = 0, old_pos = 0, custom_ie_offset = 0;
        int i;
                                                             ie, ie_len, i,
                                                             rate_masks[i],
                                                             chandef,
 -                                                           &custom_ie_offset);
 +                                                           &custom_ie_offset,
 +                                                           flags);
                        ie_desc->ies[i] = buffer + old_pos;
                        ie_desc->len[i] = pos - old_pos;
                        old_pos = pos;
@@@ -1611,7 -1560,7 +1610,7 @@@ struct sk_buff *ieee80211_build_probe_r
                                          struct ieee80211_channel *chan,
                                          const u8 *ssid, size_t ssid_len,
                                          const u8 *ie, size_t ie_len,
 -                                        bool directed)
 +                                        u32 flags)
  {
        struct ieee80211_local *local = sdata->local;
        struct cfg80211_chan_def chandef;
         * badly-behaved APs don't respond when this parameter is included.
         */
        chandef.width = sdata->vif.bss_conf.chandef.width;
 -      if (directed)
 +      if (flags & IEEE80211_PROBE_FLAG_DIRECTED)
                chandef.chan = NULL;
        else
                chandef.chan = chan;
        ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
                                           skb_tailroom(skb), &dummy_ie_desc,
                                           ie, ie_len, BIT(chan->band),
 -                                         rate_masks, &chandef);
 +                                         rate_masks, &chandef, flags);
        skb_put(skb, ies_len);
  
        if (dst) {
        return skb;
  }
  
 -void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata,
 -                            const u8 *src, const u8 *dst,
 -                            const u8 *ssid, size_t ssid_len,
 -                            const u8 *ie, size_t ie_len,
 -                            u32 ratemask, bool directed, u32 tx_flags,
 -                            struct ieee80211_channel *channel, bool scan)
 -{
 -      struct sk_buff *skb;
 -
 -      skb = ieee80211_build_probe_req(sdata, src, dst, ratemask, channel,
 -                                      ssid, ssid_len,
 -                                      ie, ie_len, directed);
 -      if (skb) {
 -              IEEE80211_SKB_CB(skb)->flags |= tx_flags;
 -              if (scan)
 -                      ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
 -              else
 -                      ieee80211_tx_skb(sdata, skb);
 -      }
 -}
 -
  u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
                            struct ieee802_11_elems *elems,
                            enum nl80211_band band, u32 *basic_rates)
@@@ -2442,72 -2412,6 +2441,72 @@@ u8 *ieee80211_ie_build_vht_cap(u8 *pos
        return pos;
  }
  
 +u8 *ieee80211_ie_build_he_cap(u8 *pos,
 +                            const struct ieee80211_sta_he_cap *he_cap,
 +                            u8 *end)
 +{
 +      u8 n;
 +      u8 ie_len;
 +      u8 *orig_pos = pos;
 +
 +      /* Make sure we have place for the IE */
 +      /*
 +       * TODO: the 1 added is because this temporarily is under the EXTENSION
 +       * IE. Get rid of it when it moves.
 +       */
 +      if (!he_cap)
 +              return orig_pos;
 +
 +      n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem);
 +      ie_len = 2 + 1 +
 +               sizeof(he_cap->he_cap_elem) + n +
 +               ieee80211_he_ppe_size(he_cap->ppe_thres[0],
 +                                     he_cap->he_cap_elem.phy_cap_info);
 +
 +      if ((end - pos) < ie_len)
 +              return orig_pos;
 +
 +      *pos++ = WLAN_EID_EXTENSION;
 +      pos++; /* We'll set the size later below */
 +      *pos++ = WLAN_EID_EXT_HE_CAPABILITY;
 +
 +      /* Fixed data */
 +      memcpy(pos, &he_cap->he_cap_elem, sizeof(he_cap->he_cap_elem));
 +      pos += sizeof(he_cap->he_cap_elem);
 +
 +      memcpy(pos, &he_cap->he_mcs_nss_supp, n);
 +      pos += n;
 +
 +      /* Check if PPE Threshold should be present */
 +      if ((he_cap->he_cap_elem.phy_cap_info[6] &
 +           IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0)
 +              goto end;
 +
 +      /*
 +       * Calculate how many PPET16/PPET8 pairs are to come. Algorithm:
 +       * (NSS_M1 + 1) x (num of 1 bits in RU_INDEX_BITMASK)
 +       */
 +      n = hweight8(he_cap->ppe_thres[0] &
 +                   IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK);
 +      n *= (1 + ((he_cap->ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) >>
 +                 IEEE80211_PPE_THRES_NSS_POS));
 +
 +      /*
 +       * Each pair is 6 bits, and we need to add the 7 "header" bits to the
 +       * total size.
 +       */
 +      n = (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) + 7;
 +      n = DIV_ROUND_UP(n, 8);
 +
 +      /* Copy PPE Thresholds */
 +      memcpy(pos, &he_cap->ppe_thres, n);
 +      pos += n;
 +
 +end:
 +      orig_pos[1] = (pos - orig_pos) - 2;
 +      return pos;
 +}
 +
  u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                               const struct cfg80211_chan_def *chandef,
                               u16 prot_mode, bool rifs_mode)
diff --combined net/wireless/nl80211.c
index 5fb9b7dd98318b6e9d4842474361104855b42983,733ccf8679728d091d0aeacc3aada4abc8318d12..4b8ec659e797ff743267773e315c6220b90993d0
@@@ -428,8 -428,6 +428,8 @@@ static const struct nla_policy nl80211_
        [NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 },
        [NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 },
        [NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
 +      [NL80211_ATTR_HE_CAPABILITY] = { .type = NLA_BINARY,
 +                                       .len = NL80211_HE_MAX_CAPABILITY_LEN },
  };
  
  /* policy for the key attributes */
@@@ -669,13 -667,13 +669,13 @@@ static int nl80211_msg_put_wmm_rules(st
                        goto nla_put_failure;
  
                if (nla_put_u16(msg, NL80211_WMMR_CW_MIN,
-                               rule->wmm_rule->client[j].cw_min) ||
+                               rule->wmm_rule.client[j].cw_min) ||
                    nla_put_u16(msg, NL80211_WMMR_CW_MAX,
-                               rule->wmm_rule->client[j].cw_max) ||
+                               rule->wmm_rule.client[j].cw_max) ||
                    nla_put_u8(msg, NL80211_WMMR_AIFSN,
-                              rule->wmm_rule->client[j].aifsn) ||
-                   nla_put_u8(msg, NL80211_WMMR_TXOP,
-                              rule->wmm_rule->client[j].cot))
+                              rule->wmm_rule.client[j].aifsn) ||
+                   nla_put_u16(msg, NL80211_WMMR_TXOP,
+                               rule->wmm_rule.client[j].cot))
                        goto nla_put_failure;
  
                nla_nest_end(msg, nl_wmm_rule);
@@@ -766,9 -764,9 +766,9 @@@ static int nl80211_msg_put_channel(stru
  
        if (large) {
                const struct ieee80211_reg_rule *rule =
-                       freq_reg_info(wiphy, chan->center_freq);
+                       freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
  
-               if (!IS_ERR(rule) && rule->wmm_rule) {
+               if (!IS_ERR_OR_NULL(rule) && rule->has_wmm) {
                        if (nl80211_msg_put_wmm_rules(msg, rule))
                                goto nla_put_failure;
                }
@@@ -1326,34 -1324,6 +1326,34 @@@ static int nl80211_send_coalesce(struc
        return 0;
  }
  
 +static int
 +nl80211_send_iftype_data(struct sk_buff *msg,
 +                       const struct ieee80211_sband_iftype_data *iftdata)
 +{
 +      const struct ieee80211_sta_he_cap *he_cap = &iftdata->he_cap;
 +
 +      if (nl80211_put_iftypes(msg, NL80211_BAND_IFTYPE_ATTR_IFTYPES,
 +                              iftdata->types_mask))
 +              return -ENOBUFS;
 +
 +      if (he_cap->has_he) {
 +              if (nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC,
 +                          sizeof(he_cap->he_cap_elem.mac_cap_info),
 +                          he_cap->he_cap_elem.mac_cap_info) ||
 +                  nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY,
 +                          sizeof(he_cap->he_cap_elem.phy_cap_info),
 +                          he_cap->he_cap_elem.phy_cap_info) ||
 +                  nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
 +                          sizeof(he_cap->he_mcs_nss_supp),
 +                          &he_cap->he_mcs_nss_supp) ||
 +                  nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
 +                          sizeof(he_cap->ppe_thres), he_cap->ppe_thres))
 +                      return -ENOBUFS;
 +      }
 +
 +      return 0;
 +}
 +
  static int nl80211_send_band_rateinfo(struct sk_buff *msg,
                                      struct ieee80211_supported_band *sband)
  {
                         sband->vht_cap.cap)))
                return -ENOBUFS;
  
 +      if (sband->n_iftype_data) {
 +              struct nlattr *nl_iftype_data =
 +                      nla_nest_start(msg, NL80211_BAND_ATTR_IFTYPE_DATA);
 +              int err;
 +
 +              if (!nl_iftype_data)
 +                      return -ENOBUFS;
 +
 +              for (i = 0; i < sband->n_iftype_data; i++) {
 +                      struct nlattr *iftdata;
 +
 +                      iftdata = nla_nest_start(msg, i + 1);
 +                      if (!iftdata)
 +                              return -ENOBUFS;
 +
 +                      err = nl80211_send_iftype_data(msg,
 +                                                     &sband->iftype_data[i]);
 +                      if (err)
 +                              return err;
 +
 +                      nla_nest_end(msg, iftdata);
 +              }
 +
 +              nla_nest_end(msg, nl_iftype_data);
 +      }
 +
        /* add bitrates */
        nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
        if (!nl_rates)
@@@ -2813,8 -2757,7 +2813,8 @@@ static int nl80211_send_iface(struct sk
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, wdev_address(wdev)) ||
            nla_put_u32(msg, NL80211_ATTR_GENERATION,
                        rdev->devlist_generation ^
 -                      (cfg80211_rdev_list_generation << 2)))
 +                      (cfg80211_rdev_list_generation << 2)) ||
 +          nla_put_u8(msg, NL80211_ATTR_4ADDR, wdev->use_4addr))
                goto nla_put_failure;
  
        if (rdev->ops->get_channel) {
@@@ -4529,9 -4472,6 +4529,9 @@@ static bool nl80211_put_sta_rate(struc
        case RATE_INFO_BW_160:
                rate_flg = NL80211_RATE_INFO_160_MHZ_WIDTH;
                break;
 +      case RATE_INFO_BW_HE_RU:
 +              rate_flg = 0;
 +              WARN_ON(!(info->flags & RATE_INFO_FLAGS_HE_MCS));
        }
  
        if (rate_flg && nla_put_flag(msg, rate_flg))
                if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
                    nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
                        return false;
 +      } else if (info->flags & RATE_INFO_FLAGS_HE_MCS) {
 +              if (nla_put_u8(msg, NL80211_RATE_INFO_HE_MCS, info->mcs))
 +                      return false;
 +              if (nla_put_u8(msg, NL80211_RATE_INFO_HE_NSS, info->nss))
 +                      return false;
 +              if (nla_put_u8(msg, NL80211_RATE_INFO_HE_GI, info->he_gi))
 +                      return false;
 +              if (nla_put_u8(msg, NL80211_RATE_INFO_HE_DCM, info->he_dcm))
 +                      return false;
 +              if (info->bw == RATE_INFO_BW_HE_RU &&
 +                  nla_put_u8(msg, NL80211_RATE_INFO_HE_RU_ALLOC,
 +                             info->he_ru_alloc))
 +                      return false;
        }
  
        nla_nest_end(msg, rate);
@@@ -4620,13 -4547,13 +4620,13 @@@ static int nl80211_send_station(struct 
  
  #define PUT_SINFO(attr, memb, type) do {                              \
        BUILD_BUG_ON(sizeof(type) == sizeof(u64));                      \
 -      if (sinfo->filled & (1ULL << NL80211_STA_INFO_ ## attr) &&      \
 +      if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&       \
            nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr,            \
                             sinfo->memb))                              \
                goto nla_put_failure;                                   \
        } while (0)
  #define PUT_SINFO_U64(attr, memb) do {                                        \
 -      if (sinfo->filled & (1ULL << NL80211_STA_INFO_ ## attr) &&      \
 +      if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&       \
            nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr,           \
                              sinfo->memb, NL80211_STA_INFO_PAD))       \
                goto nla_put_failure;                                   \
        PUT_SINFO(CONNECTED_TIME, connected_time, u32);
        PUT_SINFO(INACTIVE_TIME, inactive_time, u32);
  
 -      if (sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES) |
 -                           BIT(NL80211_STA_INFO_RX_BYTES64)) &&
 +      if (sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
 +                           BIT_ULL(NL80211_STA_INFO_RX_BYTES64)) &&
            nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
                        (u32)sinfo->rx_bytes))
                goto nla_put_failure;
  
 -      if (sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES) |
 -                           BIT(NL80211_STA_INFO_TX_BYTES64)) &&
 +      if (sinfo->filled & (BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
 +                           BIT_ULL(NL80211_STA_INFO_TX_BYTES64)) &&
            nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
                        (u32)sinfo->tx_bytes))
                goto nla_put_failure;
        default:
                break;
        }
 -      if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL)) {
 +      if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) {
                if (!nl80211_put_signal(msg, sinfo->chains,
                                        sinfo->chain_signal,
                                        NL80211_STA_INFO_CHAIN_SIGNAL))
                        goto nla_put_failure;
        }
 -      if (sinfo->filled & BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {
 +      if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {
                if (!nl80211_put_signal(msg, sinfo->chains,
                                        sinfo->chain_signal_avg,
                                        NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
                        goto nla_put_failure;
        }
 -      if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE)) {
 +      if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) {
                if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
                                          NL80211_STA_INFO_TX_BITRATE))
                        goto nla_put_failure;
        }
 -      if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE)) {
 +      if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) {
                if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
                                          NL80211_STA_INFO_RX_BITRATE))
                        goto nla_put_failure;
        PUT_SINFO(PEER_PM, peer_pm, u32);
        PUT_SINFO(NONPEER_PM, nonpeer_pm, u32);
  
 -      if (sinfo->filled & BIT(NL80211_STA_INFO_BSS_PARAM)) {
 +      if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
                bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
                if (!bss_param)
                        goto nla_put_failure;
  
                nla_nest_end(msg, bss_param);
        }
 -      if ((sinfo->filled & BIT(NL80211_STA_INFO_STA_FLAGS)) &&
 +      if ((sinfo->filled & BIT_ULL(NL80211_STA_INFO_STA_FLAGS)) &&
            nla_put(msg, NL80211_STA_INFO_STA_FLAGS,
                    sizeof(struct nl80211_sta_flag_update),
                    &sinfo->sta_flags))
@@@ -4960,8 -4887,7 +4960,8 @@@ int cfg80211_check_station_change(struc
                        return -EINVAL;
                if (params->supported_rates)
                        return -EINVAL;
 -              if (params->ext_capab || params->ht_capa || params->vht_capa)
 +              if (params->ext_capab || params->ht_capa || params->vht_capa ||
 +                  params->he_capa)
                        return -EINVAL;
        }
  
@@@ -5167,15 -5093,6 +5167,15 @@@ static int nl80211_set_station_tdls(str
        if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
                params->vht_capa =
                        nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 +      if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) {
 +              params->he_capa =
 +                      nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
 +              params->he_capa_len =
 +                      nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
 +
 +              if (params->he_capa_len < NL80211_HE_MIN_CAPABILITY_LEN)
 +                      return -EINVAL;
 +      }
  
        err = nl80211_parse_sta_channel_info(info, params);
        if (err)
@@@ -5403,17 -5320,6 +5403,17 @@@ static int nl80211_new_station(struct s
                params.vht_capa =
                        nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
  
 +      if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) {
 +              params.he_capa =
 +                      nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
 +              params.he_capa_len =
 +                      nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
 +
 +              /* max len is validated in nla policy */
 +              if (params.he_capa_len < NL80211_HE_MIN_CAPABILITY_LEN)
 +                      return -EINVAL;
 +      }
 +
        if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
                params.opmode_notif_used = true;
                params.opmode_notif =
        if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) {
                params.ht_capa = NULL;
                params.vht_capa = NULL;
 +
 +              /* HE requires WME */
 +              if (params.he_capa_len)
 +                      return -EINVAL;
        }
  
        /* When you run into this, adjust the code below for the new flag */
@@@ -6947,16 -6849,6 +6947,16 @@@ static bool cfg80211_off_channel_oper_a
        return regulatory_pre_cac_allowed(wdev->wiphy);
  }
  
 +static bool nl80211_check_scan_feat(struct wiphy *wiphy, u32 flags, u32 flag,
 +                                  enum nl80211_ext_feature_index feat)
 +{
 +      if (!(flags & flag))
 +              return true;
 +      if (wiphy_ext_feature_isset(wiphy, feat))
 +              return true;
 +      return false;
 +}
 +
  static int
  nl80211_check_scan_flags(struct wiphy *wiphy, struct wireless_dev *wdev,
                         void *request, struct nlattr **attrs,
  
        if (((*flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
             !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
 -          ((*flags & NL80211_SCAN_FLAG_LOW_SPAN) &&
 -           !wiphy_ext_feature_isset(wiphy,
 -                                    NL80211_EXT_FEATURE_LOW_SPAN_SCAN)) ||
 -          ((*flags & NL80211_SCAN_FLAG_LOW_POWER) &&
 -           !wiphy_ext_feature_isset(wiphy,
 -                                    NL80211_EXT_FEATURE_LOW_POWER_SCAN)) ||
 -          ((*flags & NL80211_SCAN_FLAG_HIGH_ACCURACY) &&
 -           !wiphy_ext_feature_isset(wiphy,
 -                                    NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN)))
 +          !nl80211_check_scan_feat(wiphy, *flags,
 +                                   NL80211_SCAN_FLAG_LOW_SPAN,
 +                                   NL80211_EXT_FEATURE_LOW_SPAN_SCAN) ||
 +          !nl80211_check_scan_feat(wiphy, *flags,
 +                                   NL80211_SCAN_FLAG_LOW_POWER,
 +                                   NL80211_EXT_FEATURE_LOW_POWER_SCAN) ||
 +          !nl80211_check_scan_feat(wiphy, *flags,
 +                                   NL80211_SCAN_FLAG_HIGH_ACCURACY,
 +                                   NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN) ||
 +          !nl80211_check_scan_feat(wiphy, *flags,
 +                                   NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME,
 +                                   NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) ||
 +          !nl80211_check_scan_feat(wiphy, *flags,
 +                                   NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP,
 +                                   NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) ||
 +          !nl80211_check_scan_feat(wiphy, *flags,
 +                                   NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION,
 +                                   NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION) ||
 +          !nl80211_check_scan_feat(wiphy, *flags,
 +                                   NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE,
 +                                   NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) ||
 +          !nl80211_check_scan_feat(wiphy, *flags,
 +                                   NL80211_SCAN_FLAG_RANDOM_SN,
 +                                   NL80211_EXT_FEATURE_SCAN_RANDOM_SN) ||
 +          !nl80211_check_scan_feat(wiphy, *flags,
 +                                   NL80211_SCAN_FLAG_MIN_PREQ_CONTENT,
 +                                   NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT))
                return -EOPNOTSUPP;
  
        if (*flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
                        return err;
        }
  
 -      if ((*flags & NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME) &&
 -          !wiphy_ext_feature_isset(wiphy,
 -                                   NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME))
 -              return -EOPNOTSUPP;
 -
 -      if ((*flags & NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP) &&
 -         !wiphy_ext_feature_isset(wiphy,
 -                                  NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP))
 -              return -EOPNOTSUPP;
 -
 -      if ((*flags & NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION) &&
 -          !wiphy_ext_feature_isset(wiphy,
 -                                   NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION))
 -              return -EOPNOTSUPP;
 -
 -      if ((*flags & NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE) &&
 -          !wiphy_ext_feature_isset(wiphy,
 -                                   NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE))
 -              return -EOPNOTSUPP;
 -
        return 0;
  }
  
@@@ -10254,7 -10148,7 +10254,7 @@@ static int cfg80211_cqm_rssi_update(str
                if (err)
                        return err;
  
 -              if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
 +              if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
                        wdev->cqm_config->last_rssi_event_value =
                                (s8) sinfo.rx_beacon_signal_avg;
        }
@@@ -12205,6 -12099,7 +12205,7 @@@ static int nl80211_update_ft_ies(struc
                return -EOPNOTSUPP;
  
        if (!info->attrs[NL80211_ATTR_MDID] ||
+           !info->attrs[NL80211_ATTR_IE] ||
            !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
  
diff --combined net/wireless/util.c
index e0825a019e9fb255adc2f4f749b08e241b2c2dde,908bf5b6d89e397bc1a57049e36106d691e8f04f..959ed3acd2407c8c4beb4ce91b625315bf926109
@@@ -4,7 -4,6 +4,7 @@@
   *
   * Copyright 2007-2009        Johannes Berg <[email protected]>
   * Copyright 2013-2014  Intel Mobile Communications GmbH
 + * Copyright 2017     Intel Deutschland GmbH
   */
  #include <linux/export.h>
  #include <linux/bitops.h>
@@@ -1143,85 -1142,6 +1143,85 @@@ static u32 cfg80211_calculate_bitrate_v
        return 0;
  }
  
 +static u32 cfg80211_calculate_bitrate_he(struct rate_info *rate)
 +{
 +#define SCALE 2048
 +      u16 mcs_divisors[12] = {
 +              34133, /* 16.666666... */
 +              17067, /*  8.333333... */
 +              11378, /*  5.555555... */
 +               8533, /*  4.166666... */
 +               5689, /*  2.777777... */
 +               4267, /*  2.083333... */
 +               3923, /*  1.851851... */
 +               3413, /*  1.666666... */
 +               2844, /*  1.388888... */
 +               2560, /*  1.250000... */
 +               2276, /*  1.111111... */
 +               2048, /*  1.000000... */
 +      };
 +      u32 rates_160M[3] = { 960777777, 907400000, 816666666 };
 +      u32 rates_969[3] =  { 480388888, 453700000, 408333333 };
 +      u32 rates_484[3] =  { 229411111, 216666666, 195000000 };
 +      u32 rates_242[3] =  { 114711111, 108333333,  97500000 };
 +      u32 rates_106[3] =  {  40000000,  37777777,  34000000 };
 +      u32 rates_52[3]  =  {  18820000,  17777777,  16000000 };
 +      u32 rates_26[3]  =  {   9411111,   8888888,   8000000 };
 +      u64 tmp;
 +      u32 result;
 +
 +      if (WARN_ON_ONCE(rate->mcs > 11))
 +              return 0;
 +
 +      if (WARN_ON_ONCE(rate->he_gi > NL80211_RATE_INFO_HE_GI_3_2))
 +              return 0;
 +      if (WARN_ON_ONCE(rate->he_ru_alloc >
 +                       NL80211_RATE_INFO_HE_RU_ALLOC_2x996))
 +              return 0;
 +      if (WARN_ON_ONCE(rate->nss < 1 || rate->nss > 8))
 +              return 0;
 +
 +      if (rate->bw == RATE_INFO_BW_160)
 +              result = rates_160M[rate->he_gi];
 +      else if (rate->bw == RATE_INFO_BW_80 ||
 +               (rate->bw == RATE_INFO_BW_HE_RU &&
 +                rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_996))
 +              result = rates_969[rate->he_gi];
 +      else if (rate->bw == RATE_INFO_BW_40 ||
 +               (rate->bw == RATE_INFO_BW_HE_RU &&
 +                rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_484))
 +              result = rates_484[rate->he_gi];
 +      else if (rate->bw == RATE_INFO_BW_20 ||
 +               (rate->bw == RATE_INFO_BW_HE_RU &&
 +                rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_242))
 +              result = rates_242[rate->he_gi];
 +      else if (rate->bw == RATE_INFO_BW_HE_RU &&
 +               rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_106)
 +              result = rates_106[rate->he_gi];
 +      else if (rate->bw == RATE_INFO_BW_HE_RU &&
 +               rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_52)
 +              result = rates_52[rate->he_gi];
 +      else if (rate->bw == RATE_INFO_BW_HE_RU &&
 +               rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_26)
 +              result = rates_26[rate->he_gi];
 +      else if (WARN(1, "invalid HE MCS: bw:%d, ru:%d\n",
 +                    rate->bw, rate->he_ru_alloc))
 +              return 0;
 +
 +      /* now scale to the appropriate MCS */
 +      tmp = result;
 +      tmp *= SCALE;
 +      do_div(tmp, mcs_divisors[rate->mcs]);
 +      result = tmp;
 +
 +      /* and take NSS, DCM into account */
 +      result = (result * rate->nss) / 8;
 +      if (rate->he_dcm)
 +              result /= 2;
 +
 +      return result;
 +}
 +
  u32 cfg80211_calculate_bitrate(struct rate_info *rate)
  {
        if (rate->flags & RATE_INFO_FLAGS_MCS)
                return cfg80211_calculate_bitrate_60g(rate);
        if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
                return cfg80211_calculate_bitrate_vht(rate);
 +      if (rate->flags & RATE_INFO_FLAGS_HE_MCS)
 +              return cfg80211_calculate_bitrate_he(rate);
  
        return rate->legacy;
  }
@@@ -1456,7 -1374,7 +1456,7 @@@ bool ieee80211_chandef_to_operating_cla
                                          u8 *op_class)
  {
        u8 vht_opclass;
-       u16 freq = chandef->center_freq1;
+       u32 freq = chandef->center_freq1;
  
        if (freq >= 2412 && freq <= 2472) {
                if (chandef->width > NL80211_CHAN_WIDTH_40)
@@@ -1873,9 -1791,8 +1873,9 @@@ bool cfg80211_does_bw_fit_range(const s
  
  int cfg80211_sinfo_alloc_tid_stats(struct station_info *sinfo, gfp_t gfp)
  {
 -      sinfo->pertid = kcalloc(sizeof(*(sinfo->pertid)),
 -                              IEEE80211_NUM_TIDS + 1, gfp);
 +      sinfo->pertid = kcalloc(IEEE80211_NUM_TIDS + 1,
 +                              sizeof(*(sinfo->pertid)),
 +                              gfp);
        if (!sinfo->pertid)
                return -ENOMEM;
  
This page took 0.227827 seconds and 4 git commands to generate.