cfg80211: Add background scan period attribute.
[cascardo/linux.git] / net / wireless / nl80211.c
index afeea32..4c1eb94 100644 (file)
@@ -204,6 +204,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
                .len = NL80211_HT_CAPABILITY_LEN
        },
        [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
+       [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
+       [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
 };
 
 /* policy for the key attributes */
@@ -427,10 +429,9 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
 
        if (tb[NL80211_KEY_DEFAULT_TYPES]) {
                struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
-               int err = nla_parse_nested(kdt,
-                                          NUM_NL80211_KEY_DEFAULT_TYPES - 1,
-                                          tb[NL80211_KEY_DEFAULT_TYPES],
-                                          nl80211_key_default_policy);
+               err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
+                                      tb[NL80211_KEY_DEFAULT_TYPES],
+                                      nl80211_key_default_policy);
                if (err)
                        return err;
 
@@ -872,7 +873,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        CMD(add_virtual_intf, NEW_INTERFACE);
        CMD(change_virtual_intf, SET_INTERFACE);
        CMD(add_key, NEW_KEY);
-       CMD(add_beacon, NEW_BEACON);
+       CMD(start_ap, START_AP);
        CMD(add_station, NEW_STATION);
        CMD(add_mpath, NEW_MPATH);
        CMD(update_mesh_config, SET_MESH_CONFIG);
@@ -2076,15 +2077,10 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
-static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_parse_beacon(struct genl_info *info,
+                               struct cfg80211_beacon_data *bcn)
 {
-        int (*call)(struct wiphy *wiphy, struct net_device *dev,
-                   struct beacon_parameters *info);
-       struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       struct net_device *dev = info->user_ptr[1];
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct beacon_parameters params;
-       int haveinfo = 0, err;
+       bool haveinfo = false;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
            !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
@@ -2092,149 +2088,190 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
            !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
                return -EINVAL;
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
-               return -EOPNOTSUPP;
-
-       memset(&params, 0, sizeof(params));
-
-       switch (info->genlhdr->cmd) {
-       case NL80211_CMD_NEW_BEACON:
-               /* these are required for NEW_BEACON */
-               if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
-                   !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
-                   !info->attrs[NL80211_ATTR_BEACON_HEAD])
-                       return -EINVAL;
-
-               params.interval =
-                       nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
-               params.dtim_period =
-                       nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
-
-               err = cfg80211_validate_beacon_int(rdev, params.interval);
-               if (err)
-                       return err;
-
-               /*
-                * In theory, some of these attributes could be required for
-                * NEW_BEACON, but since they were not used when the command was
-                * originally added, keep them optional for old user space
-                * programs to work with drivers that do not need the additional
-                * information.
-                */
-               if (info->attrs[NL80211_ATTR_SSID]) {
-                       params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
-                       params.ssid_len =
-                               nla_len(info->attrs[NL80211_ATTR_SSID]);
-                       if (params.ssid_len == 0 ||
-                           params.ssid_len > IEEE80211_MAX_SSID_LEN)
-                               return -EINVAL;
-               }
-
-               if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
-                       params.hidden_ssid = nla_get_u32(
-                               info->attrs[NL80211_ATTR_HIDDEN_SSID]);
-                       if (params.hidden_ssid !=
-                           NL80211_HIDDEN_SSID_NOT_IN_USE &&
-                           params.hidden_ssid !=
-                           NL80211_HIDDEN_SSID_ZERO_LEN &&
-                           params.hidden_ssid !=
-                           NL80211_HIDDEN_SSID_ZERO_CONTENTS)
-                               return -EINVAL;
-               }
-
-               params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
-
-               if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
-                       params.auth_type = nla_get_u32(
-                               info->attrs[NL80211_ATTR_AUTH_TYPE]);
-                       if (!nl80211_valid_auth_type(params.auth_type))
-                               return -EINVAL;
-               } else
-                       params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
-
-               err = nl80211_crypto_settings(rdev, info, &params.crypto,
-                                             NL80211_MAX_NR_CIPHER_SUITES);
-               if (err)
-                       return err;
-
-               call = rdev->ops->add_beacon;
-               break;
-       case NL80211_CMD_SET_BEACON:
-               call = rdev->ops->set_beacon;
-               break;
-       default:
-               WARN_ON(1);
-               return -EOPNOTSUPP;
-       }
-
-       if (!call)
-               return -EOPNOTSUPP;
+       memset(bcn, 0, sizeof(*bcn));
 
        if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
-               params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
-               params.head_len =
-                   nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
-               haveinfo = 1;
+               bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
+               bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
+               if (!bcn->head_len)
+                       return -EINVAL;
+               haveinfo = true;
        }
 
        if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
-               params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
-               params.tail_len =
+               bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
+               bcn->tail_len =
                    nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
-               haveinfo = 1;
+               haveinfo = true;
        }
 
        if (!haveinfo)
                return -EINVAL;
 
        if (info->attrs[NL80211_ATTR_IE]) {
-               params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
-               params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+               bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
+               bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
        if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
-               params.proberesp_ies =
+               bcn->proberesp_ies =
                        nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
-               params.proberesp_ies_len =
+               bcn->proberesp_ies_len =
                        nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
        }
 
        if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
-               params.assocresp_ies =
+               bcn->assocresp_ies =
                        nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
-               params.assocresp_ies_len =
+               bcn->assocresp_ies_len =
                        nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
        }
 
        if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
-               params.probe_resp =
+               bcn->probe_resp =
                        nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
-               params.probe_resp_len =
+               bcn->probe_resp_len =
                        nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
        }
 
-       err = call(&rdev->wiphy, dev, &params);
-       if (!err && params.interval)
-               wdev->beacon_interval = params.interval;
+       return 0;
+}
+
+static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_ap_settings params;
+       int err;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EOPNOTSUPP;
+
+       if (!rdev->ops->start_ap)
+               return -EOPNOTSUPP;
+
+       if (wdev->beacon_interval)
+               return -EALREADY;
+
+       memset(&params, 0, sizeof(params));
+
+       /* these are required for START_AP */
+       if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
+           !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
+           !info->attrs[NL80211_ATTR_BEACON_HEAD])
+               return -EINVAL;
+
+       err = nl80211_parse_beacon(info, &params.beacon);
+       if (err)
+               return err;
+
+       params.beacon_interval =
+               nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
+       params.dtim_period =
+               nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
+
+       err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
+       if (err)
+               return err;
+
+       /*
+        * In theory, some of these attributes should be required here
+        * but since they were not used when the command was originally
+        * added, keep them optional for old user space programs to let
+        * them continue to work with drivers that do not need the
+        * additional information -- drivers must check!
+        */
+       if (info->attrs[NL80211_ATTR_SSID]) {
+               params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+               params.ssid_len =
+                       nla_len(info->attrs[NL80211_ATTR_SSID]);
+               if (params.ssid_len == 0 ||
+                   params.ssid_len > IEEE80211_MAX_SSID_LEN)
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
+               params.hidden_ssid = nla_get_u32(
+                       info->attrs[NL80211_ATTR_HIDDEN_SSID]);
+               if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE &&
+                   params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN &&
+                   params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS)
+                       return -EINVAL;
+       }
+
+       params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
+
+       if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+               params.auth_type = nla_get_u32(
+                       info->attrs[NL80211_ATTR_AUTH_TYPE]);
+               if (!nl80211_valid_auth_type(params.auth_type))
+                       return -EINVAL;
+       } else
+               params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+
+       err = nl80211_crypto_settings(rdev, info, &params.crypto,
+                                     NL80211_MAX_NR_CIPHER_SUITES);
+       if (err)
+               return err;
+
+       if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
+               if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
+                       return -EOPNOTSUPP;
+               params.inactivity_timeout = nla_get_u16(
+                       info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
+       }
+
+       err = rdev->ops->start_ap(&rdev->wiphy, dev, &params);
+       if (!err)
+               wdev->beacon_interval = params.beacon_interval;
        return err;
 }
 
-static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_beacon_data params;
        int err;
 
-       if (!rdev->ops->del_beacon)
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EOPNOTSUPP;
+
+       if (!rdev->ops->change_beacon)
+               return -EOPNOTSUPP;
+
+       if (!wdev->beacon_interval)
+               return -EINVAL;
+
+       err = nl80211_parse_beacon(info, &params);
+       if (err)
+               return err;
+
+       return rdev->ops->change_beacon(&rdev->wiphy, dev, &params);
+}
+
+static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       int err;
+
+       if (!rdev->ops->stop_ap)
                return -EOPNOTSUPP;
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
                return -EOPNOTSUPP;
 
-       err = rdev->ops->del_beacon(&rdev->wiphy, dev);
+       if (!wdev->beacon_interval)
+               return -ENOENT;
+
+       err = rdev->ops->stop_ap(&rdev->wiphy, dev);
        if (!err)
                wdev->beacon_interval = 0;
        return err;
@@ -2655,13 +2692,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
-               /* disallow things sta doesn't support */
-               if (params.plink_action)
-                       return -EINVAL;
-               if (params.ht_capa)
-                       return -EINVAL;
-               if (params.listen_interval >= 0)
-                       return -EINVAL;
                /*
                 * Don't allow userspace to change the TDLS_PEER flag,
                 * but silently ignore attempts to change it since we
@@ -2669,7 +2699,15 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                 * to change the flag.
                 */
                params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
-
+               /* fall through */
+       case NL80211_IFTYPE_ADHOC:
+               /* disallow things sta doesn't support */
+               if (params.plink_action)
+                       return -EINVAL;
+               if (params.ht_capa)
+                       return -EINVAL;
+               if (params.listen_interval >= 0)
+                       return -EINVAL;
                /* reject any changes other than AUTHORIZED */
                if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
                        return -EINVAL;
@@ -3259,6 +3297,10 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
                        cur_params.dot11MeshHWMPRannInterval);
        NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
                        cur_params.dot11MeshGateAnnouncementProtocol);
+       NLA_PUT_U8(msg, NL80211_MESHCONF_FORWARDING,
+                       cur_params.dot11MeshForwarding);
+       NLA_PUT_U32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
+                       cur_params.rssi_threshold);
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
        return genlmsg_reply(msg, info);
@@ -3290,6 +3332,8 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
        [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
        [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
        [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
+       [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
+       [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32},
 };
 
 static const struct nla_policy
@@ -3379,6 +3423,10 @@ do {\
                        dot11MeshGateAnnouncementProtocol, mask,
                        NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
                        nla_get_u8);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding,
+                       mask, NL80211_MESHCONF_FORWARDING, nla_get_u8);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold,
+                       mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_u32);
        if (mask_out)
                *mask_out = mask;
 
@@ -4079,7 +4127,6 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
        struct cfg80211_bss *res = &intbss->pub;
        void *hdr;
        struct nlattr *bss;
-       int i;
 
        ASSERT_WDEV_LOCK(wdev);
 
@@ -4132,13 +4179,6 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                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)
@@ -4406,10 +4446,16 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
 
        local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
 
+       /*
+        * Since we no longer track auth state, ignore
+        * requests to only change local state.
+        */
+       if (local_state_change)
+               return 0;
+
        return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
                                  ssid, ssid_len, ie, ie_len,
-                                 key.p.key, key.p.key_len, key.idx,
-                                 local_state_change);
+                                 key.p.key, key.p.key_len, key.idx);
 }
 
 static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
@@ -4781,7 +4827,6 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                        nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
                struct ieee80211_supported_band *sband =
                        wiphy->bands[ibss.channel->band];
-               int err;
 
                err = ieee80211_get_ratemask(sband, rates, n_rates,
                                             &ibss.basic_rates);
@@ -4801,6 +4846,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                        return PTR_ERR(connkeys);
        }
 
+       ibss.control_port =
+               nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
+
        err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
        if (err)
                kfree(connkeys);
@@ -5069,6 +5117,13 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
 
        wiphy = &rdev->wiphy;
 
+       connect.bg_scan_period = -1;
+       if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
+               (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
+               connect.bg_scan_period =
+                       nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
+       }
+
        if (info->attrs[NL80211_ATTR_MAC])
                connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
        connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
@@ -5390,9 +5445,39 @@ static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
        return mask;
 }
 
+static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
+                              u8 *rates, u8 rates_len,
+                              u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
+{
+       u8 i;
+
+       memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
+
+       for (i = 0; i < rates_len; i++) {
+               int ridx, rbit;
+
+               ridx = rates[i] / 8;
+               rbit = BIT(rates[i] % 8);
+
+               /* check validity */
+               if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
+                       return false;
+
+               /* check availability */
+               if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
+                       mcs[ridx] |= rbit;
+               else
+                       return false;
+       }
+
+       return true;
+}
+
 static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
        [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
                                    .len = NL80211_MAX_SUPP_RATES },
