nl80211: allow using wdev identifiers to get scan results
[firefly-linux-kernel-4.4.55.git] / net / wireless / nl80211.c
index f187a920ec71c92e2f48cab164d9d997f437093c..8c8a57937b224093f8086ca830c96800d6f516ea 100644 (file)
@@ -371,6 +371,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
        [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
        [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
+       [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
+       [NL80211_ATTR_VHT_CAPABILITY_MASK] = {
+               .len = NL80211_VHT_CAPABILITY_LEN,
+       },
+       [NL80211_ATTR_MDID] = { .type = NLA_U16 },
+       [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
+                                 .len = IEEE80211_MAX_DATA_LEN },
 };
 
 /* policy for the key attributes */
@@ -440,62 +447,69 @@ nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
        [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
 };
 
-/* ifidx get helper */
-static int nl80211_get_ifidx(struct netlink_callback *cb)
+static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
+                                    struct netlink_callback *cb,
+                                    struct cfg80211_registered_device **rdev,
+                                    struct wireless_dev **wdev)
 {
-       int res;
-
-       res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
-                         nl80211_fam.attrbuf, nl80211_fam.maxattr,
-                         nl80211_policy);
-       if (res)
-               return res;
-
-       if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
-               return -EINVAL;
+       int err;
 
-       res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
-       if (!res)
-               return -EINVAL;
-       return res;
-}
+       rtnl_lock();
+       mutex_lock(&cfg80211_mutex);
 
-static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
-                                      struct netlink_callback *cb,
-                                      struct cfg80211_registered_device **rdev,
-                                      struct net_device **dev)
-{
-       int ifidx = cb->args[0];
-       int err;
+       if (!cb->args[0]) {
+               err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+                                 nl80211_fam.attrbuf, nl80211_fam.maxattr,
+                                 nl80211_policy);
+               if (err)
+                       goto out_unlock;
 
-       if (!ifidx)
-               ifidx = nl80211_get_ifidx(cb);
-       if (ifidx < 0)
-               return ifidx;
+               *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk),
+                                                  nl80211_fam.attrbuf);
+               if (IS_ERR(*wdev)) {
+                       err = PTR_ERR(*wdev);
+                       goto out_unlock;
+               }
+               *rdev = wiphy_to_dev((*wdev)->wiphy);
+               cb->args[0] = (*rdev)->wiphy_idx;
+               cb->args[1] = (*wdev)->identifier;
+       } else {
+               struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0]);
+               struct wireless_dev *tmp;
 
-       cb->args[0] = ifidx;
+               if (!wiphy) {
+                       err = -ENODEV;
+                       goto out_unlock;
+               }
+               *rdev = wiphy_to_dev(wiphy);
+               *wdev = NULL;
 
-       rtnl_lock();
+               mutex_lock(&(*rdev)->devlist_mtx);
+               list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
+                       if (tmp->identifier == cb->args[1]) {
+                               *wdev = tmp;
+                               break;
+                       }
+               }
+               mutex_unlock(&(*rdev)->devlist_mtx);
 
-       *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
-       if (!*dev) {
-               err = -ENODEV;
-               goto out_rtnl;
+               if (!*wdev) {
+                       err = -ENODEV;
+                       goto out_unlock;
+               }
        }
 
-       *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
-       if (IS_ERR(*rdev)) {
-               err = PTR_ERR(*rdev);
-               goto out_rtnl;
-       }
+       cfg80211_lock_rdev(*rdev);
 
+       mutex_unlock(&cfg80211_mutex);
        return 0;
- out_rtnl:
+ out_unlock:
+       mutex_unlock(&cfg80211_mutex);
        rtnl_unlock();
        return err;
 }
 
-static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
+static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
 {
        cfg80211_unlock_rdev(rdev);
        rtnl_unlock();
@@ -540,7 +554,8 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
 }
 
 static int nl80211_msg_put_channel(struct sk_buff *msg,
-                                  struct ieee80211_channel *chan)
+                                  struct ieee80211_channel *chan,
+                                  bool large)
 {
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
                        chan->center_freq))
