cfg80211: allow survey data to return global data
[deliverable/linux.git] / net / wireless / nl80211.c
index 5839c85075f15407e86d84526cfb29087c70aa81..9555ef9fd99eead9f1a50420629c5e834e0f8b19 100644 (file)
@@ -59,13 +59,13 @@ enum nl80211_multicast_groups {
 };
 
 static const struct genl_multicast_group nl80211_mcgrps[] = {
-       [NL80211_MCGRP_CONFIG] = { .name = "config", },
-       [NL80211_MCGRP_SCAN] = { .name = "scan", },
-       [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
-       [NL80211_MCGRP_MLME] = { .name = "mlme", },
-       [NL80211_MCGRP_VENDOR] = { .name = "vendor", },
+       [NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG },
+       [NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN },
+       [NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG },
+       [NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME },
+       [NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },
 #ifdef CONFIG_NL80211_TESTMODE
-       [NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
+       [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }
 #endif
 };
 
@@ -209,7 +209,7 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
 }
 
 /* policy for the attributes */
-static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
+static 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 },
@@ -388,13 +388,15 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
        [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
        [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
-       [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
+       [NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG },
        [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
        [NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
        [NL80211_ATTR_TSID] = { .type = NLA_U8 },
        [NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
        [NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
        [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
+       [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
+       [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -428,6 +430,7 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
        [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
        [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
        [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
+       [NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED },
 };
 
 static const struct nla_policy
@@ -884,7 +887,12 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
                if (!wdev->current_bss)
                        return -ENOLINK;
                break;
-       default:
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NL80211_IFTYPE_OCB:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_P2P_DEVICE:
+       case NL80211_IFTYPE_WDS:
+       case NUM_NL80211_IFTYPES:
                return -EINVAL;
        }
 
@@ -1083,6 +1091,8 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
        if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
                return -ENOBUFS;
 
+       /* TODO: send wowlan net detect */
+
        nla_nest_end(msg, nl_wowlan);
 
        return 0;
@@ -1514,8 +1524,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                        if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
                                CMD(channel_switch, CHANNEL_SWITCH);
                        CMD(set_qos_map, SET_QOS_MAP);
-                       if (rdev->wiphy.flags &
-                                       WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)
+                       if (rdev->wiphy.features &
+                                       NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
                                CMD(add_tx_ts, ADD_TX_TS);
                }
                /* add into the if now */
@@ -1692,6 +1702,15 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                               rdev->wiphy.max_num_csa_counters))
                        goto nla_put_failure;
 
+               if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+                   nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+                       goto nla_put_failure;
+
+               if (nla_put(msg, NL80211_ATTR_EXT_FEATURES,
+                           sizeof(rdev->wiphy.ext_features),
+                           rdev->wiphy.ext_features))
+                       goto nla_put_failure;
+
                /* done */
                state->split_start = 0;
                break;
@@ -2308,7 +2327,8 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
 static int nl80211_send_chandef(struct sk_buff *msg,
                                const struct cfg80211_chan_def *chandef)
 {
-       WARN_ON(!cfg80211_chandef_valid(chandef));
+       if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+               return -EINVAL;
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
                        chandef->chan->center_freq))
@@ -2336,12 +2356,16 @@ static int nl80211_send_chandef(struct sk_buff *msg,
 
 static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
                              struct cfg80211_registered_device *rdev,
-                             struct wireless_dev *wdev)
+                             struct wireless_dev *wdev, bool removal)
 {
        struct net_device *dev = wdev->netdev;
+       u8 cmd = NL80211_CMD_NEW_INTERFACE;
        void *hdr;
 
-       hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE);
+       if (removal)
+               cmd = NL80211_CMD_DEL_INTERFACE;
+
+       hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
        if (!hdr)
                return -1;
 
@@ -2408,7 +2432,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
                        }
                        if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
                                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                              rdev, wdev) < 0) {
+                                              rdev, wdev, false) < 0) {
                                goto out;
                        }
                        if_idx++;
@@ -2436,7 +2460,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
                return -ENOMEM;
 
        if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
-                              rdev, wdev) < 0) {
+                              rdev, wdev, false) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@ -2582,7 +2606,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct vif_params params;
        struct wireless_dev *wdev;
-       struct sk_buff *msg;
+       struct sk_buff *msg, *event;
        int err;
        enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
        u32 flags;
@@ -2605,7 +2629,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
            !(rdev->wiphy.interface_modes & (1 << type)))
                return -EOPNOTSUPP;
 
-       if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) {
+       if ((type == NL80211_IFTYPE_P2P_DEVICE ||
+            rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
+           info->attrs[NL80211_ATTR_MAC]) {
                nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
                           ETH_ALEN);
                if (!is_valid_ether_addr(params.macaddr))
@@ -2634,12 +2660,15 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        wdev = rdev_add_virtual_intf(rdev,
                                nla_data(info->attrs[NL80211_ATTR_IFNAME]),
                                type, err ? NULL : &flags, &params);
-       if (IS_ERR(wdev)) {
+       if (WARN_ON(!wdev)) {
+               nlmsg_free(msg);
+               return -EPROTO;
+       } else if (IS_ERR(wdev)) {
                nlmsg_free(msg);
                return PTR_ERR(wdev);
        }
 
-       if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
+       if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
                wdev->owner_nlportid = info->snd_portid;
 
        switch (type) {
@@ -2675,11 +2704,25 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
-                              rdev, wdev) < 0) {
+                              rdev, wdev, false) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
 
+       event = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (event) {
+               if (nl80211_send_iface(event, 0, 0, 0,
+                                      rdev, wdev, false) < 0) {
+                       nlmsg_free(event);
+                       goto out;
+               }
+
+               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+                                       event, 0, NL80211_MCGRP_CONFIG,
+                                       GFP_KERNEL);
+       }
+
+out:
        return genlmsg_reply(msg, info);
 }
 
