Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[cascardo/linux.git] / net / wireless / nl80211.c
index d5aed3b..e545023 100644 (file)
@@ -37,10 +37,10 @@ static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
 
 /* the netlink family */
 static struct genl_family nl80211_fam = {
-       .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
-       .name = "nl80211",      /* have users key off the name instead */
-       .hdrsize = 0,           /* no private header */
-       .version = 1,           /* no particular meaning now */
+       .id = GENL_ID_GENERATE,         /* don't bother with a hardcoded ID */
+       .name = NL80211_GENL_NAME,      /* have users key off the name instead */
+       .hdrsize = 0,                   /* no private header */
+       .version = 1,                   /* no particular meaning now */
        .maxattr = NL80211_ATTR_MAX,
        .netnsok = true,
        .pre_doit = nl80211_pre_doit,
@@ -59,7 +59,7 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
        int wiphy_idx = -1;
        int ifidx = -1;
 
-       assert_cfg80211_lock();
+       ASSERT_RTNL();
 
        if (!have_ifidx && !have_wdev_id)
                return ERR_PTR(-EINVAL);
@@ -80,7 +80,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
                if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
                        continue;
 
-               mutex_lock(&rdev->devlist_mtx);
                list_for_each_entry(wdev, &rdev->wdev_list, list) {
                        if (have_ifidx && wdev->netdev &&
                            wdev->netdev->ifindex == ifidx) {
@@ -92,7 +91,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
                                break;
                        }
                }
-               mutex_unlock(&rdev->devlist_mtx);
 
                if (result)
                        break;
@@ -109,7 +107,7 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
        struct cfg80211_registered_device *rdev = NULL, *tmp;
        struct net_device *netdev;
 
-       assert_cfg80211_lock();
+       ASSERT_RTNL();
 
        if (!attrs[NL80211_ATTR_WIPHY] &&
            !attrs[NL80211_ATTR_IFINDEX] &&
@@ -128,14 +126,12 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
                tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
                if (tmp) {
                        /* make sure wdev exists */
-                       mutex_lock(&tmp->devlist_mtx);
                        list_for_each_entry(wdev, &tmp->wdev_list, list) {
                                if (wdev->identifier != (u32)wdev_id)
                                        continue;
                                found = true;
                                break;
                        }
-                       mutex_unlock(&tmp->devlist_mtx);
 
                        if (!found)
                                tmp = NULL;
@@ -182,19 +178,6 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
 /*
  * This function returns a pointer to the driver
  * that the genl_info item that is passed refers to.
- * If successful, it returns non-NULL and also locks
- * the driver's mutex!
- *
- * This means that you need to call cfg80211_unlock_rdev()
- * before being allowed to acquire &cfg80211_mutex!
- *
- * This is necessary because we need to lock the global
- * mutex to get an item off the list safely, and then
- * we lock the rdev mutex so it doesn't go away under us.
- *
- * We don't want to keep cfg80211_mutex locked
- * for all the time in order to allow requests on
- * other interfaces to go through at the same time.
  *
  * The result of this can be a PTR_ERR and hence must
  * be checked with IS_ERR() for errors.
@@ -202,20 +185,7 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
 static struct cfg80211_registered_device *
 cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
 {
-       struct cfg80211_registered_device *rdev;
-
-       mutex_lock(&cfg80211_mutex);
-       rdev = __cfg80211_rdev_from_attrs(netns, info->attrs);
-
-       /* if it is not an error we grab the lock on
-        * it to assure it won't be going away while
-        * we operate on it */
-       if (!IS_ERR(rdev))
-               mutex_lock(&rdev->mtx);
-
-       mutex_unlock(&cfg80211_mutex);
-
-       return rdev;
+       return __cfg80211_rdev_from_attrs(netns, info->attrs);
 }
 
 /* policy for the attributes */
@@ -378,6 +348,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_MDID] = { .type = NLA_U16 },
        [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
                                  .len = IEEE80211_MAX_DATA_LEN },
+       [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
 };
 
 /* policy for the key attributes */
@@ -455,7 +426,6 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
        int err;
 
        rtnl_lock();
-       mutex_lock(&cfg80211_mutex);
 
        if (!cb->args[0]) {
                err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
@@ -484,14 +454,12 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                *rdev = wiphy_to_dev(wiphy);
                *wdev = NULL;
 
-               mutex_lock(&(*rdev)->devlist_mtx);
                list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
                        if (tmp->identifier == cb->args[1]) {
                                *wdev = tmp;
                                break;
                        }
                }
-               mutex_unlock(&(*rdev)->devlist_mtx);
 
                if (!*wdev) {
                        err = -ENODEV;
@@ -499,19 +467,14 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                }
        }
 
-       cfg80211_lock_rdev(*rdev);
-
-       mutex_unlock(&cfg80211_mutex);
        return 0;
  out_unlock:
-       mutex_unlock(&cfg80211_mutex);
        rtnl_unlock();
        return err;
 }
 
 static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
 {
-       cfg80211_unlock_rdev(rdev);
        rtnl_unlock();
 }
 
