nl80211: report BSS status
[GitHub/LineageOS/android_kernel_samsung_universal7580.git] / net / wireless / nl80211.c
index df22048419e319e0be03668ddb08be943dd106f5..da450ef1fc7eb30654dcfe2b65c018ec5877a20f 100644 (file)
@@ -29,9 +29,9 @@ static struct genl_family nl80211_fam = {
        .maxattr = NL80211_ATTR_MAX,
 };
 
-/* internal helper: get drv and dev */
-static int get_drv_dev_by_info_ifindex(struct nlattr **attrs,
-                                      struct cfg80211_registered_device **drv,
+/* internal helper: get rdev and dev */
+static int get_rdev_dev_by_info_ifindex(struct nlattr **attrs,
+                                      struct cfg80211_registered_device **rdev,
                                       struct net_device **dev)
 {
        int ifindex;
@@ -44,10 +44,10 @@ static int get_drv_dev_by_info_ifindex(struct nlattr **attrs,
        if (!*dev)
                return -ENODEV;
 
-       *drv = cfg80211_get_dev_from_ifindex(ifindex);
-       if (IS_ERR(*drv)) {
+       *rdev = cfg80211_get_dev_from_ifindex(ifindex);
+       if (IS_ERR(*rdev)) {
                dev_put(*dev);
-               return PTR_ERR(*drv);
+               return PTR_ERR(*rdev);
        }
 
        return 0;
@@ -73,6 +73,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
        [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
        [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
 
+       [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
        [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
                                    .len = WLAN_MAX_KEY_LEN },
        [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
@@ -134,6 +135,17 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
        [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
 };
 
+/* policy for the attributes */
+static struct nla_policy
+nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = {
+       [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
+       [NL80211_KEY_IDX] = { .type = NLA_U8 },
+       [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
+       [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
+       [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
+       [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
+};
+
 /* IE validation */
 static bool is_valid_ie_attr(const struct nlattr *attr)
 {
@@ -198,6 +210,177 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
 
 /* netlink command implementations */
 
+struct key_parse {
+       struct key_params p;
+       int idx;
+       bool def, defmgmt;
+};
+
+static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
+{
+       struct nlattr *tb[NL80211_KEY_MAX + 1];
+       int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
+                                  nl80211_key_policy);
+       if (err)
+               return err;
+
+       k->def = !!tb[NL80211_KEY_DEFAULT];
+       k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
+
+       if (tb[NL80211_KEY_IDX])
+               k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
+
+       if (tb[NL80211_KEY_DATA]) {
+               k->p.key = nla_data(tb[NL80211_KEY_DATA]);
+               k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
+       }
+
+       if (tb[NL80211_KEY_SEQ]) {
+               k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
+               k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
+       }
+
+       if (tb[NL80211_KEY_CIPHER])
+               k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
+
+       return 0;
+}
+
+static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
+{
+       if (info->attrs[NL80211_ATTR_KEY_DATA]) {
+               k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
+               k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
+       }
+
+       if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
+               k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
+               k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
+       }
+
+       if (info->attrs[NL80211_ATTR_KEY_IDX])
+               k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
+
+       if (info->attrs[NL80211_ATTR_KEY_CIPHER])
+               k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
+
+       k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
+       k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
+
+       return 0;
+}
+
+static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
+{
+       int err;
+
+       memset(k, 0, sizeof(*k));
+       k->idx = -1;
+
+       if (info->attrs[NL80211_ATTR_KEY])
+               err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
+       else
+               err = nl80211_parse_key_old(info, k);
+
+       if (err)
+               return err;
+
+       if (k->def && k->defmgmt)
+               return -EINVAL;
+
+       if (k->idx != -1) {
+               if (k->defmgmt) {
+                       if (k->idx < 4 || k->idx > 5)
+                               return -EINVAL;
+               } else if (k->def) {
+                       if (k->idx < 0 || k->idx > 3)
+                               return -EINVAL;
+               } else {
+                       if (k->idx < 0 || k->idx > 5)
+                               return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static struct cfg80211_cached_keys *
+nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
+                      struct nlattr *keys)
+{
+       struct key_parse parse;
+       struct nlattr *key;
+       struct cfg80211_cached_keys *result;
+       int rem, err, def = 0;
+
+       result = kzalloc(sizeof(*result), GFP_KERNEL);
+       if (!result)
+               return ERR_PTR(-ENOMEM);
+
+       result->def = -1;
+       result->defmgmt = -1;
+
+       nla_for_each_nested(key, keys, rem) {
+               memset(&parse, 0, sizeof(parse));
+               parse.idx = -1;
+
+               err = nl80211_parse_key_new(key, &parse);
+               if (err)
+                       goto error;
+               err = -EINVAL;
+               if (!parse.p.key)
+                       goto error;
+               if (parse.idx < 0 || parse.idx > 4)
+                       goto error;
+               if (parse.def) {
+                       if (def)
+                               goto error;
+                       def = 1;
+                       result->def = parse.idx;
+               } else if (parse.defmgmt)
+                       goto error;
+               err = cfg80211_validate_key_settings(rdev, &parse.p,
+                                                    parse.idx, NULL);
+               if (err)
+                       goto error;
+               result->params[parse.idx].cipher = parse.p.cipher;
+               result->params[parse.idx].key_len = parse.p.key_len;
+               result->params[parse.idx].key = result->data[parse.idx];
+               memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
+       }
+
+       return result;
+ error:
+       kfree(result);
+       return ERR_PTR(err);
+}
+
+static int nl80211_key_allowed(struct wireless_dev *wdev)
+{
+       ASSERT_WDEV_LOCK(wdev);
+
+       if (!netif_running(wdev->netdev))
+               return -ENETDOWN;
+
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (!wdev->current_bss)
+                       return -ENOLINK;
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (wdev->sme_state != CFG80211_SME_CONNECTED)
+                       return -ENOLINK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                              struct cfg80211_registered_device *dev)
 {
@@ -378,7 +561,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
        struct cfg80211_registered_device *dev;
 
        mutex_lock(&cfg80211_mutex);
-       list_for_each_entry(dev, &cfg80211_drv_list, list) {
+       list_for_each_entry(dev, &cfg80211_rdev_list, list) {
                if (++idx <= start)
                        continue;
                if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
@@ -413,7 +596,7 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
 
        cfg80211_unlock_rdev(dev);
 
-       return genlmsg_unicast(msg, info->snd_pid);
+       return genlmsg_reply(msg, info);
 
  out_free:
        nlmsg_free(msg);
@@ -460,7 +643,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
        mutex_lock(&cfg80211_mutex);
 
-       rdev = __cfg80211_drv_from_info(info);
+       rdev = __cfg80211_rdev_from_info(info);
        if (IS_ERR(rdev)) {
                mutex_unlock(&cfg80211_mutex);
                result = PTR_ERR(rdev);
@@ -683,7 +866,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
        struct wireless_dev *wdev;
 
        mutex_lock(&cfg80211_mutex);
-       list_for_each_entry(dev, &cfg80211_drv_list, list) {
+       list_for_each_entry(dev, &cfg80211_rdev_list, list) {
                if (wp_idx < wp_start) {
                        wp_idx++;
                        continue;
@@ -724,7 +907,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
        struct net_device *netdev;
        int err;
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &dev, &netdev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &dev, &netdev);
        if (err)
                return err;
 
@@ -739,7 +922,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
        dev_put(netdev);
        cfg80211_unlock_rdev(dev);
 
-       return genlmsg_unicast(msg, info->snd_pid);
+       return genlmsg_reply(msg, info);
 
  out_free:
        nlmsg_free(msg);
@@ -780,7 +963,7 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
 
 static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct vif_params params;
        int err;
        enum nl80211_iftype otype, ntype;
@@ -792,7 +975,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -808,8 +991,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       if (!drv->ops->change_virtual_intf ||
-           !(drv->wiphy.interface_modes & (1 << ntype))) {
+       if (!rdev->ops->change_virtual_intf ||
+           !(rdev->wiphy.interface_modes & (1 << ntype))) {
                err = -EOPNOTSUPP;
                goto unlock;
        }
@@ -839,7 +1022,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (change)
-               err = drv->ops->change_virtual_intf(&drv->wiphy, dev,
+               err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev,
                                                    ntype, flags, &params);
        else
                err = 0;
@@ -853,7 +1036,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
 
  unlock:
        dev_put(dev);
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
  unlock_rtnl:
        rtnl_unlock();
        return err;
@@ -861,7 +1044,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct vif_params params;
        int err;
        enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
@@ -880,14 +1063,14 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       drv = cfg80211_get_dev_from_info(info);
-       if (IS_ERR(drv)) {
-               err = PTR_ERR(drv);
+       rdev = cfg80211_get_dev_from_info(info);
+       if (IS_ERR(rdev)) {
+               err = PTR_ERR(rdev);
                goto unlock_rtnl;
        }
 
-       if (!drv->ops->add_virtual_intf ||
-           !(drv->wiphy.interface_modes & (1 << type))) {
+       if (!rdev->ops->add_virtual_intf ||
+           !(rdev->wiphy.interface_modes & (1 << type))) {
                err = -EOPNOTSUPP;
                goto unlock;
        }
@@ -901,12 +1084,12 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
        err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
-       err = drv->ops->add_virtual_intf(&drv->wiphy,
+       err = rdev->ops->add_virtual_intf(&rdev->wiphy,
                nla_data(info->attrs[NL80211_ATTR_IFNAME]),
                type, err ? NULL : &flags, &params);
 
  unlock:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
  unlock_rtnl:
        rtnl_unlock();
        return err;
@@ -914,27 +1097,27 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int ifindex, err;
        struct net_device *dev;
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
        ifindex = dev->ifindex;
        dev_put(dev);
 
-       if (!drv->ops->del_virtual_intf) {
+       if (!rdev->ops->del_virtual_intf) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
+       err = rdev->ops->del_virtual_intf(&rdev->wiphy, ifindex);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
  unlock_rtnl:
        rtnl_unlock();
        return err;
@@ -943,10 +1126,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
 struct get_key_cookie {
        struct sk_buff *msg;
        int error;
+       int idx;
 };
 
 static void get_key_callback(void *c, struct key_params *params)
 {
+       struct nlattr *key;
        struct get_key_cookie *cookie = c;
 
        if (params->key)
@@ -961,6 +1146,26 @@ static void get_key_callback(void *c, struct key_params *params)
                NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
                            params->cipher);
 
+       key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
+       if (!key)
+               goto nla_put_failure;
+
+       if (params->key)
+               NLA_PUT(cookie->msg, NL80211_KEY_DATA,
+                       params->key_len, params->key);
+
+       if (params->seq)
+               NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
+                       params->seq_len, params->seq);
+
+       if (params->cipher)
+               NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
+                           params->cipher);
+
+       NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
+
+       nla_nest_end(cookie->msg, key);
+
        return;
  nla_put_failure:
        cookie->error = 1;
@@ -968,7 +1173,7 @@ static void get_key_callback(void *c, struct key_params *params)
 
 static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        u8 key_idx = 0;
@@ -990,11 +1195,11 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (!drv->ops->get_key) {
+       if (!rdev->ops->get_key) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -1010,34 +1215,36 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
 
        if (IS_ERR(hdr)) {
                err = PTR_ERR(hdr);
-               goto out;
+               goto free_msg;
        }
 
        cookie.msg = msg;
+       cookie.idx = key_idx;
 
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
        NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
        if (mac_addr)
                NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
 
-       err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
+       err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr,
                                &cookie, get_key_callback);
 
        if (err)
-               goto out;
+               goto free_msg;
 
        if (cookie.error)
                goto nla_put_failure;
 
        genlmsg_end(msg, hdr);
-       err = genlmsg_unicast(msg, info->snd_pid);
+       err = genlmsg_reply(msg, info);
        goto out;
 
  nla_put_failure:
        err = -ENOBUFS;
+ free_msg:
        nlmsg_free(msg);
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  unlock_rtnl:
        rtnl_unlock();
@@ -1047,57 +1254,57 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
+       struct key_parse key;
        int err;
        struct net_device *dev;
-       u8 key_idx;
        int (*func)(struct wiphy *wiphy, struct net_device *netdev,
                    u8 key_index);
 
-       if (!info->attrs[NL80211_ATTR_KEY_IDX])
-               return -EINVAL;
-
-       key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
+       err = nl80211_parse_key(info, &key);
+       if (err)
+               return err;
 
-       if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) {
-               if (key_idx < 4 || key_idx > 5)
-                       return -EINVAL;
-       } else if (key_idx > 3)
+       if (key.idx < 0)
                return -EINVAL;
 
-       /* currently only support setting default key */
-       if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] &&
-           !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
+       /* only support setting default key */
+       if (!key.def && !key.defmgmt)
                return -EINVAL;
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
-               func = drv->ops->set_default_key;
+       if (key.def)
+               func = rdev->ops->set_default_key;
        else
-               func = drv->ops->set_default_mgmt_key;
+               func = rdev->ops->set_default_mgmt_key;
 
        if (!func) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       err = func(&drv->wiphy, dev, key_idx);
+       wdev_lock(dev->ieee80211_ptr);
+       err = nl80211_key_allowed(dev->ieee80211_ptr);
+       if (!err)
+               err = func(&rdev->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;
+               if (func == rdev->ops->set_default_key)
+                       dev->ieee80211_ptr->wext.default_key = key.idx;
                else
-                       dev->ieee80211_ptr->wext.default_mgmt_key = key_idx;
+                       dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
        }
 #endif
+       wdev_unlock(dev->ieee80211_ptr);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 
  unlock_rtnl:
@@ -1108,62 +1315,47 @@ 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, i;
+       struct cfg80211_registered_device *rdev;
+       int err;
        struct net_device *dev;
-       struct key_params params;
-       u8 key_idx = 0;
+       struct key_parse key;
        u8 *mac_addr = NULL;
 
-       memset(&params, 0, sizeof(params));
+       err = nl80211_parse_key(info, &key);
+       if (err)
+               return err;
 
-       if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
+       if (!key.p.key)
                return -EINVAL;
 
-       if (info->attrs[NL80211_ATTR_KEY_DATA]) {
-               params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
-               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]);
-
-       params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
-
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
-       if (cfg80211_validate_key_settings(&params, key_idx, mac_addr))
-               return -EINVAL;
-
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        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;
+       if (!rdev->ops->add_key) {
+               err = -EOPNOTSUPP;
                goto out;
        }
 
-       if (!drv->ops->add_key) {
-               err = -EOPNOTSUPP;
+       if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) {
+               err = -EINVAL;
                goto out;
        }
 
-       err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
+       wdev_lock(dev->ieee80211_ptr);
+       err = nl80211_key_allowed(dev->ieee80211_ptr);
+       if (!err)
+               err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
+                                        mac_addr, &key.p);
+       wdev_unlock(dev->ieee80211_ptr);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  unlock_rtnl:
        rtnl_unlock();
@@ -1173,45 +1365,47 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
-       u8 key_idx = 0;
        u8 *mac_addr = NULL;
+       struct key_parse key;
 
-       if (info->attrs[NL80211_ATTR_KEY_IDX])
-               key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
-
-       if (key_idx > 5)
-               return -EINVAL;
+       err = nl80211_parse_key(info, &key);
+       if (err)
+               return err;
 
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (!drv->ops->del_key) {
+       if (!rdev->ops->del_key) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
+       wdev_lock(dev->ieee80211_ptr);
+       err = nl80211_key_allowed(dev->ieee80211_ptr);
+       if (!err)
+               err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
 
 #ifdef CONFIG_WIRELESS_EXT
        if (!err) {
-               if (key_idx == dev->ieee80211_ptr->wext.default_key)
+               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)
+               else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
                        dev->ieee80211_ptr->wext.default_mgmt_key = -1;
        }
 #endif
+       wdev_unlock(dev->ieee80211_ptr);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 
  unlock_rtnl:
@@ -1224,7 +1418,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
 {
         int (*call)(struct wiphy *wiphy, struct net_device *dev,
                    struct beacon_parameters *info);
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        struct beacon_parameters params;
@@ -1235,7 +1429,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -1254,10 +1448,10 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
                        goto out;
                }
 
-               call = drv->ops->add_beacon;
+               call = rdev->ops->add_beacon;
                break;
        case NL80211_CMD_SET_BEACON:
-               call = drv->ops->set_beacon;
+               call = rdev->ops->set_beacon;
                break;
        default:
                WARN_ON(1);
@@ -1303,10 +1497,10 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = call(&drv->wiphy, dev, &params);
+       err = call(&rdev->wiphy, dev, &params);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  unlock_rtnl:
        rtnl_unlock();
@@ -1316,17 +1510,17 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (!drv->ops->del_beacon) {
+       if (!rdev->ops->del_beacon) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -1335,10 +1529,10 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
                err = -EOPNOTSUPP;
                goto out;
        }
-       err = drv->ops->del_beacon(&drv->wiphy, dev);
+       err = rdev->ops->del_beacon(&rdev->wiphy, dev);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  unlock_rtnl:
        rtnl_unlock();
@@ -1581,7 +1775,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
 
 static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        struct station_info sinfo;
@@ -1597,16 +1791,16 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       if (!drv->ops->get_station) {
+       if (!rdev->ops->get_station) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
+       err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
        if (err)
                goto out;
 
@@ -1618,13 +1812,13 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
                                 dev, mac_addr, &sinfo) < 0)
                goto out_free;
 
-       err = genlmsg_unicast(msg, info->snd_pid);
+       err = genlmsg_reply(msg, info);
        goto out;
 
  out_free:
        nlmsg_free(msg);
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1655,7 +1849,7 @@ static int get_vlan(struct nlattr *vlanattr,
 
 static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        struct station_parameters params;
@@ -1697,11 +1891,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
+       err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, &params.vlan);
        if (err)
                goto out;
 
@@ -1750,17 +1944,17 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto out;
 
-       if (!drv->ops->change_station) {
+       if (!rdev->ops->change_station) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
+       err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
 
  out:
        if (params.vlan)
                dev_put(params.vlan);
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1770,7 +1964,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        struct station_parameters params;
@@ -1810,11 +2004,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
+       err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, &params.vlan);
        if (err)
                goto out;
 
@@ -1850,7 +2044,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto out;
 
-       if (!drv->ops->add_station) {
+       if (!rdev->ops->add_station) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -1860,12 +2054,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
+       err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
 
  out:
        if (params.vlan)
                dev_put(params.vlan);
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -1875,7 +2069,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        u8 *mac_addr = NULL;
@@ -1885,7 +2079,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -1896,15 +2090,15 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (!drv->ops->del_station) {
+       if (!rdev->ops->del_station) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
+       err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2044,7 +2238,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
 
 static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        struct mpath_info pinfo;
@@ -2061,11 +2255,11 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       if (!drv->ops->get_mpath) {
+       if (!rdev->ops->get_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -2075,7 +2269,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
+       err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
        if (err)
                goto out;
 
@@ -2087,13 +2281,13 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
                                 dev, dst, next_hop, &pinfo) < 0)
                goto out_free;
 
-       err = genlmsg_unicast(msg, info->snd_pid);
+       err = genlmsg_reply(msg, info);
        goto out;
 
  out_free:
        nlmsg_free(msg);
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2103,7 +2297,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        u8 *dst = NULL;
@@ -2120,11 +2314,11 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       if (!drv->ops->change_mpath) {
+       if (!rdev->ops->change_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -2139,10 +2333,10 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
+       err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2151,7 +2345,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
 }
 static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        u8 *dst = NULL;
@@ -2168,11 +2362,11 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       if (!drv->ops->add_mpath) {
+       if (!rdev->ops->add_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -2187,10 +2381,10 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
+       err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2200,7 +2394,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        u8 *dst = NULL;
@@ -2210,19 +2404,19 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       if (!drv->ops->del_mpath) {
+       if (!rdev->ops->del_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
+       err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2232,7 +2426,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        int err;
        struct net_device *dev;
        struct bss_parameters params;
@@ -2261,11 +2455,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       if (!drv->ops->change_bss) {
+       if (!rdev->ops->change_bss) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -2275,10 +2469,10 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = drv->ops->change_bss(&drv->wiphy, dev, &params);
+       err = rdev->ops->change_bss(&rdev->wiphy, dev, &params);
 
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2369,7 +2563,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
 static int nl80211_get_mesh_params(struct sk_buff *skb,
        struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct mesh_config cur_params;
        int err;
        struct net_device *dev;
@@ -2380,17 +2574,17 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        rtnl_lock();
 
        /* Look up our device */
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       if (!drv->ops->get_mesh_params) {
+       if (!rdev->ops->get_mesh_params) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
        /* Get the mesh params */
-       err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
+       err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params);
        if (err)
                goto out;
 
@@ -2436,7 +2630,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
                        cur_params.dot11MeshHWMPnetDiameterTraversalTime);
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
-       err = genlmsg_unicast(msg, info->snd_pid);
+       err = genlmsg_reply(msg, info);
        goto out;
 
  nla_put_failure:
@@ -2444,7 +2638,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        err = -EMSGSIZE;
  out:
        /* Cleanup */
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2482,7 +2676,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
 {
        int err;
        u32 mask;
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        struct mesh_config cfg;
        struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
@@ -2497,11 +2691,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       if (!drv->ops->set_mesh_params) {
+       if (!rdev->ops->set_mesh_params) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -2546,11 +2740,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
                        nla_get_u16);
 
        /* Apply changes */
-       err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
+       err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);
 
  out:
        /* cleanup */
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2624,7 +2818,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
        nla_nest_end(msg, nl_reg_rules);
 
        genlmsg_end(msg, hdr);
-       err = genlmsg_unicast(msg, info->snd_pid);
+       err = genlmsg_reply(msg, info);
        goto out;
 
 nla_put_failure:
@@ -2737,7 +2931,7 @@ static int validate_scan_freqs(struct nlattr *freqs)
 
 static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        struct cfg80211_scan_request *request;
        struct cfg80211_ssid *ssid;
@@ -2753,13 +2947,13 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       wiphy = &drv->wiphy;
+       wiphy = &rdev->wiphy;
 
-       if (!drv->ops->scan) {
+       if (!rdev->ops->scan) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -2769,7 +2963,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       if (drv->scan_req) {
+       if (rdev->scan_req) {
                err = -EBUSY;
                goto out;
        }
@@ -2876,21 +3070,21 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        }
 
        request->ifidx = dev->ifindex;
-       request->wiphy = &drv->wiphy;
+       request->wiphy = &rdev->wiphy;
 
-       drv->scan_req = request;
-       err = drv->ops->scan(&drv->wiphy, dev, request);
+       rdev->scan_req = request;
+       err = rdev->ops->scan(&rdev->wiphy, dev, request);
 
        if (!err)
-               nl80211_send_scan_start(drv, dev);
+               nl80211_send_scan_start(rdev, dev);
 
  out_free:
        if (err) {
-               drv->scan_req = NULL;
+               rdev->scan_req = NULL;
                kfree(request);
        }
  out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
  out_rtnl:
        rtnl_unlock();
@@ -2900,11 +3094,15 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                            struct cfg80211_registered_device *rdev,
-                           struct net_device *dev,
-                           struct cfg80211_bss *res)
+                           struct wireless_dev *wdev,
+                           struct cfg80211_internal_bss *intbss)
 {
+       struct cfg80211_bss *res = &intbss->pub;
        void *hdr;
        struct nlattr *bss;
+       int i;
+
+       ASSERT_WDEV_LOCK(wdev);
 
        hdr = nl80211hdr_put(msg, pid, seq, flags,
                             NL80211_CMD_NEW_SCAN_RESULTS);
@@ -2913,7 +3111,7 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
 
        NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
                    rdev->bss_generation);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
 
        bss = nla_nest_start(msg, NL80211_ATTR_BSS);
        if (!bss)
@@ -2942,6 +3140,28 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                break;
        }
 
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_STATION:
+               if (intbss == wdev->current_bss)
+                       NLA_PUT_U32(msg, NL80211_BSS_STATUS,
+                                   NL80211_BSS_STATUS_ASSOCIATED);
+               else for (i = 0; i < MAX_AUTH_BSSES; i++) {
+                       if (intbss != wdev->auth_bsses[i])
+                               continue;
+                       NLA_PUT_U32(msg, NL80211_BSS_STATUS,
+                                   NL80211_BSS_STATUS_AUTHENTICATED);
+                       break;
+               }
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (intbss == wdev->current_bss)
+                       NLA_PUT_U32(msg, NL80211_BSS_STATUS,
+                                   NL80211_BSS_STATUS_IBSS_JOINED);
+               break;
+       default:
+               break;
+       }
+
        nla_nest_end(msg, bss);
 
        return genlmsg_end(msg, hdr);
@@ -2954,9 +3174,10 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
 static int nl80211_dump_scan(struct sk_buff *skb,
                             struct netlink_callback *cb)
 {
-       struct cfg80211_registered_device *dev;
-       struct net_device *netdev;
+       struct cfg80211_registered_device *rdev;
+       struct net_device *dev;
        struct cfg80211_internal_bss *scan;
+       struct wireless_dev *wdev;
        int ifidx = cb->args[0];
        int start = cb->args[1], idx = 0;
        int err;
@@ -2977,39 +3198,43 @@ static int nl80211_dump_scan(struct sk_buff *skb,
                cb->args[0] = ifidx;
        }
 
-       netdev = dev_get_by_index(&init_net, ifidx);
-       if (!netdev)
+       dev = dev_get_by_index(&init_net, ifidx);
+       if (!dev)
                return -ENODEV;
 
-       dev = cfg80211_get_dev_from_ifindex(ifidx);
-       if (IS_ERR(dev)) {
-               err = PTR_ERR(dev);
+       rdev = cfg80211_get_dev_from_ifindex(ifidx);
+       if (IS_ERR(rdev)) {
+               err = PTR_ERR(rdev);
                goto out_put_netdev;
        }
 
-       spin_lock_bh(&dev->bss_lock);
-       cfg80211_bss_expire(dev);
+       wdev = dev->ieee80211_ptr;
+
+       wdev_lock(wdev);
+       spin_lock_bh(&rdev->bss_lock);
+       cfg80211_bss_expire(rdev);
 
-       list_for_each_entry(scan, &dev->bss_list, list) {
+       list_for_each_entry(scan, &rdev->bss_list, list) {
                if (++idx <= start)
                        continue;
                if (nl80211_send_bss(skb,
                                NETLINK_CB(cb->skb).pid,
                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                               dev, netdev, &scan->pub) < 0) {
+                               rdev, wdev, scan) < 0) {
                        idx--;
                        goto out;
                }
        }
 
  out:
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
+       wdev_unlock(wdev);
 
        cb->args[1] = idx;
        err = skb->len;
-       cfg80211_unlock_rdev(dev);
+       cfg80211_unlock_rdev(rdev);
  out_put_netdev:
-       dev_put(netdev);
+       dev_put(dev);
 
        return err;
 }
@@ -3043,12 +3268,13 @@ static bool nl80211_valid_cipher_suite(u32 cipher)
 
 static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        struct ieee80211_channel *chan;
        const u8 *bssid, *ssid, *ie = NULL;
        int err, ssid_len, ie_len = 0;
        enum nl80211_auth_type auth_type;
+       struct key_parse key;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
@@ -3065,13 +3291,32 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
        if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
                return -EINVAL;
 
+       err = nl80211_parse_key(info, &key);
+       if (err)
+               return err;
+
+       if (key.idx >= 0) {
+               if (!key.p.key || !key.p.key_len)
+                       return -EINVAL;
+               if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
+                    key.p.key_len != WLAN_KEY_LEN_WEP40) &&
+                   (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
+                    key.p.key_len != WLAN_KEY_LEN_WEP104))
+                       return -EINVAL;
+               if (key.idx > 4)
+                       return -EINVAL;
+       } else {
+               key.p.key_len = 0;
+               key.p.key = NULL;
+       }
+
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (!drv->ops->auth) {
+       if (!rdev->ops->auth) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -3087,7 +3332,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
        }
 
        bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
-       chan = ieee80211_get_channel(&drv->wiphy,
+       chan = ieee80211_get_channel(&rdev->wiphy,
                nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
        if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
                err = -EINVAL;
@@ -3108,11 +3353,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = cfg80211_mlme_auth(drv, dev, chan, auth_type, bssid,
-                                ssid, ssid_len, ie, ie_len);
+       err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
+                                ssid, ssid_len, ie, ie_len,
+                                key.p.key, key.p.key_len, key.idx);
 
 out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
        rtnl_unlock();
@@ -3202,7 +3448,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3239,11 +3485,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_USE_MFP]) {
-               enum nl80211_mfp use_mfp =
+               enum nl80211_mfp mfp =
                        nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
-               if (use_mfp == NL80211_MFP_REQUIRED)
+               if (mfp == NL80211_MFP_REQUIRED)
                        use_mfp = true;
-               else if (use_mfp != NL80211_MFP_NO) {
+               else if (mfp != NL80211_MFP_NO) {
                        err = -EINVAL;
                        goto out;
                }
@@ -3268,7 +3514,7 @@ unlock_rtnl:
 
 static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        const u8 *ie = NULL, *bssid;
        int err, ie_len = 0;
@@ -3285,11 +3531,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (!drv->ops->deauth) {
+       if (!rdev->ops->deauth) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -3318,10 +3564,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
                ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
-       err = cfg80211_mlme_deauth(drv, dev, bssid, ie, ie_len, reason_code);
+       err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code);
 
 out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
        rtnl_unlock();
@@ -3330,7 +3576,7 @@ unlock_rtnl:
 
 static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        const u8 *ie = NULL, *bssid;
        int err, ie_len = 0;
@@ -3347,11 +3593,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (!drv->ops->disassoc) {
+       if (!rdev->ops->disassoc) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -3380,10 +3626,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
                ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
-       err = cfg80211_mlme_disassoc(drv, dev, bssid, ie, ie_len, reason_code);
+       err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code);
 
 out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
        rtnl_unlock();
@@ -3392,10 +3638,11 @@ unlock_rtnl:
 
 static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        struct cfg80211_ibss_params ibss;
        struct wiphy *wiphy;
+       struct cfg80211_cached_keys *connkeys = NULL;
        int err;
 
        memset(&ibss, 0, sizeof(ibss));
@@ -3419,11 +3666,11 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (!drv->ops->join_ibss) {
+       if (!rdev->ops->join_ibss) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -3438,7 +3685,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       wiphy = &drv->wiphy;
+       wiphy = &rdev->wiphy;
 
        if (info->attrs[NL80211_ATTR_MAC])
                ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
@@ -3460,30 +3707,43 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        }
 
        ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
+       ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
+
+       if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
+               connkeys = nl80211_parse_connkeys(rdev,
+                                       info->attrs[NL80211_ATTR_KEYS]);
+               if (IS_ERR(connkeys)) {
+                       err = PTR_ERR(connkeys);
+                       connkeys = NULL;
+                       goto out;
+               }
+       }
 
-       err = cfg80211_join_ibss(drv, dev, &ibss);
+       err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
 
 out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
+       if (err)
+               kfree(connkeys);
        rtnl_unlock();
        return err;
 }
 
 static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        int err;
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       if (!drv->ops->leave_ibss) {
+       if (!rdev->ops->leave_ibss) {
                err = -EOPNOTSUPP;
                goto out;
        }
@@ -3498,10 +3758,10 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = cfg80211_leave_ibss(drv, dev, false);
+       err = cfg80211_leave_ibss(rdev, dev, false);
 
 out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
        rtnl_unlock();
@@ -3632,10 +3892,11 @@ EXPORT_SYMBOL(cfg80211_testmode_event);
 
 static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        struct cfg80211_connect_params connect;
        struct wiphy *wiphy;
+       struct cfg80211_cached_keys *connkeys = NULL;
        int err;
 
        memset(&connect, 0, sizeof(connect));
@@ -3663,7 +3924,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                return err;
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3677,11 +3938,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       wiphy = &drv->wiphy;
-
-       connect.bssid = NULL;
-       connect.channel = NULL;
-       connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+       wiphy = &rdev->wiphy;
 
        if (info->attrs[NL80211_ATTR_MAC])
                connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
@@ -3704,19 +3961,31 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       err = cfg80211_connect(drv, dev, &connect);
+       if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
+               connkeys = nl80211_parse_connkeys(rdev,
+                                       info->attrs[NL80211_ATTR_KEYS]);
+               if (IS_ERR(connkeys)) {
+                       err = PTR_ERR(connkeys);
+                       connkeys = NULL;
+                       goto out;
+               }
+       }
+
+       err = cfg80211_connect(rdev, dev, &connect, connkeys);
 
 out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
+       if (err)
+               kfree(connkeys);
        rtnl_unlock();
        return err;
 }
 
 static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
+       struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        int err;
        u16 reason;
@@ -3731,7 +4000,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3745,10 +4014,10 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = cfg80211_disconnect(drv, dev, reason, true);
+       err = cfg80211_disconnect(rdev, dev, reason, true);
 
 out:
-       cfg80211_unlock_rdev(drv);
+       cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
        rtnl_unlock();
@@ -4029,6 +4298,8 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
        struct nlattr *nest;
        int i;
 
+       ASSERT_RDEV_LOCK(rdev);
+
        if (WARN_ON(!req))
                return 0;
 
@@ -4391,12 +4662,12 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
 
 void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
                               struct net_device *netdev, u16 reason,
-                              u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp)
+                              const u8 *ie, size_t ie_len, bool from_ap)
 {
        struct sk_buff *msg;
        void *hdr;
 
-       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!msg)
                return;
 
@@ -4420,7 +4691,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
        return;
 
  nla_put_failure: