wiphy: parse HE capabilities from band info

The HE capabilities information is contained in
NL80211_BAND_ATTR_IFTYPE_DATA where each entry is a set of attributes
which define the rules for one or more interface types. This patch
specifically parses the HE PHY and HE MCS data which will be used for
data rate estimation.

Since the set of info is per-iftype(s) the data is stored in a queue
where each entry contains the PHY/MCS info, and a uint32 bit mask where
each bit index signifies an interface type.
This commit is contained in:
James Prestwood 2022-07-19 11:55:40 -07:00 committed by Denis Kenzior
parent 3d48d147ca
commit 9baaaebd38
1 changed files with 177 additions and 3 deletions

View File

@ -936,6 +936,74 @@ static void wiphy_print_mcs_info(const uint8_t *mcs_map,
}
}
static void wiphy_print_he_capabilities(struct band *band,
const struct band_he_capabilities *he_cap)
{
int i;
char type_buf[128];
char *s = type_buf;
uint8_t width_set = bit_field(he_cap->he_phy_capa[0], 1, 7);
for (i = 0; i < 32; i++) {
if (!(he_cap->iftypes & (1 << i)))
continue;
if (L_WARN_ON(s >= type_buf + sizeof(type_buf)))
return;
switch (i) {
case NETDEV_IFTYPE_ADHOC:
s += sprintf(s, "%s ", "Ad-Hoc");
break;
case NETDEV_IFTYPE_STATION:
s += sprintf(s, "%s ", "Station");
break;
case NETDEV_IFTYPE_AP:
s += sprintf(s, "%s ", "AP");
break;
case NETDEV_IFTYPE_P2P_CLIENT:
s += sprintf(s, "%s ", "P2P Client");
break;
case NETDEV_IFTYPE_P2P_GO:
s += sprintf(s, "%s ", "P2P GO");
break;
}
}
l_info("\t\t\tInterface Types: %s", type_buf);
switch (band->freq) {
case BAND_FREQ_2_4_GHZ:
wiphy_print_mcs_info(he_cap->he_mcs_set,
"HE RX <= 80MHz", 7, 9, 11);
wiphy_print_mcs_info(he_cap->he_mcs_set + 2,
"HE TX <= 80MHz", 7, 9, 11);
break;
case BAND_FREQ_5_GHZ:
case BAND_FREQ_6_GHZ:
wiphy_print_mcs_info(he_cap->he_mcs_set,
"HE RX <= 80MHz", 7, 9, 11);
wiphy_print_mcs_info(he_cap->he_mcs_set + 2,
"HE TX <= 80MHz", 7, 9, 11);
if (test_bit(&width_set, 2)) {
wiphy_print_mcs_info(he_cap->he_mcs_set + 4,
"HE RX <= 160MHz", 7, 9, 11);
wiphy_print_mcs_info(he_cap->he_mcs_set + 6,
"HE TX <= 160MHz", 7, 9, 11);
}
if (test_bit(&width_set, 3)) {
wiphy_print_mcs_info(he_cap->he_mcs_set + 8,
"HE RX <= 80+80MHz", 7, 9, 11);
wiphy_print_mcs_info(he_cap->he_mcs_set + 10,
"HE TX <= 80+80MHz", 7, 9, 11);
}
break;
}
}
static void wiphy_print_band_info(struct band *band, const char *name)
{
int i;
@ -995,6 +1063,20 @@ static void wiphy_print_band_info(struct band *band, const char *name)
wiphy_print_mcs_info(band->vht_mcs_set, "RX", 7, 8, 9);
wiphy_print_mcs_info(band->vht_mcs_set + 4, "TX", 7, 8, 9);
}
if (band->he_capabilities) {
const struct l_queue_entry *entry;
l_info("\t\tHE Capabilities");
for (entry = l_queue_get_entries(band->he_capabilities);
entry; entry = entry->next) {
const struct band_he_capabilities *he_cap = entry->data;
wiphy_print_he_capabilities(band, he_cap);
}
}
}
static void wiphy_print_basic_info(struct wiphy *wiphy)
@ -1208,6 +1290,90 @@ static struct band *band_new_from_message(struct l_genl_attr *band)
return ret;
}
static uint32_t get_iftypes(struct l_genl_attr *iftypes)
{
uint16_t type;
uint16_t len;
uint32_t types = 0;
while (l_genl_attr_next(iftypes, &type, &len, NULL)) {
if (len != 0)
continue;
types |= (1 << type);
}
return types;
}
static void parse_iftype_attrs(struct band *band, struct l_genl_attr *types)
{
uint16_t type;
uint16_t len;
const void *data;
unsigned int count = 0;
struct band_he_capabilities *he_cap =
l_new(struct band_he_capabilities, 1);
while (l_genl_attr_next(types, &type, &len, &data)) {
struct l_genl_attr iftypes;
switch (type) {
case NL80211_BAND_IFTYPE_ATTR_IFTYPES:
if (!l_genl_attr_recurse(types, &iftypes))
goto parse_error;
he_cap->iftypes = get_iftypes(&iftypes);
break;
case NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY:
if (len > sizeof(he_cap->he_phy_capa))
continue;
memcpy(he_cap->he_phy_capa, data, len);
count++;
break;
case NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET:
if (len > sizeof(he_cap->he_mcs_set))
continue;
memcpy(he_cap->he_mcs_set, data, len);
count++;
break;
default:
break;
}
}
/*
* Since the capabilities element indicates what values are present in
* the MCS set ensure both values are parsed
*/
if (count != 2 || !he_cap->iftypes)
goto parse_error;
if (!band->he_capabilities)
band->he_capabilities = l_queue_new();
l_queue_push_head(band->he_capabilities, he_cap);
return;
parse_error:
l_free(he_cap);
}
static void parse_band_iftype_data(struct band *band, struct l_genl_attr *ifdata)
{
while (l_genl_attr_next(ifdata, NULL, NULL, NULL)) {
struct l_genl_attr types;
if (!l_genl_attr_recurse(ifdata, &types))
continue;
parse_iftype_attrs(band, &types);
}
}
static void parse_supported_bands(struct wiphy *wiphy,
struct l_genl_attr *bands)
{
@ -1256,15 +1422,17 @@ static void parse_supported_bands(struct wiphy *wiphy,
} else
band = *bandp;
while (l_genl_attr_next(&attr, &type, &len, &data)) {
struct l_genl_attr freqs;
struct l_genl_attr nested;
switch (type) {
case NL80211_BAND_ATTR_FREQS:
if (!l_genl_attr_recurse(&attr, &freqs))
if (!l_genl_attr_recurse(&attr, &nested))
continue;
parse_supported_frequencies(wiphy, &freqs);
parse_supported_frequencies(wiphy, &nested);
break;
case NL80211_BAND_ATTR_RATES:
@ -1304,6 +1472,12 @@ static void parse_supported_bands(struct wiphy *wiphy,
memcpy(band->ht_capabilities, data, len);
band->ht_supported = true;
break;
case NL80211_BAND_ATTR_IFTYPE_DATA:
if (!l_genl_attr_recurse(&attr, &nested))
continue;
parse_band_iftype_data(band, &nested);
break;
}
}