nl80211: fix per-station group key get/del and memory leak
[pandora-kernel.git] / net / wireless / nl80211.c
index 5839c85..8887c6e 100644 (file)
@@ -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,14 @@ 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 },
 };
 
 /* policy for the key attributes */
@@ -428,6 +429,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 +886,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 +1090,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 +1523,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 */
@@ -2308,7 +2317,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 +2346,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 +2422,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 +2450,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 +2596,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 +2619,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 +2650,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 +2694,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 +2720,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 +2742,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)
@@ -2805,6 +2854,9 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->get_key)
                return -EOPNOTSUPP;
 
+       if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
+               return -ENOENT;
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return -ENOMEM;
@@ -2824,10 +2876,6 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
                goto nla_put_failure;
 
-       if (pairwise && mac_addr &&
-           !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
-               return -ENOENT;
-
        err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,
                           get_key_callback);
 
@@ -2998,7 +3046,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
        wdev_lock(dev->ieee80211_ptr);
        err = nl80211_key_allowed(dev->ieee80211_ptr);
 
-       if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
+       if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&
            !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
                err = -ENOENT;
 
@@ -4398,10 +4446,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 +4462,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 +4494,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 +4695,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];
@@ -5260,11 +5421,11 @@ 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 +5519,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,6 +5733,25 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                        err = -EOPNOTSUPP;
                        goto out_free;
                }
+
+               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 =
@@ -5561,14 +5778,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 +5791,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 +5827,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 +5837,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 +5854,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 +5895,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 +5944,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 +5959,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 +6001,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 +6016,88 @@ 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;
+       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;
+
+       rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
+                                                       info->attrs);
+       err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
+       if (err)
+               goto out_err;
+
+       err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
+       if (err)
+               goto out_free;
+
+       rdev->sched_scan_req->dev = dev;
+       rdev->sched_scan_req->wiphy = &rdev->wiphy;
+
+       nl80211_send_sched_scan(rdev, dev,
+                               NL80211_CMD_START_SCHED_SCAN);
+       return 0;
+
+out_free:
+       kfree(rdev->sched_scan_req);
+out_err:
+       rdev->sched_scan_req = NULL;
        return err;
 }
 
@@ -5923,7 +6177,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 +6312,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;
@@ -6311,8 +6562,6 @@ 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;
@@ -6325,9 +6574,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
                        goto out;
                }
 
-               chan = ieee80211_get_channel(&rdev->wiphy,
-                                            survey.channel->center_freq);
-               if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+               if (survey.channel->flags & IEEE80211_CHAN_DISABLED) {
                        survey_idx++;
                        continue;
                }
@@ -8159,6 +8406,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,10 +8811,43 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
        return 0;
 }
 
-static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
+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 cfg80211_registered_device *rdev = info->user_ptr[0];
-       struct nlattr *tb[NUM_NL80211_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];
+       struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
        struct cfg80211_wowlan new_triggers = {};
        struct cfg80211_wowlan *ntrig;
        const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
@@ -8687,6 +8989,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 +9754,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 +9770,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 +9829,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 +10181,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,
@@ -10095,6 +10504,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 +10719,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 */
@@ -11325,55 +11766,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 +12032,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 +12043,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 +12055,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 +12081,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;
-       }
-
-       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;
+       trace_cfg80211_ch_switch_started_notify(dev, chandef);
 
- 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 +12151,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 +12238,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 +12393,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);
        }