/* * Common [OS-independent] rate management * 802.11 Networking Adapter Device Driver. * * Broadcom Proprietary and Confidential. Copyright (C) 2020, * All Rights Reserved. * * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom; * the contents of this file may not be disclosed to third parties, * copied or duplicated in any form, in whole or in part, without * the prior written permission of Broadcom. * * * <> */ #include #ifdef BCMDRIVER #include #else #include #ifndef ASSERT #define ASSERT(e) assert(e) #endif #ifndef ASSERT_FP #define ASSERT_FP(e) assert(e) #endif #endif /* BCMDRIVER */ #include <802.11.h> #include <802.11ax.h> #include #include #include /* TODO: Consolidate rate utility functions from wlc_rate.c and bcmwifi_monitor.c * into here if they're shared by non wl layer as well... */ /* ============================================ */ /* Moved from wlc_rate.c */ /* ============================================ */ /* HE mcs info */ struct ieee_80211_mcs_rate_info { uint8 constellation_bits; uint8 coding_q; uint8 coding_d; uint8 dcm_capable; /* 1 if dcm capable */ }; static const struct ieee_80211_mcs_rate_info wlc_mcs_info[] = { { 1, 1, 2, 1 }, /* MCS 0: MOD: BPSK, CR 1/2, dcm capable */ { 2, 1, 2, 1 }, /* MCS 1: MOD: QPSK, CR 1/2, dcm capable */ { 2, 3, 4, 0 }, /* MCS 2: MOD: QPSK, CR 3/4, NOT dcm capable */ { 4, 1, 2, 1 }, /* MCS 3: MOD: 16QAM, CR 1/2, dcm capable */ { 4, 3, 4, 1 }, /* MCS 4: MOD: 16QAM, CR 3/4, dcm capable */ { 6, 2, 3, 0 }, /* MCS 5: MOD: 64QAM, CR 2/3, NOT dcm capable */ { 6, 3, 4, 0 }, /* MCS 6: MOD: 64QAM, CR 3/4, NOT dcm capable */ { 6, 5, 6, 0 }, /* MCS 7: MOD: 64QAM, CR 5/6, NOT dcm capable */ { 8, 3, 4, 0 }, /* MCS 8: MOD: 256QAM, CR 3/4, NOT dcm capable */ { 8, 5, 6, 0 }, /* MCS 9: MOD: 256QAM, CR 5/6, NOT dcm capable */ { 10, 3, 4, 0 }, /* MCS 10: MOD: 1024QAM, CR 3/4, NOT dcm capable */ { 10, 5, 6, 0 }, /* MCS 11: MOD: 1024QAM, CR 5/6, NOT dcm capable */ #ifdef WL11BE /* TODO: for now EHT shares this table with HE, * create a new table if needed once we know more * about EHT rate calculation... */ { 12, 3, 4, 0 }, /* MCS 12: MOD: 4096QAM, CR 3/4, NOT dcm capable */ { 12, 5, 6, 0 }, /* MCS 13: MOD: 4096QAM, CR 5/6, NOT dcm capable */ #endif }; /* Nsd values Draft0.4 Table 26.63 onwards */ static const uint wlc_he_nsd[] = { 234, /* BW20 */ 468, /* BW40 */ 980, /* BW80 */ 1960, /* BW160 */ #ifdef WL11BE /* TODO: for now EHT shares this table with HE, * create a new table if needed once we know more * about EHT rate calculation... */ 2940, /* BW240 */ 3920 /* BW320 */ #endif }; /* Nsd values Draft3.3 Table 28-15 */ static const uint wlc_he_ru_nsd[] = { 24, /* 26T */ 48, /* 52T */ 102, /* 106T */ 234, /* 242T/BW20 */ 468, /* 484T/BW40 */ 980, /* 996T/BW80 */ 1960, /* 2*996T/BW160 */ #ifdef WL11BE /* TODO: for now EHT shares this table with HE, * create a new table if needed once we know more * about EHT rate calculation... */ 2940, /* 3*996T/BW240 */ 3920 /* 4*996T/BW320 */ #endif }; #define HE_RU_TO_NSD(ru_idx) \ (ru_idx < ARRAYSIZE(wlc_he_ru_nsd)) ? \ wlc_he_ru_nsd[ru_idx] : 0 /* sym_len = 12.8 us. For calculation purpose, *10 */ #define HE_SYM_LEN_FACTOR (128) /* GI values = 0.8 , 1.6 or 3.2 us. For calculation purpose, *10 */ #define HE_GI_800us_FACTOR (8) #define HE_GI_1600us_FACTOR (16) #define HE_GI_3200us_FACTOR (32) /* To avoid ROM invalidation use the old macro as is... */ #ifdef WL11BE #define HE_BW_TO_NSD(bwi) \ ((bwi) > 0u && (bwi) <= ARRAYSIZE(wlc_he_nsd)) ? \ wlc_he_nsd[(bwi) - 1u] : 0u #else #define HE_BW_TO_NSD(bwi) \ ((bwi) > 0 && ((bwi) << WL_RSPEC_BW_SHIFT) <= WL_RSPEC_BW_160MHZ) ? \ wlc_he_nsd[(bwi)-1] : 0 #endif /* WL11BE */ #define ksps 250 /* kilo symbols per sec, 4 us sym */ #ifdef WL11BE /* Table "wlc_nsd" is derived from HT and VHT #defines below, but extended for HE * for rate calculation purpose at a given NSS and bandwidth combination. * * It should and can only be used in where it wants to know the relative rate in kbps * for a different NSS and bandwidth combination at a given mcs e.g. in fallback rate * search. It shouldn not and can not be used in where it calculates the absolute rate * i.e. the result doesn't agree with what the spec says otherwise. * * See Std 802.11-2016 "Table 21-61 VHT-MCSs for optional 160 MHz and 80+80 MHz, NSS = 8" * for VHT, and P802.11ax/D6.0 "Table 27-111 HE-MCSs for 2x996-tone RU, NSS = 8" for HE, * for 160Mhz bandwidth for resulting rate comparison. * * It's again extended for EHT 240/320Mhz bandwidth, for the same purpose. */ static const uint16 wlc_nsd[] = { 52, /* 20MHz */ 108, /* 40MHz */ 234, /* 80Mhz */ 468, /* 160MHz */ 702, /* 240MHz */ 936, /* 320MHz */ }; #define BW_TO_NSD(bwi) \ ((bwi) > 0u && (bwi) <= ARRAYSIZE(wlc_nsd)) ? \ wlc_nsd[(bwi) - 1u] : 0u static uint wf_nsd2ndbps(uint mcs, uint nss, uint nsd, bool dcm) { uint Ndbps; /* multiply number of spatial streams, * bits per number from the constellation, * and coding quotient */ Ndbps = nsd * nss * wlc_mcs_info[mcs].coding_q * wlc_mcs_info[mcs].constellation_bits; /* adjust for the coding rate divisor */ Ndbps = Ndbps / wlc_mcs_info[mcs].coding_d; /* take care of dcm: dcm divides R by 2. If not dcm mcs, ignore */ if (dcm) { if (wlc_mcs_info[mcs].dcm_capable) { Ndbps >>= 1u; } } return Ndbps; } #else /* for HT and VHT? */ #define Nsd_20MHz 52 #define Nsd_40MHz 108 #define Nsd_80MHz 234 #define Nsd_160MHz 468 #endif /* WL11BE */ uint wf_he_mcs_to_Ndbps(uint mcs, uint nss, uint bw, bool dcm) { uint Nsd; uint Ndbps; /* find the number of complex numbers per symbol */ Nsd = HE_BW_TO_NSD(bw >> WL_RSPEC_BW_SHIFT); #ifdef WL11BE Ndbps = wf_nsd2ndbps(mcs, nss, Nsd, dcm); #else /* multiply number of spatial streams, * bits per number from the constellation, * and coding quotient */ Ndbps = Nsd * nss * wlc_mcs_info[mcs].coding_q * wlc_mcs_info[mcs].constellation_bits; /* adjust for the coding rate divisor */ Ndbps = Ndbps / wlc_mcs_info[mcs].coding_d; /* take care of dcm: dcm divides R by 2. If not dcm mcs, ignore */ if (dcm) { if (wlc_mcs_info[mcs].dcm_capable) { Ndbps >>= 1; } } #endif /* WL11BE */ return Ndbps; } uint32 wf_he_mcs_ru_to_ndbps(uint8 mcs, uint8 nss, bool dcm, uint8 ru_index) { uint32 nsd; uint32 ndbps; /* find the number of complex numbers per symbol */ nsd = HE_RU_TO_NSD(ru_index); #ifdef WL11BE ndbps = wf_nsd2ndbps(mcs, nss, nsd, dcm); #else /* multiply number of spatial streams, * bits per number from the constellation, * and coding quotient * Ndbps = Nss x Nsd x (Nbpscs x R) x (DCM/2) */ ndbps = nsd * nss * wlc_mcs_info[mcs].coding_q * wlc_mcs_info[mcs].constellation_bits; /* adjust for the coding rate divisor */ ndbps = ndbps / wlc_mcs_info[mcs].coding_d; /* take care of dcm: dcm divides R by 2. If not dcm mcs, ignore */ if (dcm && wlc_mcs_info[mcs].dcm_capable) { ndbps >>= 1; } #endif /* WL11BE */ return ndbps; } /** * Returns the rate in [Kbps] units for a caller supplied MCS/bandwidth/Nss/Sgi/dcm combination. * 'mcs' : a *single* spatial stream MCS (11ax) * formula as per http: * WLAN&preview=/323036249/344457953/11ax_rate_table.xlsx * Symbol length = 12.8 usec [given as sym_len/10 below] * GI value = 0.8 or 1.6 or 3.2 usec [given as GI_value/10 below] * rate (Kbps) = (Nsd * Nbpscs * nss * (coding_q/coding_d) * 1000) / ((sym_len/10) + (GI_value/10)) * Note that, for calculation purpose, following is used. [to be careful with overflows] * rate (Kbps) = (Nsd * Nbpscs * nss * (coding_q/coding_d) * 1000) / ((sym_len + GI_value) / 10) * rate (Kbps) = (Nsd * Nbpscs * nss * (coding_q/coding_d) * 1000) / (sym_len + GI_value) * 10 */ uint wf_he_mcs_to_rate(uint mcs, uint nss, uint bw, uint gi, bool dcm) { uint rate; uint rate_deno; rate = HE_BW_TO_NSD(bw >> WL_RSPEC_BW_SHIFT); #ifdef WL11BE rate = wf_nsd2ndbps(mcs, nss, rate, dcm); #else /* Nbpscs: multiply by bits per number from the constellation in use */ rate = rate * wlc_mcs_info[mcs].constellation_bits; /* Nss: adjust for the number of spatial streams */ rate = rate * nss; /* R: adjust for the coding rate given as a quotient and divisor */ rate = (rate * wlc_mcs_info[mcs].coding_q) / wlc_mcs_info[mcs].coding_d; /* take care of dcm: dcm divides R by 2. If not dcm mcs, ignore */ if (dcm) { if (wlc_mcs_info[mcs].dcm_capable) { rate >>= 1; } } #endif /* WL11BE */ /* add sym len factor */ rate_deno = HE_SYM_LEN_FACTOR; /* get GI for denominator */ if (HE_IS_GI_3_2us(gi)) { rate_deno += HE_GI_3200us_FACTOR; } else if (HE_IS_GI_1_6us(gi)) { rate_deno += HE_GI_1600us_FACTOR; } else { /* assuming HE_GI_0_8us */ rate_deno += HE_GI_800us_FACTOR; } /* as per above formula */ rate *= 1000; /* factor of 10. *100 to accommodate 2 places */ rate /= rate_deno; rate *= 10; /* *100 was already done above. Splitting is done to avoid overflow. */ return rate; } uint wf_mcs_to_Ndbps(uint mcs, uint nss, uint bw) { uint Nsd; uint Ndbps; /* This calculation works for 11n HT and 11ac VHT if the HT mcs values * are decomposed into a base MCS = MCS % 8, and Nss = 1 + MCS / 8. * That is, HT MCS 23 is a base MCS = 7, Nss = 3 */ /* find the number of complex numbers per symbol */ #ifdef WL11BE Nsd = BW_TO_NSD(bw >> WL_RSPEC_BW_SHIFT); Ndbps = wf_nsd2ndbps(mcs, nss, Nsd, FALSE); #else if (bw == WL_RSPEC_BW_20MHZ) { Nsd = Nsd_20MHz; } else if (bw == WL_RSPEC_BW_40MHZ) { Nsd = Nsd_40MHz; } else if (bw == WL_RSPEC_BW_80MHZ) { Nsd = Nsd_80MHz; } else if (bw == WL_RSPEC_BW_160MHZ) { Nsd = Nsd_160MHz; } else { Nsd = 0; } /* multiply number of spatial streams, * bits per number from the constellation, * and coding quotient */ Ndbps = Nsd * nss * wlc_mcs_info[mcs].coding_q * wlc_mcs_info[mcs].constellation_bits; /* adjust for the coding rate divisor */ Ndbps = Ndbps / wlc_mcs_info[mcs].coding_d; #endif /* WL11BE */ return Ndbps; } /** * Returns the rate in [Kbps] units for a caller supplied MCS/bandwidth/Nss/Sgi combination. * 'mcs' : a *single* spatial stream MCS (11n or 11ac) */ uint wf_mcs_to_rate(uint mcs, uint nss, uint bw, int sgi) { uint rate; if (mcs == 32) { /* just return fixed values for mcs32 instead of trying to parametrize */ rate = (sgi == 0) ? 6000 : 6778; } else { /* This calculation works for 11n HT, 11ac VHT and 11ax HE if the HT mcs values * are decomposed into a base MCS = MCS % 8, and Nss = 1 + MCS / 8. * That is, HT MCS 23 is a base MCS = 7, Nss = 3 */ #if defined(WLPROPRIETARY_11N_RATES) switch (mcs) { case 87: mcs = 8; /* MCS 8: MOD: 256QAM, CR 3/4 */ break; case 88: mcs = 9; /* MCS 9: MOD: 256QAM, CR 5/6 */ break; default: break; } #endif /* WLPROPRIETARY_11N_RATES */ #ifdef WL11BE rate = wf_mcs_to_Ndbps(mcs, nss, bw); #else /* find the number of complex numbers per symbol */ if (RSPEC_IS20MHZ(bw)) { /* 4360 TODO: eliminate Phy const in rspec bw, then just compare * as in 80 and 160 case below instead of RSPEC_IS20MHZ(bw) */ rate = Nsd_20MHz; } else if (RSPEC_IS40MHZ(bw)) { /* 4360 TODO: eliminate Phy const in rspec bw, then just compare * as in 80 and 160 case below instead of RSPEC_IS40MHZ(bw) */ rate = Nsd_40MHz; } else if (bw == WL_RSPEC_BW_80MHZ) { rate = Nsd_80MHz; } else if (bw == WL_RSPEC_BW_160MHZ) { rate = Nsd_160MHz; } else { rate = 0; } /* multiply by bits per number from the constellation in use */ rate = rate * wlc_mcs_info[mcs].constellation_bits; /* adjust for the number of spatial streams */ rate = rate * nss; /* adjust for the coding rate given as a quotient and divisor */ rate = (rate * wlc_mcs_info[mcs].coding_q) / wlc_mcs_info[mcs].coding_d; #endif /* WL11BE */ /* multiply by Kilo symbols per sec to get Kbps */ rate = rate * ksps; /* adjust the symbols per sec for SGI * symbol duration is 4 us without SGI, and 3.6 us with SGI, * so ratio is 10 / 9 */ if (sgi) { /* add 4 for rounding of division by 9 */ rate = ((rate * 10) + 4) / 9; } } return rate; } /* wf_mcs_to_rate */ /* This function needs update to handle MU frame PLCP as well (MCS is conveyed via VHT-SIGB * field in case of MU frames). Currently this support needs to be added in uCode to communicate * MCS information for an MU frame * * For VHT frame: * bit 0-3 mcs index * bit 6-4 nsts for VHT * bit 7: 1 for VHT * Note: bit 7 is used to indicate to the rate sel the mcs is a non HT mcs! * * Essentially it's the NSS:MCS portions of the rspec */ uint8 wf_vht_plcp_to_rate(uint8 *plcp) { uint8 rate, gid; uint nss; uint32 plcp0 = plcp[0] + (plcp[1] << 8); /* don't need plcp[2] */ gid = (plcp0 & VHT_SIGA1_GID_MASK) >> VHT_SIGA1_GID_SHIFT; if (gid > VHT_SIGA1_GID_TO_AP && gid < VHT_SIGA1_GID_NOT_TO_AP) { /* for MU packet we hacked Signal Tail field in VHT-SIG-A2 to save nss and mcs, * copy from murate in d11 rx header. * nss = bit 18:19 (for 11ac 2 bits to indicate maximum 4 nss) * mcs = 20:23 */ rate = (plcp[5] & 0xF0) >> 4; nss = ((plcp[5] & 0x0C) >> 2) + 1; } else { rate = (plcp[3] >> VHT_SIGA2_MCS_SHIFT); nss = ((plcp0 & VHT_SIGA1_NSTS_SHIFT_MASK_USER0) >> VHT_SIGA1_NSTS_SHIFT) + 1; if (plcp0 & VHT_SIGA1_STBC) nss = nss >> 1; } rate |= ((nss << WL_RSPEC_VHT_NSS_SHIFT) | WF_NON_HT_MCS); return rate; } /** * Function for computing NSS:MCS from HE SU PLCP or * MCS:LTF-GI from HE MU PLCP * * based on rev3.10 : * https://docs.google.com/spreadsheets/d/ * 1eP6ZCRrtnF924ds1R-XmbcH0IdQ0WNJpS1-FHmWeb9g/edit#gid=1492656555 * * For HE SU frame: * bit 0-3 mcs index * bit 6-4 nsts for HE * bit 7: 1 for HE * Note: bit 7 is used to indicate to the rate sel the mcs is a non HT mcs! * Essentially it's the NSS:MCS portions of the rspec * * For HE MU frame: * bit 0-3 mcs index * bit 4-5 LTF-GI value * bit 6 STBC * Essentially it's the MCS and LTF-GI portion of the rspec */ /* Macros to be used for calculating rate from PLCP */ #define HE_SU_PLCP2RATE_MCS_MASK 0x0F #define HE_SU_PLCP2RATE_MCS_SHIFT 0 #define HE_SU_PLCP2RATE_NSS_MASK 0x70 #define HE_SU_PLCP2RATE_NSS_SHIFT 4 #define HE_MU_PLCP2RATE_LTF_GI_MASK 0x30 #define HE_MU_PLCP2RATE_LTF_GI_SHIFT 4 #define HE_MU_PLCP2RATE_STBC_MASK 0x40 #define HE_MU_PLCP2RATE_STBC_SHIFT 6 uint8 wf_he_plcp_to_rate(uint8 *plcp, bool is_mu) { uint8 rate = 0; uint8 nss = 0; uint32 plcp0 = 0; uint32 plcp1 = 0; uint8 he_ltf_gi; uint8 stbc; ASSERT(plcp); BCM_REFERENCE(nss); BCM_REFERENCE(he_ltf_gi); plcp0 = ((plcp[3] << 24) | (plcp[2] << 16) | (plcp[1] << 8) | plcp[0]); plcp1 = ((plcp[5] << 8) | plcp[4]); if (!is_mu) { /* For SU frames return rate in MCS:NSS format */ rate = ((plcp0 & HE_SU_RE_SIGA_MCS_MASK) >> HE_SU_RE_SIGA_MCS_SHIFT); nss = ((plcp0 & HE_SU_RE_SIGA_NSTS_MASK) >> HE_SU_RE_SIGA_NSTS_SHIFT) + 1; rate |= ((nss << HE_SU_PLCP2RATE_NSS_SHIFT) | WF_NON_HT_MCS); } else { /* For MU frames return rate in MCS:LTF-GI format */ rate = (plcp0 & HE_MU_SIGA_SIGB_MCS_MASK) >> HE_MU_SIGA_SIGB_MCS_SHIFT; he_ltf_gi = (plcp0 & HE_MU_SIGA_GI_LTF_MASK) >> HE_MU_SIGA_GI_LTF_SHIFT; stbc = (plcp1 & HE_MU_SIGA_STBC_MASK) >> HE_MU_SIGA_STBC_SHIFT; /* LTF-GI shall take the same position as NSS */ rate |= (he_ltf_gi << HE_MU_PLCP2RATE_LTF_GI_SHIFT); /* STBC needs to be filled in bit 6 */ rate |= (stbc << HE_MU_PLCP2RATE_STBC_SHIFT); } return rate; } /** * Function for computing NSS:MCS from EHT SU PLCP or * MCS:LTF-GI from EHT MU PLCP * * TODO: add link to the HW spec. * FIXME: do we really need to support mu? */ uint8 wf_eht_plcp_to_rate(uint8 *plcp, bool is_mu) { BCM_REFERENCE(plcp); BCM_REFERENCE(is_mu); ASSERT(!"wf_eht_plcp_to_rate: not implemented!"); return 0; } /* ============================================ */ /* Moved from wlc_rate_def.c */ /* ============================================ */ /** * Some functions require a single stream MCS as an input parameter. Given an MCS, this function * returns the single spatial stream MCS equivalent. */ uint8 wf_get_single_stream_mcs(uint mcs) { if (mcs < 32) { return mcs % 8; } switch (mcs) { case 32: return 32; case 87: case 99: case 101: return 87; /* MCS 87: SS 1, MOD: 256QAM, CR 3/4 */ default: return 88; /* MCS 88: SS 1, MOD: 256QAM, CR 5/6 */ } } /* ============================================ */ /* Moved from wlc_phy_iovar.c */ /* ============================================ */ const uint8 plcp_ofdm_rate_tbl[] = { DOT11_RATE_48M, /* 8: 48Mbps */ DOT11_RATE_24M, /* 9: 24Mbps */ DOT11_RATE_12M, /* A: 12Mbps */ DOT11_RATE_6M, /* B: 6Mbps */ DOT11_RATE_54M, /* C: 54Mbps */ DOT11_RATE_36M, /* D: 36Mbps */ DOT11_RATE_18M, /* E: 18Mbps */ DOT11_RATE_9M /* F: 9Mbps */ };