@@ -837,12 +800,9 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
        case NL80211_IFTYPE_MESH_POINT:
                break;
        case NL80211_IFTYPE_ADHOC:
-               if (!wdev->current_bss)
-                       return -ENOLINK;
-               break;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
-               if (wdev->sme_state != CFG80211_SME_CONNECTED)
+               if (!wdev->current_bss)
                        return -ENOLINK;
                break;
        default:
@@ -945,7 +905,7 @@ nla_put_failure:
 static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
                                        struct sk_buff *msg)
 {
-       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
+       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp;
        struct nlattr *nl_tcp;
 
        if (!tcp)
@@ -988,37 +948,37 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
 {
        struct nlattr *nl_wowlan;
 
-       if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
+       if (!dev->wiphy.wowlan)
                return 0;
 
        nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
        if (!nl_wowlan)
                return -ENOBUFS;
 
-       if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
+       if (((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
-           ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
+           ((dev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
             nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
                return -ENOBUFS;
 
-       if (dev->wiphy.wowlan.n_patterns) {
+       if (dev->wiphy.wowlan->n_patterns) {
                struct nl80211_wowlan_pattern_support pat = {
-                       .max_patterns = dev->wiphy.wowlan.n_patterns,
-                       .min_pattern_len = dev->wiphy.wowlan.pattern_min_len,
-                       .max_pattern_len = dev->wiphy.wowlan.pattern_max_len,
-                       .max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
+                       .max_patterns = dev->wiphy.wowlan->n_patterns,
+                       .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
+                       .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
+                       .max_pkt_offset = dev->wiphy.wowlan->max_pkt_offset,
                };
 
                if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
@@ -1564,12 +1524,18 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
        struct cfg80211_registered_device *dev;
        s64 filter_wiphy = -1;
        bool split = false;
-       struct nlattr **tb = nl80211_fam.attrbuf;
+       struct nlattr **tb;
        int res;
 
-       mutex_lock(&cfg80211_mutex);
+       /* will be zeroed in nlmsg_parse() */
+       tb = kmalloc(sizeof(*tb) * (NL80211_ATTR_MAX + 1), GFP_KERNEL);
+       if (!tb)
+               return -ENOMEM;
+
+       rtnl_lock();
+
        res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
-                         tb, nl80211_fam.maxattr, nl80211_policy);
+                         tb, NL80211_ATTR_MAX, nl80211_policy);
        if (res == 0) {
                split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
                if (tb[NL80211_ATTR_WIPHY])
@@ -1582,7 +1548,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
 
                        netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
                        if (!netdev) {
-                               mutex_unlock(&cfg80211_mutex);
+                               rtnl_unlock();
+                               kfree(tb);
                                return -ENODEV;
                        }
                        if (netdev->ieee80211_ptr) {
@@ -1593,6 +1560,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
                        dev_put(netdev);
                }
        }
+       kfree(tb);
 
        list_for_each_entry(dev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
@@ -1628,7 +1596,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
                                    !skb->len &&
                                    cb->min_dump_alloc < 4096) {
                                        cb->min_dump_alloc = 4096;
-                                       mutex_unlock(&cfg80211_mutex);
+                                       rtnl_unlock();
                                        return 1;
                                }
                                idx--;
@@ -1637,7 +1605,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
                } while (cb->args[1] > 0);
                break;
        }
-       mutex_unlock(&cfg80211_mutex);
+       rtnl_unlock();
 
        cb->args[0] = idx;
 
@@ -1792,7 +1760,6 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
        if (result)
                return result;
 
-       mutex_lock(&rdev->devlist_mtx);
        switch (iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
@@ -1816,7 +1783,6 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
        default:
                result = -EINVAL;
        }
-       mutex_unlock(&rdev->devlist_mtx);
 
        return result;
 }
@@ -1865,6 +1831,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        u32 frag_threshold = 0, rts_threshold = 0;
        u8 coverage_class = 0;
 
+       ASSERT_RTNL();
+
        /*
         * Try to find the wiphy and netdev. Normally this
         * function shouldn't need the netdev, but this is
@@ -1874,31 +1842,25 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
         * also passed a netdev to set_wiphy, so that it is
         * possible to let that go to the right netdev!
         */
-       mutex_lock(&cfg80211_mutex);
 
        if (info->attrs[NL80211_ATTR_IFINDEX]) {
                int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
 
                netdev = dev_get_by_index(genl_info_net(info), ifindex);
-               if (netdev && netdev->ieee80211_ptr) {
+               if (netdev && netdev->ieee80211_ptr)
                        rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
-                       mutex_lock(&rdev->mtx);
-               } else
+               else
                        netdev = NULL;
        }
 
        if (!netdev) {
                rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
                                                  info->attrs);
-               if (IS_ERR(rdev)) {
-                       mutex_unlock(&cfg80211_mutex);
+               if (IS_ERR(rdev))
                        return PTR_ERR(rdev);
-               }
                wdev = NULL;
                netdev = NULL;
                result = 0;
-
-               mutex_lock(&rdev->mtx);
        } else
                wdev = netdev->ieee80211_ptr;
 
@@ -1911,8 +1873,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                result = cfg80211_dev_rename(
                        rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
 
-       mutex_unlock(&cfg80211_mutex);
-
        if (result)
                goto bad_res;
 
@@ -2119,7 +2079,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        }
 
  bad_res:
-       mutex_unlock(&rdev->mtx);
        if (netdev)
                dev_put(netdev);
        return result;
@@ -2217,7 +2176,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
        struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
 
-       mutex_lock(&cfg80211_mutex);
+       rtnl_lock();
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
                        continue;
@@ -2227,7 +2186,6 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
                }
                if_idx = 0;
 
-               mutex_lock(&rdev->devlist_mtx);
                list_for_each_entry(wdev, &rdev->wdev_list, list) {
                        if (if_idx < if_start) {
                                if_idx++;
@@ -2236,17 +2194,15 @@ 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) {
-                               mutex_unlock(&rdev->devlist_mtx);
                                goto out;
                        }
                        if_idx++;
                }
-               mutex_unlock(&rdev->devlist_mtx);
 
                wp_idx++;
        }
  out:
-       mutex_unlock(&cfg80211_mutex);
+       rtnl_unlock();
 
        cb->args[0] = wp_idx;
        cb->args[1] = if_idx;
@@ -2279,6 +2235,7 @@ static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
        [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
        [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
        [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
+       [NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG },
 };
 
 static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
@@ -2390,6 +2347,10 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                change = true;
        }
 
+       if (flags && (*flags & NL80211_MNTR_FLAG_ACTIVE) &&
+           !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
+               return -EOPNOTSUPP;
+
        if (change)
                err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
        else
@@ -2447,6 +2408,11 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
+
+       if (!err && (flags & NL80211_MNTR_FLAG_ACTIVE) &&
+           !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
+               return -EOPNOTSUPP;
+
        wdev = rdev_add_virtual_intf(rdev,
                                nla_data(info->attrs[NL80211_ATTR_IFNAME]),
                                type, err ? NULL : &flags, &params);
@@ -2479,11 +2445,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                INIT_LIST_HEAD(&wdev->mgmt_registrations);
                spin_lock_init(&wdev->mgmt_registrations_lock);
 
-               mutex_lock(&rdev->devlist_mtx);
                wdev->identifier = ++rdev->wdev_id;
                list_add_rcu(&wdev->list, &rdev->wdev_list);
                rdev->devlist_generation++;
-               mutex_unlock(&rdev->devlist_mtx);
                break;
        default:
                break;
@@ -2992,8 +2956,6 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
        struct wireless_dev *wdev;
        bool ret = false;
 
-       mutex_lock(&rdev->devlist_mtx);
-
        list_for_each_entry(wdev, &rdev->wdev_list, list) {
                if (wdev->iftype != NL80211_IFTYPE_AP &&
                    wdev->iftype != NL80211_IFTYPE_P2P_GO)
@@ -3007,8 +2969,6 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
                break;
        }
 
-       mutex_unlock(&rdev->devlist_mtx);
-
        return ret;
 }
 
@@ -3170,13 +3130,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                params.radar_required = true;
        }
 
-       mutex_lock(&rdev->devlist_mtx);
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
                                           params.chandef.chan,
                                           CHAN_MODE_SHARED,
                                           radar_detect_width);
-       mutex_unlock(&rdev->devlist_mtx);
-
        if (err)
                return err;
 
@@ -3376,6 +3333,32 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
        return true;
 }
 
