cfg80211: fix locking
[linux-2.6-block.git] / net / wireless / nl80211.c
index 2456e4ee445e04ab884fa17a2fb8ed835ab59996..cf4ac786b20a8c039fcf51d4e7cc93812d803be7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This is the new netlink-based wireless configuration interface.
  *
- * Copyright 2006, 2007        Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net>
  */
 
 #include <linux/if.h>
@@ -57,22 +57,28 @@ static int get_drv_dev_by_info_ifindex(struct nlattr **attrs,
 static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
        [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
        [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
-                                     .len = BUS_ID_SIZE-1 },
+                                     .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_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
+       [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
+       [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
+       [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
 
        [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
        [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
        [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
 
        [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
+       [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
 
        [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
                                    .len = WLAN_MAX_KEY_LEN },
        [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
        [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
        [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
+       [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
 
        [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
        [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
@@ -116,8 +122,48 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
                                .len = IEEE80211_MAX_SSID_LEN },
        [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
        [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
+       [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
+       [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
+       [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
+       [NL80211_ATTR_STA_FLAGS2] = {
+               .len = sizeof(struct nl80211_sta_flag_update),
+       },
+       [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
+       [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
+       [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
+       [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
 };
 
+/* IE validation */
+static bool is_valid_ie_attr(const struct nlattr *attr)
+{
+       const u8 *pos;
+       int len;
+
+       if (!attr)
+               return true;
+
+       pos = nla_data(attr);
+       len = nla_len(attr);
+
+       while (len) {
+               u8 elemlen;
+
+               if (len < 2)
+                       return false;
+               len -= 2;
+
+               elemlen = pos[1];
+               if (elemlen > len)
+                       return false;
+
+               len -= elemlen;
+               pos += 2 + elemlen;
+       }
+
+       return true;
+}
+
 /* message building helper */
 static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
                                   int flags, u8 cmd)
@@ -126,6 +172,30 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
        return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
 }
 
+static int nl80211_msg_put_channel(struct sk_buff *msg,
+                                  struct ieee80211_channel *chan)
+{
+       NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
+                   chan->center_freq);
+
+       if (chan->flags & IEEE80211_CHAN_DISABLED)
+               NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
+       if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+               NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
+       if (chan->flags & IEEE80211_CHAN_NO_IBSS)
+               NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
+       if (chan->flags & IEEE80211_CHAN_RADAR)
+               NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
+
+       NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+                   DBM_TO_MBM(chan->max_power));
+
+       return 0;
+
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
 /* netlink command implementations */
 
 static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
@@ -149,8 +219,24 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
 
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
        NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
+
+       NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
+                  dev->wiphy.retry_short);
+       NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
+                  dev->wiphy.retry_long);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+                   dev->wiphy.frag_threshold);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+                   dev->wiphy.rts_threshold);
+
        NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
                   dev->wiphy.max_scan_ssids);
+       NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
+                   dev->wiphy.max_scan_ie_len);
+
+       NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
+               sizeof(u32) * dev->wiphy.n_cipher_suites,
+               dev->wiphy.cipher_suites);
 
        nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
        if (!nl_modes)
@@ -202,20 +288,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                                goto nla_put_failure;
 
                        chan = &dev->wiphy.bands[band]->channels[i];
-                       NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
-                                   chan->center_freq);
-
-                       if (chan->flags & IEEE80211_CHAN_DISABLED)
-                               NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
-                       if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
-                               NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
-                       if (chan->flags & IEEE80211_CHAN_NO_IBSS)
-                               NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
-                       if (chan->flags & IEEE80211_CHAN_RADAR)
-                               NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
 
-                       NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
-                                   DBM_TO_MBM(chan->max_power));
+                       if (nl80211_msg_put_channel(msg, chan))
+                               goto nla_put_failure;
 
                        nla_nest_end(msg, nl_freq);
                }
@@ -273,8 +348,20 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        CMD(assoc, ASSOCIATE);
        CMD(deauth, DEAUTHENTICATE);
        CMD(disassoc, DISASSOCIATE);
+       CMD(join_ibss, JOIN_IBSS);
 
 #undef CMD
+
+       if (dev->ops->connect || dev->ops->auth) {
+               i++;
+               NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
+       }
+
+       if (dev->ops->disconnect || dev->ops->deauth) {
+               i++;
+               NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
+       }
+
        nla_nest_end(msg, nl_cmds);
 
        return genlmsg_end(msg, hdr);
@@ -317,21 +404,21 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(dev))
                return PTR_ERR(dev);
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                goto out_err;
 
        if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
                goto out_free;
 
-       cfg80211_put_dev(dev);
+       cfg80211_unlock_rdev(dev);
 
        return genlmsg_unicast(msg, info->snd_pid);
 
  out_free:
        nlmsg_free(msg);
  out_err:
-       cfg80211_put_dev(dev);
+       cfg80211_unlock_rdev(dev);
        return -ENOBUFS;
 }
 
@@ -365,6 +452,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev;
        int result = 0, rem_txq_params = 0;
        struct nlattr *nl_txq_params;
+       u32 changed;
+       u8 retry_short = 0, retry_long = 0;
+       u32 frag_threshold = 0, rts_threshold = 0;
 
        rtnl_lock();
 
@@ -372,6 +462,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
        rdev = __cfg80211_drv_from_info(info);
        if (IS_ERR(rdev)) {
+               mutex_unlock(&cfg80211_mutex);
                result = PTR_ERR(rdev);
                goto unlock;
        }
@@ -418,7 +509,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
                struct ieee80211_channel *chan;
                struct ieee80211_sta_ht_cap *ht_cap;
-               u32 freq, sec_freq;
+               u32 freq;
 
                if (!rdev->ops->set_channel) {
                        result = -EOPNOTSUPP;
@@ -444,33 +535,28 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
                        goto bad_res;
 
-               if (channel_type == NL80211_CHAN_HT40MINUS)
-                       sec_freq = freq - 20;
-               else if (channel_type == NL80211_CHAN_HT40PLUS)
-                       sec_freq = freq + 20;
-               else
-                       sec_freq = 0;
-
-               ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
-
-               /* no HT capabilities */
-               if (channel_type != NL80211_CHAN_NO_HT &&
-                   !ht_cap->ht_supported)
+               if (channel_type == NL80211_CHAN_HT40MINUS &&
+                   (chan->flags & IEEE80211_CHAN_NO_HT40MINUS))
                        goto bad_res;
+               else if (channel_type == NL80211_CHAN_HT40PLUS &&
+                        (chan->flags & IEEE80211_CHAN_NO_HT40PLUS))
+                       goto bad_res;
+
+               /*
+                * At this point we know if that if HT40 was requested
+                * we are allowed to use it and the extension channel
+                * exists.
+                */
 
-               if (sec_freq) {
-                       struct ieee80211_channel *schan;
+               ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
 
-                       /* no 40 MHz capabilities */
+               /* no HT capabilities or intolerant */
+               if (channel_type != NL80211_CHAN_NO_HT) {
+                       if (!ht_cap->ht_supported)
+                               goto bad_res;
                        if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
                            (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
                                goto bad_res;
-
-                       schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
-
-                       /* Secondary channel not allowed */
-                       if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
-                               goto bad_res;
                }
 
                result = rdev->ops->set_channel(&rdev->wiphy, chan,
@@ -479,6 +565,84 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        goto bad_res;
        }
 
+       changed = 0;
+
+       if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
+               retry_short = nla_get_u8(
+                       info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
+               if (retry_short == 0) {
+                       result = -EINVAL;
+                       goto bad_res;
+               }
+               changed |= WIPHY_PARAM_RETRY_SHORT;
+       }
+
+       if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
+               retry_long = nla_get_u8(
+                       info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
+               if (retry_long == 0) {
+                       result = -EINVAL;
+                       goto bad_res;
+               }
+               changed |= WIPHY_PARAM_RETRY_LONG;
+       }
+
+       if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
+               frag_threshold = nla_get_u32(
+                       info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
+               if (frag_threshold < 256) {
+                       result = -EINVAL;
+                       goto bad_res;
+               }
+               if (frag_threshold != (u32) -1) {
+                       /*
+                        * Fragments (apart from the last one) are required to
+                        * have even length. Make the fragmentation code
+                        * simpler by stripping LSB should someone try to use
+                        * odd threshold value.
+                        */
+                       frag_threshold &= ~0x1;
+               }
+               changed |= WIPHY_PARAM_FRAG_THRESHOLD;
+       }
+
+       if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
+               rts_threshold = nla_get_u32(
+                       info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
+               changed |= WIPHY_PARAM_RTS_THRESHOLD;
+       }
+
+       if (changed) {
+               u8 old_retry_short, old_retry_long;
+               u32 old_frag_threshold, old_rts_threshold;
+
+               if (!rdev->ops->set_wiphy_params) {
+                       result = -EOPNOTSUPP;
+                       goto bad_res;
+               }
+
+               old_retry_short = rdev->wiphy.retry_short;
+               old_retry_long = rdev->wiphy.retry_long;
+               old_frag_threshold = rdev->wiphy.frag_threshold;
+               old_rts_threshold = rdev->wiphy.rts_threshold;
+
+               if (changed & WIPHY_PARAM_RETRY_SHORT)
+                       rdev->wiphy.retry_short = retry_short;
+               if (changed & WIPHY_PARAM_RETRY_LONG)
+                       rdev->wiphy.retry_long = retry_long;
+               if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
+                       rdev->wiphy.frag_threshold = frag_threshold;
+               if (changed & WIPHY_PARAM_RTS_THRESHOLD)
+                       rdev->wiphy.rts_threshold = rts_threshold;
+
+               result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
+               if (result) {
+                       rdev->wiphy.retry_short = old_retry_short;
+                       rdev->wiphy.retry_long = old_retry_long;
+                       rdev->wiphy.frag_threshold = old_frag_threshold;
+                       rdev->wiphy.rts_threshold = old_rts_threshold;
+               }
+       }
 
  bad_res:
        mutex_unlock(&rdev->mtx);
@@ -489,6 +653,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
 
 static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+                             struct cfg80211_registered_device *rdev,
                              struct net_device *dev)
 {
        void *hdr;
@@ -498,6 +663,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                return -1;
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
        NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
        return genlmsg_end(msg, hdr);
@@ -532,7 +698,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
                        }
                        if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
                                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                              wdev->netdev) < 0) {
+                                              dev, wdev->netdev) < 0) {
                                mutex_unlock(&dev->devlist_mtx);
                                goto out;
                        }
@@ -562,15 +728,16 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                goto out_err;
 
-       if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
+       if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
+                              dev, netdev) < 0)
                goto out_free;
 
        dev_put(netdev);
-       cfg80211_put_dev(dev);
+       cfg80211_unlock_rdev(dev);
 
        return genlmsg_unicast(msg, info->snd_pid);
 
@@ -578,7 +745,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
        nlmsg_free(msg);
  out_err:
        dev_put(netdev);
-       cfg80211_put_dev(dev);
+       cfg80211_unlock_rdev(dev);
        return -ENOBUFS;
 }
 
@@ -615,8 +782,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
        struct vif_params params;
-       int err, ifindex;
-       enum nl80211_iftype type;
+       int err;
+       enum nl80211_iftype otype, ntype;
        struct net_device *dev;
        u32 _flags, *flags = NULL;
        bool change = false;
@@ -629,31 +796,26 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto unlock_rtnl;
 
-       ifindex = dev->ifindex;
-       type = dev->ieee80211_ptr->iftype;
-       dev_put(dev);
+       otype = ntype = dev->ieee80211_ptr->iftype;
 
        if (info->attrs[NL80211_ATTR_IFTYPE]) {
-               enum nl80211_iftype ntype;
-
                ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
-               if (type != ntype)
+               if (otype != ntype)
                        change = true;
-               type = ntype;
-               if (type > NL80211_IFTYPE_MAX) {
+               if (ntype > NL80211_IFTYPE_MAX) {
                        err = -EINVAL;
                        goto unlock;
                }
        }
 
        if (!drv->ops->change_virtual_intf ||
-           !(drv->wiphy.interface_modes & (1 << type))) {
+           !(drv->wiphy.interface_modes & (1 << ntype))) {
                err = -EOPNOTSUPP;
                goto unlock;
        }
 
        if (info->attrs[NL80211_ATTR_MESH_ID]) {
-               if (type != NL80211_IFTYPE_MESH_POINT) {
+               if (ntype != NL80211_IFTYPE_MESH_POINT) {
                        err = -EINVAL;
                        goto unlock;
                }
@@ -663,7 +825,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
-               if (type != NL80211_IFTYPE_MONITOR) {
+               if (ntype != NL80211_IFTYPE_MONITOR) {
                        err = -EINVAL;
                        goto unlock;
                }
@@ -677,16 +839,21 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (change)
-               err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
-                                                   type, flags, &params);
+               err = drv->ops->change_virtual_intf(&drv->wiphy, dev,
+                                                   ntype, flags, &params);
        else
                err = 0;
 
-       dev = __dev_get_by_index(&init_net, ifindex);
-       WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type));
+       WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype);
+
+       if (!err && (ntype != otype)) {
+               if (otype == NL80211_IFTYPE_ADHOC)
+                       cfg80211_clear_ibss(dev, false);
+       }
 
  unlock:
-       cfg80211_put_dev(drv);
+       dev_put(dev);
+       cfg80211_unlock_rdev(drv);
  unlock_rtnl:
        rtnl_unlock();
        return err;
@@ -739,7 +906,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                type, err ? NULL : &flags, &params);
 
  unlock:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
  unlock_rtnl:
        rtnl_unlock();
        return err;
@@ -767,7 +934,7 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
        err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
  unlock_rtnl:
        rtnl_unlock();
        return err;
@@ -832,7 +999,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg) {
                err = -ENOMEM;
                goto out;
@@ -870,7 +1037,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
        err = -ENOBUFS;
        nlmsg_free(msg);
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  unlock_rtnl:
        rtnl_unlock();
@@ -920,9 +1087,17 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
        }
 
        err = func(&drv->wiphy, dev, key_idx);
+#ifdef CONFIG_WIRELESS_EXT
+       if (!err) {
+               if (func == drv->ops->set_default_key)
+                       dev->ieee80211_ptr->wext.default_key = key_idx;
+               else
+                       dev->ieee80211_ptr->wext.default_mgmt_key = key_idx;
+       }
+#endif
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
 
  unlock_rtnl:
@@ -934,7 +1109,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
 static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
-       int err;
+       int err, i;
        struct net_device *dev;
        struct key_params params;
        u8 key_idx = 0;
@@ -950,6 +1125,11 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
                params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
        }
 
+       if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
+               params.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
+               params.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
+       }
+
        if (info->attrs[NL80211_ATTR_KEY_IDX])
                key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
 
