]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/wireless/nl80211.c
Merge tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6
[karo-tx-linux.git] / net / wireless / nl80211.c
index 5d3167d71b5fb2a3f44fb676959b11343b17079b..f45706adaf3411813133704d41a12aa9d1d59712 100644 (file)
@@ -223,8 +223,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
                                      .len = 20-1 },
        [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+
        [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
        [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
+       [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
+       [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
+       [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
+
        [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
        [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
        [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
@@ -358,6 +363,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
        [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
        [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
+       [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
+       [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
 };
 
 /* policy for the key attributes */
@@ -1110,6 +1117,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
                        goto nla_put_failure;
        }
        CMD(start_p2p_device, START_P2P_DEVICE);
+       CMD(set_mcast_rate, SET_MCAST_RATE);
 
 #ifdef CONFIG_NL80211_TESTMODE
        CMD(testmode_cmd, TESTMODE);
@@ -1359,51 +1367,83 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
                wdev->iftype == NL80211_IFTYPE_P2P_GO;
 }
 
-static bool nl80211_valid_channel_type(struct genl_info *info,
-                                      enum nl80211_channel_type *channel_type)
+static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
+                                struct genl_info *info,
+                                struct cfg80211_chan_def *chandef)
 {
-       enum nl80211_channel_type tmp;
+       u32 control_freq;
 
-       if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
-               return false;
+       if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+               return -EINVAL;
 
-       tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
-       if (tmp != NL80211_CHAN_NO_HT &&
-           tmp != NL80211_CHAN_HT20 &&
-           tmp != NL80211_CHAN_HT40PLUS &&
-           tmp != NL80211_CHAN_HT40MINUS)
-               return false;
+       control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
 
-       if (channel_type)
-               *channel_type = tmp;
+       chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
+       chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+       chandef->center_freq1 = control_freq;
+       chandef->center_freq2 = 0;
 
-       return true;
+       /* Primary channel not allowed */
+       if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
+               return -EINVAL;
+
+       if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+               enum nl80211_channel_type chantype;
+
+               chantype = nla_get_u32(
+                               info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+
+               switch (chantype) {
+               case NL80211_CHAN_NO_HT:
+               case NL80211_CHAN_HT20:
+               case NL80211_CHAN_HT40PLUS:
+               case NL80211_CHAN_HT40MINUS:
+                       cfg80211_chandef_create(chandef, chandef->chan,
+                                               chantype);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
+               chandef->width =
+                       nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
+               if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
+                       chandef->center_freq1 =
+                               nla_get_u32(
+                                       info->attrs[NL80211_ATTR_CENTER_FREQ1]);
+               if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
+                       chandef->center_freq2 =
+                               nla_get_u32(
+                                       info->attrs[NL80211_ATTR_CENTER_FREQ2]);
+       }
+
+       if (!cfg80211_chandef_valid(chandef))
+               return -EINVAL;
+
+       if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
+                                    IEEE80211_CHAN_DISABLED))
+               return -EINVAL;
+
+       return 0;
 }
 
 static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
                                 struct wireless_dev *wdev,
                                 struct genl_info *info)
 {
-       struct ieee80211_channel *channel;
-       enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-       u32 freq;
+       struct cfg80211_chan_def chandef;
        int result;
        enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
 
        if (wdev)
                iftype = wdev->iftype;
 
-       if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
-               return -EINVAL;
-
        if (!nl80211_can_set_dev_channel(wdev))
                return -EOPNOTSUPP;
 
-       if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
-           !nl80211_valid_channel_type(info, &channel_type))
-               return -EINVAL;
-
-       freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+       result = nl80211_parse_chandef(rdev, info, &chandef);
+       if (result)
+               return result;
 
        mutex_lock(&rdev->devlist_mtx);
        switch (iftype) {
@@ -1413,22 +1453,18 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
                        result = -EBUSY;
                        break;
                }
-               channel = rdev_freq_to_chan(rdev, freq, channel_type);
-               if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
-                                                             channel,
-                                                             channel_type)) {
+               if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) {
                        result = -EINVAL;
                        break;
                }
-               wdev->preset_chan = channel;
-               wdev->preset_chantype = channel_type;
+               wdev->preset_chandef = chandef;
                result = 0;
                break;
        case NL80211_IFTYPE_MESH_POINT:
-               result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type);
+               result = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
                break;
        case NL80211_IFTYPE_MONITOR:
-               result = cfg80211_set_monitor_channel(rdev, freq, channel_type);
+               result = cfg80211_set_monitor_channel(rdev, &chandef);
                break;
        default:
                result = -EINVAL;
@@ -1516,10 +1552,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                result = 0;
 
                mutex_lock(&rdev->mtx);
-       } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
+       } else
                wdev = netdev->ieee80211_ptr;
-       else
-               wdev = NULL;
 
        /*
         * end workaround code, by now the rdev is available
@@ -1579,15 +1613,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-               result = __nl80211_set_channel(rdev, wdev, info);
+               result = __nl80211_set_channel(rdev,
+                               nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
+                               info);
                if (result)
                        goto bad_res;
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
+               struct wireless_dev *txp_wdev = wdev;
                enum nl80211_tx_power_setting type;
                int idx, mbm = 0;
 
+               if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
+                       txp_wdev = NULL;
+
                if (!rdev->ops->set_tx_power) {
                        result = -EOPNOTSUPP;
                        goto bad_res;
@@ -1607,7 +1647,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        mbm = nla_get_u32(info->attrs[idx]);
                }
 
-               result = rdev_set_tx_power(rdev, type, mbm);
+               result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
                if (result)
                        goto bad_res;
        }
@@ -1744,6 +1784,35 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
               ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32);
 }
 
+static int nl80211_send_chandef(struct sk_buff *msg,
+                                struct cfg80211_chan_def *chandef)
+{
+       WARN_ON(!cfg80211_chandef_valid(chandef));
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+                       chandef->chan->center_freq))
+               return -ENOBUFS;
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+       case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_40:
+               if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+                               cfg80211_get_chandef_type(chandef)))
+                       return -ENOBUFS;
+               break;
+       default:
+               break;
+       }
+       if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
+               return -ENOBUFS;
+       if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
+               return -ENOBUFS;
+       if (chandef->center_freq2 &&
+           nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
+               return -ENOBUFS;
+       return 0;
+}
+
 static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
                              struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev)
@@ -1770,15 +1839,18 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
                goto nla_put_failure;
 
        if (rdev->ops->get_channel) {
-               struct ieee80211_channel *chan;
-               enum nl80211_channel_type channel_type;
-
-               chan = rdev_get_channel(rdev, wdev, &channel_type);
-               if (chan &&
-                   (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
-                                chan->center_freq) ||
-                    nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
-                                channel_type)))
+               int ret;
+               struct cfg80211_chan_def chandef;
+
+               ret = rdev_get_channel(rdev, wdev, &chandef);
+               if (ret == 0) {
+                       if (nl80211_send_chandef(msg, &chandef))
+                               goto nla_put_failure;
+               }
+       }
+
+       if (wdev->ssid_len) {
+               if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
                        goto nla_put_failure;
        }
 
@@ -2482,11 +2554,10 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
                    wdev->iftype != NL80211_IFTYPE_P2P_GO)
                        continue;
 
-               if (!wdev->preset_chan)
+               if (!wdev->preset_chandef.chan)
                        continue;
 
-               params->channel = wdev->preset_chan;
-               params->channel_type = wdev->preset_chantype;
+               params->chandef = wdev->preset_chandef;
                ret = true;
                break;
        }
@@ -2607,31 +2678,46 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                        info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
        }
 
-       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-               enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-
-               if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
-                   !nl80211_valid_channel_type(info, &channel_type))
+       if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
+               if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+                       return -EINVAL;
+               params.p2p_ctwindow =
+                       nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
+               if (params.p2p_ctwindow > 127)
                        return -EINVAL;
+               if (params.p2p_ctwindow != 0 &&
+                   !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
+               u8 tmp;
 
-               params.channel = rdev_freq_to_chan(rdev,
-                       nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
-                       channel_type);
-               if (!params.channel)
+               if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
                        return -EINVAL;
-               params.channel_type = channel_type;
-       } else if (wdev->preset_chan) {
-               params.channel = wdev->preset_chan;
-               params.channel_type = wdev->preset_chantype;
+               tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
+               if (tmp > 1)
+                       return -EINVAL;
+               params.p2p_opp_ps = tmp;
+               if (params.p2p_opp_ps != 0 &&
+                   !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               err = nl80211_parse_chandef(rdev, info, &params.chandef);
+               if (err)
+                       return err;
+       } else if (wdev->preset_chandef.chan) {
+               params.chandef = wdev->preset_chandef;
        } else if (!nl80211_get_ap_channel(rdev, &params))
                return -EINVAL;
 
-       if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel,
-                                         params.channel_type))
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
                return -EINVAL;
 
        mutex_lock(&rdev->devlist_mtx);
-       err = cfg80211_can_use_chan(rdev, wdev, params.channel,
+       err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan,
                                    CHAN_MODE_SHARED);
        mutex_unlock(&rdev->devlist_mtx);
 
@@ -2640,10 +2726,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
 
        err = rdev_start_ap(rdev, dev, &params);
        if (!err) {
-               wdev->preset_chan = params.channel;
-               wdev->preset_chantype = params.channel_type;
+               wdev->preset_chandef = params.chandef;
                wdev->beacon_interval = params.beacon_interval;
-               wdev->channel = params.channel;
+               wdev->channel = params.chandef.chan;
+               wdev->ssid_len = params.ssid_len;
+               memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
        }
        return err;
 }
@@ -2775,29 +2862,52 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
 
        rate = nla_nest_start(msg, attr);
        if (!rate)
-               goto nla_put_failure;
+               return false;
 
        /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
        bitrate = cfg80211_calculate_bitrate(info);
        /* report 16-bit bitrate only if we can */
        bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
-       if ((bitrate > 0 &&
-            nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) ||
-           (bitrate_compat > 0 &&
-            nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) ||
-           ((info->flags & RATE_INFO_FLAGS_MCS) &&
-            nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) ||
-           ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) &&
-            nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) ||
-           ((info->flags & RATE_INFO_FLAGS_SHORT_GI) &&
-            nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)))
-               goto nla_put_failure;
+       if (bitrate > 0 &&
+           nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate))
+               return false;
+       if (bitrate_compat > 0 &&
+           nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
+               return false;
+
+       if (info->flags & RATE_INFO_FLAGS_MCS) {
+               if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+                       return false;
+       } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) {
+               if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs))
+                       return false;
+               if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+                       return false;
+       }
 
        nla_nest_end(msg, rate);
        return true;