+static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
+                              int id)
+{
+       void *attr;
+       int i = 0;
+
+       if (!mask)
+               return true;
+
+       attr = nla_nest_start(msg, id);
+       if (!attr)
+               return false;
+
+       for (i = 0; i < IEEE80211_MAX_CHAINS; i++) {
+               if (!(mask & BIT(i)))
+                       continue;
+
+               if (nla_put_u8(msg, i, signal[i]))
+                       return false;
+       }
+
+       nla_nest_end(msg, attr);
+
+       return true;
+}
+
 static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
                                int flags,
                                struct cfg80211_registered_device *rdev,
@@ -3447,6 +3430,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
        default:
                break;
        }
+       if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) {
+               if (!nl80211_put_signal(msg, sinfo->chains,
+                                       sinfo->chain_signal,
+                                       NL80211_STA_INFO_CHAIN_SIGNAL))
+                       goto nla_put_failure;
+       }
+       if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) {
+               if (!nl80211_put_signal(msg, sinfo->chains,
+                                       sinfo->chain_signal_avg,
+                                       NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
+                       goto nla_put_failure;
+       }
        if (sinfo->filled & STATION_INFO_TX_BITRATE) {
                if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
                                          NL80211_STA_INFO_TX_BITRATE))
@@ -3834,6 +3829,8 @@ static int nl80211_set_station_tdls(struct genl_info *info,
                                    struct station_parameters *params)
 {
        /* Dummy STA entry gets updated once the peer capabilities are known */
+       if (info->attrs[NL80211_ATTR_PEER_AID])
+               params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
                params->ht_capa =
                        nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
@@ -3974,7 +3971,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
                return -EINVAL;
 
-       if (!info->attrs[NL80211_ATTR_STA_AID])
+       if (!info->attrs[NL80211_ATTR_STA_AID] &&
+           !info->attrs[NL80211_ATTR_PEER_AID])
                return -EINVAL;
 
        mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
@@ -3985,7 +3983,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        params.listen_interval =
                nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
 
-       params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
+       if (info->attrs[NL80211_ATTR_PEER_AID])
+               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+       else
+               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
        if (!params.aid || params.aid > IEEE80211_MAX_AID)
                return -EINVAL;
 
@@ -4037,7 +4038,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
 
                /* TDLS peers cannot be added */
-               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+                   info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                /* but don't bother the driver with it */
                params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
@@ -4063,7 +4065,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
                        return -EINVAL;
                /* TDLS peers cannot be added */
-               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+               if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
+                   info->attrs[NL80211_ATTR_PEER_AID])
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_STATION:
@@ -4585,7 +4588,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
            nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
                        cur_params.power_mode) ||
            nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
