Merge tag 'mac80211-next-for-davem-2018-12-19' of git://git.kernel.org/pub/scm/linux...
authorDavid S. Miller <davem@davemloft.net>
Wed, 19 Dec 2018 16:36:18 +0000 (08:36 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 19 Dec 2018 16:36:18 +0000 (08:36 -0800)
Johannes Berg says:

====================
This time we have too many changes to list, highlights:
 * virt_wifi - wireless control simulation on top of
   another network interface
 * hwsim configurability to test capabilities similar
   to real hardware
 * various mesh improvements
 * various radiotap vendor data fixes in mac80211
 * finally the nl_set_extack_cookie_u64() we talked
   about previously, used for
 * peer measurement APIs, right now only with FTM
   (flight time measurement) for location
 * made nl80211 radio/interface announcements more complete
 * various new HE (802.11ax) things:
   updates, TWT support, ...
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
1  2 
drivers/net/wireless/mac80211_hwsim.c
net/mac80211/cfg.c
net/mac80211/iface.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/tx.c
net/wireless/nl80211.c
net/wireless/util.c

index d1464e3e1be21a23f0cbc2d82bd4360bced5b4ee,e4db8e2b2b548dec59bc26ed7299a59cab05799b..3a4b8786f7ea92d4286135c2070fe5a9158691dd
@@@ -374,6 -374,20 +374,20 @@@ static const struct ieee80211_rate hwsi
        { .bitrate = 540 }
  };
  
+ static const u32 hwsim_ciphers[] = {
+       WLAN_CIPHER_SUITE_WEP40,
+       WLAN_CIPHER_SUITE_WEP104,
+       WLAN_CIPHER_SUITE_TKIP,
+       WLAN_CIPHER_SUITE_CCMP,
+       WLAN_CIPHER_SUITE_CCMP_256,
+       WLAN_CIPHER_SUITE_GCMP,
+       WLAN_CIPHER_SUITE_GCMP_256,
+       WLAN_CIPHER_SUITE_AES_CMAC,
+       WLAN_CIPHER_SUITE_BIP_CMAC_256,
+       WLAN_CIPHER_SUITE_BIP_GMAC_128,
+       WLAN_CIPHER_SUITE_BIP_GMAC_256,
+ };
  #define OUI_QCA 0x001374
  #define QCA_NL80211_SUBCMD_TEST 1
  enum qca_nl80211_vendor_subcmds {
@@@ -451,48 -465,6 +465,6 @@@ static const struct nl80211_vendor_cmd_
        { .vendor_id = OUI_QCA, .subcmd = 1 },
  };
  
- static const struct ieee80211_iface_limit hwsim_if_limits[] = {
-       { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
-       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) |
-                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
- #ifdef CONFIG_MAC80211_MESH
-                                BIT(NL80211_IFTYPE_MESH_POINT) |
- #endif
-                                BIT(NL80211_IFTYPE_AP) |
-                                BIT(NL80211_IFTYPE_P2P_GO) },
-       /* must be last, see hwsim_if_comb */
-       { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }
- };
- static const struct ieee80211_iface_combination hwsim_if_comb[] = {
-       {
-               .limits = hwsim_if_limits,
-               /* remove the last entry which is P2P_DEVICE */
-               .n_limits = ARRAY_SIZE(hwsim_if_limits) - 1,
-               .max_interfaces = 2048,
-               .num_different_channels = 1,
-               .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
-                                      BIT(NL80211_CHAN_WIDTH_20) |
-                                      BIT(NL80211_CHAN_WIDTH_40) |
-                                      BIT(NL80211_CHAN_WIDTH_80) |
-                                      BIT(NL80211_CHAN_WIDTH_160),
-       },
- };
- static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = {
-       {
-               .limits = hwsim_if_limits,
-               .n_limits = ARRAY_SIZE(hwsim_if_limits),
-               .max_interfaces = 2048,
-               .num_different_channels = 1,
-               .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
-                                      BIT(NL80211_CHAN_WIDTH_20) |
-                                      BIT(NL80211_CHAN_WIDTH_40) |
-                                      BIT(NL80211_CHAN_WIDTH_80) |
-                                      BIT(NL80211_CHAN_WIDTH_160),
-       },
- };
  static spinlock_t hwsim_radio_lock;
  static LIST_HEAD(hwsim_radios);
  static struct rhashtable hwsim_radios_rht;
@@@ -515,6 -487,10 +487,10 @@@ struct mac80211_hwsim_data 
        struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
        struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
        struct ieee80211_iface_combination if_combination;
+       struct ieee80211_iface_limit if_limits[3];
+       int n_if_limits;
+       u32 ciphers[ARRAY_SIZE(hwsim_ciphers)];
  
        struct mac_address addresses[2];
        int channels, idx;
@@@ -642,6 -618,8 +618,8 @@@ static const struct nla_policy hwsim_ge
        [HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
        [HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
        [HWSIM_ATTR_PERM_ADDR] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
+       [HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 },
+       [HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY },
  };
  
  static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
@@@ -2414,6 -2392,9 +2392,9 @@@ struct hwsim_new_radio_params 
        const char *hwname;
        bool no_vif;
        const u8 *perm_addr;
+       u32 iftypes;
+       u32 *ciphers;
+       u8 n_ciphers;
  };
  
  static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
@@@ -2630,6 -2611,27 +2611,27 @@@ static void mac80211_hswim_he_capab(str
        sband->n_iftype_data = 1;
  }
  