@@ -2687,10 +2730,18 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct wireless_dev *wdev = info->user_ptr[1];
+       struct sk_buff *msg;
+       int status;
 
        if (!rdev->ops->del_virtual_intf)
                return -EOPNOTSUPP;
 
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (msg && nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, true) < 0) {
+               nlmsg_free(msg);
+               msg = NULL;
+       }
+
        /*
         * If we remove a wireless device without a netdev then clear
         * user_ptr[1] so that nl80211_post_doit won't dereference it
@@ -2701,7 +2752,15 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
        if (!wdev->netdev)
                info->user_ptr[1] = NULL;
 
-       return rdev_del_virtual_intf(rdev, wdev);
+       status = rdev_del_virtual_intf(rdev, wdev);
+       if (status >= 0 && msg)
+               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+                                       msg, 0, NL80211_MCGRP_CONFIG,
+                                       GFP_KERNEL);
+       else
+               nlmsg_free(msg);
+
+       return status;
 }
 
 static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
@@ -4398,10 +4457,12 @@ static int nl80211_del_station(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];
-       u8 *mac_addr = NULL;
+       struct station_del_parameters params;
+
+       memset(&params, 0, sizeof(params));
 
        if (info->attrs[NL80211_ATTR_MAC])
-               mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+               params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
@@ -4412,7 +4473,28 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->del_station)
                return -EOPNOTSUPP;
 
-       return rdev_del_station(rdev, dev, mac_addr);
+       if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) {
+               params.subtype =
+                       nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
+               if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 &&
+                   params.subtype != IEEE80211_STYPE_DEAUTH >> 4)
+                       return -EINVAL;
+       } else {
+               /* Default to Deauthentication frame */
+               params.subtype = IEEE80211_STYPE_DEAUTH >> 4;
+       }
+
+       if (info->attrs[NL80211_ATTR_REASON_CODE]) {
+               params.reason_code =
+                       nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+               if (params.reason_code == 0)
+                       return -EINVAL; /* 0 is reserved */
+       } else {
+               /* Default to reason code 2 */
+               params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID;
+       }
+
+       return rdev_del_station(rdev, dev, &params);
 }
 
 static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
@@ -4423,7 +4505,7 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
        void *hdr;
        struct nlattr *pinfoattr;
 
-       hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
+       hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_MPATH);
        if (!hdr)
                return -1;
 
@@ -4624,6 +4706,96 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
        return rdev_del_mpath(rdev, dev, dst);
 }
 
+static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       int err;
+       struct net_device *dev = info->user_ptr[1];
+       struct mpath_info pinfo;
+       struct sk_buff *msg;
+       u8 *dst = NULL;
+       u8 mpp[ETH_ALEN];
+
+       memset(&pinfo, 0, sizeof(pinfo));
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       if (!rdev->ops->get_mpp)
+               return -EOPNOTSUPP;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+               return -EOPNOTSUPP;
+
+       err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo);
+       if (err)
+               return err;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0,
+                              dev, dst, mpp, &pinfo) < 0) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+
+       return genlmsg_reply(msg, info);
+}
+
+static int nl80211_dump_mpp(struct sk_buff *skb,
+                           struct netlink_callback *cb)
+{
+       struct mpath_info pinfo;
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+       u8 dst[ETH_ALEN];
+       u8 mpp[ETH_ALEN];
+       int path_idx = cb->args[2];
+       int err;
+
+       err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
+       if (err)
+               return err;
+
+       if (!rdev->ops->dump_mpp) {
+               err = -EOPNOTSUPP;
+               goto out_err;
+       }
+
+       if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out_err;
+       }
+
+       while (1) {
+               err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst,
+                                   mpp, &pinfo);
+               if (err == -ENOENT)
+                       break;
+               if (err)
+                       goto out_err;
+
+               if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
+                                      cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                      wdev->netdev, dst, mpp,
+                                      &pinfo) < 0)
+                       goto out;
+
+               path_idx++;
+       }
+
+ out:
+       cb->args[2] = path_idx;
+       err = skb->len;
+ out_err:
+       nl80211_finish_wdev_dump(rdev);
+       return err;
+}
+
 static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5165,42 +5337,20 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,
        return err;
 }
 
-static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom,
+                             struct sk_buff *msg)
 {
-       const struct ieee80211_regdomain *regdom;
-       struct sk_buff *msg;
-       void *hdr = NULL;
        struct nlattr *nl_reg_rules;
        unsigned int i;
 
-       if (!cfg80211_regdomain)
-               return -EINVAL;
-
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg)
-               return -ENOBUFS;
-
-       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
-                            NL80211_CMD_GET_REG);
-       if (!hdr)
-               goto put_failure;
-
-       if (reg_last_request_cell_base() &&
-           nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
-                       NL80211_USER_REG_HINT_CELL_BASE))
-               goto nla_put_failure;
-
-       rcu_read_lock();
-       regdom = rcu_dereference(cfg80211_regdomain);
-
        if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
            (regdom->dfs_region &&
             nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
-               goto nla_put_failure_rcu;
+               goto nla_put_failure;
 
        nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
        if (!nl_reg_rules)
-               goto nla_put_failure_rcu;
+               goto nla_put_failure;
 
        for (i = 0; i < regdom->n_reg_rules; i++) {
                struct nlattr *nl_reg_rule;
@@ -5215,7 +5365,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
 
                nl_reg_rule = nla_nest_start(msg, i);
                if (!nl_reg_rule)
-                       goto nla_put_failure_rcu;
+                       goto nla_put_failure;
 
                max_bandwidth_khz = freq_range->max_bandwidth_khz;
                if (!max_bandwidth_khz)
@@ -5236,13 +5386,74 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
                                power_rule->max_eirp) ||
                    nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
                                reg_rule->dfs_cac_ms))