@@ -958,44 +1138,8 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
-       if (key_idx > 5)
-               return -EINVAL;
-
-       /*
-        * Disallow pairwise keys with non-zero index unless it's WEP
-        * (because current deployments use pairwise WEP keys with
-        * non-zero indizes but 802.11i clearly specifies to use zero)
-        */
-       if (mac_addr && key_idx &&
-           params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
-           params.cipher != WLAN_CIPHER_SUITE_WEP104)
-               return -EINVAL;
-
-       /* TODO: add definitions for the lengths to linux/ieee80211.h */
-       switch (params.cipher) {
-       case WLAN_CIPHER_SUITE_WEP40:
-               if (params.key_len != 5)
-                       return -EINVAL;
-               break;
-       case WLAN_CIPHER_SUITE_TKIP:
-               if (params.key_len != 32)
-                       return -EINVAL;
-               break;
-       case WLAN_CIPHER_SUITE_CCMP:
-               if (params.key_len != 16)
-                       return -EINVAL;
-               break;
-       case WLAN_CIPHER_SUITE_WEP104:
-               if (params.key_len != 13)
-                       return -EINVAL;
-               break;
-       case WLAN_CIPHER_SUITE_AES_CMAC:
-               if (params.key_len != 16)
-                       return -EINVAL;
-               break;
-       default:
+       if (cfg80211_validate_key_settings(&params, key_idx, mac_addr))
                return -EINVAL;
-       }
 
        rtnl_lock();
 
