|
@@ -144,6 +144,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
|
|
|
.len = WLAN_PMKID_LEN },
|
|
|
[NL80211_ATTR_DURATION] = { .type = NLA_U32 },
|
|
|
[NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
|
|
|
+ [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
|
|
|
};
|
|
|
|
|
|
/* policy for the attributes */
|
|
@@ -575,6 +576,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
|
|
CMD(del_pmksa, DEL_PMKSA);
|
|
|
CMD(flush_pmksa, FLUSH_PMKSA);
|
|
|
CMD(remain_on_channel, REMAIN_ON_CHANNEL);
|
|
|
+ CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
|
|
|
if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
|
|
|
i++;
|
|
|
NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
|
|
@@ -4438,6 +4440,109 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
|
|
|
+ u8 *rates, u8 rates_len)
|
|
|
+{
|
|
|
+ u8 i;
|
|
|
+ u32 mask = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < rates_len; i++) {
|
|
|
+ int rate = (rates[i] & 0x7f) * 5;
|
|
|
+ int ridx;
|
|
|
+ for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
|
|
|
+ struct ieee80211_rate *srate =
|
|
|
+ &sband->bitrates[ridx];
|
|
|
+ if (rate == srate->bitrate) {
|
|
|
+ mask |= 1 << ridx;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (ridx == sband->n_bitrates)
|
|
|
+ return 0; /* rate not found */
|
|
|
+ }
|
|
|
+
|
|
|
+ return mask;
|
|
|
+}
|
|
|
+
|
|
|
+static struct nla_policy
|
|
|
+nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] __read_mostly = {
|
|
|
+ [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
|
|
|
+ .len = NL80211_MAX_SUPP_RATES },
|
|
|
+};
|
|
|
+
|
|
|
+static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
|
|
|
+ struct genl_info *info)
|
|
|
+{
|
|
|
+ struct nlattr *tb[NL80211_TXRATE_MAX + 1];
|
|
|
+ struct cfg80211_registered_device *rdev;
|
|
|
+ struct cfg80211_bitrate_mask mask;
|
|
|
+ int err, rem, i;
|
|
|
+ struct net_device *dev;
|
|
|
+ struct nlattr *tx_rates;
|
|
|
+ struct ieee80211_supported_band *sband;
|
|
|
+
|
|
|
+ if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ rtnl_lock();
|
|
|
+
|
|
|
+ err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
+ if (err)
|
|
|
+ goto unlock_rtnl;
|
|
|
+
|
|
|
+ if (!rdev->ops->set_bitrate_mask) {
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(&mask, 0, sizeof(mask));
|
|
|
+ /* Default to all rates enabled */
|
|
|
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
|
|
+ sband = rdev->wiphy.bands[i];
|
|
|
+ mask.control[i].legacy =
|
|
|
+ sband ? (1 << sband->n_bitrates) - 1 : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The nested attribute uses enum nl80211_band as the index. This maps
|
|
|
+ * directly to the enum ieee80211_band values used in cfg80211.
|
|
|
+ */
|
|
|
+ nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
|
|
|
+ {
|
|
|
+ enum ieee80211_band band = nla_type(tx_rates);
|
|
|
+ if (band < 0 || band >= IEEE80211_NUM_BANDS) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+ sband = rdev->wiphy.bands[band];
|
|
|
+ if (sband == NULL) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+ nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
|
|
|
+ nla_len(tx_rates), nl80211_txattr_policy);
|
|
|
+ if (tb[NL80211_TXRATE_LEGACY]) {
|
|
|
+ mask.control[band].legacy = rateset_to_mask(
|
|
|
+ sband,
|
|
|
+ nla_data(tb[NL80211_TXRATE_LEGACY]),
|
|
|
+ nla_len(tb[NL80211_TXRATE_LEGACY]));
|
|
|
+ if (mask.control[band].legacy == 0) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
|
|
|
+
|
|
|
+ unlock:
|
|
|
+ dev_put(dev);
|
|
|
+ cfg80211_unlock_rdev(rdev);
|
|
|
+ unlock_rtnl:
|
|
|
+ rtnl_unlock();
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static struct genl_ops nl80211_ops[] = {
|
|
|
{
|
|
|
.cmd = NL80211_CMD_GET_WIPHY,
|
|
@@ -4712,6 +4817,12 @@ static struct genl_ops nl80211_ops[] = {
|
|
|
.policy = nl80211_policy,
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
},
|
|
|
+ {
|
|
|
+ .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
|
|
|
+ .doit = nl80211_set_tx_bitrate_mask,
|
|
|
+ .policy = nl80211_policy,
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
static struct genl_multicast_group nl80211_mlme_mcgrp = {
|