-                       goto nla_put_failure_rcu;
+                       goto nla_put_failure;
 
                nla_nest_end(msg, nl_reg_rule);
        }
-       rcu_read_unlock();
 
        nla_nest_end(msg, nl_reg_rules);
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
+{
+       const struct ieee80211_regdomain *regdom = NULL;
+       struct cfg80211_registered_device *rdev;
+       struct wiphy *wiphy = NULL;
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOBUFS;
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_GET_REG);
+       if (!hdr)
+               goto put_failure;
+
+       if (info->attrs[NL80211_ATTR_WIPHY]) {
+               bool self_managed;
+
+               rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
+               if (IS_ERR(rdev)) {
+                       nlmsg_free(msg);
+                       return PTR_ERR(rdev);
+               }
+
+               wiphy = &rdev->wiphy;
+               self_managed = wiphy->regulatory_flags &
+                              REGULATORY_WIPHY_SELF_MANAGED;
+               regdom = get_wiphy_regdom(wiphy);
+
+               /* a self-managed-reg device must have a private regdom */
+               if (WARN_ON(!regdom && self_managed)) {
+                       nlmsg_free(msg);
+                       return -EINVAL;
+               }
+
+               if (regdom &&
+                   nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+                       goto nla_put_failure;
+       }
+
+       if (!wiphy && reg_last_request_cell_base() &&
+           nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+                       NL80211_USER_REG_HINT_CELL_BASE))
+               goto nla_put_failure;
+
+       rcu_read_lock();
+
+       if (!regdom)
+               regdom = rcu_dereference(cfg80211_regdomain);
+
+       if (nl80211_put_regdom(regdom, msg))
+               goto nla_put_failure_rcu;
+
+       rcu_read_unlock();
 
        genlmsg_end(msg, hdr);
        return genlmsg_reply(msg, info);
@@ -5256,15 +5467,92 @@ put_failure:
        return -EMSGSIZE;
 }
 
+static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb,
+                              u32 seq, int flags, struct wiphy *wiphy,
+                              const struct ieee80211_regdomain *regdom)
+{
+       void *hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags,
+                                  NL80211_CMD_GET_REG);
+
+       if (!hdr)
+               return -1;
+
+       genl_dump_check_consistent(cb, hdr, &nl80211_fam);
+
+       if (nl80211_put_regdom(regdom, msg))
+               goto nla_put_failure;
+
+       if (!wiphy && reg_last_request_cell_base() &&
+           nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
+                       NL80211_USER_REG_HINT_CELL_BASE))
+               goto nla_put_failure;
+
+       if (wiphy &&
+           nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
+               goto nla_put_failure;
+
+       if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+           nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+               goto nla_put_failure;
+
+       return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int nl80211_get_reg_dump(struct sk_buff *skb,
+                               struct netlink_callback *cb)
+{
+       const struct ieee80211_regdomain *regdom = NULL;
+       struct cfg80211_registered_device *rdev;
+       int err, reg_idx, start = cb->args[2];
+
+       rtnl_lock();
+
+       if (cfg80211_regdomain && start == 0) {
+               err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+                                         NLM_F_MULTI, NULL,
+                                         rtnl_dereference(cfg80211_regdomain));
+               if (err < 0)
+                       goto out_err;
+       }
+
+       /* the global regdom is idx 0 */
+       reg_idx = 1;
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               regdom = get_wiphy_regdom(&rdev->wiphy);
+               if (!regdom)
+                       continue;
+
+               if (++reg_idx <= start)
+                       continue;
+
+               err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
+                                         NLM_F_MULTI, &rdev->wiphy, regdom);
+               if (err < 0) {
+                       reg_idx--;
+                       break;
+               }
+       }
+
+       cb->args[2] = reg_idx;
+       err = skb->len;
+out_err:
+       rtnl_unlock();
+       return err;
+}
+
 static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
        struct nlattr *nl_reg_rule;
-       char *alpha2 = NULL;
-       int rem_reg_rules = 0, r = 0;
+       char *alpha2;
+       int rem_reg_rules, r;
        u32 num_rules = 0, rule_idx = 0, size_of_regd;
        enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
-       struct ieee80211_regdomain *rd = NULL;
+       struct ieee80211_regdomain *rd;
 
        if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
                return -EINVAL;
@@ -5358,6 +5646,43 @@ static int validate_scan_freqs(struct nlattr *freqs)
        return n_channels;
 }
 
+static int nl80211_parse_random_mac(struct nlattr **attrs,
+                                   u8 *mac_addr, u8 *mac_addr_mask)
+{
+       int i;
+
+       if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) {
+               memset(mac_addr, 0, ETH_ALEN);
+               memset(mac_addr_mask, 0, ETH_ALEN);
+               mac_addr[0] = 0x2;
+               mac_addr_mask[0] = 0x3;
+
+               return 0;
+       }
+
+       /* need both or none */
+       if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK])
+               return -EINVAL;
+
+       memcpy(mac_addr, nla_data(attrs[NL80211_ATTR_MAC]), ETH_ALEN);
+       memcpy(mac_addr_mask, nla_data(attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN);
+
+       /* don't allow or configure an mcast address */
+       if (!is_multicast_ether_addr(mac_addr_mask) ||
+           is_multicast_ether_addr(mac_addr))
+               return -EINVAL;
+
+       /*
+        * allow users to pass a MAC address that has bits set outside
+        * of the mask, but don't bother drivers with having to deal
+        * with such bits
+        */
+       for (i = 0; i < ETH_ALEN; i++)
+               mac_addr[i] &= mac_addr_mask[i];
+
+       return 0;
+}
+
 static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5535,9 +5860,28 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                        err = -EOPNOTSUPP;
                        goto out_free;
                }