@@ -555,9 +570,37 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
        if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
            nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
                goto nla_put_failure;
-       if ((chan->flags & IEEE80211_CHAN_RADAR) &&
-           nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
-               goto nla_put_failure;
+       if (chan->flags & IEEE80211_CHAN_RADAR) {
+               if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
+                       goto nla_put_failure;
+               if (large) {
+                       u32 time;
+
+                       time = elapsed_jiffies_msecs(chan->dfs_state_entered);
+
+                       if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
+                                       chan->dfs_state))
+                               goto nla_put_failure;
+                       if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
+                                       time))
+                               goto nla_put_failure;
+               }
+       }
+
+       if (large) {
+               if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
+                       goto nla_put_failure;
+       }
 
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
                        DBM_TO_MBM(chan->max_power)))
@@ -833,7 +876,8 @@ nla_put_failure:
 }
 
 static int nl80211_put_iface_combinations(struct wiphy *wiphy,
-                                         struct sk_buff *msg)
+                                         struct sk_buff *msg,
+                                         bool large)
 {
        struct nlattr *nl_combis;
        int i, j;
@@ -882,6 +926,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,
                    nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
                                c->max_interfaces))
                        goto nla_put_failure;
+               if (large &&
+                   nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+                               c->radar_detect_widths))
+                       goto nla_put_failure;
 
                nla_nest_end(msg, nl_combi);
        }
@@ -894,8 +942,49 @@ nla_put_failure:
 }
 
 #ifdef CONFIG_PM
+static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
+                                       struct sk_buff *msg)
+{
+       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
+       struct nlattr *nl_tcp;
+
+       if (!tcp)
+               return 0;
+
+       nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
+       if (!nl_tcp)
+               return -ENOBUFS;
+
+       if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+                       tcp->data_payload_max))
+               return -ENOBUFS;
+
+       if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+                       tcp->data_payload_max))
+               return -ENOBUFS;
+
+       if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ))
+               return -ENOBUFS;
+
+       if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+                               sizeof(*tcp->tok), tcp->tok))
+               return -ENOBUFS;
+
+       if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
+                       tcp->data_interval_max))
+               return -ENOBUFS;
+
+       if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+                       tcp->wake_payload_max))
+               return -ENOBUFS;
+
+       nla_nest_end(msg, nl_tcp);
+       return 0;
+}
+
 static int nl80211_send_wowlan(struct sk_buff *msg,
-                              struct cfg80211_registered_device *dev)
+                              struct cfg80211_registered_device *dev,
+                              bool large)
 {
        struct nlattr *nl_wowlan;
 
@@ -937,6 +1026,9 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
                        return -ENOBUFS;
        }
 
+       if (large && nl80211_send_wowlan_tcp_caps(dev, msg))
+               return -ENOBUFS;
+
        nla_nest_end(msg, nl_wowlan);
 
        return 0;
@@ -1074,6 +1166,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
        const struct ieee80211_txrx_stypes *mgmt_stypes =
                                dev->wiphy.mgmt_stypes;
        long start = 0, start_chan = 0, start_band = 0;
+       u32 features;
 
        hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
        if (!hdr)
@@ -1231,7 +1324,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
 
                                        chan = &sband->channels[i];
 
-                                       if (nl80211_msg_put_channel(msg, chan))
+                                       if (nl80211_msg_put_channel(msg, chan,
+                                                                   split))
                                                goto nla_put_failure;
 
                                        nla_nest_end(msg, nl_freq);
@@ -1372,7 +1466,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                        break;
        case 6:
 #ifdef CONFIG_PM
-               if (nl80211_send_wowlan(msg, dev))
+               if (nl80211_send_wowlan(msg, dev, split))
                        goto nla_put_failure;
                (*split_start)++;
                if (split)