-                       cur_params.dot11MeshAwakeWindowDuration))
+                       cur_params.dot11MeshAwakeWindowDuration) ||
+           nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+                       cur_params.plink_timeout))
                goto nla_put_failure;
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
@@ -4626,6 +4631,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
        [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
        [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
        [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
 };
 
 static const struct nla_policy
@@ -4634,6 +4640,7 @@ static const struct nla_policy
        [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
+       [NL80211_MESH_SETUP_AUTH_PROTOCOL] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
        [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
                                    .len = IEEE80211_MAX_DATA_LEN },
@@ -4762,6 +4769,9 @@ do {                                                                          \
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
                                  0, 65535, mask,
                                  NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff,
+                                 mask, NL80211_MESHCONF_PLINK_TIMEOUT,
+                                 nla_get_u32);
        if (mask_out)
                *mask_out = mask;
 
@@ -4819,6 +4829,13 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
        if (setup->is_secure)
                setup->user_mpm = true;
 
+       if (tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]) {
+               if (!setup->user_mpm)
+                       return -EINVAL;
+               setup->auth_id =
+                       nla_get_u8(tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]);
+       }
+
        return 0;
 }
 
@@ -4861,18 +4878,13 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
        void *hdr = NULL;
        struct nlattr *nl_reg_rules;
        unsigned int i;
-       int err = -EINVAL;
-
-       mutex_lock(&cfg80211_mutex);
 
        if (!cfg80211_regdomain)
-               goto out;
+               return -EINVAL;
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg) {
-               err = -ENOBUFS;
-               goto out;
-       }
+       if (!msg)
+               return -ENOBUFS;
 
        hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
                             NL80211_CMD_GET_REG);
@@ -4931,8 +4943,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
        nla_nest_end(msg, nl_reg_rules);
 
        genlmsg_end(msg, hdr);
-       err = genlmsg_reply(msg, info);
-       goto out;
+       return genlmsg_reply(msg, info);
 
 nla_put_failure_rcu:
        rcu_read_unlock();
@@ -4940,10 +4951,7 @@ nla_put_failure:
        genlmsg_cancel(msg, hdr);
 put_failure:
        nlmsg_free(msg);
-       err = -EMSGSIZE;
-out:
-       mutex_unlock(&cfg80211_mutex);
-       return err;
+       return -EMSGSIZE;
 }
 
 static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
@@ -5009,12 +5017,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       mutex_lock(&cfg80211_mutex);
-
        r = set_regdom(rd);
        /* set_regdom took ownership */
        rd = NULL;
-       mutex_unlock(&cfg80211_mutex);
 
  bad_reg:
        kfree(rd);
@@ -5064,7 +5069,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->scan)
                return -EOPNOTSUPP;
 
-       mutex_lock(&rdev->sched_scan_mtx);
        if (rdev->scan_req) {
                err = -EBUSY;
                goto unlock;
@@ -5250,7 +5254,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        }
 
  unlock:
-       mutex_unlock(&rdev->sched_scan_mtx);
        return err;
 }
 
@@ -5322,8 +5325,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        if (ie_len > wiphy->max_sched_scan_ie_len)
                return -EINVAL;
 
-       mutex_lock(&rdev->sched_scan_mtx);
-
        if (rdev->sched_scan_req) {
                err = -EINPROGRESS;
                goto out;
@@ -5491,7 +5492,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 out_free:
        kfree(request);
 out:
-       mutex_unlock(&rdev->sched_scan_mtx);
        return err;
 }
 
@@ -5499,17 +5499,12 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb,
                                   struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       int err;
 
        if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
            !rdev->ops->sched_scan_stop)
                return -EOPNOTSUPP;
 