+       [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
+                                .len = NL80211_MAX_SUPP_HT_RATES },
 };
 
 static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
@@ -5418,12 +5503,20 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                sband = rdev->wiphy.bands[i];
                mask.control[i].legacy =
                        sband ? (1 << sband->n_bitrates) - 1 : 0;
+               if (sband)
+                       memcpy(mask.control[i].mcs,
+                              sband->ht_cap.mcs.rx_mask,
+                              sizeof(mask.control[i].mcs));
+               else
+                       memset(mask.control[i].mcs, 0,
+                              sizeof(mask.control[i].mcs));
        }
 
        /*
         * The nested attribute uses enum nl80211_band as the index. This maps
         * directly to the enum ieee80211_band values used in cfg80211.
         */
+       BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
        nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
        {
                enum ieee80211_band band = nla_type(tx_rates);
@@ -5439,7 +5532,28 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                                sband,
                                nla_data(tb[NL80211_TXRATE_LEGACY]),
                                nla_len(tb[NL80211_TXRATE_LEGACY]));
-                       if (mask.control[band].legacy == 0)
+               }
+               if (tb[NL80211_TXRATE_MCS]) {
+                       if (!ht_rateset_to_mask(
+                                       sband,
+                                       nla_data(tb[NL80211_TXRATE_MCS]),
+                                       nla_len(tb[NL80211_TXRATE_MCS]),
+                                       mask.control[band].mcs))
+                               return -EINVAL;
+               }
+
+               if (mask.control[band].legacy == 0) {
+                       /* don't allow empty legacy rates if HT
+                        * is not even supported. */
+                       if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
+                               return -EINVAL;
+
+                       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+                               if (mask.control[band].mcs[i])
+                                       break;
+
+                       /* legacy and mcs rates may not be both empty */
+                       if (i == IEEE80211_HT_MCS_MASK_LEN)
                                return -EINVAL;
                }
        }
@@ -6293,23 +6407,23 @@ static struct genl_ops nl80211_ops[] = {
                .cmd = NL80211_CMD_SET_BEACON,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
-               .doit = nl80211_addset_beacon,
+               .doit = nl80211_set_beacon,
                .internal_flags = NL80211_FLAG_NEED_NETDEV |
                                  NL80211_FLAG_NEED_RTNL,
        },
        {
-               .cmd = NL80211_CMD_NEW_BEACON,
+               .cmd = NL80211_CMD_START_AP,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
-               .doit = nl80211_addset_beacon,
+               .doit = nl80211_start_ap,
                .internal_flags = NL80211_FLAG_NEED_NETDEV |
                                  NL80211_FLAG_NEED_RTNL,
        },
        {
-               .cmd = NL80211_CMD_DEL_BEACON,
+               .cmd = NL80211_CMD_STOP_AP,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
-               .doit = nl80211_del_beacon,
+               .doit = nl80211_stop_ap,
                .internal_flags = NL80211_FLAG_NEED_NETDEV |
                                  NL80211_FLAG_NEED_RTNL,
        },
@@ -7580,7 +7694,8 @@ bool nl80211_unexpected_4addr_frame(struct net_device *dev,
 
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
                      struct net_device *netdev, u32 nlpid,
-                     int freq, const u8 *buf, size_t len, gfp_t gfp)
+                     int freq, int sig_dbm,
+                     const u8 *buf, size_t len, gfp_t gfp)
 {
        struct sk_buff *msg;
        void *hdr;
@@ -7598,6 +7713,8 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       if (sig_dbm)
+               NLA_PUT_U32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm);
        NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
 
        genlmsg_end(msg, hdr);
@@ -7859,7 +7976,7 @@ EXPORT_SYMBOL(cfg80211_probe_status);
 
 void cfg80211_report_obss_beacon(struct wiphy *wiphy,
                                 const u8 *frame, size_t len,
-                                int freq, gfp_t gfp)
+                                int freq, int sig_dbm, gfp_t gfp)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
@@ -7882,6 +7999,8 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
        if (freq)
                NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       if (sig_dbm)
+               NLA_PUT_U32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm);
        NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame);
 
        genlmsg_end(msg, hdr);