@@ -1385,7 +1479,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                                        dev->wiphy.software_iftypes))
                        goto nla_put_failure;
 
-               if (nl80211_put_iface_combinations(&dev->wiphy, msg))
+               if (nl80211_put_iface_combinations(&dev->wiphy, msg, split))
                        goto nla_put_failure;
 
                (*split_start)++;
@@ -1397,8 +1491,15 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                                dev->wiphy.ap_sme_capa))
                        goto nla_put_failure;
 
-               if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS,
-                               dev->wiphy.features))
+               features = dev->wiphy.features;
+               /*
+                * We can only add the per-channel limit information if the
+                * dump is split, otherwise it makes it too big. Therefore
+                * only advertise it in that case.
+                */
+               if (split)
+                       features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
+               if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
                        goto nla_put_failure;
 
                if (dev->wiphy.ht_capa_mod_mask &&
@@ -1426,7 +1527,20 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                (*split_start)++;
                break;
        case 9:
-               /* placeholder */
+               if (dev->wiphy.extended_capabilities &&
+                   (nla_put(msg, NL80211_ATTR_EXT_CAPA,
+                            dev->wiphy.extended_capabilities_len,
+                            dev->wiphy.extended_capabilities) ||
+                    nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
+                            dev->wiphy.extended_capabilities_len,
+                            dev->wiphy.extended_capabilities_mask)))
+                       goto nla_put_failure;
+
+               if (dev->wiphy.vht_capa_mod_mask &&
+                   nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
+                           sizeof(*dev->wiphy.vht_capa_mod_mask),
+                           dev->wiphy.vht_capa_mod_mask))
+                       goto nla_put_failure;
 
                /* done */
                *split_start = 0;
@@ -3418,15 +3532,20 @@ static int nl80211_dump_station(struct sk_buff *skb,
 {
        struct station_info sinfo;
        struct cfg80211_registered_device *dev;
-       struct net_device *netdev;
+       struct wireless_dev *wdev;
        u8 mac_addr[ETH_ALEN];
-       int sta_idx = cb->args[1];
+       int sta_idx = cb->args[2];
        int err;
 
-       err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
+       err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
        if (err)
                return err;
 
+       if (!wdev->netdev) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
        if (!dev->ops->dump_station) {
                err = -EOPNOTSUPP;
                goto out_err;
@@ -3434,7 +3553,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
 
        while (1) {
                memset(&sinfo, 0, sizeof(sinfo));
-               err = rdev_dump_station(dev, netdev, sta_idx,
+               err = rdev_dump_station(dev, wdev->netdev, sta_idx,
                                        mac_addr, &sinfo);
                if (err == -ENOENT)
                        break;
@@ -3444,7 +3563,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
                if (nl80211_send_station(skb,
                                NETLINK_CB(cb->skb).portid,
                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                               dev, netdev, mac_addr,
+                               dev, wdev->netdev, mac_addr,
                                &sinfo) < 0)
                        goto out;
 
@@ -3453,10 +3572,10 @@ static int nl80211_dump_station(struct sk_buff *skb,
 
 
  out:
-       cb->args[1] = sta_idx;
+       cb->args[2] = sta_idx;
        err = skb->len;
  out_err:
-       nl80211_finish_netdev_dump(dev);
+       nl80211_finish_wdev_dump(dev);
 
        return err;
 }
@@ -3510,8 +3629,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
        BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
 
        switch (statype) {
-       case CFG80211_STA_MESH_PEER_NONSEC:
-       case CFG80211_STA_MESH_PEER_SECURE:
+       case CFG80211_STA_MESH_PEER_KERNEL:
+       case CFG80211_STA_MESH_PEER_USER:
                /*
                 * No ignoring the TDLS flag here -- the userspace mesh
                 * code doesn't have the bug of including TDLS in the
@@ -3613,11 +3732,11 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
        case CFG80211_STA_TDLS_PEER_ACTIVE:
                /* reject any changes */
                return -EINVAL;
-       case CFG80211_STA_MESH_PEER_NONSEC:
+       case CFG80211_STA_MESH_PEER_KERNEL:
                if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
                        return -EINVAL;
                break;
-       case CFG80211_STA_MESH_PEER_SECURE:
+       case CFG80211_STA_MESH_PEER_USER:
                if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
                        return -EINVAL;
                break;
@@ -3944,6 +4063,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
                /* ignore uAPSD data */
                params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
 
@@ -4059,13 +4179,13 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
 {
        struct mpath_info pinfo;
        struct cfg80211_registered_device *dev;
-       struct net_device *netdev;
+       struct wireless_dev *wdev;
        u8 dst[ETH_ALEN];
        u8 next_hop[ETH_ALEN];
-       int path_idx = cb->args[1];
+       int path_idx = cb->args[2];
        int err;
 
-       err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
+       err = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
        if (err)
                return err;
 
@@ -4074,14 +4194,14 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
                goto out_err;
        }
 
-       if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+       if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
                err = -EOPNOTSUPP;
                goto out_err;
        }
 
        while (1) {
-               err = rdev_dump_mpath(dev, netdev, path_idx, dst, next_hop,
-                                     &pinfo);
+               err = rdev_dump_mpath(dev, wdev->netdev, path_idx, dst,
+                                     next_hop, &pinfo);
                if (err == -ENOENT)
                        break;
                if (err)
@@ -4089,7 +4209,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
 
                if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
                                       cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                      netdev, dst, next_hop,
+                                      wdev->netdev, dst, next_hop,
                                       &pinfo) < 0)
                        goto out;
 
@@ -4098,10 +4218,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
 
 
  out:
-       cb->args[1] = path_idx;
+       cb->args[2] = path_idx;
        err = skb->len;
  out_err:
-       nl80211_finish_netdev_dump(dev);
+       nl80211_finish_wdev_dump(dev);
        return err;
 }
 
@@ -4510,6 +4630,7 @@ static const struct nla_policy
        [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
+       [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
        [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
                                    .len = IEEE80211_MAX_DATA_LEN },
        [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
@@ -4648,6 +4769,7 @@ do {                                                                          \
 static int nl80211_parse_mesh_setup(struct genl_info *info,
                                     struct mesh_setup *setup)
 {
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
 
        if (!info->attrs[NL80211_ATTR_MESH_SETUP])
@@ -4684,8 +4806,14 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
                setup->ie = nla_data(ieattr);
                setup->ie_len = nla_len(ieattr);
        }
+       if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] &&
+           !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM))
+               return -EINVAL;
+       setup->user_mpm = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_MPM]);
        setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
        setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
+       if (setup->is_secure)
+               setup->user_mpm = true;
 
        return 0;
 }
@@ -5436,9 +5564,13 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
 
        genl_dump_check_consistent(cb, hdr, &nl80211_fam);
 
-       if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation) ||
+       if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation))
+               goto nla_put_failure;
+       if (wdev->netdev &&
            nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex))
                goto nla_put_failure;
+       if (nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
+               goto nla_put_failure;
 
        bss = nla_nest_start(msg, NL80211_ATTR_BSS);
        if (!bss)
@@ -5518,22 +5650,18 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
        return -EMSGSIZE;
 }
 
-static int nl80211_dump_scan(struct sk_buff *skb,
-                            struct netlink_callback *cb)
+static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct cfg80211_registered_device *rdev;
-       struct net_device *dev;
        struct cfg80211_internal_bss *scan;
        struct wireless_dev *wdev;
-       int start = cb->args[1], idx = 0;
+       int start = cb->args[2], idx = 0;
        int err;
 
-       err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
+       err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
        if (err)
                return err;
 