-       }
 
-       request->no_cck =
+               if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+                       if (!(wiphy->features &
+                                       NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)) {
+                               err = -EOPNOTSUPP;
+                               goto out_free;
+                       }
+
+                       if (wdev->current_bss) {
+                               err = -EOPNOTSUPP;
+                               goto out_free;
+                       }
+
+                       err = nl80211_parse_random_mac(info->attrs,
+                                                      request->mac_addr,
+                                                      request->mac_addr_mask);
+                       if (err)
+                               goto out_free;
+               }
+       }
+
+       request->no_cck =
                nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
 
        request->wdev = wdev;
@@ -5561,14 +5905,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
-static int nl80211_start_sched_scan(struct sk_buff *skb,
-                                   struct genl_info *info)
+static struct cfg80211_sched_scan_request *
+nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        struct nlattr **attrs)
 {
        struct cfg80211_sched_scan_request *request;
-       struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       struct net_device *dev = info->user_ptr[1];
        struct nlattr *attr;
-       struct wiphy *wiphy;
        int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
        u32 interval;
        enum ieee80211_band band;
@@ -5576,38 +5918,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
        s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
 
-       if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
-           !rdev->ops->sched_scan_start)
-               return -EOPNOTSUPP;
-
-       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
-               return -EINVAL;
+       if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
+               return ERR_PTR(-EINVAL);
 
-       if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
-               return -EINVAL;
+       if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+               return ERR_PTR(-EINVAL);
 
-       interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
+       interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
        if (interval == 0)
-               return -EINVAL;
-
-       wiphy = &rdev->wiphy;
+               return ERR_PTR(-EINVAL);
 
-       if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+       if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
                n_channels = validate_scan_freqs(
-                               info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
+                               attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
                if (!n_channels)
-                       return -EINVAL;
+                       return ERR_PTR(-EINVAL);
        } else {
                n_channels = ieee80211_get_num_supported_channels(wiphy);
        }
 
-       if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
-               nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+       if (attrs[NL80211_ATTR_SCAN_SSIDS])
+               nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
                                    tmp)
                        n_ssids++;
 
        if (n_ssids > wiphy->max_sched_scan_ssids)
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
        /*
         * First, count the number of 'real' matchsets. Due to an issue with
@@ -5618,9 +5954,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
         * older userspace that treated a matchset with only the RSSI as the
         * global RSSI for all other matchsets - if there are other matchsets.
         */
-       if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+       if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
                nla_for_each_nested(attr,
-                                   info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+                                   attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
                                    tmp) {
                        struct nlattr *rssi;
 
@@ -5628,7 +5964,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                                        nla_data(attr), nla_len(attr),
                                        nl80211_match_policy);
                        if (err)
-                               return err;
+                               return ERR_PTR(err);
                        /* add other standalone attributes here */
                        if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
                                n_match_sets++;
@@ -5645,30 +5981,23 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                n_match_sets = 1;
 
        if (n_match_sets > wiphy->max_match_sets)
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
-       if (info->attrs[NL80211_ATTR_IE])
-               ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       if (attrs[NL80211_ATTR_IE])
+               ie_len = nla_len(attrs[NL80211_ATTR_IE]);
        else
                ie_len = 0;
 
        if (ie_len > wiphy->max_sched_scan_ie_len)
-               return -EINVAL;
-
-       if (rdev->sched_scan_req) {
-               err = -EINPROGRESS;
-               goto out;
-       }
+               return ERR_PTR(-EINVAL);
 
        request = kzalloc(sizeof(*request)
                        + sizeof(*request->ssids) * n_ssids
                        + sizeof(*request->match_sets) * n_match_sets
                        + sizeof(*request->channels) * n_channels
                        + ie_len, GFP_KERNEL);
-       if (!request) {
-               err = -ENOMEM;
-               goto out;
-       }
+       if (!request)
+               return ERR_PTR(-ENOMEM);
 
        if (n_ssids)
                request->ssids = (void *)&request->channels[n_channels];
@@ -5693,10 +6022,10 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        request->n_match_sets = n_match_sets;
 
        i = 0;
-       if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+       if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
                /* user specified, bail out if channel not found */
                nla_for_each_nested(attr,
-                                   info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
+                                   attrs[NL80211_ATTR_SCAN_FREQUENCIES],
                                    tmp) {
                        struct ieee80211_channel *chan;
 
@@ -5742,8 +6071,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        request->n_channels = i;
 
        i = 0;
-       if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
-               nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+       if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
+               nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
                                    tmp) {
                        if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
                                err = -EINVAL;
@@ -5757,9 +6086,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        }
 
        i = 0;
-       if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+       if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
                nla_for_each_nested(attr,
-                                   info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+                                   attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
                                    tmp) {
                        struct nlattr *ssid, *rssi;
 
@@ -5799,7 +6128,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                }
 
                /* there was no other matchset, so the RSSI one is alone */
-               if (i == 0)
+               if (i == 0 && n_match_sets)
                        request->match_sets[0].rssi_thold = default_match_rssi;
 
                request->min_rssi_thold = INT_MAX;
@@ -5814,36 +6143,94 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        if (ie_len) {
                request->ie_len = ie_len;
                memcpy((void *)request->ie,
-                      nla_data(info->attrs[NL80211_ATTR_IE]),
+                      nla_data(attrs[NL80211_ATTR_IE]),
                       request->ie_len);
        }
 
-       if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
+       if (attrs[NL80211_ATTR_SCAN_FLAGS]) {
                request->flags = nla_get_u32(
-                       info->attrs[NL80211_ATTR_SCAN_FLAGS]);
+                       attrs[NL80211_ATTR_SCAN_FLAGS]);
                if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
                    !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
                        err = -EOPNOTSUPP;
                        goto out_free;
                }