-       mutex_lock(&rdev->sched_scan_mtx);
-       err = __cfg80211_stop_sched_scan(rdev, false);
-       mutex_unlock(&rdev->sched_scan_mtx);
-
-       return err;
+       return __cfg80211_stop_sched_scan(rdev, false);
 }
 
 static int nl80211_start_radar_detection(struct sk_buff *skb,
@@ -5541,12 +5536,11 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (!rdev->ops->start_radar_detection)
                return -EOPNOTSUPP;
 
-       mutex_lock(&rdev->devlist_mtx);
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
                                           chandef.chan, CHAN_MODE_SHARED,
                                           BIT(chandef.width));
        if (err)
-               goto err_locked;
+               return err;
 
        err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
        if (!err) {
@@ -5554,9 +5548,6 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
                wdev->cac_started = true;
                wdev->cac_start_time = jiffies;
        }
-err_locked:
-       mutex_unlock(&rdev->devlist_mtx);
-
        return err;
 }
 
@@ -5939,10 +5930,13 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
        if (local_state_change)
                return 0;
 
-       return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
-                                 ssid, ssid_len, ie, ie_len,
-                                 key.p.key, key.p.key_len, key.idx,
-                                 sae_data, sae_data_len);
+       wdev_lock(dev->ieee80211_ptr);
+       err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
+                                ssid, ssid_len, ie, ie_len,
+                                key.p.key, key.p.key_len, key.idx,
+                                sae_data, sae_data_len);
+       wdev_unlock(dev->ieee80211_ptr);
+       return err;
 }
 
 static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
@@ -6109,9 +6103,12 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        }
 
        err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
-       if (!err)
+       if (!err) {
+               wdev_lock(dev->ieee80211_ptr);
                err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
                                          ssid, ssid_len, &req);
+               wdev_unlock(dev->ieee80211_ptr);
+       }
 
        return err;
 }
@@ -6121,7 +6118,7 @@ static int nl80211_deauthenticate(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];
        const u8 *ie = NULL, *bssid;
-       int ie_len = 0;
+       int ie_len = 0, err;
        u16 reason_code;
        bool local_state_change;
 
@@ -6156,8 +6153,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
 
        local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
 
-       return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
-                                   local_state_change);
+       wdev_lock(dev->ieee80211_ptr);
+       err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
+                                  local_state_change);
+       wdev_unlock(dev->ieee80211_ptr);
+       return err;
 }
 
 static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
@@ -6165,7 +6165,7 @@ static int nl80211_disassociate(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];
        const u8 *ie = NULL, *bssid;
-       int ie_len = 0;
+       int ie_len = 0, err;
        u16 reason_code;
        bool local_state_change;
 
@@ -6200,8 +6200,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
 
        local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
 
-       return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
-                                     local_state_change);
+       wdev_lock(dev->ieee80211_ptr);
+       err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
+                                    local_state_change);
+       wdev_unlock(dev->ieee80211_ptr);
+       return err;
 }
 
 static bool
@@ -6419,6 +6422,8 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
        void *data = NULL;
        int data_len = 0;
 
+       rtnl_lock();
+
        if (cb->args[0]) {
                /*
                 * 0 is a valid index, but not valid for args[0],
@@ -6430,18 +6435,16 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
                                  nl80211_fam.attrbuf, nl80211_fam.maxattr,
                                  nl80211_policy);
                if (err)
-                       return err;
+                       goto out_err;
 
-               mutex_lock(&cfg80211_mutex);
                rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
                                                  nl80211_fam.attrbuf);
                if (IS_ERR(rdev)) {
-                       mutex_unlock(&cfg80211_mutex);
-                       return PTR_ERR(rdev);
+                       err = PTR_ERR(rdev);
+                       goto out_err;
                }
                phy_idx = rdev->wiphy_idx;
                rdev = NULL;
-               mutex_unlock(&cfg80211_mutex);
 
                if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
                        cb->args[1] =
@@ -6453,14 +6456,11 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
                data_len = nla_len((void *)cb->args[1]);
        }
 
-       mutex_lock(&cfg80211_mutex);
        rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
        if (!rdev) {
-               mutex_unlock(&cfg80211_mutex);
-               return -ENOENT;
+               err = -ENOENT;
+               goto out_err;
        }
-       cfg80211_lock_rdev(rdev);
-       mutex_unlock(&cfg80211_mutex);
 
        if (!rdev->ops->testmode_dump) {
                err = -EOPNOTSUPP;
@@ -6501,7 +6501,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
        /* see above */
        cb->args[0] = phy_idx + 1;
  out_err:
-       cfg80211_unlock_rdev(rdev);
+       rtnl_unlock();
        return err;
 }
 
@@ -6709,7 +6709,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                       sizeof(connect.vht_capa));
        }
 
-       err = cfg80211_connect(rdev, dev, &connect, connkeys);
+       wdev_lock(dev->ieee80211_ptr);
+       err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
+       wdev_unlock(dev->ieee80211_ptr);
        if (err)
                kfree(connkeys);
        return err;
@@ -6720,6 +6722,7 @@ static int nl80211_disconnect(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];
        u16 reason;
+       int ret;
 
        if (!info->attrs[NL80211_ATTR_REASON_CODE])
                reason = WLAN_REASON_DEAUTH_LEAVING;
@@ -6733,7 +6736,10 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
                return -EOPNOTSUPP;
 
-       return cfg80211_disconnect(rdev, dev, reason, true);
+       wdev_lock(dev->ieee80211_ptr);
+       ret = cfg80211_disconnect(rdev, dev, reason, true);
+       wdev_unlock(dev->ieee80211_ptr);
+       return ret;
 }
 
 static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
@@ -7152,6 +7158,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                return -EOPNOTSUPP;
 
        switch (wdev->iftype) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+                       return -EINVAL;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_P2P_CLIENT:
@@ -7159,7 +7168,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
-       case NL80211_IFTYPE_P2P_DEVICE:
                break;
        default:
                return -EOPNOTSUPP;
@@ -7187,9 +7195,18 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 
        no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
 
-       err = nl80211_parse_chandef(rdev, info, &chandef);
-       if (err)
-               return err;
+       /* get the channel if any has been specified, otherwise pass NULL to
+        * the driver. The latter will use the current one
+        */
+       chandef.chan = NULL;
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               err = nl80211_parse_chandef(rdev, info, &chandef);
+               if (err)
+                       return err;
+       }
+
+       if (!chandef.chan && offchan)
+               return -EINVAL;
 
        if (!dont_wait_for_ack) {
                msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -7494,6 +7511,23 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
                setup.chandef.chan = NULL;
        }
 
+       if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+               u8 *rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+               int n_rates =
+                       nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+               struct ieee80211_supported_band *sband;
+
+               if (!setup.chandef.chan)
+                       return -EINVAL;
+
+               sband = rdev->wiphy.bands[setup.chandef.chan->band];
+
+               err = ieee80211_get_ratemask(sband, rates, n_rates,
+                                            &setup.basic_rates);
+               if (err)
+                       return err;
+       }
+
        return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
 }
 
@@ -7509,28 +7543,29 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
 static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
                                        struct cfg80211_registered_device *rdev)
 {
+       struct cfg80211_wowlan *wowlan = rdev->wiphy.wowlan_config;
        struct nlattr *nl_pats, *nl_pat;
        int i, pat_len;
 
-       if (!rdev->wowlan->n_patterns)
+       if (!wowlan->n_patterns)
                return 0;
 
        nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN);
        if (!nl_pats)
                return -ENOBUFS;
 
-       for (i = 0; i < rdev->wowlan->n_patterns; i++) {
+       for (i = 0; i < wowlan->n_patterns; i++) {
                nl_pat = nla_nest_start(msg, i + 1);
                if (!nl_pat)
                        return -ENOBUFS;
-               pat_len = rdev->wowlan->patterns[i].pattern_len;
+               pat_len = wowlan->patterns[i].pattern_len;
                if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
                            DIV_ROUND_UP(pat_len, 8),
-                           rdev->wowlan->patterns[i].mask) ||
+                           wowlan->patterns[i].mask) ||
                    nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
-                           pat_len, rdev->wowlan->patterns[i].pattern) ||
+                           pat_len, wowlan->patterns[i].pattern) ||
                    nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
-                               rdev->wowlan->patterns[i].pkt_offset))
+                               wowlan->patterns[i].pkt_offset))
                        return -ENOBUFS;
                nla_nest_end(msg, nl_pat);
        }
@@ -7589,16 +7624,15 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
        void *hdr;
        u32 size = NLMSG_DEFAULT_SIZE;
 
-       if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
-           !rdev->wiphy.wowlan.tcp)
+       if (!rdev->wiphy.wowlan)
                return -EOPNOTSUPP;
 
-       if (rdev->wowlan && rdev->wowlan->tcp) {
+       if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) {
                /* adjust size to have room for all the data */
-               size += rdev->wowlan->tcp->tokens_size +
-                       rdev->wowlan->tcp->payload_len +
-                       rdev->wowlan->tcp->wake_len +
-                       rdev->wowlan->tcp->wake_len / 8;
+               size += rdev->wiphy.wowlan_config->tcp->tokens_size +
+                       rdev->wiphy.wowlan_config->tcp->payload_len +
+                       rdev->wiphy.wowlan_config->tcp->wake_len +
+                       rdev->wiphy.wowlan_config->tcp->wake_len / 8;
        }
 
        msg = nlmsg_new(size, GFP_KERNEL);
@@ -7610,33 +7644,34 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
        if (!hdr)
                goto nla_put_failure;
 
-       if (rdev->wowlan) {
+       if (rdev->wiphy.wowlan_config) {
                struct nlattr *nl_wowlan;
 
                nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
                if (!nl_wowlan)
                        goto nla_put_failure;
 
-               if ((rdev->wowlan->any &&
+               if ((rdev->wiphy.wowlan_config->any &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
-                   (rdev->wowlan->disconnect &&
+                   (rdev->wiphy.wowlan_config->disconnect &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
-                   (rdev->wowlan->magic_pkt &&
+                   (rdev->wiphy.wowlan_config->magic_pkt &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
-                   (rdev->wowlan->gtk_rekey_failure &&
+                   (rdev->wiphy.wowlan_config->gtk_rekey_failure &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
-                   (rdev->wowlan->eap_identity_req &&
+                   (rdev->wiphy.wowlan_config->eap_identity_req &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
-                   (rdev->wowlan->four_way_handshake &&
+                   (rdev->wiphy.wowlan_config->four_way_handshake &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
-                   (rdev->wowlan->rfkill_release &&
+                   (rdev->wiphy.wowlan_config->rfkill_release &&
                     nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
                        goto nla_put_failure;
 
                if (nl80211_send_wowlan_patterns(msg, rdev))
                        goto nla_put_failure;
 
-               if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp))
+               if (nl80211_send_wowlan_tcp(msg,
+                                           rdev->wiphy.wowlan_config->tcp))
                        goto nla_put_failure;
 
                nla_nest_end(msg, nl_wowlan);
@@ -7662,7 +7697,7 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
        u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
        int err, port;
 
-       if (!rdev->wiphy.wowlan.tcp)
+       if (!rdev->wiphy.wowlan->tcp)
                return -EINVAL;
 
        err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP,
@@ -7682,16 +7717,16 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
                return -EINVAL;
 
        data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
-       if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max)
+       if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max)
                return -EINVAL;
 
        if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
-                       rdev->wiphy.wowlan.tcp->data_interval_max ||
+                       rdev->wiphy.wowlan->tcp->data_interval_max ||
            nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
                return -EINVAL;
 
        wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
-       if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max)
+       if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max)
                return -EINVAL;
 
        wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
@@ -7706,13 +7741,13 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
 
                if (!tok->len || tokens_size % tok->len)
                        return -EINVAL;
-               if (!rdev->wiphy.wowlan.tcp->tok)
+               if (!rdev->wiphy.wowlan->tcp->tok)
                        return -EINVAL;
-               if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len)
+               if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len)
                        return -EINVAL;
-               if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len)
+               if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len)
                        return -EINVAL;
-               if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize)
+               if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize)
                        return -EINVAL;
                if (tok->offset + tok->len > data_size)
                        return -EINVAL;
@@ -7720,7 +7755,7 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
 
        if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
                seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
-               if (!rdev->wiphy.wowlan.tcp->seq)
+               if (!rdev->wiphy.wowlan->tcp->seq)
                        return -EINVAL;
                if (seq->len == 0 || seq->len > 4)
                        return -EINVAL;
@@ -7801,17 +7836,16 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
        struct cfg80211_wowlan new_triggers = {};
        struct cfg80211_wowlan *ntrig;
-       struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
+       const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
        int err, i;
-       bool prev_enabled = rdev->wowlan;
+       bool prev_enabled = rdev->wiphy.wowlan_config;
 
-       if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
-           !rdev->wiphy.wowlan.tcp)
+       if (!wowlan)
                return -EOPNOTSUPP;
 
        if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
                cfg80211_rdev_free_wowlan(rdev);
-               rdev->wowlan = NULL;
+               rdev->wiphy.wowlan_config = NULL;
                goto set_wakeup;
        }
 
@@ -7947,11 +7981,12 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                goto error;
        }
        cfg80211_rdev_free_wowlan(rdev);
-       rdev->wowlan = ntrig;
+       rdev->wiphy.wowlan_config = ntrig;
 
  set_wakeup:
-       if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
-               rdev_set_wakeup(rdev, rdev->wowlan);
+       if (rdev->ops->set_wakeup &&
+           prev_enabled != !!rdev->wiphy.wowlan_config)
+               rdev_set_wakeup(rdev, rdev->wiphy.wowlan_config);
 
        return 0;
  error:
@@ -8136,9 +8171,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
        if (wdev->p2p_started)
                return 0;
 
-       mutex_lock(&rdev->devlist_mtx);
        err = cfg80211_can_add_interface(rdev, wdev->iftype);
-       mutex_unlock(&rdev->devlist_mtx);
        if (err)
                return err;
 
@@ -8147,9 +8180,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
                return err;
 
        wdev->p2p_started = true;
-       mutex_lock(&rdev->devlist_mtx);
        rdev->opencount++;
-       mutex_unlock(&rdev->devlist_mtx);
 
        return 0;
 }
@@ -8165,11 +8196,7 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->stop_p2p_device)
                return -EOPNOTSUPP;
 
-       mutex_lock(&rdev->devlist_mtx);
-       mutex_lock(&rdev->sched_scan_mtx);
        cfg80211_stop_p2p_device(rdev, wdev);
-       mutex_unlock(&rdev->sched_scan_mtx);
-       mutex_unlock(&rdev->devlist_mtx);
 
        return 0;
 }
@@ -8312,11 +8339,11 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
                info->user_ptr[0] = rdev;
        } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
                   ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
