cfg80211: Add support to configure a beacon data rate
This allows an option to configure a single beacon tx rate for an AP. Signed-off-by: Purushottam Kushwaha <pkushwah@qti.qualcomm.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Git-repo: git://git.kernel.org/cgit/linux/kernel/git/jberg/mac80211.git Git-commit: a7c7fbff6a408d00431c705bbe3dfc5f51e3f1c4 CRs-fixed: 1025311 Change-Id: I5f9627964d86c51cb8fb0c5c5f7c9f5f2e19b3e7 Signed-off-by: Purushottam Kushwaha <pkushwah@codeaurora.org>
This commit is contained in:
parent
46aa49c118
commit
0d7846f7e6
2 changed files with 302 additions and 232 deletions
|
@ -695,6 +695,18 @@ struct cfg80211_acl_data {
|
||||||
struct mac_address mac_addrs[];
|
struct mac_address mac_addrs[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cfg80211_bitrate_mask - masks for bitrate control
|
||||||
|
*/
|
||||||
|
struct cfg80211_bitrate_mask {
|
||||||
|
struct {
|
||||||
|
u32 legacy;
|
||||||
|
u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
|
||||||
|
u16 vht_mcs[NL80211_VHT_NSS_MAX];
|
||||||
|
enum nl80211_txrate_gi gi;
|
||||||
|
} control[IEEE80211_NUM_BANDS];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct cfg80211_ap_settings - AP configuration
|
* struct cfg80211_ap_settings - AP configuration
|
||||||
*
|
*
|
||||||
|
@ -719,6 +731,7 @@ struct cfg80211_acl_data {
|
||||||
* MAC address based access control
|
* MAC address based access control
|
||||||
* @pbss: If set, start as a PCP instead of AP. Relevant for DMG
|
* @pbss: If set, start as a PCP instead of AP. Relevant for DMG
|
||||||
* networks.
|
* networks.
|
||||||
|
* @beacon_rate: masks for setting user configured beacon tx rate.
|
||||||
*/
|
*/
|
||||||
struct cfg80211_ap_settings {
|
struct cfg80211_ap_settings {
|
||||||
struct cfg80211_chan_def chandef;
|
struct cfg80211_chan_def chandef;
|
||||||
|
@ -738,6 +751,7 @@ struct cfg80211_ap_settings {
|
||||||
bool p2p_opp_ps;
|
bool p2p_opp_ps;
|
||||||
const struct cfg80211_acl_data *acl;
|
const struct cfg80211_acl_data *acl;
|
||||||
bool pbss;
|
bool pbss;
|
||||||
|
struct cfg80211_bitrate_mask beacon_rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1945,17 +1959,6 @@ enum wiphy_params_flags {
|
||||||
WIPHY_PARAM_DYN_ACK = 1 << 5,
|
WIPHY_PARAM_DYN_ACK = 1 << 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* cfg80211_bitrate_mask - masks for bitrate control
|
|
||||||
*/
|
|
||||||
struct cfg80211_bitrate_mask {
|
|
||||||
struct {
|
|
||||||
u32 legacy;
|
|
||||||
u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
|
|
||||||
u16 vht_mcs[NL80211_VHT_NSS_MAX];
|
|
||||||
enum nl80211_txrate_gi gi;
|
|
||||||
} control[IEEE80211_NUM_BANDS];
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* struct cfg80211_pmksa - PMK Security Association
|
* struct cfg80211_pmksa - PMK Security Association
|
||||||
*
|
*
|
||||||
|
|
|
@ -3245,6 +3245,279 @@ static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
|
||||||
return err;
|
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 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 u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
|
||||||
|
{
|
||||||
|
u16 mcs_mask = 0;
|
||||||
|
|
||||||
|
switch (vht_mcs_map) {
|
||||||
|
case IEEE80211_VHT_MCS_NOT_SUPPORTED:
|
||||||
|
break;
|
||||||
|
case IEEE80211_VHT_MCS_SUPPORT_0_7:
|
||||||
|
mcs_mask = 0x00FF;
|
||||||
|
break;
|
||||||
|
case IEEE80211_VHT_MCS_SUPPORT_0_8:
|
||||||
|
mcs_mask = 0x01FF;
|
||||||
|
break;
|
||||||
|
case IEEE80211_VHT_MCS_SUPPORT_0_9:
|
||||||
|
mcs_mask = 0x03FF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mcs_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vht_build_mcs_mask(u16 vht_mcs_map,
|
||||||
|
u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
|
||||||
|
{
|
||||||
|
u8 nss;
|
||||||
|
|
||||||
|
for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
|
||||||
|
vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
|
||||||
|
vht_mcs_map >>= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
|
||||||
|
struct nl80211_txrate_vht *txrate,
|
||||||
|
u16 mcs[NL80211_VHT_NSS_MAX])
|
||||||
|
{
|
||||||
|
u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
|
||||||
|
u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
|
||||||
|
u8 i;
|
||||||
|
|
||||||
|
if (!sband->vht_cap.vht_supported)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
|
||||||
|
|
||||||
|
/* Build vht_mcs_mask from VHT capabilities */
|
||||||
|
vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
|
||||||
|
|
||||||
|
for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
|
||||||
|
if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
|
||||||
|
mcs[i] = txrate->mcs[i];
|
||||||
|
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_HT] = { .type = NLA_BINARY,
|
||||||
|
.len = NL80211_MAX_SUPP_HT_RATES },
|
||||||
|
[NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
|
||||||
|
[NL80211_TXRATE_GI] = { .type = NLA_U8 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
|
||||||
|
struct cfg80211_bitrate_mask *mask)
|
||||||
|
{
|
||||||
|
struct nlattr *tb[NL80211_TXRATE_MAX + 1];
|
||||||
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||||
|
int rem, i;
|
||||||
|
struct nlattr *tx_rates;
|
||||||
|
struct ieee80211_supported_band *sband;
|
||||||
|
u16 vht_tx_mcs_map;
|
||||||
|
|
||||||
|
memset(mask, 0, sizeof(*mask));
|
||||||
|
/* Default to all rates enabled */
|
||||||
|
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
||||||
|
sband = rdev->wiphy.bands[i];
|
||||||
|
|
||||||
|
if (!sband)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mask->control[i].legacy = (1 << sband->n_bitrates) - 1;
|
||||||
|
memcpy(mask->control[i].ht_mcs,
|
||||||
|
sband->ht_cap.mcs.rx_mask,
|
||||||
|
sizeof(mask->control[i].ht_mcs));
|
||||||
|
|
||||||
|
if (!sband->vht_cap.vht_supported)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
|
||||||
|
vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if no rates are given set it back to the defaults */
|
||||||
|
if (!info->attrs[NL80211_ATTR_TX_RATES])
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* The nested attribute uses enum nl80211_band as the index. This maps
|
||||||
|
* directly to the enum nl80211_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);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (band < 0 || band >= IEEE80211_NUM_BANDS)
|
||||||
|
return -EINVAL;
|
||||||
|
sband = rdev->wiphy.bands[band];
|
||||||
|
if (sband == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
|
||||||
|
nla_len(tx_rates), nl80211_txattr_policy);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
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) &&
|
||||||
|
nla_len(tb[NL80211_TXRATE_LEGACY]))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (tb[NL80211_TXRATE_HT]) {
|
||||||
|
if (!ht_rateset_to_mask(
|
||||||
|
sband,
|
||||||
|
nla_data(tb[NL80211_TXRATE_HT]),
|
||||||
|
nla_len(tb[NL80211_TXRATE_HT]),
|
||||||
|
mask->control[band].ht_mcs))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (tb[NL80211_TXRATE_VHT]) {
|
||||||
|
if (!vht_set_mcs_mask(
|
||||||
|
sband,
|
||||||
|
nla_data(tb[NL80211_TXRATE_VHT]),
|
||||||
|
mask->control[band].vht_mcs))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (tb[NL80211_TXRATE_GI]) {
|
||||||
|
mask->control[band].gi =
|
||||||
|
nla_get_u8(tb[NL80211_TXRATE_GI]);
|
||||||
|
if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask->control[band].legacy == 0) {
|
||||||
|
/* don't allow empty legacy rates if HT or VHT
|
||||||
|
* are not even supported.
|
||||||
|
*/
|
||||||
|
if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
|
||||||
|
rdev->wiphy.bands[band]->vht_cap.vht_supported))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
|
||||||
|
if (mask->control[band].ht_mcs[i])
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
|
||||||
|
if (mask->control[band].vht_mcs[i])
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* legacy and mcs rates may not be both empty */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int validate_beacon_tx_rate(struct cfg80211_ap_settings *params)
|
||||||
|
{
|
||||||
|
u32 rate, count_ht, count_vht, i;
|
||||||
|
enum nl80211_band band;
|
||||||
|
|
||||||
|
band = params->chandef.chan->band;
|
||||||
|
rate = params->beacon_rate.control[band].legacy;
|
||||||
|
|
||||||
|
/* Allow only one rate */
|
||||||
|
if (hweight32(rate) > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
count_ht = 0;
|
||||||
|
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
|
||||||
|
if (hweight8(params->beacon_rate.control[band].ht_mcs[i]) > 1) {
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (params->beacon_rate.control[band].ht_mcs[i]) {
|
||||||
|
count_ht++;
|
||||||
|
if (count_ht > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (count_ht && rate)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
count_vht = 0;
|
||||||
|
for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
|
||||||
|
if (hweight16(params->beacon_rate.control[band].vht_mcs[i]) > 1) {
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (params->beacon_rate.control[band].vht_mcs[i]) {
|
||||||
|
count_vht++;
|
||||||
|
if (count_vht > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (count_vht && rate)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((count_ht && count_vht) || (!rate && !count_ht && !count_vht))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int nl80211_parse_beacon(struct nlattr *attrs[],
|
static int nl80211_parse_beacon(struct nlattr *attrs[],
|
||||||
struct cfg80211_beacon_data *bcn)
|
struct cfg80211_beacon_data *bcn)
|
||||||
{
|
{
|
||||||
|
@ -3474,6 +3747,16 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
||||||
wdev->iftype))
|
wdev->iftype))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (info->attrs[NL80211_ATTR_TX_RATES]) {
|
||||||
|
err = nl80211_parse_tx_bitrate_mask(info, ¶ms.beacon_rate);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = validate_beacon_tx_rate(¶ms);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
|
if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
|
||||||
params.smps_mode =
|
params.smps_mode =
|
||||||
nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]);
|
nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]);
|
||||||
|
@ -8292,237 +8575,21 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
|
||||||
return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
|
return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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 u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
|
|
||||||
{
|
|
||||||
u16 mcs_mask = 0;
|
|
||||||
|
|
||||||
switch (vht_mcs_map) {
|
|
||||||
case IEEE80211_VHT_MCS_NOT_SUPPORTED:
|
|
||||||
break;
|
|
||||||
case IEEE80211_VHT_MCS_SUPPORT_0_7:
|
|
||||||
mcs_mask = 0x00FF;
|
|
||||||
break;
|
|
||||||
case IEEE80211_VHT_MCS_SUPPORT_0_8:
|
|
||||||
mcs_mask = 0x01FF;
|
|
||||||
break;
|
|
||||||
case IEEE80211_VHT_MCS_SUPPORT_0_9:
|
|
||||||
mcs_mask = 0x03FF;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mcs_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vht_build_mcs_mask(u16 vht_mcs_map,
|
|
||||||
u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
|
|
||||||
{
|
|
||||||
u8 nss;
|
|
||||||
|
|
||||||
for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
|
|
||||||
vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
|
|
||||||
vht_mcs_map >>= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
|
|
||||||
struct nl80211_txrate_vht *txrate,
|
|
||||||
u16 mcs[NL80211_VHT_NSS_MAX])
|
|
||||||
{
|
|
||||||
u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
|
|
||||||
u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
|
|
||||||
u8 i;
|
|
||||||
|
|
||||||
if (!sband->vht_cap.vht_supported)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
|
|
||||||
|
|
||||||
/* Build vht_mcs_mask from VHT capabilities */
|
|
||||||
vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
|
|
||||||
|
|
||||||
for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
|
|
||||||
if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
|
|
||||||
mcs[i] = txrate->mcs[i];
|
|
||||||
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_HT] = { .type = NLA_BINARY,
|
|
||||||
.len = NL80211_MAX_SUPP_HT_RATES },
|
|
||||||
[NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
|
|
||||||
[NL80211_TXRATE_GI] = { .type = NLA_U8 },
|
|
||||||
};
|
|
||||||
|
|
||||||
static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
|
static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
|
||||||
struct genl_info *info)
|
struct genl_info *info)
|
||||||
{
|
{
|
||||||
struct nlattr *tb[NL80211_TXRATE_MAX + 1];
|
|
||||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
||||||
struct cfg80211_bitrate_mask mask;
|
struct cfg80211_bitrate_mask mask;
|
||||||
int rem, i;
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||||
struct net_device *dev = info->user_ptr[1];
|
struct net_device *dev = info->user_ptr[1];
|
||||||
struct nlattr *tx_rates;
|
int err;
|
||||||
struct ieee80211_supported_band *sband;
|
|
||||||
u16 vht_tx_mcs_map;
|
|
||||||
|
|
||||||
if (!rdev->ops->set_bitrate_mask)
|
if (!rdev->ops->set_bitrate_mask)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
memset(&mask, 0, sizeof(mask));
|
err = nl80211_parse_tx_bitrate_mask(info, &mask);
|
||||||
/* Default to all rates enabled */
|
|
||||||
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
|
||||||
sband = rdev->wiphy.bands[i];
|
|
||||||
|
|
||||||
if (!sband)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
mask.control[i].legacy = (1 << sband->n_bitrates) - 1;
|
|
||||||
memcpy(mask.control[i].ht_mcs,
|
|
||||||
sband->ht_cap.mcs.rx_mask,
|
|
||||||
sizeof(mask.control[i].ht_mcs));
|
|
||||||
|
|
||||||
if (!sband->vht_cap.vht_supported)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
|
|
||||||
vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if no rates are given set it back to the defaults */
|
|
||||||
if (!info->attrs[NL80211_ATTR_TX_RATES])
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (band < 0 || band >= IEEE80211_NUM_BANDS)
|
|
||||||
return -EINVAL;
|
|
||||||
sband = rdev->wiphy.bands[band];
|
|
||||||
if (sband == NULL)
|
|
||||||
return -EINVAL;
|
|
||||||
err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
|
|
||||||
nla_len(tx_rates), nl80211_txattr_policy);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
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) &&
|
|
||||||
nla_len(tb[NL80211_TXRATE_LEGACY]))
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
if (tb[NL80211_TXRATE_HT]) {
|
|
||||||
if (!ht_rateset_to_mask(
|
|
||||||
sband,
|
|
||||||
nla_data(tb[NL80211_TXRATE_HT]),
|
|
||||||
nla_len(tb[NL80211_TXRATE_HT]),
|
|
||||||
mask.control[band].ht_mcs))
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
if (tb[NL80211_TXRATE_VHT]) {
|
|
||||||
if (!vht_set_mcs_mask(
|
|
||||||
sband,
|
|
||||||
nla_data(tb[NL80211_TXRATE_VHT]),
|
|
||||||
mask.control[band].vht_mcs))
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
if (tb[NL80211_TXRATE_GI]) {
|
|
||||||
mask.control[band].gi =
|
|
||||||
nla_get_u8(tb[NL80211_TXRATE_GI]);
|
|
||||||
if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask.control[band].legacy == 0) {
|
|
||||||
/* don't allow empty legacy rates if HT or VHT
|
|
||||||
* are not even supported.
|
|
||||||
*/
|
|
||||||
if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
|
|
||||||
rdev->wiphy.bands[band]->vht_cap.vht_supported))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
|
|
||||||
if (mask.control[band].ht_mcs[i])
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
|
|
||||||
if (mask.control[band].vht_mcs[i])
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* legacy and mcs rates may not be both empty */
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
|
return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue