mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-26 18:59:22 +01:00
ie: scan: use VHT rates in scan ranking
This adds support for parsing the VHT IE, which allows a BSS supporting VHT (80211ac) to be ranked higher than a BSS supporting only HT/basic rates. Now, with basic/HT/VHT parsing we can calculate the theoretical maximum data rate for all three and rank the BSS based on that.
This commit is contained in:
parent
ad2bf340a4
commit
49b02907a8
213
src/ie.c
213
src/ie.c
@ -1664,6 +1664,210 @@ static int ie_parse_ht_capability_from_data(const uint8_t *data, uint8_t len,
|
|||||||
return ie_parse_ht_capability(&iter, rssi, data_rate);
|
return ie_parse_ht_capability(&iter, rssi, data_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IEEE 802.11 - Table 9-250
|
||||||
|
*
|
||||||
|
* For simplicity, we are ignoring the Extended BSS BW support, per NOTE 11:
|
||||||
|
*
|
||||||
|
* NOTE 11—A receiving STA in which dot11VHTExtendedNSSCapable is false will
|
||||||
|
* ignore the Extended NSS BW Support subfield and effectively evaluate this
|
||||||
|
* table only at the entries where Extended NSS BW Support is 0.
|
||||||
|
*
|
||||||
|
* This also allows us to group the 160/80+80 widths together, since they are
|
||||||
|
* the same when Extended NSS BW is zero.
|
||||||
|
*/
|
||||||
|
static const uint8_t vht_width_map[3][5] = {
|
||||||
|
[0] = { 1, 1, 1, 0, 0 },
|
||||||
|
[1] = { 1, 1, 1, 1, 0 },
|
||||||
|
[2] = { 1, 1, 1, 1, 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ie_parse_vht_capability(struct ie_tlv_iter *vht_iter,
|
||||||
|
struct ie_tlv_iter *ht_iter, int32_t rssi,
|
||||||
|
uint64_t *data_rate)
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int mcs;
|
||||||
|
unsigned int nss;
|
||||||
|
unsigned int len;
|
||||||
|
const uint8_t *data;
|
||||||
|
uint8_t channel_width_set;
|
||||||
|
uint8_t rx_mcs_map[2];
|
||||||
|
uint8_t tx_mcs_map[2];
|
||||||
|
unsigned int max_rx_mcs = 0;
|
||||||
|
unsigned int rx_nss;
|
||||||
|
unsigned int max_tx_mcs = 0;
|
||||||
|
unsigned int tx_nss;
|
||||||
|
uint8_t ht_cap;
|
||||||
|
bool short_gi_20mhz;
|
||||||
|
bool short_gi_40mhz;
|
||||||
|
bool short_gi_80mhz;
|
||||||
|
bool short_gi_160mhz;
|
||||||
|
uint64_t highest_rate = 0;
|
||||||
|
|
||||||
|
/* grab the short GI bits from the HT IE */
|
||||||
|
len = ie_tlv_iter_get_length(ht_iter);
|
||||||
|
|
||||||
|
if (len != 26)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
data = ie_tlv_iter_get_data(ht_iter);
|
||||||
|
|
||||||
|
ht_cap = l_get_u8(data);
|
||||||
|
|
||||||
|
short_gi_20mhz = util_is_bit_set(ht_cap, 5);
|
||||||
|
short_gi_40mhz = util_is_bit_set(ht_cap, 6);
|
||||||
|
|
||||||
|
/* now move onto VHT */
|
||||||
|
len = ie_tlv_iter_get_length(vht_iter);
|
||||||
|
|
||||||
|
if (len != 12)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
data = ie_tlv_iter_get_data(vht_iter);
|
||||||
|
|
||||||
|
channel_width_set = util_bit_field(*data, 2, 2);
|
||||||
|
short_gi_80mhz = util_bit_field(*data, 5, 1);
|
||||||
|
short_gi_160mhz = util_bit_field(*data, 6, 1);
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
|
||||||
|
rx_mcs_map[0] = *data++;
|
||||||
|
rx_mcs_map[1] = *data++;
|
||||||
|
|
||||||
|
data += 2;
|
||||||
|
|
||||||
|
tx_mcs_map[0] = *data++;
|
||||||
|
tx_mcs_map[1] = *data++;
|
||||||
|
|
||||||
|
/* NSS->MCS map values are grouped in 2-bit values */
|
||||||
|
for (mcs = 15; mcs >= 0; mcs -= 2) {
|
||||||
|
uint8_t rx_val = util_bit_field(rx_mcs_map[mcs / 8],
|
||||||
|
mcs % 8, 2);
|
||||||
|
uint8_t tx_val = util_bit_field(tx_mcs_map[mcs / 8],
|
||||||
|
mcs % 8, 2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0 indicates support for MCS 0-7
|
||||||
|
* 1 indicates support for MCS 0-8
|
||||||
|
* 2 indicates support for MCS 0-9
|
||||||
|
*
|
||||||
|
* Therefore 7 + rx/tx_val gives us our max MCS index.
|
||||||
|
*/
|
||||||
|
if (!max_rx_mcs && rx_val < 3) {
|
||||||
|
max_rx_mcs = 7 + rx_val;
|
||||||
|
rx_nss = (mcs / 2) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!max_tx_mcs && tx_val < 3) {
|
||||||
|
max_tx_mcs = 7 + tx_val;
|
||||||
|
tx_nss = (mcs / 2) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_rx_mcs && max_tx_mcs)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!max_rx_mcs && !max_tx_mcs)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, using channel width, MCS index, and NSS we can determine the
|
||||||
|
* theoretical maximum data rate. We iterate through all possible
|
||||||
|
* combinations (width, MCS, NSS), saving the highest data rate we find.
|
||||||
|
*
|
||||||
|
* We could calculate a maximum data rate separately for TX/RX, but
|
||||||
|
* since this is only used for BSS ranking, the minumum between the
|
||||||
|
* two should be good enough.
|
||||||
|
*/
|
||||||
|
for (width = sizeof(vht_width_map[0]) - 1; width >= 0; width--) {
|
||||||
|
bool sgi = false;
|
||||||
|
|
||||||
|
if (!vht_width_map[channel_width_set][width])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Consolidate short GI support into a single boolean, dependent
|
||||||
|
* on the channel width for this iteration.
|
||||||
|
*/
|
||||||
|
switch (width) {
|
||||||
|
case HT_VHT_CHANNEL_WIDTH_20MHZ:
|
||||||
|
sgi = short_gi_20mhz;
|
||||||
|
break;
|
||||||
|
case HT_VHT_CHANNEL_WIDTH_40MHZ:
|
||||||
|
sgi = short_gi_40mhz;
|
||||||
|
break;
|
||||||
|
case HT_VHT_CHANNEL_WIDTH_80MHZ:
|
||||||
|
sgi = short_gi_80mhz;
|
||||||
|
break;
|
||||||
|
case HT_VHT_CHANNEL_WIDTH_160MHZ:
|
||||||
|
sgi = short_gi_160mhz;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (nss = minsize(rx_nss, tx_nss); nss > 0; nss--) {
|
||||||
|
/* NSS > 4 does not apply to 20/40MHz */
|
||||||
|
if (width <= HT_VHT_CHANNEL_WIDTH_40MHZ && nss > 4)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (mcs = minsize(max_rx_mcs, max_tx_mcs);
|
||||||
|
mcs >= 0; mcs--) {
|
||||||
|
uint64_t drate;
|
||||||
|
|
||||||
|
if (!calculate_ht_vht_data_rate(mcs, width,
|
||||||
|
rssi, nss, sgi, &drate))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (drate > highest_rate)
|
||||||
|
highest_rate = drate;
|
||||||
|
|
||||||
|
/* Lower MCS index will only have lower rates */
|
||||||
|
goto next_chanwidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next_chanwidth: ; /* empty statement */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (highest_rate == 0)
|
||||||
|
return -ENOTSUP;
|
||||||
|
|
||||||
|
*data_rate = highest_rate;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ie_parse_vht_capability_from_data(const uint8_t *vht_ie,
|
||||||
|
size_t vht_len, const uint8_t *ht_ie,
|
||||||
|
size_t ht_len, int32_t rssi,
|
||||||
|
uint64_t *data_rate)
|
||||||
|
{
|
||||||
|
struct ie_tlv_iter vht_iter;
|
||||||
|
struct ie_tlv_iter ht_iter;
|
||||||
|
uint8_t tag;
|
||||||
|
|
||||||
|
ie_tlv_iter_init(&vht_iter, vht_ie, vht_len);
|
||||||
|
|
||||||
|
if (!ie_tlv_iter_next(&vht_iter))
|
||||||
|
return -EMSGSIZE;
|
||||||
|
|
||||||
|
tag = ie_tlv_iter_get_tag(&vht_iter);
|
||||||
|
|
||||||
|
if (tag != IE_TYPE_VHT_CAPABILITIES)
|
||||||
|
return -EPROTOTYPE;
|
||||||
|
|
||||||
|
ie_tlv_iter_init(&ht_iter, ht_ie, ht_len);
|
||||||
|
|
||||||
|
if (!ie_tlv_iter_next(&ht_iter))
|
||||||
|
return -EMSGSIZE;
|
||||||
|
|
||||||
|
tag = ie_tlv_iter_get_tag(&ht_iter);
|
||||||
|
|
||||||
|
if (tag != IE_TYPE_HT_CAPABILITIES)
|
||||||
|
return -EPROTOTYPE;
|
||||||
|
|
||||||
|
return ie_parse_vht_capability(&vht_iter, &ht_iter, rssi, data_rate);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculates the theoretical maximum data rates out of the provided
|
* Calculates the theoretical maximum data rates out of the provided
|
||||||
* supported rates IE, HT IE, and VHT IE. All 3 parsing functions are allowed
|
* supported rates IE, HT IE, and VHT IE. All 3 parsing functions are allowed
|
||||||
@ -1674,6 +1878,7 @@ static int ie_parse_ht_capability_from_data(const uint8_t *data, uint8_t len,
|
|||||||
int ie_parse_data_rates(const uint8_t *supp_rates_ie,
|
int ie_parse_data_rates(const uint8_t *supp_rates_ie,
|
||||||
const uint8_t *ext_supp_rates_ie,
|
const uint8_t *ext_supp_rates_ie,
|
||||||
const uint8_t *ht_ie,
|
const uint8_t *ht_ie,
|
||||||
|
const uint8_t *vht_ie,
|
||||||
int32_t rssi,
|
int32_t rssi,
|
||||||
uint64_t *data_rate)
|
uint64_t *data_rate)
|
||||||
{
|
{
|
||||||
@ -1684,6 +1889,14 @@ int ie_parse_data_rates(const uint8_t *supp_rates_ie,
|
|||||||
if (rssi < -82)
|
if (rssi < -82)
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
|
||||||
|
if (ht_ie && vht_ie) {
|
||||||
|
ret = ie_parse_vht_capability_from_data(vht_ie, IE_LEN(vht_ie),
|
||||||
|
ht_ie, IE_LEN(ht_ie),
|
||||||
|
rssi, &rate);
|
||||||
|
if (ret == 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
if (ht_ie) {
|
if (ht_ie) {
|
||||||
ret = ie_parse_ht_capability_from_data(ht_ie, IE_LEN(ht_ie),
|
ret = ie_parse_ht_capability_from_data(ht_ie, IE_LEN(ht_ie),
|
||||||
rssi, &rate);
|
rssi, &rate);
|
||||||
|
1
src/ie.h
1
src/ie.h
@ -422,6 +422,7 @@ int ie_parse_supported_rates_from_data(const uint8_t *supp_rates_ie,
|
|||||||
int ie_parse_data_rates(const uint8_t *supp_rates_ie,
|
int ie_parse_data_rates(const uint8_t *supp_rates_ie,
|
||||||
const uint8_t *ext_supp_rates_ie,
|
const uint8_t *ext_supp_rates_ie,
|
||||||
const uint8_t *ht_ie,
|
const uint8_t *ht_ie,
|
||||||
|
const uint8_t *vht_ie,
|
||||||
int32_t rssi,
|
int32_t rssi,
|
||||||
uint64_t *data_rate);
|
uint64_t *data_rate);
|
||||||
|
|
||||||
|
13
src/scan.c
13
src/scan.c
@ -873,6 +873,14 @@ static bool scan_parse_bss_information_elements(struct scan_bss *bss,
|
|||||||
bss->ht_capable = true;
|
bss->ht_capable = true;
|
||||||
memcpy(bss->ht_ie, iter.data - 2, iter.len + 2);
|
memcpy(bss->ht_ie, iter.data - 2, iter.len + 2);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case IE_TYPE_VHT_CAPABILITIES:
|
||||||
|
if (iter.len != 12)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bss->vht_capable = true;
|
||||||
|
memcpy(bss->vht_ie, iter.data - 2, iter.len + 2);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1061,15 +1069,16 @@ static void scan_bss_compute_rank(struct scan_bss *bss)
|
|||||||
bss->supp_rates_ie : NULL,
|
bss->supp_rates_ie : NULL,
|
||||||
bss->ext_supp_rates_ie,
|
bss->ext_supp_rates_ie,
|
||||||
bss->ht_capable ? bss->ht_ie : NULL,
|
bss->ht_capable ? bss->ht_ie : NULL,
|
||||||
|
bss->vht_capable ? bss->vht_ie : NULL,
|
||||||
bss->signal_strength / 100,
|
bss->signal_strength / 100,
|
||||||
&data_rate) == 0) {
|
&data_rate) == 0) {
|
||||||
double factor = RANK_MAX_SUPPORTED_RATE_FACTOR -
|
double factor = RANK_MAX_SUPPORTED_RATE_FACTOR -
|
||||||
RANK_MIN_SUPPORTED_RATE_FACTOR;
|
RANK_MIN_SUPPORTED_RATE_FACTOR;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Maximum rate is 600 Mbps (HT)
|
* Maximum rate is 2340Mbps (VHT)
|
||||||
*/
|
*/
|
||||||
factor = factor * data_rate / 600000000 +
|
factor = factor * data_rate / 2340000000 +
|
||||||
RANK_MIN_SUPPORTED_RATE_FACTOR;
|
RANK_MIN_SUPPORTED_RATE_FACTOR;
|
||||||
rank *= factor;
|
rank *= factor;
|
||||||
} else
|
} else
|
||||||
|
@ -60,11 +60,13 @@ struct scan_bss {
|
|||||||
uint8_t cc[3];
|
uint8_t cc[3];
|
||||||
uint16_t rank;
|
uint16_t rank;
|
||||||
uint8_t ht_ie[28];
|
uint8_t ht_ie[28];
|
||||||
|
uint8_t vht_ie[14];
|
||||||
bool mde_present : 1;
|
bool mde_present : 1;
|
||||||
bool cc_present : 1;
|
bool cc_present : 1;
|
||||||
bool cap_rm_neighbor_report : 1;
|
bool cap_rm_neighbor_report : 1;
|
||||||
bool has_sup_rates : 1;
|
bool has_sup_rates : 1;
|
||||||
bool ht_capable : 1;
|
bool ht_capable : 1;
|
||||||
|
bool vht_capable : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct scan_parameters {
|
struct scan_parameters {
|
||||||
|
Loading…
Reference in New Issue
Block a user