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 "ell/useful.h"
|
||||||
#include "src/util.h"
|
#include "src/util.h"
|
||||||
#include "src/crypto.h"
|
#include "src/crypto.h"
|
||||||
#include "src/band.h"
|
|
||||||
#include "src/ie.h"
|
#include "src/ie.h"
|
||||||
|
|
||||||
const unsigned char ieee_oui[3] = { 0x00, 0x0f, 0xac };
|
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);
|
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,
|
int ie_parse_mobility_domain(struct ie_tlv_iter *iter, uint16_t *mdid,
|
||||||
bool *ft_over_ds, bool *resource_req)
|
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,
|
uint8_t *out_channel_utilization,
|
||||||
uint16_t *out_admission_capacity);
|
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,
|
int ie_parse_mobility_domain(struct ie_tlv_iter *iter, uint16_t *mdid,
|
||||||
bool *ft_over_ds, bool *resource_req);
|
bool *ft_over_ds, bool *resource_req);
|
||||||
int ie_parse_mobility_domain_from_data(const uint8_t *data, uint8_t len,
|
int ie_parse_mobility_domain_from_data(const uint8_t *data, uint8_t len,
|
||||||
|
Loading…
Reference in New Issue
Block a user