-
-nla_put_failure:
-       return false;
 }
 
 static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
@@ -3560,6 +3670,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
        params.use_short_slot_time = -1;
        params.ap_isolate = -1;
        params.ht_opmode = -1;
+       params.p2p_ctwindow = -1;
+       params.p2p_opp_ps = -1;
 
        if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
                params.use_cts_prot =
@@ -3582,6 +3694,32 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
                params.ht_opmode =
                        nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
 
+       if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
+               if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+                       return -EINVAL;
+               params.p2p_ctwindow =
+                       nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
+               if (params.p2p_ctwindow < 0)
+                       return -EINVAL;
+               if (params.p2p_ctwindow != 0 &&
+                   !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
+               u8 tmp;
+
+               if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+                       return -EINVAL;
+               tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
+               if (tmp > 1)
+                       return -EINVAL;
+               params.p2p_opp_ps = tmp;
+               if (params.p2p_opp_ps &&
+                   !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
+                       return -EINVAL;
+       }
+
        if (!rdev->ops->change_bss)
                return -EOPNOTSUPP;
 
@@ -4670,6 +4808,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                            struct cfg80211_internal_bss *intbss)
 {
        struct cfg80211_bss *res = &intbss->pub;
+       const struct cfg80211_bss_ies *ies;
        void *hdr;
        struct nlattr *bss;
 
@@ -4690,16 +4829,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
        if (!bss)
                goto nla_put_failure;
        if ((!is_zero_ether_addr(res->bssid) &&
-            nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) ||
-           (res->information_elements && res->len_information_elements &&
-            nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
-                    res->len_information_elements,
-                    res->information_elements)) ||
-           (res->beacon_ies && res->len_beacon_ies &&
-            res->beacon_ies != res->information_elements &&
-            nla_put(msg, NL80211_BSS_BEACON_IES,
-                    res->len_beacon_ies, res->beacon_ies)))
+            nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
+               goto nla_put_failure;
+
+       rcu_read_lock();
+       ies = rcu_dereference(res->ies);
+       if (ies && ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
+                                      ies->len, ies->data)) {
+               rcu_read_unlock();
                goto nla_put_failure;
+       }
+       ies = rcu_dereference(res->beacon_ies);
+       if (ies && ies->len && nla_put(msg, NL80211_BSS_BEACON_IES,
+                                      ies->len, ies->data)) {
+               rcu_read_unlock();
+               goto nla_put_failure;
+       }
+       rcu_read_unlock();
+
        if (res->tsf &&
            nla_put_u64(msg, NL80211_BSS_TSF, res->tsf))
                goto nla_put_failure;