@@ -1003,6 +1147,14 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto unlock_rtnl;
 
+       for (i = 0; i < drv->wiphy.n_cipher_suites; i++)
+               if (params.cipher == drv->wiphy.cipher_suites[i])
+                       break;
+       if (i == drv->wiphy.n_cipher_suites) {
+               err = -EINVAL;
+               goto out;
+       }
+
        if (!drv->ops->add_key) {
                err = -EOPNOTSUPP;
                goto out;
@@ -1011,7 +1163,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
        err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  unlock_rtnl:
        rtnl_unlock();
@@ -1049,8 +1201,17 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
 
        err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
 
+#ifdef CONFIG_WIRELESS_EXT
+       if (!err) {
+               if (key_idx == dev->ieee80211_ptr->wext.default_key)
+                       dev->ieee80211_ptr->wext.default_key = -1;
+               else if (key_idx == dev->ieee80211_ptr->wext.default_mgmt_key)
+                       dev->ieee80211_ptr->wext.default_mgmt_key = -1;
+       }
+#endif
+
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
 
  unlock_rtnl:
@@ -1069,6 +1230,9 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
        struct beacon_parameters params;
        int haveinfo = 0;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
+               return -EINVAL;
+
        rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -1142,7 +1306,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
        err = call(&drv->wiphy, dev, &params);
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  unlock_rtnl:
        rtnl_unlock();
@@ -1174,7 +1338,7 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
        err = drv->ops->del_beacon(&drv->wiphy, dev);
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  unlock_rtnl:
        rtnl_unlock();
@@ -1186,15 +1350,36 @@ static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
        [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
        [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
        [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
+       [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
 };
 
-static int parse_station_flags(struct nlattr *nla, u32 *staflags)
+static int parse_station_flags(struct genl_info *info,
+                              struct station_parameters *params)
 {
        struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
+       struct nlattr *nla;
        int flag;
 
-       *staflags = 0;
+       /*
+        * Try parsing the new attribute first so userspace
+        * can specify both for older kernels.
+        */
+       nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
+       if (nla) {
+               struct nl80211_sta_flag_update *sta_flags;
+
+               sta_flags = nla_data(nla);
+               params->sta_flags_mask = sta_flags->mask;
+               params->sta_flags_set = sta_flags->set;
+               if ((params->sta_flags_mask |
+                    params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
+                       return -EINVAL;
+               return 0;
+       }
+
+       /* if present, parse the old attribute */
 
+       nla = info->attrs[NL80211_ATTR_STA_FLAGS];
        if (!nla)
                return 0;
 
@@ -1202,11 +1387,12 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags)
                             nla, sta_flags_policy))
                return -EINVAL;
 
-       *staflags = STATION_FLAG_CHANGED;
+       params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
+       params->sta_flags_mask &= ~1;
 
        for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
                if (flags[flag])
-                       *staflags |= (1<<flag);
+                       params->sta_flags_set |= (1<<flag);
 
        return 0;
 }
@@ -1386,7 +1572,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
        cb->args[1] = sta_idx;
        err = skb->len;
  out_err:
-       cfg80211_put_dev(dev);
+       cfg80211_unlock_rdev(dev);
  out_rtnl:
        rtnl_unlock();
 
@@ -1424,7 +1610,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto out;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                goto out;
 
@@ -1438,7 +1624,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
  out_free:
        nlmsg_free(msg);
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1502,8 +1688,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                params.ht_capa =
                        nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
 
-       if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
-                               &params.station_flags))
+       if (parse_station_flags(info, &params))
                return -EINVAL;
 
        if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
@@ -1517,6 +1702,51 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                goto out_rtnl;
 
        err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
+       if (err)
+               goto out;
+
+       /* validate settings */
+       err = 0;
+
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+               /* disallow mesh-specific things */
+               if (params.plink_action)
+                       err = -EINVAL;
+               break;
+       case NL80211_IFTYPE_STATION:
+               /* disallow everything but AUTHORIZED flag */
+               if (params.plink_action)
+                       err = -EINVAL;
+               if (params.vlan)
+                       err = -EINVAL;
+               if (params.supported_rates)
+                       err = -EINVAL;
+               if (params.ht_capa)
+                       err = -EINVAL;
+               if (params.listen_interval >= 0)
+                       err = -EINVAL;
+               if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
+                       err = -EINVAL;
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               /* disallow things mesh doesn't support */
+               if (params.vlan)
+                       err = -EINVAL;
+               if (params.ht_capa)
+                       err = -EINVAL;
+               if (params.listen_interval >= 0)
+                       err = -EINVAL;
+               if (params.supported_rates)
+                       err = -EINVAL;
+               if (params.sta_flags_mask)
+                       err = -EINVAL;
+               break;
+       default:
+               err = -EINVAL;
+       }
+
        if (err)
                goto out;
 
@@ -1530,7 +1760,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
  out:
        if (params.vlan)
                dev_put(params.vlan);
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1551,9 +1781,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        if (!info->attrs[NL80211_ATTR_MAC])
                return -EINVAL;
 
-       if (!info->attrs[NL80211_ATTR_STA_AID])
-               return -EINVAL;
-
        if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
                return -EINVAL;
 
@@ -1567,13 +1794,18 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
        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_STA_AID]) {
+               params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
+               if (!params.aid || params.aid > IEEE80211_MAX_AID)
+                       return -EINVAL;
+       }
+
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
                params.ht_capa =
                        nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
 
-       if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS],
-                               &params.station_flags))
+       if (parse_station_flags(info, &params))
                return -EINVAL;
 
        rtnl_lock();
@@ -1583,6 +1815,38 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                goto out_rtnl;
 
        err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
+       if (err)
+               goto out;
+
+       /* validate settings */
+       err = 0;
+
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+               /* all ok but must have AID */
+               if (!params.aid)
+                       err = -EINVAL;
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               /* disallow things mesh doesn't support */
+               if (params.vlan)
+                       err = -EINVAL;
+               if (params.aid)
+                       err = -EINVAL;
+               if (params.ht_capa)
+                       err = -EINVAL;
+               if (params.listen_interval >= 0)
+                       err = -EINVAL;
+               if (params.supported_rates)
+                       err = -EINVAL;
+               if (params.sta_flags_mask)
+                       err = -EINVAL;
+               break;
+       default:
+               err = -EINVAL;
+       }
+
        if (err)
                goto out;
 
@@ -1601,7 +1865,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
  out:
        if (params.vlan)
                dev_put(params.vlan);
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1625,6 +1889,13 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto out_rtnl;
 
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EINVAL;
+               goto out;
+       }
+
        if (!drv->ops->del_station) {
                err = -EOPNOTSUPP;
                goto out;
@@ -1633,7 +1904,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
        err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1764,7 +2035,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
        cb->args[1] = path_idx;
        err = skb->len;
  out_err:
-       cfg80211_put_dev(dev);
+       cfg80211_unlock_rdev(dev);
  out_rtnl:
        rtnl_unlock();
 
@@ -1808,7 +2079,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto out;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                goto out;
 
@@ -1822,7 +2093,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
  out_free:
        nlmsg_free(msg);
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1871,7 +2142,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
        err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1919,7 +2190,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
        err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1951,7 +2222,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
        err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2007,7 +2278,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
        err = drv->ops->change_bss(&drv->wiphy, dev, &params);
 
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2124,7 +2395,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
                goto out;
 
        /* Draw up a netlink message to send back */
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg) {
                err = -ENOBUFS;
                goto out;
@@ -2173,7 +2444,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        err = -EMSGSIZE;
  out:
        /* Cleanup */
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2279,7 +2550,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
 
  out:
        /* cleanup */
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2302,7 +2573,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
        if (!cfg80211_regdomain)
                goto out;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg) {
                err = -ENOBUFS;
                goto out;
@@ -2385,18 +2656,24 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                        rem_reg_rules) {
                num_rules++;
                if (num_rules > NL80211_MAX_SUPP_REG_RULES)
-                       goto bad_reg;
+                       return -EINVAL;
        }
 
-       if (!reg_is_valid_request(alpha2))
-               return -EINVAL;
+       mutex_lock(&cfg80211_mutex);
+
+       if (!reg_is_valid_request(alpha2)) {
+               r = -EINVAL;
+               goto bad_reg;
+       }
 
        size_of_regd = sizeof(struct ieee80211_regdomain) +
                (num_rules * sizeof(struct ieee80211_reg_rule));
 
        rd = kzalloc(size_of_regd, GFP_KERNEL);
-       if (!rd)
-               return -ENOMEM;
+       if (!rd) {
+               r = -ENOMEM;
+               goto bad_reg;
+       }
 
        rd->n_reg_rules = num_rules;
        rd->alpha2[0] = alpha2[0];
@@ -2413,20 +2690,49 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 
                rule_idx++;
 
-               if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
+               if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
+                       r = -EINVAL;
                        goto bad_reg;
+               }
        }
 
        BUG_ON(rule_idx != num_rules);
 