-       wdev = dev->ieee80211_ptr;
-
        wdev_lock(wdev);
        spin_lock_bh(&rdev->bss_lock);
        cfg80211_bss_expire(rdev);
@@ -5554,8 +5682,8 @@ static int nl80211_dump_scan(struct sk_buff *skb,
        spin_unlock_bh(&rdev->bss_lock);
        wdev_unlock(wdev);
 
-       cb->args[1] = idx;
-       nl80211_finish_netdev_dump(rdev);
+       cb->args[2] = idx;
+       nl80211_finish_wdev_dump(rdev);
 
        return skb->len;
 }
@@ -5624,14 +5752,19 @@ static int nl80211_dump_survey(struct sk_buff *skb,
 {
        struct survey_info survey;
        struct cfg80211_registered_device *dev;
-       struct net_device *netdev;
-       int survey_idx = cb->args[1];
+       struct wireless_dev *wdev;
+       int survey_idx = cb->args[2];
        int res;
 
-       res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
+       res = nl80211_prepare_wdev_dump(skb, cb, &dev, &wdev);
        if (res)
                return res;
 
+       if (!wdev->netdev) {
+               res = -EINVAL;
+               goto out_err;
+       }
+
        if (!dev->ops->dump_survey) {
                res = -EOPNOTSUPP;
                goto out_err;
@@ -5640,7 +5773,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
        while (1) {
                struct ieee80211_channel *chan;
 
-               res = rdev_dump_survey(dev, netdev, survey_idx, &survey);
+               res = rdev_dump_survey(dev, wdev->netdev, survey_idx, &survey);
                if (res == -ENOENT)
                        break;
                if (res)
@@ -5662,17 +5795,16 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                if (nl80211_send_survey(skb,
                                NETLINK_CB(cb->skb).portid,
                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                               netdev,
-                               &survey) < 0)
+                               wdev->netdev, &survey) < 0)
                        goto out;
                survey_idx++;
        }
 
  out:
-       cb->args[1] = survey_idx;
+       cb->args[2] = survey_idx;
        res = skb->len;
  out_err:
-       nl80211_finish_netdev_dump(dev);
+       nl80211_finish_wdev_dump(dev);
        return res;
 }
 
@@ -5880,14 +6012,10 @@ static int nl80211_associate(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 cfg80211_crypto_settings crypto;
        struct ieee80211_channel *chan;
-       const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
-       int err, ssid_len, ie_len = 0;
-       bool use_mfp = false;
-       u32 flags = 0;
-       struct ieee80211_ht_cap *ht_capa = NULL;
-       struct ieee80211_ht_cap *ht_capa_mask = NULL;
+       struct cfg80211_assoc_request req = {};
+       const u8 *bssid, *ssid;
+       int err, ssid_len = 0;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
@@ -5915,41 +6043,58 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
        if (info->attrs[NL80211_ATTR_IE]) {
-               ie = nla_data(info->attrs[NL80211_ATTR_IE]);
-               ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
        if (info->attrs[NL80211_ATTR_USE_MFP]) {
                enum nl80211_mfp mfp =
                        nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
                if (mfp == NL80211_MFP_REQUIRED)
-                       use_mfp = true;
+                       req.use_mfp = true;
                else if (mfp != NL80211_MFP_NO)
                        return -EINVAL;
        }
 
        if (info->attrs[NL80211_ATTR_PREV_BSSID])
-               prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+               req.prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
 
        if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
-               flags |= ASSOC_REQ_DISABLE_HT;
+               req.flags |= ASSOC_REQ_DISABLE_HT;
 
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
-               ht_capa_mask =
-                       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
+               memcpy(&req.ht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+                      sizeof(req.ht_capa_mask));
 
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
-               if (!ht_capa_mask)
+               if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+                       return -EINVAL;
+               memcpy(&req.ht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+                      sizeof(req.ht_capa));
+       }
+
+       if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+               req.flags |= ASSOC_REQ_DISABLE_VHT;
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+               memcpy(&req.vht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+                      sizeof(req.vht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
                        return -EINVAL;
-               ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+               memcpy(&req.vht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+                      sizeof(req.vht_capa));
        }
 
-       err = nl80211_crypto_settings(rdev, info, &crypto, 1);
+       err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
        if (!err)
-               err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
-                                         ssid, ssid_len, ie, ie_len, use_mfp,
-                                         &crypto, flags, ht_capa,
-                                         ht_capa_mask);
+               err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
+                                         ssid, ssid_len, &req);
 
        return err;
 }