@@ -5318,8 +5465,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
 
-       if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
-           !info->attrs[NL80211_ATTR_SSID] ||
+       if (!info->attrs[NL80211_ATTR_SSID] ||
            !nla_len(info->attrs[NL80211_ATTR_SSID]))
                return -EINVAL;
 
@@ -5354,34 +5500,17 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
-       if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
-               enum nl80211_channel_type channel_type;
-
-               if (!nl80211_valid_channel_type(info, &channel_type))
-                       return -EINVAL;
-
-               if (channel_type != NL80211_CHAN_NO_HT &&
-                   !(wiphy->features & NL80211_FEATURE_HT_IBSS))
-                       return -EINVAL;
-
-               ibss.channel_type = channel_type;
-       } else {
-               ibss.channel_type = NL80211_CHAN_NO_HT;
-       }
+       err = nl80211_parse_chandef(rdev, info, &ibss.chandef);
+       if (err)
+               return err;
 
-       ibss.channel = rdev_freq_to_chan(rdev,
-               nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
-               ibss.channel_type);
-       if (!ibss.channel ||
-           ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
-           ibss.channel->flags & IEEE80211_CHAN_DISABLED)
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
                return -EINVAL;
 
-       /* Both channels should be able to initiate communication */
-       if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
-            ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
-           !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
-                                         ibss.channel_type))
+       if (ibss.chandef.width > NL80211_CHAN_WIDTH_40)
+               return -EINVAL;
+       if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+           !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
                return -EINVAL;
 
        ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
