| /****************************************************************************** |
| * |
| * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. |
| * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH |
| * Copyright(c) 2016 - 2017 Intel Deutschland GmbH |
| * Copyright(c) 2018 Intel Corporation |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name Intel Corporation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| *****************************************************************************/ |
| #include "_rateScaleMng.h" |
| |
| #define BOOLEAN bool |
| |
| #define SHIFT_AND_MASK(val, mask, pos) (((val) >> (pos)) & ((mask) >> (pos))) |
| #define SEC_TO_USEC(x) ((x)*USEC_PER_SEC) |
| #define MSEC_TO_USEC(x) ((x)*USEC_PER_MSEC) |
| #define _memclr(p, s) (memset((p), 0, (s))) |
| #define DBG_PRINTF(...) |
| #define NON_SHARED_ANT_RFIC_ID (staInfo->mvm->cfg->non_shared_ant == ANT_A ? 0 : 1) |
| #define BT_COEX_SHARED_ANT_ID (staInfo->mvm->cfg->non_shared_ant ^ ANT_AB) |
| #define PWR_IS_SLEEP_ALLOWED (!staInfo->mvm->ps_disabled) |
| |
| #define MSB2ORD msb2ord |
| #define LSB2ORD lsb2ord |
| |
| static inline unsigned long msb2ord(unsigned long x) { |
| return find_last_bit(&x, BITS_PER_LONG); |
| } |
| |
| static inline unsigned long lsb2ord(unsigned long x) { |
| return find_first_bit(&x, BITS_PER_LONG); |
| } |
| |
| // TODO - move to coex.c |
| static bool btCoexManagerIsAntAvailable(struct iwl_mvm* mvm, uint8_t ant) { |
| if (mvm->cfg->bt_shared_single_ant) { return true; } |
| |
| if (!(ant & ~mvm->cfg->non_shared_ant)) { return true; } |
| |
| return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; |
| } |
| |
| static bool btCoexManagerBtOwnsAnt(struct iwl_mvm* mvm) { |
| return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) >= BT_HIGH_TRAFFIC; |
| } |
| |
| typedef struct _TLC_STAT_COMMON_API_S { |
| /* @brief number of packets sent |
| * txed[0] - rate index 0 |
| * txed[1] - rate index != 0 |
| */ |
| U16 txed[2]; |
| /** |
| * @brief number of packets we got acknowledgment for packet sent |
| * acked[0] - rate index 0 |
| * acked[1] - rate index != 0 |
| */ |
| U16 acked[2]; |
| /* Number of frames we got BA response for (regardless of success) */ |
| U16 trafficLoad; |
| U16 baTxed; |
| U16 baAcked; |
| } TLC_STAT_COMMON_API_S; |
| |
| static const U08 RS_NON_HT_RATE_TO_API_RATE[] = { |
| [RS_NON_HT_RATE_CCK_1M] = R_1M, [RS_NON_HT_RATE_CCK_2M] = R_2M, |
| [RS_NON_HT_RATE_CCK_5_5M] = R_5_5M, [RS_NON_HT_RATE_CCK_11M] = R_11M, |
| [RS_NON_HT_RATE_OFDM_6M] = R_6M, [RS_NON_HT_RATE_OFDM_9M] = R_9M, |
| [RS_NON_HT_RATE_OFDM_12M] = R_12M, [RS_NON_HT_RATE_OFDM_18M] = R_18M, |
| [RS_NON_HT_RATE_OFDM_24M] = R_24M, [RS_NON_HT_RATE_OFDM_36M] = R_36M, |
| [RS_NON_HT_RATE_OFDM_48M] = R_48M, [RS_NON_HT_RATE_OFDM_54M] = R_54M, |
| }; |
| |
| // This array converts VHT rate configuration to phy rate |
| // See ieee80211-2016 spec, section 21.4 tabled for reference values |
| // Note the values are in bps, instead of mbps (i.e. multiplied by 2^20) |
| // The table access is rsMngVhtRateToBps[BW][MSC]. |
| static const U32 rsMngVhtRateToBps[][10] = { |
| [CHANNEL_WIDTH20] = {6815744, 13631488, 20447232, 27262976, 40894464, 54525952, 61341696, |
| 68157440, 81788928, 90701824}, |
| [CHANNEL_WIDTH40] = {14155776, 28311552, 42467328, 56623104, 84934656, 113246208, 127401984, |
| 141557760, 169869312, 188743680}, |
| [CHANNEL_WIDTH80] = {30723277 /*rounded*/, 61341696, 92064973 /*rounded*/, 122683392, 184025088, |
| 245366784, 276090061 /*rounded*/, 306708480, 368050176, 408944640}, |
| [CHANNEL_WIDTH160] = {61341696, 122683392, 184025088, 245366784, 368050176, 490733568, |
| 552075264, 613416960, 736100352, 817889280}, |
| }; |
| |
| // The array cell index is the MCS. e.g. - cell 0 - MCS0 6M. etc. |
| static const U08 downColMcsToLegacy[] = { |
| [RS_MCS_0_HE_ER_AND_DCM] = RS_NON_HT_RATE_OFDM_6M, |
| [RS_MCS_0] = RS_NON_HT_RATE_OFDM_6M, |
| [RS_MCS_1] = RS_NON_HT_RATE_OFDM_12M, |
| [RS_MCS_2] = RS_NON_HT_RATE_OFDM_18M, |
| [RS_MCS_3] = RS_NON_HT_RATE_OFDM_24M, |
| [RS_MCS_4] = RS_NON_HT_RATE_OFDM_36M, |
| [RS_MCS_5] = RS_NON_HT_RATE_OFDM_48M, |
| [RS_MCS_6] = RS_NON_HT_RATE_OFDM_54M, |
| [RS_MCS_7] = RS_NON_HT_RATE_OFDM_54M, |
| [RS_MCS_8] = RS_NON_HT_RATE_OFDM_54M, |
| [RS_MCS_9] = RS_NON_HT_RATE_OFDM_54M, |
| [RS_MCS_10] = RS_NON_HT_RATE_OFDM_54M, |
| [RS_MCS_11] = RS_NON_HT_RATE_OFDM_54M, |
| }; |
| |
| typedef struct _RS_MNG_DYN_BW_STAY { |
| RS_MCS_E lowestStayMcs; |
| RS_MCS_E highestStayMcs; |
| } RS_MNG_DYN_BW_STAY; |
| |
| #define RS_MNG_DYN_BW_STAY_MCS(_bw, _nss1min, _nss1max, _nss2min, _nss2max) \ |
| [CHANNEL_WIDTH## \ |
| _bw] = {{.lowestStayMcs = RS_MCS_##_nss1min, .highestStayMcs = RS_MCS_##_nss1max}, \ |
| {.lowestStayMcs = RS_MCS_##_nss2min, .highestStayMcs = RS_MCS_##_nss2max}} |
| |
| // thresholds above/below which bandwidth will be increase/decreased |
| static RS_MNG_DYN_BW_STAY g_rsMngDynBwStayMcs[][2] = { |
| RS_MNG_DYN_BW_STAY_MCS(20, 0, 1, 0, 1), RS_MNG_DYN_BW_STAY_MCS(40, 2, 2, 2, 2), |
| RS_MNG_DYN_BW_STAY_MCS(80, 5, 8, 4, 7), RS_MNG_DYN_BW_STAY_MCS(160, 7, 9, 6, 9)}; |
| |
| /***********************************************************************/ |
| /* |
| * The following tables contain the expected throughput metrics for all rates |
| * |
| * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits |
| * |
| * where invalid entries are zeros. |
| * |
| * CCK rates are only valid in legacy table and will only be used in G |
| * (2.4 GHz) band. |
| */ |
| |
| static const TPT_BY_RATE_ARR expectedTptNonHt = { |
| [RS_NON_HT_RATE_CCK_1M] = 7, [RS_NON_HT_RATE_CCK_2M] = 12, |
| [RS_NON_HT_RATE_CCK_5_5M] = 33, [RS_NON_HT_RATE_CCK_11M] = 54, |
| [RS_NON_HT_RATE_OFDM_6M] = 38, [RS_NON_HT_RATE_OFDM_9M] = 58, |
| [RS_NON_HT_RATE_OFDM_12M] = 76, [RS_NON_HT_RATE_OFDM_18M] = 108, |
| [RS_NON_HT_RATE_OFDM_24M] = 135, [RS_NON_HT_RATE_OFDM_36M] = 176, |
| [RS_NON_HT_RATE_OFDM_48M] = 208, [RS_NON_HT_RATE_OFDM_54M] = 221, |
| }; |
| |
| static const TPT_BY_RATE_ARR expectedTptHtVht[2][MAX_CHANNEL_BW_INDX][2][2] = { |
| [RS_MNG_NO_AGG] = { |
| [CHANNEL_WIDTH20] = { |
| [RS_MNG_NGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 49, [RS_MCS_1] = 86, [RS_MCS_2] = 115, [RS_MCS_3] = 140, |
| [RS_MCS_4] = 178, [RS_MCS_5] = 204, [RS_MCS_6] = 215, [RS_MCS_7] = 223, |
| [RS_MCS_8] = 240, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 84, [RS_MCS_1] = 136, [RS_MCS_2] = 173, [RS_MCS_3] = 200, |
| [RS_MCS_4] = 238, [RS_MCS_5] = 260, [RS_MCS_6] = 267, [RS_MCS_7] = 275, |
| [RS_MCS_8] = 289, |
| }, |
| }, |
| [RS_MNG_SGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 53, [RS_MCS_1] = 93, [RS_MCS_2] = 124, [RS_MCS_3] = 149, |
| [RS_MCS_4] = 188, [RS_MCS_5] = 214, [RS_MCS_6] = 225, [RS_MCS_7] = 233, |
| [RS_MCS_8] = 249, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 91, [RS_MCS_1] = 144, [RS_MCS_2] = 182, [RS_MCS_3] = 210, |
| [RS_MCS_4] = 246, [RS_MCS_5] = 267, [RS_MCS_6] = 274, [RS_MCS_7] = 282, |
| [RS_MCS_8] = 295, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH40] = { |
| [RS_MNG_NGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 87, [RS_MCS_1] = 140, [RS_MCS_2] = 177, [RS_MCS_3] = 207, |
| [RS_MCS_4] = 245, [RS_MCS_5] = 265, [RS_MCS_6] = 273, [RS_MCS_7] = 281, |
| [RS_MCS_8] = 296, [RS_MCS_9] = 299, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 135, [RS_MCS_1] = 197, [RS_MCS_2] = 234, [RS_MCS_3] = 259, |
| [RS_MCS_4] = 292, [RS_MCS_5] = 304, [RS_MCS_6] = 311, [RS_MCS_7] = 314, |
| [RS_MCS_8] = 321, [RS_MCS_9] = 331, |
| }, |
| }, |
| [RS_MNG_SGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 94, [RS_MCS_1] = 149, [RS_MCS_2] = 187, [RS_MCS_3] = 216, |
| [RS_MCS_4] = 253, [RS_MCS_5] = 273, [RS_MCS_6] = 280, [RS_MCS_7] = 288, |
| [RS_MCS_8] = 302, [RS_MCS_9] = 305, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 144, [RS_MCS_1] = 205, [RS_MCS_2] = 242, [RS_MCS_3] = 266, |
| [RS_MCS_4] = 297, [RS_MCS_5] = 309, [RS_MCS_6] = 315, [RS_MCS_7] = 318, |
| [RS_MCS_8] = 325, [RS_MCS_9] = 335, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH80] = { |
| [RS_MNG_NGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 142, [RS_MCS_1] = 205, [RS_MCS_2] = 240, [RS_MCS_3] = 267, |
| [RS_MCS_4] = 299, [RS_MCS_5] = 312, [RS_MCS_6] = 319, [RS_MCS_7] = 323, |
| [RS_MCS_8] = 330, [RS_MCS_9] = 334, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 195, [RS_MCS_1] = 251, [RS_MCS_2] = 283, [RS_MCS_3] = 303, |
| [RS_MCS_4] = 325, [RS_MCS_5] = 333, [RS_MCS_6] = 337, [RS_MCS_7] = 337, |
| [RS_MCS_8] = 341, [RS_MCS_9] = 345, |
| }, |
| }, |
| [RS_MNG_SGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 151, [RS_MCS_1] = 213, [RS_MCS_2] = 248, [RS_MCS_3] = 274, |
| [RS_MCS_4] = 305, [RS_MCS_5] = 317, [RS_MCS_6] = 323, [RS_MCS_7] = 327, |
| [RS_MCS_8] = 334, [RS_MCS_9] = 337, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 203, [RS_MCS_1] = 258, [RS_MCS_2] = 288, [RS_MCS_3] = 308, |
| [RS_MCS_4] = 328, [RS_MCS_5] = 335, [RS_MCS_6] = 339, [RS_MCS_7] = 339, |
| [RS_MCS_8] = 343, [RS_MCS_9] = 346, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH160] = { |
| [RS_MNG_NGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 197, [RS_MCS_1] = 254, [RS_MCS_2] = 287, [RS_MCS_3] = 307, |
| [RS_MCS_4] = 330, [RS_MCS_5] = 338, [RS_MCS_6] = 342, [RS_MCS_7] = 342, |
| [RS_MCS_8] = 346, [RS_MCS_9] = 350, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 239, [RS_MCS_1] = 286, [RS_MCS_2] = 311, [RS_MCS_3] = 327, |
| [RS_MCS_4] = 341, [RS_MCS_5] = 345, [RS_MCS_6] = 349, [RS_MCS_7] = 349, |
| [RS_MCS_8] = 349, [RS_MCS_9] = 353, |
| }, |
| }, |
| [RS_MNG_SGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 205, [RS_MCS_1] = 261, [RS_MCS_2] = 292, [RS_MCS_3] = 312, |
| [RS_MCS_4] = 334, [RS_MCS_5] = 341, [RS_MCS_6] = 345, [RS_MCS_7] = 345, |
| [RS_MCS_8] = 348, [RS_MCS_9] = 352, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 245, [RS_MCS_1] = 290, [RS_MCS_2] = 314, [RS_MCS_3] = 330, |
| [RS_MCS_4] = 343, [RS_MCS_5] = 346, [RS_MCS_6] = 350, [RS_MCS_7] = 350, |
| [RS_MCS_8] = 350, [RS_MCS_9] = 354, |
| }, |
| }, |
| }, |
| }, |
| [RS_MNG_AGG] = { |
| [CHANNEL_WIDTH20] = { |
| [RS_MNG_NGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 53, [RS_MCS_1] = 106, [RS_MCS_2] = 159, [RS_MCS_3] = 213, |
| [RS_MCS_4] = 319, [RS_MCS_5] = 426, [RS_MCS_6] = 481, [RS_MCS_7] = 534, |
| [RS_MCS_8] = 641, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 105, [RS_MCS_1] = 211, [RS_MCS_2] = 318, [RS_MCS_3] = 425, |
| [RS_MCS_4] = 640, [RS_MCS_5] = 846, [RS_MCS_6] = 951, [RS_MCS_7] = 1060, |
| [RS_MCS_8] = 1266, |
| }, |
| }, |
| [RS_MNG_SGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 58, [RS_MCS_1] = 116, [RS_MCS_2] = 177, [RS_MCS_3] = 237, |
| [RS_MCS_4] = 356, [RS_MCS_5] = 474, [RS_MCS_6] = 534, [RS_MCS_7] = 593, |
| [RS_MCS_8] = 712, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 116, [RS_MCS_1] = 235, [RS_MCS_2] = 354, [RS_MCS_3] = 472, |
| [RS_MCS_4] = 712, [RS_MCS_5] = 942, [RS_MCS_6] = 1058, [RS_MCS_7] = 1175, |
| [RS_MCS_8] = 1401, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH40] = { |
| [RS_MNG_NGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 109, [RS_MCS_1] = 219, [RS_MCS_2] = 331, [RS_MCS_3] = 442, |
| [RS_MCS_4] = 665, [RS_MCS_5] = 882, [RS_MCS_6] = 991, [RS_MCS_7] = 1103, |
| [RS_MCS_8] = 1316, [RS_MCS_9] = 1450, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 222, [RS_MCS_1] = 445, [RS_MCS_2] = 669, [RS_MCS_3] = 892, |
| [RS_MCS_4] = 1334, [RS_MCS_5] = 1725, [RS_MCS_6] = 1907, [RS_MCS_7] = 2085, |
| [RS_MCS_8] = 2422, [RS_MCS_9] = 2637, |
| }, |
| }, |
| [RS_MNG_SGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 120, [RS_MCS_1] = 244, [RS_MCS_2] = 368, [RS_MCS_3] = 492, |
| [RS_MCS_4] = 740, [RS_MCS_5] = 978, [RS_MCS_6] = 1101, [RS_MCS_7] = 1222, |
| [RS_MCS_8] = 1456, [RS_MCS_9] = 1601, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 246, [RS_MCS_1] = 494, [RS_MCS_2] = 742, [RS_MCS_3] = 991, |
| [RS_MCS_4] = 1470, [RS_MCS_5] = 1888, [RS_MCS_6] = 2085, [RS_MCS_7] = 2276, |
| [RS_MCS_8] = 2636, [RS_MCS_9] = 2865, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH80] = { |
| [RS_MNG_NGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 237, [RS_MCS_1] = 478, [RS_MCS_2] = 718, [RS_MCS_3] = 959, |
| [RS_MCS_4] = 1427, [RS_MCS_5] = 1849, [RS_MCS_6] = 2043, [RS_MCS_7] = 2232, |
| [RS_MCS_8] = 2589, [RS_MCS_9] = 2813, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 475, [RS_MCS_1] = 953, [RS_MCS_2] = 1420, [RS_MCS_3] = 1843, |
| [RS_MCS_4] = 2584, [RS_MCS_5] = 3229, [RS_MCS_6] = 3522, [RS_MCS_7] = 3799, |
| [RS_MCS_8] = 4303, [RS_MCS_9] = 4603, |
| }, |
| }, |
| [RS_MNG_SGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 263, [RS_MCS_1] = 531, [RS_MCS_2] = 797, [RS_MCS_3] = 1065, |
| [RS_MCS_4] = 1577, [RS_MCS_5] = 2022, [RS_MCS_6] = 2231, [RS_MCS_7] = 2434, |
| [RS_MCS_8] = 2814, [RS_MCS_9] = 3052, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 528, [RS_MCS_1] = 1058, [RS_MCS_2] = 1569, [RS_MCS_3] = 2016, |
| [RS_MCS_4] = 2808, [RS_MCS_5] = 3490, [RS_MCS_6] = 3798, [RS_MCS_7] = 4087, |
| [RS_MCS_8] = 4610, [RS_MCS_9] = 4919, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH160] = { |
| [RS_MNG_NGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 476, [RS_MCS_1] = 954, [RS_MCS_2] = 1422, [RS_MCS_3] = 1846, |
| [RS_MCS_4] = 2589, [RS_MCS_5] = 3237, [RS_MCS_6] = 3531, [RS_MCS_7] = 3810, |
| [RS_MCS_8] = 4317, [RS_MCS_9] = 4619, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 948, [RS_MCS_1] = 1833, [RS_MCS_2] = 2569, [RS_MCS_3] = 3221, |
| [RS_MCS_4] = 4303, [RS_MCS_5] = 5162, [RS_MCS_6] = 5530, [RS_MCS_7] = 5856, |
| [RS_MCS_8] = 6449, [RS_MCS_9] = 6767, |
| }, |
| }, |
| [RS_MNG_SGI] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 529, [RS_MCS_1] = 1060, [RS_MCS_2] = 1571, [RS_MCS_3] = 2019, |
| [RS_MCS_4] = 2814, [RS_MCS_5] = 3499, [RS_MCS_6] = 3808, [RS_MCS_7] = 4100, |
| [RS_MCS_8] = 4626, [RS_MCS_9] = 4937, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 1053, [RS_MCS_1] = 2004, [RS_MCS_2] = 2790, [RS_MCS_3] = 3481, |
| [RS_MCS_4] = 4610, [RS_MCS_5] = 5491, [RS_MCS_6] = 5864, [RS_MCS_7] = 6194, |
| [RS_MCS_8] = 6787, [RS_MCS_9] = 7103, |
| }, |
| }, |
| }, |
| }, |
| }; |
| |
| static const TPT_BY_RATE_ARR expectedTptHe[2][MAX_CHANNEL_BW_INDX][3][2] = { |
| [RS_MNG_NO_AGG] = { |
| [CHANNEL_WIDTH20] = { |
| [RS_MNG_GI_3_2] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 30, [RS_MCS_1] = 61, [RS_MCS_2] = 92, [RS_MCS_3] = 122, |
| [RS_MCS_4] = 184, [RS_MCS_5] = 245, [RS_MCS_6] = 276, [RS_MCS_7] = 307, |
| [RS_MCS_8] = 368, [RS_MCS_9] = 409, [RS_MCS_10] = 460, [RS_MCS_11] = 511, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 61, [RS_MCS_1] = 122, [RS_MCS_2] = 184, [RS_MCS_3] = 245, |
| [RS_MCS_4] = 368, [RS_MCS_5] = 491, [RS_MCS_6] = 552, [RS_MCS_7] = 614, |
| [RS_MCS_8] = 737, [RS_MCS_9] = 819, [RS_MCS_10] = 921, [RS_MCS_11] = 1023, |
| }, |
| }, |
| [RS_MNG_GI_1_6] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 34, [RS_MCS_1] = 68, [RS_MCS_2] = 102, [RS_MCS_3] = 136, |
| [RS_MCS_4] = 204, [RS_MCS_5] = 273, [RS_MCS_6] = 307, [RS_MCS_7] = 341, |
| [RS_MCS_8] = 409, [RS_MCS_9] = 455, [RS_MCS_10] = 511, [RS_MCS_11] = 568, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 68, [RS_MCS_1] = 136, [RS_MCS_2] = 204, [RS_MCS_3] = 273, |
| [RS_MCS_4] = 409, [RS_MCS_5] = 546, [RS_MCS_6] = 614, [RS_MCS_7] = 682, |
| [RS_MCS_8] = 819, [RS_MCS_9] = 910, [RS_MCS_10] = 1023, [RS_MCS_11] = 1137, |
| }, |
| }, |
| [RS_MNG_GI_0_8] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 36, [RS_MCS_1] = 72, [RS_MCS_2] = 108, [RS_MCS_3] = 144, |
| [RS_MCS_4] = 216, [RS_MCS_5] = 289, [RS_MCS_6] = 325, [RS_MCS_7] = 361, |
| [RS_MCS_8] = 433, [RS_MCS_9] = 481, [RS_MCS_10] = 541, [RS_MCS_11] = 602, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 72, [RS_MCS_1] = 144, [RS_MCS_2] = 216, [RS_MCS_3] = 289, |
| [RS_MCS_4] = 433, [RS_MCS_5] = 578, [RS_MCS_6] = 650, [RS_MCS_7] = 722, |
| [RS_MCS_8] = 867, [RS_MCS_9] = 963, [RS_MCS_10] = 1083, [RS_MCS_11] = 1204, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH40] = { |
| [RS_MNG_GI_3_2] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 61, [RS_MCS_1] = 122, [RS_MCS_2] = 184, [RS_MCS_3] = 245, |
| [RS_MCS_4] = 368, [RS_MCS_5] = 491, [RS_MCS_6] = 552, [RS_MCS_7] = 614, |
| [RS_MCS_8] = 737, [RS_MCS_9] = 819, [RS_MCS_10] = 921, [RS_MCS_11] = 1023, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 122, [RS_MCS_1] = 245, [RS_MCS_2] = 368, [RS_MCS_3] = 491, |
| [RS_MCS_4] = 737, [RS_MCS_5] = 982, [RS_MCS_6] = 1105, [RS_MCS_7] = 1228, |
| [RS_MCS_8] = 1474, [RS_MCS_9] = 1638, [RS_MCS_10] = 1842, [RS_MCS_11] = 2047, |
| }, |
| }, |
| [RS_MNG_GI_1_6] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 68, [RS_MCS_1] = 136, [RS_MCS_2] = 204, [RS_MCS_3] = 273, |
| [RS_MCS_4] = 409, [RS_MCS_5] = 546, [RS_MCS_6] = 614, [RS_MCS_7] = 682, |
| [RS_MCS_8] = 819, [RS_MCS_9] = 910, [RS_MCS_10] = 1023, [RS_MCS_11] = 1137, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 136, [RS_MCS_1] = 273, [RS_MCS_2] = 409, [RS_MCS_3] = 546, |
| [RS_MCS_4] = 819, [RS_MCS_5] = 1092, [RS_MCS_6] = 1228, [RS_MCS_7] = 1365, |
| [RS_MCS_8] = 1638, [RS_MCS_9] = 1820, [RS_MCS_10] = 2047, [RS_MCS_11] = 2275, |
| }, |
| }, |
| [RS_MNG_GI_0_8] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 72, [RS_MCS_1] = 144, [RS_MCS_2] = 216, [RS_MCS_3] = 289, |
| [RS_MCS_4] = 433, [RS_MCS_5] = 578, [RS_MCS_6] = 650, [RS_MCS_7] = 722, |
| [RS_MCS_8] = 867, [RS_MCS_9] = 963, [RS_MCS_10] = 1083, [RS_MCS_11] = 1204, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 144, [RS_MCS_1] = 289, [RS_MCS_2] = 433, [RS_MCS_3] = 578, |
| [RS_MCS_4] = 867, [RS_MCS_5] = 1156, [RS_MCS_6] = 1300, [RS_MCS_7] = 1445, |
| [RS_MCS_8] = 1734, [RS_MCS_9] = 1927, [RS_MCS_10] = 2167, [RS_MCS_11] = 2408, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH80] = { |
| [RS_MNG_GI_3_2] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 128, [RS_MCS_1] = 257, [RS_MCS_2] = 385, [RS_MCS_3] = 514, |
| [RS_MCS_4] = 771, [RS_MCS_5] = 1029, [RS_MCS_6] = 1157, [RS_MCS_7] = 1286, |
| [RS_MCS_8] = 1543, [RS_MCS_9] = 1715, [RS_MCS_10] = 1929, [RS_MCS_11] = 2143, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 257, [RS_MCS_1] = 514, [RS_MCS_2] = 771, [RS_MCS_3] = 1029, |
| [RS_MCS_4] = 1543, [RS_MCS_5] = 2058, [RS_MCS_6] = 2315, [RS_MCS_7] = 2572, |
| [RS_MCS_8] = 3087, [RS_MCS_9] = 3430, [RS_MCS_10] = 3858, [RS_MCS_11] = 4287, |
| }, |
| }, |
| [RS_MNG_GI_1_6] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 142, [RS_MCS_1] = 285, [RS_MCS_2] = 428, [RS_MCS_3] = 571, |
| [RS_MCS_4] = 857, [RS_MCS_5] = 1143, [RS_MCS_6] = 1286, [RS_MCS_7] = 1429, |
| [RS_MCS_8] = 1715, [RS_MCS_9] = 1905, [RS_MCS_10] = 2143, [RS_MCS_11] = 2381, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 285, [RS_MCS_1] = 571, [RS_MCS_2] = 857, [RS_MCS_3] = 1143, |
| [RS_MCS_4] = 1715, [RS_MCS_5] = 2286, [RS_MCS_6] = 2572, [RS_MCS_7] = 2858, |
| [RS_MCS_8] = 3430, [RS_MCS_9] = 3811, [RS_MCS_10] = 4287, [RS_MCS_11] = 4763, |
| }, |
| }, |
| [RS_MNG_GI_0_8] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 151, [RS_MCS_1] = 302, [RS_MCS_2] = 453, [RS_MCS_3] = 605, |
| [RS_MCS_4] = 907, [RS_MCS_5] = 1210, [RS_MCS_6] = 1361, [RS_MCS_7] = 1513, |
| [RS_MCS_8] = 1815, [RS_MCS_9] = 2017, [RS_MCS_10] = 2269, [RS_MCS_11] = 2522, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 302, [RS_MCS_1] = 605, [RS_MCS_2] = 907, [RS_MCS_3] = 1210, |
| [RS_MCS_4] = 1815, [RS_MCS_5] = 2421, [RS_MCS_6] = 2723, [RS_MCS_7] = 3026, |
| [RS_MCS_8] = 3631, [RS_MCS_9] = 4035, [RS_MCS_10] = 4539, [RS_MCS_11] = 5044, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH160] = { |
| [RS_MNG_GI_3_2] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 257, [RS_MCS_1] = 514, [RS_MCS_2] = 771, [RS_MCS_3] = 1029, |
| [RS_MCS_4] = 1543, [RS_MCS_5] = 2058, [RS_MCS_6] = 2315, [RS_MCS_7] = 2572, |
| [RS_MCS_8] = 3087, [RS_MCS_9] = 3430, [RS_MCS_10] = 3858, [RS_MCS_11] = 4287, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 514, [RS_MCS_1] = 1029, [RS_MCS_2] = 1543, [RS_MCS_3] = 2058, |
| [RS_MCS_4] = 3087, [RS_MCS_5] = 4116, [RS_MCS_6] = 4630, [RS_MCS_7] = 5145, |
| [RS_MCS_8] = 6174, [RS_MCS_9] = 6860, [RS_MCS_10] = 7717, [RS_MCS_11] = 8575, |
| }, |
| }, |
| [RS_MNG_GI_1_6] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 285, [RS_MCS_1] = 571, [RS_MCS_2] = 857, [RS_MCS_3] = 1143, |
| [RS_MCS_4] = 1715, [RS_MCS_5] = 2286, [RS_MCS_6] = 2572, [RS_MCS_7] = 2858, |
| [RS_MCS_8] = 3430, [RS_MCS_9] = 3811, [RS_MCS_10] = 4287, [RS_MCS_11] = 4763, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 571, [RS_MCS_1] = 1143, [RS_MCS_2] = 1715, [RS_MCS_3] = 2286, |
| [RS_MCS_4] = 3430, [RS_MCS_5] = 4573, [RS_MCS_6] = 5145, [RS_MCS_7] = 5716, |
| [RS_MCS_8] = 6860, [RS_MCS_9] = 7622, [RS_MCS_10] = 8575, [RS_MCS_11] = 9527, |
| }, |
| }, |
| [RS_MNG_GI_0_8] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 302, [RS_MCS_1] = 605, [RS_MCS_2] = 907, [RS_MCS_3] = 1210, |
| [RS_MCS_4] = 1815, [RS_MCS_5] = 2421, [RS_MCS_6] = 2723, [RS_MCS_7] = 3026, |
| [RS_MCS_8] = 3631, [RS_MCS_9] = 4035, [RS_MCS_10] = 4539, [RS_MCS_11] = 5044, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 605, [RS_MCS_1] = 1210, [RS_MCS_2] = 1815, [RS_MCS_3] = 2421, |
| [RS_MCS_4] = 3631, [RS_MCS_5] = 4842, [RS_MCS_6] = 5447, [RS_MCS_7] = 6052, |
| [RS_MCS_8] = 7263, [RS_MCS_9] = 8070, [RS_MCS_10] = 9079, [RS_MCS_11] = 10088, |
| }, |
| }, |
| }, |
| }, |
| [RS_MNG_AGG] = { |
| [CHANNEL_WIDTH20] = { |
| [RS_MNG_GI_3_2] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 62, [RS_MCS_1] = 124, [RS_MCS_2] = 186, [RS_MCS_3] = 248, |
| [RS_MCS_4] = 372, [RS_MCS_5] = 497, [RS_MCS_6] = 559, [RS_MCS_7] = 621, |
| [RS_MCS_8] = 745, [RS_MCS_9] = 828, [RS_MCS_10] = 932, [RS_MCS_11] = 1035, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 124, [RS_MCS_1] = 248, [RS_MCS_2] = 372, [RS_MCS_3] = 497, |
| [RS_MCS_4] = 745, [RS_MCS_5] = 994, [RS_MCS_6] = 1118, [RS_MCS_7] = 1243, |
| [RS_MCS_8] = 1491, [RS_MCS_9] = 1657, [RS_MCS_10] = 1864, [RS_MCS_11] = 2071, |
| }, |
| }, |
| [RS_MNG_GI_1_6] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 69, [RS_MCS_1] = 138, [RS_MCS_2] = 207, [RS_MCS_3] = 276, |
| [RS_MCS_4] = 414, [RS_MCS_5] = 552, [RS_MCS_6] = 621, [RS_MCS_7] = 690, |
| [RS_MCS_8] = 828, [RS_MCS_9] = 920, [RS_MCS_10] = 1035, [RS_MCS_11] = 1151, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 138, [RS_MCS_1] = 276, [RS_MCS_2] = 414, [RS_MCS_3] = 552, |
| [RS_MCS_4] = 828, [RS_MCS_5] = 1105, [RS_MCS_6] = 1243, [RS_MCS_7] = 1381, |
| [RS_MCS_8] = 1657, [RS_MCS_9] = 1841, [RS_MCS_10] = 2071, [RS_MCS_11] = 2302, |
| }, |
| }, |
| [RS_MNG_GI_0_8] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 73, [RS_MCS_1] = 146, [RS_MCS_2] = 219, [RS_MCS_3] = 292, |
| [RS_MCS_4] = 438, [RS_MCS_5] = 585, [RS_MCS_6] = 658, [RS_MCS_7] = 731, |
| [RS_MCS_8] = 877, [RS_MCS_9] = 975, [RS_MCS_10] = 1096, [RS_MCS_11] = 1218, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 146, [RS_MCS_1] = 292, [RS_MCS_2] = 438, [RS_MCS_3] = 585, |
| [RS_MCS_4] = 877, [RS_MCS_5] = 1170, [RS_MCS_6] = 1316, [RS_MCS_7] = 1462, |
| [RS_MCS_8] = 1755, [RS_MCS_9] = 1950, [RS_MCS_10] = 2193, [RS_MCS_11] = 2437, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH40] = { |
| [RS_MNG_GI_3_2] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 124, [RS_MCS_1] = 248, [RS_MCS_2] = 372, [RS_MCS_3] = 497, |
| [RS_MCS_4] = 745, [RS_MCS_5] = 994, [RS_MCS_6] = 1118, [RS_MCS_7] = 1243, |
| [RS_MCS_8] = 1491, [RS_MCS_9] = 1657, [RS_MCS_10] = 1864, [RS_MCS_11] = 2071, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 248, [RS_MCS_1] = 497, [RS_MCS_2] = 745, [RS_MCS_3] = 994, |
| [RS_MCS_4] = 1491, [RS_MCS_5] = 1989, [RS_MCS_6] = 2237, [RS_MCS_7] = 2486, |
| [RS_MCS_8] = 2983, [RS_MCS_9] = 3315, [RS_MCS_10] = 3729, [RS_MCS_11] = 4143, |
| }, |
| }, |
| [RS_MNG_GI_1_6] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 138, [RS_MCS_1] = 276, [RS_MCS_2] = 414, [RS_MCS_3] = 552, |
| [RS_MCS_4] = 828, [RS_MCS_5] = 1105, [RS_MCS_6] = 1243, [RS_MCS_7] = 1381, |
| [RS_MCS_8] = 1657, [RS_MCS_9] = 1841, [RS_MCS_10] = 2071, [RS_MCS_11] = 2302, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 276, [RS_MCS_1] = 552, [RS_MCS_2] = 828, [RS_MCS_3] = 1105, |
| [RS_MCS_4] = 1657, [RS_MCS_5] = 2210, [RS_MCS_6] = 2486, [RS_MCS_7] = 2762, |
| [RS_MCS_8] = 3315, [RS_MCS_9] = 3683, [RS_MCS_10] = 4143, [RS_MCS_11] = 4604, |
| }, |
| }, |
| [RS_MNG_GI_0_8] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 146, [RS_MCS_1] = 292, [RS_MCS_2] = 438, [RS_MCS_3] = 585, |
| [RS_MCS_4] = 877, [RS_MCS_5] = 1170, [RS_MCS_6] = 1316, [RS_MCS_7] = 1462, |
| [RS_MCS_8] = 1755, [RS_MCS_9] = 1950, [RS_MCS_10] = 2193, [RS_MCS_11] = 2437, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 292, [RS_MCS_1] = 585, [RS_MCS_2] = 877, [RS_MCS_3] = 1170, |
| [RS_MCS_4] = 1755, [RS_MCS_5] = 2340, [RS_MCS_6] = 2632, [RS_MCS_7] = 2925, |
| [RS_MCS_8] = 3510, [RS_MCS_9] = 3900, [RS_MCS_10] = 4387, [RS_MCS_11] = 4875, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH80] = { |
| [RS_MNG_GI_3_2] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 260, [RS_MCS_1] = 520, [RS_MCS_2] = 780, [RS_MCS_3] = 1041, |
| [RS_MCS_4] = 1561, [RS_MCS_5] = 2082, [RS_MCS_6] = 2342, [RS_MCS_7] = 2603, |
| [RS_MCS_8] = 3123, [RS_MCS_9] = 3470, [RS_MCS_10] = 3904, [RS_MCS_11] = 4338, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 520, [RS_MCS_1] = 1041, [RS_MCS_2] = 1561, [RS_MCS_3] = 2082, |
| [RS_MCS_4] = 3123, [RS_MCS_5] = 4165, [RS_MCS_6] = 4685, [RS_MCS_7] = 5206, |
| [RS_MCS_8] = 6247, [RS_MCS_9] = 6941, [RS_MCS_10] = 7809, [RS_MCS_11] = 8677, |
| }, |
| }, |
| [RS_MNG_GI_1_6] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 289, [RS_MCS_1] = 578, [RS_MCS_2] = 867, [RS_MCS_3] = 1156, |
| [RS_MCS_4] = 1735, [RS_MCS_5] = 2313, [RS_MCS_6] = 2603, [RS_MCS_7] = 2892, |
| [RS_MCS_8] = 3470, [RS_MCS_9] = 3856, [RS_MCS_10] = 4338, [RS_MCS_11] = 4820, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 578, [RS_MCS_1] = 1156, [RS_MCS_2] = 1735, [RS_MCS_3] = 2313, |
| [RS_MCS_4] = 3470, [RS_MCS_5] = 4627, [RS_MCS_6] = 5206, [RS_MCS_7] = 5784, |
| [RS_MCS_8] = 6941, [RS_MCS_9] = 7712, [RS_MCS_10] = 8677, [RS_MCS_11] = 9641, |
| }, |
| }, |
| [RS_MNG_GI_0_8] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 306, [RS_MCS_1] = 612, [RS_MCS_2] = 918, [RS_MCS_3] = 1225, |
| [RS_MCS_4] = 1837, [RS_MCS_5] = 2450, [RS_MCS_6] = 2756, [RS_MCS_7] = 3062, |
| [RS_MCS_8] = 3675, [RS_MCS_9] = 4083, [RS_MCS_10] = 4593, [RS_MCS_11] = 5104, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 612, [RS_MCS_1] = 1225, [RS_MCS_2] = 1837, [RS_MCS_3] = 2450, |
| [RS_MCS_4] = 3675, [RS_MCS_5] = 4900, [RS_MCS_6] = 5512, [RS_MCS_7] = 6125, |
| [RS_MCS_8] = 7350, [RS_MCS_9] = 8166, [RS_MCS_10] = 9187, [RS_MCS_11] = 10208, |
| }, |
| }, |
| }, |
| [CHANNEL_WIDTH160] = { |
| [RS_MNG_GI_3_2] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 520, [RS_MCS_1] = 1041, [RS_MCS_2] = 1561, [RS_MCS_3] = 2082, |
| [RS_MCS_4] = 3123, [RS_MCS_5] = 4165, [RS_MCS_6] = 4685, [RS_MCS_7] = 5206, |
| [RS_MCS_8] = 6247, [RS_MCS_9] = 6941, [RS_MCS_10] = 7809, [RS_MCS_11] = 8677, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 1041, [RS_MCS_1] = 2082, [RS_MCS_2] = 3123, [RS_MCS_3] = 4165, |
| [RS_MCS_4] = 6247, [RS_MCS_5] = 8330, [RS_MCS_6] = 9371, [RS_MCS_7] = 10412, |
| [RS_MCS_8] = 12495, [RS_MCS_9] = 13883, [RS_MCS_10] = 15618, [RS_MCS_11] = 17354, |
| }, |
| }, |
| [RS_MNG_GI_1_6] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 578, [RS_MCS_1] = 1156, [RS_MCS_2] = 1735, [RS_MCS_3] = 2313, |
| [RS_MCS_4] = 3470, [RS_MCS_5] = 4627, [RS_MCS_6] = 5206, [RS_MCS_7] = 5784, |
| [RS_MCS_8] = 6941, [RS_MCS_9] = 7712, [RS_MCS_10] = 8677, [RS_MCS_11] = 9641, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 1156, [RS_MCS_1] = 2313, [RS_MCS_2] = 3470, [RS_MCS_3] = 4627, |
| [RS_MCS_4] = 6941, [RS_MCS_5] = 9255, [RS_MCS_6] = 10412, [RS_MCS_7] = 11569, |
| [RS_MCS_8] = 13883, [RS_MCS_9] = 15425, [RS_MCS_10] = 17354, [RS_MCS_11] = 19282, |
| }, |
| }, |
| [RS_MNG_GI_0_8] = { |
| [RS_MNG_SISO] = { |
| [RS_MCS_0] = 612, [RS_MCS_1] = 1225, [RS_MCS_2] = 1837, [RS_MCS_3] = 2450, |
| [RS_MCS_4] = 3675, [RS_MCS_5] = 4900, [RS_MCS_6] = 5512, [RS_MCS_7] = 6125, |
| [RS_MCS_8] = 7350, [RS_MCS_9] = 8166, [RS_MCS_10] = 9187, [RS_MCS_11] = 10208, |
| }, |
| [RS_MNG_MIMO] = { |
| [RS_MCS_0] = 1225, [RS_MCS_1] = 2450, [RS_MCS_2] = 3675, [RS_MCS_3] = 4900, |
| [RS_MCS_4] = 7350, [RS_MCS_5] = 9800, [RS_MCS_6] = 11025, [RS_MCS_7] = 12250, |
| [RS_MCS_8] = 14700, [RS_MCS_9] = 16333, [RS_MCS_10] = 18375, [RS_MCS_11] = 20416, |
| }, |
| }, |
| }, |
| }, |
| }; |
| |
| /*******************************************************************************/ |
| |
| /************************************************************************************/ |
| /************************ Aggergation ********************************/ |
| /************************************************************************************/ |
| // Default values for Aggregation Params |
| // info about the relevant fields can be found for LINK_QUAL_AGG_PARAMS_API_S |
| #define RS_MNG_AGG_DISABLE_START_TH 3 |
| |
| /*******************************************************************************/ |
| static const RS_MNG_STA_LIMITS_S g_rsMngStaModLimits[] = { |
| { |
| // HT/VHT/HE |
| .successFramesLimit = RS_MNG_NON_LEGACY_SUCCESS_LIMIT, |
| .failedFramesLimit = RS_MNG_NON_LEGACY_FAILURE_LIMIT, |
| .statsFlushTimeLimit = RS_MNG_STATS_FLUSH_TIME_LIMIT, |
| .clearTblWindowsLimit = RS_MNG_NON_LEGACY_MOD_COUNTER_LIMIT, |
| }, |
| { |
| // non-HT |
| .successFramesLimit = RS_MNG_LEGACY_SUCCESS_LIMIT, |
| .failedFramesLimit = RS_MNG_LEGACY_FAILURE_LIMIT, |
| .statsFlushTimeLimit = RS_MNG_STATS_FLUSH_TIME_LIMIT, |
| .clearTblWindowsLimit = RS_MNG_LEGACY_MOD_COUNTER_LIMIT, |
| }}; |
| |
| static BOOLEAN _allowColAnt(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol); |
| static BOOLEAN _allowColMimo(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol); |
| static BOOLEAN _allowColSiso(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol); |
| static BOOLEAN _allowColSgi(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol); |
| static BOOLEAN _alloCol2xLTF(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol); |
| static BOOLEAN _allowColHe(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol); |
| static BOOLEAN _allowColHtVht(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol); |
| |
| static const RS_MNG_COL_ELEM_S rsMngColumns[] = { |
| [RS_MNG_COL_NON_HT_ANT_A] = |
| { |
| .mode = RS_MNG_MODUL_LEGACY, |
| .ant = TLC_MNG_CHAIN_A_MSK, |
| .nextCols = {RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_SISO_ANT_A, RS_MNG_COL_MIMO2, |
| RS_MNG_COL_HE_3_2_SISO_ANT_A, RS_MNG_COL_HE_3_2_MIMO, RS_MNG_COL_INVALID, |
| RS_MNG_COL_INVALID}, |
| .checks = {_allowColAnt}, |
| }, |
| [RS_MNG_COL_NON_HT_ANT_B] = |
| { |
| .mode = RS_MNG_MODUL_LEGACY, |
| .ant = TLC_MNG_CHAIN_B_MSK, |
| .nextCols = {RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_SISO_ANT_B, RS_MNG_COL_MIMO2, |
| RS_MNG_COL_HE_3_2_SISO_ANT_B, RS_MNG_COL_HE_3_2_MIMO, RS_MNG_COL_INVALID, |
| RS_MNG_COL_INVALID}, |
| .checks = {_allowColAnt}, |
| }, |
| [RS_MNG_COL_SISO_ANT_A] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_A_MSK, |
| .nextCols = {RS_MNG_COL_SISO_ANT_B, RS_MNG_COL_MIMO2, RS_MNG_COL_SISO_ANT_A_SGI, |
| RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID, |
| RS_MNG_COL_INVALID}, |
| .checks = {_allowColHtVht, _allowColSiso, _allowColAnt}, |
| }, |
| [RS_MNG_COL_SISO_ANT_B] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_B_MSK, |
| .nextCols = {RS_MNG_COL_SISO_ANT_A, RS_MNG_COL_MIMO2, RS_MNG_COL_SISO_ANT_B_SGI, |
| RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID, |
| RS_MNG_COL_INVALID}, |
| .checks = {_allowColHtVht, _allowColSiso, _allowColAnt}, |
| }, |
| [RS_MNG_COL_SISO_ANT_A_SGI] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_A_MSK, |
| .gi = HT_VHT_SGI, |
| .nextCols = {RS_MNG_COL_SISO_ANT_B_SGI, RS_MNG_COL_MIMO2_SGI, RS_MNG_COL_SISO_ANT_A, |
| RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID, |
| RS_MNG_COL_INVALID}, |
| .checks = {_allowColHtVht, _allowColSiso, _allowColAnt, _allowColSgi}, |
| }, |
| [RS_MNG_COL_SISO_ANT_B_SGI] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_B_MSK, |
| .gi = HT_VHT_SGI, |
| .nextCols = {RS_MNG_COL_SISO_ANT_A_SGI, RS_MNG_COL_MIMO2_SGI, RS_MNG_COL_SISO_ANT_B, |
| RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID, |
| RS_MNG_COL_INVALID}, |
| .checks = {_allowColHtVht, _allowColSiso, _allowColAnt, _allowColSgi}, |
| }, |
| [RS_MNG_COL_MIMO2] = |
| { |
| .mode = RS_MNG_MODUL_MIMO2, |
| .ant = TLC_MNG_CHAIN_A_MSK | TLC_MNG_CHAIN_B_MSK, |
| .nextCols = |
| {RS_MNG_COL_SISO_ANT_A, // + NON_SHARED_ANT_RFIC_ID (see _rsMngGetNextColId) |
| RS_MNG_COL_MIMO2_SGI, RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, |
| RS_MNG_COL_INVALID, RS_MNG_COL_INVALID, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHtVht, _allowColMimo}, |
| }, |
| [RS_MNG_COL_MIMO2_SGI] = |
| { |
| .mode = RS_MNG_MODUL_MIMO2, |
| .ant = TLC_MNG_CHAIN_A_MSK | TLC_MNG_CHAIN_B_MSK, |
| .gi = HT_VHT_SGI, |
| .nextCols = |
| {RS_MNG_COL_SISO_ANT_A_SGI, // + NON_SHARED_ANT_RFIC_ID (see _rsMngGetNextColId) |
| RS_MNG_COL_MIMO2, RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, |
| RS_MNG_COL_INVALID, RS_MNG_COL_INVALID, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHtVht, _allowColMimo, _allowColSgi}, |
| }, |
| [RS_MNG_COL_HE_3_2_SISO_ANT_A] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_A_MSK, |
| .gi = HE_3_2_GI, |
| .nextCols = {RS_MNG_COL_HE_3_2_SISO_ANT_B, RS_MNG_COL_HE_3_2_MIMO, |
| RS_MNG_COL_HE_1_6_SISO_ANT_A, RS_MNG_COL_NON_HT_ANT_A, |
| RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHe, _allowColSiso, _allowColAnt}, |
| }, |
| [RS_MNG_COL_HE_1_6_SISO_ANT_A] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_A_MSK, |
| .gi = HE_1_6_GI, |
| .nextCols = {RS_MNG_COL_HE_1_6_SISO_ANT_B, RS_MNG_COL_HE_1_6_MIMO, |
| RS_MNG_COL_HE_0_8_SISO_ANT_A, RS_MNG_COL_HE_3_2_SISO_ANT_A, |
| RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHe, _allowColSiso, _allowColAnt, _alloCol2xLTF}, |
| }, |
| [RS_MNG_COL_HE_0_8_SISO_ANT_A] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_A_MSK, |
| .gi = HE_0_8_GI, |
| .nextCols = {RS_MNG_COL_HE_0_8_SISO_ANT_B, RS_MNG_COL_HE_0_8_MIMO, |
| RS_MNG_COL_HE_1_6_SISO_ANT_A, RS_MNG_COL_NON_HT_ANT_A, |
| RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHe, _allowColSiso, _allowColAnt, _alloCol2xLTF}, |
| }, |
| [RS_MNG_COL_HE_3_2_SISO_ANT_B] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_B_MSK, |
| .gi = HE_3_2_GI, |
| .nextCols = {RS_MNG_COL_HE_3_2_SISO_ANT_A, RS_MNG_COL_HE_3_2_MIMO, |
| RS_MNG_COL_HE_1_6_SISO_ANT_B, RS_MNG_COL_NON_HT_ANT_A, |
| RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHe, _allowColSiso, _allowColAnt}, |
| }, |
| [RS_MNG_COL_HE_1_6_SISO_ANT_B] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_B_MSK, |
| .gi = HE_1_6_GI, |
| .nextCols = {RS_MNG_COL_HE_1_6_SISO_ANT_A, RS_MNG_COL_HE_1_6_MIMO, |
| RS_MNG_COL_HE_0_8_SISO_ANT_B, RS_MNG_COL_HE_3_2_SISO_ANT_B, |
| RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHe, _allowColSiso, _allowColAnt, _alloCol2xLTF}, |
| }, |
| [RS_MNG_COL_HE_0_8_SISO_ANT_B] = |
| { |
| .mode = RS_MNG_MODUL_SISO, |
| .ant = TLC_MNG_CHAIN_B_MSK, |
| .gi = HE_0_8_GI, |
| .nextCols = {RS_MNG_COL_HE_0_8_SISO_ANT_A, RS_MNG_COL_HE_0_8_MIMO, |
| RS_MNG_COL_HE_1_6_SISO_ANT_B, RS_MNG_COL_NON_HT_ANT_A, |
| RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHe, _allowColSiso, _allowColAnt, _alloCol2xLTF}, |
| }, |
| [RS_MNG_COL_HE_3_2_MIMO] = |
| { |
| .mode = RS_MNG_MODUL_MIMO2, |
| .ant = TLC_MNG_CHAIN_A_MSK | TLC_MNG_CHAIN_B_MSK, |
| .gi = HE_3_2_GI, |
| .nextCols = |
| {RS_MNG_COL_HE_3_2_SISO_ANT_A, // + NON_SHARED_ANT_RFIC_ID (see _rsMngGetNextColId) |
| RS_MNG_COL_HE_1_6_MIMO, RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, |
| RS_MNG_COL_INVALID, RS_MNG_COL_INVALID, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHe, _allowColMimo}, |
| }, |
| [RS_MNG_COL_HE_1_6_MIMO] = |
| { |
| .mode = RS_MNG_MODUL_MIMO2, |
| .ant = TLC_MNG_CHAIN_A_MSK | TLC_MNG_CHAIN_B_MSK, |
| .gi = HE_1_6_GI, |
| .nextCols = |
| {RS_MNG_COL_HE_1_6_SISO_ANT_A, // + NON_SHARED_ANT_RFIC_ID (see _rsMngGetNextColId) |
| RS_MNG_COL_HE_0_8_MIMO, RS_MNG_COL_HE_3_2_MIMO, RS_MNG_COL_NON_HT_ANT_A, |
| RS_MNG_COL_NON_HT_ANT_B, RS_MNG_COL_INVALID, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHe, _allowColMimo, _alloCol2xLTF}, |
| }, |
| [RS_MNG_COL_HE_0_8_MIMO] = |
| { |
| .mode = RS_MNG_MODUL_MIMO2, |
| .ant = TLC_MNG_CHAIN_A_MSK | TLC_MNG_CHAIN_B_MSK, |
| .gi = HE_0_8_GI, |
| .nextCols = |
| {RS_MNG_COL_HE_0_8_SISO_ANT_A, // + NON_SHARED_ANT_RFIC_ID (see _rsMngGetNextColId) |
| RS_MNG_COL_HE_1_6_MIMO, RS_MNG_COL_NON_HT_ANT_A, RS_MNG_COL_NON_HT_ANT_B, |
| RS_MNG_COL_INVALID, RS_MNG_COL_INVALID, RS_MNG_COL_INVALID}, |
| .checks = {_allowColHe, _allowColMimo, _alloCol2xLTF}, |
| }, |
| |
| }; |
| /*******************************************************************************/ |
| |
| /*******************************************************************************/ |
| /* General Helper functions */ |
| /*******************************************************************************/ |
| |
| static void _rsMngRateCheckSet(const RS_MNG_RATE_S* rsMngRate, |
| RS_MNG_RATE_SETTING_BITMAP_E setting) { |
| WARN_ON(rsMngRate->unset & setting); |
| } |
| |
| static RS_MNG_MODULATION_E _rsMngRateGetModulation(const RS_MNG_RATE_S* rsMngRate) { |
| RS_MNG_MODULATION_E modulation; |
| |
| if (IS_RATE_OFDM_VHT_API_M(rsMngRate->rate) || IS_RATE_OFDM_HT_API_M(rsMngRate->rate) || |
| IS_RATE_OFDM_HE_API_M(rsMngRate->rate)) { |
| if (GET_MIMO_INDEX_API_M(rsMngRate->rate) == SISO_INDX) { |
| modulation = RS_MNG_MODUL_SISO; |
| } else { |
| modulation = RS_MNG_MODUL_MIMO2; |
| } |
| } else { |
| modulation = RS_MNG_MODUL_LEGACY; |
| } |
| |
| _rsMngRateCheckSet(rsMngRate, RS_MNG_RATE_MODULATION); |
| return modulation; |
| } |
| |
| static void _rsMngRateSetModulation(RS_MNG_RATE_S* rsMngRate, RS_MNG_MODULATION_E mod) { |
| if (mod == RS_MNG_MODUL_MIMO2) { |
| if (IS_RATE_OFDM_VHT_API_M(rsMngRate->rate) || IS_RATE_OFDM_HE_API_M(rsMngRate->rate)) { |
| rsMngRate->rate.rate_n_flags |= RATE_MCS_VHT_MIMO2; |
| } else { |
| rsMngRate->rate.rate_n_flags |= RATE_MCS_HT_MIMO2_MSK; |
| } |
| } |
| |
| if (mod == RS_MNG_MODUL_SISO) { |
| if (IS_RATE_OFDM_VHT_API_M(rsMngRate->rate) || IS_RATE_OFDM_HE_API_M(rsMngRate->rate)) { |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_VHT_MIMO2; |
| } else { |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_HT_MIMO2_MSK; |
| } |
| } |
| |
| rsMngRate->unset &= ~RS_MNG_RATE_MODULATION; |
| rsMngRate->unset |= RS_MNG_RATE_STBC; |
| } |
| |
| static TLC_MNG_MODE_E _rsMngRateGetMode(const RS_MNG_RATE_S* rsMngRate) { |
| TLC_MNG_MODE_E rateMode; |
| |
| _rsMngRateCheckSet(rsMngRate, RS_MNG_RATE_MODE); |
| |
| if (IS_RATE_OFDM_HE_API_M(rsMngRate->rate)) { |
| rateMode = TLC_MNG_MODE_HE; |
| } else if (IS_RATE_OFDM_VHT_API_M(rsMngRate->rate)) { |
| rateMode = TLC_MNG_MODE_VHT; |
| } else if (IS_RATE_OFDM_HT_API_M(rsMngRate->rate)) { |
| rateMode = TLC_MNG_MODE_HT; |
| } else { |
| rateMode = TLC_MNG_MODE_LEGACY; |
| } |
| |
| return rateMode; |
| } |
| |
| static void _rsMngRateSetMode(RS_MNG_RATE_S* rsMngRate, TLC_MNG_MODE_E mode) { |
| /* This resets the rate completely */ |
| rsMngRate->rate.rate_n_flags = 0; |
| /* We don't really use bfer, so don't mark it as reset */ |
| rsMngRate->unset = RS_MNG_RATE_SET_ALL & ~(RS_MNG_RATE_MODE | RS_MNG_RATE_BFER); |
| |
| switch (mode) { |
| case TLC_MNG_MODE_LEGACY: |
| break; |
| case TLC_MNG_MODE_HT: |
| rsMngRate->rate.rate_n_flags |= RATE_MCS_HT_MSK; |
| break; |
| case TLC_MNG_MODE_VHT: |
| rsMngRate->rate.rate_n_flags |= RATE_MCS_VHT_MSK; |
| break; |
| case TLC_MNG_MODE_HE: |
| rsMngRate->rate.rate_n_flags |= RATE_MCS_HE_MSK; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static U32 _rsMngRateGetBw(const RS_MNG_RATE_S* rsMngRate) { |
| U32 bw = GET_BW_INDEX_API_M(rsMngRate->rate); |
| |
| _rsMngRateCheckSet(rsMngRate, RS_MNG_RATE_BW); |
| return bw; |
| } |
| |
| static void _rsMngRateSetBw(RS_MNG_RATE_S* rsMngRate, U32 bw) { |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_FAT_MSK_API_D; |
| rsMngRate->rate.rate_n_flags |= bw << RATE_MCS_FAT_POS; |
| rsMngRate->unset &= ~RS_MNG_RATE_BW; |
| } |
| |
| static RS_MNG_GI_E _rsMngRateGetGi(const RS_MNG_RATE_S* rsMngRate) { |
| _rsMngRateCheckSet(rsMngRate, RS_MNG_RATE_GI); |
| |
| if (_rsMngRateGetMode(rsMngRate) != TLC_MNG_MODE_HE) { |
| return (rsMngRate->rate.rate_n_flags & RATE_MCS_SGI_MSK) ? HT_VHT_SGI : HT_VHT_NGI; |
| } |
| |
| switch (GET_OFDM_HE_GI_LTF_INDX_API_M(rsMngRate->rate)) { |
| case 0: |
| case 1: |
| return HE_0_8_GI; |
| case 2: |
| return HE_1_6_GI; |
| case 3: |
| return HE_3_2_GI; |
| } |
| |
| return HT_VHT_NGI; // impossible |
| } |
| |
| static void _rsMngRateSetGi(RS_MNG_RATE_S* rsMngRate, RS_MNG_GI_E gi) { |
| if (_rsMngRateGetMode(rsMngRate) != TLC_MNG_MODE_HE) { |
| WARN_ON(!(gi == HT_VHT_NGI || gi == HT_VHT_SGI)); |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_SGI_MSK; |
| rsMngRate->rate.rate_n_flags |= (gi == HT_VHT_SGI) << RATE_MCS_SGI_POS; |
| } else { |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_HE_GI_LTF_MSK; |
| switch (gi) { |
| case HT_VHT_NGI: |
| case HT_VHT_SGI: |
| WARN_ON(1); |
| break; |
| case HE_0_8_GI: |
| // 2xLTF |
| rsMngRate->rate.rate_n_flags |= 1 << RATE_MCS_HE_GI_LTF_POS; |
| break; |
| case HE_1_6_GI: |
| // 2xLTF |
| rsMngRate->rate.rate_n_flags |= 2 << RATE_MCS_HE_GI_LTF_POS; |
| break; |
| case HE_3_2_GI: |
| // 4xLTF |
| rsMngRate->rate.rate_n_flags |= 3 << RATE_MCS_HE_GI_LTF_POS; |
| break; |
| } |
| } |
| |
| rsMngRate->unset &= ~RS_MNG_RATE_GI; |
| } |
| |
| static void _rsMngRateSetLdpc(RS_MNG_RATE_S* rsMngRate, BOOLEAN ldpc) { |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_LDPC_MSK; |
| rsMngRate->rate.rate_n_flags |= (!!ldpc) << RATE_MCS_LDPC_POS; |
| rsMngRate->unset &= ~RS_MNG_RATE_LDPC; |
| } |
| |
| static BOOLEAN _rsMngRateGetStbc(const RS_MNG_RATE_S* rsMngRate) { |
| _rsMngRateCheckSet(rsMngRate, RS_MNG_RATE_STBC); |
| return !!(rsMngRate->rate.rate_n_flags & RATE_MCS_STBC_MSK); |
| } |
| |
| static void _rsMngRateSetStbc(RS_MNG_RATE_S* rsMngRate, BOOLEAN stbc) { |
| WARN_ON(!(!stbc || !(rsMngRate->rate.rate_n_flags & RATE_MCS_HE_DCM_MSK))); |
| |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_STBC_MSK; |
| rsMngRate->rate.rate_n_flags |= (!!stbc) << RATE_MCS_STBC_POS; |
| rsMngRate->unset &= ~RS_MNG_RATE_STBC; |
| rsMngRate->unset |= RS_MNG_RATE_ANT; |
| } |
| |
| static void _rsMngRateSetBfer(RS_MNG_RATE_S* rsMngRate, BOOLEAN bfer) { |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_BF_MSK; |
| rsMngRate->rate.rate_n_flags |= (!!bfer) << RATE_MCS_BF_POS; |
| rsMngRate->unset &= ~RS_MNG_RATE_BFER; |
| } |
| |
| static U08 _rsMngRateGetAnt(const RS_MNG_RATE_S* rsMngRate) { |
| _rsMngRateCheckSet(rsMngRate, RS_MNG_RATE_ANT); |
| return (U08)GET_ANT_CHAIN_API_M(rsMngRate->rate); |
| ; |
| } |
| |
| static void _rsMngRateSetAnt(RS_MNG_RATE_S* rsMngRate, U08 ant) { |
| // compilation asserts to make sure tlc offload api and rate api agree |
| BUILD_BUG_ON(!(TLC_MNG_CHAIN_A_MSK == |
| SHIFT_AND_MASK(RATE_MCS_ANT_A_MSK, RATE_MCS_ANT_ABC_MSK, RATE_MCS_ANT_A_POS))); |
| BUILD_BUG_ON(!(TLC_MNG_CHAIN_B_MSK == |
| SHIFT_AND_MASK(RATE_MCS_ANT_B_MSK, RATE_MCS_ANT_ABC_MSK, RATE_MCS_ANT_A_POS))); |
| |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; |
| rsMngRate->rate.rate_n_flags |= ant << RATE_MCS_ANT_A_POS; |
| |
| rsMngRate->unset &= ~RS_MNG_RATE_ANT; |
| } |
| |
| static U08 _rsMngRateGetIdx(const RS_MNG_RATE_S* rsMngRate) { |
| return rsMngRate->idx.idx; |
| } |
| |
| static void _rsMngRateSetIdx(RS_MNG_RATE_S* rsMngRate, U08 idx) { |
| rsMngRate->idx.idx = idx; |
| |
| // DCM and STBC can't coexist. Since DCM is set here, make sure stbc has been set before setting |
| // the index, so the stbc setting could be overriden here without issue |
| _rsMngRateCheckSet(rsMngRate, RS_MNG_RATE_STBC); |
| |
| switch (_rsMngRateGetMode(rsMngRate)) { |
| case TLC_MNG_MODE_HE: |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_VHT_RATE_CODE_MSK; |
| if (idx == RS_MCS_0_HE_ER_AND_DCM) { |
| rsMngRate->rate.rate_n_flags |= |
| RATE_MCS_HE_DCM_MSK | (RATE_MCS_HE_EXT_RANGE << RATE_MCS_VHT_HE_TYPE_POS); |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_STBC_MSK; |
| } else { |
| rsMngRate->rate.rate_n_flags &= ~(RATE_MCS_HE_DCM_MSK | RATE_MCS_VHT_HE_TYPE_MSK); |
| rsMngRate->rate.rate_n_flags |= idx; |
| } |
| break; |
| case TLC_MNG_MODE_VHT: |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_VHT_RATE_CODE_MSK; |
| rsMngRate->rate.rate_n_flags |= idx; |
| break; |
| case TLC_MNG_MODE_HT: |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_HT_RATE_CODE_MSK; |
| rsMngRate->rate.rate_n_flags |= idx; |
| break; |
| case TLC_MNG_MODE_LEGACY: { |
| RS_NON_HT_RATES_E nonHtIdx = (RS_NON_HT_RATES_E)idx; |
| |
| rsMngRate->rate.rate_n_flags &= ~0xff; |
| rsMngRate->rate.rate_n_flags |= RS_NON_HT_RATE_TO_API_RATE[nonHtIdx]; |
| if (nonHtIdx <= RS_NON_HT_RATE_CCK_LAST) { |
| rsMngRate->rate.rate_n_flags |= RATE_MCS_CCK_MSK; |
| } else { |
| rsMngRate->rate.rate_n_flags &= ~RATE_MCS_CCK_MSK; |
| } |
| break; |
| } |
| default: |
| // shouldn't happen. return now so the index remains unset and an assert will be hit when |
| // trying to build the rate table with this rate |
| return; |
| } |
| |
| rsMngRate->unset &= ~RS_MNG_RATE_U_IDX; |
| } |
| |
| static void _rsMngRateInvalidate(RS_MNG_RATE_S* rsMngRate) { |
| rsMngRate->unset = RS_MNG_RATE_SET_ALL; |
| } |
| |
| static U16 _rsMngGetSupportedRatesByModeAndBw(const RS_MNG_STA_INFO_S* staInfo, |
| RS_MNG_MODULATION_E modulation, |
| TLC_MNG_CH_WIDTH_E bw) { |
| BOOLEAN isBw160; |
| U32 supportedRates; |
| |
| if (modulation == RS_MNG_MODUL_LEGACY) { return staInfo->config.nonHt; } |
| |
| isBw160 = (bw == TLC_MNG_CH_WIDTH_160MHZ); |
| supportedRates = |
| (modulation == RS_MNG_MODUL_SISO ? staInfo->config.mcs[TLC_MNG_NSS_1][isBw160] |
| : staInfo->config.mcs[TLC_MNG_NSS_2][isBw160]); |
| |
| if (staInfo->config.bestSuppMode == TLC_MNG_MODE_VHT && bw == CHANNEL_WIDTH20) { |
| // In VHT, mcs 9 is never posible at 20mhz bandwidth |
| supportedRates &= ~BIT(RS_MCS_9); |
| } |
| |
| return (U16)supportedRates; |
| } |
| |
| static U16 _rsMngGetSuppRatesSameMode(const RS_MNG_STA_INFO_S* staInfo, |
| const RS_MNG_RATE_S* rsMngRate) { |
| return _rsMngGetSupportedRatesByModeAndBw(staInfo, _rsMngRateGetModulation(rsMngRate), |
| _rsMngRateGetBw(rsMngRate)); |
| } |
| |
| static TLC_MNG_CH_WIDTH_E _rsMngGetMaxChWidth(const RS_MNG_STA_INFO_S* staInfo) { |
| return (TLC_MNG_CH_WIDTH_E)staInfo->config.maxChWidth; |
| } |
| |
| static BOOLEAN _rsMngAreAggsSupported(TLC_MNG_MODE_E bestSuppMode) { |
| return bestSuppMode > TLC_MNG_MODE_LEGACY; |
| } |
| |
| static BOOLEAN _rsMngIsDcmSupported(const RS_MNG_STA_INFO_S* staInfo, BOOLEAN isMimo) { |
| if (isMimo) { return !!(staInfo->config.configFlags & TLC_MNG_CONFIG_FLAGS_HE_DCM_NSS_2_MSK); } |
| |
| return !!(staInfo->config.configFlags & TLC_MNG_CONFIG_FLAGS_HE_DCM_NSS_1_MSK); |
| } |
| |
| static BOOLEAN _rsMngRateIsOptimal(const RS_MNG_STA_INFO_S* staInfo, |
| const RS_MNG_RATE_S* rsMngRate) { |
| U32 bw = _rsMngRateGetBw(rsMngRate); |
| BOOLEAN mimoAllowed = staInfo->config.mcs[TLC_MNG_NSS_2][bw == CHANNEL_WIDTH160]; |
| |
| if (_rsMngRateGetMode(rsMngRate) != staInfo->config.bestSuppMode) { return FALSE; } |
| |
| if (_rsMngRateGetIdx(rsMngRate) != MSB2ORD(_rsMngGetSuppRatesSameMode(staInfo, rsMngRate))) { |
| return FALSE; |
| } |
| |
| // TODO: check for best ltf/gi in HE. This condition currently means that tpc won't be enabled |
| // in HE. |
| if ((staInfo->config.sgiChWidthSupport & BIT(bw)) && _rsMngRateGetGi(rsMngRate) != HT_VHT_SGI) { |
| return FALSE; |
| } |
| |
| if (mimoAllowed && _rsMngRateGetModulation(rsMngRate) != RS_MNG_MODUL_MIMO2) { return FALSE; } |
| |
| if (bw != _rsMngGetMaxChWidth(staInfo)) { return FALSE; } |
| |
| return TRUE; |
| } |
| |
| static U08 _rsMngGetHigherRateIdx(U08 initRateIdx, U32 supportedRatesMsk) { |
| U32 tmpRateMsk; |
| |
| if (initRateIdx == RS_MCS_0_HE_ER_AND_DCM) { return (U08)LSB2ORD(supportedRatesMsk); } |
| |
| tmpRateMsk = supportedRatesMsk & (0xFFFFFFFF << (initRateIdx + 1)); |
| |
| return (U08)(tmpRateMsk == 0 ? RS_MNG_INVALID_RATE_IDX : LSB2ORD(tmpRateMsk)); |
| } |
| |
| static U08 _rsMngGetLowerRateIdx(const RS_MNG_STA_INFO_S* staInfo, const RS_MNG_RATE_S* rate, |
| U32 supportedRatesMsk) { |
| U08 idx = _rsMngRateGetIdx(rate); |
| U32 tmpRateMsk = (supportedRatesMsk & ((1 << idx) - 1)); |
| |
| if (idx == RS_MCS_0_HE_ER_AND_DCM) { return RS_MNG_INVALID_RATE_IDX; } |
| |
| if (tmpRateMsk == 0) { |
| if (_rsMngRateGetMode(rate) == TLC_MNG_MODE_HE && |
| _rsMngRateGetBw(rate) == CHANNEL_WIDTH20 && |
| _rsMngIsDcmSupported(staInfo, _rsMngRateGetModulation(rate) == RS_MNG_MODUL_MIMO2)) { |
| return RS_MCS_0_HE_ER_AND_DCM; |
| } |
| |
| return RS_MNG_INVALID_RATE_IDX; |
| } |
| |
| return (U08)MSB2ORD(tmpRateMsk); |
| } |
| |
| // rs_get_adjacent_rate |
| // get adjacent supported rate |
| // suppRateDir : use GET_HIGHER_SUPPORTED_RATE or GET_LOWER_SUPPORTED_RATE |
| static U08 _rsMngGetAdjacentRateIdx(const RS_MNG_STA_INFO_S* staInfo, const RS_MNG_RATE_S* initRate, |
| U08 suppRateDir) { |
| U08 initRateIdx = _rsMngRateGetIdx(initRate); |
| U32 supportedRatesMsk = _rsMngGetSuppRatesSameMode(staInfo, initRate); |
| |
| return (U08)(suppRateDir == GET_LOWER_SUPPORTED_RATE |
| ? _rsMngGetLowerRateIdx(staInfo, initRate, supportedRatesMsk) |
| : _rsMngGetHigherRateIdx(initRateIdx, supportedRatesMsk)); |
| } |
| |
| // TODO - check. what if bt doesn't allow? |
| static BOOLEAN _rsMngIsStbcSupported(const RS_MNG_STA_INFO_S* staInfo) { |
| return !!(staInfo->config.configFlags & TLC_MNG_CONFIG_FLAGS_STBC_MSK); |
| } |
| |
| static BOOLEAN _rsMngIsStbcAllowed(const RS_MNG_STA_INFO_S* staInfo, const RS_MNG_RATE_S* rate) { |
| if ((iwl_mvm_get_valid_tx_ant(staInfo->mvm) & rsMngGetDualAntMsk()) != rsMngGetDualAntMsk()) { |
| return FALSE; |
| } |
| return _rsMngIsStbcSupported(staInfo) && !(rate->rate.rate_n_flags & RATE_MCS_HE_DCM_MSK); |
| } |
| |
| static BOOLEAN _rsMngCoexIsLongAggAllowed(const RS_MNG_STA_INFO_S* staInfo) { |
| if (staInfo->config.band != NL80211_BAND_2GHZ) { return TRUE; } |
| |
| if (btCoexManagerBtOwnsAnt(staInfo->mvm)) { return FALSE; } |
| |
| return TRUE; |
| } |
| |
| static BOOLEAN _rsMngIsLdpcAllowed(const RS_MNG_STA_INFO_S* staInfo) { |
| return !!(staInfo->config.configFlags & TLC_MNG_CONFIG_FLAGS_LDPC_MSK); |
| } |
| |
| static BOOLEAN _rsMngIsAntSupported(const RS_MNG_STA_INFO_S* staInfo, U08 ant) { |
| return (ant & staInfo->config.chainsEnabled) == ant && |
| (iwl_mvm_get_valid_tx_ant(staInfo->mvm) & ant) == ant; |
| } |
| /*******************************************************************************/ |
| |
| /************************************************************************************/ |
| /* allowColFuncs */ |
| /************************************************************************************/ |
| |
| static BOOLEAN _allowColAnt(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol) { |
| if (!_rsMngIsAntSupported(staInfo, nextCol->ant)) { return FALSE; } |
| |
| if (!_rsMngIsAntSupported(staInfo, (U08)(nextCol->ant ^ rsMngGetDualAntMsk()))) { |
| // If the other antenna is disabled for some reason, this antenna is the only one allowed so |
| // we must ignore possible BT-Coex restrictions. Also note that this function is only called |
| // for siso columns, so nextCol->ant always has just one bit set so the xor makes sense. |
| return TRUE; |
| } |
| |
| if (staInfo->config.band != NL80211_BAND_2GHZ) { return TRUE; } |
| |
| if (btCoexManagerIsAntAvailable(staInfo->mvm, nextCol->ant)) { return TRUE; } |
| |
| return FALSE; |
| } |
| |
| static U16 _rsMngGetAggTimeLimit(RS_MNG_STA_INFO_S* staInfo) { |
| // Someone configured debug values, use them no matter what |
| if (staInfo->aggDurationLimit != RS_MNG_AGG_DURATION_LIMIT) { |
| return staInfo->aggDurationLimit; |
| } |
| |
| if (_rsMngCoexIsLongAggAllowed(staInfo)) { |
| staInfo->longAggEnabled = TRUE; |
| return RS_MNG_AGG_DURATION_LIMIT; |
| } |
| |
| staInfo->longAggEnabled = FALSE; |
| return RS_MNG_AGG_DURATION_LIMIT_SHORT; |
| } |
| |
| static BOOLEAN _allowColMimo(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol) { |
| BOOLEAN isBw160 = (bw == TLC_MNG_CH_WIDTH_160MHZ); |
| |
| // TODO - check if ht/vht supported? redundent |
| // if no mimo rate is supported |
| if (!(staInfo->config.mcs[TLC_MNG_NSS_2][isBw160])) { return FALSE; } |
| |
| if (staInfo->config.chainsEnabled != rsMngGetDualAntMsk()) { return FALSE; } |
| |
| if (iwl_mvm_get_valid_tx_ant(staInfo->mvm) != rsMngGetDualAntMsk()) { return FALSE; } |
| |
| return TRUE; |
| } |
| |
| static BOOLEAN _allowColSiso(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol) { |
| BOOLEAN isBw160 = (bw == TLC_MNG_CH_WIDTH_160MHZ); |
| |
| // if there are supported SISO rates - return true. else - return false |
| return (!!(staInfo->config.mcs[TLC_MNG_NSS_1][isBw160])); |
| } |
| |
| static BOOLEAN _allowColHe(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol) { |
| return !!(staInfo->config.bestSuppMode == TLC_MNG_MODE_HE); |
| } |
| |
| static BOOLEAN _allowColHtVht(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol) { |
| return !!(staInfo->config.bestSuppMode == TLC_MNG_MODE_HT || |
| staInfo->config.bestSuppMode == TLC_MNG_MODE_VHT); |
| } |
| |
| static BOOLEAN _allowColSgi(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol) { |
| U08 sgiChWidthSupport = staInfo->config.sgiChWidthSupport; |
| |
| return !!(sgiChWidthSupport & BIT(bw)); |
| } |
| |
| static BOOLEAN _alloCol2xLTF(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol) { |
| return !(staInfo->config.configFlags & TLC_MNG_CONFIG_FLAGS_HE_BLOCK_2X_LTF_MSK); |
| } |
| |
| /***************************************************************/ |
| |
| static BOOLEAN _rsMngTpcIsActive(const RS_MNG_STA_INFO_S* staInfo) { |
| // There are 2 values for currStep that mean tpc isn't working currently - RS_MNG_TPC_INACTIVE |
| // and RS_MNG_TPC_DISABLED. |
| return staInfo->tpcTable.currStep < RS_MNG_TPC_NUM_STEPS; |
| } |
| static BOOLEAN _rsMngIsTestWindow(const RS_MNG_STA_INFO_S* staInfo) { |
| return staInfo->tryingRateUpscale || staInfo->searchBetterTbl || staInfo->tpcTable.testing; |
| } |
| |
| static void _rsMngFillAggParamsLQCmd(RS_MNG_STA_INFO_S* staInfo, struct iwl_lq_cmd* lqCmd) { |
| lqCmd->agg_time_limit = cpu_to_le16(_rsMngGetAggTimeLimit(staInfo)); |
| lqCmd->agg_disable_start_th = RS_MNG_AGG_DISABLE_START_TH; |
| |
| // W/A for a HW bug that causes it to not prepare a second burst if the first one uses |
| // all frames in the Fifo. W/A this by making sure there's always at least one frame left. |
| lqCmd->agg_frame_cnt_limit = (U08)(staInfo->staBuffSize - 1); |
| } |
| |
| // Get the next supported lower rate in the current column. |
| // return: |
| // the found rate index, or |
| // RS_MNG_INVALID_RATE_IDX if no such rate exists |
| static U08 _rsMngSetLowerRate(const RS_MNG_STA_INFO_S* staInfo, RS_MNG_RATE_S* rsMngRate) { |
| U08 lowerSuppRateIdx = _rsMngGetAdjacentRateIdx(staInfo, rsMngRate, GET_LOWER_SUPPORTED_RATE); |
| |
| // if this is the lowest rate possible and this is not legacy rate - break; |
| if (RS_MNG_INVALID_RATE_IDX != lowerSuppRateIdx) { |
| _rsMngRateSetIdx(rsMngRate, lowerSuppRateIdx); |
| } |
| |
| return lowerSuppRateIdx; |
| } |
| |
| static void tlcMngNotifyAmsdu(const RS_MNG_STA_INFO_S* staInfo, U16 amsduSize, U16 tidBitmap) { |
| int i; |
| |
| staInfo->mvmsta->amsdu_enabled = tidBitmap; |
| staInfo->mvmsta->max_amsdu_len = amsduSize; |
| staInfo->sta->max_rc_amsdu_len = staInfo->mvmsta->max_amsdu_len; |
| |
| for (i = 0; i < IWL_MAX_TID_COUNT; i++) { |
| if (staInfo->mvmsta->amsdu_enabled & BIT(i)) |
| staInfo->sta->max_tid_amsdu_len[i] = |
| iwl_mvm_max_amsdu_size(staInfo->mvm, staInfo->sta, i); |
| else |
| /* |
| * Not so elegant, but this will effectively |
| * prevent AMSDU on this TID |
| */ |
| { |
| staInfo->sta->max_tid_amsdu_len[i] = 1; |
| } |
| } |
| } |
| |
| static void _rsMngFillNonHtRates(const RS_MNG_STA_INFO_S* staInfo, struct iwl_lq_cmd* lqCmd, U08 i, |
| RS_MNG_RATE_S* rsMngRate) { |
| BOOLEAN togglingPossible = _rsMngIsAntSupported(staInfo, rsMngGetDualAntMsk()) && |
| btCoexManagerIsAntAvailable(staInfo->mvm, BT_COEX_SHARED_ANT_ID); |
| |
| if (_rsMngRateGetMode(rsMngRate) != TLC_MNG_MODE_LEGACY) { |
| U08 currIdx = _rsMngRateGetIdx(rsMngRate); |
| |
| _rsMngRateSetMode(rsMngRate, TLC_MNG_MODE_LEGACY); |
| _rsMngRateSetModulation(rsMngRate, RS_MNG_MODUL_LEGACY); |
| _rsMngRateSetBw(rsMngRate, CHANNEL_WIDTH20); |
| _rsMngRateSetLdpc(rsMngRate, FALSE); |
| _rsMngRateSetStbc(rsMngRate, FALSE); |
| |
| // Always start with the non-shared antenna if it's available. If there's toggling, it |
| // doesn't make much difference, and if there's no toggling due to bt-coex it promises we'll |
| // stay on the non-shared antenna. |
| _rsMngRateSetAnt(rsMngRate, rsMngGetSingleAntMsk(staInfo->config.chainsEnabled)); |
| _rsMngRateSetIdx(rsMngRate, downColMcsToLegacy[currIdx]); |
| } else { |
| _rsMngSetLowerRate(staInfo, rsMngRate); |
| } |
| |
| for (; i < LQ_MAX_RETRY_NUM; i++) { |
| lqCmd->rs_table[i] = cpu_to_le32(rsMngRate->rate.rate_n_flags); |
| |
| _rsMngSetLowerRate(staInfo, rsMngRate); |
| |
| if (togglingPossible) { |
| _rsMngRateSetAnt(rsMngRate, (U08)(_rsMngRateGetAnt(rsMngRate) ^ rsMngGetDualAntMsk())); |
| } |
| } |
| } |
| |
| static void _rsMngBuildRatesTbl(const RS_MNG_STA_INFO_S* staInfo, struct iwl_lq_cmd* lqCmd) { |
| RS_MNG_RATE_S rsMngRate; |
| U08 i = 0; |
| U08 j; |
| |
| memcpy(&rsMngRate, &staInfo->rateTblInfo.rsMngRate, sizeof(rsMngRate)); |
| |
| if (staInfo->searchBetterTbl) { |
| // When trying a new column, only the initial rate should be of that column. The rest of the |
| // table is constructed from the "stable" column. |
| lqCmd->rs_table[0] = cpu_to_le32(staInfo->searchColData.rsMngRate.rate.rate_n_flags); |
| i++; |
| } else if (staInfo->tryingRateUpscale) { |
| // When trying a higher mcs, try it only once. The next retries will be from the previous |
| // mcs which is known to be good (otherwise wouldn't be trying a higher one). |
| lqCmd->rs_table[0] = cpu_to_le32(staInfo->rateTblInfo.rsMngRate.rate.rate_n_flags); |
| i++; |
| _rsMngSetLowerRate(staInfo, &rsMngRate); |
| } |
| |
| // Fill RS_MNG_RETRY_TABLE_INITIAL_RATE_NUM copies of the best known stable rate |
| for (j = 0; j < RS_MNG_RETRY_TABLE_INITIAL_RATE_NUM; j++) { |
| lqCmd->rs_table[i + j] = cpu_to_le32(rsMngRate.rate.rate_n_flags); |
| } |
| i += j; |
| |
| if (!(staInfo->searchBetterTbl || staInfo->tryingRateUpscale) && |
| _rsMngSetLowerRate(staInfo, &rsMngRate) != RS_MNG_INVALID_RATE_IDX) { |
| // In case the first rate is not a test rate, put here RS_MNG_RETRY_TABLE_SECONDARY_RATE_NUM |
| // copies of the initial rate but mcs-1 |
| // Note that a tpc test window is not treated as a test rate for the purpose of construction |
| // of the retry table. |
| for (j = 0; j < RS_MNG_RETRY_TABLE_SECONDARY_RATE_NUM; j++) { |
| lqCmd->rs_table[i + j] = cpu_to_le32(rsMngRate.rate.rate_n_flags); |
| } |
| i += j; |
| |
| // Now put RS_MNG_RETRY_TABLE_SECONDARY_RATE_20MHZ_NUM copies of the secondary rate with |
| // 20mhz bandwidth. |
| _rsMngRateSetBw(&rsMngRate, CHANNEL_WIDTH20); |
| for (j = 0; j < RS_MNG_RETRY_TABLE_SECONDARY_RATE_20MHZ_NUM; j++) { |
| lqCmd->rs_table[i + j] = cpu_to_le32(rsMngRate.rate.rate_n_flags); |
| } |
| i += j; |
| } |
| |
| // Fill the rest of the retry table with non-ht rates |
| _rsMngFillNonHtRates(staInfo, lqCmd, i, &rsMngRate); |
| } |
| |
| static void _rsMngFillLQCmd(RS_MNG_STA_INFO_S* staInfo, struct iwl_lq_cmd* lqCmd) { |
| int i; |
| |
| memset(lqCmd, 0, sizeof(*lqCmd)); |
| lqCmd->sta_id = staInfo->mvmsta->sta_id; |
| |
| if (_rsMngTpcIsActive(staInfo)) { |
| // reduce Tx power in steps of 3db. Note that currStep == 0 means reduce 3db, hence the '+1' |
| lqCmd->reduced_tpc = (U08)(RS_MNG_TPC_STEP_SIZE * (staInfo->tpcTable.currStep + 1)); |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngFillLQCmd: reducing tx power by %d db", |
| lqCmd->reduced_tpc); |
| } |
| |
| _rsMngFillAggParamsLQCmd(staInfo, lqCmd); |
| |
| _rsMngBuildRatesTbl(staInfo, lqCmd); |
| |
| lqCmd->single_stream_ant_msk = rsMngGetSingleAntMsk(staInfo->config.chainsEnabled); |
| lqCmd->dual_stream_ant_msk = rsMngGetDualAntMsk(); |
| |
| if (_rsMngIsTestWindow(staInfo)) { |
| if (IS_RATE_OFDM_HT_VHT_HE_API_M(le32_to_cpu(lqCmd->rs_table[0]))) { |
| // For 11a/b/g rates, where there are no aggregations anyway, RTS protection just hurts |
| // the tpt. |
| lqCmd->rs_table[0] |= cpu_to_le32(RATE_MCS_RTS_REQUIRED_MSK); |
| // TODO: lqCmd->agg_params.uAggFrameCntInTestWin = RS_MNG_UPSCALE_AGG_FRAME_COUNT; |
| } |
| // TODO: lqCmd->general_params.flags |= LINK_QUAL_FLAGS_TEST_WINDOWS_MSK; |
| } |
| |
| if (staInfo->mvmsta->tx_protection) { lqCmd->flags |= LQ_FLAG_USE_RTS_MSK; } |
| |
| // When Amsdu's are enabled, enable RTS protection for all rates that use A-MPDUs, since in this |
| // case there could be really long frames and this should help reduce collisions. |
| if (staInfo->amsduEnabledSize != RS_MNG_AMSDU_INVALID) { |
| for (i = 0; i < RS_MNG_AGG_DISABLE_START_TH; i++) { |
| lqCmd->rs_table[i] |= cpu_to_le32(RATE_MCS_RTS_REQUIRED_MSK); |
| } |
| } |
| } |
| |
| // rs_update_rate_tbl |
| static void _rsMngUpdateRateTbl(RS_MNG_STA_INFO_S* staInfo, BOOLEAN notifyHost) { |
| _rsMngFillLQCmd(staInfo, &staInfo->mvmsta->lq_sta.rs_drv.lq); |
| |
| iwl_mvm_send_lq_cmd(staInfo->mvm, &staInfo->mvmsta->lq_sta.rs_drv.lq, !staInfo->enabled); |
| } |
| |
| static void _rsMngClearWinArr(RS_MNG_WIN_STAT_S* winArr, U08 numWin) { |
| U08 i; |
| |
| _memclr(winArr, (sizeof(*winArr) * numWin)); |
| |
| for (i = 0; i < numWin; i++) { |
| winArr[i].successRatio = RS_MNG_INVALID_VAL; |
| winArr[i].averageTpt = RS_MNG_INVALID_VAL; |
| } |
| } |
| |
| static void _rsMngClearTblWindows(RS_MNG_STA_INFO_S* staInfo) { |
| _rsMngClearWinArr(staInfo->rateTblInfo.win, RS_MNG_MAX_RATES_NUM); |
| |
| _rsMngClearWinArr(staInfo->tpcTable.windows, RS_MNG_TPC_NUM_STEPS); |
| } |
| |
| static void _rsMngSetVisitedColumn(RS_MNG_STA_INFO_S* staInfo, RS_MNG_COLUMN_DESC_E colId) { |
| // to make the code for setting both siso columns in case of stbc simpler, make sure that each |
| // such pair of columns has only bit 0 different. |
| BUILD_BUG_ON(!((RS_MNG_COL_SISO_ANT_A ^ RS_MNG_COL_SISO_ANT_B) == 1)); |
| BUILD_BUG_ON(!((RS_MNG_COL_SISO_ANT_A_SGI ^ RS_MNG_COL_SISO_ANT_B_SGI) == 1)); |
| BUILD_BUG_ON(!((RS_MNG_COL_HE_3_2_SISO_ANT_A ^ RS_MNG_COL_HE_3_2_SISO_ANT_B) == 1)); |
| BUILD_BUG_ON(!((RS_MNG_COL_HE_1_6_SISO_ANT_A ^ RS_MNG_COL_HE_1_6_SISO_ANT_B) == 1)); |
| BUILD_BUG_ON(!((RS_MNG_COL_HE_0_8_SISO_ANT_A ^ RS_MNG_COL_HE_0_8_SISO_ANT_B) == 1)); |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngSetVisitedColumn: colId %d, stbc allowed %d, visited columns 0x%x", colId, |
| _rsMngIsStbcSupported(staInfo), staInfo->visitedColumns); |
| |
| staInfo->visitedColumns |= BIT(colId); |
| if (rsMngColumns[colId].mode == RS_MNG_MODUL_SISO && _rsMngIsStbcSupported(staInfo)) { |
| staInfo->visitedColumns |= BIT(colId ^ 1); |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngSetVisitedColumn: visited columns 0x%x", |
| staInfo->visitedColumns); |
| } |
| |
| static U32 _rsMngVhtRateToPhyRate(U32 bw, RS_MCS_E mcs, RS_MNG_GI_E gi, RS_MNG_MODULATION_E nss) { |
| U32 bitrate; |
| |
| if (WARN_ON(!(mcs < 10 && bw < MAX_CHANNEL_BW_INDX && nss >= RS_MNG_MODUL_SISO))) { return 1; } |
| |
| bitrate = rsMngVhtRateToBps[bw][mcs]; |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngAmsduRate: bw %d, mcs %d sgi %d nss %d", bw, mcs, |
| gi, nss); |
| |
| if (nss == RS_MNG_MODUL_MIMO2) { bitrate *= 2; } |
| |
| if (gi == HT_VHT_SGI) { bitrate = bitrate + (bitrate / 9); } |
| |
| return bitrate >> 20; |
| } |
| |
| static U32 _rsMngHeRateToPhyRate(U32 bw, RS_MCS_E mcs, RS_MNG_GI_E gi, RS_MNG_MODULATION_E nss) { |
| #define RATIO_SCALE 2048 |
| #define MBPS_X_10_TO_KBPS(x) (((x) << 10) / 10) |
| static const U16 mcsRatios[12] = { |
| 34133, /* 16.666666... */ |
| 17067, /* 8.333333... */ |
| 11378, /* 5.555555... */ |
| 8533, /* 4.166666... */ |
| 5689, /* 2.777777... */ |
| 4267, /* 2.083333... */ |
| 3923, /* 1.851851... */ |
| 3413, /* 1.666666... */ |
| 2844, /* 1.388888... */ |
| 2560, /* 1.250000... */ |
| 2276, /* 1.111111... */ |
| 2048, /* 1.000000... */ |
| }; |
| static const U32 ratesPerGi[][3] = { |
| // phy rate in kbps per GI for mcs 11 with 2 ss |
| // HE_3_2_GI HE_1_6_GI HE_0_8_GI |
| [CHANNEL_WIDTH20] = {MBPS_X_10_TO_KBPS(2438), MBPS_X_10_TO_KBPS(2708), |
| MBPS_X_10_TO_KBPS(2868)}, |
| [CHANNEL_WIDTH40] = {MBPS_X_10_TO_KBPS(4875), MBPS_X_10_TO_KBPS(5417), |
| MBPS_X_10_TO_KBPS(5735)}, |
| [CHANNEL_WIDTH80] = {MBPS_X_10_TO_KBPS(10208), MBPS_X_10_TO_KBPS(11343), |
| MBPS_X_10_TO_KBPS(12010)}, |
| [CHANNEL_WIDTH160] = {MBPS_X_10_TO_KBPS(20416), MBPS_X_10_TO_KBPS(22685), |
| MBPS_X_10_TO_KBPS(24019)}, |
| }; |
| U64 tmp; |
| U32 bitrate; |
| BOOLEAN isDcm = FALSE; |
| |
| if (mcs == RS_MCS_0_HE_ER_AND_DCM) { |
| isDcm = TRUE; |
| mcs = RS_MCS_0; |
| } |
| |
| if (WARN_ON(!(mcs < 12 && bw < MAX_CHANNEL_BW_INDX && gi >= HE_FIRST_GI && |
| nss >= RS_MNG_MODUL_SISO))) { |
| return 1; |
| } |
| |
| bitrate = ratesPerGi[bw][gi - HE_FIRST_GI]; |
| tmp = bitrate; |
| tmp *= RATIO_SCALE; |
| tmp /= mcsRatios[mcs]; |
| bitrate = (U32)tmp; |
| |
| if (nss == RS_MNG_MODUL_SISO) { bitrate /= 2; } |
| |
| if (isDcm) { bitrate /= 2; } |
| |
| return bitrate >> 10; |
| } |
| |
| static U32 _rsMngRateToPhyRate(TLC_MNG_MODE_E mode, U32 bw, RS_MCS_E mcs, RS_MNG_GI_E gi, |
| RS_MNG_MODULATION_E nss) { |
| if (mode == TLC_MNG_MODE_VHT) { return _rsMngVhtRateToPhyRate(bw, mcs, gi, nss); } |
| if (mode == TLC_MNG_MODE_HE) { return _rsMngHeRateToPhyRate(bw, mcs, gi, nss); } |
| |
| return 0; |
| } |
| |
| static RS_MNG_TX_AMSDU_SIZE_E _rsMngAmsduSize(const RS_MNG_STA_INFO_S* staInfo, TLC_MNG_MODE_E mode, |
| U32 bw, RS_MCS_E mcs, RS_MNG_GI_E gi, |
| RS_MNG_MODULATION_E nss) { |
| RS_MNG_TX_AMSDU_SIZE_E amsdu_3k, amsdu_5k, amsdu_6k, amsdu_8k; |
| U32 phyRate = _rsMngRateToPhyRate(mode, bw, mcs, gi, nss); |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngAmsduRate: sta %d, phyRate %d, blacklist bitmap 0x%X", |
| _rsMngStaInfoToStaId(staInfo), phyRate, staInfo->amsduBlacklist); |
| |
| amsdu_3k = RS_MNG_AMSDU_3500B; |
| amsdu_5k = RS_MNG_AMSDU_5000B; |
| |
| if (staInfo->amsduBlacklist) { |
| // If we disabled 3k AMSDU - don't use it at all |
| if (staInfo->amsduBlacklist & BIT(RS_MNG_AMSDU_3500B)) { amsdu_3k = RS_MNG_AMSDU_INVALID; } |
| |
| // If we disabled some amsdu size, use a smaller size. |
| // Note that smaller sizes that are blacklisted as well will still not be used. |
| if (staInfo->amsduBlacklist & BIT(RS_MNG_AMSDU_5000B)) { amsdu_5k = amsdu_3k; } |
| } |
| |
| if (mode == TLC_MNG_MODE_HE) { |
| amsdu_6k = RS_MNG_AMSDU_6500B; |
| amsdu_8k = RS_MNG_AMSDU_8000B; |
| |
| if (staInfo->amsduBlacklist & BIT(RS_MNG_AMSDU_6500B)) { amsdu_6k = amsdu_5k; } |
| |
| if (staInfo->amsduBlacklist & BIT(RS_MNG_AMSDU_8000B)) { amsdu_8k = amsdu_6k; } |
| |
| if (phyRate > RS_MNG_AMSDU_HE_8K_THRESHOLD) { return amsdu_8k; } |
| |
| if (phyRate > RS_MNG_AMSDU_HE_6K_THRESHOLD) { return amsdu_6k; } |
| } |
| |
| if (phyRate > RS_MNG_AMSDU_5K_THRESHOLD) { return amsdu_5k; } |
| |
| if (phyRate > RS_MNG_AMSDU_3K_THRESHOLD) { return amsdu_3k; } |
| |
| return RS_MNG_AMSDU_INVALID; |
| } |
| |
| static const TPT_BY_RATE_ARR* _rsMngGetExpectedTptTable(const RS_MNG_COL_ELEM_S* col, |
| TLC_MNG_CH_WIDTH_E bw, BOOLEAN isAgg) { |
| U32 nss; |
| U32 gi; |
| |
| if (col->mode == RS_MNG_MODUL_LEGACY) { return &expectedTptNonHt; } |
| |
| nss = col->mode == RS_MNG_MODUL_SISO ? RS_MNG_SISO : RS_MNG_MIMO; |
| |
| switch (col->gi) { |
| case HT_VHT_NGI: |
| gi = RS_MNG_NGI; |
| break; |
| case HT_VHT_SGI: |
| gi = RS_MNG_SGI; |
| break; |
| case HE_3_2_GI: |
| gi = RS_MNG_GI_3_2; |
| break; |
| case HE_1_6_GI: |
| gi = RS_MNG_GI_1_6; |
| break; |
| case HE_0_8_GI: |
| gi = RS_MNG_GI_0_8; |
| break; |
| default: |
| WARN_ON(1); |
| gi = 0; |
| } |
| |
| DBG_PRINTF( |
| UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetExpectedTptTable: expected Tpt table - isHE %d, isAgg %d, BW %d, GI %d, NSS %d", |
| col->gi >= HE_FIRST_GI, isAgg, bw, gi, nss); |
| |
| if (col->gi < HE_FIRST_GI) { return &expectedTptHtVht[isAgg][bw][gi][nss]; } |
| |
| return &expectedTptHe[isAgg][bw][gi][nss]; |
| } |
| |
| static U32 _rsMngGetExpectedTpt(const RS_MNG_STA_INFO_S* staInfo, const RS_MNG_COL_ELEM_S* col, |
| TLC_MNG_CH_WIDTH_E bw, BOOLEAN isAgg, RS_MCS_E rateIdx) { |
| const TPT_BY_RATE_ARR* expectedTptTable = _rsMngGetExpectedTptTable(col, bw, isAgg); |
| U32 ret; |
| |
| if (expectedTptTable == &expectedTptNonHt) { return (*expectedTptTable)[rateIdx]; } |
| if (rateIdx == RS_MCS_0_HE_ER_AND_DCM) { |
| // rateIdx == RS_MCS_0_HE_ER_AND_DCM. DCM cuts expected tpt in half. |
| // TODO: add an additional small penalty for ER |
| return (*expectedTptTable)[RS_MCS_0] / 2; |
| } |
| |
| ret = (*expectedTptTable)[rateIdx]; |
| |
| if (staInfo->amsduSupport && staInfo->mvmsta->agg_tids && staInfo->amsduInAmpdu) { |
| switch (staInfo->amsduEnabledSize) { |
| case RS_MNG_AMSDU_8000B: |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetExpectedTpt: adding 50%% thanks to 8k amsdu"); |
| ret += (ret / 2); |
| break; |
| case RS_MNG_AMSDU_6500B: |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetExpectedTpt: adding 37.5%% thanks to 6.5k amsdu"); |
| ret += (ret / 4) + (ret / 8); |
| break; |
| case RS_MNG_AMSDU_5000B: |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetExpectedTpt: adding 25%% thanks to 5k amsdu"); |
| ret += (ret / 4); |
| break; |
| case RS_MNG_AMSDU_3500B: |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetExpectedTpt: adding 12.5%% thanks to 3.5k amsdu"); |
| ret += (ret / 8); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| static BOOLEAN _isAvgTptCalcPossible(RS_MNG_WIN_STAT_S* win) { |
| return ((win->successCounter >= RS_MNG_RATE_MIN_SUCCESS_TH) || |
| ((win->framesCounter - win->successCounter) >= RS_MNG_RATE_MIN_FAILURE_TH)); |
| } |
| |
| // rs_get_rate_action |
| // |
| // return after every if. so the 'strength' of the conditions are as follows : |
| // |
| // Downscale if: |
| // - the current window success ratio <= 15% || avg tpt for this window is 0 |
| // Upscale if: |
| // - low and high tpt (tpt for the next/prev rates) are invalid *but* the next rate is valid |
| // - low_tpt < current_tpt (tpt for prev rate < avg tpt in current window ) *and* |
| // next rate is valid although it's tpt isn't |
| // - high tpt > current tpt |
| // Stay if: |
| // - low and high tpt < current tpt |
| //----- but if none of the above conditions are met. for example - due to invalid high/low tpt : |
| // if low_tpt > current tpt or low tpt is invalid, but the lower index entry is : |
| // - if low is valid: |
| // Stay if: |
| // - the current suceess rate is >= 85% (good enough) |
| // - current tpt > expected tpt at [low] (was supposed to do downscale, but current tpt is good |
| // enough although the success rate isn't. so we are optomistics) |
| // Downscale: |
| // - both above are not met |
| // - low is invalid |
| static RS_MNG_ACTION_E _rsMngGetScaleAction(const RS_MNG_STA_INFO_S* staInfo, |
| const RS_MNG_WIN_STAT_S* currWin, U32 lowerRateIdx, |
| U32 higherRateIdx) { |
| const RS_MNG_TBL_INFO_S* tblInfo = &staInfo->rateTblInfo; |
| U32 currTpt; |
| U32 lowTpt; |
| U32 highTpt; |
| RS_MNG_ACTION_E action = RS_MNG_ACTION_STAY; |
| enum { |
| RS_MNG_SCALE_REASON_BELOW_FORCE_DECREASE, |
| RS_MNG_SCALE_REASON_NO_DATA_ON_HIGHER_RATE, |
| RS_MNG_SCALE_REASON_HIGHER_RATE_HAS_HIGHER_TPT, |
| RS_MNG_SCALE_REASON_CURRENT_RATE_HAS_HIGHEST_TPT, |
| RS_MNG_SCALE_REASON_SR_ABOVE_NO_DECREASE_THRESHOLD, |
| RS_MNG_SCALE_REASON_LOWER_RATE_TPT_UNKOWN_OR_BETTER, |
| RS_MNG_SCALE_REASON_DEFAULT, |
| }; |
| |
| currTpt = currWin->averageTpt; |
| lowTpt = ((lowerRateIdx == RS_MNG_INVALID_RATE_IDX) ? RS_MNG_INVALID_VAL |
| : tblInfo->win[lowerRateIdx].averageTpt); |
| highTpt = ((higherRateIdx == RS_MNG_INVALID_RATE_IDX) ? RS_MNG_INVALID_VAL |
| : tblInfo->win[higherRateIdx].averageTpt); |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetScaleAction:\ncurrTpt: %d, successRatio: %d,\nlowerRateIdx: %d, lowTpt: " |
| "%d,\nhigherRateIdx: %d, highTpt: %d", |
| currTpt, currWin->successRatio, lowerRateIdx, lowTpt, higherRateIdx, highTpt); |
| |
| // current Success ratio is insufficient or Tpt for the current window is 0 => downscale |
| if ((currWin->successRatio <= RS_MNG_PERCENT(RS_MNG_SR_FORCE_DECREASE)) || (0 == currTpt)) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetScaleAction: DOWNSCALE due to insufficient success ratio or 0 tpt"); |
| action = RS_MNG_ACTION_DOWNSCALE; |
| goto out; |
| } |
| |
| // No Tpt data about high/low rate => upscale |
| if ((RS_MNG_INVALID_VAL == lowTpt) && (RS_MNG_INVALID_VAL == highTpt) && |
| (RS_MNG_INVALID_RATE_IDX != higherRateIdx)) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetScaleAction: UPSCALE due to no data about higher or lower rates"); |
| action = RS_MNG_ACTION_UPSCALE; |
| goto out; |
| } |
| |
| // if there's no Tpt data about the higerRateIdx but the Tpt for the lowerRateIdx is worse then |
| // the curr tpt => Upscale |
| if (((RS_MNG_INVALID_VAL == highTpt) && (RS_MNG_INVALID_RATE_IDX != higherRateIdx)) && |
| ((RS_MNG_INVALID_VAL != lowTpt) && (lowTpt < currTpt))) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetScaleAction: UPSCALE due to no data on higher rate and lower rate has " |
| "worse tpt"); |
| action = RS_MNG_ACTION_UPSCALE; |
| goto out; |
| } |
| |
| // if higherRateIdx tpt > currTpt => upscale |
| if ((RS_MNG_INVALID_VAL != highTpt) && (highTpt > currTpt)) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetScaleAction: UPSCALE due to higher rate having higher tpt"); |
| action = RS_MNG_ACTION_UPSCALE; |
| goto out; |
| } |
| |
| // if Tpt for the higherRateIdx and for LowerRateIdx are both worse then the current Tpt => stay |
| if (((RS_MNG_INVALID_VAL != highTpt) && (highTpt < currTpt)) && |
| ((RS_MNG_INVALID_VAL != lowTpt) && (lowTpt < currTpt))) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetScaleAction: STAY due to current rate having better tpt that both " |
| "higher and lower rates"); |
| action = RS_MNG_ACTION_STAY; |
| goto out; |
| } |
| |
| // If Tpt for LowerRateIdx > currTpt, or it is unknown but lowerRateIdx is valid |
| if (((RS_MNG_INVALID_VAL != lowTpt) && (lowTpt > currTpt)) || |
| ((RS_MNG_INVALID_VAL == lowTpt) && (RS_MNG_INVALID_RATE_IDX != lowerRateIdx))) { |
| U32 lowerRateExpectedTpt = _rsMngGetExpectedTpt( |
| staInfo, &rsMngColumns[tblInfo->column], _rsMngRateGetBw(&tblInfo->rsMngRate), |
| !!(staInfo->mvmsta->agg_tids), lowerRateIdx); |
| |
| // if CurrWin success ratio reached the no decrease TH, or currTpt is higher then expected |
| // Tpt => stay |
| if ((RS_MNG_INVALID_RATE_IDX != lowerRateIdx) && |
| ((currWin->successRatio >= RS_MNG_PERCENT(RS_MNG_SR_NO_DECREASE)) || |
| (currTpt > lowerRateExpectedTpt))) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetScaleAction: STAY due to lower rate having expected tpt lower " |
| "than current tpt or current success ratio is above the 'no-decrease' " |
| "threshold. lower rate expected tpt: %d", |
| lowerRateExpectedTpt); |
| action = RS_MNG_ACTION_STAY; |
| goto out; |
| } |
| // curr SR is insufficient and either lowTpt is valid and > currTpt or lowerRateIdx is valid |
| // => downscale |
| else { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetScaleAction: DOWNSCALE due to low success ratio or lower rate " |
| "having higher tpt than current rate. lower rate expected tpt: %d", |
| lowerRateExpectedTpt); |
| action = RS_MNG_ACTION_DOWNSCALE; |
| goto out; |
| } |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngGetScaleAction: STAY (default)"); |
| action = RS_MNG_ACTION_STAY; |
| |
| out: |
| return action; |
| } |
| |
| // return: TRUE if there is a better start rate, so need to send LQ command |
| // newIdx: valid only if the return value is true |
| // RS_MNG_INVALID_RATE_IDX - if need to keep using the current index |
| // new index to use - if there is another rate that will provide better tpt / tpc |
| static RS_MNG_ACTION_E _rsMngSearchBetterStartRate(const RS_MNG_STA_INFO_S* staInfo, |
| RS_MNG_WIN_STAT_S* currWin, |
| const RS_MNG_RATE_S* currRate, U08* newIdx) { |
| U08 lowerSuppRateIdx; |
| U08 higherSuppRateIdx; |
| RS_MNG_ACTION_E scaleAction; |
| |
| lowerSuppRateIdx = _rsMngGetAdjacentRateIdx(staInfo, currRate, GET_LOWER_SUPPORTED_RATE); |
| higherSuppRateIdx = _rsMngGetAdjacentRateIdx(staInfo, currRate, GET_HIGHER_SUPPORTED_RATE); |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngSearchBetterStartRate: curr rate idx: %d, lower: %d, higher: %d. supported " |
| "rates mask: 0x%x", |
| _rsMngRateGetIdx(currRate), lowerSuppRateIdx, higherSuppRateIdx, supportedRatesMsk); |
| |
| scaleAction = _rsMngGetScaleAction(staInfo, currWin, lowerSuppRateIdx, higherSuppRateIdx); |
| |
| // FMAC TODO - add 'fix' in case we are in MIMO and BT doesn't 'allow' MIMO ? (currently a dead |
| // code in fmac) |
| |
| // Set the new rate index + tx power reduction, if needed |
| switch (scaleAction) { |
| case RS_MNG_ACTION_STAY: |
| if (staInfo->rsMngState == RS_MNG_STATE_STAY_IN_COLUMN) { |
| // TODO - add tpc support |
| // rs_tpc_perform(sta, lq_sta, tbl); |
| } |
| break; |
| case RS_MNG_ACTION_DOWNSCALE: |
| if (RS_MNG_INVALID_RATE_IDX != lowerSuppRateIdx) { |
| // TODO - add tpc |
| *newIdx = lowerSuppRateIdx; |
| } |
| // else - already at the lowest possible rate -> can't downscale |
| else { |
| scaleAction = RS_MNG_ACTION_STAY; |
| } |
| break; |
| case RS_MNG_ACTION_UPSCALE: |
| if (RS_MNG_INVALID_RATE_IDX != higherSuppRateIdx) { |
| // TODO - add tpc |
| *newIdx = higherSuppRateIdx; |
| } |
| // else - already at the highest possible rate -> can't upscale |
| else { |
| scaleAction = RS_MNG_ACTION_STAY; |
| } |
| break; |
| default: |
| // TODO add assert? |
| break; |
| } |
| |
| return scaleAction; |
| } |
| |
| static U08 _rsMngGetLowestSupportedRate(const RS_MNG_STA_INFO_S* staInfo, |
| RS_MNG_MODULATION_E modulation, U32 bw, |
| U16 supportedRates) { |
| if (modulation != RS_MNG_MODUL_LEGACY && staInfo->config.bestSuppMode == TLC_MNG_MODE_HE && |
| bw == CHANNEL_WIDTH20 && _rsMngIsDcmSupported(staInfo, modulation == RS_MNG_MODUL_MIMO2)) { |
| return RS_MCS_0_HE_ER_AND_DCM; |
| } |
| |
| return (U08)LSB2ORD(supportedRates); |
| } |
| // |
| // Returns the index of the lowest rate in the given column with expected tpt higher |
| // than the target tpt. |
| // |
| static U08 _rsMngGetBestRate(const RS_MNG_STA_INFO_S* staInfo, const RS_MNG_TBL_INFO_S* activeTbl, |
| const RS_MNG_COL_ELEM_S* col, U32 bw, U32 targetTpt) { |
| U16 supportedRates = _rsMngGetSupportedRatesByModeAndBw(staInfo, col->mode, bw); |
| U08 rateIdx = _rsMngGetLowestSupportedRate(staInfo, col->mode, bw, supportedRates); |
| U32 expectedTpt = 0; |
| |
| while (rateIdx != RS_MNG_INVALID_RATE_IDX) { |
| expectedTpt = _rsMngGetExpectedTpt(staInfo, col, bw, !!staInfo->mvmsta->agg_tids, rateIdx); |
| |
| if (targetTpt <= expectedTpt) { break; } |
| |
| rateIdx = _rsMngGetHigherRateIdx(rateIdx, supportedRates); |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetBestRate: best rateIdx %d. targetTpt: %d, new expected tpt: %d", rateIdx, |
| targetTpt, expectedTpt); |
| |
| return rateIdx; |
| } |
| |
| static BOOLEAN _rsMngIsColAllowed(const RS_MNG_STA_INFO_S* staInfo, U32 bw, |
| const RS_MNG_COL_ELEM_S* nextCol) { |
| int i; |
| |
| for (i = 0; i < MAX_COLUMN_CHECKS; i++) { |
| ALLOW_COL_FUNC_F allowColFunc = nextCol->checks[i]; |
| |
| if (allowColFunc && !allowColFunc(staInfo, bw, nextCol)) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngIsColAllowed: Function[%d] check failed", |
| i); |
| return FALSE; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| // rs_get_next_column |
| // return column with expected better tpt |
| // or invalid column if such doesn't exist |
| // |
| // flow: |
| // get next column according to rs_tx_columns [index].next_columns[]. |
| // if the next column is invalid - continue to next column in the next_columns list |
| // if already visited the next column - continue to next column in the next_columns list |
| // if the chosen antenna is not supported - continue to next column in the next_columns list |
| // if one of the function checks failed - continue to next column in the next_columns list |
| // if there is no expected throughput in the table - continue to next column in the next_columns |
| // list |
| |
| // now we have the next column. |
| // get expected tpt table according to siso/mimo + BW + /GI/SGI/AGG/SGI+AGG / legacy (one table) |
| // compare the current tpt with max expected tpt in this table. |
| // if current tpt >= max. => continue to next column |
| // |
| // else - return the found column id. |
| // |
| static RS_MNG_COLUMN_DESC_E _rsMngGetNextColId(const RS_MNG_STA_INFO_S* staInfo, |
| const RS_MNG_TBL_INFO_S* tblInfo, U32 targetTpt, |
| U32* bw, U08* rateIdx) { |
| const RS_MNG_COL_ELEM_S* currCol = &rsMngColumns[tblInfo->column]; |
| const RS_MNG_COL_ELEM_S* nextCol; |
| RS_MNG_COLUMN_DESC_E nextColId = RS_MNG_COL_INVALID; // for compilation. won't be used |
| int i; |
| |
| // Check that the defines' value allow to assume that we can add NON_SHARED_ANT_RFIC_ID to |
| // RS_MNG_COL_SISO_ANT_A to get the shared antenna |
| BUILD_BUG_ON(!(RS_MNG_COL_SISO_ANT_A + 1 == RS_MNG_COL_SISO_ANT_B)); |
| BUILD_BUG_ON(!(RS_MNG_COL_SISO_ANT_A_SGI + 1 == RS_MNG_COL_SISO_ANT_B_SGI)); |
| BUILD_BUG_ON(!(RS_MNG_COL_HE_3_2_SISO_ANT_A + 1 == RS_MNG_COL_HE_3_2_SISO_ANT_B)); |
| BUILD_BUG_ON(!(RS_MNG_COL_HE_1_6_SISO_ANT_A + 1 == RS_MNG_COL_HE_1_6_SISO_ANT_B)); |
| BUILD_BUG_ON(!(RS_MNG_COL_HE_0_8_SISO_ANT_A + 1 == RS_MNG_COL_HE_0_8_SISO_ANT_B)); |
| |
| for (i = 0; i < MAX_NEXT_COLUMNS; i++) { |
| nextColId = currCol->nextCols[i]; |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetNextColId: Checking nextCol %d (column id %d)", i, nextColId); |
| |
| if (RS_MNG_COL_INVALID == nextColId) { |
| DBG_PRINTF( |
| UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetNextColId: invalid column. next columns are also invalid, so break"); |
| break; // if column is invalid, all the following ones will be invalid as well |
| } |
| |
| if (0 == i && RS_MNG_MODUL_MIMO2 == currCol->mode) { nextColId += NON_SHARED_ANT_RFIC_ID; } |
| |
| if (staInfo->visitedColumns & BIT(nextColId)) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetNextColId: This column was already visited. continue"); |
| continue; |
| } |
| |
| nextCol = &rsMngColumns[nextColId]; |
| // when moving to a new column, if it's a non-HT column the bw must be 20. If attempting to |
| // leave non-HT, jump to the highest available width, otherwise keep the current bw (bw |
| // changes are handled separately) |
| *bw = (nextCol->mode == RS_MNG_MODUL_LEGACY |
| ? CHANNEL_WIDTH20 |
| : (currCol->mode == RS_MNG_MODUL_LEGACY ? _rsMngGetMaxChWidth(staInfo) |
| : _rsMngRateGetBw(&tblInfo->rsMngRate))); |
| |
| if (!_rsMngIsColAllowed(staInfo, *bw, nextCol)) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetNextColId: column not allowed. continue"); |
| continue; |
| } |
| |
| if ((*rateIdx = _rsMngGetBestRate(staInfo, tblInfo, nextCol, *bw, targetTpt)) == |
| RS_MNG_INVALID_RATE_IDX) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetNextColId: currTpt >= maxTpt of potential column. continue"); |
| continue; |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetNextColId: Found potential column %d, with chosen bw %d and potential " |
| "rateIdx %d", |
| nextColId, *bw, *rateIdx); |
| |
| // Found potential column. |
| break; |
| } |
| |
| if (MAX_NEXT_COLUMNS == i) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngGetNextColId: Couldn't find a better column, staying in this one"); |
| return RS_MNG_COL_INVALID; |
| } |
| |
| return nextColId; |
| } |
| |
| // rs_switch_to_column |
| static void _rsMngSetUpSearchColData(RS_MNG_STA_INFO_S* staInfo, RS_MNG_COLUMN_DESC_E nextColId, |
| U32 bw, U08 rateIdx) { |
| RS_MNG_SEARCH_COL_DATA* searchData = &staInfo->searchColData; |
| RS_MNG_RATE_S* rsMngRate = &searchData->rsMngRate; |
| const RS_MNG_COL_ELEM_S* nextCol = &rsMngColumns[nextColId]; |
| |
| switch (nextCol->mode) { |
| case RS_MNG_MODUL_LEGACY: |
| _rsMngRateSetMode(rsMngRate, TLC_MNG_MODE_LEGACY); |
| _rsMngRateSetLdpc(rsMngRate, FALSE); |
| _rsMngRateSetModulation(rsMngRate, RS_MNG_MODUL_LEGACY); |
| break; |
| case RS_MNG_MODUL_SISO: |
| _rsMngRateSetMode(rsMngRate, staInfo->config.bestSuppMode); |
| _rsMngRateSetModulation(rsMngRate, RS_MNG_MODUL_SISO); |
| break; |
| case RS_MNG_MODUL_MIMO2: |
| _rsMngRateSetMode(rsMngRate, staInfo->config.bestSuppMode); |
| _rsMngRateSetModulation(rsMngRate, RS_MNG_MODUL_MIMO2); |
| break; |
| default: |
| WARN_ON(1); |
| } |
| |
| if (nextCol->mode != RS_MNG_MODUL_LEGACY) { |
| _rsMngRateSetLdpc(rsMngRate, _rsMngIsLdpcAllowed(staInfo)); |
| } |
| |
| _rsMngRateSetBw(rsMngRate, bw); |
| // Set the search rate according to the new column and station info |
| _rsMngRateSetGi(rsMngRate, nextCol->gi); |
| if (nextCol->mode == RS_MNG_MODUL_SISO && _rsMngIsStbcAllowed(staInfo, rsMngRate)) { |
| _rsMngRateSetStbc(rsMngRate, TRUE); |
| _rsMngRateSetAnt(rsMngRate, TLC_MNG_CHAIN_A_MSK | TLC_MNG_CHAIN_B_MSK); |
| } else { |
| _rsMngRateSetStbc(rsMngRate, FALSE); |
| _rsMngRateSetAnt(rsMngRate, nextCol->ant); |
| } |
| _rsMngRateSetIdx(rsMngRate, rateIdx); |
| |
| _memclr(&searchData->win, sizeof(searchData->win)); |
| searchData->column = nextColId; |
| searchData->expectedTpt = _rsMngGetExpectedTpt(staInfo, &rsMngColumns[nextColId], bw, |
| !!staInfo->mvmsta->agg_tids, rateIdx); |
| _rsMngSetVisitedColumn(staInfo, nextColId); |
| |
| DBG_PRINTF( |
| UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngSetUpSearchColData: starting new col at rate index %d (visited columns: 0x%X)", |
| _rsMngRateGetIdx(rsMngRate), staInfo->visitedColumns); |
| } |
| |
| static RS_MNG_COLUMN_DESC_E _rsMngGetMatchingMimoColumn(RS_MNG_COLUMN_DESC_E col) { |
| switch (col) { |
| case RS_MNG_COL_SISO_ANT_A: |
| case RS_MNG_COL_SISO_ANT_B: |
| return RS_MNG_COL_MIMO2; |
| case RS_MNG_COL_SISO_ANT_A_SGI: |
| case RS_MNG_COL_SISO_ANT_B_SGI: |
| return RS_MNG_COL_MIMO2_SGI; |
| case RS_MNG_COL_HE_3_2_SISO_ANT_A: |
| case RS_MNG_COL_HE_3_2_SISO_ANT_B: |
| return RS_MNG_COL_HE_3_2_MIMO; |
| case RS_MNG_COL_HE_1_6_SISO_ANT_A: |
| case RS_MNG_COL_HE_1_6_SISO_ANT_B: |
| return RS_MNG_COL_HE_1_6_MIMO; |
| case RS_MNG_COL_HE_0_8_SISO_ANT_A: |
| case RS_MNG_COL_HE_0_8_SISO_ANT_B: |
| return RS_MNG_COL_HE_0_8_MIMO; |
| default: |
| return RS_MNG_COL_INVALID; |
| } |
| } |
| |
| static BOOLEAN _rsMngSearchBetterCol(RS_MNG_STA_INFO_S* staInfo, const RS_MNG_TBL_INFO_S* tblInfo, |
| U32 currTpt) { |
| BOOLEAN ret = FALSE; |
| RS_MNG_COLUMN_DESC_E nextColId; |
| RS_MNG_COLUMN_DESC_E mimoCol; |
| U08 currRateIdx = _rsMngRateGetIdx(&tblInfo->rsMngRate); |
| U32 successRatio = tblInfo->win[currRateIdx].successRatio; |
| // set target_tpt: |
| // - if the current success ratio >= 85% -> keep the expected_tpt for this idx |
| // - if the success ratio is too low -> revert to last_tpt (current ?) |
| U32 targetTpt = (successRatio <= RS_MNG_PERCENT(RS_MNG_SR_NO_DECREASE)) |
| ? currTpt |
| : _rsMngGetExpectedTpt(staInfo, &rsMngColumns[tblInfo->column], |
| _rsMngRateGetBw(&tblInfo->rsMngRate), |
| !!(staInfo->mvmsta->agg_tids), currRateIdx); |
| U32 bw; |
| U08 rateIdx; |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngSearchBetterCol: starting search. currTpt=%d targetTpt=%d", currTpt, |
| targetTpt); |
| |
| // return column with expected better throughput, or invalid column if such doesn't exist |
| nextColId = _rsMngGetNextColId(staInfo, tblInfo, targetTpt, &bw, &rateIdx); |
| if (RS_MNG_COL_INVALID != nextColId) { |
| _rsMngSetUpSearchColData(staInfo, nextColId, bw, rateIdx); |
| ret = TRUE; |
| } else if (staInfo->searchBw != MAX_CHANNEL_BW_INDX) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngSearchBetterCol: checking if bw change could help. new bw %d", |
| staInfo->searchBw); |
| |
| if ((rateIdx = _rsMngGetBestRate(staInfo, tblInfo, &rsMngColumns[staInfo->stableColumn], |
| staInfo->searchBw, targetTpt)) != |
| RS_MNG_INVALID_RATE_IDX) { |
| nextColId = staInfo->stableColumn; |
| } else if ((mimoCol = _rsMngGetMatchingMimoColumn(staInfo->stableColumn)) != |
| RS_MNG_COL_INVALID && |
| (rateIdx = _rsMngGetBestRate(staInfo, tblInfo, &rsMngColumns[mimoCol], |
| staInfo->searchBw, targetTpt)) != |
| RS_MNG_INVALID_RATE_IDX) { |
| nextColId = mimoCol; |
| } |
| |
| if (nextColId != RS_MNG_COL_INVALID && |
| _rsMngIsColAllowed(staInfo, staInfo->searchBw, &rsMngColumns[nextColId])) { |
| _rsMngSetUpSearchColData(staInfo, nextColId, staInfo->searchBw, rateIdx); |
| ret = TRUE; |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngSearchBetterCol: trying col %d with the new bw", nextColId); |
| } else { |
| DBG_PRINTF( |
| UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngSearchBetterCol: New bw can't improve tpt or isn't allowed, so not trying " |
| "it. col %d, allowed: %d", |
| nextColId, |
| nextColId == RS_MNG_COL_INVALID |
| ? 0 |
| : _rsMngIsColAllowed(staInfo, staInfo->searchBw, &rsMngColumns[nextColId])); |
| } |
| } |
| |
| return ret; |
| } |
| |
| static BOOLEAN _rsMngShouldStartUpscaleSearchCycle(const RS_MNG_STA_INFO_S* staInfo, |
| const RS_MNG_STA_LIMITS_S* staLimits, |
| unsigned long timeLastSearch) { |
| // isUpscaleSearchCycle here is referring to the type of the previous search cycle. |
| // This is here to prevent two consecutive upscale search cycles (i.e. started because of |
| // passing the successFramesLimit threshold) within too short a time. |
| return staInfo->totalFramesSuccess > staLimits->successFramesLimit && |
| (!staInfo->isUpscaleSearchCycle || |
| time_after(jiffies, |
| timeLastSearch + usecs_to_jiffies(RS_MNG_UPSCALE_SEARCH_CYCLE_MAX_FREQ))); |
| } |
| |
| static BOOLEAN _rsMngShouldStartDownscaleSearchCycle(const RS_MNG_STA_INFO_S* staInfo, |
| const RS_MNG_STA_LIMITS_S* staLimits, |
| unsigned long timeLastSearch) { |
| return time_after(jiffies, timeLastSearch + usecs_to_jiffies(staLimits->statsFlushTimeLimit)) || |
| staInfo->totalFramesFailed > staLimits->failedFramesLimit; |
| } |
| |
| static BOOLEAN _rsMngShouldStartSearchCycle(const RS_MNG_STA_INFO_S* staInfo, |
| BOOLEAN* isUpscaleSearchCycle) { |
| const RS_MNG_RATE_S* rsMngRate = &staInfo->rateTblInfo.rsMngRate; |
| BOOLEAN isNonHt = _rsMngRateGetMode(rsMngRate) == TLC_MNG_MODE_LEGACY; |
| const RS_MNG_STA_LIMITS_S* staLimits = &g_rsMngStaModLimits[isNonHt]; |
| unsigned long timeLastSearch = staInfo->lastSearchCycleEndTimeJiffies; |
| |
| if (_rsMngShouldStartUpscaleSearchCycle(staInfo, staLimits, timeLastSearch)) { |
| *isUpscaleSearchCycle = TRUE; |
| return TRUE; |
| } |
| |
| if (_rsMngShouldStartDownscaleSearchCycle(staInfo, staLimits, timeLastSearch)) { |
| *isUpscaleSearchCycle = FALSE; |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| static void _rsMngPrepareForBwChangeAttempt(RS_MNG_STA_INFO_S* staInfo, |
| const RS_MNG_RATE_S* rsMngRate) { |
| BOOLEAN isNonHt = _rsMngRateGetMode(rsMngRate) == TLC_MNG_MODE_LEGACY; |
| RS_MCS_E mcs; |
| U32 bw; |
| U32 isMimo; |
| |
| staInfo->searchBw = MAX_CHANNEL_BW_INDX; |
| if (isNonHt) { return; } |
| |
| mcs = _rsMngRateGetIdx(rsMngRate); |
| bw = _rsMngRateGetBw(rsMngRate); |
| isMimo = _rsMngRateGetModulation(rsMngRate) == RS_MNG_MODUL_MIMO2; |
| |
| if (mcs > g_rsMngDynBwStayMcs[bw][isMimo].highestStayMcs && _rsMngGetMaxChWidth(staInfo) > bw) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngPrepareForBwChangeAttempt: will try higher bandwidth (mcs %d, isMimo %d, " |
| "bw %d->%d)", |
| mcs, isMimo, bw, bw + 1); |
| staInfo->searchBw = bw + 1; |
| } else if (mcs < g_rsMngDynBwStayMcs[bw][isMimo].lowestStayMcs) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngPrepareForBwChangeAttempt: will try lower bandwidth (mcs %d, isMimo %d, " |
| "bw %d->%d)", |
| mcs, isMimo, bw, bw - 1); |
| staInfo->searchBw = bw - 1; |
| } |
| } |
| |
| static void _rsMngSetStayInCol(RS_MNG_STA_INFO_S* staInfo) { |
| staInfo->rsMngState = RS_MNG_STATE_STAY_IN_COLUMN; |
| staInfo->stableColumn = staInfo->rateTblInfo.column; |
| |
| staInfo->totalFramesFailed = 0; |
| staInfo->totalFramesSuccess = 0; |
| staInfo->lastSearchCycleEndTimeJiffies = jiffies; |
| staInfo->txedFrames = 0; |
| staInfo->visitedColumns = 0; |
| } |
| |
| static BOOLEAN _rsMngTryColumnSwitch(RS_MNG_STA_INFO_S* staInfo, U32 currTpt, BOOLEAN* updateHost) { |
| const RS_MNG_TBL_INFO_S* tblInfo = &staInfo->rateTblInfo; |
| |
| if (_rsMngSearchBetterCol(staInfo, tblInfo, currTpt)) { |
| staInfo->searchBetterTbl = TRUE; |
| *updateHost = FALSE; |
| |
| return TRUE; |
| } else { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTryColumnSwitch: No potential column found, change state to " |
| "RS_MNG_STATE_STAY_IN_COLUMN"); |
| _rsMngSetStayInCol(staInfo); |
| *updateHost = TRUE; |
| |
| return FALSE; |
| } |
| } |
| |
| static BOOLEAN _rsMngStartSearchCycle(RS_MNG_STA_INFO_S* staInfo, U32 currTpt, |
| BOOLEAN* updateHost) { |
| RS_MNG_TBL_INFO_S* tblInfo = &staInfo->rateTblInfo; |
| RS_MNG_RATE_S* rsMngRate = &staInfo->rateTblInfo.rsMngRate; |
| |
| staInfo->rsMngState = RS_MNG_STATE_SEARCH_CYCLE_STARTED; |
| _rsMngSetVisitedColumn(staInfo, tblInfo->column); |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngStartSearchCycle: Moving to state SEARCH_CYCLE_STARTED, upscale search cycle " |
| "%d, visited cols bitmask: 0x%X, curr rate: 0x%X", |
| staInfo->isUpscaleSearchCycle, staInfo->visitedColumns, |
| rsMngRate->rate.rate_n_flags); |
| |
| // If we're in HT/VHT/HE, we may want to test a different bandwidth during the search cycle. |
| // According to requirements, this is decided based on the reason the search cycle started |
| // (upscale or downscale) and the configuration and mcs when the sycle begins. |
| // The new bandwidth will be tested on the column from the start of the search cycle. |
| // Do that logic now. |
| _rsMngPrepareForBwChangeAttempt(staInfo, rsMngRate); |
| |
| return _rsMngTryColumnSwitch(staInfo, currTpt, updateHost); |
| } |
| |
| static void _rsMngSwitchToSearchCol(RS_MNG_STA_INFO_S* staInfo) { |
| RS_MNG_TBL_INFO_S* tblInfo = &staInfo->rateTblInfo; |
| RS_MNG_SEARCH_COL_DATA* searchData = &staInfo->searchColData; |
| |
| tblInfo->rsMngRate = searchData->rsMngRate; |
| _rsMngClearTblWindows(staInfo); |
| tblInfo->win[_rsMngRateGetIdx(&searchData->rsMngRate)] = searchData->win; |
| tblInfo->column = searchData->column; |
| } |
| |
| static BOOLEAN _rsMngTryScaleWithinColumn(RS_MNG_STA_INFO_S* staInfo, RS_MNG_WIN_STAT_S* currWin, |
| BOOLEAN* updateHost) { |
| RS_MNG_TBL_INFO_S* tblInfo = &staInfo->rateTblInfo; |
| RS_MNG_ACTION_E action; |
| U08 newIdx = RS_MNG_INVALID_RATE_IDX; |
| |
| action = _rsMngSearchBetterStartRate(staInfo, currWin, &tblInfo->rsMngRate, &newIdx); |
| if (action == RS_MNG_ACTION_UPSCALE) { |
| if (staInfo->rsMngState == RS_MNG_STATE_SEARCH_CYCLE_STARTED || |
| time_after(jiffies, staInfo->lastRateUpscaleTimeJiffies + |
| usecs_to_jiffies(RS_MNG_UPSCALE_MAX_FREQUENCY))) { |
| // Rate upscaling could happen both during a search cycle and while in STAY_IN_COLUMN |
| // state. When in stay in column the upscaling frequency is limited to no more than once |
| // per 100ms. When in search cycle there is no limit because search cycles need to be as |
| // short as possible. |
| staInfo->lastRateUpscaleTimeJiffies = jiffies; |
| } else { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTryScaleWithinColumn: not upscaling because not enough time passed " |
| "since last upscale (%d usec)", |
| systemTimeGetUsecDiffTime(staInfo->lastRateUpscaleTimeJiffies)); |
| action = RS_MNG_ACTION_STAY; |
| } |
| } |
| |
| staInfo->tryingRateUpscale = (U08)(action == RS_MNG_ACTION_UPSCALE); |
| |
| if (action != RS_MNG_ACTION_STAY) { |
| _rsMngRateSetIdx(&tblInfo->rsMngRate, newIdx); |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| static void _rsMngLookForNextSearchRate(RS_MNG_STA_INFO_S* staInfo, RS_MNG_WIN_STAT_S* currWin, |
| BOOLEAN* updateHost) { |
| // During a search cycle first search for the optimal rate within the current column, and once |
| // that is found (i.e. _rsMngTryScaleWithinColumn returns FALSE), search for a better column to |
| // switch to. |
| if (!_rsMngTryScaleWithinColumn(staInfo, currWin, updateHost)) { |
| _rsMngTryColumnSwitch(staInfo, currWin->averageTpt, updateHost); |
| } |
| } |
| |
| typedef enum _RS_MNG_TPC_ALLOWED_REASON_E { |
| RS_MNG_TPC_DISALLOWED_DEBUG_HOOK, |
| RS_MNG_TPC_DISALLOWED_SLEEP_DISALLOWED, |
| RS_MNG_TPC_DISALLOWED_IN_SEARCH_CYCLE, |
| RS_MNG_TPC_DISALLOWED_RATE_IS_NON_HT, |
| RS_MNG_TPC_DISALLOWED_RATE_IS_NOT_OPTIMAL, |
| RS_MNG_TPC_DISALLOWED_TEST_RATE, |
| RS_MNG_TPC_DISALLOWED_AMSDU_INACTIVE, |
| RS_MNG_TPC_DISALLOWED_AMSDU_TIME_LIMIT, |
| RS_MNG_TPC_ALLOWED, |
| } RS_MNG_TPC_ALLOWED_REASON_E; |
| |
| static RS_MNG_TPC_ALLOWED_REASON_E _rsMngTpcAllowed(const RS_MNG_STA_INFO_S* staInfo, |
| U32* tpcAllowedData) { |
| const RS_MNG_RATE_S* rsMngRate = &staInfo->rateTblInfo.rsMngRate; |
| TLC_MNG_MODE_E mode; |
| RS_MNG_TX_AMSDU_SIZE_E amsduSize; |
| |
| if (!PWR_IS_SLEEP_ALLOWED) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcAllowed: TPC disallowed because sleep disallowed"); |
| return RS_MNG_TPC_DISALLOWED_SLEEP_DISALLOWED; |
| } |
| |
| if (staInfo->rsMngState == RS_MNG_STATE_SEARCH_CYCLE_STARTED) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcAllowed: TPC disallowed because in search cycle"); |
| return RS_MNG_TPC_DISALLOWED_IN_SEARCH_CYCLE; |
| } |
| |
| mode = _rsMngRateGetMode(rsMngRate); |
| if (mode == TLC_MNG_MODE_LEGACY) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcAllowed: TPC disallowed because in non-HT rate"); |
| return RS_MNG_TPC_DISALLOWED_RATE_IS_NON_HT; |
| } |
| |
| if (!_rsMngRateIsOptimal(staInfo, rsMngRate)) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcAllowed: TPC disallowed because rate is not optimal"); |
| *tpcAllowedData = rsMngRate->rate.rate_n_flags; |
| return RS_MNG_TPC_DISALLOWED_RATE_IS_NOT_OPTIMAL; |
| } |
| |
| if (staInfo->tryingRateUpscale) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcAllowed: TPC disallowed because current rate is test rate"); |
| return RS_MNG_TPC_DISALLOWED_TEST_RATE; |
| } |
| |
| amsduSize = |
| _rsMngAmsduSize(staInfo, mode, _rsMngRateGetBw(rsMngRate), _rsMngRateGetIdx(rsMngRate), |
| _rsMngRateGetGi(rsMngRate), _rsMngRateGetModulation(rsMngRate)); |
| if (staInfo->amsduSupport && amsduSize != RS_MNG_AMSDU_INVALID) { |
| // amsdu is supported in general, and specifically for this rate (i.e. the phy rate of the |
| // optimal rate is above the amsdu min phy rate threshold) |
| if (staInfo->amsduEnabledSize == RS_MNG_AMSDU_INVALID) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcAllowed: TPC disallowed because amsdu is not yet active"); |
| return RS_MNG_TPC_DISALLOWED_AMSDU_INACTIVE; |
| } |
| |
| if (time_before(jiffies, |
| staInfo->lastEnableJiffies + usecs_to_jiffies(RS_MNG_TPC_AMSDU_ENABLE))) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcAllowed: TPC disallowed because not enough time elapsed since " |
| "amsdu enablement. time elapsed: %u", |
| time); |
| return RS_MNG_TPC_DISALLOWED_AMSDU_TIME_LIMIT; |
| } |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngTpcAllowed: TPC is allowed"); |
| return RS_MNG_TPC_ALLOWED; |
| } |
| |
| typedef enum _RS_MNG_TPC_ACTION { |
| RS_MNG_TPC_ACTION_STAY, |
| RS_MNG_TPC_ACTION_INCREASE, |
| RS_MNG_TPC_ACTION_DECREASE, |
| RS_MNG_TPC_ACTION_DISABLE, |
| } RS_MNG_TPC_ACTION; |
| |
| static RS_MNG_TPC_ACTION _rsMngTpcGetAction(const RS_MNG_STA_INFO_S* staInfo) { |
| const RS_MNG_TPC_TBL_S* tpcTbl = &staInfo->tpcTable; |
| U32 currStepSR; |
| U08 currStep = tpcTbl->currStep; |
| BOOLEAN tpcInactive = currStep == RS_MNG_TPC_INACTIVE; |
| |
| if (WARN_ON(!(currStep < RS_MNG_TPC_DISABLED))) { return RS_MNG_TPC_ACTION_STAY; } |
| |
| if (!tpcInactive) { |
| currStepSR = tpcTbl->windows[currStep].successRatio; |
| } else { |
| currStepSR = staInfo->rateTblInfo.win[_rsMngRateGetIdx(&staInfo->rateTblInfo.rsMngRate)] |
| .successRatio; |
| } |
| |
| if (!tpcInactive) { |
| // if testing is true, then we are now operating on results from a tpc_action_increase test |
| // window. In this case we don't want to completely disable tpc even if the current SR is |
| // relatively bad. Instead we will just decrease back to the last good step. |
| if (currStepSR <= RS_MNG_TPC_SR_DISABLE && !tpcTbl->testing) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcGetAction: DISABLING tpc. currStep = %d, currStepSR = %d", |
| currStep, currStepSR); |
| return RS_MNG_TPC_ACTION_DISABLE; |
| } |
| if (currStepSR <= RS_MNG_TPC_SR_DECREASE) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcGetAction: DECREASING tpc. currStep = %d, currStepSR = %d", |
| currStep, currStepSR); |
| return RS_MNG_TPC_ACTION_DECREASE; |
| } |
| } |
| |
| if (tpcInactive || currStep < RS_MNG_TPC_NUM_STEPS - 1) { |
| U08 higherStep = (U08)(tpcInactive ? 0 : currStep + 1); |
| U32 higherStepSR = tpcTbl->windows[higherStep].successRatio; |
| |
| // only attempt to increase power reduction if this hasn't been tried since the last time |
| // statistics for the higher step were cleared |
| if (currStepSR >= RS_MNG_TPC_SR_INCREASE && higherStepSR == RS_MNG_INVALID_VAL) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcGetAction: INCREASING tpc. currStep = %d, currStepSR = %d", |
| currStep, currStepSR); |
| return RS_MNG_TPC_ACTION_INCREASE; |
| } |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTpcGetAction: STAY tpc. currStep = %d, currStepSR = %d", currStep, |
| currStepSR); |
| return RS_MNG_TPC_ACTION_STAY; |
| } |
| |
| static void _rsMngTpcDoAction(RS_MNG_STA_INFO_S* staInfo, RS_MNG_TPC_ACTION action) { |
| RS_MNG_TPC_TBL_S* tpcTbl = &staInfo->tpcTable; |
| |
| switch (action) { |
| case RS_MNG_TPC_ACTION_INCREASE: |
| if (tpcTbl->currStep == RS_MNG_TPC_INACTIVE) { |
| tpcTbl->currStep = 0; |
| } else { |
| tpcTbl->currStep++; |
| } |
| tpcTbl->testing = TRUE; |
| break; |
| case RS_MNG_TPC_ACTION_DECREASE: |
| if (tpcTbl->currStep > 0) { |
| tpcTbl->currStep--; |
| } else { |
| tpcTbl->currStep = RS_MNG_TPC_INACTIVE; |
| } |
| tpcTbl->testing = FALSE; |
| break; |
| case RS_MNG_TPC_ACTION_DISABLE: |
| tpcTbl->currStep = RS_MNG_TPC_INACTIVE; |
| /* fall through */ |
| case RS_MNG_TPC_ACTION_STAY: |
| /* fall through */ |
| default: |
| tpcTbl->testing = FALSE; |
| break; |
| } |
| } |
| |
| static void _rsMngHandleBwChange(RS_MNG_STA_INFO_S* staInfo) { |
| RS_MNG_SEARCH_COL_DATA* searchData = &staInfo->searchColData; |
| RS_MNG_RATE_S* prevRate = &staInfo->rateTblInfo.rsMngRate; |
| |
| // check if this was a bandwidth search |
| if (_rsMngRateGetModulation(&searchData->rsMngRate) != RS_MNG_MODUL_LEGACY && |
| staInfo->stableColumn >= RS_MNG_COL_FIRST_HT_VHT && |
| _rsMngRateGetBw(&searchData->rsMngRate) != _rsMngRateGetBw(prevRate)) { |
| staInfo->searchBw = MAX_CHANNEL_BW_INDX; |
| |
| // attempting a bandwidth change is always the very last configuration change attempt in a |
| // search cycle. mark all columns as visited in order to make sure the search cycle will |
| // end. |
| staInfo->visitedColumns = (U32)-1; |
| } |
| } |
| |
| static BOOLEAN _rsMngHandleBtCoex(const RS_MNG_STA_INFO_S* staInfo, RS_MNG_RATE_S* rate, |
| RS_MNG_COLUMN_DESC_E* col) { |
| /* |
| * BT-Coex only restricts use of the shared antenna if the rate is a SISO non-STBC rate. In case |
| * of MIMO (or SISO with STBC) nothing needs to be done. |
| * So check here only if the column uses only the shared antenna. |
| */ |
| if (rsMngColumns[*col].ant != BT_COEX_SHARED_ANT_ID) { return FALSE; } |
| |
| if (!_rsMngRateGetStbc(rate)) { |
| U08 singleAnt = rsMngGetSingleAntMsk(staInfo->config.chainsEnabled); |
| |
| /* |
| * Set the antenna to the non-shared one if possible. |
| * In case of SAR limitation, for example, it may be that BT is in a high activity grading |
| * and would prefer that wifi use the other antenna, but due to the SAR restriction this is |
| * not possible. Because rsMngGetSingleAntMsk always returns the non-shared antenna if it's |
| * available (i.e. enabled by configuration and not restricted due to SAR limitation), using |
| * the antenna returned by that function handles this type of case too. |
| */ |
| if (_rsMngRateGetAnt(rate) == singleAnt) { return FALSE; } |
| |
| _rsMngRateSetAnt(rate, singleAnt); |
| *col ^= 1; |
| return TRUE; |
| } |
| |
| /* |
| * Just to avoid reaching here again the next time around, set the column to the non-shared-ant |
| * one. In reality there's no difference because with STBC both antennas are used in any case. |
| * The xor here does the trick thanks to the compilation asserts near _rsMngSetVisitedColumn. |
| */ |
| *col ^= 1; |
| |
| return FALSE; |
| } |
| |
| static void _rsMngRateScalePerform(RS_MNG_STA_INFO_S* staInfo, BOOLEAN forceUpdate) { |
| RS_MNG_TBL_INFO_S* tblInfo = &staInfo->rateTblInfo; |
| RS_MNG_WIN_STAT_S* currWin; |
| BOOLEAN updateLmac = forceUpdate, updateHost = FALSE; |
| RS_MNG_TPC_ALLOWED_REASON_E tpcAllowed; |
| U32 tpcAllowedData = 0; |
| |
| if (_rsMngCoexIsLongAggAllowed(staInfo) != staInfo->longAggEnabled) { updateLmac = TRUE; } |
| |
| if (btCoexManagerBtOwnsAnt(staInfo->mvm)) { |
| updateLmac |= _rsMngHandleBtCoex(staInfo, &tblInfo->rsMngRate, &tblInfo->column); |
| |
| /* |
| * If these stats are on a search rate, and that rate just happens to be siso on the shared |
| * antenna, change it to the non-shared one too. |
| */ |
| if (staInfo->rsMngState == RS_MNG_STATE_SEARCH_CYCLE_STARTED && staInfo->searchBetterTbl) { |
| _rsMngHandleBtCoex(staInfo, &staInfo->searchColData.rsMngRate, |
| &staInfo->searchColData.column); |
| } |
| } |
| |
| if (staInfo->rsMngState == RS_MNG_STATE_SEARCH_CYCLE_STARTED) { |
| updateLmac = TRUE; |
| |
| if (staInfo->searchBetterTbl) { |
| // Last rate sent to lmac was of a test for a new column. Check if it's any good. |
| U32 stableRateTpt = tblInfo->win[_rsMngRateGetIdx(&tblInfo->rsMngRate)].averageTpt; |
| |
| staInfo->searchBetterTbl = FALSE; |
| currWin = &staInfo->searchColData.win; |
| |
| _rsMngHandleBwChange(staInfo); |
| |
| if (currWin->averageTpt >= stableRateTpt) { |
| // Yay! The search column is better! switch over to it. |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngRateScalePerform: search col Tpt %d > lastTpt %d. swapping to " |
| "the search table", |
| currWin->averageTpt, stableRateTpt); |
| |
| _rsMngSwitchToSearchCol(staInfo); |
| |
| _rsMngLookForNextSearchRate(staInfo, currWin, &updateHost); |
| } else { |
| // The search col didn't turn out to improve tpt, so search for another new column. |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngRateScalePerform: search col Tpt %d <= lastTpt %d. Searching for " |
| "another column", |
| currWin->averageTpt, stableRateTpt); |
| |
| _rsMngTryColumnSwitch(staInfo, stableRateTpt, &updateHost); |
| } |
| } else { |
| // In the middle of searching for an optimal rate within a column. Continue this effort. |
| currWin = &tblInfo->win[_rsMngRateGetIdx(&tblInfo->rsMngRate)]; |
| |
| _rsMngLookForNextSearchRate(staInfo, currWin, &updateHost); |
| } |
| } else { |
| // Not in a search cycle. |
| |
| if (!_rsMngTpcIsActive(staInfo)) { |
| // If TPC is active (i.e. actually reducing Tx power), we don't try to scale within the |
| // column, will only do tpc scaling farther down in this function. |
| |
| // If previous rate was an upscale test, need to update lmac regardless of the next rate |
| // because even if it's the same rate again, need to send the lq command without the |
| // test-rate bit set. |
| updateLmac |= staInfo->tryingRateUpscale; |
| |
| currWin = &tblInfo->win[_rsMngRateGetIdx(&tblInfo->rsMngRate)]; |
| |
| // Check if to try changing rate within the column. If so - update lmac. Otherwise - |
| // check if it's time to start a new search cycle and act upon that decision |
| if (_rsMngTryScaleWithinColumn(staInfo, currWin, &updateHost)) { |
| updateLmac = TRUE; |
| } else if (_rsMngShouldStartSearchCycle(staInfo, &staInfo->isUpscaleSearchCycle)) { |
| updateLmac |= _rsMngStartSearchCycle(staInfo, currWin->averageTpt, &updateHost); |
| } else { |
| // Note that if the rate didn't really change the host-update function will not send |
| // a notification to host regadless of the value of this boolean. |
| updateHost = TRUE; |
| } |
| } |
| } |
| |
| tpcAllowed = _rsMngTpcAllowed(staInfo, &tpcAllowedData); |
| if (tpcAllowed == RS_MNG_TPC_ALLOWED) { |
| RS_MNG_TPC_ACTION action; |
| |
| if (staInfo->tpcTable.currStep == RS_MNG_TPC_DISABLED) { |
| // First time tpc is allowed, start a tpc search cycle (quick movement between tpc |
| // steps) |
| _rsMngClearWinArr(staInfo->tpcTable.windows, RS_MNG_TPC_NUM_STEPS); |
| staInfo->tpcTable.currStep = RS_MNG_TPC_INACTIVE; |
| staInfo->rsMngState = RS_MNG_STATE_TPC_SEARCH; |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngRateScalePerform: starting tpc search"); |
| } |
| |
| action = _rsMngTpcGetAction(staInfo); |
| |
| updateLmac |= staInfo->tpcTable.testing; |
| |
| if (staInfo->rsMngState == RS_MNG_STATE_TPC_SEARCH) { |
| if (action == RS_MNG_TPC_ACTION_STAY) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngRateScalePerform: change state to RS_MNG_STATE_STAY_IN_COLUMN"); |
| |
| staInfo->rsMngState = RS_MNG_STATE_STAY_IN_COLUMN; |
| updateHost = TRUE; |
| } |
| } else { |
| if (action == RS_MNG_TPC_ACTION_INCREASE) { |
| if (time_after(jiffies, staInfo->lastRateUpscaleTimeJiffies + |
| usecs_to_jiffies(RS_MNG_UPSCALE_MAX_FREQUENCY))) { |
| staInfo->lastRateUpscaleTimeJiffies = jiffies; |
| } else { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngTryScaleWithinColumn: overriding tpc increase. time since " |
| "last increase %dusec", |
| systemTimeGetUsecDiffTime(staInfo->lastRateUpscaleTimeJiffies)); |
| action = RS_MNG_TPC_ACTION_STAY; |
| } |
| } |
| } |
| |
| _rsMngTpcDoAction(staInfo, action); |
| updateLmac |= (action != RS_MNG_TPC_ACTION_STAY); |
| } else if (staInfo->tpcTable.currStep != RS_MNG_TPC_DISABLED) { |
| // TPC was just disallowed. Reset tpc state. |
| |
| // If TPC was active or was testing a new step, need to resend an lq with no power reduction |
| // and no test-window bit. |
| updateLmac |= (_rsMngTpcIsActive(staInfo) || staInfo->tpcTable.testing); |
| |
| staInfo->tpcTable.testing = FALSE; |
| staInfo->tpcTable.currStep = RS_MNG_TPC_DISABLED; |
| |
| if (staInfo->rsMngState == RS_MNG_STATE_TPC_SEARCH) { |
| staInfo->rsMngState = RS_MNG_STATE_STAY_IN_COLUMN; |
| updateHost = TRUE; |
| } |
| } |
| |
| if (updateLmac) { |
| _rsMngUpdateRateTbl(staInfo, |
| staInfo->rsMngState == RS_MNG_STATE_STAY_IN_COLUMN && updateHost); |
| } |
| } |
| |
| static void rsMngInitAmsdu(RS_MNG_STA_INFO_S* staInfo) { |
| if (staInfo->config.amsduSupported && staInfo->config.bestSuppMode >= TLC_MNG_MODE_VHT && |
| _rsMngGetMaxChWidth(staInfo) >= TLC_MNG_CH_WIDTH_40MHZ) { |
| staInfo->amsduSupport = TRUE; |
| } else { |
| staInfo->amsduSupport = FALSE; |
| } |
| |
| staInfo->amsduEnabledSize = RS_MNG_AMSDU_INVALID; |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "rsMngInitAmsdu: sta %d, AMSDU support %d", |
| _rsMngStaInfoToStaId(staInfo), staInfo->amsduSupport); |
| |
| staInfo->lastTrafficLoadStatJiffies = jiffies; |
| } |
| |
| static U16 _rsMngAmsduEnumToSize(RS_MNG_TX_AMSDU_SIZE_E value) { |
| switch (value) { |
| case RS_MNG_AMSDU_3500B: |
| return 3500; |
| case RS_MNG_AMSDU_5000B: |
| return 5000; |
| case RS_MNG_AMSDU_6500B: |
| return 6500; |
| case RS_MNG_AMSDU_8000B: |
| return 8000; |
| default: |
| return 0; |
| } |
| } |
| |
| static void _rsMngNotifAmsdu(RS_MNG_STA_INFO_S* staInfo, U32 successRatio, U32 trafficLoadPerSec) { |
| U16 bitmap = 0; |
| U16 size = 0; |
| |
| if (staInfo->amsduEnabledSize != RS_MNG_AMSDU_INVALID) { |
| // Check which TIDs are not low latency and have an AMSDU in AMPDU session. |
| // We activate AMSDU only for those. |
| bitmap = |
| (U16)(RS_MNG_AMSDU_VALID_TIDS_MSK & staInfo->mvmsta->agg_tids & staInfo->amsduInAmpdu); |
| size = _rsMngAmsduEnumToSize(staInfo->amsduEnabledSize); |
| if (size > staInfo->config.maxMpduLen) { size = staInfo->config.maxMpduLen; } |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngNotifAmsdu: sta %d AMSDU enabled ? %u. bitmap %x, size %d", |
| _rsMngStaInfoToStaId(staInfo), staInfo->amsduEnabledSize != RS_MNG_AMSDU_INVALID, |
| bitmap, size); |
| |
| tlcMngNotifyAmsdu(staInfo, size, bitmap); |
| } |
| |
| // Activate fail safe mechanism: |
| // We may have interoperability issues, where the peer cannot receive correctly |
| // AMSDUs, in which case we will constantly try to enable AMSDU, and will fall |
| // back to disabling it. |
| // In case we consecutively toggle to disable within a FAIL_TIME_THRESHOLD from |
| // enablement FAIL_CONSEC_THRESHOLD times - blacklist this AMSDU size permanently. |
| static void _rsMngAmsduFailSafe(RS_MNG_STA_INFO_S* staInfo, unsigned long currentTime) { |
| // Since traffic load 1 sec window is not exactly one sec, check to see if we |
| // are within its window by comparing the update of the stat timestamp with |
| // the enable event. |
| // If the "1 sec" window has passed, then the timestamp was updated. |
| if (time_before(currentTime, staInfo->lastEnableJiffies + |
| usecs_to_jiffies(RS_MNG_AMSDU_FAIL_TIME_THRESHOLD)) || |
| staInfo->lastEnableJiffies == staInfo->lastTrafficLoadStatJiffies) { |
| staInfo->failSafeCounter++; |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngAmsduFailSafe: Sta %d IOP counter incremented to %d", |
| _rsMngStaInfoToStaId(staInfo), staInfo->failSafeCounter); |
| } else { |
| staInfo->failSafeCounter = 0; |
| } |
| |
| if (staInfo->failSafeCounter >= RS_MNG_AMSDU_FAIL_CONSEC_THRESHOLD) { |
| // disable AMSDU permanently for this AMSDU size |
| staInfo->amsduBlacklist |= BIT(staInfo->amsduEnabledSize); |
| staInfo->failSafeCounter = 0; |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngAmsduFailSafe: sta %d, blacklisted size %d", |
| _rsMngStaInfoToStaId(staInfo), staInfo->amsduEnabledSize); |
| |
| // if all disabled - save some future processing |
| if (staInfo->amsduBlacklist == RS_MNG_AMSDU_SIZE_ALL) { staInfo->amsduSupport = FALSE; } |
| } |
| } |
| |
| static void _rsMngAmsduChanged(RS_MNG_STA_INFO_S* staInfo, RS_MNG_TX_AMSDU_SIZE_E size, |
| unsigned long time, U32 successRatio, U32 trafficLoadPerSec) { |
| if (size < staInfo->amsduEnabledSize) { _rsMngAmsduFailSafe(staInfo, time); } |
| staInfo->amsduEnabledSize = size; |
| if (size != RS_MNG_AMSDU_INVALID) { staInfo->lastEnableJiffies = time; } |
| _rsMngNotifAmsdu(staInfo, successRatio, trafficLoadPerSec); |
| } |
| |
| /* |
| * Returns whether amsdu state changed from enabled to disabled or vice-versa (size changes are not |
| * considered a state change in this respect) |
| */ |
| static BOOLEAN _rsMngCollectAmsduTlcData(RS_MNG_STA_INFO_S* staInfo, U32 baTxed, U32 baAcked, |
| U16 trafficLoad) { |
| RS_MNG_TX_AMSDU_SIZE_E size; |
| unsigned long elapsedTime, currentTime, trafficLoadPerSec, successRatio; |
| BOOLEAN ret = FALSE; |
| const U32 baTxedUnnormalized = baTxed; |
| RS_MNG_RATE_S* rsMngRate = &staInfo->rateTblInfo.rsMngRate; |
| |
| currentTime = jiffies; |
| elapsedTime = jiffies_to_usecs(currentTime - staInfo->lastTrafficLoadStatJiffies) >> 10; |
| elapsedTime = elapsedTime ?: 1; |
| |
| staInfo->trafficLoad += trafficLoad; |
| size = _rsMngAmsduSize(staInfo, _rsMngRateGetMode(rsMngRate), _rsMngRateGetBw(rsMngRate), |
| _rsMngRateGetIdx(rsMngRate), _rsMngRateGetGi(rsMngRate), |
| _rsMngRateGetModulation(rsMngRate)); |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngCollectAmsduTlcData: baTxed %d, baAcked %d, new trafficLoad %d, time from " |
| "last traffic load check %d, trafficLoad %d potential size %d", |
| baTxed, baAcked, trafficLoad, elapsedTime, staInfo->trafficLoad, size); |
| |
| // Normalize the baTxed to be able to divide by it without explicit zero check |
| // baAcked is zero as well in this case, so it has no functionality impact |
| baTxed = baTxed ?: 1; |
| |
| if (staInfo->amsduEnabledSize == RS_MNG_AMSDU_INVALID) { |
| // AMSDU TLC traffic load works in a window of 1 second |
| if (elapsedTime < 1000) { |
| DBG_PRINTF( |
| UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngCollectAmsduTlcData: amsdu disabled, but elapsedTime less than one second"); |
| return FALSE; |
| } |
| |
| // This div will only be done 1 time in a second, if AMSDU is supported - not that expensive |
| successRatio = (baAcked * 128) / baTxed; |
| // normalize the traffic load to a 1 second window |
| trafficLoadPerSec = (staInfo->trafficLoad << 10) / elapsedTime; |
| |
| DBG_PRINTF( |
| UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngCollectAmsduTlcData: amsdu disabled. successRatio %d, trafficLoadPerSec %d", |
| successRatio, trafficLoadPerSec); |
| |
| if (successRatio > RS_MNG_AMSDU_SR_ENABLE_THRESHOLD && |
| trafficLoadPerSec > RS_MNG_AMSDU_TL_ENABLE_THRESHOLD && size != RS_MNG_AMSDU_INVALID) { |
| // AMSDU is disabled and should be enabled |
| _rsMngAmsduChanged(staInfo, size, currentTime, successRatio, trafficLoadPerSec); |
| ret = TRUE; |
| } |
| } else { |
| successRatio = (baAcked * 128) / baTxed; |
| // normalize the traffic load to a 1 second window |
| trafficLoadPerSec = (staInfo->trafficLoad << 10) / elapsedTime; |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngCollectAmsduTlcData: amsdu is enabled with size %d, successRatio %d, " |
| "trafficLoadPerSec %d", |
| staInfo->amsduEnabledSize, successRatio, trafficLoadPerSec); |
| |
| // AMSDU is enabled - check if we should disable or up/down-grade. |
| // Note that we upgrade here without waiting for a second to pass since the last change in |
| // order to recover as fast as possible from a momentary rate reduction (at the price of |
| // perhaps triggering the failsafe mechanism too early). |
| if (size != staInfo->amsduEnabledSize) { |
| DBG_PRINTF( |
| UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngCollectAmsduTlcData: new size is different than current size. updating"); |
| _rsMngAmsduChanged(staInfo, size, currentTime, successRatio, trafficLoadPerSec); |
| return size == RS_MNG_AMSDU_INVALID; |
| } |
| |
| if (successRatio < RS_MNG_AMSDU_SR_DISABLE_THRESHOLD && baTxedUnnormalized) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngCollectAmsduTlcData: success ratio too low. Disabling amsdu"); |
| _rsMngAmsduChanged(staInfo, RS_MNG_AMSDU_INVALID, currentTime, successRatio, |
| trafficLoadPerSec); |
| return TRUE; |
| } |
| |
| // AMSDU TLC traffic load works in a window of 1 second |
| if (elapsedTime < 1000) { |
| DBG_PRINTF( |
| UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngCollectAmsduTlcData: elapsed time less than a second, skipping TL check"); |
| return FALSE; |
| } |
| |
| if (trafficLoadPerSec < RS_MNG_AMSDU_TL_DISABLE_THRESHOLD) { |
| staInfo->trafficLoad = trafficLoadPerSec; |
| _rsMngAmsduChanged(staInfo, RS_MNG_AMSDU_INVALID, currentTime, successRatio, |
| trafficLoadPerSec); |
| ret = TRUE; |
| } |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngCollectAmsduTlcData: resetting trafficLoad counter"); |
| |
| staInfo->trafficLoad = 0; |
| staInfo->lastTrafficLoadStatJiffies = currentTime; |
| |
| return ret; |
| } |
| |
| /** |
| * Update the success/failure sliding window |
| * |
| * We keep a sliding window of the last 64 packets transmitted |
| * at this rate. window->data contains the bitmask of |
| * successful packets. |
| */ |
| // TODO - handle the position of the acks/fails in the window. for now - we only care about the |
| // counters (how many failed/succeeded regardless of when) |
| static BOOLEAN _rsMngCollectTlcData(RS_MNG_STA_INFO_S* staInfo, int attempts, int successes) { |
| RS_MNG_WIN_STAT_S* window; |
| const RS_MNG_RATE_S* rsMngRate = &staInfo->rateTblInfo.rsMngRate; |
| U32 expectedTpt; |
| U32 tmpSumFrames = attempts; |
| U32 tmpExtraFrames; |
| U32 windowSize; |
| BOOLEAN ret = TRUE; |
| |
| if (staInfo->searchBetterTbl) { |
| RS_MNG_SEARCH_COL_DATA* searchData = &staInfo->searchColData; |
| |
| window = &searchData->win; |
| expectedTpt = searchData->expectedTpt; |
| rsMngRate = &searchData->rsMngRate; |
| } else if (_rsMngTpcIsActive(staInfo)) { |
| window = &staInfo->tpcTable.windows[staInfo->tpcTable.currStep]; |
| // In TPC we don't care about expected/average tpt, only about success ratio. Initialize |
| // this var here anyways so the compiler won't complain. |
| expectedTpt = 0; |
| } else { |
| RS_MNG_TBL_INFO_S* tblInfo = &staInfo->rateTblInfo; |
| U32 rateIdx = _rsMngRateGetIdx(&tblInfo->rsMngRate); |
| |
| window = &tblInfo->win[rateIdx]; |
| expectedTpt = _rsMngGetExpectedTpt(staInfo, &rsMngColumns[tblInfo->column], |
| _rsMngRateGetBw(&tblInfo->rsMngRate), |
| !!(staInfo->mvmsta->agg_tids), rateIdx); |
| } |
| |
| windowSize = _rsMngRateGetMode(rsMngRate) != TLC_MNG_MODE_LEGACY |
| ? RS_MNG_MAX_WINDOW_SIZE |
| : RS_MNG_MAX_WINDOW_SIZE_NON_HT; |
| |
| tmpSumFrames += window->framesCounter; |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngCollectTlcData: old data: frames: %d, success: %d", |
| window->framesCounter, window->successCounter); |
| |
| if (attempts >= windowSize) { |
| window->successCounter = (windowSize * successes) / attempts; |
| window->framesCounter = windowSize; |
| } else if (tmpSumFrames > windowSize) { |
| /***** Calculate window->successCounter *****/ |
| // TODO - replace with good algorithem. for now this is a workaround |
| tmpExtraFrames = (tmpSumFrames - windowSize); |
| |
| // 1. calculated the ratio of window->successCounter in window->framesCounter and substruct |
| // the matching part of the frames substructed |
| // to avoid exceeding the MAX window size. |
| window->successCounter -= |
| (tmpExtraFrames * window->successCounter) / MAX(1, window->framesCounter); |
| |
| // 2. add the successes to window->successCounter, as long as it dosn't exceed the max |
| // window size |
| window->successCounter = min_t(U32, (window->successCounter + successes), windowSize); |
| |
| window->framesCounter = windowSize; |
| } else { |
| if (!WARN_ON(!((window->successCounter + successes) <= windowSize))) { |
| window->successCounter += successes; |
| window->framesCounter += attempts; |
| } |
| } |
| |
| // Calculate current success ratio |
| if (window->framesCounter > 0) { |
| window->successRatio = (128 * window->successCounter) / window->framesCounter; |
| |
| // Calculate average throughput, if we have enough history. |
| if (_isAvgTptCalcPossible(window)) { |
| window->averageTpt = ((window->successRatio * expectedTpt) + 64) / 128; |
| } else { |
| window->averageTpt = RS_MNG_INVALID_VAL; |
| ret = FALSE; |
| } |
| } else { |
| window->successRatio = RS_MNG_INVALID_VAL; |
| window->averageTpt = RS_MNG_INVALID_VAL; |
| ret = FALSE; |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngCollectTlcData: new data: framesCounter: %d, successCounter: %d, " |
| "successRatio: %d/128, avg Tpt: %d, expected Tpt: %d", |
| window->framesCounter, window->successCounter, window->successRatio, |
| window->averageTpt, expectedTpt); |
| |
| return ret; |
| } |
| |
| static U16 _rsMngGetCurrentThreshold(const RS_MNG_STA_INFO_S* staInfo) { |
| return (U16)(_rsMngIsTestWindow(staInfo) ? RS_MNG_UPSCALE_AGG_FRAME_COUNT : RS_STAT_THOLD); |
| } |
| |
| static BOOLEAN _rsMngUpdateGlobalStats(RS_MNG_STA_INFO_S* staInfo, TLC_STAT_COMMON_API_S* stats) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngUpdateGlobalStats: tpc stats: no-tpc %u, step1 %u, step2 %u, step3 %u, step4 " |
| "%u, step5 %u", |
| g_rsMngTpcStats.noTpc, g_rsMngTpcStats.step[0], g_rsMngTpcStats.step[1], |
| g_rsMngTpcStats.step[2], g_rsMngTpcStats.step[3], g_rsMngTpcStats.step[4]); |
| |
| if (staInfo->rsMngState == RS_MNG_STATE_STAY_IN_COLUMN && !_rsMngIsTestWindow(staInfo)) { |
| BOOLEAN isNonHt = _rsMngRateGetMode(&staInfo->rateTblInfo.rsMngRate) == TLC_MNG_MODE_LEGACY; |
| |
| staInfo->totalFramesSuccess += stats->acked[0] + stats->acked[1]; |
| staInfo->totalFramesFailed += |
| stats->txed[0] - stats->acked[0] + stats->txed[1] - stats->acked[1]; |
| staInfo->txedFrames += stats->txed[0] + stats->txed[1]; |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "Total frames succeeded: %d, total frames failed: %d", |
| staInfo->totalFramesSuccess, staInfo->totalFramesFailed); |
| |
| // If we have been in this column long enough (regardless of the fail/success rate or time |
| // elapsed) - start the statistics calculation from scratch |
| if (staInfo->txedFrames >= g_rsMngStaModLimits[isNonHt].clearTblWindowsLimit) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "_rsMngUpdateGlobalStats: Stayed in same column for a long time. Clear " |
| "table windows. modulation counter was %d", |
| staInfo->txedFrames); |
| |
| staInfo->txedFrames = 0; |
| _rsMngClearTblWindows(staInfo); |
| } |
| } |
| |
| staInfo->framesSinceLastRun += stats->txed[0] + stats->txed[1]; |
| if (staInfo->framesSinceLastRun >= _rsMngGetCurrentThreshold(staInfo)) { |
| staInfo->framesSinceLastRun = 0; |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| static void tlcStatUpdateHandler(RS_MNG_STA_INFO_S* staInfo, TLC_STAT_COMMON_API_S* stats, |
| struct iwl_mvm* mvm, struct ieee80211_sta* sta, int tid, |
| bool is_ndp) { |
| BOOLEAN forceLmacUpdate = FALSE; |
| int i; |
| |
| if (!staInfo->enabled) { |
| // Could happen if statistics from lmac were sent while umac was handling remove station. |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "tlcStatUpdateHandler: received stats for invalid station %d. Ignoring", i); |
| return; |
| } |
| |
| if (!(stats->txed[0] || stats->txed[1])) { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "tlcStatUpdateHandler: no new info for station %u, skipping it", i); |
| return; |
| } |
| |
| // TODO: optimize, no need for array |
| staInfo->amsduInAmpdu = 0; |
| for (i = 0; i < IWL_MAX_TID_COUNT; i++) |
| if (staInfo->mvmsta->tid_data[i].amsdu_in_ampdu_allowed) { |
| staInfo->amsduInAmpdu |= BIT(i); |
| } |
| |
| if (!_rsMngIsTestWindow(staInfo) && staInfo->amsduSupport && staInfo->mvmsta->agg_tids && |
| staInfo->amsduInAmpdu) { |
| forceLmacUpdate = |
| _rsMngCollectAmsduTlcData(staInfo, stats->baTxed, stats->baAcked, stats->trafficLoad); |
| } |
| |
| if (_rsMngAreAggsSupported(staInfo->config.bestSuppMode)) { |
| // find all tids such that data was sent on them but aggregations weren't opened for them |
| // yet |
| if (tid < IWL_MAX_TID_COUNT && !is_ndp) { iwl_start_agg(mvm, sta, tid); } |
| } |
| |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "tlcStatUpdateHandler: Received statistics for sta %d, txed[0]: %d, acked[0]: %d, " |
| "tids: 0x%x", |
| i, stats->txed[0], stats->acked[0], stats->tids); |
| |
| if (!staInfo->ignoreNextTlcNotif && !staInfo->fixedRate) { |
| BOOLEAN doRateScale = _rsMngUpdateGlobalStats(staInfo, stats); |
| doRateScale &= _rsMngCollectTlcData(staInfo, stats->txed[0], stats->acked[0]); |
| |
| if (doRateScale) { |
| _rsMngRateScalePerform(staInfo, forceLmacUpdate); |
| } else { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "tlcStatUpdateHandler: not performing rate scaling. #frames since last rate " |
| "scale perform: %d", |
| staInfo->framesSinceLastRun); |
| } |
| } else { |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, |
| "tlcStatUpdateHandler: ignoring notification. fixed rate: 0x%x", |
| staInfo->fixedRate); |
| staInfo->ignoreNextTlcNotif = FALSE; |
| } |
| } |
| |
| /*********************************************************************/ |
| /* External Functions + funcs used by them */ |
| /*********************************************************************/ |
| |
| static RS_MNG_COLUMN_DESC_E _rsMngGetColByRate(RS_MNG_RATE_S* rsMngRate) { |
| RS_MNG_GI_E gi = _rsMngRateGetGi(rsMngRate); |
| U08 ant = _rsMngRateGetAnt(rsMngRate); |
| RS_MNG_COLUMN_DESC_E colId = RS_MNG_COL_INVALID; |
| |
| switch (_rsMngRateGetModulation(rsMngRate)) { |
| case RS_MNG_MODUL_LEGACY: |
| switch (ant) { |
| case TLC_MNG_CHAIN_A_MSK: |
| colId = RS_MNG_COL_NON_HT_ANT_A; |
| break; |
| case TLC_MNG_CHAIN_B_MSK: |
| colId = RS_MNG_COL_NON_HT_ANT_B; |
| break; |
| default: |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, ERROR, "invalid antenna Msk 0x%x for legacy rate", |
| _rsMngRateGetAnt(rsMngRate)); |
| break; |
| } |
| break; |
| case RS_MNG_MODUL_SISO: |
| switch (gi) { |
| case HT_VHT_NGI: |
| colId = RS_MNG_COL_SISO_ANT_A; |
| break; |
| case HT_VHT_SGI: |
| colId = RS_MNG_COL_SISO_ANT_A_SGI; |
| break; |
| case HE_3_2_GI: |
| colId = RS_MNG_COL_HE_3_2_SISO_ANT_A; |
| break; |
| case HE_1_6_GI: |
| colId = RS_MNG_COL_HE_1_6_SISO_ANT_A; |
| break; |
| case HE_0_8_GI: |
| colId = RS_MNG_COL_HE_0_8_SISO_ANT_A; |
| break; |
| } |
| |
| if (ant == TLC_MNG_CHAIN_B_MSK) { |
| // This works thanks to the compilation asserts near _rsMngSetVisitedColumn |
| colId ^= 1; |
| } |
| break; |
| case RS_MNG_MODUL_MIMO2: |
| switch (gi) { |
| case HT_VHT_NGI: |
| colId = RS_MNG_COL_MIMO2; |
| break; |
| case HT_VHT_SGI: |
| colId = RS_MNG_COL_MIMO2_SGI; |
| break; |
| case HE_3_2_GI: |
| colId = RS_MNG_COL_HE_3_2_MIMO; |
| break; |
| case HE_1_6_GI: |
| colId = RS_MNG_COL_HE_1_6_MIMO; |
| break; |
| case HE_0_8_GI: |
| colId = RS_MNG_COL_HE_0_8_MIMO; |
| break; |
| } |
| break; |
| default: |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, ERROR, "invalid modulation %d", |
| _rsMngRateGetModulation(rsMngRate)); |
| } |
| |
| return colId; |
| } |
| |
| static void _rsMngSetInitRate(RS_MNG_STA_INFO_S* staInfo, RS_MNG_RATE_S* rsMngRate) { |
| U16 nonHtRates = staInfo->config.nonHt; |
| TLC_MNG_MODE_E mode = staInfo->config.bestSuppMode; |
| |
| _rsMngRateSetMode(rsMngRate, mode); |
| if (mode == TLC_MNG_MODE_LEGACY) { |
| _rsMngRateSetModulation(rsMngRate, RS_MNG_MODUL_LEGACY); |
| _rsMngRateSetLdpc(rsMngRate, FALSE); |
| } else { |
| _rsMngRateSetModulation(rsMngRate, RS_MNG_MODUL_SISO); |
| _rsMngRateSetLdpc(rsMngRate, _rsMngIsLdpcAllowed(staInfo)); |
| } |
| |
| _rsMngRateSetBw(rsMngRate, CHANNEL_WIDTH20); |
| |
| if (mode == TLC_MNG_MODE_HE) { |
| _rsMngRateSetGi(rsMngRate, HE_3_2_GI); |
| } else { |
| _rsMngRateSetGi(rsMngRate, HT_VHT_NGI); |
| } |
| _rsMngRateSetBfer(rsMngRate, FALSE); |
| |
| if (mode > TLC_MNG_MODE_LEGACY && _rsMngIsStbcAllowed(staInfo, rsMngRate)) { |
| _rsMngRateSetStbc(rsMngRate, TRUE); |
| _rsMngRateSetAnt(rsMngRate, rsMngGetDualAntMsk()); |
| } else { |
| _rsMngRateSetStbc(rsMngRate, FALSE); |
| _rsMngRateSetAnt(rsMngRate, rsMngGetSingleAntMsk(staInfo->config.chainsEnabled)); |
| } |
| |
| if (mode > TLC_MNG_MODE_LEGACY) { |
| U08 idx = (U08)(_rsMngGetSuppRatesSameMode(staInfo, rsMngRate) & BIT(RS_MCS_3) ? RS_MCS_3 |
| : RS_MCS_0); |
| |
| _rsMngRateSetIdx(rsMngRate, idx); |
| } else { |
| if (LSB2ORD(nonHtRates) > RS_NON_HT_RATE_CCK_LAST) { |
| // 11a |
| _rsMngRateSetIdx(rsMngRate, RS_NON_HT_RATE_OFDM_24M); |
| } else if (MSB2ORD(nonHtRates) > RS_NON_HT_RATE_CCK_LAST) { |
| // 11g |
| _rsMngRateSetIdx(rsMngRate, RS_NON_HT_RATE_OFDM_18M); |
| } else { |
| // 11b |
| _rsMngRateSetIdx(rsMngRate, RS_NON_HT_RATE_CCK_5_5M); |
| } |
| |
| if (!(BIT(_rsMngRateGetIdx(rsMngRate)) & nonHtRates)) { |
| // we don't support the mean rate as defined in the SAS, |
| // so just start from the lowest supported rate. |
| _rsMngRateSetIdx(rsMngRate, (U08)LSB2ORD(nonHtRates)); |
| } |
| } |
| } |
| |
| static void rsMngInitTlcTbl(RS_MNG_STA_INFO_S* staInfo, RS_MNG_TBL_INFO_S* tblInfo) { |
| _rsMngSetInitRate(staInfo, &tblInfo->rsMngRate); |
| |
| tblInfo->column = _rsMngGetColByRate(&(tblInfo->rsMngRate)); |
| staInfo->stableColumn = tblInfo->column; |
| |
| _rsMngUpdateRateTbl(staInfo, TRUE); |
| } |
| |
| static void rsMngResetStaInfo(struct iwl_mvm* mvm, struct ieee80211_sta* sta, |
| struct iwl_mvm_sta* mvmsta, RS_MNG_STA_INFO_S* staInfo, |
| BOOLEAN reconfigure) { |
| U32 fixedRate = staInfo->fixedRate; |
| U16 aggDurationLimit = staInfo->aggDurationLimit; |
| U08 amsduInAmpdu = staInfo->amsduInAmpdu; |
| BOOLEAN longAggEnabled = staInfo->longAggEnabled; |
| |
| _memclr(staInfo, sizeof(*staInfo)); |
| |
| staInfo->mvm = mvm; |
| staInfo->sta = sta; |
| staInfo->mvmsta = mvmsta; |
| staInfo->lastSearchCycleEndTimeJiffies = jiffies; |
| staInfo->lastRateUpscaleTimeJiffies = jiffies; |
| staInfo->lastEnableJiffies = jiffies; |
| |
| if (reconfigure) { |
| staInfo->fixedRate = fixedRate; |
| staInfo->amsduInAmpdu = amsduInAmpdu; |
| staInfo->longAggEnabled = longAggEnabled; |
| staInfo->aggDurationLimit = aggDurationLimit; |
| } else { |
| staInfo->aggDurationLimit = RS_MNG_AGG_DURATION_LIMIT; |
| } |
| |
| // aggState IDLE is 0. so memclear sets it |
| staInfo->rsMngState = RS_MNG_STATE_STAY_IN_COLUMN; |
| |
| _rsMngRateInvalidate(&staInfo->rateTblInfo.rsMngRate); |
| _rsMngClearTblWindows(staInfo); |
| _rsMngClearWinArr(staInfo->tpcTable.windows, RS_MNG_TPC_NUM_STEPS); |
| |
| staInfo->tpcTable.currStep = RS_MNG_TPC_DISABLED; |
| staInfo->staBuffSize = RS_MNG_AGG_FRAME_CNT_LIMIT; |
| staInfo->amsduEnabledSize = RS_MNG_AMSDU_INVALID; |
| staInfo->amsduSupport = FALSE; |
| staInfo->failSafeCounter = 0; |
| staInfo->amsduBlacklist = 0; |
| } |
| |
| static bool _tlcMngConfigValid(TLC_MNG_CONFIG_PARAMS_CMD_API_S* params) { |
| U08 chainsEnabled = params->chainsEnabled; |
| |
| // no valid chain is selected |
| if (WARN_ON(!(chainsEnabled && (chainsEnabled & rsMngGetDualAntMsk()) == chainsEnabled))) { |
| return false; |
| } |
| |
| // at least one non-HT rate MUST be valid |
| if (WARN_ON(!params->nonHt)) { return false; } |
| |
| if (params->bestSuppMode == TLC_MNG_MODE_LEGACY) { |
| // check that the no non-HT rates are set |
| if (WARN_ON(params->mcs[TLC_MNG_NSS_1][0] || params->mcs[TLC_MNG_NSS_1][1] || |
| params->mcs[TLC_MNG_NSS_2][0] || params->mcs[TLC_MNG_NSS_2][1])) { |
| return false; |
| } |
| |
| // all config flags make sense only in HT/VHT/HE scenarios, and non-ht rates can only |
| // support 20MHz bandwidth |
| if (WARN_ON(!(!params->configFlags && params->maxChWidth == TLC_MNG_CH_WIDTH_20MHZ))) { |
| return false; |
| } |
| } else { // HT/VHT/HE |
| // check that there are valid rates for the best supported mode |
| if (WARN_ON(!(params->mcs[TLC_MNG_NSS_1][0]))) { return false; } |
| |
| if (chainsEnabled != rsMngGetDualAntMsk()) { |
| // the following flags all require 2 antennas to be used |
| if (WARN_ON(params->configFlags & |
| (TLC_MNG_CONFIG_FLAGS_STBC_MSK | TLC_MNG_CONFIG_FLAGS_HE_STBC_160MHZ_MSK | |
| TLC_MNG_CONFIG_FLAGS_HE_DCM_NSS_2_MSK))) { |
| return false; |
| } |
| |
| // only one chain => can't do mimo |
| if (WARN_ON(params->mcs[TLC_MNG_NSS_2][0] || params->mcs[TLC_MNG_NSS_2][1])) { |
| return false; |
| } |
| } |
| |
| if (params->bestSuppMode == TLC_MNG_MODE_HT) { |
| if (WARN_ON(!((params->bestSuppMode == TLC_MNG_MODE_HT && |
| params->maxChWidth <= TLC_MNG_CH_WIDTH_40MHZ) || |
| params->maxChWidth < TLC_MNG_CH_WIDTH_MAX))) { |
| return false; |
| } |
| } |
| |
| if (params->bestSuppMode < TLC_MNG_MODE_HE) { |
| // the following flags are for HE only |
| if (WARN_ON(params->configFlags & (TLC_MNG_CONFIG_FLAGS_HE_STBC_160MHZ_MSK | |
| TLC_MNG_CONFIG_FLAGS_HE_DCM_NSS_1_MSK | |
| TLC_MNG_CONFIG_FLAGS_HE_DCM_NSS_2_MSK | |
| TLC_MNG_CONFIG_FLAGS_HE_BLOCK_2X_LTF_MSK))) { |
| return false; |
| } |
| } else { |
| if (WARN_ON(params->sgiChWidthSupport)) { return false; } |
| } |
| } |
| |
| if (WARN_ON(!(params->amsduSupported <= TLC_AMSDU_SUPPORTED))) { return false; } |
| |
| return true; |
| } |
| |
| // rs_initialize_lq |
| // |
| // Initialize a station's hardware rate table |
| // The uCode's station table contains a table of fallback rates |
| // for automatic fallback during transmission. |
| // |
| // NOTE: This sets up a default set of values. These will be replaced later |
| static void _rsMngTlcInit(RS_MNG_STA_INFO_S* staInfo) { |
| RS_MNG_TBL_INFO_S* tblInfo = &staInfo->rateTblInfo; |
| |
| rsMngInitTlcTbl(staInfo, tblInfo); |
| |
| // Start in search cycle state in order allow quick convergence on the optimal rate. |
| staInfo->rsMngState = RS_MNG_STATE_SEARCH_CYCLE_STARTED; |
| _rsMngSetVisitedColumn(staInfo, tblInfo->column); |
| _rsMngPrepareForBwChangeAttempt(staInfo, &staInfo->rateTblInfo.rsMngRate); |
| DBG_PRINTF(UT, TLC_OFFLOAD_DBG, INFO, "_rsMngTlcInit: starting at column %d", tblInfo->column); |
| } |
| |
| /**********************************************************************/ |
| /* Cmd Handlers */ |
| /**********************************************************************/ |
| |
| static void cmdHandlerTlcMngConfig(struct iwl_mvm* mvm, struct ieee80211_sta* sta, |
| struct iwl_mvm_sta* mvmsta, RS_MNG_STA_INFO_S* staInfo, |
| TLC_MNG_CONFIG_PARAMS_CMD_API_S* config, BOOLEAN reconfigure) { |
| if (!_tlcMngConfigValid(config)) { return; } |
| |
| // Check if this a reconfiguration of an existing station |
| if (staInfo->enabled && reconfigure) { |
| // Switching between non-HT/HT/VHT/HE requires completely de-associating before |
| // reassociating with the new mode. That being said, windows driver always sends a tlc |
| // config command with only non-HT mode when first adding a station, and then updates it to |
| // the correct mode after association. Since the switch from non-HT to HT/VHT/HE doesn't |
| // require any extra processing here (no aggregation state to possibly change etc.), this is |
| // allowed even though it's weird. |
| if (WARN_ON(!(config->bestSuppMode >= staInfo->config.bestSuppMode))) { return; } |
| } |
| |
| rsMngResetStaInfo(mvm, sta, mvmsta, staInfo, staInfo->enabled && reconfigure); |
| BUILD_BUG_ON(sizeof(staInfo->config) != sizeof(*config)); |
| memcpy(&staInfo->config, config, sizeof(staInfo->config)); |
| |
| rsMngInitAmsdu(staInfo); |
| |
| // send LQ command with basic rates table |
| _rsMngTlcInit(staInfo); |
| |
| staInfo->enabled = TRUE; |
| } |