@@ -6529,6 +6674,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                       sizeof(connect.ht_capa));
        }
 
+       if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+               connect.flags |= ASSOC_REQ_DISABLE_VHT;
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+               memcpy(&connect.vht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+                      sizeof(connect.vht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) {
+                       kfree(connkeys);
+                       return -EINVAL;
+               }
+               memcpy(&connect.vht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+                      sizeof(connect.vht_capa));
+       }
+
        err = cfg80211_connect(rdev, dev, &connect, connkeys);
        if (err)
                kfree(connkeys);
@@ -7302,6 +7465,9 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
                        return err;
        }
 
+       if (setup.user_mpm)
+               cfg.auto_open_plinks = false;
+
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
                err = nl80211_parse_chandef(rdev, info, &setup.chandef);
                if (err)
@@ -7501,7 +7667,8 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
                return -EINVAL;
 
        if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
-                       rdev->wiphy.wowlan.tcp->data_interval_max)
+                       rdev->wiphy.wowlan.tcp->data_interval_max ||
+           nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
                return -EINVAL;
 
        wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
@@ -8024,6 +8191,27 @@ static int nl80211_get_protocol_features(struct sk_buff *skb,
        return -ENOBUFS;
 }
 
+static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct cfg80211_update_ft_ies_params ft_params;
+       struct net_device *dev = info->user_ptr[1];
+
+       if (!rdev->ops->update_ft_ies)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_MDID] ||
+           !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       memset(&ft_params, 0, sizeof(ft_params));
+       ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
+       ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+       ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+
+       return rdev_update_ft_ies(rdev, dev, &ft_params);
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -8705,6 +8893,14 @@ static struct genl_ops nl80211_ops[] = {
                .doit = nl80211_get_protocol_features,
                .policy = nl80211_policy,
        },
+       {
+               .cmd = NL80211_CMD_UPDATE_FT_IES,
+               .doit = nl80211_update_ft_ies,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -9057,21 +9253,31 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                                NL80211_CMD_DISASSOCIATE, gfp);
 }
 
-void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
-                               struct net_device *netdev, const u8 *buf,
-                               size_t len, gfp_t gfp)
+void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
+                                size_t len)
 {
-       nl80211_send_mlme_event(rdev, netdev, buf, len,
-                               NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_send_unprot_deauth(dev);
+       nl80211_send_mlme_event(rdev, dev, buf, len,
+                               NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
 }
+EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
 
-void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
-                                 struct net_device *netdev, const u8 *buf,
-                                 size_t len, gfp_t gfp)
+void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
+                                  size_t len)
 {
-       nl80211_send_mlme_event(rdev, netdev, buf, len,
-                               NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_send_unprot_disassoc(dev);
+       nl80211_send_mlme_event(rdev, dev, buf, len,
+                               NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
 }
+EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
 
 static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev, int cmd,
@@ -9274,14 +9480,19 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
-               struct net_device *netdev,
-               const u8 *macaddr, const u8* ie, u8 ie_len,
-               gfp_t gfp)
+void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
+                                       const u8* ie, u8 ie_len, gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
+               return;
+
+       trace_cfg80211_notify_new_peer_candidate(dev, addr);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9293,8 +9504,8 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
-           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr) ||
+           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)))
                goto nla_put_failure;
@@ -9309,6 +9520,7 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
 
 void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
                                 struct net_device *netdev, const u8 *addr,
@@ -9377,7 +9589,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_before))
+       if (nl80211_msg_put_channel(msg, channel_before, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -9385,7 +9597,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_after))
+       if (nl80211_msg_put_channel(msg, channel_after, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -9447,31 +9659,42 @@ static void nl80211_send_remain_on_chan_event(
        nlmsg_free(msg);
 }
 
-void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
-                                   struct wireless_dev *wdev, u64 cookie,
-                                   struct ieee80211_channel *chan,
-                                   unsigned int duration, gfp_t gfp)
+void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
+                              struct ieee80211_channel *chan,
+                              unsigned int duration, gfp_t gfp)
 {
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
        nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
                                          rdev, wdev, cookie, chan,
                                          duration, gfp);
 }
+EXPORT_SYMBOL(cfg80211_ready_on_channel);
 
-void nl80211_send_remain_on_channel_cancel(
-       struct cfg80211_registered_device *rdev,
-       struct wireless_dev *wdev,
-       u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)
+void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
+                                       struct ieee80211_channel *chan,
+                                       gfp_t gfp)
 {
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
        nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
                                          rdev, wdev, cookie, chan, 0, gfp);
 }
+EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
 
-void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
-                           struct net_device *dev, const u8 *mac_addr,
-                           struct station_info *sinfo, gfp_t gfp)
+void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
+                     struct station_info *sinfo, gfp_t gfp)
 {
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
 
+       trace_cfg80211_new_sta(dev, mac_addr, sinfo);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9485,14 +9708,17 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
 }
+EXPORT_SYMBOL(cfg80211_new_sta);
 
-void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
-                               struct net_device *dev, const u8 *mac_addr,
-                               gfp_t gfp)
+void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
 {
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
+       trace_cfg80211_del_sta(dev, mac_addr);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9517,12 +9743,14 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_del_sta);
 
-void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
-                                   struct net_device *dev, const u8 *mac_addr,
-                                   enum nl80211_connect_failed_reason reason,
-                                   gfp_t gfp)
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+                         enum nl80211_connect_failed_reason reason,
+                         gfp_t gfp)
 {
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -9551,6 +9779,7 @@ void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_conn_failed);
 
 static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
                                       const u8 *addr, gfp_t gfp)
@@ -9595,19 +9824,47 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
        return true;
 }
 
-bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
+                               const u8 *addr, gfp_t gfp)
 {
-       return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
-                                         addr, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       bool ret;
+
+       trace_cfg80211_rx_spurious_frame(dev, addr);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
+               trace_cfg80211_return_bool(false);
+               return false;
+       }
+       ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
+                                        addr, gfp);
+       trace_cfg80211_return_bool(ret);
+       return ret;
 }
+EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
 
-bool nl80211_unexpected_4addr_frame(struct net_device *dev,
-                                   const u8 *addr, gfp_t gfp)
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+                                       const u8 *addr, gfp_t gfp)
 {
-       return __nl80211_unexpected_frame(dev,
-                                         NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
-                                         addr, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       bool ret;
+
+       trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+                   wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
+               trace_cfg80211_return_bool(false);
+               return false;
+       }
+       ret = __nl80211_unexpected_frame(dev,
+                                        NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+                                        addr, gfp);
+       trace_cfg80211_return_bool(ret);
+       return ret;
 }
+EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
 
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
                      struct wireless_dev *wdev, u32 nlportid,
@@ -9647,15 +9904,17 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
        return -ENOBUFS;
 }
 