-       mutex_lock(&cfg80211_mutex);
        r = set_regdom(rd);
+
        mutex_unlock(&cfg80211_mutex);
+
        return r;
 
  bad_reg:
+       mutex_unlock(&cfg80211_mutex);
        kfree(rd);
-       return -EINVAL;
+       return r;
+}
+
+static int validate_scan_freqs(struct nlattr *freqs)
+{
+       struct nlattr *attr1, *attr2;
+       int n_channels = 0, tmp1, tmp2;
+
+       nla_for_each_nested(attr1, freqs, tmp1) {
+               n_channels++;
+               /*
+                * Some hardware has a limited channel list for
+                * scanning, and it is pretty much nonsensical
+                * to scan for a channel twice, so disallow that
+                * and don't require drivers to check that the
+                * channel list they get isn't longer than what
+                * they can scan, as long as they can scan all
+                * the channels they registered at once.
+                */
+               nla_for_each_nested(attr2, freqs, tmp2)
+                       if (attr1 != attr2 &&
+                           nla_get_u32(attr1) == nla_get_u32(attr2))
+                               return 0;
+       }
+
+       return n_channels;
 }
 
 static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
@@ -2438,10 +2744,13 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        struct ieee80211_channel *channel;
        struct nlattr *attr;
        struct wiphy *wiphy;
-       int err, tmp, n_ssids = 0, n_channels = 0, i;
+       int err, tmp, n_ssids = 0, n_channels, i;
        enum ieee80211_band band;
        size_t ie_len;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
        rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
@@ -2466,13 +2775,15 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
-               nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
-                       n_channels++;
+               n_channels = validate_scan_freqs(
+                               info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
                if (!n_channels) {
                        err = -EINVAL;
                        goto out;
                }
        } else {
+               n_channels = 0;
+
                for (band = 0; band < IEEE80211_NUM_BANDS; band++)
                        if (wiphy->bands[band])
                                n_channels += wiphy->bands[band]->n_channels;
@@ -2492,6 +2803,11 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        else
                ie_len = 0;
 
+       if (ie_len > wiphy->max_scan_ie_len) {
+               err = -EINVAL;
+               goto out;
+       }
+
        request = kzalloc(sizeof(*request)
                        + sizeof(*ssid) * n_ssids
                        + sizeof(channel) * n_channels
@@ -2554,7 +2870,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
        if (info->attrs[NL80211_ATTR_IE]) {
                request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
-               memcpy(request->ie, nla_data(info->attrs[NL80211_ATTR_IE]),
+               memcpy((void *)request->ie,
+                      nla_data(info->attrs[NL80211_ATTR_IE]),
                       request->ie_len);
        }
 
@@ -2564,13 +2881,16 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        drv->scan_req = request;
        err = drv->ops->scan(&drv->wiphy, dev, request);
 
+       if (!err)
+               nl80211_send_scan_start(drv, dev);
+
  out_free:
        if (err) {
                drv->scan_req = NULL;
                kfree(request);
        }
  out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2687,7 +3007,7 @@ static int nl80211_dump_scan(struct sk_buff *skb,
 
        cb->args[1] = idx;
        err = skb->len;
-       cfg80211_put_dev(dev);
+       cfg80211_unlock_rdev(dev);
  out_put_netdev:
        dev_put(netdev);
 
@@ -2696,19 +3016,54 @@ static int nl80211_dump_scan(struct sk_buff *skb,
 
 static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
 {
-       return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
-               auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
-               auth_type == NL80211_AUTHTYPE_FT ||
-               auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
+       return auth_type <= NL80211_AUTHTYPE_MAX;
+}
+
+static bool nl80211_valid_wpa_versions(u32 wpa_versions)
+{
+       return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
+                                 NL80211_WPA_VERSION_2));
+}
+
+static bool nl80211_valid_akm_suite(u32 akm)
+{
+       return akm == WLAN_AKM_SUITE_8021X ||
+               akm == WLAN_AKM_SUITE_PSK;
+}
+
+static bool nl80211_valid_cipher_suite(u32 cipher)
+{
+       return cipher == WLAN_CIPHER_SUITE_WEP40 ||
+               cipher == WLAN_CIPHER_SUITE_WEP104 ||
+               cipher == WLAN_CIPHER_SUITE_TKIP ||
+               cipher == WLAN_CIPHER_SUITE_CCMP ||
+               cipher == WLAN_CIPHER_SUITE_AES_CMAC;
 }
 
+
 static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
        struct net_device *dev;
-       struct cfg80211_auth_request req;
-       struct wiphy *wiphy;
-       int err;
+       struct ieee80211_channel *chan;
+       const u8 *bssid, *ssid, *ie = NULL;
+       int err, ssid_len, ie_len = 0;
+       enum nl80211_auth_type auth_type;
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_SSID])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+               return -EINVAL;
 
        rtnl_lock();
 
@@ -2731,72 +3086,129 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (!info->attrs[NL80211_ATTR_MAC]) {
+       bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       chan = ieee80211_get_channel(&drv->wiphy,
+               nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+       if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
                err = -EINVAL;
                goto out;
        }
 
-       wiphy = &drv->wiphy;
-       memset(&req, 0, sizeof(req));
-
-       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
-
-       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-               req.chan = ieee80211_get_channel(
-                       wiphy,
-                       nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
-               if (!req.chan) {
-                       err = -EINVAL;
-                       goto out;
-               }
-       }
-
-       if (info->attrs[NL80211_ATTR_SSID]) {
-               req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
-               req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
-       }
+       ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+       ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
        if (info->attrs[NL80211_ATTR_IE]) {
-               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
-               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+               ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
-       if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
-               req.auth_type =
-                       nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
-               if (!nl80211_valid_auth_type(req.auth_type)) {
-                       err = -EINVAL;
-                       goto out;
-               }
+       auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+       if (!nl80211_valid_auth_type(auth_type)) {
+               err = -EINVAL;
+               goto out;
        }
 
-       err = drv->ops->auth(&drv->wiphy, dev, &req);
+       err = cfg80211_mlme_auth(drv, dev, chan, auth_type, bssid,
+                                ssid, ssid_len, ie, ie_len);
 
 out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
 unlock_rtnl:
        rtnl_unlock();
        return err;
 }
 
-static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_crypto_settings(struct genl_info *info,
+                                  struct cfg80211_crypto_settings *settings,
+                                  int cipher_limit)
 {
-       struct cfg80211_registered_device *drv;
-       struct net_device *dev;
-       struct cfg80211_assoc_request req;
-       struct wiphy *wiphy;
-       int err;
+       settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
 
-       rtnl_lock();
+       if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
+               void *data;
+               int len, i;
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
-       if (err)
-               goto unlock_rtnl;
+               data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
+               len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
+               settings->n_ciphers_pairwise = len / sizeof(u32);
 
-       if (!drv->ops->assoc) {
-               err = -EOPNOTSUPP;
-               goto out;
+               if (len % sizeof(u32))
+                       return -EINVAL;
+
+               if (settings->n_ciphers_pairwise > cipher_limit)
+                       return -EINVAL;
+
+               memcpy(settings->ciphers_pairwise, data, len);
+
+               for (i = 0; i < settings->n_ciphers_pairwise; i++)
+                       if (!nl80211_valid_cipher_suite(
+                                       settings->ciphers_pairwise[i]))
+                               return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
+               settings->cipher_group =
+                       nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
+               if (!nl80211_valid_cipher_suite(settings->cipher_group))
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
+               settings->wpa_versions =
+                       nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
+               if (!nl80211_valid_wpa_versions(settings->wpa_versions))
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
+               void *data;
+               int len, i;
+
+               data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
+               len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
+               settings->n_akm_suites = len / sizeof(u32);
+
+               if (len % sizeof(u32))
+                       return -EINVAL;
+
+               memcpy(settings->akm_suites, data, len);
+
+               for (i = 0; i < settings->n_ciphers_pairwise; i++)
+                       if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
+                               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev;
+       struct net_device *dev;
+       struct cfg80211_crypto_settings crypto;
+       struct ieee80211_channel *chan;
+       const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
+       int err, ssid_len, ie_len = 0;
+       bool use_mfp = false;
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC] ||
+           !info->attrs[NL80211_ATTR_SSID] ||
+           !info->attrs[NL80211_ATTR_WIPHY_FREQ])
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!rdev->ops->assoc) {
+               err = -EOPNOTSUPP;
+               goto out;
        }
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
@@ -2809,39 +3221,45 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (!info->attrs[NL80211_ATTR_MAC] ||
-           !info->attrs[NL80211_ATTR_SSID]) {
+       bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       chan = ieee80211_get_channel(&rdev->wiphy,
+               nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+       if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
                err = -EINVAL;
                goto out;
        }
 
-       wiphy = &drv->wiphy;
-       memset(&req, 0, sizeof(req));
+       ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+       ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
-       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       if (info->attrs[NL80211_ATTR_IE]) {
+               ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
 
-       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
-               req.chan = ieee80211_get_channel(
-                       wiphy,
-                       nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
-               if (!req.chan) {
+       if (info->attrs[NL80211_ATTR_USE_MFP]) {
+               enum nl80211_mfp mfp =
+                       nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
+               if (mfp == NL80211_MFP_REQUIRED)
+                       use_mfp = true;
+               else if (mfp != NL80211_MFP_NO) {
                        err = -EINVAL;
                        goto out;
                }
        }
 
-       req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
-       req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
-
-       if (info->attrs[NL80211_ATTR_IE]) {
-               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
-               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
-       }
+       if (info->attrs[NL80211_ATTR_PREV_BSSID])
+               prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
 
-       err = drv->ops->assoc(&drv->wiphy, dev, &req);
+       err = nl80211_crypto_settings(info, &crypto, 1);
+       if (!err)
+               err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
+                                         ssid, ssid_len, ie, ie_len, use_mfp,
+                                         &crypto);
 
 out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
        rtnl_unlock();
@@ -2852,9 +3270,18 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
        struct net_device *dev;
-       struct cfg80211_deauth_request req;
-       struct wiphy *wiphy;
-       int err;
+       const u8 *ie = NULL, *bssid;
+       int err, ie_len = 0;
+       u16 reason_code;
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_REASON_CODE])
+               return -EINVAL;
 
        rtnl_lock();
 
@@ -2877,61 +3304,131 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (!info->attrs[NL80211_ATTR_MAC]) {
+       bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+       if (reason_code == 0) {
+               /* Reason Code 0 is reserved */
                err = -EINVAL;
                goto out;
        }
 
-       wiphy = &drv->wiphy;
-       memset(&req, 0, sizeof(req));
+       if (info->attrs[NL80211_ATTR_IE]) {
+               ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
 
-       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       err = cfg80211_mlme_deauth(drv, dev, bssid, ie, ie_len, reason_code);
 
-       if (info->attrs[NL80211_ATTR_REASON_CODE]) {
-               req.reason_code =
-                       nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
-               if (req.reason_code == 0) {
-                       /* Reason Code 0 is reserved */
-                       err = -EINVAL;
-                       goto out;
-               }
+out:
+       cfg80211_unlock_rdev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       const u8 *ie = NULL, *bssid;
+       int err, ie_len = 0;
+       u16 reason_code;
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_REASON_CODE])
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->disassoc) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+       if (reason_code == 0) {
+               /* Reason Code 0 is reserved */
+               err = -EINVAL;
+               goto out;
        }
 
        if (info->attrs[NL80211_ATTR_IE]) {
-               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
-               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+               ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
-       err = drv->ops->deauth(&drv->wiphy, dev, &req);
+       err = cfg80211_mlme_disassoc(drv, dev, bssid, ie, ie_len, reason_code);
 
 out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
 unlock_rtnl:
        rtnl_unlock();
        return err;
 }
 
-static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
        struct net_device *dev;
-       struct cfg80211_disassoc_request req;
+       struct cfg80211_ibss_params ibss;
        struct wiphy *wiphy;
        int err;
 
+       memset(&ibss, 0, sizeof(ibss));
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+           !info->attrs[NL80211_ATTR_SSID] ||
+           !nla_len(info->attrs[NL80211_ATTR_SSID]))
+               return -EINVAL;
+
+       ibss.beacon_interval = 100;
+
+       if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
+               ibss.beacon_interval =
+                       nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
+               if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
+                       return -EINVAL;
+       }
+
        rtnl_lock();
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (!drv->ops->disassoc) {
+       if (!drv->ops->join_ibss) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -2941,35 +3438,317 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (!info->attrs[NL80211_ATTR_MAC]) {
+       wiphy = &drv->wiphy;
+
+       if (info->attrs[NL80211_ATTR_MAC])
+               ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+       ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       ibss.channel = ieee80211_get_channel(wiphy,
+               nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+       if (!ibss.channel ||
+           ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
+           ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
                err = -EINVAL;
                goto out;
        }
 
+       ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
+
+       err = cfg80211_join_ibss(drv, dev, &ibss);
+
+out:
+       cfg80211_unlock_rdev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       int err;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->leave_ibss) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       err = cfg80211_leave_ibss(drv, dev, false);
+
+out:
+       cfg80211_unlock_rdev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+#ifdef CONFIG_NL80211_TESTMODE
+static struct genl_multicast_group nl80211_testmode_mcgrp = {
+       .name = "testmode",
+};
+
+static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev;
+       int err;
+
+       if (!info->attrs[NL80211_ATTR_TESTDATA])
+               return -EINVAL;
+
+       rtnl_lock();
+
+       rdev = cfg80211_get_dev_from_info(info);
+       if (IS_ERR(rdev)) {
+               err = PTR_ERR(rdev);
+               goto unlock_rtnl;
+       }
+
+       err = -EOPNOTSUPP;
+       if (rdev->ops->testmode_cmd) {
+               rdev->testmode_info = info;
+               err = rdev->ops->testmode_cmd(&rdev->wiphy,
+                               nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
+                               nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
+               rdev->testmode_info = NULL;
+       }
+
+       cfg80211_unlock_rdev(rdev);
+
+ unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static struct sk_buff *
+__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
+                             int approxlen, u32 pid, 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, pid, seq, 0, NL80211_CMD_TESTMODE);
+       if (!hdr) {
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       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_pid,
+                               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);
+}
+EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
+
+void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
+{
+       void *hdr = ((void **)skb->cb)[1];
+       struct nlattr *data = ((void **)skb->cb)[2];
+
+       nla_nest_end(skb, data);
+       genlmsg_end(skb, hdr);
+       genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
+}
+EXPORT_SYMBOL(cfg80211_testmode_event);
+#endif
+
+static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct cfg80211_connect_params connect;
+       struct wiphy *wiphy;
+       int err;
+
+       memset(&connect, 0, sizeof(connect));
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_SSID] ||
+           !nla_len(info->attrs[NL80211_ATTR_SSID]))
+               return -EINVAL;
+
+       if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+               connect.auth_type =
+                       nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+               if (!nl80211_valid_auth_type(connect.auth_type))
+                       return -EINVAL;
+       } else
+               connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+
+       connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
+
+       err = nl80211_crypto_settings(info, &connect.crypto,
+                                     NL80211_MAX_NR_CIPHER_SUITES);
+       if (err)
+               return err;
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
        wiphy = &drv->wiphy;