@@ -5393,7 +5522,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                int n_rates =
                        nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
                struct ieee80211_supported_band *sband =
-                       wiphy->bands[ibss.channel->band];
+                       wiphy->bands[ibss.chandef.chan->band];
 
                err = ieee80211_get_ratemask(sband, rates, n_rates,
                                             &ibss.basic_rates);
@@ -5415,7 +5544,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                if (IS_ERR(connkeys))
                        return PTR_ERR(connkeys);
 
-               if ((ibss.channel_type != NL80211_CHAN_NO_HT) && no_ht) {
+               if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
+                   no_ht) {
                        kfree(connkeys);
                        return -EINVAL;
                }
@@ -5444,6 +5574,36 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
        return cfg80211_leave_ibss(rdev, dev, false);
 }
 
+static int nl80211_set_mcast_rate(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];
+       int mcast_rate[IEEE80211_NUM_BANDS];
+       u32 nla_rate;
+       int err;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+               return -EOPNOTSUPP;
+
+       if (!rdev->ops->set_mcast_rate)
+               return -EOPNOTSUPP;
+
+       memset(mcast_rate, 0, sizeof(mcast_rate));
+
+       if (!info->attrs[NL80211_ATTR_MCAST_RATE])
+               return -EINVAL;
+
+       nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]);
+       if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
+               return -EINVAL;
+
+       err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+
+       return err;
+}
+
+
 #ifdef CONFIG_NL80211_TESTMODE
 static struct genl_multicast_group nl80211_testmode_mcgrp = {
        .name = "testmode",
@@ -5906,12 +6066,11 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct wireless_dev *wdev = info->user_ptr[1];
-       struct ieee80211_channel *chan;
+       struct cfg80211_chan_def chandef;
        struct sk_buff *msg;
        void *hdr;
        u64 cookie;
-       enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-       u32 freq, duration;
+       u32 duration;
        int err;
 
        if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
@@ -5932,14 +6091,9 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
            duration > rdev->wiphy.max_remain_on_channel_duration)
                return -EINVAL;
 