-void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
-                                struct wireless_dev *wdev, u64 cookie,
-                                const u8 *buf, size_t len, bool ack,
-                                gfp_t gfp)
+void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
+                            const u8 *buf, size_t len, bool ack, gfp_t gfp)
 {
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct net_device *netdev = wdev->netdev;
        struct sk_buff *msg;
        void *hdr;
 
+       trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9683,17 +9942,21 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
 
-void
-nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
-                            struct net_device *netdev,
-                            enum nl80211_cqm_rssi_threshold_event rssi_event,
-                            gfp_t gfp)
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+                             enum nl80211_cqm_rssi_threshold_event rssi_event,
+                             gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
 
+       trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9705,7 +9968,7 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
                goto nla_put_failure;
 
        pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
@@ -9728,10 +9991,11 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
 
-void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev, const u8 *bssid,
-                             const u8 *replay_ctr, gfp_t gfp)
+static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+                                    struct net_device *netdev, const u8 *bssid,
+                                    const u8 *replay_ctr, gfp_t gfp)
 {
        struct sk_buff *msg;
        struct nlattr *rekey_attr;
@@ -9773,9 +10037,22 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
-                                   struct net_device *netdev, int index,
-                                   const u8 *bssid, bool preauth, gfp_t gfp)
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
+                              const u8 *replay_ctr, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_gtk_rekey_notify(dev, bssid);
+       nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
+
+static void
+nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
+                              struct net_device *netdev, int index,
+                              const u8 *bssid, bool preauth, gfp_t gfp)
 {
        struct sk_buff *msg;
        struct nlattr *attr;
@@ -9818,9 +10095,22 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev,
-                             struct cfg80211_chan_def *chandef, gfp_t gfp)
+void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
+                                    const u8 *bssid, bool preauth, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
+       nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
+}
+EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+
+static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+                                    struct net_device *netdev,
+                                    struct cfg80211_chan_def *chandef,
+                                    gfp_t gfp)
 {
        struct sk_buff *msg;
        void *hdr;
@@ -9852,11 +10142,36 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void
-nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
-                           struct net_device *netdev, const u8 *peer,
-                           u32 num_packets, u32 rate, u32 intvl, gfp_t gfp)
+void cfg80211_ch_switch_notify(struct net_device *dev,
+                              struct cfg80211_chan_def *chandef)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ch_switch_notify(dev, chandef);
+
+       wdev_lock(wdev);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO))
+               goto out;
+
+       wdev->channel = chandef->chan;
+       nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
+out:
+       wdev_unlock(wdev);
+       return;
+}
+EXPORT_SYMBOL(cfg80211_ch_switch_notify);
+
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+                            const u8 *peer, u32 num_packets,
+                            u32 rate, u32 intvl, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
@@ -9872,7 +10187,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
                goto nla_put_failure;
 
@@ -9901,6 +10216,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
 
 void
 nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -9953,15 +10269,18 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void
-nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
-                               struct net_device *netdev, const u8 *peer,
-                               u32 num_packets, gfp_t gfp)
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+                                const u8 *peer, u32 num_packets, gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
 
+       trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9973,7 +10292,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
                goto nla_put_failure;
 
@@ -9996,6 +10315,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
 
 void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
                           u64 cookie, bool acked, gfp_t gfp)
@@ -10282,6 +10602,50 @@ static struct notifier_block nl80211_netlink_notifier = {
        .notifier_call = nl80211_netlink_notify,
 };
 
+void cfg80211_ft_event(struct net_device *netdev,
+                      struct cfg80211_ft_event_params *ft_event)
+{
+       struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+       int err;
+
+       trace_cfg80211_ft_event(wiphy, netdev, ft_event);
+
+       if (!ft_event->target_ap)
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap);
+       if (ft_event->ies)
+               nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies);
+       if (ft_event->ric_ies)
+               nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
+                       ft_event->ric_ies);
+
+       err = genlmsg_end(msg, hdr);
+       if (err < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, GFP_KERNEL);
+}
+EXPORT_SYMBOL(cfg80211_ft_event);
+
 /* initialisation/exit functions */
 
 int nl80211_init(void)