mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-12-22 21:22:37 +01:00
ie: Remove old data rate estimation utilities
This commit is contained in:
parent
297cde85bc
commit
647f1e9b91
449
src/ie.c
449
src/ie.c
@ -31,7 +31,6 @@
|
||||
#include "ell/useful.h"
|
||||
#include "src/util.h"
|
||||
#include "src/crypto.h"
|
||||
#include "src/band.h"
|
||||
#include "src/ie.h"
|
||||
|
||||
const unsigned char ieee_oui[3] = { 0x00, 0x0f, 0xac };
|
||||
@ -1632,454 +1631,6 @@ int ie_parse_bss_load_from_data(const uint8_t *data, uint8_t len,
|
||||
out_channel_utilization, out_admission_capacity);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to store this mapping since basic rates don't come with a convenient
|
||||
* MCS index. Rates are stored as they are encoded in the Supported Rates IE.
|
||||
* This does not include non 802.11g data rates, e.g. 1/2/4Mbps. This data was
|
||||
* taken from 802.11 Section 17.3.10.2 and Table 10-7.
|
||||
*
|
||||
* Section 17.3.10.2 defines minimum RSSI for modulations, and Table
|
||||
* 10-7 defines reference rates for the different modulations. Together we
|
||||
* have minimum RSSI required for a given data rate.
|
||||
*/
|
||||
struct basic_rate_map {
|
||||
int32_t rssi;
|
||||
uint8_t rate;
|
||||
};
|
||||
|
||||
/*
|
||||
* Rates are stored in 500Kbps increments. This is how the IE encodes the data
|
||||
* so its more convenient to match by this encoding. The actual data rate is
|
||||
* converted to Mbps after we find a match
|
||||
*/
|
||||
static const struct basic_rate_map rate_rssi_map[] = {
|
||||
{ -82, 12 },
|
||||
{ -81, 18 },
|
||||
{ -79, 24 },
|
||||
{ -77, 36 },
|
||||
{ -74, 48 },
|
||||
{ -70, 72 },
|
||||
{ -66, 96 },
|
||||
{ -65, 108 },
|
||||
};
|
||||
|
||||
static int ie_parse_supported_rates_from_data(const uint8_t *supp_rates_ie,
|
||||
const uint8_t *ext_supp_rates_ie,
|
||||
int32_t rssi, uint64_t *data_rate)
|
||||
{
|
||||
uint8_t max_rate = 0;
|
||||
uint8_t highest = 0;
|
||||
const uint8_t *rates;
|
||||
unsigned int len;
|
||||
unsigned int i;
|
||||
|
||||
/* Find highest rates possible with our RSSI */
|
||||
for (i = 0; i < L_ARRAY_SIZE(rate_rssi_map); i++) {
|
||||
const struct basic_rate_map *map = &rate_rssi_map[i];
|
||||
|
||||
if (rssi < map->rssi)
|
||||
break;
|
||||
|
||||
max_rate = map->rate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find highest rate in Supported Rates IE. These IEs have at least
|
||||
* been verfied that the length is within the buffer bounds (as has
|
||||
* ext_supp_rates_ie).
|
||||
*/
|
||||
if (supp_rates_ie) {
|
||||
len = supp_rates_ie[1];
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
rates = supp_rates_ie + 2;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t r = rates[i] & 0x7f;
|
||||
|
||||
if (r <= max_rate && r > highest)
|
||||
highest = r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find highest rate in Extended Supported Rates IE */
|
||||
if (ext_supp_rates_ie) {
|
||||
len = ext_supp_rates_ie[1];
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
rates = ext_supp_rates_ie + 2;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t r = rates[i] & 0x7f;
|
||||
|
||||
if (r <= max_rate && r > highest)
|
||||
highest = r;
|
||||
}
|
||||
}
|
||||
|
||||
if (highest)
|
||||
*data_rate = (highest / 2) * 1000000;
|
||||
else
|
||||
*data_rate = (max_rate / 2) * 1000000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 = test_bit(&ht_cap, 1);
|
||||
short_gi_20mhz = test_bit(&ht_cap, 5);
|
||||
short_gi_40mhz = test_bit(&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;
|
||||
|
||||
if (!test_bit(data, i))
|
||||
continue;
|
||||
|
||||
if (!support_40mhz)
|
||||
goto check_20;
|
||||
|
||||
if (band_ofdm_rate(i % 8, OFDM_CHANNEL_WIDTH_40MHZ,
|
||||
rssi, (i / 8) + 1, short_gi_40mhz, &drate)) {
|
||||
*data_rate = drate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
check_20:
|
||||
if (!band_ofdm_rate(i % 8, OFDM_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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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][4] = {
|
||||
[0] = { 1, 1, 1, 0 },
|
||||
[1] = { 1, 1, 1, 1 },
|
||||
[2] = { 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 bitoffset;
|
||||
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 = 1;
|
||||
unsigned int max_tx_mcs = 0;
|
||||
unsigned int tx_nss = 1;
|
||||
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 = test_bit(&ht_cap, 5);
|
||||
short_gi_40mhz = test_bit(&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 = bit_field(*data, 2, 2);
|
||||
short_gi_80mhz = bit_field(*data, 5, 1);
|
||||
short_gi_160mhz = 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 (bitoffset = 14; bitoffset >= 0; bitoffset -= 2) {
|
||||
uint8_t rx_val = bit_field(rx_mcs_map[bitoffset / 8],
|
||||
bitoffset % 8, 2);
|
||||
uint8_t tx_val = bit_field(tx_mcs_map[bitoffset / 8],
|
||||
bitoffset % 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 = (bitoffset / 2) + 1;
|
||||
}
|
||||
|
||||
if (!max_tx_mcs && tx_val < 3) {
|
||||
max_tx_mcs = 7 + tx_val;
|
||||
tx_nss = (bitoffset / 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 minimum 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 OFDM_CHANNEL_WIDTH_20MHZ:
|
||||
sgi = short_gi_20mhz;
|
||||
break;
|
||||
case OFDM_CHANNEL_WIDTH_40MHZ:
|
||||
sgi = short_gi_40mhz;
|
||||
break;
|
||||
case OFDM_CHANNEL_WIDTH_80MHZ:
|
||||
sgi = short_gi_80mhz;
|
||||
break;
|
||||
case OFDM_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 <= OFDM_CHANNEL_WIDTH_40MHZ && nss > 4)
|
||||
continue;
|
||||
|
||||
for (mcs = minsize(max_rx_mcs, max_tx_mcs);
|
||||
mcs >= 0; mcs--) {
|
||||
uint64_t drate;
|
||||
|
||||
if (!band_ofdm_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
|
||||
* 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,
|
||||
const uint8_t *vht_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 && 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) {
|
||||
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,
|
||||
ext_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)
|
||||
{
|
||||
|
7
src/ie.h
7
src/ie.h
@ -484,13 +484,6 @@ int ie_parse_bss_load_from_data(const uint8_t *data, uint8_t len,
|
||||
uint8_t *out_channel_utilization,
|
||||
uint16_t *out_admission_capacity);
|
||||
|
||||
int ie_parse_data_rates(const uint8_t *supp_rates_ie,
|
||||
const uint8_t *ext_supp_rates_ie,
|
||||
const uint8_t *ht_ie,
|
||||
const uint8_t *vht_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,
|
||||
|
Loading…
Reference in New Issue
Block a user