]> Pileus Git - ~andy/linux/blobdiff - net/wireless/nl80211.c
Merge remote-tracking branch 'wireless-next/master' into mac80211-next
[~andy/linux] / net / wireless / nl80211.c
index 138dc3bb8b67d8c345531a95ce0c8342271ce79e..2d0c19c6133b3586d2f512fcb487396a349bf091 100644 (file)
@@ -376,6 +376,10 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
        [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
        [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
+       [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 },
+       [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 },
+       [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 },
+       [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
 };
 
 /* policy for the key attributes */
@@ -564,12 +568,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
        if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
            nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
                goto nla_put_failure;
-       if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
-           nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN))
-               goto nla_put_failure;
-       if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
-           nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
-               goto nla_put_failure;
+       if (chan->flags & IEEE80211_CHAN_NO_IR) {
+               if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR))
+                       goto nla_put_failure;
+               if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS))
+                       goto nla_put_failure;
+       }
        if (chan->flags & IEEE80211_CHAN_RADAR) {
                if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
                        goto nla_put_failure;
@@ -1184,6 +1188,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
        struct nlattr *nl_bands, *nl_band;
        struct nlattr *nl_freqs, *nl_freq;
        struct nlattr *nl_cmds;
+       struct nlattr *nl_vendor_cmds;
        enum ieee80211_band band;
        struct ieee80211_channel *chan;
        int i;
@@ -1247,10 +1252,6 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
                    nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
                        goto nla_put_failure;
-               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
-                   nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ))
-                       goto nla_put_failure;
-
                state->split_start++;
                if (state->split)
                        break;
@@ -1579,6 +1580,24 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                if (nl80211_send_coalesce(msg, dev))
                        goto nla_put_failure;
 
+               if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
+                   (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
+                    nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
+                       goto nla_put_failure;
+               state->split_start++;
+               break;
+       case 11:
+               nl_vendor_cmds = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+               if (!nl_vendor_cmds)
+                       goto nla_put_failure;
+
+               for (i = 0; i < dev->wiphy.n_vendor_commands; i++)
+                       if (nla_put(msg, i + 1,
+                                   sizeof(struct nl80211_vendor_cmd_info),
+                                   &dev->wiphy.vendor_commands[i].info))
+                               goto nla_put_failure;
+               nla_nest_end(msg, nl_vendor_cmds);
+
                /* done */
                state->split_start = 0;
                break;
@@ -2187,7 +2206,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
 }
 
 static int nl80211_send_chandef(struct sk_buff *msg,
-                                struct cfg80211_chan_def *chandef)
+                               const struct cfg80211_chan_def *chandef)
 {
        WARN_ON(!cfg80211_chandef_valid(chandef));
 
@@ -3236,6 +3255,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                        return PTR_ERR(params.acl);
        }
 
+       wdev_lock(wdev);
        err = rdev_start_ap(rdev, dev, &params);
        if (!err) {
                wdev->preset_chandef = params.chandef;
@@ -3244,6 +3264,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                wdev->ssid_len = params.ssid_len;
                memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
        }
+       wdev_unlock(wdev);
 
        kfree(params.acl);
 
@@ -3272,7 +3293,11 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       return rdev_change_beacon(rdev, dev, &params);
+       wdev_lock(wdev);
+       err = rdev_change_beacon(rdev, dev, &params);
+       wdev_unlock(wdev);
+
+       return err;
 }
 
 static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
@@ -4144,6 +4169,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                params.vht_capa =
                        nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 
+       if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
+               params.opmode_notif_used = true;
+               params.opmode_notif =
+                       nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
+       }
+
        if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
                params.plink_action =
                        nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
@@ -4478,7 +4509,9 @@ static int nl80211_set_bss(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 bss_parameters params;
+       int err;
 
        memset(&params, 0, sizeof(params));
        /* default to not changing parameters */
@@ -4544,7 +4577,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
                return -EOPNOTSUPP;
 
-       return rdev_change_bss(rdev, dev, &params);
+       wdev_lock(wdev);
+       err = rdev_change_bss(rdev, dev, &params);
+       wdev_unlock(wdev);
+
+       return err;
 }
 
 static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
@@ -5098,7 +5135,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        char *alpha2 = NULL;
        int rem_reg_rules = 0, r = 0;
        u32 num_rules = 0, rule_idx = 0, size_of_regd;
-       u8 dfs_region = 0;
+       enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
        struct ieee80211_regdomain *rd = NULL;
 
        if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
@@ -5119,6 +5156,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
+       if (!reg_is_valid_request(alpha2))
+               return -EINVAL;
+
        size_of_regd = sizeof(struct ieee80211_regdomain) +
                       num_rules * sizeof(struct ieee80211_reg_rule);
 
