cfg80211: Add support for HE
Add support for the HE in cfg80211 and also add userspace API to nl80211 to send rate information out, conforming with P802.11ax_D2.0. Signed-off-by: Liad Kaufman <liad.kaufman@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Ilan Peer <ilan.peer@intel.com> Signed-off-by: Ido Yariv <idox.yariv@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
This commit is contained in:

committed by
Johannes Berg

parent
446faa15c6
commit
c4cbaf7973
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright 2015 Intel Deutschland GmbH
|
||||
* Copyright 2015-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
@@ -744,6 +744,8 @@ int wiphy_register(struct wiphy *wiphy)
|
||||
|
||||
/* sanity check supported bands/channels */
|
||||
for (band = 0; band < NUM_NL80211_BANDS; band++) {
|
||||
u16 types = 0;
|
||||
|
||||
sband = wiphy->bands[band];
|
||||
if (!sband)
|
||||
continue;
|
||||
@@ -788,6 +790,23 @@ int wiphy_register(struct wiphy *wiphy)
|
||||
sband->channels[i].band = band;
|
||||
}
|
||||
|
||||
for (i = 0; i < sband->n_iftype_data; i++) {
|
||||
const struct ieee80211_sband_iftype_data *iftd;
|
||||
|
||||
iftd = &sband->iftype_data[i];
|
||||
|
||||
if (WARN_ON(!iftd->types_mask))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(types & iftd->types_mask))
|
||||
return -EINVAL;
|
||||
|
||||
/* at least one piece of information must be present */
|
||||
if (WARN_ON(!iftd->he_cap.has_he))
|
||||
return -EINVAL;
|
||||
|
||||
types |= iftd->types_mask;
|
||||
}
|
||||
|
||||
have_band = true;
|
||||
}
|
||||
|
||||
|
@@ -428,6 +428,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
[NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_HE_CAPABILITY] = { .type = NLA_BINARY,
|
||||
.len = NL80211_HE_MAX_CAPABILITY_LEN },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@@ -1324,6 +1326,34 @@ static int nl80211_send_coalesce(struct sk_buff *msg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nl80211_send_iftype_data(struct sk_buff *msg,
|
||||
const struct ieee80211_sband_iftype_data *iftdata)
|
||||
{
|
||||
const struct ieee80211_sta_he_cap *he_cap = &iftdata->he_cap;
|
||||
|
||||
if (nl80211_put_iftypes(msg, NL80211_BAND_IFTYPE_ATTR_IFTYPES,
|
||||
iftdata->types_mask))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (he_cap->has_he) {
|
||||
if (nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC,
|
||||
sizeof(he_cap->he_cap_elem.mac_cap_info),
|
||||
he_cap->he_cap_elem.mac_cap_info) ||
|
||||
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY,
|
||||
sizeof(he_cap->he_cap_elem.phy_cap_info),
|
||||
he_cap->he_cap_elem.phy_cap_info) ||
|
||||
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
|
||||
sizeof(he_cap->he_mcs_nss_supp),
|
||||
&he_cap->he_mcs_nss_supp) ||
|
||||
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
|
||||
sizeof(he_cap->ppe_thres), he_cap->ppe_thres))
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_send_band_rateinfo(struct sk_buff *msg,
|
||||
struct ieee80211_supported_band *sband)
|
||||
{
|
||||
@@ -1353,6 +1383,32 @@ static int nl80211_send_band_rateinfo(struct sk_buff *msg,
|
||||
sband->vht_cap.cap)))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (sband->n_iftype_data) {
|
||||
struct nlattr *nl_iftype_data =
|
||||
nla_nest_start(msg, NL80211_BAND_ATTR_IFTYPE_DATA);
|
||||
int err;
|
||||
|
||||
if (!nl_iftype_data)
|
||||
return -ENOBUFS;
|
||||
|
||||
for (i = 0; i < sband->n_iftype_data; i++) {
|
||||
struct nlattr *iftdata;
|
||||
|
||||
iftdata = nla_nest_start(msg, i + 1);
|
||||
if (!iftdata)
|
||||
return -ENOBUFS;
|
||||
|
||||
err = nl80211_send_iftype_data(msg,
|
||||
&sband->iftype_data[i]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
nla_nest_end(msg, iftdata);
|
||||
}
|
||||
|
||||
nla_nest_end(msg, nl_iftype_data);
|
||||
}
|
||||
|
||||
/* add bitrates */
|
||||
nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
|
||||
if (!nl_rates)
|
||||
@@ -4472,6 +4528,9 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
|
||||
case RATE_INFO_BW_160:
|
||||
rate_flg = NL80211_RATE_INFO_160_MHZ_WIDTH;
|
||||
break;
|
||||
case RATE_INFO_BW_HE_RU:
|
||||
rate_flg = 0;
|
||||
WARN_ON(!(info->flags & RATE_INFO_FLAGS_HE_MCS));
|
||||
}
|
||||
|
||||
if (rate_flg && nla_put_flag(msg, rate_flg))
|
||||
@@ -4491,6 +4550,19 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
|
||||
if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
|
||||
nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
|
||||
return false;
|
||||
} else if (info->flags & RATE_INFO_FLAGS_HE_MCS) {
|
||||
if (nla_put_u8(msg, NL80211_RATE_INFO_HE_MCS, info->mcs))
|
||||
return false;
|
||||
if (nla_put_u8(msg, NL80211_RATE_INFO_HE_NSS, info->nss))
|
||||
return false;
|
||||
if (nla_put_u8(msg, NL80211_RATE_INFO_HE_GI, info->he_gi))
|
||||
return false;
|
||||
if (nla_put_u8(msg, NL80211_RATE_INFO_HE_DCM, info->he_dcm))
|
||||
return false;
|
||||
if (info->bw == RATE_INFO_BW_HE_RU &&
|
||||
nla_put_u8(msg, NL80211_RATE_INFO_HE_RU_ALLOC,
|
||||
info->he_ru_alloc))
|
||||
return false;
|
||||
}
|
||||
|
||||
nla_nest_end(msg, rate);
|
||||
@@ -4887,7 +4959,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
|
||||
return -EINVAL;
|
||||
if (params->supported_rates)
|
||||
return -EINVAL;
|
||||
if (params->ext_capab || params->ht_capa || params->vht_capa)
|
||||
if (params->ext_capab || params->ht_capa || params->vht_capa ||
|
||||
params->he_capa)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -5093,6 +5166,15 @@ static int nl80211_set_station_tdls(struct genl_info *info,
|
||||
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
||||
params->vht_capa =
|
||||
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
||||
if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) {
|
||||
params->he_capa =
|
||||
nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
|
||||
params->he_capa_len =
|
||||
nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
|
||||
|
||||
if (params->he_capa_len < NL80211_HE_MIN_CAPABILITY_LEN)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = nl80211_parse_sta_channel_info(info, params);
|
||||
if (err)
|
||||
@@ -5320,6 +5402,17 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
||||
params.vht_capa =
|
||||
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) {
|
||||
params.he_capa =
|
||||
nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
|
||||
params.he_capa_len =
|
||||
nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
|
||||
|
||||
/* max len is validated in nla policy */
|
||||
if (params.he_capa_len < NL80211_HE_MIN_CAPABILITY_LEN)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
|
||||
params.opmode_notif_used = true;
|
||||
params.opmode_notif =
|
||||
@@ -5352,6 +5445,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) {
|
||||
params.ht_capa = NULL;
|
||||
params.vht_capa = NULL;
|
||||
|
||||
/* HE requires WME */
|
||||
if (params.he_capa_len)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* When you run into this, adjust the code below for the new flag */
|
||||
|
@@ -4,6 +4,7 @@
|
||||
*
|
||||
* Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright 2017 Intel Deutschland GmbH
|
||||
*/
|
||||
#include <linux/export.h>
|
||||
#include <linux/bitops.h>
|
||||
@@ -1142,6 +1143,85 @@ static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 cfg80211_calculate_bitrate_he(struct rate_info *rate)
|
||||
{
|
||||
#define SCALE 2048
|
||||
u16 mcs_divisors[12] = {
|
||||
34133, /* 16.666666... */
|
||||
17067, /* 8.333333... */
|
||||
11378, /* 5.555555... */
|
||||
8533, /* 4.166666... */
|
||||
5689, /* 2.777777... */
|
||||
4267, /* 2.083333... */
|
||||
3923, /* 1.851851... */
|
||||
3413, /* 1.666666... */
|
||||
2844, /* 1.388888... */
|
||||
2560, /* 1.250000... */
|
||||
2276, /* 1.111111... */
|
||||
2048, /* 1.000000... */
|
||||
};
|
||||
u32 rates_160M[3] = { 960777777, 907400000, 816666666 };
|
||||
u32 rates_969[3] = { 480388888, 453700000, 408333333 };
|
||||
u32 rates_484[3] = { 229411111, 216666666, 195000000 };
|
||||
u32 rates_242[3] = { 114711111, 108333333, 97500000 };
|
||||
u32 rates_106[3] = { 40000000, 37777777, 34000000 };
|
||||
u32 rates_52[3] = { 18820000, 17777777, 16000000 };
|
||||
u32 rates_26[3] = { 9411111, 8888888, 8000000 };
|
||||
u64 tmp;
|
||||
u32 result;
|
||||
|
||||
if (WARN_ON_ONCE(rate->mcs > 11))
|
||||
return 0;
|
||||
|
||||
if (WARN_ON_ONCE(rate->he_gi > NL80211_RATE_INFO_HE_GI_3_2))
|
||||
return 0;
|
||||
if (WARN_ON_ONCE(rate->he_ru_alloc >
|
||||
NL80211_RATE_INFO_HE_RU_ALLOC_2x996))
|
||||
return 0;
|
||||
if (WARN_ON_ONCE(rate->nss < 1 || rate->nss > 8))
|
||||
return 0;
|
||||
|
||||
if (rate->bw == RATE_INFO_BW_160)
|
||||
result = rates_160M[rate->he_gi];
|
||||
else if (rate->bw == RATE_INFO_BW_80 ||
|
||||
(rate->bw == RATE_INFO_BW_HE_RU &&
|
||||
rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_996))
|
||||
result = rates_969[rate->he_gi];
|
||||
else if (rate->bw == RATE_INFO_BW_40 ||
|
||||
(rate->bw == RATE_INFO_BW_HE_RU &&
|
||||
rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_484))
|
||||
result = rates_484[rate->he_gi];
|
||||
else if (rate->bw == RATE_INFO_BW_20 ||
|
||||
(rate->bw == RATE_INFO_BW_HE_RU &&
|
||||
rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_242))
|
||||
result = rates_242[rate->he_gi];
|
||||
else if (rate->bw == RATE_INFO_BW_HE_RU &&
|
||||
rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_106)
|
||||
result = rates_106[rate->he_gi];
|
||||
else if (rate->bw == RATE_INFO_BW_HE_RU &&
|
||||
rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_52)
|
||||
result = rates_52[rate->he_gi];
|
||||
else if (rate->bw == RATE_INFO_BW_HE_RU &&
|
||||
rate->he_ru_alloc == NL80211_RATE_INFO_HE_RU_ALLOC_26)
|
||||
result = rates_26[rate->he_gi];
|
||||
else if (WARN(1, "invalid HE MCS: bw:%d, ru:%d\n",
|
||||
rate->bw, rate->he_ru_alloc))
|
||||
return 0;
|
||||
|
||||
/* now scale to the appropriate MCS */
|
||||
tmp = result;
|
||||
tmp *= SCALE;
|
||||
do_div(tmp, mcs_divisors[rate->mcs]);
|
||||
result = tmp;
|
||||
|
||||
/* and take NSS, DCM into account */
|
||||
result = (result * rate->nss) / 8;
|
||||
if (rate->he_dcm)
|
||||
result /= 2;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 cfg80211_calculate_bitrate(struct rate_info *rate)
|
||||
{
|
||||
if (rate->flags & RATE_INFO_FLAGS_MCS)
|
||||
@@ -1150,6 +1230,8 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
|
||||
return cfg80211_calculate_bitrate_60g(rate);
|
||||
if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
|
||||
return cfg80211_calculate_bitrate_vht(rate);
|
||||
if (rate->flags & RATE_INFO_FLAGS_HE_MCS)
|
||||
return cfg80211_calculate_bitrate_he(rate);
|
||||
|
||||
return rate->legacy;
|
||||
}
|
||||
|
Reference in New Issue
Block a user