+
+               if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+                       u32 flg = NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+
+                       if (!wdev) /* must be net-detect */
+                               flg = NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+
+                       if (!(wiphy->features & flg)) {
+                               err = -EOPNOTSUPP;
+                               goto out_free;
+                       }
+
+                       if (wdev && wdev->current_bss) {
+                               err = -EOPNOTSUPP;
+                               goto out_free;
+                       }
+
+                       err = nl80211_parse_random_mac(attrs, request->mac_addr,
+                                                      request->mac_addr_mask);
+                       if (err)
+                               goto out_free;
+               }
        }
 
-       request->dev = dev;
-       request->wiphy = &rdev->wiphy;
        request->interval = interval;
        request->scan_start = jiffies;
 
-       err = rdev_sched_scan_start(rdev, dev, request);
-       if (!err) {
-               rdev->sched_scan_req = request;
-               nl80211_send_sched_scan(rdev, dev,
-                                       NL80211_CMD_START_SCHED_SCAN);
-               goto out;
-       }
+       return request;
 
 out_free:
        kfree(request);
-out:
+       return ERR_PTR(err);
+}
+
+static int nl80211_start_sched_scan(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 cfg80211_sched_scan_request *sched_scan_req;
+       int err;
+
+       if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
+           !rdev->ops->sched_scan_start)
+               return -EOPNOTSUPP;
+
+       if (rdev->sched_scan_req)
+               return -EINPROGRESS;
+
+       sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
+                                                 info->attrs);
+
+       err = PTR_ERR_OR_ZERO(sched_scan_req);
+       if (err)
+               goto out_err;
+
+       err = rdev_sched_scan_start(rdev, dev, sched_scan_req);
+       if (err)
+               goto out_free;
+
+       sched_scan_req->dev = dev;
+       sched_scan_req->wiphy = &rdev->wiphy;
+
+       if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
+               sched_scan_req->owner_nlportid = info->snd_portid;
+
+       rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req);
+
+       nl80211_send_sched_scan(rdev, dev,
+                               NL80211_CMD_START_SCHED_SCAN);
+       return 0;
+
+out_free:
+       kfree(sched_scan_req);
+out_err:
        return err;
 }
 
@@ -5923,7 +6310,6 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
         * function is called under RTNL lock, so this should not be a problem.
         */
        static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
-       u8 radar_detect_width = 0;
        int err;
        bool need_new_beacon = false;
        int len, i;
@@ -6059,10 +6445,8 @@ skip_beacons:
        if (err < 0)
                return err;
 
-       if (err > 0) {
-               radar_detect_width = BIT(params.chandef.width);
+       if (err > 0)
                params.radar_required = true;
-       }
 
        if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
                params.block_tx = true;
@@ -6229,12 +6613,17 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
 }
 
 static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
-                               int flags, struct net_device *dev,
-                               struct survey_info *survey)
+                              int flags, struct net_device *dev,
+                              bool allow_radio_stats,
+                              struct survey_info *survey)
 {
        void *hdr;
        struct nlattr *infoattr;
 
+       /* skip radio stats if userspace didn't request them */
+       if (!survey->channel && !allow_radio_stats)
+               return 0;
+
        hdr = nl80211hdr_put(msg, portid, seq, flags,
                             NL80211_CMD_NEW_SURVEY_RESULTS);
        if (!hdr)
@@ -6247,7 +6636,8 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
        if (!infoattr)
                goto nla_put_failure;
 
-       if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
+       if (survey->channel &&
+           nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
                        survey->channel->center_freq))
                goto nla_put_failure;
 
@@ -6257,25 +6647,25 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
        if ((survey->filled & SURVEY_INFO_IN_USE) &&
            nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
                goto nla_put_failure;
-       if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) &&
-           nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
-                       survey->channel_time))
+       if ((survey->filled & SURVEY_INFO_TIME) &&
+           nla_put_u64(msg, NL80211_SURVEY_INFO_TIME,
+                       survey->time))
                goto nla_put_failure;
-       if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) &&
-           nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
-                       survey->channel_time_busy))
+       if ((survey->filled & SURVEY_INFO_TIME_BUSY) &&
+           nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_BUSY,
+                       survey->time_busy))
                goto nla_put_failure;
-       if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) &&
-           nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
-                       survey->channel_time_ext_busy))
+       if ((survey->filled & SURVEY_INFO_TIME_EXT_BUSY) &&
+           nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+                       survey->time_ext_busy))
                goto nla_put_failure;
-       if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) &&
-           nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
-                       survey->channel_time_rx))
+       if ((survey->filled & SURVEY_INFO_TIME_RX) &&
+           nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_RX,
+                       survey->time_rx))
                goto nla_put_failure;
-       if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) &&
-           nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
-                       survey->channel_time_tx))
+       if ((survey->filled & SURVEY_INFO_TIME_TX) &&
+           nla_put_u64(msg, NL80211_SURVEY_INFO_TIME_TX,
+                       survey->time_tx))
                goto nla_put_failure;
 
        nla_nest_end(msg, infoattr);
@@ -6287,19 +6677,22 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
        return -EMSGSIZE;
 }
 
-static int nl80211_dump_survey(struct sk_buff *skb,
-                       struct netlink_callback *cb)
+static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct survey_info survey;
        struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
        int survey_idx = cb->args[2];
        int res;
+       bool radio_stats;
 
        res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
        if (res)
                return res;
 
+       /* prepare_wdev_dump parsed the attributes */
+       radio_stats = nl80211_fam.attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS];
+
        if (!wdev->netdev) {
                res = -EINVAL;
                goto out_err;
@@ -6311,23 +6704,15 @@ static int nl80211_dump_survey(struct sk_buff *skb,
        }
 
        while (1) {
-               struct ieee80211_channel *chan;
-
                res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
                if (res == -ENOENT)
                        break;
                if (res)
                        goto out_err;
 
-               /* Survey without a channel doesn't make sense */
-               if (!survey.channel) {
-                       res = -EINVAL;
-                       goto out;
-               }
-
-               chan = ieee80211_get_channel(&rdev->wiphy,
-                                            survey.channel->center_freq);
-               if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+               /* don't send disabled channels, but do send non-channel data */
+               if (survey.channel &&
+                   survey.channel->flags & IEEE80211_CHAN_DISABLED) {
                        survey_idx++;
                        continue;
                }
@@ -6335,7 +6720,7 @@ 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,
-                               wdev->netdev, &survey) < 0)
+                               wdev->netdev, radio_stats, &survey) < 0)
                        goto out;
                survey_idx++;
        }
@@ -8159,6 +8544,28 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
        return -EINVAL;
 }
 
+static int nl80211_join_ocb(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 ocb_setup setup = {};
+       int err;
+
+       err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+       if (err)
+               return err;
+
+       return cfg80211_join_ocb(rdev, dev, &setup);
+}
+
+static int nl80211_leave_ocb(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];
+
+       return cfg80211_leave_ocb(rdev, dev);
+}
+
 static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -8542,6 +8949,39 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
        return 0;
 }
 
+static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev,
+                                  const struct wiphy_wowlan_support *wowlan,
+                                  struct nlattr *attr,
+                                  struct cfg80211_wowlan *trig)
+{
+       struct nlattr **tb;
+       int err;
+
+       tb = kzalloc(NUM_NL80211_ATTR * sizeof(*tb), GFP_KERNEL);
+       if (!tb)
+               return -ENOMEM;
+
+       if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       err = nla_parse(tb, NL80211_ATTR_MAX,
+                       nla_data(attr), nla_len(attr),
+                       nl80211_policy);
+       if (err)
+               goto out;
+
+       trig->nd_config = nl80211_parse_sched_scan(&rdev->wiphy, NULL, tb);
+       err = PTR_ERR_OR_ZERO(trig->nd_config);
+       if (err)
+               trig->nd_config = NULL;
+
+out:
+       kfree(tb);
+       return err;
+}
+
 static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -8687,6 +9127,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                        goto error;
        }
 
+       if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
+               err = nl80211_parse_wowlan_nd(
+                       rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
+                       &new_triggers);
+               if (err)
+                       goto error;
+       }
+
        ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
        if (!ntrig) {
                err = -ENOMEM;
@@ -9444,7 +9892,7 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
        u16 admitted_time = 0;
        int err;
 
-       if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
+       if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION))
                return -EOPNOTSUPP;
 
        if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
@@ -9460,12 +9908,10 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        /* WMM uses TIDs 0-7 even for TSPEC */
-       if (tsid < IEEE80211_FIRST_TSPEC_TSID) {
-               if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
-                       return -EINVAL;
-       } else {
+       if (tsid >= IEEE80211_FIRST_TSPEC_TSID) {
                /* TODO: handle 802.11 TSPEC/admission control
-                * need more attributes for that (e.g. BA session requirement)
+                * need more attributes for that (e.g. BA session requirement);
+                * change the WMM adminssion test above to allow both then
                 */
                return -EINVAL;
        }
@@ -9521,6 +9967,98 @@ static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+static int nl80211_tdls_channel_switch(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 cfg80211_chan_def chandef = {};
+       const u8 *addr;
+       u8 oper_class;
+       int err;
+
+       if (!rdev->ops->tdls_channel_switch ||
+           !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+               return -EOPNOTSUPP;
+
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       if (!info->attrs[NL80211_ATTR_MAC] ||
+           !info->attrs[NL80211_ATTR_OPER_CLASS])
+               return -EINVAL;
+
+       err = nl80211_parse_chandef(rdev, info, &chandef);
+       if (err)
+               return err;
+
+       /*
+        * Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012
+        * section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the
+        * specification is not defined for them.
+        */
+       if (chandef.chan->band == IEEE80211_BAND_2GHZ &&
+           chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+           chandef.width != NL80211_CHAN_WIDTH_20)
+               return -EINVAL;
+
+       /* we will be active on the TDLS link */
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, wdev->iftype))
+               return -EINVAL;
+
+       /* don't allow switching to DFS channels */
+       if (cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, wdev->iftype))
+               return -EINVAL;
+
+       addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       oper_class = nla_get_u8(info->attrs[NL80211_ATTR_OPER_CLASS]);
+
+       wdev_lock(wdev);
+       err = rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef);
+       wdev_unlock(wdev);
+
+       return err;
+}
+
+static int nl80211_tdls_cancel_channel_switch(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;
+       const u8 *addr;
+
+       if (!rdev->ops->tdls_channel_switch ||
+           !rdev->ops->tdls_cancel_channel_switch ||
+           !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+               return -EOPNOTSUPP;
+
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       wdev_lock(wdev);
+       rdev_tdls_cancel_channel_switch(rdev, dev, addr);
+       wdev_unlock(wdev);
+
+       return 0;
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -9781,6 +10319,15 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_GET_MPP,
+               .doit = nl80211_get_mpp,
+               .dumpit = nl80211_dump_mpp,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
        {
                .cmd = NL80211_CMD_SET_MPATH,
                .doit = nl80211_set_mpath,
@@ -9815,7 +10362,8 @@ static const struct genl_ops nl80211_ops[] = {
        },
        {
                .cmd = NL80211_CMD_GET_REG,
-               .doit = nl80211_get_reg,
+               .doit = nl80211_get_reg_do,
+               .dumpit = nl80211_get_reg_dump,
                .policy = nl80211_policy,
                .internal_flags = NL80211_FLAG_NEED_RTNL,
                /* can be retrieved by unprivileged users */
@@ -10095,6 +10643,22 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_JOIN_OCB,
+               .doit = nl80211_join_ocb,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_LEAVE_OCB,
+               .doit = nl80211_leave_ocb,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 #ifdef CONFIG_PM
        {
                .cmd = NL80211_CMD_GET_WOWLAN,
@@ -10294,6 +10858,22 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH,
+               .doit = nl80211_tdls_channel_switch,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+               .doit = nl80211_tdls_cancel_channel_switch,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 /* notification functions */
@@ -10497,25 +11077,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
                                NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
-/*
- * This can happen on global regulatory changes or device specific settings
- * based on custom world regulatory domains.
- */
-void nl80211_send_reg_change_event(struct regulatory_request *request)
+static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
+                                         struct regulatory_request *request)
 {
-       struct sk_buff *msg;
-       void *hdr;
-
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg)
-               return;
-
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
-       if (!hdr) {
-               nlmsg_free(msg);
-               return;
-       }
-
        /* Userspace can always count this one always being set */
        if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
                goto nla_put_failure;
@@ -10541,8 +11105,46 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
                        goto nla_put_failure;
        }
 
-       if (request->wiphy_idx != WIPHY_IDX_INVALID &&
-           nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
+       if (request->wiphy_idx != WIPHY_IDX_INVALID) {
+               struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx);
+
+               if (wiphy &&
+                   nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
+                       goto nla_put_failure;
+
+               if (wiphy &&
+                   wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+                   nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG))
+                       goto nla_put_failure;
+       }
+
+       return true;
+
+nla_put_failure:
+       return false;
+}
+
+/*
+ * This can happen on global regulatory changes or device specific settings
+ * based on custom regulatory domains.
+ */
+void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
+                                    struct regulatory_request *request)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nl80211_reg_change_event_fill(msg, request) == false)
                goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
@@ -11325,55 +11927,155 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
 }
 EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
 
-void cfg80211_cqm_rssi_notify(struct net_device *dev,
-                             enum nl80211_cqm_rssi_threshold_event rssi_event,
-                             gfp_t gfp)
+static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
+                                           const char *mac, gfp_t gfp)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-       struct sk_buff *msg;
-       struct nlattr *pinfoattr;
-       void *hdr;
-
-       trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       void **cb;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
-               return;
+               return NULL;
 
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
-       if (!hdr) {
+       cb = (void **)msg->cb;
+
+       cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+       if (!cb[0]) {
                nlmsg_free(msg);
-               return;
+               return NULL;
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
            nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
                goto nla_put_failure;
 
-       pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
-       if (!pinfoattr)
+       if (mac && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac))
                goto nla_put_failure;
 
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
-                       rssi_event))
+       cb[1] = nla_nest_start(msg, NL80211_ATTR_CQM);
+       if (!cb[1])
                goto nla_put_failure;
 
-       nla_nest_end(msg, pinfoattr);
+       cb[2] = rdev;
 
-       genlmsg_end(msg, hdr);
+       return msg;
+ nla_put_failure:
+       nlmsg_free(msg);
+       return NULL;
+}
+
+static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp)
+{
+       void **cb = (void **)msg->cb;
+       struct cfg80211_registered_device *rdev = cb[2];
+
+       nla_nest_end(msg, cb[1]);
+       genlmsg_end(msg, cb[0]);
+
+       memset(msg->cb, 0, sizeof(msg->cb));
 
        genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
                                NL80211_MCGRP_MLME, gfp);
+}
+
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+                             enum nl80211_cqm_rssi_threshold_event rssi_event,
+                             gfp_t gfp)
+{
+       struct sk_buff *msg;
+
+       trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
+       if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
+                   rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
+               return;
+
+       msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+       if (!msg)
+               return;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+                       rssi_event))
+               goto nla_put_failure;
+
+       cfg80211_send_cqm(msg, gfp);
+
        return;
 
  nla_put_failure:
-       genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
 EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
 
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+                            const u8 *peer, u32 num_packets,
+                            u32 rate, u32 intvl, gfp_t gfp)
+{
+       struct sk_buff *msg;
+
+       msg = cfg80211_prepare_cqm(dev, peer, gfp);
+       if (!msg)
+               return;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
+               goto nla_put_failure;
+
+       cfg80211_send_cqm(msg, gfp);
+       return;
+
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+                                const u8 *peer, u32 num_packets, gfp_t gfp)
+{
+       struct sk_buff *msg;
+
+       trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
+       msg = cfg80211_prepare_cqm(dev, peer, gfp);
+       if (!msg)
+               return;
+
+       if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
+               goto nla_put_failure;
+
+       cfg80211_send_cqm(msg, gfp);
+       return;
+
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
+
+void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
+{
+       struct sk_buff *msg;
+
+       msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+       if (!msg)
+               return;
+
+       if (nla_put_flag(msg, NL80211_ATTR_CQM_BEACON_LOSS_EVENT))
+               goto nla_put_failure;
+
+       cfg80211_send_cqm(msg, gfp);
+       return;
+
+ nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify);
+
 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)
@@ -11491,7 +12193,9 @@ 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)
+                                    gfp_t gfp,
+                                    enum nl80211_commands notif,
+                                    u8 count)
 {
        struct sk_buff *msg;
        void *hdr;
@@ -11500,7 +12204,7 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
        if (!msg)
                return;
 
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
+       hdr = nl80211hdr_put(msg, 0, 0, 0, notif);
        if (!hdr) {
                nlmsg_free(msg);
                return;
@@ -11512,6 +12216,10 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
        if (nl80211_send_chandef(msg, chandef))
                goto nla_put_failure;
 
+       if ((notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) &&
+           (nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, count)))
+                       goto nla_put_failure;
+
        genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
@@ -11534,70 +12242,27 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
 
        trace_cfg80211_ch_switch_notify(dev, chandef);
 
-       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO &&
-                   wdev->iftype != NL80211_IFTYPE_ADHOC &&
-                   wdev->iftype != NL80211_IFTYPE_MESH_POINT))
-               return;
-
        wdev->chandef = *chandef;
        wdev->preset_chandef = *chandef;
