blob: e5ea48deab4197f6cae94ede6d16ad5565c2c948 [file] [log] [blame]
// Copyright (c) 2020 The Fuchsia Authors
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without
// fee is hereby granted, provided that the above copyright notice and this permission notice
// appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
// SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
// OF THIS SOFTWARE.
#include <fuchsia/wlan/common/cpp/fidl.h>
#include <zircon/compiler.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <wlan/common/logging.h>
#include <wlan/common/mcs_rate_lookup.h>
namespace wlan::common {
namespace {
// HT has 2 valid guard intervals.
const uint8_t kHtGuardIntervalCount = 2;
using ht_mcs_entry_t = uint32_t[kHtGuardIntervalCount];
// When accessing items in ht_mcs_entry_t, the rates are indexed by guard interval.
const uint8_t kLongGiIndex = 0;
const uint8_t kShortGiIndex = 1;
#define HT_INVALID_RATE 0
// Where an HT MCS index is invalid, the lookup table will return this placeholder.
#define HT_MCS_INVALID \
{ HT_INVALID_RATE, HT_INVALID_RATE }
// All HT tables have the same number of MCS indices, 0-76.
const uint8_t kHtMaxMcs = 76;
const uint8_t kHtMaxMcsCount = 77;
using ht_per_bandwidth_table_t = ht_mcs_entry_t[kHtMaxMcsCount];
// These are MCS rate lookup tables for HT, one table per channel bandwidth.
// Each table is a multidimensional array, indexed by MCS, and then GI.
// For example, to lookup the rate for 40 MHz channel bandwidth, MCS 8, and short GI:
// uint8_t mcs = 8;
// uint8_t gi = kShortGiIndex;
// uint32_t rate = kHt40mhzRateLookup[mcs][kShortGiIndex];
const ht_per_bandwidth_table_t kHt20mhzRateLookup = {
// IEEE 802.11-2016 Table 19-27. NSS=1, NES=1
{6500, 7200}, // MCS index 0
{13000, 14400},
{19500, 21700},
{26000, 28900},
{39000, 43300},
{52000, 57800},
{58500, 65000},
{65000, 72200},
// IEEE 802.11-2016 Table 19-28. NSS=2, NES=1, EQM
{13000, 14400}, // MCS index 8
{26000, 28900},
{39000, 43300},
{52000, 57800},
{78000, 86700},
{104000, 115600},
{117000, 130000},
{130000, 144400},
// IEEE 802.11-2016 Table 19-29. NSS=3, NES=1, EQM
{19500, 21700}, // MCS index 16
{39000, 43300},
{58500, 65000},
{78000, 86700},
{117000, 130000},
{156000, 173300},
{175500, 195000},
{195000, 216700},
// IEEE 802.11-2016 Table 19-30. NSS=4, NES=1, EQM
{26000, 28900}, // MCS index 24
{52000, 57800},
{78000, 86700},
{104000, 115600},
{156000, 173300},
{208000, 231100},
{234000, 260000},
{260000, 288900},
// MCS index 32, which is not defined
HT_MCS_INVALID, // MCS index 32
// IEEE 802.11-2016 Table 19-36. NSS=2, NES=1, UEQM
{39000, 43300}, // MCS index 33
{52000, 57800},
{65000, 72200},
{58500, 65000},
{78000, 86700},
{97500, 108300},
// IEEE 802.11-2016 Table 19-37. NSS=3, NES=1, UEQM
{52000, 57800}, // MCS index 39
{65000, 72200},
{65000, 72200},
{78000, 86700},
{91000, 101100},
{91000, 101100},
{104000, 115600},
{78000, 86700},
{97500, 108300},
{97500, 108300},
{117000, 130000},
{136500, 151700},
{136500, 151700},
{156000, 173300},
// IEEE 802.11-2016 Table 19-38. NSS=4, NES=1, UEQM
{65000, 72200}, // MCS index 53
{78000, 86700},
{91000, 101100},
{78000, 86700},
{91000, 101100},
{104000, 115600},
{117000, 130000},
{104000, 115600},
{117000, 130000},
{130000, 144400},
{130000, 144400},
{143000, 158900},
{97500, 108300},
{117000, 130000},
{136500, 151700},
{117000, 130000},
{136500, 151700},
{156000, 173300},
{175500, 195000},
{156000, 173300},
{175500, 195000},
{195000, 216700},
{195000, 216700},
{214500, 238200}}; // MCS index 76
const ht_per_bandwidth_table_t kHt40mhzRateLookup = {
// IEEE 802.11-2016 Table 19-31. NSS=1, NES=1
{13500, 15000}, // MCS index 0
{27000, 30000},
{40500, 45000},
{54000, 60000},
{81000, 90000},
{108000, 120000},
{121500, 135000},
{135000, 150000},
// IEEE 802.11-2016 Table 19-32. NSS=2, NES=1, EQM
{27000, 30000}, // MCS index 8
{54000, 60000},
{81000, 90000},
{108000, 120000},
{162000, 180000},
{216000, 240000},
{243000, 270000},
{270000, 300000},
// IEEE 802.11-2016 Table 19-33. NSS=3, NES=1, EQM
{40500, 45000}, // MCS index 16
{81000, 90000},
{121500, 135000},
{162000, 180000},
{243000, 270000},
{324000, 360000},
{364500, 405000},
{405000, 450000},
// IEEE 802.11-2016 Table 19-34. NSS=4, NES=1, EQM
{54000, 60000}, // MCS index 24
{108000, 120000},
{162000, 180000},
{216000, 240000},
{324000, 360000},
{432000, 480000},
{486000, 540000},
{540000, 600000},
// IEEE 802.11-2016 Table 19-35. NSS=1, NES=1, MCS 32 format
{6000, 6700}, // MCS index 32
// IEEE 802.11-2016 Table 19-39. NSS=2, NES=1, UEQM
{81000, 90000}, // MCS index 33
{108000, 120000},
{135000, 150000},
{121500, 135000},
{162000, 180000},
{202500, 225000},
// IEEE 802.11-2016 Table 19-40. NSS=3, UEQM
{108000, 120000}, // MCS index 39
{135000, 150000},
{135000, 150000},
{162000, 180000},
{189000, 210000},
{189000, 210000},
{216000, 240000},
{162000, 180000},
{202500, 225000},
{202500, 225000},
{243000, 270000},
{283500, 315000},
{283500, 315000},
{324000, 360000},
// IEEE 802.11-2016 Table 19-41. NSS=4, UEQM
{135000, 150000}, // MCS index 53
{162000, 180000},
{189000, 210000},
{162000, 180000},
{189000, 210000},
{216000, 240000},
{243000, 270000},
{216000, 240000},
{243000, 270000},
{270000, 300000},
{270000, 300000},
{297000, 330000},
{202500, 225000},
{243000, 270000},
{283500, 315000},
{243000, 270000},
{283500, 315000},
{324000, 360000},
{364500, 405000},
{324000, 360000},
{364500, 405000},
{405000, 450000},
{405000, 450000},
{445500, 495000}}; // MCS index 76
// These are MCS rate lookup tables for VHT, one table per channel bandwidth.
// Each table is a multidimensional array, indexed by NSS, then MCS, and GI.
// For example, to lookup the rate for 20 MHz channel bandwidth, NSS 2, MCS 8, and short GI:
// uint8_t nss = 2;
// uint8_t mcs = 8;
// uint8_t gi = kShortGiIndex;
// uint32_t rate = VkHt20mhzRateLookup[nss][mcs][kShortGiIndex];
// VHT has 2 valid guard intervals.
const uint8_t kVhtGuardIntervalCount = 2;
// Like ht_mcs_entry_t, each vht_mcs_entry_t is indexed by guard interval.
using vht_mcs_entry_t = uint32_t[kVhtGuardIntervalCount];
// Some MCS indices are invalid, so these entries are filled with a placeholder.
#define VHT_INVALID_RATE 0
#define VHT_MCS_INVALID \
{ VHT_INVALID_RATE, VHT_INVALID_RATE }
// Valid MCS index range is 0-9.
const uint8_t kVhtMaxMcs = 9;
const uint8_t kVhtMaxMcsCount = 10;
using vht_per_nss_table_t = vht_mcs_entry_t[kVhtMaxMcsCount];
// The per-NSS tables have 1 extra entry for NSS = 0, which is invalid. The NSS=0 entry is just a
// placeholder to make it easy to use the actual NSS value as an index.
const uint8_t kVhtPerNssSize = 9;
using vht_per_bandwidth_table_t = vht_per_nss_table_t[kVhtPerNssSize];
const vht_per_bandwidth_table_t kVht20mhzRateLookup{
// NSS = 0 is invalid, so we have one empty entry.
{},
// IEEE 802.11-2016 Table 21-30. NSS = 1
{{6500, 7200},
{13000, 14400},
{19500, 21700},
{26000, 28900},
{39000, 43300},
{52000, 57800},
{58500, 65000},
{65000, 72200},
{78000, 86700},
VHT_MCS_INVALID},
// IEEE 802.11-2016 Table 21-31. NSS = 2
{{13000, 14400},
{26000, 28900},
{39000, 43300},
{52000, 57800},
{78000, 86700},
{104000, 115600},
{117000, 130000},
{130000, 144400},
{156000, 173300},
VHT_MCS_INVALID},
// IEEE 802.11-2016 Table 21-32. NSS = 3
{{19500, 21700},
{39000, 43300},
{58500, 65000},
{78000, 86700},
{117000, 130000},
{156000, 173300},
{175500, 195000},
{195000, 216700},
{234000, 260000},
{260000, 288900}},
// IEEE 802.11-2016 Table 21-33. NSS = 4
{{26000, 28900},
{52000, 57800},
{78000, 86700},
{104000, 115600},
{156000, 173300},
{208000, 231100},
{234000, 260000},
{260000, 288900},
{312000, 346700},
VHT_MCS_INVALID},
// IEEE 802.11-2016 Table 21-34. NSS = 5
{{32500, 36100},
{65000, 72200},
{97500, 108300},
{130000, 144400},
{195000, 216700},
{260000, 288900},
{292500, 325000},
{325000, 361100},
{390000, 433300},
VHT_MCS_INVALID},
// IEEE 802.11-2016 Table 21-35. NSS = 6
{{39000, 43300},
{78000, 86700},
{117000, 130000},
{156000, 173300},
{234000, 260000},
{312000, 346700},
{351000, 390000},
{390000, 433300},
{468000, 520000},
{520000, 577800}},
// IEEE 802.11-2016 Table 21-36. NSS = 7
{{45500, 50600},
{91000, 101100},
{136500, 151700},
{182000, 202200},
{273000, 303300},
{364000, 404400},
{409500, 455000},
{455000, 505600},
{546000, 606700},
VHT_MCS_INVALID},
// IEEE 802.11-2016 Table 21-37. NSS = 8
{{52000, 57800},
{104000, 115600},
{156000, 173300},
{208000, 231100},
{312000, 346700},
{416000, 462200},
{468000, 520000},
{520000, 577800},
{624000, 693300},
VHT_MCS_INVALID}};
const vht_per_bandwidth_table_t kVht40mhzRateLookup = {
// NSS = 0 is invalid, so we have one empty entry.
{},
// IEEE 802.11-2016 Table 21-38. NSS = 1
{{13500, 15000},
{27000, 30000},
{40500, 45000},
{54000, 60000},
{81000, 90000},
{108000, 120000},
{121500, 135000},
{135000, 150000},
{162000, 180000},
{180000, 200000}},
// IEEE 802.11-2016 Table 21-39. NSS = 2
{{27000, 30000},
{54000, 60000},
{81000, 90000},
{108000, 120000},
{162000, 180000},
{216000, 240000},
{243000, 270000},
{270000, 300000},
{324000, 360000},
{360000, 400000}},
// IEEE 802.11-2016 Table 21-40. NSS = 3
{{40500, 45000},
{81000, 90000},
{121500, 135000},
{162000, 180000},
{243000, 270000},
{324000, 360000},
{364500, 405000},
{405000, 450000},
{486000, 540000},
{540000, 600000}},
// IEEE 802.11-2016 Table 21-41. NSS = 4
{{54000, 60000},
{108000, 120000},
{162000, 180000},
{216000, 240000},
{324000, 360000},
{432000, 480000},
{486000, 540000},
{540000, 600000},
{648000, 720000},
{720000, 800000}},
// IEEE 802.11-2016 Table 21-42. NSS = 5
{{67500, 75000},
{135000, 150000},
{202500, 225000},
{270000, 300000},
{405000, 450000},
{540000, 600000},
{607500, 675000},
{675000, 750000},
{810000, 900000},
{900000, 1000000}},
// IEEE 802.11-2016 Table 21-43. NSS = 6
{{81000, 90000},
{162000, 180000},
{243000, 270000},
{324000, 360000},
{486000, 540000},
{648000, 720000},
{729000, 810000},
{810000, 900000},
{972000, 1080000},
{1080000, 1200000}},
// IEEE 802.11-2016 Table 21-44. NSS = 7
{{94500, 105000},
{189000, 210000},
{283500, 315000},
{378000, 420000},
{567000, 630000},
{756000, 840000},
{850500, 945000},
{945000, 1050000},
{1134000, 1260000},
{1260000, 1400000}},
// IEEE 802.11-2016 Table 21-45. NSS = 8
{{108000, 120000},
{216000, 240000},
{324000, 360000},
{432000, 480000},
{648000, 720000},
{864000, 960000},
{972000, 1080000},
{1080000, 1200000},
{1296000, 1440000},
{1440000, 1600000}},
};
const vht_per_bandwidth_table_t kVht80mhzRateLookup = {
// NSS = 0 is invalid, so we have one empty entry.
{},
// IEEE 802.11-2016 Table 21-46. NSS = 1.
{{29300, 32500},
{58500, 65000},
{87800, 97500},
{117000, 130000},
{175500, 195000},
{234000, 260000},
{263300, 292500},
{292500, 325000},
{351000, 390000},
{390000, 433300}},
// IEEE 802.11-2016 Table 21-47. NSS = 2
{{58500, 65000},
{117000, 130000},
{175500, 195000},
{234000, 260000},
{351000, 390000},
{468000, 520000},
{526500, 585000},
{585000, 650000},
{702000, 780000},
{780000, 866700}},
// IEEE 802.11-2016 Table 21-48. NSS = 3
{{87800, 97500},
{175500, 195000},
{263300, 292500},
{351000, 390000},
{526500, 585000},
{702000, 780000},
VHT_MCS_INVALID,
{877500, 975000},
{1053000, 1170000},
{1170000, 1300000}},
// IEEE 802.11-2016 Table 21-49. NSS = 4
{{117000, 130000},
{234000, 260000},
{351000, 390000},
{468000, 520000},
{702000, 780000},
{936000, 1040000},
{1053000, 1170000},
{1170000, 1300000},
{1404000, 1560000},
{1560000, 1733300}},
// IEEE 802.11-2016 Table 21-50. NSS = 5
{{146300, 162500},
{292500, 325000},
{438800, 487500},
{585000, 650000},
{877500, 975000},
{1170000, 1300000},
{1316300, 1462500},
{1462500, 1625000},
{1755000, 1950000},
{1950000, 2166700}},
// IEEE 802.11-2016 Table 21-51. NSS = 6
{{175500, 195000},
{351000, 390000},
{526500, 585000},
{702000, 780000},
{1053000, 1170000},
{1404000, 1560000},
{1579500, 1755000},
{1755000, 1950000},
{2106000, 2340000}},
// IEEE 802.11-2016 Table 21-52. NSS = 7
{{204800, 227500},
{409500, 455000},
{614300, 682500},
{819000, 910000},
{1228500, 1365000},
{1638000, 1820000},
VHT_MCS_INVALID,
{2047500, 2275000},
{2457000, 2730000},
{2730000, 3033300}},
// IEEE 802.11-2016 Table 21-53. NSS = 8
{{234000, 260000},
{468000, 520000},
{702000, 780000},
{936000, 1040000},
{1404000, 1560000},
{1872000, 2080000},
{2106000, 2340000},
{2340000, 2600000},
{2808000, 3120000},
{3120000, 3466700}}};
// Note this lookup table is also valid for channel bandwidth = 80+80 MHz.
const vht_per_bandwidth_table_t kVht160mhzRateLookup = {
// NSS = 0 is invalid, so we have one empty entry.
{},
// IEEE 802.11-2016 Table 21-54. NSS = 1
{{58500, 65000},
{117000, 130000},
{175500, 195000},
{234000, 260000},
{351000, 390000},
{468000, 520000},
{526500, 585000},
{585000, 650000},
{702000, 780000},
{780000, 866700}},
// IEEE 802.11-2016 Table 21-55. NSS = 2
{{117000, 130000},
{234000, 260000},
{351000, 390000},
{468000, 520000},
{702000, 780000},
{936000, 1040000},
{1053000, 1170000},
{1170000, 1300000},
{1404000, 1560000},
{1560000, 1733300}},
// IEEE 802.11-2016 Table 21-56. NSS = 3
{{175500, 195000},
{351000, 390000},
{526500, 585000},
{702000, 780000},
{1053000, 1170000},
{1404000, 1560000},
{1579500, 1755000},
{1755000, 1950000},
{2106000, 2340000},
VHT_MCS_INVALID},
// IEEE 802.11-2016 Table 21-57. NSS = 4
{{234000, 260000},
{468000, 520000},
{702000, 780000},
{936000, 1040000},
{1404000, 1560000},
{1872000, 2080000},
{2106000, 2340000},
{2340000, 2600000},
{2808000, 3120000},
{3120000, 3466700}},
// IEEE 802.11-2016 Table 21-58. NSS = 5
{{292500, 325000},
{585000, 650000},
{877500, 975000},
{1170000, 1300000},
{1755000, 1950000},
{2340000, 2600000},
{2632500, 2925000},
{2925000, 3250000},
{3510000, 3900000},
{3900000, 4333300}},
// IEEE 802.11-2016 Table 21-59. NSS = 6
{{351000, 390000},
{702000, 780000},
{1053000, 1170000},
{1404000, 1560000},
{2106000, 2340000},
{2808000, 3120000},
{3159000, 3510000},
{3510000, 3900000},
{4212000, 4680000},
{4680000, 5200000}},
// IEEE 802.11-2016 Table 21-60. NSS = 7
{{409500, 455000},
{819000, 910000},
{1228500, 1365000},
{1638000, 1820000},
{2457000, 2730000},
{3276000, 3640000},
{3685500, 4095000},
{4095000, 4550000},
{4914000, 5460000},
{5460000, 6066700}},
// IEEE 802.11-2016 Table 21-61. NSS = 8
{{468000, 520000},
{936000, 1040000},
{1404000, 1560000},
{1872000, 2080000},
{2808000, 3120000},
{3744000, 4160000},
{4212000, 4680000},
{4680000, 5200000},
{5616000, 6240000},
{6240000, 6933300}}};
zx_status_t ValidateHtLookupRequestBounds(const ::fuchsia::wlan::common::CBW& cbw, uint8_t mcs,
const ::fuchsia::wlan::common::GuardInterval& gi) {
auto status = ZX_OK;
if (mcs >= kHtMaxMcsCount) {
status = ZX_ERR_OUT_OF_RANGE;
errorf("Invalid HT MCS index %d (%s)\n", mcs, zx_status_get_string(status));
return status;
}
// HT PHY channel bandwidth must be either 20 MHz or 40 MHz, see IEEE 802.11 19.1.1.
if (cbw != ::fuchsia::wlan::common::CBW::CBW20 && cbw != ::fuchsia::wlan::common::CBW::CBW40) {
status = ZX_ERR_OUT_OF_RANGE;
errorf("Invalid HT channel bandwidth (%s)\n", zx_status_get_string(status));
return status;
}
// HT PHY guard intervals must be either short or long, see IEEE 802.11-2016 19.1.1.
if (gi != ::fuchsia::wlan::common::GuardInterval::SHORT_GI &&
gi != ::fuchsia::wlan::common::GuardInterval::LONG_GI) {
status = ZX_ERR_OUT_OF_RANGE;
errorf("Invalid HT guard interval (%s)\n", zx_status_get_string(status));
return status;
}
// Valid MCS indices are specified in IEEE 802.11-2016 19.5.
// The MCS table for each channel bandwidth has MCS indices 0-76, which may include entries
// for invalid combinations of parameters. Here we are just checking bounds.
if (mcs > kHtMaxMcs) {
errorf("Invalid HT MCS index %d (%s)\n", mcs, zx_status_get_string(status));
return status;
}
return ZX_OK;
}
zx_status_t ValidateVhtLookupRequestBounds(const ::fuchsia::wlan::common::CBW& cbw, uint8_t mcs,
const ::fuchsia::wlan::common::GuardInterval& gi,
uint8_t nss) {
auto status = ZX_OK;
if (nss < 1 || nss > 8) {
status = ZX_ERR_OUT_OF_RANGE;
errorf("Invalid VHT NSS: %d (%s)", nss, zx_status_get_string(status));
return status;
}
// VHT PHY guard intervals must be either short or long, see IEEE 802.11-2016 21.1.1.
if (gi != ::fuchsia::wlan::common::GuardInterval::SHORT_GI &&
gi != ::fuchsia::wlan::common::GuardInterval::LONG_GI) {
status = ZX_ERR_OUT_OF_RANGE;
errorf("Invalid VHT guard interval (%s)\n", zx_status_get_string(status));
return status;
}
// Valid MCS indices are specified in IEEE 802.11-2016 21.5.
// The MCS table for each channel bandwidth has MCS indices 0-9, which may include entries
// for invalid combinations of parameters. Here we are just checking bounds.
if (mcs > kVhtMaxMcs) {
status = ZX_ERR_OUT_OF_RANGE;
errorf("Invalid VHT MCS index %d (%s)\n", mcs, zx_status_get_string(status));
return status;
}
return ZX_OK;
}
// Translate the GI enum value into an index into the lookup table.
zx_status_t GiEnumToIndex(::fuchsia::wlan::common::GuardInterval gi, uint8_t* gi_index) {
if (gi == ::fuchsia::wlan::common::GuardInterval::LONG_GI) {
*gi_index = kLongGiIndex;
} else if (gi == ::fuchsia::wlan::common::GuardInterval::SHORT_GI) {
*gi_index = kShortGiIndex;
} else {
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
} // namespace
zx_status_t HtDataRateLookup(const ::fuchsia::wlan::common::CBW& cbw, uint8_t mcs,
const ::fuchsia::wlan::common::GuardInterval& gi, uint32_t* out_kbps) {
auto status = ValidateHtLookupRequestBounds(cbw, mcs, gi);
if (status != ZX_OK) {
return status;
}
uint8_t gi_index;
status = GiEnumToIndex(gi, &gi_index);
if (status != ZX_OK) {
return status;
}
uint32_t rate_kbps = HT_INVALID_RATE;
switch (cbw) {
case ::fuchsia::wlan::common::CBW::CBW20:
rate_kbps = kHt20mhzRateLookup[mcs][gi_index];
break;
case ::fuchsia::wlan::common::CBW::CBW40:
rate_kbps = kHt40mhzRateLookup[mcs][gi_index];
break;
default:
// The validation above should have prevented failed lookups.
errorf("Invalid HT channel bandwidth value: %d\n", cbw);
return (ZX_ERR_INTERNAL);
}
// Check for invalid rates, which exist for some combinations of parameters.
if (rate_kbps == HT_INVALID_RATE) {
errorf("Invalid HT MCS index %d (%s)\n", mcs, zx_status_get_string(status));
status = ZX_ERR_OUT_OF_RANGE;
} else {
*out_kbps = rate_kbps;
}
return status;
}
zx_status_t VhtDataRateLookup(const ::fuchsia::wlan::common::CBW& cbw, uint8_t mcs,
const ::fuchsia::wlan::common::GuardInterval& gi, uint8_t num_sts,
uint8_t stbc, uint32_t* out_kbps) {
// IEEE 802.11-2016 8.3.4.4 defines this formula for finding the VHT NSS.
const uint8_t nss = num_sts / (stbc + 1);
return VhtDataRateLookup(cbw, mcs, gi, nss, out_kbps);
}
zx_status_t VhtDataRateLookup(const ::fuchsia::wlan::common::CBW& cbw, uint8_t mcs,
const ::fuchsia::wlan::common::GuardInterval& gi, uint8_t nss,
uint32_t* out_kbps) {
auto status = ValidateVhtLookupRequestBounds(cbw, mcs, gi, nss);
if (status != ZX_OK) {
return status;
}
uint8_t gi_index;
status = GiEnumToIndex(gi, &gi_index);
if (status != ZX_OK) {
return status;
}
uint32_t rate_kbps = VHT_INVALID_RATE;
switch (cbw) {
case ::fuchsia::wlan::common::CBW::CBW20:
rate_kbps = kVht20mhzRateLookup[nss][mcs][gi_index];
break;
case ::fuchsia::wlan::common::CBW::CBW40:
rate_kbps = kVht40mhzRateLookup[nss][mcs][gi_index];
break;
case ::fuchsia::wlan::common::CBW::CBW80:
rate_kbps = kVht80mhzRateLookup[nss][mcs][gi_index];
break;
case ::fuchsia::wlan::common::CBW::CBW80P80:
__FALLTHROUGH; // 80+80 uses the same lookup table as 160 MHz.
case ::fuchsia::wlan::common::CBW::CBW160:
rate_kbps = kVht160mhzRateLookup[nss][mcs][gi_index];
break;
default:
// The validation above should have prevented failed lookups.
errorf("Invalid VHT channel bandwidth value: %d\n", cbw);
return (ZX_ERR_INTERNAL);
}
// Check for invalid rates, which exist for some combinations of parameters.
if (rate_kbps == VHT_INVALID_RATE) {
errorf("Invalid VHT MCS index %d (%s)\n", mcs, zx_status_get_string(status));
status = ZX_ERR_OUT_OF_RANGE;
} else {
*out_kbps = rate_kbps;
}
return status;
}
} // namespace wlan::common