+ #ifdef CONFIG_MAC80211_MESH
+ #define HWSIM_MESH_BIT BIT(NL80211_IFTYPE_MESH_POINT)
+ #else
+ #define HWSIM_MESH_BIT 0
+ #endif
+ #define HWSIM_DEFAULT_IF_LIMIT \
+       (BIT(NL80211_IFTYPE_STATION) | \
+        BIT(NL80211_IFTYPE_P2P_CLIENT) | \
+        BIT(NL80211_IFTYPE_AP) | \
+        BIT(NL80211_IFTYPE_P2P_GO) | \
+        HWSIM_MESH_BIT)
+ #define HWSIM_IFTYPE_SUPPORT_MASK \
+       (BIT(NL80211_IFTYPE_STATION) | \
+        BIT(NL80211_IFTYPE_AP) | \
+        BIT(NL80211_IFTYPE_P2P_CLIENT) | \
+        BIT(NL80211_IFTYPE_P2P_GO) | \
+        BIT(NL80211_IFTYPE_ADHOC) | \
+        BIT(NL80211_IFTYPE_MESH_POINT))
  static int mac80211_hwsim_new_radio(struct genl_info *info,
                                    struct hwsim_new_radio_params *param)
  {
        const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
        struct net *net;
        int idx;
+       int n_limits = 0;
  
        if (WARN_ON(param->channels > 1 && !param->use_chanctx))
                return -EINVAL;
        if (info)
                data->portid = info->snd_portid;
  
+       /* setup interface limits, only on interface types we support */
+       if (param->iftypes & BIT(NL80211_IFTYPE_ADHOC)) {
+               data->if_limits[n_limits].max = 1;
+               data->if_limits[n_limits].types = BIT(NL80211_IFTYPE_ADHOC);
+               n_limits++;
+       }
+       if (param->iftypes & HWSIM_DEFAULT_IF_LIMIT) {
+               data->if_limits[n_limits].max = 2048;
+               /*
+                * For this case, we may only support a subset of
+                * HWSIM_DEFAULT_IF_LIMIT, therefore we only want to add the
+                * bits that both param->iftype & HWSIM_DEFAULT_IF_LIMIT have.
+                */
+               data->if_limits[n_limits].types =
+                                       HWSIM_DEFAULT_IF_LIMIT & param->iftypes;
+               n_limits++;
+       }
+       if (param->iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) {
+               data->if_limits[n_limits].max = 1;
+               data->if_limits[n_limits].types =
+                                               BIT(NL80211_IFTYPE_P2P_DEVICE);
+               n_limits++;
+       }
        if (data->use_chanctx) {
                hw->wiphy->max_scan_ssids = 255;
                hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
                hw->wiphy->max_remain_on_channel_duration = 1000;
-               hw->wiphy->iface_combinations = &data->if_combination;
-               if (param->p2p_device)
-                       data->if_combination = hwsim_if_comb_p2p_dev[0];
-               else
-                       data->if_combination = hwsim_if_comb[0];
-               hw->wiphy->n_iface_combinations = 1;
-               /* For channels > 1 DFS is not allowed */
                data->if_combination.radar_detect_widths = 0;
                data->if_combination.num_different_channels = data->channels;
-       } else if (param->p2p_device) {
-               hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
-               hw->wiphy->n_iface_combinations =
-                       ARRAY_SIZE(hwsim_if_comb_p2p_dev);
        } else {
-               hw->wiphy->iface_combinations = hwsim_if_comb;
-               hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
+               data->if_combination.num_different_channels = 1;
+               data->if_combination.radar_detect_widths =
+                                       BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                       BIT(NL80211_CHAN_WIDTH_20) |
+                                       BIT(NL80211_CHAN_WIDTH_40) |
+                                       BIT(NL80211_CHAN_WIDTH_80) |
+                                       BIT(NL80211_CHAN_WIDTH_160);
+       }
+       data->if_combination.n_limits = n_limits;
+       data->if_combination.max_interfaces = 2048;
+       data->if_combination.limits = data->if_limits;
+       hw->wiphy->iface_combinations = &data->if_combination;
+       hw->wiphy->n_iface_combinations = 1;
+       if (param->ciphers) {
+               memcpy(data->ciphers, param->ciphers,
+                      param->n_ciphers * sizeof(u32));
+               hw->wiphy->cipher_suites = data->ciphers;
+               hw->wiphy->n_cipher_suites = param->n_ciphers;
        }
  
        INIT_DELAYED_WORK(&data->roc_start, hw_roc_start);
  
        hw->queues = 5;
        hw->offchannel_tx_hw_queue = 4;
-       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-                                    BIT(NL80211_IFTYPE_AP) |
-                                    BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                                    BIT(NL80211_IFTYPE_P2P_GO) |
-                                    BIT(NL80211_IFTYPE_ADHOC) |
-                                    BIT(NL80211_IFTYPE_MESH_POINT);
-       if (param->p2p_device)
-               hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
  
        ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
        ieee80211_hw_set(hw, CHANCTX_STA_CSA);
                               NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
        wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
  
+       hw->wiphy->interface_modes = param->iftypes;
        /* ask mac80211 to reserve space for magic */
        hw->vif_data_size = sizeof(struct hwsim_vif_priv);
        hw->sta_data_size = sizeof(struct hwsim_sta_priv);
  
        wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
  
 +      tasklet_hrtimer_init(&data->beacon_timer,
 +                           mac80211_hwsim_beacon,
 +                           CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
 +
        err = ieee80211_register_hw(hw);
        if (err < 0) {
                pr_debug("mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
                                    data->debugfs,
                                    data, &hwsim_simulate_radar);
  
 -      tasklet_hrtimer_init(&data->beacon_timer,
 -                           mac80211_hwsim_beacon,
 -                           CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
 -
        spin_lock_bh(&hwsim_radio_lock);
        err = rhashtable_insert_fast(&hwsim_radios_rht, &data->rht,
                                     hwsim_rht_params);
@@@ -3293,6 -3323,29 +3323,29 @@@ static int hwsim_register_received_nl(s
        return 0;
  }
  
+ /* ensures ciphers only include ciphers listed in 'hwsim_ciphers' array */
+ static bool hwsim_known_ciphers(const u32 *ciphers, int n_ciphers)
+ {
+       int i;
+       for (i = 0; i < n_ciphers; i++) {
+               int j;
+               int found = 0;
+               for (j = 0; j < ARRAY_SIZE(hwsim_ciphers); j++) {
+                       if (ciphers[i] == hwsim_ciphers[j]) {
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found)
+                       return false;
+       }
+       return true;
+ }
  static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
  {
        struct hwsim_new_radio_params param = { 0 };
        if (info->attrs[HWSIM_ATTR_NO_VIF])
                param.no_vif = true;
  
-       if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
-               hwname = kasprintf(GFP_KERNEL, "%.*s",
-                                  nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
-                                  (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
-               if (!hwname)
-                       return -ENOMEM;
-               param.hwname = hwname;
-       }
        if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
                param.use_chanctx = true;
        else
        if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
                u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
  
-               if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) {
-                       kfree(hwname);
+               if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
                        return -EINVAL;
-               }
  
                idx = array_index_nospec(idx,
                                         ARRAY_SIZE(hwsim_world_regdom_custom));
                        GENL_SET_ERR_MSG(info,"MAC is no valid source addr");
                        NL_SET_BAD_ATTR(info->extack,
                                        info->attrs[HWSIM_ATTR_PERM_ADDR]);
-                       kfree(hwname);
                        return -EINVAL;
                }
  
                param.perm_addr = nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]);
        }
  
+       if (info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]) {
+               param.iftypes =
+                       nla_get_u32(info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]);
+               if (param.iftypes & ~HWSIM_IFTYPE_SUPPORT_MASK) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT],
+                                           "cannot support more iftypes than kernel");
+                       return -EINVAL;
+               }
+       } else {
+               param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
+       }
+       /* ensure both flag and iftype support is honored */
+       if (param.p2p_device ||
+           param.iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) {
+               param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
+               param.p2p_device = true;
+       }
+       if (info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]) {
+               u32 len = nla_len(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]);
+               param.ciphers =
+                       nla_data(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]);
+               if (len % sizeof(u32)) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
+                                           "bad cipher list length");
+                       return -EINVAL;
+               }
+               param.n_ciphers = len / sizeof(u32);
+               if (param.n_ciphers > ARRAY_SIZE(hwsim_ciphers)) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
+                                           "too many ciphers specified");
+                       return -EINVAL;
+               }
+               if (!hwsim_known_ciphers(param.ciphers, param.n_ciphers)) {
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
+                                           "unsupported ciphers specified");
+                       return -EINVAL;
+               }
+       }
+       if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
+               hwname = kasprintf(GFP_KERNEL, "%.*s",
+                                  nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+                                  (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
+               if (!hwname)
+                       return -ENOMEM;
+               param.hwname = hwname;
+       }
        ret = mac80211_hwsim_new_radio(info, &param);
        kfree(hwname);
        return ret;
@@@ -3703,16 -3803,16 +3803,16 @@@ static int __init init_mac80211_hwsim(v
        if (err)
                goto out_unregister_pernet;
  
 +      err = hwsim_init_netlink();
 +      if (err)
 +              goto out_unregister_driver;
 +
        hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim");
        if (IS_ERR(hwsim_class)) {
                err = PTR_ERR(hwsim_class);
 -              goto out_unregister_driver;
 +              goto out_exit_netlink;
        }
  
 -      err = hwsim_init_netlink();
 -      if (err < 0)
 -              goto out_unregister_driver;
 -
        for (i = 0; i < radios; i++) {
                struct hwsim_new_radio_params param = { 0 };
  
  
                param.p2p_device = support_p2p_device;
                param.use_chanctx = channels > 1;
+               param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
  
                err = mac80211_hwsim_new_radio(NULL, &param);
                if (err < 0)
@@@ -3818,8 -3919,6 +3919,8 @@@ out_free_mon
        free_netdev(hwsim_mon);
  out_free_radios:
        mac80211_hwsim_free();
 +out_exit_netlink:
 +      hwsim_exit_netlink();
  out_unregister_driver:
        platform_driver_unregister(&mac80211_hwsim_driver);
  out_unregister_pernet:
diff --combined net/mac80211/cfg.c
index 818aa006034950785768d80a3f5ba8ad5f2f7f0f,567c63ff830f4ab1c53ce88dcf99708b5e6e1968..de65fe3ed9cc66e9d6373c4df22d356e8b36579d
@@@ -800,8 -800,8 +800,8 @@@ static int ieee80211_set_ftm_responder_
        u8 *pos;
        int len;
  
-       if ((!lci || !lci_len) && (!civicloc || !civicloc_len))
-               return 1;
+       if (!lci_len && !civicloc_len)
+               return 0;
  
        bss_conf = &sdata->vif.bss_conf;
        old = bss_conf->ftmr_params;
@@@ -2028,6 -2028,9 +2028,9 @@@ static int ieee80211_update_mesh_config
                        nconf->dot11MeshAwakeWindowDuration;
        if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
                conf->plink_timeout = nconf->plink_timeout;
+       if (_chg_mesh_attr(NL80211_MESHCONF_CONNECTED_TO_GATE, mask))
+               conf->dot11MeshConnectedToMeshGate =
+                       nconf->dot11MeshConnectedToMeshGate;
        ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
        return 0;
  }
@@@ -2891,7 -2894,7 +2894,7 @@@ cfg80211_beacon_dup(struct cfg80211_bea
  
        len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
              beacon->proberesp_ies_len + beacon->assocresp_ies_len +
 -            beacon->probe_resp_len;
 +            beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len;
  
        new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
        if (!new_beacon)
                memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
                pos += beacon->probe_resp_len;
        }
 -      if (beacon->ftm_responder)
 -              new_beacon->ftm_responder = beacon->ftm_responder;
 +
 +      /* might copy -1, meaning no changes requested */
 +      new_beacon->ftm_responder = beacon->ftm_responder;
        if (beacon->lci) {
                new_beacon->lci_len = beacon->lci_len;
                new_beacon->lci = pos;
@@@ -3850,6 -3852,26 +3853,26 @@@ ieee80211_get_ftm_responder_stats(struc
        return drv_get_ftm_responder_stats(local, sdata, ftm_stats);
  }
  
+ static int
+ ieee80211_start_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
+                    struct cfg80211_pmsr_request *request)
+ {
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
+       return drv_start_pmsr(local, sdata, request);
+ }
+ static void
+ ieee80211_abort_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
+                    struct cfg80211_pmsr_request *request)
+ {
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
+       return drv_abort_pmsr(local, sdata, request);
+ }
  const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
        .tx_control_port = ieee80211_tx_control_port,
        .get_txq_stats = ieee80211_get_txq_stats,
        .get_ftm_responder_stats = ieee80211_get_ftm_responder_stats,
+       .start_pmsr = ieee80211_start_pmsr,
+       .abort_pmsr = ieee80211_abort_pmsr,
  };
diff --combined net/mac80211/iface.c
index 5f3c81e705c7df9ea7ff7c69eb3b6aff00df17ee,6944ec7b624588ac31778cc90648f729b5522dd1..e9652e623a31f95fba50f95c57f54ef330040c5e
@@@ -1015,8 -1015,6 +1015,8 @@@ static void ieee80211_do_stop(struct ie
        if (local->open_count == 0)
                ieee80211_clear_tx_pending(local);
  
 +      sdata->vif.bss_conf.beacon_int = 0;
 +
        /*
         * If the interface goes down while suspended, presumably because
         * the device was unplugged and that happens before our resume,
@@@ -1801,7 -1799,7 +1801,7 @@@ int ieee80211_if_add(struct ieee80211_l
                }
  
                ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
-               if (params && is_valid_ether_addr(params->macaddr))
+               if (is_valid_ether_addr(params->macaddr))
                        memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
                else
                        memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
        ieee80211_setup_sdata(sdata, type);
  
        if (ndev) {
-               if (params) {
-                       ndev->ieee80211_ptr->use_4addr = params->use_4addr;
-                       if (type == NL80211_IFTYPE_STATION)
-                               sdata->u.mgd.use_4addr = params->use_4addr;
-               }
+               ndev->ieee80211_ptr->use_4addr = params->use_4addr;
+               if (type == NL80211_IFTYPE_STATION)
+                       sdata->u.mgd.use_4addr = params->use_4addr;
  
                ndev->features |= local->hw.netdev_features;
  
diff --combined net/mac80211/mlme.c
index bcf5ffc1567a4ff1c6054f7aecc0aa28dfd2c3fa,54e511dbbc12071821a17678c45f25f35dc51835..687821567287104451d429d84db8192a7a8e1b24
@@@ -916,6 -916,15 +916,15 @@@ static void ieee80211_send_assoc(struc
                ieee80211_add_vht_ie(sdata, skb, sband,
                                     &assoc_data->ap_vht_cap);
  
+       /*
+        * If AP doesn't support HT, 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))
+               ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
                ieee80211_add_he_ie(sdata, skb, sband);
  
@@@ -1869,7 -1878,7 +1878,7 @@@ ieee80211_sta_wmm_params(struct ieee802
        struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        size_t left;
-       int count, ac;
+       int count, mu_edca_count, ac;
        const u8 *pos;
        u8 uapsd_queues = 0;
  
                uapsd_queues = ifmgd->uapsd_queues;
  
        count = wmm_param[6] & 0x0f;
-       if (count == ifmgd->wmm_last_param_set)
+       /* -1 is the initial value of ifmgd->mu_edca_last_param_set.
+        * if mu_edca was preset before and now it disappeared tell
+        * the driver about it.
+        */
+       mu_edca_count = mu_edca ? mu_edca->mu_qos_info & 0x0f : -1;
+       if (count == ifmgd->wmm_last_param_set &&
+           mu_edca_count == ifmgd->mu_edca_last_param_set)
                return false;
        ifmgd->wmm_last_param_set = count;
+       ifmgd->mu_edca_last_param_set = mu_edca_count;
  
        pos = wmm_param + 8;
        left = wmm_param_len - 8;
@@@ -2766,7 -2782,6 +2782,7 @@@ static bool ieee80211_mark_sta_auth(str
  {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct sta_info *sta;
 +      bool result = true;
  
        sdata_info(sdata, "authenticated\n");
        ifmgd->auth_data->done = true;
        sta = sta_info_get(sdata, bssid);
        if (!sta) {
                WARN_ONCE(1, "%s: STA %pM not found", sdata->name, bssid);
 -              return false;
 +              result = false;
 +              goto out;
        }
        if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) {
                sdata_info(sdata, "failed moving %pM to auth\n", bssid);
 -              return false;
 +              result = false;
 +              goto out;
        }
 -      mutex_unlock(&sdata->local->sta_mtx);
  
 -      return true;
 +out:
 +      mutex_unlock(&sdata->local->sta_mtx);
 +      return result;
  }
  
  static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
@@@ -3062,6 -3074,19 +3078,19 @@@ static void ieee80211_get_rates(struct 
        }
  }
  
+ static bool ieee80211_twt_req_supported(const struct sta_info *sta,
+                                       const struct ieee802_11_elems *elems)
+ {
+       if (elems->ext_capab_len < 10)
+               return false;
+       if (!(elems->ext_capab[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT))
+               return false;
+       return sta->sta.he_cap.he_cap_elem.mac_cap_info[0] &
+               IEEE80211_HE_MAC_CAP0_TWT_RES;
+ }
  static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
                                    struct cfg80211_bss *cbss,
                                    struct ieee80211_mgmt *mgmt, size_t len)
                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);
                                                  sta);
  
                bss_conf->he_support = sta->sta.he_cap.has_he;
+               bss_conf->twt_requester =
+                       ieee80211_twt_req_supported(sta, &elems);
        } else {
                bss_conf->he_support = false;
+               bss_conf->twt_requester = false;
        }
  
        if (bss_conf->he_support) {
         * 4-bit value.
         */
        ifmgd->wmm_last_param_set = -1;
+       ifmgd->mu_edca_last_param_set = -1;
  
        if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
                ieee80211_set_wmm_default(sdata, false, false);
@@@ -4660,8 -4679,10 +4683,10 @@@ static int ieee80211_prep_channel(struc
                }
        }
  
-       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
-           ieee80211_get_he_sta_cap(sband)) {
+       if (!ieee80211_get_he_sta_cap(sband))
+               ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) {
                const struct cfg80211_bss_ies *ies;
                const u8 *he_oper_ie;
  
diff --combined net/mac80211/rx.c
index 428f7ad5f9b59f7964405c1c534d2b1a130fe25e,b33d37186576ab8b73867694e7b3b8f9437e84f1..45aad3d3108cccce9626c2682ae390a8b0991568
@@@ -143,6 -143,9 +143,9 @@@ ieee80211_rx_radiotap_hdrlen(struct iee
        /* allocate extra bitmaps */
        if (status->chains)
                len += 4 * hweight8(status->chains);
+       /* vendor presence bitmap */
+       if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)
+               len += 4;
  
        if (ieee80211_have_rx_timestamp(status)) {
                len = ALIGN(len, 8);
        if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
                struct ieee80211_vendor_radiotap *rtap = (void *)skb->data;
  
-               /* vendor presence bitmap */
-               len += 4;
                /* alignment for fixed 6-byte vendor data header */
                len = ALIGN(len, 2);
                /* vendor data header */
@@@ -753,6 -754,7 +754,7 @@@ ieee80211_rx_monitor(struct ieee80211_l
        struct ieee80211_sub_if_data *monitor_sdata =
                rcu_dereference(local->monitor_sdata);
        bool only_monitor = false;
+       unsigned int min_head_len;
  
        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 (status->flag & RX_FLAG_RADIOTAP_LSIG)
+               rtap_space += sizeof(struct ieee80211_radiotap_lsig);
        if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
-               struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
+               struct ieee80211_vendor_radiotap *rtap =
+                       (void *)(origskb->data + rtap_space);
  
                rtap_space += sizeof(*rtap) + rtap->len + rtap->pad;
        }
  
+       min_head_len = rtap_space;
        /*
         * First, we may need to make a copy of the skb because
         *  (1) we need to modify it for radiotap (if not present), and
         * the SKB because it has a bad FCS/PLCP checksum.
         */
  
-       if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
-               if (unlikely(origskb->len <= FCS_LEN)) {
-                       /* driver bug */
-                       WARN_ON(1);
-                       dev_kfree_skb(origskb);
-                       return NULL;
+       if (!(status->flag & RX_FLAG_NO_PSDU)) {
+               if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
+                       if (unlikely(origskb->len <= FCS_LEN + rtap_space)) {
+                               /* driver bug */
+                               WARN_ON(1);
+                               dev_kfree_skb(origskb);
+                               return NULL;
+                       }
+                       present_fcs_len = FCS_LEN;
                }
-               present_fcs_len = FCS_LEN;
+               /* also consider the hdr->frame_control */
+               min_head_len += 2;
        }
  
-       /* ensure hdr->frame_control and vendor radiotap data are in skb head */
-       if (!pskb_may_pull(origskb, 2 + rtap_space)) {
+       /* ensure that the expected data elements are in skb head */
+       if (!pskb_may_pull(origskb, min_head_len)) {
                dev_kfree_skb(origskb);
                return NULL;
        }
@@@ -1403,7 -1416,6 +1416,7 @@@ ieee80211_rx_h_check_dup(struct ieee802
                return RX_CONTINUE;
  
        if (ieee80211_is_ctl(hdr->frame_control) ||
 +          ieee80211_is_nullfunc(hdr->frame_control) ||
            ieee80211_is_qos_nullfunc(hdr->frame_control) ||
            is_multicast_ether_addr(hdr->addr1))
                return RX_CONTINUE;
@@@ -3064,7 -3076,7 +3077,7 @@@ ieee80211_rx_h_action(struct ieee80211_
                        cfg80211_sta_opmode_change_notify(sdata->dev,
                                                          rx->sta->addr,
                                                          &sta_opmode,
 -                                                        GFP_KERNEL);
 +                                                        GFP_ATOMIC);
                        goto handled;
                }
                case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
                        cfg80211_sta_opmode_change_notify(sdata->dev,
                                                          rx->sta->addr,
                                                          &sta_opmode,
 -                                                        GFP_KERNEL);
 +                                                        GFP_ATOMIC);
                        goto handled;
                }
                default:
diff --combined net/mac80211/tx.c
index 1f536ba573b4852ef18f1a80129e56c4961be520,4f348b11654941984dd0688fc7db964a9b5c7863..f170d6c6629a0097f7bf3f874799049fdb39d046
@@@ -439,8 -439,8 +439,8 @@@ ieee80211_tx_h_multicast_ps_buf(struct 
        if (ieee80211_hw_check(&tx->local->hw, QUEUE_CONTROL))
                info->hw_queue = tx->sdata->vif.cab_queue;
  
 -      /* no stations in PS mode */
 -      if (!atomic_read(&ps->num_sta_ps))
 +      /* no stations in PS mode and no buffered packets */
 +      if (!atomic_read(&ps->num_sta_ps) && skb_queue_empty(&ps->bc_buf))
                return TX_CONTINUE;
  
        info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
@@@ -3218,6 -3218,9 +3218,9 @@@ static bool ieee80211_amsdu_aggregate(s
        if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
                return false;
  
+       if (skb_is_gso(skb))
+               return false;
        if (!txq)
                return false;
  
        tin = &txqi->tin;
        flow = fq_flow_classify(fq, tin, skb, fq_flow_get_default_func);
        head = skb_peek_tail(&flow->queue);
-       if (!head)
+       if (!head || skb_is_gso(head))
                goto out;
  
        orig_len = head->len;
@@@ -3583,7 -3586,7 +3586,7 @@@ begin
                        skb_queue_splice_tail(&tx.skbs, &txqi->frags);
        }
  
-       if (skb && skb_has_frag_list(skb) &&
+       if (skb_has_frag_list(skb) &&
            !ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) {
                if (skb_linearize(skb)) {
                        ieee80211_free_txskb(&local->hw, skb);
@@@ -4579,7 -4582,7 +4582,7 @@@ struct sk_buff *ieee80211_nullfunc_get(
                                              IEEE80211_STYPE_NULLFUNC |
                                              IEEE80211_FCTL_TODS);
        if (qos) {
-               __le16 qos = cpu_to_le16(7);
+               __le16 qoshdr = cpu_to_le16(7);
  
                BUILD_BUG_ON((IEEE80211_STYPE_QOS_NULLFUNC |
                              IEEE80211_STYPE_NULLFUNC) !=
                        cpu_to_le16(IEEE80211_STYPE_QOS_NULLFUNC);
                skb->priority = 7;
                skb_set_queue_mapping(skb, IEEE80211_AC_VO);
-               skb_put_data(skb, &qos, sizeof(qos));
+               skb_put_data(skb, &qoshdr, sizeof(qoshdr));
        }
  
        memcpy(nullfunc->addr1, ifmgd->bssid, ETH_ALEN);
diff --combined net/wireless/nl80211.c
index 8d763725498c15fc7474f5ca78802233800ee4c5,71a54ada377b699ae2a311af47f5b6ed776135ef..10ec0558979553169e28362b6ec16778d7d44bdd
@@@ -240,7 -240,63 +240,63 @@@ nl80211_ftm_responder_policy[NL80211_FT
                                             .len = U8_MAX },
  };
  
- static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ static const struct nla_policy
+ nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
+       [NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG },
+       [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 },
+       [NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] =
+               NLA_POLICY_MAX(NLA_U8, 15),
+       [NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 },
+       [NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION] =
+               NLA_POLICY_MAX(NLA_U8, 15),
+       [NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST] =
+               NLA_POLICY_MAX(NLA_U8, 15),
+       [NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES] = { .type = NLA_U8 },
+       [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI] = { .type = NLA_FLAG },
+       [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC] = { .type = NLA_FLAG },
+ };
+ static const struct nla_policy
+ nl80211_pmsr_req_data_policy[NL80211_PMSR_TYPE_MAX + 1] = {
+       [NL80211_PMSR_TYPE_FTM] =
+               NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX,
+                                 nl80211_pmsr_ftm_req_attr_policy),
+ };
+ static const struct nla_policy
+ nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = {
+       [NL80211_PMSR_REQ_ATTR_DATA] =
+               NLA_POLICY_NESTED(NL80211_PMSR_TYPE_MAX,
+                                 nl80211_pmsr_req_data_policy),
+       [NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG },
+ };
+ static const struct nla_policy
+ nl80211_psmr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
+       [NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR,
+       /*
+        * we could specify this again to be the top-level policy,
+        * but that would open us up to recursion problems ...
+        */
+       [NL80211_PMSR_PEER_ATTR_CHAN] = { .type = NLA_NESTED },
+       [NL80211_PMSR_PEER_ATTR_REQ] =
+               NLA_POLICY_NESTED(NL80211_PMSR_REQ_ATTR_MAX,
+                                 nl80211_pmsr_req_attr_policy),
+       [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT },
+ };
+ static const struct nla_policy
+ nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = {
+       [NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_REJECT },
+       [NL80211_PMSR_ATTR_REPORT_AP_TSF] = { .type = NLA_REJECT },
+       [NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_REJECT },
+       [NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_REJECT },
+       [NL80211_PMSR_ATTR_PEERS] =
+               NLA_POLICY_NESTED_ARRAY(NL80211_PMSR_PEER_ATTR_MAX,
+                                       nl80211_psmr_peer_attr_policy),
+ };
+ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
        [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
                                      .len = 20-1 },
                .type = NLA_NESTED,
                .validation_data = nl80211_ftm_responder_policy,
        },
+       [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1),
+       [NL80211_ATTR_PEER_MEASUREMENTS] =
+               NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX,
+                                 nl80211_pmsr_attr_policy),
  };
  
  /* policy for the key attributes */
@@@ -637,9 -697,9 +697,9 @@@ nl80211_packet_pattern_policy[MAX_NL802
        [NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 },
  };
  
static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
-                                    struct cfg80211_registered_device **rdev,
-                                    struct wireless_dev **wdev)
+ int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
+                             struct cfg80211_registered_device **rdev,
+                             struct wireless_dev **wdev)
  {
        int err;
  
  }
  
  /* message building helper */
static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
-                                  int flags, u8 cmd)
+ void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
+                    int flags, u8 cmd)
  {
        /* since there is no private header just add the generic one */
        return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
@@@ -1615,6 -1675,91 +1675,91 @@@ static int nl80211_add_commands_unsplit
        return -ENOBUFS;
  }
  
+ static int
+ nl80211_send_pmsr_ftm_capa(const struct cfg80211_pmsr_capabilities *cap,
+                          struct sk_buff *msg)
+ {
+       struct nlattr *ftm;
+       if (!cap->ftm.supported)
+               return 0;
+       ftm = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM);
+       if (!ftm)
+               return -ENOBUFS;
+       if (cap->ftm.asap && nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_ASAP))
+               return -ENOBUFS;
+       if (cap->ftm.non_asap &&
+           nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP))
+               return -ENOBUFS;
+       if (cap->ftm.request_lci &&
+           nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI))
+               return -ENOBUFS;
+       if (cap->ftm.request_civicloc &&
+           nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC))
+               return -ENOBUFS;
+       if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES,
+                       cap->ftm.preambles))
+               return -ENOBUFS;
+       if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS,
+                       cap->ftm.bandwidths))
+               return -ENOBUFS;
+       if (cap->ftm.max_bursts_exponent >= 0 &&
+           nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT,
+                       cap->ftm.max_bursts_exponent))
+               return -ENOBUFS;
+       if (cap->ftm.max_ftms_per_burst &&
+           nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST,
+                       cap->ftm.max_ftms_per_burst))
+               return -ENOBUFS;
+       nla_nest_end(msg, ftm);
+       return 0;
+ }
+ static int nl80211_send_pmsr_capa(struct cfg80211_registered_device *rdev,
+                                 struct sk_buff *msg)
+ {
+       const struct cfg80211_pmsr_capabilities *cap = rdev->wiphy.pmsr_capa;
+       struct nlattr *pmsr, *caps;
+       if (!cap)
+               return 0;
+       /*
+        * we don't need to clean up anything here since the caller
+        * will genlmsg_cancel() if we fail
+        */
+       pmsr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS);
+       if (!pmsr)
+               return -ENOBUFS;
+       if (nla_put_u32(msg, NL80211_PMSR_ATTR_MAX_PEERS, cap->max_peers))
+               return -ENOBUFS;
+       if (cap->report_ap_tsf &&
+           nla_put_flag(msg, NL80211_PMSR_ATTR_REPORT_AP_TSF))
+               return -ENOBUFS;
+       if (cap->randomize_mac_addr &&
+           nla_put_flag(msg, NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR))
+               return -ENOBUFS;
+       caps = nla_nest_start(msg, NL80211_PMSR_ATTR_TYPE_CAPA);
+       if (!caps)
+               return -ENOBUFS;
+       if (nl80211_send_pmsr_ftm_capa(cap, msg))
+               return -ENOBUFS;
+       nla_nest_end(msg, caps);
+       nla_nest_end(msg, pmsr);
+       return 0;
+ }
  struct nl80211_dump_wiphy_state {
        s64 filter_wiphy;
        long start;
@@@ -1706,6 -1851,7 +1851,7 @@@ static int nl80211_send_wiphy(struct cf
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 1:
                if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
                            sizeof(u32) * rdev->wiphy.n_cipher_suites,
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 2:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
                                        rdev->wiphy.interface_modes))
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 3:
                nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
                if (!nl_bands)
                                state->chan_start++;
                                if (state->split)
                                        break;
+                               /* fall through */
                        default:
                                /* add frequencies */
                                nl_freqs = nla_nest_start(
                        state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 4:
                nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
                if (!nl_cmds)
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 5:
                if (rdev->ops->remain_on_channel &&
                    (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 6:
  #ifdef CONFIG_PM
                if (nl80211_send_wowlan(msg, rdev, state->split))
  #else
                state->split_start++;
  #endif
+               /* fall through */
        case 7:
                if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
                                        rdev->wiphy.software_iftypes))
                state->split_start++;
                if (state->split)
                        break;
+               /* fall through */
        case 8:
                if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
                    nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
                                goto nla_put_failure;
                }
  
+               state->split_start++;
+               break;
+       case 14:
+               if (nl80211_send_pmsr_capa(rdev, msg))
+                       goto nla_put_failure;
                /* done */
                state->split_start = 0;
                break;
@@@ -2318,9 -2478,9 +2478,9 @@@ static bool nl80211_can_set_dev_channel
                wdev->iftype == NL80211_IFTYPE_P2P_GO;
  }
  
static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
-                                struct genl_info *info,
-                                struct cfg80211_chan_def *chandef)
+ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
+                         struct genl_info *info,
+                         struct cfg80211_chan_def *chandef)
  {
        struct netlink_ext_ack *extack = info->extack;
        struct nlattr **attrs = info->attrs;
@@@ -2794,12 -2954,6 +2954,6 @@@ static int nl80211_set_wiphy(struct sk_
        return 0;
  }
  
- static inline u64 wdev_id(struct wireless_dev *wdev)
- {
-       return (u64)wdev->identifier |
-              ((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
- }
  static int nl80211_send_chandef(struct sk_buff *msg,
                                const struct cfg80211_chan_def *chandef)
  {
  
  static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
                              struct cfg80211_registered_device *rdev,
-                             struct wireless_dev *wdev, bool removal)
+                             struct wireless_dev *wdev,
+                             enum nl80211_commands cmd)
  {
        struct net_device *dev = wdev->netdev;
-       u8 cmd = NL80211_CMD_NEW_INTERFACE;
        void *hdr;
  
-       if (removal)
-               cmd = NL80211_CMD_DEL_INTERFACE;
+       WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE &&
+               cmd != NL80211_CMD_DEL_INTERFACE &&
+               cmd != NL80211_CMD_SET_INTERFACE);
  
        hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
        if (!hdr)
@@@ -2987,7 -3142,8 +3142,8 @@@ static int nl80211_dump_interface(struc
                        }
                        if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
                                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                              rdev, wdev, false) < 0) {
+                                              rdev, wdev,
+                                              NL80211_CMD_NEW_INTERFACE) < 0) {
                                goto out;
                        }
                        if_idx++;
@@@ -3017,7 -3173,7 +3173,7 @@@ static int nl80211_get_interface(struc
                return -ENOMEM;
  
        if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
-                              rdev, wdev, false) < 0) {
+                              rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@@ -3207,6 -3363,12 +3363,12 @@@ static int nl80211_set_interface(struc
        if (!err && params.use_4addr != -1)
                dev->ieee80211_ptr->use_4addr = params.use_4addr;
  
+       if (change && !err) {
+               struct wireless_dev *wdev = dev->ieee80211_ptr;
+               nl80211_notify_iface(rdev, wdev, NL80211_CMD_SET_INTERFACE);
+       }
        return err;
  }
  
@@@ -3298,7 -3460,7 +3460,7 @@@ static int nl80211_new_interface(struc
        }
  
        if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
-                              rdev, wdev, false) < 0) {
+                              rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@@ -4521,8 -4683,7 +4683,7 @@@ static int parse_station_flags(struct g
        return 0;
  }
  
- static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
-                                int attr)
+ bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
  {
        struct nlattr *rate;
        u32 bitrate;
@@@ -4731,6 -4892,7 +4892,7 @@@ static int nl80211_send_station(struct 
        PUT_SINFO(LOCAL_PM, local_pm, u32);
        PUT_SINFO(PEER_PM, peer_pm, u32);
        PUT_SINFO(NONPEER_PM, nonpeer_pm, u32);
+       PUT_SINFO(CONNECTED_TO_GATE, connected_to_gate, u8);
  
        if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
                bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
@@@ -6122,7 -6284,9 +6284,9 @@@ static int nl80211_get_mesh_config(stru
            nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
                        cur_params.dot11MeshAwakeWindowDuration) ||
            nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
-                       cur_params.plink_timeout))
+                       cur_params.plink_timeout) ||
+           nla_put_u8(msg, NL80211_MESHCONF_CONNECTED_TO_GATE,
+                      cur_params.dot11MeshConnectedToMeshGate))
                goto nla_put_failure;
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
@@@ -6179,6 -6343,7 +6343,7 @@@ nl80211_meshconf_params_policy[NL80211_
                                 NL80211_MESH_POWER_MAX),
        [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
        [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
+       [NL80211_MESHCONF_CONNECTED_TO_GATE] = NLA_POLICY_RANGE(NLA_U8, 0, 1),
  };
  
  static const struct nla_policy
@@@ -6290,6 -6455,9 +6455,9 @@@ do {                                                                    
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, mask,
                                  NL80211_MESHCONF_RSSI_THRESHOLD,
                                  nla_get_s32);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConnectedToMeshGate, mask,
+                                 NL80211_MESHCONF_CONNECTED_TO_GATE,
+                                 nla_get_u8);
        /*
         * Check HT operation mode based on
         * IEEE 802.11-2016 9.4.2.57 HT Operation element.
@@@ -6855,8 -7023,8 +7023,8 @@@ static int parse_bss_select(struct nlat
        return 0;
  }
  
static int nl80211_parse_random_mac(struct nlattr **attrs,
-                                   u8 *mac_addr, u8 *mac_addr_mask)
+ int nl80211_parse_random_mac(struct nlattr **attrs,
+                            u8 *mac_addr, u8 *mac_addr_mask)
  {
        int i;
  
@@@ -7822,6 -7990,60 +7990,60 @@@ static int nl80211_start_radar_detectio
        return err;
  }
  
+ static int nl80211_notify_radar_detection(struct sk_buff *skb,
+                                         struct genl_info *info)
+ {
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_chan_def chandef;
+       enum nl80211_dfs_regions dfs_region;
+       int err;
+       dfs_region = reg_get_dfs_region(wiphy);
+       if (dfs_region == NL80211_DFS_UNSET) {
+               GENL_SET_ERR_MSG(info,
+                                "DFS Region is not set. Unexpected Radar indication");
+               return -EINVAL;
+       }
+       err = nl80211_parse_chandef(rdev, info, &chandef);
+       if (err) {
+               GENL_SET_ERR_MSG(info, "Unable to extract chandef info");
+               return err;
+       }
+       err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
+       if (err < 0) {
+               GENL_SET_ERR_MSG(info, "chandef is invalid");
+               return err;
+       }
+       if (err == 0) {
+               GENL_SET_ERR_MSG(info,
+                                "Unexpected Radar indication for chandef/iftype");
+               return -EINVAL;
+       }
+       /* Do not process this notification if radar is already detected
+        * by kernel on this channel, and return success.
+        */
+       if (chandef.chan->dfs_state == NL80211_DFS_UNAVAILABLE)
+               return 0;
+       cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_UNAVAILABLE);
+       cfg80211_sched_dfs_chan_update(rdev);
+       memcpy(&rdev->radar_chandef, &chandef, sizeof(chandef));
+       /* Propagate this notification to other radios as well */
+       queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
+       return 0;
+ }
  static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
  {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        }
  
        memset(&params, 0, sizeof(params));
 +      params.beacon_csa.ftm_responder = -1;
  
        if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
            !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