-       memset(&req, 0, sizeof(req));
 
-       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       connect.bssid = NULL;
+       connect.channel = NULL;
+       connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
 
-       if (info->attrs[NL80211_ATTR_REASON_CODE]) {
-               req.reason_code =
-                       nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
-               if (req.reason_code == 0) {
-                       /* Reason Code 0 is reserved */
+       if (info->attrs[NL80211_ATTR_MAC])
+               connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+       connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               connect.channel =
+                       ieee80211_get_channel(wiphy,
+                           nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+               if (!connect.channel ||
+                   connect.channel->flags & IEEE80211_CHAN_DISABLED) {
                        err = -EINVAL;
                        goto out;
                }
        }
 
-       if (info->attrs[NL80211_ATTR_IE]) {
-               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
-               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       err = cfg80211_connect(drv, dev, &connect);
+
+out:
+       cfg80211_unlock_rdev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       int err;
+       u16 reason;
+
+       if (!info->attrs[NL80211_ATTR_REASON_CODE])
+               reason = WLAN_REASON_DEAUTH_LEAVING;
+       else
+               reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+
+       if (reason == 0)
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
        }
 
-       err = drv->ops->disassoc(&drv->wiphy, dev, &req);
+       err = cfg80211_disconnect(drv, dev, reason, true);
 
 out:
-       cfg80211_put_dev(drv);
+       cfg80211_unlock_rdev(drv);
        dev_put(dev);
 unlock_rtnl:
        rtnl_unlock();
@@ -3172,8 +3951,40 @@ static struct genl_ops nl80211_ops[] = {
                .flags = GENL_ADMIN_PERM,
        },
        {
-               .cmd = NL80211_CMD_DISASSOCIATE,
-               .doit = nl80211_disassociate,
+               .cmd = NL80211_CMD_DISASSOCIATE,
+               .doit = nl80211_disassociate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_JOIN_IBSS,
+               .doit = nl80211_join_ibss,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_LEAVE_IBSS,
+               .doit = nl80211_leave_ibss,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+#ifdef CONFIG_NL80211_TESTMODE
+       {
+               .cmd = NL80211_CMD_TESTMODE,
+               .doit = nl80211_testmode_do,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+#endif
+       {
+               .cmd = NL80211_CMD_CONNECT,
+               .doit = nl80211_connect,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_DISCONNECT,
+               .doit = nl80211_disconnect,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
@@ -3199,7 +4010,7 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
 {
        struct sk_buff *msg;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
@@ -3211,11 +4022,45 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
        genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
 }
 
-static int nl80211_send_scan_donemsg(struct sk_buff *msg,
-                                   struct cfg80211_registered_device *rdev,
-                                   struct net_device *netdev,
-                                   u32 pid, u32 seq, int flags,
-                                   u32 cmd)
+static int nl80211_add_scan_req(struct sk_buff *msg,
+                               struct cfg80211_registered_device *rdev)
+{
+       struct cfg80211_scan_request *req = rdev->scan_req;
+       struct nlattr *nest;
+       int i;
+
+       ASSERT_RDEV_LOCK(rdev);
+
+       if (WARN_ON(!req))
+               return 0;
+
+       nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
+       if (!nest)
+               goto nla_put_failure;
+       for (i = 0; i < req->n_ssids; i++)
+               NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
+       nla_nest_end(msg, nest);
+
+       nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+       if (!nest)
+               goto nla_put_failure;
+       for (i = 0; i < req->n_channels; i++)
+               NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
+       nla_nest_end(msg, nest);
+
+       if (req->ie)
+               NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
+
+       return 0;
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+static int nl80211_send_scan_msg(struct sk_buff *msg,
+                                struct cfg80211_registered_device *rdev,
+                                struct net_device *netdev,
+                                u32 pid, u32 seq, int flags,
+                                u32 cmd)
 {
        void *hdr;
 
@@ -3226,7 +4071,8 @@ static int nl80211_send_scan_donemsg(struct sk_buff *msg,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
 
-       /* XXX: we should probably bounce back the request? */
+       /* ignore errors and send incomplete event anyway */
+       nl80211_add_scan_req(msg, rdev);
 
        return genlmsg_end(msg, hdr);
 
@@ -3235,17 +4081,35 @@ static int nl80211_send_scan_donemsg(struct sk_buff *msg,
        return -EMSGSIZE;
 }
 
+void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
+                            struct net_device *netdev)
+{
+       struct sk_buff *msg;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
+                                 NL80211_CMD_TRIGGER_SCAN) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
+}
+
 void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
                            struct net_device *netdev)
 {
        struct sk_buff *msg;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
-                                     NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
+       if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
+                                 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -3258,12 +4122,12 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
 {
        struct sk_buff *msg;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
-                                     NL80211_CMD_SCAN_ABORTED) < 0) {
+       if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
+                                 NL80211_CMD_SCAN_ABORTED) < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -3280,7 +4144,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
@@ -3329,12 +4193,12 @@ nla_put_failure:
 static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
                                    struct net_device *netdev,
                                    const u8 *buf, size_t len,
-                                   enum nl80211_commands cmd)
+                                   enum nl80211_commands cmd, gfp_t gfp)
 {
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
 
@@ -3353,7 +4217,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
        return;
 
  nla_put_failure:
@@ -3362,51 +4226,350 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
 }
 
 void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
-                         struct net_device *netdev, const u8 *buf, size_t len)
+                         struct net_device *netdev, const u8 *buf,
+                         size_t len, gfp_t gfp)
 {
        nl80211_send_mlme_event(rdev, netdev, buf, len,
-                               NL80211_CMD_AUTHENTICATE);
+                               NL80211_CMD_AUTHENTICATE, gfp);
 }
 
 void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
                           struct net_device *netdev, const u8 *buf,
-                          size_t len)
+                          size_t len, gfp_t gfp)
 {
-       nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
+       nl80211_send_mlme_event(rdev, netdev, buf, len,
+                               NL80211_CMD_ASSOCIATE, gfp);
 }
 
-void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
-                           struct net_device *netdev, const u8 *buf,
-                           size_t len)
+void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
+                        struct net_device *netdev, const u8 *buf,
+                        size_t len, gfp_t gfp)
 {
        nl80211_send_mlme_event(rdev, netdev, buf, len,
-                               NL80211_CMD_DEAUTHENTICATE);
+                               NL80211_CMD_DEAUTHENTICATE, gfp);
 }
 
-void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev, const u8 *buf,
-                             size_t len)
+void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
+                          struct net_device *netdev, const u8 *buf,
+                          size_t len, gfp_t gfp)
 {
        nl80211_send_mlme_event(rdev, netdev, buf, len,
-                               NL80211_CMD_DISASSOCIATE);
+                               NL80211_CMD_DISASSOCIATE, gfp);
+}
+
+static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
+                                     struct net_device *netdev, int cmd,
+                                     const u8 *addr, gfp_t gfp)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
+void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
+                              struct net_device *netdev, const u8 *addr,
+                              gfp_t gfp)
+{
+       nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
+                                 addr, gfp);
+}
+
+void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
+                               struct net_device *netdev, const u8 *addr,
+                               gfp_t gfp)
+{
+       nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
+                                 addr, gfp);
+}
+
+void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
+                                struct net_device *netdev, const u8 *bssid,
+                                const u8 *req_ie, size_t req_ie_len,
+                                const u8 *resp_ie, size_t resp_ie_len,
+                                u16 status, gfp_t gfp)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       if (bssid)
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+       NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
+       if (req_ie)
+               NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
+       if (resp_ie)
+               NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+
+}
+
+void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
+                        struct net_device *netdev, const u8 *bssid,
+                        const u8 *req_ie, size_t req_ie_len,
+                        const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+       if (req_ie)
+               NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
+       if (resp_ie)
+               NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+
+}
+
+void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
+                              struct net_device *netdev, u16 reason,
+                              const u8 *ie, size_t ie_len, bool from_ap)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       if (from_ap && reason)
+               NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
+       if (from_ap)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
+       if (ie)
+               NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+
+}
+
+void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
+                            struct net_device *netdev, const u8 *bssid,
+                            gfp_t gfp)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
+void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
+                                struct net_device *netdev, const u8 *addr,
+                                enum nl80211_key_type key_type, int key_id,
+                                const u8 *tsc, gfp_t gfp)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       if (addr)
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
+       NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
+       if (tsc)
+               NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
+void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
+                                   struct ieee80211_channel *channel_before,
+                                   struct ieee80211_channel *channel_after)
+{
+       struct sk_buff *msg;
+       void *hdr;
+       struct nlattr *nl_freq;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       /*
+        * Since we are applying the beacon hint to a wiphy we know its
+        * wiphy_idx is valid
+        */
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
+
+       /* Before */
+       nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
+       if (!nl_freq)
+               goto nla_put_failure;
+       if (nl80211_msg_put_channel(msg, channel_before))
+               goto nla_put_failure;
+       nla_nest_end(msg, nl_freq);
+
+       /* After */
+       nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
+       if (!nl_freq)
+               goto nla_put_failure;
+       if (nl80211_msg_put_channel(msg, channel_after))
+               goto nla_put_failure;
+       nla_nest_end(msg, nl_freq);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
+
+       return;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
 }
 
 /* initialisation/exit functions */
 
 int nl80211_init(void)
 {
-       int err, i;
+       int err;
 
-       err = genl_register_family(&nl80211_fam);
+       err = genl_register_family_with_ops(&nl80211_fam,
+               nl80211_ops, ARRAY_SIZE(nl80211_ops));
        if (err)
                return err;
 
-       for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
-               err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
-               if (err)
-                       goto err_out;
-       }
-
        err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
        if (err)
                goto err_out;
@@ -3423,6 +4586,12 @@ int nl80211_init(void)
        if (err)
                goto err_out;
 
+#ifdef CONFIG_NL80211_TESTMODE
+       err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
+       if (err)
+               goto err_out;
+#endif
+
        return 0;
  err_out:
        genl_unregister_family(&nl80211_fam);