mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-26 10:39:23 +01:00
ie: scan: use HT rates in scan ranking
This adds HT IE parsing and data rate calculation for HT (80211n) rates. Now, a BSS supporting HT rates will be ranked higher than a basic rate BSS, assuming the RSSI is at an acceptable level.
This commit is contained in:
parent
5ce6e173ba
commit
ad2bf340a4
218
src/ie.c
218
src/ie.c
@ -1491,6 +1491,224 @@ int ie_parse_supported_rates_from_data(const uint8_t *supp_rates_ie,
|
||||
rssi, data_rate);
|
||||
}
|
||||
|
||||
enum ht_vht_channel_width {
|
||||
HT_VHT_CHANNEL_WIDTH_20MHZ = 0,
|
||||
HT_VHT_CHANNEL_WIDTH_40MHZ,
|
||||
HT_VHT_CHANNEL_WIDTH_80MHZ,
|
||||
HT_VHT_CHANNEL_WIDTH_160MHZ,
|
||||
};
|
||||
|
||||
/*
|
||||
* Base RSSI values for 20MHz (both HT and VHT) channel. These values can be
|
||||
* used to calculate the minimum RSSI values for all other channel widths. HT
|
||||
* MCS indexes are grouped into ranges of 8 (per spatial stream) where VHT are
|
||||
* grouped in chunks of 10. This just means HT will not use the last two
|
||||
* index's of this array.
|
||||
*/
|
||||
static const int32_t ht_vht_base_rssi[] = {
|
||||
-82, -79, -77, -74, -70, -66, -65, -64, -59, -57
|
||||
};
|
||||
|
||||
struct ht_vht_rate {
|
||||
uint64_t rate;
|
||||
uint64_t sgi_rate;
|
||||
};
|
||||
|
||||
static const struct ht_vht_rate ht_vht_rates[] = {
|
||||
[HT_VHT_CHANNEL_WIDTH_20MHZ] = { .rate = 6500000,
|
||||
.sgi_rate = 7200000 },
|
||||
[HT_VHT_CHANNEL_WIDTH_40MHZ] = { .rate = 13500000,
|
||||
.sgi_rate = 15000000 },
|
||||
[HT_VHT_CHANNEL_WIDTH_80MHZ] = { .rate = 29300000,
|
||||
.sgi_rate = 32500000 },
|
||||
[HT_VHT_CHANNEL_WIDTH_160MHZ] = { .rate = 58500000,
|
||||
.sgi_rate = 65000000 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Both HT and VHT rates are calculated in the same fashion. The only difference
|
||||
* is a relative MCS index is used for HT since, for each NSS, the formula
|
||||
* is the same with relative index's. This is why this is called with index % 8
|
||||
* for HT, but not VHT.
|
||||
*/
|
||||
static bool calculate_ht_vht_data_rate(uint8_t index,
|
||||
enum ht_vht_channel_width width,
|
||||
int32_t rssi, uint8_t nss, bool sgi,
|
||||
uint64_t *data_rate)
|
||||
{
|
||||
const struct ht_vht_rate *rate = &ht_vht_rates[width];
|
||||
int32_t width_adjust = width * 3;
|
||||
|
||||
if (rssi < ht_vht_base_rssi[index] + width_adjust)
|
||||
return false;
|
||||
|
||||
if (sgi)
|
||||
*data_rate = rate->sgi_rate;
|
||||
else
|
||||
*data_rate = rate->rate;
|
||||
|
||||
/* adjust base for spatial streams */
|
||||
*data_rate *= nss;
|
||||
|
||||
/*
|
||||
* As with HT, the VHT rates multiplier jumps up
|
||||
* by 2 after MCS index 4
|
||||
*/
|
||||
if (index < 4)
|
||||
*data_rate *= index + 1;
|
||||
else
|
||||
*data_rate *= index + 3;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ie_parse_ht_capability(struct ie_tlv_iter *iter, int32_t rssi,
|
||||
uint64_t *data_rate)
|
||||
{
|
||||
unsigned int len;
|
||||
const uint8_t *data;
|
||||
uint8_t ht_cap;
|
||||
int i;
|
||||
uint64_t highest_rate = 0;
|
||||
bool support_40mhz;
|
||||
bool short_gi_20mhz;
|
||||
bool short_gi_40mhz;
|
||||
|
||||
len = ie_tlv_iter_get_length(iter);
|
||||
|
||||
if (len < 26)
|
||||
return -EINVAL;
|
||||
|
||||
if (ie_tlv_iter_get_tag(iter) != IE_TYPE_HT_CAPABILITIES)
|
||||
return -EINVAL;
|
||||
|
||||
data = ie_tlv_iter_get_data(iter);
|
||||
|
||||
/* Parse out channel width set and short GI */
|
||||
ht_cap = l_get_u8(data++);
|
||||
|
||||
support_40mhz = util_is_bit_set(ht_cap, 1);
|
||||
short_gi_20mhz = util_is_bit_set(ht_cap, 5);
|
||||
short_gi_40mhz = util_is_bit_set(ht_cap, 6);
|
||||
|
||||
data += 2;
|
||||
|
||||
/*
|
||||
* TODO: Support MCS values 32 - 76
|
||||
*
|
||||
* The MCS values > 31 do not follow the same pattern since they use
|
||||
* unequal modulation per spatial stream. These higher MCS values
|
||||
* actually don't follow a pattern at all, since each stream can have a
|
||||
* different modulation a higher MCS value does not mean higher
|
||||
* throughput. For this reason these MCS indexes are left out.
|
||||
*/
|
||||
for (i = 31; i >= 0; i--) {
|
||||
uint64_t drate;
|
||||
uint8_t byte = i / 8;
|
||||
uint8_t bit = i % 8;
|
||||
|
||||
if (!util_is_bit_set(data[byte], bit))
|
||||
continue;
|
||||
|
||||
if (!support_40mhz)
|
||||
goto check_20;
|
||||
|
||||
if (calculate_ht_vht_data_rate(i % 8,
|
||||
HT_VHT_CHANNEL_WIDTH_40MHZ,
|
||||
rssi, (i / 8) + 1,
|
||||
short_gi_40mhz, &drate)) {
|
||||
*data_rate = drate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
check_20:
|
||||
if (!calculate_ht_vht_data_rate(i % 8,
|
||||
HT_VHT_CHANNEL_WIDTH_20MHZ,
|
||||
rssi, (i / 8) + 1,
|
||||
short_gi_20mhz, &drate))
|
||||
continue;
|
||||
|
||||
if (!support_40mhz) {
|
||||
*data_rate = drate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (drate > highest_rate)
|
||||
highest_rate = drate;
|
||||
}
|
||||
|
||||
if (!highest_rate)
|
||||
return -ENOTSUP;
|
||||
|
||||
*data_rate = highest_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ie_parse_ht_capability_from_data(const uint8_t *data, uint8_t len,
|
||||
int32_t rssi, uint64_t *data_rate)
|
||||
{
|
||||
struct ie_tlv_iter iter;
|
||||
uint8_t tag;
|
||||
|
||||
ie_tlv_iter_init(&iter, data, len);
|
||||
|
||||
if (!ie_tlv_iter_next(&iter))
|
||||
return -EMSGSIZE;
|
||||
|
||||
tag = ie_tlv_iter_get_tag(&iter);
|
||||
|
||||
if (tag != IE_TYPE_HT_CAPABILITIES)
|
||||
return -EPROTOTYPE;
|
||||
|
||||
return ie_parse_ht_capability(&iter, rssi, data_rate);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the theoretical maximum data rates out of the provided
|
||||
* supported rates IE, HT IE, and VHT IE. All 3 parsing functions are allowed
|
||||
* to return -ENOTSUP, which indicates that a data rate was not found given
|
||||
* the provided data. This is not fatal, it most likely means our RSSI was too
|
||||
* low.
|
||||
*/
|
||||
int ie_parse_data_rates(const uint8_t *supp_rates_ie,
|
||||
const uint8_t *ext_supp_rates_ie,
|
||||
const uint8_t *ht_ie,
|
||||
int32_t rssi,
|
||||
uint64_t *data_rate)
|
||||
{
|
||||
int ret = -ENOTSUP;
|
||||
uint64_t rate = 0;
|
||||
|
||||
/* An RSSI this low will not yield any rate results */
|
||||
if (rssi < -82)
|
||||
return -ENOTSUP;
|
||||
|
||||
if (ht_ie) {
|
||||
ret = ie_parse_ht_capability_from_data(ht_ie, IE_LEN(ht_ie),
|
||||
rssi, &rate);
|
||||
if (ret == 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (supp_rates_ie || ext_supp_rates_ie) {
|
||||
ret = ie_parse_supported_rates_from_data(supp_rates_ie,
|
||||
IE_LEN(supp_rates_ie),
|
||||
ext_supp_rates_ie,
|
||||
IE_LEN(supp_rates_ie),
|
||||
rssi, &rate);
|
||||
if (ret == 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
done:
|
||||
*data_rate = rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ie_parse_mobility_domain(struct ie_tlv_iter *iter, uint16_t *mdid,
|
||||
bool *ft_over_ds, bool *resource_req)
|
||||
{
|
||||
|
6
src/ie.h
6
src/ie.h
@ -419,6 +419,12 @@ int ie_parse_supported_rates_from_data(const uint8_t *supp_rates_ie,
|
||||
uint8_t ext_supp_rates_len,
|
||||
int32_t rssi, uint64_t *data_rate);
|
||||
|
||||
int ie_parse_data_rates(const uint8_t *supp_rates_ie,
|
||||
const uint8_t *ext_supp_rates_ie,
|
||||
const uint8_t *ht_ie,
|
||||
int32_t rssi,
|
||||
uint64_t *data_rate);
|
||||
|
||||
int ie_parse_mobility_domain(struct ie_tlv_iter *iter, uint16_t *mdid,
|
||||
bool *ft_over_ds, bool *resource_req);
|
||||
int ie_parse_mobility_domain_from_data(const uint8_t *data, uint8_t len,
|
||||
|
25
src/scan.c
25
src/scan.c
@ -865,6 +865,14 @@ static bool scan_parse_bss_information_elements(struct scan_bss *bss,
|
||||
bss->cc[2] = iter.data[2];
|
||||
bss->cc_present = true;
|
||||
|
||||
break;
|
||||
case IE_TYPE_HT_CAPABILITIES:
|
||||
if (iter.len != 26)
|
||||
return false;
|
||||
|
||||
bss->ht_capable = true;
|
||||
memcpy(bss->ht_ie, iter.data - 2, iter.len + 2);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1049,20 +1057,19 @@ static void scan_bss_compute_rank(struct scan_bss *bss)
|
||||
if (bss->has_sup_rates || bss->ext_supp_rates_ie) {
|
||||
uint64_t data_rate;
|
||||
|
||||
if (ie_parse_supported_rates_from_data(bss->has_sup_rates ?
|
||||
bss->supp_rates_ie : NULL,
|
||||
IE_LEN(bss->supp_rates_ie),
|
||||
bss->ext_supp_rates_ie,
|
||||
IE_LEN(bss->ext_supp_rates_ie),
|
||||
bss->signal_strength / 100,
|
||||
&data_rate) == 0) {
|
||||
if (ie_parse_data_rates(bss->has_sup_rates ?
|
||||
bss->supp_rates_ie : NULL,
|
||||
bss->ext_supp_rates_ie,
|
||||
bss->ht_capable ? bss->ht_ie : NULL,
|
||||
bss->signal_strength / 100,
|
||||
&data_rate) == 0) {
|
||||
double factor = RANK_MAX_SUPPORTED_RATE_FACTOR -
|
||||
RANK_MIN_SUPPORTED_RATE_FACTOR;
|
||||
|
||||
/*
|
||||
* Maximum rate is 54 Mbps
|
||||
* Maximum rate is 600 Mbps (HT)
|
||||
*/
|
||||
factor = factor * data_rate / 54000000 +
|
||||
factor = factor * data_rate / 600000000 +
|
||||
RANK_MIN_SUPPORTED_RATE_FACTOR;
|
||||
rank *= factor;
|
||||
} else
|
||||
|
@ -59,10 +59,12 @@ struct scan_bss {
|
||||
uint8_t utilization;
|
||||
uint8_t cc[3];
|
||||
uint16_t rank;
|
||||
uint8_t ht_ie[28];
|
||||
bool mde_present : 1;
|
||||
bool cc_present : 1;
|
||||
bool cap_rm_neighbor_report : 1;
|
||||
bool has_sup_rates : 1;
|
||||
bool ht_capable : 1;
|
||||
};
|
||||
|
||||
struct scan_parameters {
|
||||
|
Loading…
Reference in New Issue
Block a user