-       nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
+       nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+                                NL80211_CMD_CH_SWITCH_NOTIFY, 0);
 }
 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)
+void cfg80211_ch_switch_started_notify(struct net_device *dev,
+                                      struct cfg80211_chan_def *chandef,
+                                      u8 count)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-       struct sk_buff *msg;
-       struct nlattr *pinfoattr;
-       void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
-       if (!msg)
-               return;
-
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
-       if (!hdr) {
-               nlmsg_free(msg);
-               return;
-       }
+       trace_cfg80211_ch_switch_started_notify(dev, chandef);
 
-       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
-           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
-               goto nla_put_failure;
-
-       pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
-       if (!pinfoattr)
-               goto nla_put_failure;
-
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
-               goto nla_put_failure;
-
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
-               goto nla_put_failure;
-
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
-               goto nla_put_failure;
-
-       nla_nest_end(msg, pinfoattr);
-
-       genlmsg_end(msg, hdr);
-
-       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
-                               NL80211_MCGRP_MLME, gfp);
-       return;
-
- nla_put_failure:
-       genlmsg_cancel(msg, hdr);
-       nlmsg_free(msg);
+       nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+                                NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, count);
 }
-EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
 
 void
 nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -11647,54 +12312,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-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_rdev(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;
-
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
-       if (!hdr) {
-               nlmsg_free(msg);
-               return;
-       }
-
-       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
-           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
-               goto nla_put_failure;
-
-       pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
-       if (!pinfoattr)
-               goto nla_put_failure;
-
-       if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
-               goto nla_put_failure;
-
-       nla_nest_end(msg, pinfoattr);
-
-       genlmsg_end(msg, hdr);
-
-       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
-                               NL80211_MCGRP_MLME, gfp);
-       return;
-
- nla_put_failure:
-       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)
 {
@@ -11782,6 +12399,67 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
 EXPORT_SYMBOL(cfg80211_report_obss_beacon);
 
 #ifdef CONFIG_PM
+static int cfg80211_net_detect_results(struct sk_buff *msg,
+                                      struct cfg80211_wowlan_wakeup *wakeup)
+{
+       struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect;
+       struct nlattr *nl_results, *nl_match, *nl_freqs;
+       int i, j;
+
+       nl_results = nla_nest_start(
+               msg, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS);
+       if (!nl_results)
+               return -EMSGSIZE;
+
+       for (i = 0; i < nd->n_matches; i++) {
+               struct cfg80211_wowlan_nd_match *match = nd->matches[i];
+
+               nl_match = nla_nest_start(msg, i);
+               if (!nl_match)
+                       break;
+
+               /* The SSID attribute is optional in nl80211, but for
+                * simplicity reasons it's always present in the
+                * cfg80211 structure.  If a driver can't pass the
+                * SSID, that needs to be changed.  A zero length SSID
+                * is still a valid SSID (wildcard), so it cannot be
+                * used for this purpose.
+                */
+               if (nla_put(msg, NL80211_ATTR_SSID, match->ssid.ssid_len,
+                           match->ssid.ssid)) {
+                       nla_nest_cancel(msg, nl_match);
+                       goto out;
+               }
+
+               if (match->n_channels) {
+                       nl_freqs = nla_nest_start(
+                               msg, NL80211_ATTR_SCAN_FREQUENCIES);
+                       if (!nl_freqs) {
+                               nla_nest_cancel(msg, nl_match);
+                               goto out;
+                       }
+
+                       for (j = 0; j < match->n_channels; j++) {
+                               if (nla_put_u32(msg,
+                                               NL80211_ATTR_WIPHY_FREQ,
+                                               match->channels[j])) {
+                                       nla_nest_cancel(msg, nl_freqs);
+                                       nla_nest_cancel(msg, nl_match);
+                                       goto out;
+                               }
+                       }
+
+                       nla_nest_end(msg, nl_freqs);
+               }
+
+               nla_nest_end(msg, nl_match);
+       }
+
+out:
+       nla_nest_end(msg, nl_results);
+       return 0;
+}
+
 void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                                   struct cfg80211_wowlan_wakeup *wakeup,
                                   gfp_t gfp)
@@ -11876,6 +12554,10 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                                goto free_msg;
                }
 
+               if (wakeup->net_detect &&
+                   cfg80211_net_detect_results(msg, wakeup))
+                               goto free_msg;
+
                nla_nest_end(msg, reasons);
        }
 
@@ -11949,6 +12631,13 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
 
        list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
                bool schedule_destroy_work = false;
+               bool schedule_scan_stop = false;
+               struct cfg80211_sched_scan_request *sched_scan_req =
+                       rcu_dereference(rdev->sched_scan_req);
+
+               if (sched_scan_req && notify->portid &&
+                   sched_scan_req->owner_nlportid == notify->portid)
+                       schedule_scan_stop = true;
 
                list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {
                        cfg80211_mlme_unregister_socket(wdev, notify->portid);
@@ -11979,6 +12668,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
                                spin_unlock(&rdev->destroy_list_lock);
                                schedule_work(&rdev->destroy_work);
                        }
+               } else if (schedule_scan_stop) {
+                       sched_scan_req->owner_nlportid = 0;
+
+                       if (rdev->ops->sched_scan_stop &&
+                           rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+                               schedule_work(&rdev->sched_scan_stop_wk);
                }
        }
 
This page took 0.054443 seconds and 5 git commands to generate.