-       if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
-           !nl80211_valid_channel_type(info, &channel_type))
-               return -EINVAL;
-
-       freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
-       chan = rdev_freq_to_chan(rdev, freq, channel_type);
-       if (chan == NULL)
-               return -EINVAL;
+       err = nl80211_parse_chandef(rdev, info, &chandef);
+       if (err)
+               return err;
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
@@ -5953,8 +6107,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
                goto free_msg;
        }
 
-       err = rdev_remain_on_channel(rdev, wdev, chan, channel_type, duration,
-                                    &cookie);
+       err = rdev_remain_on_channel(rdev, wdev, chandef.chan,
+                                    duration, &cookie);
 
        if (err)
                goto free_msg;
@@ -6173,10 +6327,7 @@ static int nl80211_tx_mgmt(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 ieee80211_channel *chan;
-       enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-       bool channel_type_valid = false;
-       u32 freq;
+       struct cfg80211_chan_def chandef;
        int err;
        void *hdr = NULL;
        u64 cookie;
@@ -6186,8 +6337,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 
        dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
 
-       if (!info->attrs[NL80211_ATTR_FRAME] ||
-           !info->attrs[NL80211_ATTR_WIPHY_FREQ])
+       if (!info->attrs[NL80211_ATTR_FRAME])
                return -EINVAL;
 
        if (!rdev->ops->mgmt_tx)
@@ -6222,12 +6372,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 
        }
 
-       if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
-               if (!nl80211_valid_channel_type(info, &channel_type))
-                       return -EINVAL;
-               channel_type_valid = true;
-       }
-
        offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
 
        if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
@@ -6235,10 +6379,9 @@ 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]);
 
-       freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
-       chan = rdev_freq_to_chan(rdev, freq, channel_type);
-       if (chan == NULL)
-               return -EINVAL;
+       err = nl80211_parse_chandef(rdev, info, &chandef);
+       if (err)
+               return err;
 
        if (!dont_wait_for_ack) {
                msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -6254,8 +6397,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, channel_type,
-                                   channel_type_valid, wait,
+       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);
@@ -6397,14 +6539,13 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
 };
 
 static int nl80211_set_cqm_txe(struct genl_info *info,
-                               u32 rate, u32 pkts, u32 intvl)
+                              u32 rate, u32 pkts, u32 intvl)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct wireless_dev *wdev;
        struct net_device *dev = info->user_ptr[1];
 
-       if ((rate < 0 || rate > 100) ||
-           (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL))
+       if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
                return -EINVAL;
 
        wdev = dev->ieee80211_ptr;
@@ -6519,21 +6660,12 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-               enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-
-               if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
-                   !nl80211_valid_channel_type(info, &channel_type))
-                       return -EINVAL;
-
-               setup.channel = rdev_freq_to_chan(rdev,
-                       nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
-                       channel_type);
-               if (!setup.channel)
-                       return -EINVAL;
-               setup.channel_type = channel_type;
+               err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+               if (err)
+                       return err;
        } else {
                /* cfg80211_join_mesh() will sort it out */
-               setup.channel = NULL;
+               setup.chandef.chan = NULL;
        }
 
        return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
@@ -6899,16 +7031,35 @@ static int nl80211_probe_client(struct sk_buff *skb,
 static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct cfg80211_beacon_registration *reg, *nreg;
+       int rv;
 
        if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
                return -EOPNOTSUPP;
 
-       if (rdev->ap_beacons_nlportid)
-               return -EBUSY;
+       nreg = kzalloc(sizeof(*nreg), GFP_KERNEL);
+       if (!nreg)
+               return -ENOMEM;
+
+       /* First, check if already registered. */
+       spin_lock_bh(&rdev->beacon_registrations_lock);
+       list_for_each_entry(reg, &rdev->beacon_registrations, list) {
+               if (reg->nlportid == info->snd_portid) {
+                       rv = -EALREADY;
+                       goto out_err;
+               }
+       }
+       /* Add it to the list */
+       nreg->nlportid = info->snd_portid;
+       list_add(&nreg->list, &rdev->beacon_registrations);
 
-       rdev->ap_beacons_nlportid = info->snd_portid;
+       spin_unlock_bh(&rdev->beacon_registrations_lock);
 
        return 0;
+out_err:
+       spin_unlock_bh(&rdev->beacon_registrations_lock);
+       kfree(nreg);
+       return rv;
 }
 
 static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
@@ -6932,7 +7083,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       err = rdev->ops->start_p2p_device(&rdev->wiphy, wdev);
+       err = rdev_start_p2p_device(rdev, wdev);
        if (err)
                return err;
 
@@ -6958,7 +7109,7 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
        if (!wdev->p2p_started)
                return 0;
 
-       rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
+       rdev_stop_p2p_device(rdev, wdev);
        wdev->p2p_started = false;
 
        mutex_lock(&rdev->devlist_mtx);
@@ -7625,6 +7776,14 @@ static struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_SET_MCAST_RATE,
+               .doit = nl80211_set_mcast_rate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -8326,7 +8485,6 @@ static void nl80211_send_remain_on_chan_event(
        int cmd, struct cfg80211_registered_device *rdev,
        struct wireless_dev *wdev, u64 cookie,
        struct ieee80211_channel *chan,
-       enum nl80211_channel_type channel_type,
        unsigned int duration, gfp_t gfp)
 {
        struct sk_buff *msg;
@@ -8347,7 +8505,8 @@ static void nl80211_send_remain_on_chan_event(
                                         wdev->netdev->ifindex)) ||
            nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
            nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
-           nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) ||
+           nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+                       NL80211_CHAN_NO_HT) ||
            nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
                goto nla_put_failure;
 
@@ -8369,23 +8528,20 @@ static void nl80211_send_remain_on_chan_event(
 void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
                                    struct wireless_dev *wdev, u64 cookie,
                                    struct ieee80211_channel *chan,
-                                   enum nl80211_channel_type channel_type,
                                    unsigned int duration, gfp_t gfp)
 {
        nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
                                          rdev, wdev, cookie, chan,
-                                         channel_type, duration, gfp);
+                                         duration, gfp);
 }
 
 void nl80211_send_remain_on_channel_cancel(
        struct cfg80211_registered_device *rdev,
        struct wireless_dev *wdev,
-       u64 cookie, struct ieee80211_channel *chan,
-       enum nl80211_channel_type channel_type, gfp_t gfp)
+       u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)
 {
        nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
-                                         rdev, wdev, cookie, chan,
-                                         channel_type, 0, gfp);
+                                         rdev, wdev, cookie, chan, 0, gfp);
 }
 
 void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
@@ -8741,8 +8897,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
 }
 
 void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev, int freq,
-                             enum nl80211_channel_type type, gfp_t gfp)
+                             struct net_device *netdev,
+                             struct cfg80211_chan_def *chandef, gfp_t gfp)
 {
        struct sk_buff *msg;
        void *hdr;
@@ -8757,9 +8913,10 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
-           nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
-           nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type))
+       if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+               goto nla_put_failure;
+
+       if (nl80211_send_chandef(msg, chandef))
                goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
@@ -8914,46 +9071,96 @@ EXPORT_SYMBOL(cfg80211_probe_status);
 
 void cfg80211_report_obss_beacon(struct wiphy *wiphy,
                                 const u8 *frame, size_t len,
-                                int freq, int sig_dbm, gfp_t gfp)
+                                int freq, int sig_dbm)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
-       u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid);
+       struct cfg80211_beacon_registration *reg;
 
        trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm);
 
-       if (!nlportid)
-               return;
+       spin_lock_bh(&rdev->beacon_registrations_lock);
+       list_for_each_entry(reg, &rdev->beacon_registrations, list) {
+               msg = nlmsg_new(len + 100, GFP_ATOMIC);
+               if (!msg) {
+                       spin_unlock_bh(&rdev->beacon_registrations_lock);
+                       return;
+               }
+
+               hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+               if (!hdr)
+                       goto nla_put_failure;
 
-       msg = nlmsg_new(len + 100, gfp);
+               if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+                   (freq &&
+                    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+                   (sig_dbm &&
+                    nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
+                   nla_put(msg, NL80211_ATTR_FRAME, len, frame))
+                       goto nla_put_failure;
+
+               genlmsg_end(msg, hdr);
+
+               genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
+       }
+       spin_unlock_bh(&rdev->beacon_registrations_lock);
+       return;
+
+ nla_put_failure:
+       spin_unlock_bh(&rdev->beacon_registrations_lock);
+       if (hdr)
+               genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_report_obss_beacon);
+
+void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
+                               enum nl80211_tdls_operation oper,
+                               u16 reason_code, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+       int err;
+
+       trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
+                                        reason_code);
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
 
-       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);
        if (!hdr) {
                nlmsg_free(msg);
                return;
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           (freq &&
-            nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
-           (sig_dbm &&
-            nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
-           nla_put(msg, NL80211_ATTR_FRAME, len, frame))
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+           nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
+           (reason_code > 0 &&
+            nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
                goto nla_put_failure;
 
-       genlmsg_end(msg, hdr);
+       err = genlmsg_end(msg, hdr);
+       if (err < 0) {
+               nlmsg_free(msg);
+               return;
+       }
 
-       genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
        return;
 
  nla_put_failure:
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
-EXPORT_SYMBOL(cfg80211_report_obss_beacon);
+EXPORT_SYMBOL(cfg80211_tdls_oper_request);
 
 static int nl80211_netlink_notify(struct notifier_block * nb,
                                  unsigned long state,
@@ -8962,6 +9169,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
        struct netlink_notify *notify = _notify;
        struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
+       struct cfg80211_beacon_registration *reg, *tmp;
 
        if (state != NETLINK_URELEASE)
                return NOTIFY_DONE;
@@ -8971,8 +9179,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
        list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
                list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
                        cfg80211_mlme_unregister_socket(wdev, notify->portid);
-               if (rdev->ap_beacons_nlportid == notify->portid)
-                       rdev->ap_beacons_nlportid = 0;
+
+               spin_lock_bh(&rdev->beacon_registrations_lock);
+               list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
+                                        list) {
+                       if (reg->nlportid == notify->portid) {
+                               list_del(&reg->list);
+                               kfree(reg);
+                               break;
+                       }
+               }
+               spin_unlock_bh(&rdev->beacon_registrations_lock);
        }
 
        rcu_read_unlock();