@@ -5365,10 +5405,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
                request->flags = nla_get_u32(
                        info->attrs[NL80211_ATTR_SCAN_FLAGS]);
-               if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
-                    !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
-                   ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
-                    !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
+               if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+                   !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
                        err = -EOPNOTSUPP;
                        goto out_free;
                }
@@ -5608,10 +5646,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
                request->flags = nla_get_u32(
                        info->attrs[NL80211_ATTR_SCAN_FLAGS]);
-               if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
-                    !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
-                   ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
-                    !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
+               if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+                   !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
                        err = -EOPNOTSUPP;
                        goto out_free;
                }
@@ -5655,8 +5691,13 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_chan_def chandef;
+       enum nl80211_dfs_regions dfs_region;
        int err;
 
+       dfs_region = reg_get_dfs_region(wdev->wiphy);
+       if (dfs_region == NL80211_DFS_UNSET)
+               return -EINVAL;
+
        err = nl80211_parse_chandef(rdev, info, &chandef);
        if (err)
                return err;
@@ -5674,7 +5715,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (err == 0)
                return -EINVAL;
 
-       if (chandef.chan->dfs_state != NL80211_DFS_USABLE)
+       if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef))
                return -EINVAL;
 
        if (!rdev->ops->start_radar_detection)
@@ -5814,7 +5855,11 @@ skip_beacons:
        if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
                params.block_tx = true;
 
-       return rdev_channel_switch(rdev, dev, &params);
+       wdev_lock(wdev);
+       err = rdev_channel_switch(rdev, dev, &params);
+       wdev_unlock(wdev);
+
+       return err;
 }
 
 static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
@@ -6677,6 +6722,40 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+static struct sk_buff *
+__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
+                           int approxlen, u32 portid, u32 seq,
+                           enum nl80211_commands cmd,
+                           enum nl80211_attrs attr, gfp_t gfp)
+{
+       struct sk_buff *skb;
+       void *hdr;
+       struct nlattr *data;
+
+       skb = nlmsg_new(approxlen + 100, gfp);
+       if (!skb)
+               return NULL;
+
+       hdr = nl80211hdr_put(skb, portid, seq, 0, cmd);
+       if (!hdr) {
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
+               goto nla_put_failure;
+       data = nla_nest_start(skb, attr);
+
+       ((void **)skb->cb)[0] = rdev;
+       ((void **)skb->cb)[1] = hdr;
+       ((void **)skb->cb)[2] = data;
+
+       return skb;
+
+ nla_put_failure:
+       kfree_skb(skb);
+       return NULL;
+}
 
 #ifdef CONFIG_NL80211_TESTMODE
 static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
@@ -6701,11 +6780,11 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
        if (!info->attrs[NL80211_ATTR_TESTDATA])
                return -EINVAL;
 
-       rdev->testmode_info = info;
+       rdev->cur_cmd_info = info;
        err = rdev_testmode_cmd(rdev, wdev,
                                nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
                                nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
-       rdev->testmode_info = NULL;
+       rdev->cur_cmd_info = NULL;
 
        return err;
 }
@@ -6805,77 +6884,14 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
        return err;
 }
 
-static struct sk_buff *
-__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
-                             int approxlen, u32 portid, u32 seq, gfp_t gfp)
-{
-       struct sk_buff *skb;
-       void *hdr;
-       struct nlattr *data;
-
-       skb = nlmsg_new(approxlen + 100, gfp);
-       if (!skb)
-               return NULL;
-
-       hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE);
-       if (!hdr) {
-               kfree_skb(skb);
-               return NULL;
-       }
-
-       if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
-               goto nla_put_failure;
-       data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
-
-       ((void **)skb->cb)[0] = rdev;
-       ((void **)skb->cb)[1] = hdr;
-       ((void **)skb->cb)[2] = data;
-
-       return skb;
-
- nla_put_failure:
-       kfree_skb(skb);
-       return NULL;
-}
-
-struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
-                                                 int approxlen)
-{
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       if (WARN_ON(!rdev->testmode_info))
-               return NULL;
-
-       return __cfg80211_testmode_alloc_skb(rdev, approxlen,
-                               rdev->testmode_info->snd_portid,
-                               rdev->testmode_info->snd_seq,
-                               GFP_KERNEL);
-}
-EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
-
-int cfg80211_testmode_reply(struct sk_buff *skb)
-{
-       struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
-       void *hdr = ((void **)skb->cb)[1];
-       struct nlattr *data = ((void **)skb->cb)[2];
-
-       if (WARN_ON(!rdev->testmode_info)) {
-               kfree_skb(skb);
-               return -EINVAL;
-       }
-
-       nla_nest_end(skb, data);
-       genlmsg_end(skb, hdr);
-       return genlmsg_reply(skb, rdev->testmode_info);
-}
-EXPORT_SYMBOL(cfg80211_testmode_reply);
-
 struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
                                                  int approxlen, gfp_t gfp)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 
-       return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
+       return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
+                                          NL80211_CMD_TESTMODE,
+                                          NL80211_ATTR_TESTDATA, gfp);
 }
 EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
 
@@ -7315,8 +7331,8 @@ static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
 static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
        [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
                                    .len = NL80211_MAX_SUPP_RATES },
-       [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
-                                .len = NL80211_MAX_SUPP_HT_RATES },
+       [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
+                               .len = NL80211_MAX_SUPP_HT_RATES },
 };
 
 static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
@@ -7330,9 +7346,6 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
        struct nlattr *tx_rates;
        struct ieee80211_supported_band *sband;
 
-       if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
-               return -EINVAL;
-
        if (!rdev->ops->set_bitrate_mask)
                return -EOPNOTSUPP;
 
@@ -7340,17 +7353,20 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
        /* Default to all rates enabled */
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
                sband = rdev->wiphy.bands[i];
-               mask.control[i].legacy =
-                       sband ? (1 << sband->n_bitrates) - 1 : 0;
-               if (sband)
-                       memcpy(mask.control[i].mcs,
-                              sband->ht_cap.mcs.rx_mask,
-                              sizeof(mask.control[i].mcs));
-               else
-                       memset(mask.control[i].mcs, 0,
-                              sizeof(mask.control[i].mcs));
+
+               if (!sband)
+                       continue;
+
+               mask.control[i].legacy = (1 << sband->n_bitrates) - 1;
+               memcpy(mask.control[i].ht_mcs,
+                      sband->ht_cap.mcs.rx_mask,
+                      sizeof(mask.control[i].ht_mcs));
        }
 
+       /* if no rates are given set it back to the defaults */
+       if (!info->attrs[NL80211_ATTR_TX_RATES])
+               goto out;
+
        /*
         * The nested attribute uses enum nl80211_band as the index. This maps
         * directly to the enum ieee80211_band values used in cfg80211.
@@ -7375,12 +7391,12 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                            nla_len(tb[NL80211_TXRATE_LEGACY]))
                                return -EINVAL;
                }
-               if (tb[NL80211_TXRATE_MCS]) {
+               if (tb[NL80211_TXRATE_HT]) {
                        if (!ht_rateset_to_mask(
                                        sband,
-                                       nla_data(tb[NL80211_TXRATE_MCS]),
-                                       nla_len(tb[NL80211_TXRATE_MCS]),
-                                       mask.control[band].mcs))
+                                       nla_data(tb[NL80211_TXRATE_HT]),
+                                       nla_len(tb[NL80211_TXRATE_HT]),
+                                       mask.control[band].ht_mcs))
                                return -EINVAL;
                }
 
@@ -7391,7 +7407,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                                return -EINVAL;
 
                        for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
-                               if (mask.control[band].mcs[i])
+                               if (mask.control[band].ht_mcs[i])
                                        break;
 
                        /* legacy and mcs rates may not be both empty */
@@ -7400,6 +7416,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                }
        }
 
+out:
        return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
 }
 
@@ -7447,10 +7464,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        void *hdr = NULL;
        u64 cookie;
        struct sk_buff *msg = NULL;
-       unsigned int wait = 0;
-       bool offchan, no_cck, dont_wait_for_ack;
-
-       dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
+       struct cfg80211_mgmt_tx_params params = {
+               .dont_wait_for_ack =
+                       info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK],
+       };
 
        if (!info->attrs[NL80211_ATTR_FRAME])
                return -EINVAL;
@@ -7477,24 +7494,24 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_DURATION]) {
                if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
                        return -EINVAL;
-               wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
+               params.wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
 
                /*
                 * We should wait on the channel for at least a minimum amount
                 * of time (10ms) but no longer than the driver supports.
                 */
-               if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
-                   wait > rdev->wiphy.max_remain_on_channel_duration)
+               if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
+                   params.wait > rdev->wiphy.max_remain_on_channel_duration)
                        return -EINVAL;
 
        }
 
-       offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
+       params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
 
-       if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
+       if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
                return -EINVAL;
 
-       no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
+       params.no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
 
        /* get the channel if any has been specified, otherwise pass NULL to
         * the driver. The latter will use the current one
@@ -7506,10 +7523,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                        return err;
        }
 
-       if (!chandef.chan && offchan)
+       if (!chandef.chan && params.offchan)
                return -EINVAL;
 
-       if (!dont_wait_for_ack) {
+       if (!params.dont_wait_for_ack) {
                msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
                if (!msg)
                        return -ENOMEM;
@@ -7522,10 +7539,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait,
-                                   nla_data(info->attrs[NL80211_ATTR_FRAME]),
-                                   nla_len(info->attrs[NL80211_ATTR_FRAME]),
-                                   no_cck, dont_wait_for_ack, &cookie);
+       params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+       params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+       params.chan = chandef.chan;
+       err = cfg80211_mlme_mgmt_tx(rdev, wdev, &params, &cookie);
        if (err)
                goto free_msg;
 
@@ -8859,6 +8876,111 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,
        return 0;
 }
 
+static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev =
+               __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
+       int i, err;
+       u32 vid, subcmd;
+
+       if (!rdev->wiphy.vendor_commands)
+               return -EOPNOTSUPP;
+
+       if (IS_ERR(wdev)) {
+               err = PTR_ERR(wdev);
+               if (err != -EINVAL)
+                       return err;
+               wdev = NULL;
+       } else if (wdev->wiphy != &rdev->wiphy) {
+               return -EINVAL;
+       }
+
+       if (!info->attrs[NL80211_ATTR_VENDOR_ID] ||
+           !info->attrs[NL80211_ATTR_VENDOR_SUBCMD])
+               return -EINVAL;
+
+       vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]);
+       subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]);
+       for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
+               const struct wiphy_vendor_command *vcmd;
+               void *data = NULL;
+               int len = 0;
+
+               vcmd = &rdev->wiphy.vendor_commands[i];
+
+               if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
+                       continue;
+
+               if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
+                                  WIPHY_VENDOR_CMD_NEED_NETDEV)) {
+                       if (!wdev)
+                               return -EINVAL;
+                       if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
+                           !wdev->netdev)
+                               return -EINVAL;
+
+                       if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
+                               if (wdev->netdev &&
+                                   !netif_running(wdev->netdev))
+                                       return -ENETDOWN;
+                               if (!wdev->netdev && !wdev->p2p_started)
+                                       return -ENETDOWN;
+                       }
+               } else {
+                       wdev = NULL;
+               }
+
+               if (info->attrs[NL80211_ATTR_VENDOR_DATA]) {
+                       data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]);
+                       len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]);
+               }
+
+               rdev->cur_cmd_info = info;
+               err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev,
+                                                         data, len);
+               rdev->cur_cmd_info = NULL;
+               return err;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
+                                          enum nl80211_commands cmd,
+                                          enum nl80211_attrs attr,
+                                          int approxlen)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       if (WARN_ON(!rdev->cur_cmd_info))
+               return NULL;
+
+       return __cfg80211_alloc_vendor_skb(rdev, approxlen,
+                                          rdev->cur_cmd_info->snd_portid,
+                                          rdev->cur_cmd_info->snd_seq,
+                                          cmd, attr, GFP_KERNEL);
+}
+EXPORT_SYMBOL(__cfg80211_alloc_reply_skb);
+
+int cfg80211_vendor_cmd_reply(struct sk_buff *skb)
+{
+       struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
+       void *hdr = ((void **)skb->cb)[1];
+       struct nlattr *data = ((void **)skb->cb)[2];
+
+       if (WARN_ON(!rdev->cur_cmd_info)) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       nla_nest_end(skb, data);
+       genlmsg_end(skb, hdr);
+       return genlmsg_reply(skb, rdev->cur_cmd_info);
+}
+EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply);
+
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -9583,6 +9705,14 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_VENDOR,
+               .doit = nl80211_vendor_cmd,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 /* notification functions */
@@ -10810,21 +10940,18 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
        struct wiphy *wiphy = wdev->wiphy;
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 
-       trace_cfg80211_ch_switch_notify(dev, chandef);
+       ASSERT_WDEV_LOCK(wdev);
 
-       wdev_lock(wdev);
+       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))
-               goto out;
+               return;
 
        wdev->channel = chandef->chan;
        nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
-out:
-       wdev_unlock(wdev);
-       return;
 }
 EXPORT_SYMBOL(cfg80211_ch_switch_notify);
 
@@ -10883,7 +11010,7 @@ EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
 
 void
 nl80211_radar_notify(struct cfg80211_registered_device *rdev,
-                    struct cfg80211_chan_def *chandef,
+                    const struct cfg80211_chan_def *chandef,
                     enum nl80211_radar_event event,
                     struct net_device *netdev, gfp_t gfp)
 {