-               mutex_lock(&cfg80211_mutex);
+               ASSERT_RTNL();
+
                wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
                                                  info->attrs);
                if (IS_ERR(wdev)) {
-                       mutex_unlock(&cfg80211_mutex);
                        if (rtnl)
                                rtnl_unlock();
                        return PTR_ERR(wdev);
@@ -8327,7 +8354,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
 
                if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
                        if (!dev) {
-                               mutex_unlock(&cfg80211_mutex);
                                if (rtnl)
                                        rtnl_unlock();
                                return -EINVAL;
@@ -8341,7 +8367,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
                if (dev) {
                        if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
                            !netif_running(dev)) {
-                               mutex_unlock(&cfg80211_mutex);
                                if (rtnl)
                                        rtnl_unlock();
                                return -ENETDOWN;
@@ -8350,17 +8375,12 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
                        dev_hold(dev);
                } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
                        if (!wdev->p2p_started) {
-                               mutex_unlock(&cfg80211_mutex);
                                if (rtnl)
                                        rtnl_unlock();
                                return -ENETDOWN;
                        }
                }
 
-               cfg80211_lock_rdev(rdev);
-
-               mutex_unlock(&cfg80211_mutex);
-
                info->user_ptr[0] = rdev;
        }
 
@@ -8370,8 +8390,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
 static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
                              struct genl_info *info)
 {
-       if (info->user_ptr[0])
-               cfg80211_unlock_rdev(info->user_ptr[0]);
        if (info->user_ptr[1]) {
                if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
                        struct wireless_dev *wdev = info->user_ptr[1];
@@ -8393,7 +8411,8 @@ static struct genl_ops nl80211_ops[] = {
                .dumpit = nl80211_dump_wiphy,
                .policy = nl80211_policy,
                /* can be retrieved by unprivileged users */
-               .internal_flags = NL80211_FLAG_NEED_WIPHY,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_SET_WIPHY,
@@ -8408,7 +8427,8 @@ static struct genl_ops nl80211_ops[] = {
                .dumpit = nl80211_dump_interface,
                .policy = nl80211_policy,
                /* can be retrieved by unprivileged users */
-               .internal_flags = NL80211_FLAG_NEED_WDEV,
+               .internal_flags = NL80211_FLAG_NEED_WDEV |
+                                 NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_SET_INTERFACE,
@@ -8567,6 +8587,7 @@ static struct genl_ops nl80211_ops[] = {
                .cmd = NL80211_CMD_GET_REG,
                .doit = nl80211_get_reg,
                .policy = nl80211_policy,
+               .internal_flags = NL80211_FLAG_NEED_RTNL,
                /* can be retrieved by unprivileged users */
        },
        {
@@ -8574,6 +8595,7 @@ static struct genl_ops nl80211_ops[] = {
                .doit = nl80211_set_reg,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_REQ_SET_REG,
@@ -9029,8 +9051,6 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
        struct nlattr *nest;
        int i;
 
-       lockdep_assert_held(&rdev->sched_scan_mtx);
-
        if (WARN_ON(!req))
                return 0;
 
@@ -9337,31 +9357,27 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                                NL80211_CMD_DISASSOCIATE, gfp);
 }
 
-void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
-                                size_t len)
+void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
+                                 size_t len)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       const struct ieee80211_mgmt *mgmt = (void *)buf;
+       u32 cmd;
 
-       trace_cfg80211_send_unprot_deauth(dev);
-       nl80211_send_mlme_event(rdev, dev, buf, len,
-                               NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
-}
-EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
+       if (WARN_ON(len < 2))
+               return;
 
-void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
-                                  size_t len)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct wiphy *wiphy = wdev->wiphy;
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       if (ieee80211_is_deauth(mgmt->frame_control))
+               cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE;
+       else
+               cmd = NL80211_CMD_UNPROT_DISASSOCIATE;
 
-       trace_cfg80211_send_unprot_disassoc(dev);
-       nl80211_send_mlme_event(rdev, dev, buf, len,
-                               NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
+       trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len);
+       nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC);
 }
-EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
+EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt);
 
 static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev, int cmd,
@@ -9872,7 +9888,6 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
        u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
 
        if (!nlportid)
@@ -9893,12 +9908,7 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return true;
-       }
-
+       genlmsg_end(msg, hdr);
        genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
        return true;
 
@@ -10341,10 +10351,7 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
        if (nl80211_send_chandef(msg, chandef))
                goto nla_put_failure;
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10410,7 +10417,6 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_probe_status(dev, addr, cookie, acked);
 
@@ -10432,11 +10438,7 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
            (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10502,7 +10504,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err, size = 200;
+       int size = 200;
 
        trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
 
@@ -10588,9 +10590,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                nla_nest_end(msg, reasons);
        }
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0)
-               goto free_msg;
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10610,7 +10610,6 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
                                         reason_code);
@@ -10633,11 +10632,7 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
             nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
                goto nla_put_failure;
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -10695,7 +10690,6 @@ void cfg80211_ft_event(struct net_device *netdev,
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        trace_cfg80211_ft_event(wiphy, netdev, ft_event);
 
@@ -10721,11 +10715,7 @@ void cfg80211_ft_event(struct net_device *netdev,
                nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
                        ft_event->ric_ies);
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, GFP_KERNEL);