@@@ -13899,6 -14120,22 +14121,22 @@@ static const struct genl_ops nl80211_op
                .internal_flags = NL80211_FLAG_NEED_NETDEV |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_PEER_MEASUREMENT_START,
+               .doit = nl80211_pmsr_start,
+               .policy = nl80211_policy,
+               .flags = GENL_UNS_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_NOTIFY_RADAR,
+               .doit = nl80211_notify_radar_detection,
+               .policy = nl80211_policy,
+               .flags = GENL_UNS_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
  };
  
  static struct genl_family nl80211_fam __ro_after_init = {
@@@ -13946,15 -14183,11 +14184,11 @@@ void nl80211_notify_iface(struct cfg802
  {
        struct sk_buff *msg;
  
-       WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE &&
-               cmd != NL80211_CMD_DEL_INTERFACE);
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
  
-       if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev,
-                              cmd == NL80211_CMD_DEL_INTERFACE) < 0) {
+       if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, cmd) < 0) {
                nlmsg_free(msg);
                return;
        }
@@@ -14573,7 -14806,8 +14807,8 @@@ void nl80211_send_ibss_bssid(struct cfg
  }
  
  void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
-                                       const u8* ie, u8 ie_len, gfp_t gfp)
+                                       const u8 *ie, u8 ie_len,
+                                       int sig_dbm, gfp_t gfp)
  {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
            nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
            (ie_len && ie &&
-            nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
+            nla_put(msg, NL80211_ATTR_IE, ie_len, ie)) ||
+           (sig_dbm &&
+            nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)))
                goto nla_put_failure;
  
        genlmsg_end(msg, hdr);
@@@ -15882,6 -16118,8 +16119,8 @@@ static int nl80211_netlink_notify(struc
                        } else if (wdev->conn_owner_nlportid == notify->portid) {
                                schedule_work(&wdev->disconnect_wk);
                        }
+                       cfg80211_release_pmsr(wdev, notify->portid);
                }
  
                spin_lock_bh(&rdev->beacon_registrations_lock);
diff --combined net/wireless/util.c
index d473bd135da8babc52329982bafc518ffeaf52fb,06a0842368412fff590d4826f3b2b319eb7f1489..cd48cdd582c070e84375d73d6bcc6d601f3d6996
@@@ -1421,8 -1421,6 +1421,8 @@@ size_t ieee80211_ie_split_ric(const u8 
                                                          ies[pos + ext],
                                                          ext == 2))
                                        pos = skip_ie(ies, ielen, pos);
 +                              else
 +                                      break;
                        }
                } else {
                        pos = skip_ie(ies, ielen, pos);
@@@ -2015,33 -2013,32 +2015,32 @@@ int ieee80211_get_vht_max_nss(struct ie
        case IEEE80211_VHT_CHANWIDTH_160MHZ:
                if (supp_width == 0 &&
                    (ext_nss_bw == 1 || ext_nss_bw == 2))
-                       return DIV_ROUND_UP(max_vht_nss, 2);
+                       return max_vht_nss / 2;
                if (supp_width == 0 &&
                    ext_nss_bw == 3)
-                       return DIV_ROUND_UP(3 * max_vht_nss, 4);
+                       return (3 * max_vht_nss) / 4;
                if (supp_width == 1 &&
                    ext_nss_bw == 3)
                        return 2 * max_vht_nss;
                break;
        case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
-               if (supp_width == 0 &&
-                   (ext_nss_bw == 1 || ext_nss_bw == 2))
+               if (supp_width == 0 && ext_nss_bw == 1)
                        return 0; /* not possible */
                if (supp_width == 0 &&
                    ext_nss_bw == 2)
-                       return DIV_ROUND_UP(max_vht_nss, 2);
+                       return max_vht_nss / 2;
                if (supp_width == 0 &&
                    ext_nss_bw == 3)
-                       return DIV_ROUND_UP(3 * max_vht_nss, 4);
+                       return (3 * max_vht_nss) / 4;
                if (supp_width == 1 &&
                    ext_nss_bw == 0)
                        return 0; /* not possible */
                if (supp_width == 1 &&
                    ext_nss_bw == 1)
-                       return DIV_ROUND_UP(max_vht_nss, 2);
+                       return max_vht_nss / 2;
                if (supp_width == 1 &&
                    ext_nss_bw == 2)
-                       return DIV_ROUND_UP(3 * max_vht_nss, 4);
+                       return (3 * max_vht_nss) / 4;
                break;
        }