18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright(c) 2005 - 2014, 2018 - 2020 Intel Corporation. All rights reserved. 58c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 68c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Contact Information: 98c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 108c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci *****************************************************************************/ 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <net/mac80211.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 238c2ecf20Sopenharmony_ci#include "rs.h" 248c2ecf20Sopenharmony_ci#include "fw-api.h" 258c2ecf20Sopenharmony_ci#include "sta.h" 268c2ecf20Sopenharmony_ci#include "iwl-op-mode.h" 278c2ecf20Sopenharmony_ci#include "mvm.h" 288c2ecf20Sopenharmony_ci#include "debugfs.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Calculations of success ratio are done in fixed point where 12800 is 100%. 338c2ecf20Sopenharmony_ci * Use this macro when dealing with thresholds consts set as a percentage 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci#define RS_PERCENT(x) (128 * x) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic u8 rs_ht_to_legacy[] = { 388c2ecf20Sopenharmony_ci [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX, 398c2ecf20Sopenharmony_ci [IWL_RATE_MCS_1_INDEX] = IWL_RATE_9M_INDEX, 408c2ecf20Sopenharmony_ci [IWL_RATE_MCS_2_INDEX] = IWL_RATE_12M_INDEX, 418c2ecf20Sopenharmony_ci [IWL_RATE_MCS_3_INDEX] = IWL_RATE_18M_INDEX, 428c2ecf20Sopenharmony_ci [IWL_RATE_MCS_4_INDEX] = IWL_RATE_24M_INDEX, 438c2ecf20Sopenharmony_ci [IWL_RATE_MCS_5_INDEX] = IWL_RATE_36M_INDEX, 448c2ecf20Sopenharmony_ci [IWL_RATE_MCS_6_INDEX] = IWL_RATE_48M_INDEX, 458c2ecf20Sopenharmony_ci [IWL_RATE_MCS_7_INDEX] = IWL_RATE_54M_INDEX, 468c2ecf20Sopenharmony_ci [IWL_RATE_MCS_8_INDEX] = IWL_RATE_54M_INDEX, 478c2ecf20Sopenharmony_ci [IWL_RATE_MCS_9_INDEX] = IWL_RATE_54M_INDEX, 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic const u8 ant_toggle_lookup[] = { 518c2ecf20Sopenharmony_ci [ANT_NONE] = ANT_NONE, 528c2ecf20Sopenharmony_ci [ANT_A] = ANT_B, 538c2ecf20Sopenharmony_ci [ANT_B] = ANT_A, 548c2ecf20Sopenharmony_ci [ANT_AB] = ANT_AB, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \ 588c2ecf20Sopenharmony_ci [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ 598c2ecf20Sopenharmony_ci IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ 608c2ecf20Sopenharmony_ci IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ 618c2ecf20Sopenharmony_ci IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ 628c2ecf20Sopenharmony_ci IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\ 638c2ecf20Sopenharmony_ci IWL_RATE_##rp##M_INDEX, \ 648c2ecf20Sopenharmony_ci IWL_RATE_##rn##M_INDEX } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define IWL_DECLARE_MCS_RATE(s) \ 678c2ecf20Sopenharmony_ci [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP, \ 688c2ecf20Sopenharmony_ci IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ 698c2ecf20Sopenharmony_ci IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ 708c2ecf20Sopenharmony_ci IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ 718c2ecf20Sopenharmony_ci IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \ 728c2ecf20Sopenharmony_ci IWL_RATE_INVM_INDEX, \ 738c2ecf20Sopenharmony_ci IWL_RATE_INVM_INDEX } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * Parameter order: 778c2ecf20Sopenharmony_ci * rate, ht rate, prev rate, next rate 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * If there isn't a valid next or previous rate then INV is used which 808c2ecf20Sopenharmony_ci * maps to IWL_RATE_INVALID 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_cistatic const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = { 848c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(1, INV, INV, 2), /* 1mbps */ 858c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(2, INV, 1, 5), /* 2mbps */ 868c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(5, INV, 2, 11), /*5.5mbps */ 878c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(11, INV, 9, 12), /* 11mbps */ 888c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(6, 0, 5, 11), /* 6mbps ; MCS 0 */ 898c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(9, INV, 6, 11), /* 9mbps */ 908c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(12, 1, 11, 18), /* 12mbps ; MCS 1 */ 918c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(18, 2, 12, 24), /* 18mbps ; MCS 2 */ 928c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(24, 3, 18, 36), /* 24mbps ; MCS 3 */ 938c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(36, 4, 24, 48), /* 36mbps ; MCS 4 */ 948c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(48, 5, 36, 54), /* 48mbps ; MCS 5 */ 958c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(54, 6, 48, INV), /* 54mbps ; MCS 6 */ 968c2ecf20Sopenharmony_ci IWL_DECLARE_MCS_RATE(7), /* MCS 7 */ 978c2ecf20Sopenharmony_ci IWL_DECLARE_MCS_RATE(8), /* MCS 8 */ 988c2ecf20Sopenharmony_ci IWL_DECLARE_MCS_RATE(9), /* MCS 9 */ 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cienum rs_action { 1028c2ecf20Sopenharmony_ci RS_ACTION_STAY = 0, 1038c2ecf20Sopenharmony_ci RS_ACTION_DOWNSCALE = -1, 1048c2ecf20Sopenharmony_ci RS_ACTION_UPSCALE = 1, 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cienum rs_column_mode { 1088c2ecf20Sopenharmony_ci RS_INVALID = 0, 1098c2ecf20Sopenharmony_ci RS_LEGACY, 1108c2ecf20Sopenharmony_ci RS_SISO, 1118c2ecf20Sopenharmony_ci RS_MIMO2, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define MAX_NEXT_COLUMNS 7 1158c2ecf20Sopenharmony_ci#define MAX_COLUMN_CHECKS 3 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistruct rs_tx_column; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_citypedef bool (*allow_column_func_t) (struct iwl_mvm *mvm, 1208c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 1218c2ecf20Sopenharmony_ci struct rs_rate *rate, 1228c2ecf20Sopenharmony_ci const struct rs_tx_column *next_col); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistruct rs_tx_column { 1258c2ecf20Sopenharmony_ci enum rs_column_mode mode; 1268c2ecf20Sopenharmony_ci u8 ant; 1278c2ecf20Sopenharmony_ci bool sgi; 1288c2ecf20Sopenharmony_ci enum rs_column next_columns[MAX_NEXT_COLUMNS]; 1298c2ecf20Sopenharmony_ci allow_column_func_t checks[MAX_COLUMN_CHECKS]; 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 1338c2ecf20Sopenharmony_ci struct rs_rate *rate, 1348c2ecf20Sopenharmony_ci const struct rs_tx_column *next_col) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 1408c2ecf20Sopenharmony_ci struct rs_rate *rate, 1418c2ecf20Sopenharmony_ci const struct rs_tx_column *next_col) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci if (!sta->ht_cap.ht_supported) 1448c2ecf20Sopenharmony_ci return false; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (sta->smps_mode == IEEE80211_SMPS_STATIC) 1478c2ecf20Sopenharmony_ci return false; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) < 2) 1508c2ecf20Sopenharmony_ci return false; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) 1538c2ecf20Sopenharmony_ci return false; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (mvm->nvm_data->sku_cap_mimo_disabled) 1568c2ecf20Sopenharmony_ci return false; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return true; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 1628c2ecf20Sopenharmony_ci struct rs_rate *rate, 1638c2ecf20Sopenharmony_ci const struct rs_tx_column *next_col) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci if (!sta->ht_cap.ht_supported) 1668c2ecf20Sopenharmony_ci return false; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return true; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 1728c2ecf20Sopenharmony_ci struct rs_rate *rate, 1738c2ecf20Sopenharmony_ci const struct rs_tx_column *next_col) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 1768c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (is_ht20(rate) && (ht_cap->cap & 1798c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_20)) 1808c2ecf20Sopenharmony_ci return true; 1818c2ecf20Sopenharmony_ci if (is_ht40(rate) && (ht_cap->cap & 1828c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_40)) 1838c2ecf20Sopenharmony_ci return true; 1848c2ecf20Sopenharmony_ci if (is_ht80(rate) && (vht_cap->cap & 1858c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SHORT_GI_80)) 1868c2ecf20Sopenharmony_ci return true; 1878c2ecf20Sopenharmony_ci if (is_ht160(rate) && (vht_cap->cap & 1888c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SHORT_GI_160)) 1898c2ecf20Sopenharmony_ci return true; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return false; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic const struct rs_tx_column rs_tx_columns[] = { 1958c2ecf20Sopenharmony_ci [RS_COLUMN_LEGACY_ANT_A] = { 1968c2ecf20Sopenharmony_ci .mode = RS_LEGACY, 1978c2ecf20Sopenharmony_ci .ant = ANT_A, 1988c2ecf20Sopenharmony_ci .next_columns = { 1998c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_B, 2008c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_A, 2018c2ecf20Sopenharmony_ci RS_COLUMN_MIMO2, 2028c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2038c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2048c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2058c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2068c2ecf20Sopenharmony_ci }, 2078c2ecf20Sopenharmony_ci .checks = { 2088c2ecf20Sopenharmony_ci rs_ant_allow, 2098c2ecf20Sopenharmony_ci }, 2108c2ecf20Sopenharmony_ci }, 2118c2ecf20Sopenharmony_ci [RS_COLUMN_LEGACY_ANT_B] = { 2128c2ecf20Sopenharmony_ci .mode = RS_LEGACY, 2138c2ecf20Sopenharmony_ci .ant = ANT_B, 2148c2ecf20Sopenharmony_ci .next_columns = { 2158c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_A, 2168c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_B, 2178c2ecf20Sopenharmony_ci RS_COLUMN_MIMO2, 2188c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2198c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2208c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2218c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2228c2ecf20Sopenharmony_ci }, 2238c2ecf20Sopenharmony_ci .checks = { 2248c2ecf20Sopenharmony_ci rs_ant_allow, 2258c2ecf20Sopenharmony_ci }, 2268c2ecf20Sopenharmony_ci }, 2278c2ecf20Sopenharmony_ci [RS_COLUMN_SISO_ANT_A] = { 2288c2ecf20Sopenharmony_ci .mode = RS_SISO, 2298c2ecf20Sopenharmony_ci .ant = ANT_A, 2308c2ecf20Sopenharmony_ci .next_columns = { 2318c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_B, 2328c2ecf20Sopenharmony_ci RS_COLUMN_MIMO2, 2338c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_A_SGI, 2348c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_A, 2358c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_B, 2368c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2378c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2388c2ecf20Sopenharmony_ci }, 2398c2ecf20Sopenharmony_ci .checks = { 2408c2ecf20Sopenharmony_ci rs_siso_allow, 2418c2ecf20Sopenharmony_ci rs_ant_allow, 2428c2ecf20Sopenharmony_ci }, 2438c2ecf20Sopenharmony_ci }, 2448c2ecf20Sopenharmony_ci [RS_COLUMN_SISO_ANT_B] = { 2458c2ecf20Sopenharmony_ci .mode = RS_SISO, 2468c2ecf20Sopenharmony_ci .ant = ANT_B, 2478c2ecf20Sopenharmony_ci .next_columns = { 2488c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_A, 2498c2ecf20Sopenharmony_ci RS_COLUMN_MIMO2, 2508c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_B_SGI, 2518c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_A, 2528c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_B, 2538c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2548c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2558c2ecf20Sopenharmony_ci }, 2568c2ecf20Sopenharmony_ci .checks = { 2578c2ecf20Sopenharmony_ci rs_siso_allow, 2588c2ecf20Sopenharmony_ci rs_ant_allow, 2598c2ecf20Sopenharmony_ci }, 2608c2ecf20Sopenharmony_ci }, 2618c2ecf20Sopenharmony_ci [RS_COLUMN_SISO_ANT_A_SGI] = { 2628c2ecf20Sopenharmony_ci .mode = RS_SISO, 2638c2ecf20Sopenharmony_ci .ant = ANT_A, 2648c2ecf20Sopenharmony_ci .sgi = true, 2658c2ecf20Sopenharmony_ci .next_columns = { 2668c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_B_SGI, 2678c2ecf20Sopenharmony_ci RS_COLUMN_MIMO2_SGI, 2688c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_A, 2698c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_A, 2708c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_B, 2718c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2728c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2738c2ecf20Sopenharmony_ci }, 2748c2ecf20Sopenharmony_ci .checks = { 2758c2ecf20Sopenharmony_ci rs_siso_allow, 2768c2ecf20Sopenharmony_ci rs_ant_allow, 2778c2ecf20Sopenharmony_ci rs_sgi_allow, 2788c2ecf20Sopenharmony_ci }, 2798c2ecf20Sopenharmony_ci }, 2808c2ecf20Sopenharmony_ci [RS_COLUMN_SISO_ANT_B_SGI] = { 2818c2ecf20Sopenharmony_ci .mode = RS_SISO, 2828c2ecf20Sopenharmony_ci .ant = ANT_B, 2838c2ecf20Sopenharmony_ci .sgi = true, 2848c2ecf20Sopenharmony_ci .next_columns = { 2858c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_A_SGI, 2868c2ecf20Sopenharmony_ci RS_COLUMN_MIMO2_SGI, 2878c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_B, 2888c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_A, 2898c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_B, 2908c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2918c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 2928c2ecf20Sopenharmony_ci }, 2938c2ecf20Sopenharmony_ci .checks = { 2948c2ecf20Sopenharmony_ci rs_siso_allow, 2958c2ecf20Sopenharmony_ci rs_ant_allow, 2968c2ecf20Sopenharmony_ci rs_sgi_allow, 2978c2ecf20Sopenharmony_ci }, 2988c2ecf20Sopenharmony_ci }, 2998c2ecf20Sopenharmony_ci [RS_COLUMN_MIMO2] = { 3008c2ecf20Sopenharmony_ci .mode = RS_MIMO2, 3018c2ecf20Sopenharmony_ci .ant = ANT_AB, 3028c2ecf20Sopenharmony_ci .next_columns = { 3038c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_A, 3048c2ecf20Sopenharmony_ci RS_COLUMN_MIMO2_SGI, 3058c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_A, 3068c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_B, 3078c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 3088c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 3098c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 3108c2ecf20Sopenharmony_ci }, 3118c2ecf20Sopenharmony_ci .checks = { 3128c2ecf20Sopenharmony_ci rs_mimo_allow, 3138c2ecf20Sopenharmony_ci }, 3148c2ecf20Sopenharmony_ci }, 3158c2ecf20Sopenharmony_ci [RS_COLUMN_MIMO2_SGI] = { 3168c2ecf20Sopenharmony_ci .mode = RS_MIMO2, 3178c2ecf20Sopenharmony_ci .ant = ANT_AB, 3188c2ecf20Sopenharmony_ci .sgi = true, 3198c2ecf20Sopenharmony_ci .next_columns = { 3208c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_A_SGI, 3218c2ecf20Sopenharmony_ci RS_COLUMN_MIMO2, 3228c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_A, 3238c2ecf20Sopenharmony_ci RS_COLUMN_LEGACY_ANT_B, 3248c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 3258c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 3268c2ecf20Sopenharmony_ci RS_COLUMN_INVALID, 3278c2ecf20Sopenharmony_ci }, 3288c2ecf20Sopenharmony_ci .checks = { 3298c2ecf20Sopenharmony_ci rs_mimo_allow, 3308c2ecf20Sopenharmony_ci rs_sgi_allow, 3318c2ecf20Sopenharmony_ci }, 3328c2ecf20Sopenharmony_ci }, 3338c2ecf20Sopenharmony_ci}; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic inline u8 rs_extract_rate(u32 rate_n_flags) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci /* also works for HT because bits 7:6 are zero there */ 3388c2ecf20Sopenharmony_ci return (u8)(rate_n_flags & RATE_LEGACY_RATE_MSK); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci int idx = 0; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) { 3468c2ecf20Sopenharmony_ci idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK; 3478c2ecf20Sopenharmony_ci idx += IWL_RATE_MCS_0_INDEX; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* skip 9M not supported in HT*/ 3508c2ecf20Sopenharmony_ci if (idx >= IWL_RATE_9M_INDEX) 3518c2ecf20Sopenharmony_ci idx += 1; 3528c2ecf20Sopenharmony_ci if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE)) 3538c2ecf20Sopenharmony_ci return idx; 3548c2ecf20Sopenharmony_ci } else if (rate_n_flags & RATE_MCS_VHT_MSK || 3558c2ecf20Sopenharmony_ci rate_n_flags & RATE_MCS_HE_MSK) { 3568c2ecf20Sopenharmony_ci idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; 3578c2ecf20Sopenharmony_ci idx += IWL_RATE_MCS_0_INDEX; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* skip 9M not supported in VHT*/ 3608c2ecf20Sopenharmony_ci if (idx >= IWL_RATE_9M_INDEX) 3618c2ecf20Sopenharmony_ci idx++; 3628c2ecf20Sopenharmony_ci if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE)) 3638c2ecf20Sopenharmony_ci return idx; 3648c2ecf20Sopenharmony_ci if ((rate_n_flags & RATE_MCS_HE_MSK) && 3658c2ecf20Sopenharmony_ci (idx <= IWL_LAST_HE_RATE)) 3668c2ecf20Sopenharmony_ci return idx; 3678c2ecf20Sopenharmony_ci } else { 3688c2ecf20Sopenharmony_ci /* legacy rate format, search for match in table */ 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci u8 legacy_rate = rs_extract_rate(rate_n_flags); 3718c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) 3728c2ecf20Sopenharmony_ci if (iwl_rates[idx].plcp == legacy_rate) 3738c2ecf20Sopenharmony_ci return idx; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return IWL_RATE_INVALID; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic void rs_rate_scale_perform(struct iwl_mvm *mvm, 3808c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 3818c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 3828c2ecf20Sopenharmony_ci int tid, bool ndp); 3838c2ecf20Sopenharmony_cistatic void rs_fill_lq_cmd(struct iwl_mvm *mvm, 3848c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 3858c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 3868c2ecf20Sopenharmony_ci const struct rs_rate *initial_rate); 3878c2ecf20Sopenharmony_cistatic void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/** 3908c2ecf20Sopenharmony_ci * The following tables contain the expected throughput metrics for all rates 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits 3938c2ecf20Sopenharmony_ci * 3948c2ecf20Sopenharmony_ci * where invalid entries are zeros. 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * CCK rates are only valid in legacy table and will only be used in G 3978c2ecf20Sopenharmony_ci * (2.4 GHz) band. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic const u16 expected_tpt_legacy[IWL_RATE_COUNT] = { 4018c2ecf20Sopenharmony_ci 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* Expected TpT tables. 4 indexes: 4058c2ecf20Sopenharmony_ci * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_cistatic const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = { 4088c2ecf20Sopenharmony_ci {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0}, 4098c2ecf20Sopenharmony_ci {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0}, 4108c2ecf20Sopenharmony_ci {0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0}, 4118c2ecf20Sopenharmony_ci {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0}, 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = { 4158c2ecf20Sopenharmony_ci {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275}, 4168c2ecf20Sopenharmony_ci {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280}, 4178c2ecf20Sopenharmony_ci {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173}, 4188c2ecf20Sopenharmony_ci {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284}, 4198c2ecf20Sopenharmony_ci}; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = { 4228c2ecf20Sopenharmony_ci {0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308}, 4238c2ecf20Sopenharmony_ci {0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312}, 4248c2ecf20Sopenharmony_ci {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466}, 4258c2ecf20Sopenharmony_ci {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691}, 4268c2ecf20Sopenharmony_ci}; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic const u16 expected_tpt_siso_160MHz[4][IWL_RATE_COUNT] = { 4298c2ecf20Sopenharmony_ci {0, 0, 0, 0, 191, 0, 244, 288, 298, 308, 313, 318, 323, 328, 330}, 4308c2ecf20Sopenharmony_ci {0, 0, 0, 0, 200, 0, 251, 293, 302, 312, 317, 322, 327, 332, 334}, 4318c2ecf20Sopenharmony_ci {0, 0, 0, 0, 439, 0, 875, 1307, 1736, 2584, 3419, 3831, 4240, 5049, 5581}, 4328c2ecf20Sopenharmony_ci {0, 0, 0, 0, 488, 0, 972, 1451, 1925, 2864, 3785, 4240, 4691, 5581, 6165}, 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { 4368c2ecf20Sopenharmony_ci {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0}, 4378c2ecf20Sopenharmony_ci {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0}, 4388c2ecf20Sopenharmony_ci {0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0}, 4398c2ecf20Sopenharmony_ci {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0}, 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { 4438c2ecf20Sopenharmony_ci {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300}, 4448c2ecf20Sopenharmony_ci {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303}, 4458c2ecf20Sopenharmony_ci {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053}, 4468c2ecf20Sopenharmony_ci {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221}, 4478c2ecf20Sopenharmony_ci}; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = { 4508c2ecf20Sopenharmony_ci {0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319}, 4518c2ecf20Sopenharmony_ci {0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320}, 4528c2ecf20Sopenharmony_ci {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219}, 4538c2ecf20Sopenharmony_ci {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545}, 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic const u16 expected_tpt_mimo2_160MHz[4][IWL_RATE_COUNT] = { 4578c2ecf20Sopenharmony_ci {0, 0, 0, 0, 240, 0, 278, 308, 313, 319, 322, 324, 328, 330, 334}, 4588c2ecf20Sopenharmony_ci {0, 0, 0, 0, 247, 0, 282, 310, 315, 320, 323, 325, 329, 332, 338}, 4598c2ecf20Sopenharmony_ci {0, 0, 0, 0, 875, 0, 1735, 2582, 3414, 5043, 6619, 7389, 8147, 9629, 10592}, 4608c2ecf20Sopenharmony_ci {0, 0, 0, 0, 971, 0, 1925, 2861, 3779, 5574, 7304, 8147, 8976, 10592, 11640}, 4618c2ecf20Sopenharmony_ci}; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci/* mbps, mcs */ 4648c2ecf20Sopenharmony_cistatic const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { 4658c2ecf20Sopenharmony_ci { "1", "BPSK DSSS"}, 4668c2ecf20Sopenharmony_ci { "2", "QPSK DSSS"}, 4678c2ecf20Sopenharmony_ci {"5.5", "BPSK CCK"}, 4688c2ecf20Sopenharmony_ci { "11", "QPSK CCK"}, 4698c2ecf20Sopenharmony_ci { "6", "BPSK 1/2"}, 4708c2ecf20Sopenharmony_ci { "9", "BPSK 1/2"}, 4718c2ecf20Sopenharmony_ci { "12", "QPSK 1/2"}, 4728c2ecf20Sopenharmony_ci { "18", "QPSK 3/4"}, 4738c2ecf20Sopenharmony_ci { "24", "16QAM 1/2"}, 4748c2ecf20Sopenharmony_ci { "36", "16QAM 3/4"}, 4758c2ecf20Sopenharmony_ci { "48", "64QAM 2/3"}, 4768c2ecf20Sopenharmony_ci { "54", "64QAM 3/4"}, 4778c2ecf20Sopenharmony_ci { "60", "64QAM 5/6"}, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci#define MCS_INDEX_PER_STREAM (8) 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic const char *rs_pretty_ant(u8 ant) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci static const char * const ant_name[] = { 4858c2ecf20Sopenharmony_ci [ANT_NONE] = "None", 4868c2ecf20Sopenharmony_ci [ANT_A] = "A", 4878c2ecf20Sopenharmony_ci [ANT_B] = "B", 4888c2ecf20Sopenharmony_ci [ANT_AB] = "AB", 4898c2ecf20Sopenharmony_ci [ANT_C] = "C", 4908c2ecf20Sopenharmony_ci [ANT_AC] = "AC", 4918c2ecf20Sopenharmony_ci [ANT_BC] = "BC", 4928c2ecf20Sopenharmony_ci [ANT_ABC] = "ABC", 4938c2ecf20Sopenharmony_ci }; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (ant > ANT_ABC) 4968c2ecf20Sopenharmony_ci return "UNKNOWN"; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return ant_name[ant]; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic const char *rs_pretty_lq_type(enum iwl_table_type type) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci static const char * const lq_types[] = { 5048c2ecf20Sopenharmony_ci [LQ_NONE] = "NONE", 5058c2ecf20Sopenharmony_ci [LQ_LEGACY_A] = "LEGACY_A", 5068c2ecf20Sopenharmony_ci [LQ_LEGACY_G] = "LEGACY_G", 5078c2ecf20Sopenharmony_ci [LQ_HT_SISO] = "HT SISO", 5088c2ecf20Sopenharmony_ci [LQ_HT_MIMO2] = "HT MIMO", 5098c2ecf20Sopenharmony_ci [LQ_VHT_SISO] = "VHT SISO", 5108c2ecf20Sopenharmony_ci [LQ_VHT_MIMO2] = "VHT MIMO", 5118c2ecf20Sopenharmony_ci [LQ_HE_SISO] = "HE SISO", 5128c2ecf20Sopenharmony_ci [LQ_HE_MIMO2] = "HE MIMO", 5138c2ecf20Sopenharmony_ci }; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (type < LQ_NONE || type >= LQ_MAX) 5168c2ecf20Sopenharmony_ci return "UNKNOWN"; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return lq_types[type]; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic char *rs_pretty_rate(const struct rs_rate *rate) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci static char buf[40]; 5248c2ecf20Sopenharmony_ci static const char * const legacy_rates[] = { 5258c2ecf20Sopenharmony_ci [IWL_RATE_1M_INDEX] = "1M", 5268c2ecf20Sopenharmony_ci [IWL_RATE_2M_INDEX] = "2M", 5278c2ecf20Sopenharmony_ci [IWL_RATE_5M_INDEX] = "5.5M", 5288c2ecf20Sopenharmony_ci [IWL_RATE_11M_INDEX] = "11M", 5298c2ecf20Sopenharmony_ci [IWL_RATE_6M_INDEX] = "6M", 5308c2ecf20Sopenharmony_ci [IWL_RATE_9M_INDEX] = "9M", 5318c2ecf20Sopenharmony_ci [IWL_RATE_12M_INDEX] = "12M", 5328c2ecf20Sopenharmony_ci [IWL_RATE_18M_INDEX] = "18M", 5338c2ecf20Sopenharmony_ci [IWL_RATE_24M_INDEX] = "24M", 5348c2ecf20Sopenharmony_ci [IWL_RATE_36M_INDEX] = "36M", 5358c2ecf20Sopenharmony_ci [IWL_RATE_48M_INDEX] = "48M", 5368c2ecf20Sopenharmony_ci [IWL_RATE_54M_INDEX] = "54M", 5378c2ecf20Sopenharmony_ci }; 5388c2ecf20Sopenharmony_ci static const char *const ht_vht_rates[] = { 5398c2ecf20Sopenharmony_ci [IWL_RATE_MCS_0_INDEX] = "MCS0", 5408c2ecf20Sopenharmony_ci [IWL_RATE_MCS_1_INDEX] = "MCS1", 5418c2ecf20Sopenharmony_ci [IWL_RATE_MCS_2_INDEX] = "MCS2", 5428c2ecf20Sopenharmony_ci [IWL_RATE_MCS_3_INDEX] = "MCS3", 5438c2ecf20Sopenharmony_ci [IWL_RATE_MCS_4_INDEX] = "MCS4", 5448c2ecf20Sopenharmony_ci [IWL_RATE_MCS_5_INDEX] = "MCS5", 5458c2ecf20Sopenharmony_ci [IWL_RATE_MCS_6_INDEX] = "MCS6", 5468c2ecf20Sopenharmony_ci [IWL_RATE_MCS_7_INDEX] = "MCS7", 5478c2ecf20Sopenharmony_ci [IWL_RATE_MCS_8_INDEX] = "MCS8", 5488c2ecf20Sopenharmony_ci [IWL_RATE_MCS_9_INDEX] = "MCS9", 5498c2ecf20Sopenharmony_ci }; 5508c2ecf20Sopenharmony_ci const char *rate_str; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (is_type_legacy(rate->type) && (rate->index <= IWL_RATE_54M_INDEX)) 5538c2ecf20Sopenharmony_ci rate_str = legacy_rates[rate->index]; 5548c2ecf20Sopenharmony_ci else if ((is_type_ht(rate->type) || is_type_vht(rate->type)) && 5558c2ecf20Sopenharmony_ci (rate->index >= IWL_RATE_MCS_0_INDEX) && 5568c2ecf20Sopenharmony_ci (rate->index <= IWL_RATE_MCS_9_INDEX)) 5578c2ecf20Sopenharmony_ci rate_str = ht_vht_rates[rate->index]; 5588c2ecf20Sopenharmony_ci else 5598c2ecf20Sopenharmony_ci rate_str = "BAD_RATE"; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type), 5628c2ecf20Sopenharmony_ci rs_pretty_ant(rate->ant), rate_str); 5638c2ecf20Sopenharmony_ci return buf; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate, 5678c2ecf20Sopenharmony_ci const char *prefix) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 5708c2ecf20Sopenharmony_ci "%s: %s BW: %d SGI: %d LDPC: %d STBC: %d\n", 5718c2ecf20Sopenharmony_ci prefix, rs_pretty_rate(rate), rate->bw, 5728c2ecf20Sopenharmony_ci rate->sgi, rate->ldpc, rate->stbc); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci window->data = 0; 5788c2ecf20Sopenharmony_ci window->success_counter = 0; 5798c2ecf20Sopenharmony_ci window->success_ratio = IWL_INVALID_VALUE; 5808c2ecf20Sopenharmony_ci window->counter = 0; 5818c2ecf20Sopenharmony_ci window->average_tpt = IWL_INVALID_VALUE; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm, 5858c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci int i; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Clearing up window stats\n"); 5908c2ecf20Sopenharmony_ci for (i = 0; i < IWL_RATE_COUNT; i++) 5918c2ecf20Sopenharmony_ci rs_rate_scale_clear_window(&tbl->win[i]); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++) 5948c2ecf20Sopenharmony_ci rs_rate_scale_clear_window(&tbl->tpc_win[i]); 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci return (ant_type & valid_antenna) == ant_type; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, 6038c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_data, u8 tid, 6048c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci int ret; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n", 6098c2ecf20Sopenharmony_ci sta->addr, tid); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* start BA session until the peer sends del BA */ 6128c2ecf20Sopenharmony_ci ret = ieee80211_start_tx_ba_session(sta, tid, 0); 6138c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 6148c2ecf20Sopenharmony_ci /* 6158c2ecf20Sopenharmony_ci * driver and mac80211 is out of sync 6168c2ecf20Sopenharmony_ci * this might be cause by reloading firmware 6178c2ecf20Sopenharmony_ci * stop the tx ba session here 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n", 6208c2ecf20Sopenharmony_ci tid); 6218c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_session(sta, tid); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci return ret; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic void rs_tl_turn_on_agg(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, 6278c2ecf20Sopenharmony_ci u8 tid, struct iwl_lq_sta *lq_sta, 6288c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* 6338c2ecf20Sopenharmony_ci * In AP mode, tid can be equal to IWL_MAX_TID_COUNT 6348c2ecf20Sopenharmony_ci * when the frame is not QoS 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(tid > IWL_MAX_TID_COUNT)) { 6378c2ecf20Sopenharmony_ci IWL_ERR(mvm, "tid exceeds max TID count: %d/%d\n", 6388c2ecf20Sopenharmony_ci tid, IWL_MAX_TID_COUNT); 6398c2ecf20Sopenharmony_ci return; 6408c2ecf20Sopenharmony_ci } else if (tid == IWL_MAX_TID_COUNT) { 6418c2ecf20Sopenharmony_ci return; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci tid_data = &mvmsta->tid_data[tid]; 6458c2ecf20Sopenharmony_ci if (mvmsta->sta_state >= IEEE80211_STA_AUTHORIZED && 6468c2ecf20Sopenharmony_ci tid_data->state == IWL_AGG_OFF && 6478c2ecf20Sopenharmony_ci (lq_sta->tx_agg_tid_en & BIT(tid)) && 6488c2ecf20Sopenharmony_ci tid_data->tx_count_last >= IWL_MVM_RS_AGG_START_THRESHOLD) { 6498c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "try to aggregate tid %d\n", tid); 6508c2ecf20Sopenharmony_ci if (rs_tl_turn_on_agg_for_tid(mvm, lq_sta, tid, sta) == 0) 6518c2ecf20Sopenharmony_ci tid_data->state = IWL_AGG_QUEUED; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic inline int get_num_of_ant_from_rate(u32 rate_n_flags) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + 6588c2ecf20Sopenharmony_ci !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + 6598c2ecf20Sopenharmony_ci !!(rate_n_flags & RATE_MCS_ANT_C_MSK); 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci/* 6638c2ecf20Sopenharmony_ci * Static function to get the expected throughput from an iwl_scale_tbl_info 6648c2ecf20Sopenharmony_ci * that wraps a NULL pointer check 6658c2ecf20Sopenharmony_ci */ 6668c2ecf20Sopenharmony_cistatic s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci if (tbl->expected_tpt) 6698c2ecf20Sopenharmony_ci return tbl->expected_tpt[rs_index]; 6708c2ecf20Sopenharmony_ci return 0; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci/** 6748c2ecf20Sopenharmony_ci * rs_collect_tx_data - Update the success/failure sliding window 6758c2ecf20Sopenharmony_ci * 6768c2ecf20Sopenharmony_ci * We keep a sliding window of the last 62 packets transmitted 6778c2ecf20Sopenharmony_ci * at this rate. window->data contains the bitmask of successful 6788c2ecf20Sopenharmony_ci * packets. 6798c2ecf20Sopenharmony_ci */ 6808c2ecf20Sopenharmony_cistatic int _rs_collect_tx_data(struct iwl_mvm *mvm, 6818c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 6828c2ecf20Sopenharmony_ci int scale_index, int attempts, int successes, 6838c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); 6868c2ecf20Sopenharmony_ci s32 fail_count, tpt; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* Get expected throughput */ 6898c2ecf20Sopenharmony_ci tpt = get_expected_tpt(tbl, scale_index); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* 6928c2ecf20Sopenharmony_ci * Keep track of only the latest 62 tx frame attempts in this rate's 6938c2ecf20Sopenharmony_ci * history window; anything older isn't really relevant any more. 6948c2ecf20Sopenharmony_ci * If we have filled up the sliding window, drop the oldest attempt; 6958c2ecf20Sopenharmony_ci * if the oldest attempt (highest bit in bitmap) shows "success", 6968c2ecf20Sopenharmony_ci * subtract "1" from the success counter (this is the main reason 6978c2ecf20Sopenharmony_ci * we keep these bitmaps!). 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci while (attempts > 0) { 7008c2ecf20Sopenharmony_ci if (window->counter >= IWL_RATE_MAX_WINDOW) { 7018c2ecf20Sopenharmony_ci /* remove earliest */ 7028c2ecf20Sopenharmony_ci window->counter = IWL_RATE_MAX_WINDOW - 1; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (window->data & mask) { 7058c2ecf20Sopenharmony_ci window->data &= ~mask; 7068c2ecf20Sopenharmony_ci window->success_counter--; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* Increment frames-attempted counter */ 7118c2ecf20Sopenharmony_ci window->counter++; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* Shift bitmap by one frame to throw away oldest history */ 7148c2ecf20Sopenharmony_ci window->data <<= 1; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* Mark the most recent #successes attempts as successful */ 7178c2ecf20Sopenharmony_ci if (successes > 0) { 7188c2ecf20Sopenharmony_ci window->success_counter++; 7198c2ecf20Sopenharmony_ci window->data |= 0x1; 7208c2ecf20Sopenharmony_ci successes--; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci attempts--; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* Calculate current success ratio, avoid divide-by-0! */ 7278c2ecf20Sopenharmony_ci if (window->counter > 0) 7288c2ecf20Sopenharmony_ci window->success_ratio = 128 * (100 * window->success_counter) 7298c2ecf20Sopenharmony_ci / window->counter; 7308c2ecf20Sopenharmony_ci else 7318c2ecf20Sopenharmony_ci window->success_ratio = IWL_INVALID_VALUE; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci fail_count = window->counter - window->success_counter; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* Calculate average throughput, if we have enough history. */ 7368c2ecf20Sopenharmony_ci if ((fail_count >= IWL_MVM_RS_RATE_MIN_FAILURE_TH) || 7378c2ecf20Sopenharmony_ci (window->success_counter >= IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) 7388c2ecf20Sopenharmony_ci window->average_tpt = (window->success_ratio * tpt + 64) / 128; 7398c2ecf20Sopenharmony_ci else 7408c2ecf20Sopenharmony_ci window->average_tpt = IWL_INVALID_VALUE; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return 0; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic int rs_collect_tpc_data(struct iwl_mvm *mvm, 7468c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 7478c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 7488c2ecf20Sopenharmony_ci int scale_index, int attempts, int successes, 7498c2ecf20Sopenharmony_ci u8 reduced_txp) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window = NULL; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION)) 7548c2ecf20Sopenharmony_ci return -EINVAL; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci window = &tbl->tpc_win[reduced_txp]; 7578c2ecf20Sopenharmony_ci return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, 7588c2ecf20Sopenharmony_ci window); 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic void rs_update_tid_tpt_stats(struct iwl_mvm *mvm, 7628c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta, 7638c2ecf20Sopenharmony_ci u8 tid, int successes) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (tid >= IWL_MAX_TID_COUNT) 7688c2ecf20Sopenharmony_ci return; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci tid_data = &mvmsta->tid_data[tid]; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* 7738c2ecf20Sopenharmony_ci * Measure if there're enough successful transmits per second. 7748c2ecf20Sopenharmony_ci * These statistics are used only to decide if we can start a 7758c2ecf20Sopenharmony_ci * BA session, so it should be updated only when A-MPDU is 7768c2ecf20Sopenharmony_ci * off. 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_ci if (tid_data->state != IWL_AGG_OFF) 7798c2ecf20Sopenharmony_ci return; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (time_is_before_jiffies(tid_data->tpt_meas_start + HZ) || 7828c2ecf20Sopenharmony_ci (tid_data->tx_count >= IWL_MVM_RS_AGG_START_THRESHOLD)) { 7838c2ecf20Sopenharmony_ci tid_data->tx_count_last = tid_data->tx_count; 7848c2ecf20Sopenharmony_ci tid_data->tx_count = 0; 7858c2ecf20Sopenharmony_ci tid_data->tpt_meas_start = jiffies; 7868c2ecf20Sopenharmony_ci } else { 7878c2ecf20Sopenharmony_ci tid_data->tx_count += successes; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic int rs_collect_tlc_data(struct iwl_mvm *mvm, 7928c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta, u8 tid, 7938c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 7948c2ecf20Sopenharmony_ci int scale_index, int attempts, int successes) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window = NULL; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) 7998c2ecf20Sopenharmony_ci return -EINVAL; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (tbl->column != RS_COLUMN_INVALID) { 8028c2ecf20Sopenharmony_ci struct lq_sta_pers *pers = &mvmsta->lq_sta.rs_drv.pers; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci pers->tx_stats[tbl->column][scale_index].total += attempts; 8058c2ecf20Sopenharmony_ci pers->tx_stats[tbl->column][scale_index].success += successes; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci rs_update_tid_tpt_stats(mvm, mvmsta, tid, successes); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* Select window for current tx bit rate */ 8118c2ecf20Sopenharmony_ci window = &(tbl->win[scale_index]); 8128c2ecf20Sopenharmony_ci return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, 8138c2ecf20Sopenharmony_ci window); 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci/* Convert rs_rate object into ucode rate bitmask */ 8178c2ecf20Sopenharmony_cistatic u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm, 8188c2ecf20Sopenharmony_ci struct rs_rate *rate) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci u32 ucode_rate = 0; 8218c2ecf20Sopenharmony_ci int index = rate->index; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci ucode_rate |= ((rate->ant << RATE_MCS_ANT_POS) & 8248c2ecf20Sopenharmony_ci RATE_MCS_ANT_ABC_MSK); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (is_legacy(rate)) { 8278c2ecf20Sopenharmony_ci ucode_rate |= iwl_rates[index].plcp; 8288c2ecf20Sopenharmony_ci if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) 8298c2ecf20Sopenharmony_ci ucode_rate |= RATE_MCS_CCK_MSK; 8308c2ecf20Sopenharmony_ci return ucode_rate; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* set RTS protection for all non legacy rates 8348c2ecf20Sopenharmony_ci * This helps with congested environments reducing the conflict cost to 8358c2ecf20Sopenharmony_ci * RTS retries only, instead of the entire BA packet. 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_ci ucode_rate |= RATE_MCS_RTS_REQUIRED_MSK; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (is_ht(rate)) { 8408c2ecf20Sopenharmony_ci if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) { 8418c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid HT rate index %d\n", index); 8428c2ecf20Sopenharmony_ci index = IWL_LAST_HT_RATE; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci ucode_rate |= RATE_MCS_HT_MSK; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (is_ht_siso(rate)) 8478c2ecf20Sopenharmony_ci ucode_rate |= iwl_rates[index].plcp_ht_siso; 8488c2ecf20Sopenharmony_ci else if (is_ht_mimo2(rate)) 8498c2ecf20Sopenharmony_ci ucode_rate |= iwl_rates[index].plcp_ht_mimo2; 8508c2ecf20Sopenharmony_ci else 8518c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 8528c2ecf20Sopenharmony_ci } else if (is_vht(rate)) { 8538c2ecf20Sopenharmony_ci if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) { 8548c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid VHT rate index %d\n", index); 8558c2ecf20Sopenharmony_ci index = IWL_LAST_VHT_RATE; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci ucode_rate |= RATE_MCS_VHT_MSK; 8588c2ecf20Sopenharmony_ci if (is_vht_siso(rate)) 8598c2ecf20Sopenharmony_ci ucode_rate |= iwl_rates[index].plcp_vht_siso; 8608c2ecf20Sopenharmony_ci else if (is_vht_mimo2(rate)) 8618c2ecf20Sopenharmony_ci ucode_rate |= iwl_rates[index].plcp_vht_mimo2; 8628c2ecf20Sopenharmony_ci else 8638c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci } else { 8668c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid rate->type %d\n", rate->type); 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (is_siso(rate) && rate->stbc) { 8708c2ecf20Sopenharmony_ci /* To enable STBC we need to set both a flag and ANT_AB */ 8718c2ecf20Sopenharmony_ci ucode_rate |= RATE_MCS_ANT_AB_MSK; 8728c2ecf20Sopenharmony_ci ucode_rate |= RATE_MCS_STBC_MSK; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci ucode_rate |= rate->bw; 8768c2ecf20Sopenharmony_ci if (rate->sgi) 8778c2ecf20Sopenharmony_ci ucode_rate |= RATE_MCS_SGI_MSK; 8788c2ecf20Sopenharmony_ci if (rate->ldpc) 8798c2ecf20Sopenharmony_ci ucode_rate |= RATE_MCS_LDPC_MSK; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci return ucode_rate; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci/* Convert a ucode rate into an rs_rate object */ 8858c2ecf20Sopenharmony_cistatic int rs_rate_from_ucode_rate(const u32 ucode_rate, 8868c2ecf20Sopenharmony_ci enum nl80211_band band, 8878c2ecf20Sopenharmony_ci struct rs_rate *rate) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci u32 ant_msk = ucode_rate & RATE_MCS_ANT_ABC_MSK; 8908c2ecf20Sopenharmony_ci u8 num_of_ant = get_num_of_ant_from_rate(ucode_rate); 8918c2ecf20Sopenharmony_ci u8 nss; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci memset(rate, 0, sizeof(*rate)); 8948c2ecf20Sopenharmony_ci rate->index = iwl_hwrate_to_plcp_idx(ucode_rate); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (rate->index == IWL_RATE_INVALID) 8978c2ecf20Sopenharmony_ci return -EINVAL; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci rate->ant = (ant_msk >> RATE_MCS_ANT_POS); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* Legacy */ 9028c2ecf20Sopenharmony_ci if (!(ucode_rate & RATE_MCS_HT_MSK) && 9038c2ecf20Sopenharmony_ci !(ucode_rate & RATE_MCS_VHT_MSK) && 9048c2ecf20Sopenharmony_ci !(ucode_rate & RATE_MCS_HE_MSK)) { 9058c2ecf20Sopenharmony_ci if (num_of_ant == 1) { 9068c2ecf20Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 9078c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_A; 9088c2ecf20Sopenharmony_ci else 9098c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_G; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* HT, VHT or HE */ 9168c2ecf20Sopenharmony_ci if (ucode_rate & RATE_MCS_SGI_MSK) 9178c2ecf20Sopenharmony_ci rate->sgi = true; 9188c2ecf20Sopenharmony_ci if (ucode_rate & RATE_MCS_LDPC_MSK) 9198c2ecf20Sopenharmony_ci rate->ldpc = true; 9208c2ecf20Sopenharmony_ci if (ucode_rate & RATE_MCS_STBC_MSK) 9218c2ecf20Sopenharmony_ci rate->stbc = true; 9228c2ecf20Sopenharmony_ci if (ucode_rate & RATE_MCS_BF_MSK) 9238c2ecf20Sopenharmony_ci rate->bfer = true; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (ucode_rate & RATE_MCS_HT_MSK) { 9288c2ecf20Sopenharmony_ci nss = ((ucode_rate & RATE_HT_MCS_NSS_MSK) >> 9298c2ecf20Sopenharmony_ci RATE_HT_MCS_NSS_POS) + 1; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (nss == 1) { 9328c2ecf20Sopenharmony_ci rate->type = LQ_HT_SISO; 9338c2ecf20Sopenharmony_ci WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1, 9348c2ecf20Sopenharmony_ci "stbc %d bfer %d", 9358c2ecf20Sopenharmony_ci rate->stbc, rate->bfer); 9368c2ecf20Sopenharmony_ci } else if (nss == 2) { 9378c2ecf20Sopenharmony_ci rate->type = LQ_HT_MIMO2; 9388c2ecf20Sopenharmony_ci WARN_ON_ONCE(num_of_ant != 2); 9398c2ecf20Sopenharmony_ci } else { 9408c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci } else if (ucode_rate & RATE_MCS_VHT_MSK) { 9438c2ecf20Sopenharmony_ci nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> 9448c2ecf20Sopenharmony_ci RATE_VHT_MCS_NSS_POS) + 1; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (nss == 1) { 9478c2ecf20Sopenharmony_ci rate->type = LQ_VHT_SISO; 9488c2ecf20Sopenharmony_ci WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1, 9498c2ecf20Sopenharmony_ci "stbc %d bfer %d", 9508c2ecf20Sopenharmony_ci rate->stbc, rate->bfer); 9518c2ecf20Sopenharmony_ci } else if (nss == 2) { 9528c2ecf20Sopenharmony_ci rate->type = LQ_VHT_MIMO2; 9538c2ecf20Sopenharmony_ci WARN_ON_ONCE(num_of_ant != 2); 9548c2ecf20Sopenharmony_ci } else { 9558c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci } else if (ucode_rate & RATE_MCS_HE_MSK) { 9588c2ecf20Sopenharmony_ci nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> 9598c2ecf20Sopenharmony_ci RATE_VHT_MCS_NSS_POS) + 1; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci if (nss == 1) { 9628c2ecf20Sopenharmony_ci rate->type = LQ_HE_SISO; 9638c2ecf20Sopenharmony_ci WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1, 9648c2ecf20Sopenharmony_ci "stbc %d bfer %d", rate->stbc, rate->bfer); 9658c2ecf20Sopenharmony_ci } else if (nss == 2) { 9668c2ecf20Sopenharmony_ci rate->type = LQ_HE_MIMO2; 9678c2ecf20Sopenharmony_ci WARN_ON_ONCE(num_of_ant != 2); 9688c2ecf20Sopenharmony_ci } else { 9698c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_80 && 9748c2ecf20Sopenharmony_ci !is_he(rate) && !is_vht(rate)); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci/* switch to another antenna/antennas and return 1 */ 9808c2ecf20Sopenharmony_ci/* if no other valid antenna found, return 0 */ 9818c2ecf20Sopenharmony_cistatic int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci u8 new_ant_type; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (!rate->ant || WARN_ON_ONCE(rate->ant & ANT_C)) 9868c2ecf20Sopenharmony_ci return 0; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_ant, rate->ant)) 9898c2ecf20Sopenharmony_ci return 0; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci new_ant_type = ant_toggle_lookup[rate->ant]; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci while ((new_ant_type != rate->ant) && 9948c2ecf20Sopenharmony_ci !rs_is_valid_ant(valid_ant, new_ant_type)) 9958c2ecf20Sopenharmony_ci new_ant_type = ant_toggle_lookup[new_ant_type]; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci if (new_ant_type == rate->ant) 9988c2ecf20Sopenharmony_ci return 0; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci rate->ant = new_ant_type; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return 1; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, 10068c2ecf20Sopenharmony_ci struct rs_rate *rate) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci if (is_legacy(rate)) 10098c2ecf20Sopenharmony_ci return lq_sta->active_legacy_rate; 10108c2ecf20Sopenharmony_ci else if (is_siso(rate)) 10118c2ecf20Sopenharmony_ci return lq_sta->active_siso_rate; 10128c2ecf20Sopenharmony_ci else if (is_mimo2(rate)) 10138c2ecf20Sopenharmony_ci return lq_sta->active_mimo2_rate; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 10168c2ecf20Sopenharmony_ci return 0; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, 10208c2ecf20Sopenharmony_ci int rate_type) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci u8 high = IWL_RATE_INVALID; 10238c2ecf20Sopenharmony_ci u8 low = IWL_RATE_INVALID; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci /* 802.11A or ht walks to the next literal adjacent rate in 10268c2ecf20Sopenharmony_ci * the rate table */ 10278c2ecf20Sopenharmony_ci if (is_type_a_band(rate_type) || !is_type_legacy(rate_type)) { 10288c2ecf20Sopenharmony_ci int i; 10298c2ecf20Sopenharmony_ci u32 mask; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* Find the previous rate that is in the rate mask */ 10328c2ecf20Sopenharmony_ci i = index - 1; 10338c2ecf20Sopenharmony_ci if (i >= 0) 10348c2ecf20Sopenharmony_ci mask = BIT(i); 10358c2ecf20Sopenharmony_ci for (; i >= 0; i--, mask >>= 1) { 10368c2ecf20Sopenharmony_ci if (rate_mask & mask) { 10378c2ecf20Sopenharmony_ci low = i; 10388c2ecf20Sopenharmony_ci break; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* Find the next rate that is in the rate mask */ 10438c2ecf20Sopenharmony_ci i = index + 1; 10448c2ecf20Sopenharmony_ci for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { 10458c2ecf20Sopenharmony_ci if (rate_mask & mask) { 10468c2ecf20Sopenharmony_ci high = i; 10478c2ecf20Sopenharmony_ci break; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci return (high << 8) | low; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci low = index; 10558c2ecf20Sopenharmony_ci while (low != IWL_RATE_INVALID) { 10568c2ecf20Sopenharmony_ci low = iwl_rates[low].prev_rs; 10578c2ecf20Sopenharmony_ci if (low == IWL_RATE_INVALID) 10588c2ecf20Sopenharmony_ci break; 10598c2ecf20Sopenharmony_ci if (rate_mask & (1 << low)) 10608c2ecf20Sopenharmony_ci break; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci high = index; 10648c2ecf20Sopenharmony_ci while (high != IWL_RATE_INVALID) { 10658c2ecf20Sopenharmony_ci high = iwl_rates[high].next_rs; 10668c2ecf20Sopenharmony_ci if (high == IWL_RATE_INVALID) 10678c2ecf20Sopenharmony_ci break; 10688c2ecf20Sopenharmony_ci if (rate_mask & (1 << high)) 10698c2ecf20Sopenharmony_ci break; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci return (high << 8) | low; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta, 10768c2ecf20Sopenharmony_ci struct rs_rate *rate) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate); 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci/* Get the next supported lower rate in the current column. 10828c2ecf20Sopenharmony_ci * Return true if bottom rate in the current column was reached 10838c2ecf20Sopenharmony_ci */ 10848c2ecf20Sopenharmony_cistatic bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta, 10858c2ecf20Sopenharmony_ci struct rs_rate *rate) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci u8 low; 10888c2ecf20Sopenharmony_ci u16 high_low; 10898c2ecf20Sopenharmony_ci u16 rate_mask; 10908c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = lq_sta->pers.drv; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci rate_mask = rs_get_supported_rates(lq_sta, rate); 10938c2ecf20Sopenharmony_ci high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask, 10948c2ecf20Sopenharmony_ci rate->type); 10958c2ecf20Sopenharmony_ci low = high_low & 0xff; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* Bottom rate of column reached */ 10988c2ecf20Sopenharmony_ci if (low == IWL_RATE_INVALID) 10998c2ecf20Sopenharmony_ci return true; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci rate->index = low; 11028c2ecf20Sopenharmony_ci return false; 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci/* Get the next rate to use following a column downgrade */ 11068c2ecf20Sopenharmony_cistatic void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta, 11078c2ecf20Sopenharmony_ci struct rs_rate *rate) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = lq_sta->pers.drv; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (is_legacy(rate)) { 11128c2ecf20Sopenharmony_ci /* No column to downgrade from Legacy */ 11138c2ecf20Sopenharmony_ci return; 11148c2ecf20Sopenharmony_ci } else if (is_siso(rate)) { 11158c2ecf20Sopenharmony_ci /* Downgrade to Legacy if we were in SISO */ 11168c2ecf20Sopenharmony_ci if (lq_sta->band == NL80211_BAND_5GHZ) 11178c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_A; 11188c2ecf20Sopenharmony_ci else 11198c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_G; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci rate->bw = RATE_MCS_CHAN_WIDTH_20; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX || 11248c2ecf20Sopenharmony_ci rate->index > IWL_RATE_MCS_9_INDEX); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci rate->index = rs_ht_to_legacy[rate->index]; 11278c2ecf20Sopenharmony_ci rate->ldpc = false; 11288c2ecf20Sopenharmony_ci } else { 11298c2ecf20Sopenharmony_ci /* Downgrade to SISO with same MCS if in MIMO */ 11308c2ecf20Sopenharmony_ci rate->type = is_vht_mimo2(rate) ? 11318c2ecf20Sopenharmony_ci LQ_VHT_SISO : LQ_HT_SISO; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (num_of_ant(rate->ant) > 1) 11358c2ecf20Sopenharmony_ci rate->ant = first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci /* Relevant in both switching to SISO or Legacy */ 11388c2ecf20Sopenharmony_ci rate->sgi = false; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (!rs_rate_supported(lq_sta, rate)) 11418c2ecf20Sopenharmony_ci rs_get_lower_rate_in_column(lq_sta, rate); 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci/* Check if both rates share the same column */ 11458c2ecf20Sopenharmony_cistatic inline bool rs_rate_column_match(struct rs_rate *a, 11468c2ecf20Sopenharmony_ci struct rs_rate *b) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci bool ant_match; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci if (a->stbc || a->bfer) 11518c2ecf20Sopenharmony_ci ant_match = (b->ant == ANT_A || b->ant == ANT_B); 11528c2ecf20Sopenharmony_ci else 11538c2ecf20Sopenharmony_ci ant_match = (a->ant == b->ant); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) 11568c2ecf20Sopenharmony_ci && ant_match; 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic inline enum rs_column rs_get_column_from_rate(struct rs_rate *rate) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci if (is_legacy(rate)) { 11628c2ecf20Sopenharmony_ci if (rate->ant == ANT_A) 11638c2ecf20Sopenharmony_ci return RS_COLUMN_LEGACY_ANT_A; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci if (rate->ant == ANT_B) 11668c2ecf20Sopenharmony_ci return RS_COLUMN_LEGACY_ANT_B; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci goto err; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (is_siso(rate)) { 11728c2ecf20Sopenharmony_ci if (rate->ant == ANT_A || rate->stbc || rate->bfer) 11738c2ecf20Sopenharmony_ci return rate->sgi ? RS_COLUMN_SISO_ANT_A_SGI : 11748c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_A; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (rate->ant == ANT_B) 11778c2ecf20Sopenharmony_ci return rate->sgi ? RS_COLUMN_SISO_ANT_B_SGI : 11788c2ecf20Sopenharmony_ci RS_COLUMN_SISO_ANT_B; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci goto err; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (is_mimo(rate)) 11848c2ecf20Sopenharmony_ci return rate->sgi ? RS_COLUMN_MIMO2_SGI : RS_COLUMN_MIMO2; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cierr: 11878c2ecf20Sopenharmony_ci return RS_COLUMN_INVALID; 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cistatic u8 rs_get_tid(struct ieee80211_hdr *hdr) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci u8 tid = IWL_MAX_TID_COUNT; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (ieee80211_is_data_qos(hdr->frame_control)) { 11958c2ecf20Sopenharmony_ci u8 *qc = ieee80211_get_qos_ctl(hdr); 11968c2ecf20Sopenharmony_ci tid = qc[0] & 0xf; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci if (unlikely(tid > IWL_MAX_TID_COUNT)) 12008c2ecf20Sopenharmony_ci tid = IWL_MAX_TID_COUNT; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci return tid; 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci/* 12068c2ecf20Sopenharmony_ci * mac80211 sends us Tx status 12078c2ecf20Sopenharmony_ci */ 12088c2ecf20Sopenharmony_cistatic void rs_drv_mac80211_tx_status(void *mvm_r, 12098c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband, 12108c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, void *priv_sta, 12118c2ecf20Sopenharmony_ci struct sk_buff *skb) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 12148c2ecf20Sopenharmony_ci struct iwl_op_mode *op_mode = mvm_r; 12158c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); 12168c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 12178c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (!mvmsta->vif) 12208c2ecf20Sopenharmony_ci return; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci if (!ieee80211_is_data(hdr->frame_control) || 12238c2ecf20Sopenharmony_ci info->flags & IEEE80211_TX_CTL_NO_ACK) 12248c2ecf20Sopenharmony_ci return; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci iwl_mvm_rs_tx_status(mvm, sta, rs_get_tid(hdr), info, 12278c2ecf20Sopenharmony_ci ieee80211_is_qos_nullfunc(hdr->frame_control)); 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci/* 12318c2ecf20Sopenharmony_ci * Begin a period of staying with a selected modulation mode. 12328c2ecf20Sopenharmony_ci * Set "stay_in_tbl" flag to prevent any mode switches. 12338c2ecf20Sopenharmony_ci * Set frame tx success limits according to legacy vs. high-throughput, 12348c2ecf20Sopenharmony_ci * and reset overall (spanning all rates) tx success history statistics. 12358c2ecf20Sopenharmony_ci * These control how long we stay using same modulation mode before 12368c2ecf20Sopenharmony_ci * searching for a new mode. 12378c2ecf20Sopenharmony_ci */ 12388c2ecf20Sopenharmony_cistatic void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, 12398c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n"); 12428c2ecf20Sopenharmony_ci lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN; 12438c2ecf20Sopenharmony_ci if (is_legacy) { 12448c2ecf20Sopenharmony_ci lq_sta->table_count_limit = IWL_MVM_RS_LEGACY_TABLE_COUNT; 12458c2ecf20Sopenharmony_ci lq_sta->max_failure_limit = IWL_MVM_RS_LEGACY_FAILURE_LIMIT; 12468c2ecf20Sopenharmony_ci lq_sta->max_success_limit = IWL_MVM_RS_LEGACY_SUCCESS_LIMIT; 12478c2ecf20Sopenharmony_ci } else { 12488c2ecf20Sopenharmony_ci lq_sta->table_count_limit = IWL_MVM_RS_NON_LEGACY_TABLE_COUNT; 12498c2ecf20Sopenharmony_ci lq_sta->max_failure_limit = IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT; 12508c2ecf20Sopenharmony_ci lq_sta->max_success_limit = IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci lq_sta->table_count = 0; 12538c2ecf20Sopenharmony_ci lq_sta->total_failed = 0; 12548c2ecf20Sopenharmony_ci lq_sta->total_success = 0; 12558c2ecf20Sopenharmony_ci lq_sta->flush_timer = jiffies; 12568c2ecf20Sopenharmony_ci lq_sta->visited_columns = 0; 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_cistatic inline int rs_get_max_rate_from_mask(unsigned long rate_mask) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci if (rate_mask) 12628c2ecf20Sopenharmony_ci return find_last_bit(&rate_mask, BITS_PER_LONG); 12638c2ecf20Sopenharmony_ci return IWL_RATE_INVALID; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta, 12678c2ecf20Sopenharmony_ci const struct rs_tx_column *column) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci switch (column->mode) { 12708c2ecf20Sopenharmony_ci case RS_LEGACY: 12718c2ecf20Sopenharmony_ci return lq_sta->max_legacy_rate_idx; 12728c2ecf20Sopenharmony_ci case RS_SISO: 12738c2ecf20Sopenharmony_ci return lq_sta->max_siso_rate_idx; 12748c2ecf20Sopenharmony_ci case RS_MIMO2: 12758c2ecf20Sopenharmony_ci return lq_sta->max_mimo2_rate_idx; 12768c2ecf20Sopenharmony_ci default: 12778c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci return lq_sta->max_legacy_rate_idx; 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta, 12848c2ecf20Sopenharmony_ci const struct rs_tx_column *column, 12858c2ecf20Sopenharmony_ci u32 bw) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci /* Used to choose among HT tables */ 12888c2ecf20Sopenharmony_ci const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT]; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(column->mode != RS_LEGACY && 12918c2ecf20Sopenharmony_ci column->mode != RS_SISO && 12928c2ecf20Sopenharmony_ci column->mode != RS_MIMO2)) 12938c2ecf20Sopenharmony_ci return expected_tpt_legacy; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci /* Legacy rates have only one table */ 12968c2ecf20Sopenharmony_ci if (column->mode == RS_LEGACY) 12978c2ecf20Sopenharmony_ci return expected_tpt_legacy; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_mimo2_20MHz; 13008c2ecf20Sopenharmony_ci /* Choose among many HT tables depending on number of streams 13018c2ecf20Sopenharmony_ci * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation 13028c2ecf20Sopenharmony_ci * status */ 13038c2ecf20Sopenharmony_ci if (column->mode == RS_SISO) { 13048c2ecf20Sopenharmony_ci switch (bw) { 13058c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_20: 13068c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_siso_20MHz; 13078c2ecf20Sopenharmony_ci break; 13088c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_40: 13098c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_siso_40MHz; 13108c2ecf20Sopenharmony_ci break; 13118c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_80: 13128c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_siso_80MHz; 13138c2ecf20Sopenharmony_ci break; 13148c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_160: 13158c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_siso_160MHz; 13168c2ecf20Sopenharmony_ci break; 13178c2ecf20Sopenharmony_ci default: 13188c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci } else if (column->mode == RS_MIMO2) { 13218c2ecf20Sopenharmony_ci switch (bw) { 13228c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_20: 13238c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_mimo2_20MHz; 13248c2ecf20Sopenharmony_ci break; 13258c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_40: 13268c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_mimo2_40MHz; 13278c2ecf20Sopenharmony_ci break; 13288c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_80: 13298c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_mimo2_80MHz; 13308c2ecf20Sopenharmony_ci break; 13318c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_160: 13328c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_mimo2_160MHz; 13338c2ecf20Sopenharmony_ci break; 13348c2ecf20Sopenharmony_ci default: 13358c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci } else { 13388c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci if (!column->sgi && !lq_sta->is_agg) /* Normal */ 13428c2ecf20Sopenharmony_ci return ht_tbl_pointer[0]; 13438c2ecf20Sopenharmony_ci else if (column->sgi && !lq_sta->is_agg) /* SGI */ 13448c2ecf20Sopenharmony_ci return ht_tbl_pointer[1]; 13458c2ecf20Sopenharmony_ci else if (!column->sgi && lq_sta->is_agg) /* AGG */ 13468c2ecf20Sopenharmony_ci return ht_tbl_pointer[2]; 13478c2ecf20Sopenharmony_ci else /* AGG+SGI */ 13488c2ecf20Sopenharmony_ci return ht_tbl_pointer[3]; 13498c2ecf20Sopenharmony_ci} 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_cistatic void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, 13528c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl) 13538c2ecf20Sopenharmony_ci{ 13548c2ecf20Sopenharmony_ci struct rs_rate *rate = &tbl->rate; 13558c2ecf20Sopenharmony_ci const struct rs_tx_column *column = &rs_tx_columns[tbl->column]; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw); 13588c2ecf20Sopenharmony_ci} 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci/* rs uses two tables, one is active and the second is for searching better 13618c2ecf20Sopenharmony_ci * configuration. This function, according to the index of the currently 13628c2ecf20Sopenharmony_ci * active table returns the search table, which is located at the 13638c2ecf20Sopenharmony_ci * index complementary to 1 according to the active table (active = 1, 13648c2ecf20Sopenharmony_ci * search = 0 or active = 0, search = 1). 13658c2ecf20Sopenharmony_ci * Since lq_info is an arary of size 2, make sure index cannot be out of bounds. 13668c2ecf20Sopenharmony_ci */ 13678c2ecf20Sopenharmony_cistatic inline u8 rs_search_tbl(u8 active_tbl) 13688c2ecf20Sopenharmony_ci{ 13698c2ecf20Sopenharmony_ci return (active_tbl ^ 1) & 1; 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_cistatic s32 rs_get_best_rate(struct iwl_mvm *mvm, 13738c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 13748c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, /* "search" */ 13758c2ecf20Sopenharmony_ci unsigned long rate_mask, s8 index) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *active_tbl = 13788c2ecf20Sopenharmony_ci &(lq_sta->lq_info[lq_sta->active_tbl]); 13798c2ecf20Sopenharmony_ci s32 success_ratio = active_tbl->win[index].success_ratio; 13808c2ecf20Sopenharmony_ci u16 expected_current_tpt = active_tbl->expected_tpt[index]; 13818c2ecf20Sopenharmony_ci const u16 *tpt_tbl = tbl->expected_tpt; 13828c2ecf20Sopenharmony_ci u16 high_low; 13838c2ecf20Sopenharmony_ci u32 target_tpt; 13848c2ecf20Sopenharmony_ci int rate_idx; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (success_ratio >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { 13878c2ecf20Sopenharmony_ci target_tpt = 100 * expected_current_tpt; 13888c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 13898c2ecf20Sopenharmony_ci "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n", 13908c2ecf20Sopenharmony_ci success_ratio, target_tpt); 13918c2ecf20Sopenharmony_ci } else { 13928c2ecf20Sopenharmony_ci target_tpt = lq_sta->last_tpt; 13938c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 13948c2ecf20Sopenharmony_ci "SR %d not that good. Find rate exceeding ACTUAL_TPT %d\n", 13958c2ecf20Sopenharmony_ci success_ratio, target_tpt); 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci while (rate_idx != IWL_RATE_INVALID) { 14018c2ecf20Sopenharmony_ci if (target_tpt < (100 * tpt_tbl[rate_idx])) 14028c2ecf20Sopenharmony_ci break; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask, 14058c2ecf20Sopenharmony_ci tbl->rate.type); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci rate_idx = (high_low >> 8) & 0xff; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n", 14118c2ecf20Sopenharmony_ci rate_idx, target_tpt, 14128c2ecf20Sopenharmony_ci rate_idx != IWL_RATE_INVALID ? 14138c2ecf20Sopenharmony_ci 100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci return rate_idx; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *sta_vht_cap = &sta->vht_cap; 14218c2ecf20Sopenharmony_ci struct ieee80211_vht_cap vht_cap = { 14228c2ecf20Sopenharmony_ci .vht_cap_info = cpu_to_le32(sta_vht_cap->cap), 14238c2ecf20Sopenharmony_ci .supp_mcs = sta_vht_cap->vht_mcs, 14248c2ecf20Sopenharmony_ci }; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci switch (sta->bandwidth) { 14278c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_160: 14288c2ecf20Sopenharmony_ci /* 14298c2ecf20Sopenharmony_ci * Don't use 160 MHz if VHT extended NSS support 14308c2ecf20Sopenharmony_ci * says we cannot use 2 streams, we don't want to 14318c2ecf20Sopenharmony_ci * deal with this. 14328c2ecf20Sopenharmony_ci * We only check MCS 0 - they will support that if 14338c2ecf20Sopenharmony_ci * we got here at all and we don't care which MCS, 14348c2ecf20Sopenharmony_ci * we want to determine a more global state. 14358c2ecf20Sopenharmony_ci */ 14368c2ecf20Sopenharmony_ci if (ieee80211_get_vht_max_nss(&vht_cap, 14378c2ecf20Sopenharmony_ci IEEE80211_VHT_CHANWIDTH_160MHZ, 14388c2ecf20Sopenharmony_ci 0, true, 14398c2ecf20Sopenharmony_ci sta->rx_nss) < sta->rx_nss) 14408c2ecf20Sopenharmony_ci return RATE_MCS_CHAN_WIDTH_80; 14418c2ecf20Sopenharmony_ci return RATE_MCS_CHAN_WIDTH_160; 14428c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_80: 14438c2ecf20Sopenharmony_ci return RATE_MCS_CHAN_WIDTH_80; 14448c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_40: 14458c2ecf20Sopenharmony_ci return RATE_MCS_CHAN_WIDTH_40; 14468c2ecf20Sopenharmony_ci case IEEE80211_STA_RX_BW_20: 14478c2ecf20Sopenharmony_ci default: 14488c2ecf20Sopenharmony_ci return RATE_MCS_CHAN_WIDTH_20; 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci/* 14538c2ecf20Sopenharmony_ci * Check whether we should continue using same modulation mode, or 14548c2ecf20Sopenharmony_ci * begin search for a new mode, based on: 14558c2ecf20Sopenharmony_ci * 1) # tx successes or failures while using this mode 14568c2ecf20Sopenharmony_ci * 2) # times calling this function 14578c2ecf20Sopenharmony_ci * 3) elapsed time in this mode (not used, for now) 14588c2ecf20Sopenharmony_ci */ 14598c2ecf20Sopenharmony_cistatic void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) 14608c2ecf20Sopenharmony_ci{ 14618c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl; 14628c2ecf20Sopenharmony_ci int active_tbl; 14638c2ecf20Sopenharmony_ci int flush_interval_passed = 0; 14648c2ecf20Sopenharmony_ci struct iwl_mvm *mvm; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci mvm = lq_sta->pers.drv; 14678c2ecf20Sopenharmony_ci active_tbl = lq_sta->active_tbl; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[active_tbl]); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci /* If we've been disallowing search, see if we should now allow it */ 14728c2ecf20Sopenharmony_ci if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { 14738c2ecf20Sopenharmony_ci /* Elapsed time using current modulation mode */ 14748c2ecf20Sopenharmony_ci if (lq_sta->flush_timer) 14758c2ecf20Sopenharmony_ci flush_interval_passed = 14768c2ecf20Sopenharmony_ci time_after(jiffies, 14778c2ecf20Sopenharmony_ci (unsigned long)(lq_sta->flush_timer + 14788c2ecf20Sopenharmony_ci (IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT * HZ))); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci /* 14818c2ecf20Sopenharmony_ci * Check if we should allow search for new modulation mode. 14828c2ecf20Sopenharmony_ci * If many frames have failed or succeeded, or we've used 14838c2ecf20Sopenharmony_ci * this same modulation for a long time, allow search, and 14848c2ecf20Sopenharmony_ci * reset history stats that keep track of whether we should 14858c2ecf20Sopenharmony_ci * allow a new search. Also (below) reset all bitmaps and 14868c2ecf20Sopenharmony_ci * stats in active history. 14878c2ecf20Sopenharmony_ci */ 14888c2ecf20Sopenharmony_ci if (force_search || 14898c2ecf20Sopenharmony_ci (lq_sta->total_failed > lq_sta->max_failure_limit) || 14908c2ecf20Sopenharmony_ci (lq_sta->total_success > lq_sta->max_success_limit) || 14918c2ecf20Sopenharmony_ci ((!lq_sta->search_better_tbl) && 14928c2ecf20Sopenharmony_ci (lq_sta->flush_timer) && (flush_interval_passed))) { 14938c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 14948c2ecf20Sopenharmony_ci "LQ: stay is expired %d %d %d\n", 14958c2ecf20Sopenharmony_ci lq_sta->total_failed, 14968c2ecf20Sopenharmony_ci lq_sta->total_success, 14978c2ecf20Sopenharmony_ci flush_interval_passed); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci /* Allow search for new mode */ 15008c2ecf20Sopenharmony_ci lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_STARTED; 15018c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 15028c2ecf20Sopenharmony_ci "Moving to RS_STATE_SEARCH_CYCLE_STARTED\n"); 15038c2ecf20Sopenharmony_ci lq_sta->total_failed = 0; 15048c2ecf20Sopenharmony_ci lq_sta->total_success = 0; 15058c2ecf20Sopenharmony_ci lq_sta->flush_timer = 0; 15068c2ecf20Sopenharmony_ci /* mark the current column as visited */ 15078c2ecf20Sopenharmony_ci lq_sta->visited_columns = BIT(tbl->column); 15088c2ecf20Sopenharmony_ci /* 15098c2ecf20Sopenharmony_ci * Else if we've used this modulation mode enough repetitions 15108c2ecf20Sopenharmony_ci * (regardless of elapsed time or success/failure), reset 15118c2ecf20Sopenharmony_ci * history bitmaps and rate-specific stats for all rates in 15128c2ecf20Sopenharmony_ci * active table. 15138c2ecf20Sopenharmony_ci */ 15148c2ecf20Sopenharmony_ci } else { 15158c2ecf20Sopenharmony_ci lq_sta->table_count++; 15168c2ecf20Sopenharmony_ci if (lq_sta->table_count >= 15178c2ecf20Sopenharmony_ci lq_sta->table_count_limit) { 15188c2ecf20Sopenharmony_ci lq_sta->table_count = 0; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 15218c2ecf20Sopenharmony_ci "LQ: stay in table clear win\n"); 15228c2ecf20Sopenharmony_ci rs_rate_scale_clear_tbl_windows(mvm, tbl); 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* If transitioning to allow "search", reset all history 15278c2ecf20Sopenharmony_ci * bitmaps and stats in active table (this will become the new 15288c2ecf20Sopenharmony_ci * "search" table). */ 15298c2ecf20Sopenharmony_ci if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) { 15308c2ecf20Sopenharmony_ci rs_rate_scale_clear_tbl_windows(mvm, tbl); 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci} 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_cistatic void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 15368c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 15378c2ecf20Sopenharmony_ci enum rs_action scale_action) 15388c2ecf20Sopenharmony_ci{ 15398c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 15408c2ecf20Sopenharmony_ci int i; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci sta->max_amsdu_len = rs_fw_get_max_amsdu_len(sta); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci /* 15458c2ecf20Sopenharmony_ci * In case TLC offload is not active amsdu_enabled is either 0xFFFF 15468c2ecf20Sopenharmony_ci * or 0, since there is no per-TID alg. 15478c2ecf20Sopenharmony_ci */ 15488c2ecf20Sopenharmony_ci if ((!is_vht(&tbl->rate) && !is_ht(&tbl->rate)) || 15498c2ecf20Sopenharmony_ci tbl->rate.index < IWL_RATE_MCS_5_INDEX || 15508c2ecf20Sopenharmony_ci scale_action == RS_ACTION_DOWNSCALE) 15518c2ecf20Sopenharmony_ci mvmsta->amsdu_enabled = 0; 15528c2ecf20Sopenharmony_ci else 15538c2ecf20Sopenharmony_ci mvmsta->amsdu_enabled = 0xFFFF; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (mvmsta->vif->bss_conf.he_support && 15568c2ecf20Sopenharmony_ci !iwlwifi_mod_params.disable_11ax) 15578c2ecf20Sopenharmony_ci mvmsta->max_amsdu_len = sta->max_amsdu_len; 15588c2ecf20Sopenharmony_ci else 15598c2ecf20Sopenharmony_ci mvmsta->max_amsdu_len = min_t(int, sta->max_amsdu_len, 8500); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci sta->max_rc_amsdu_len = mvmsta->max_amsdu_len; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci for (i = 0; i < IWL_MAX_TID_COUNT; i++) { 15648c2ecf20Sopenharmony_ci if (mvmsta->amsdu_enabled) 15658c2ecf20Sopenharmony_ci sta->max_tid_amsdu_len[i] = 15668c2ecf20Sopenharmony_ci iwl_mvm_max_amsdu_size(mvm, sta, i); 15678c2ecf20Sopenharmony_ci else 15688c2ecf20Sopenharmony_ci /* 15698c2ecf20Sopenharmony_ci * Not so elegant, but this will effectively 15708c2ecf20Sopenharmony_ci * prevent AMSDU on this TID 15718c2ecf20Sopenharmony_ci */ 15728c2ecf20Sopenharmony_ci sta->max_tid_amsdu_len[i] = 1; 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci/* 15778c2ecf20Sopenharmony_ci * setup rate table in uCode 15788c2ecf20Sopenharmony_ci */ 15798c2ecf20Sopenharmony_cistatic void rs_update_rate_tbl(struct iwl_mvm *mvm, 15808c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 15818c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 15828c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl) 15838c2ecf20Sopenharmony_ci{ 15848c2ecf20Sopenharmony_ci rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate); 15858c2ecf20Sopenharmony_ci iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq); 15868c2ecf20Sopenharmony_ci} 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_cistatic bool rs_tweak_rate_tbl(struct iwl_mvm *mvm, 15898c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 15908c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 15918c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 15928c2ecf20Sopenharmony_ci enum rs_action scale_action) 15938c2ecf20Sopenharmony_ci{ 15948c2ecf20Sopenharmony_ci if (rs_bw_from_sta_bw(sta) != RATE_MCS_CHAN_WIDTH_80) 15958c2ecf20Sopenharmony_ci return false; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci if (!is_vht_siso(&tbl->rate)) 15988c2ecf20Sopenharmony_ci return false; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_80) && 16018c2ecf20Sopenharmony_ci (tbl->rate.index == IWL_RATE_MCS_0_INDEX) && 16028c2ecf20Sopenharmony_ci (scale_action == RS_ACTION_DOWNSCALE)) { 16038c2ecf20Sopenharmony_ci tbl->rate.bw = RATE_MCS_CHAN_WIDTH_20; 16048c2ecf20Sopenharmony_ci tbl->rate.index = IWL_RATE_MCS_4_INDEX; 16058c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Switch 80Mhz SISO MCS0 -> 20Mhz MCS4\n"); 16068c2ecf20Sopenharmony_ci goto tweaked; 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* Go back to 80Mhz MCS1 only if we've established that 20Mhz MCS5 is 16108c2ecf20Sopenharmony_ci * sustainable, i.e. we're past the test window. We can't go back 16118c2ecf20Sopenharmony_ci * if MCS5 is just tested as this will happen always after switching 16128c2ecf20Sopenharmony_ci * to 20Mhz MCS4 because the rate stats are cleared. 16138c2ecf20Sopenharmony_ci */ 16148c2ecf20Sopenharmony_ci if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_20) && 16158c2ecf20Sopenharmony_ci (((tbl->rate.index == IWL_RATE_MCS_5_INDEX) && 16168c2ecf20Sopenharmony_ci (scale_action == RS_ACTION_STAY)) || 16178c2ecf20Sopenharmony_ci ((tbl->rate.index > IWL_RATE_MCS_5_INDEX) && 16188c2ecf20Sopenharmony_ci (scale_action == RS_ACTION_UPSCALE)))) { 16198c2ecf20Sopenharmony_ci tbl->rate.bw = RATE_MCS_CHAN_WIDTH_80; 16208c2ecf20Sopenharmony_ci tbl->rate.index = IWL_RATE_MCS_1_INDEX; 16218c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Switch 20Mhz SISO MCS5 -> 80Mhz MCS1\n"); 16228c2ecf20Sopenharmony_ci goto tweaked; 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci return false; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_citweaked: 16288c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, tbl); 16298c2ecf20Sopenharmony_ci rs_rate_scale_clear_tbl_windows(mvm, tbl); 16308c2ecf20Sopenharmony_ci return true; 16318c2ecf20Sopenharmony_ci} 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_cistatic enum rs_column rs_get_next_column(struct iwl_mvm *mvm, 16348c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 16358c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 16368c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci int i, j, max_rate; 16398c2ecf20Sopenharmony_ci enum rs_column next_col_id; 16408c2ecf20Sopenharmony_ci const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column]; 16418c2ecf20Sopenharmony_ci const struct rs_tx_column *next_col; 16428c2ecf20Sopenharmony_ci allow_column_func_t allow_func; 16438c2ecf20Sopenharmony_ci u8 valid_ants = iwl_mvm_get_valid_tx_ant(mvm); 16448c2ecf20Sopenharmony_ci const u16 *expected_tpt_tbl; 16458c2ecf20Sopenharmony_ci u16 tpt, max_expected_tpt; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NEXT_COLUMNS; i++) { 16488c2ecf20Sopenharmony_ci next_col_id = curr_col->next_columns[i]; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci if (next_col_id == RS_COLUMN_INVALID) 16518c2ecf20Sopenharmony_ci continue; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci if (lq_sta->visited_columns & BIT(next_col_id)) { 16548c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Skip already visited column %d\n", 16558c2ecf20Sopenharmony_ci next_col_id); 16568c2ecf20Sopenharmony_ci continue; 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci next_col = &rs_tx_columns[next_col_id]; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_ants, next_col->ant)) { 16628c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 16638c2ecf20Sopenharmony_ci "Skip column %d as ANT config isn't supported by chip. valid_ants 0x%x column ant 0x%x\n", 16648c2ecf20Sopenharmony_ci next_col_id, valid_ants, next_col->ant); 16658c2ecf20Sopenharmony_ci continue; 16668c2ecf20Sopenharmony_ci } 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci for (j = 0; j < MAX_COLUMN_CHECKS; j++) { 16698c2ecf20Sopenharmony_ci allow_func = next_col->checks[j]; 16708c2ecf20Sopenharmony_ci if (allow_func && !allow_func(mvm, sta, &tbl->rate, 16718c2ecf20Sopenharmony_ci next_col)) 16728c2ecf20Sopenharmony_ci break; 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (j != MAX_COLUMN_CHECKS) { 16768c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 16778c2ecf20Sopenharmony_ci "Skip column %d: not allowed (check %d failed)\n", 16788c2ecf20Sopenharmony_ci next_col_id, j); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci continue; 16818c2ecf20Sopenharmony_ci } 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci tpt = lq_sta->last_tpt / 100; 16848c2ecf20Sopenharmony_ci expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col, 16858c2ecf20Sopenharmony_ci rs_bw_from_sta_bw(sta)); 16868c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!expected_tpt_tbl)) 16878c2ecf20Sopenharmony_ci continue; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci max_rate = rs_get_max_allowed_rate(lq_sta, next_col); 16908c2ecf20Sopenharmony_ci if (max_rate == IWL_RATE_INVALID) { 16918c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 16928c2ecf20Sopenharmony_ci "Skip column %d: no rate is allowed in this column\n", 16938c2ecf20Sopenharmony_ci next_col_id); 16948c2ecf20Sopenharmony_ci continue; 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci max_expected_tpt = expected_tpt_tbl[max_rate]; 16988c2ecf20Sopenharmony_ci if (tpt >= max_expected_tpt) { 16998c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 17008c2ecf20Sopenharmony_ci "Skip column %d: can't beat current TPT. Max expected %d current %d\n", 17018c2ecf20Sopenharmony_ci next_col_id, max_expected_tpt, tpt); 17028c2ecf20Sopenharmony_ci continue; 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 17068c2ecf20Sopenharmony_ci "Found potential column %d. Max expected %d current %d\n", 17078c2ecf20Sopenharmony_ci next_col_id, max_expected_tpt, tpt); 17088c2ecf20Sopenharmony_ci break; 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci if (i == MAX_NEXT_COLUMNS) 17128c2ecf20Sopenharmony_ci return RS_COLUMN_INVALID; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci return next_col_id; 17158c2ecf20Sopenharmony_ci} 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_cistatic int rs_switch_to_column(struct iwl_mvm *mvm, 17188c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 17198c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 17208c2ecf20Sopenharmony_ci enum rs_column col_id) 17218c2ecf20Sopenharmony_ci{ 17228c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; 17238c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *search_tbl = 17248c2ecf20Sopenharmony_ci &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; 17258c2ecf20Sopenharmony_ci struct rs_rate *rate = &search_tbl->rate; 17268c2ecf20Sopenharmony_ci const struct rs_tx_column *column = &rs_tx_columns[col_id]; 17278c2ecf20Sopenharmony_ci const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column]; 17288c2ecf20Sopenharmony_ci unsigned long rate_mask = 0; 17298c2ecf20Sopenharmony_ci u32 rate_idx = 0; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, offsetof(struct iwl_scale_tbl_info, win)); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci rate->sgi = column->sgi; 17348c2ecf20Sopenharmony_ci rate->ant = column->ant; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci if (column->mode == RS_LEGACY) { 17378c2ecf20Sopenharmony_ci if (lq_sta->band == NL80211_BAND_5GHZ) 17388c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_A; 17398c2ecf20Sopenharmony_ci else 17408c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_G; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci rate->bw = RATE_MCS_CHAN_WIDTH_20; 17438c2ecf20Sopenharmony_ci rate->ldpc = false; 17448c2ecf20Sopenharmony_ci rate_mask = lq_sta->active_legacy_rate; 17458c2ecf20Sopenharmony_ci } else if (column->mode == RS_SISO) { 17468c2ecf20Sopenharmony_ci rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; 17478c2ecf20Sopenharmony_ci rate_mask = lq_sta->active_siso_rate; 17488c2ecf20Sopenharmony_ci } else if (column->mode == RS_MIMO2) { 17498c2ecf20Sopenharmony_ci rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; 17508c2ecf20Sopenharmony_ci rate_mask = lq_sta->active_mimo2_rate; 17518c2ecf20Sopenharmony_ci } else { 17528c2ecf20Sopenharmony_ci WARN_ONCE(1, "Bad column mode"); 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (column->mode != RS_LEGACY) { 17568c2ecf20Sopenharmony_ci rate->bw = rs_bw_from_sta_bw(sta); 17578c2ecf20Sopenharmony_ci rate->ldpc = lq_sta->ldpc; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci search_tbl->column = col_id; 17618c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, search_tbl); 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci lq_sta->visited_columns |= BIT(col_id); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci /* Get the best matching rate if we're changing modes. e.g. 17668c2ecf20Sopenharmony_ci * SISO->MIMO, LEGACY->SISO, MIMO->SISO 17678c2ecf20Sopenharmony_ci */ 17688c2ecf20Sopenharmony_ci if (curr_column->mode != column->mode) { 17698c2ecf20Sopenharmony_ci rate_idx = rs_get_best_rate(mvm, lq_sta, search_tbl, 17708c2ecf20Sopenharmony_ci rate_mask, rate->index); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if ((rate_idx == IWL_RATE_INVALID) || 17738c2ecf20Sopenharmony_ci !(BIT(rate_idx) & rate_mask)) { 17748c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 17758c2ecf20Sopenharmony_ci "can not switch with index %d" 17768c2ecf20Sopenharmony_ci " rate mask %lx\n", 17778c2ecf20Sopenharmony_ci rate_idx, rate_mask); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci goto err; 17808c2ecf20Sopenharmony_ci } 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci rate->index = rate_idx; 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n", 17868c2ecf20Sopenharmony_ci col_id, rate->index); 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci return 0; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_cierr: 17918c2ecf20Sopenharmony_ci rate->type = LQ_NONE; 17928c2ecf20Sopenharmony_ci return -1; 17938c2ecf20Sopenharmony_ci} 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_cistatic enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, 17968c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 17978c2ecf20Sopenharmony_ci s32 sr, int low, int high, 17988c2ecf20Sopenharmony_ci int current_tpt, 17998c2ecf20Sopenharmony_ci int low_tpt, int high_tpt) 18008c2ecf20Sopenharmony_ci{ 18018c2ecf20Sopenharmony_ci enum rs_action action = RS_ACTION_STAY; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if ((sr <= RS_PERCENT(IWL_MVM_RS_SR_FORCE_DECREASE)) || 18048c2ecf20Sopenharmony_ci (current_tpt == 0)) { 18058c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 18068c2ecf20Sopenharmony_ci "Decrease rate because of low SR\n"); 18078c2ecf20Sopenharmony_ci return RS_ACTION_DOWNSCALE; 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci if ((low_tpt == IWL_INVALID_VALUE) && 18118c2ecf20Sopenharmony_ci (high_tpt == IWL_INVALID_VALUE) && 18128c2ecf20Sopenharmony_ci (high != IWL_RATE_INVALID)) { 18138c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 18148c2ecf20Sopenharmony_ci "No data about high/low rates. Increase rate\n"); 18158c2ecf20Sopenharmony_ci return RS_ACTION_UPSCALE; 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if ((high_tpt == IWL_INVALID_VALUE) && 18198c2ecf20Sopenharmony_ci (high != IWL_RATE_INVALID) && 18208c2ecf20Sopenharmony_ci (low_tpt != IWL_INVALID_VALUE) && 18218c2ecf20Sopenharmony_ci (low_tpt < current_tpt)) { 18228c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 18238c2ecf20Sopenharmony_ci "No data about high rate and low rate is worse. Increase rate\n"); 18248c2ecf20Sopenharmony_ci return RS_ACTION_UPSCALE; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if ((high_tpt != IWL_INVALID_VALUE) && 18288c2ecf20Sopenharmony_ci (high_tpt > current_tpt)) { 18298c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 18308c2ecf20Sopenharmony_ci "Higher rate is better. Increate rate\n"); 18318c2ecf20Sopenharmony_ci return RS_ACTION_UPSCALE; 18328c2ecf20Sopenharmony_ci } 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci if ((low_tpt != IWL_INVALID_VALUE) && 18358c2ecf20Sopenharmony_ci (high_tpt != IWL_INVALID_VALUE) && 18368c2ecf20Sopenharmony_ci (low_tpt < current_tpt) && 18378c2ecf20Sopenharmony_ci (high_tpt < current_tpt)) { 18388c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 18398c2ecf20Sopenharmony_ci "Both high and low are worse. Maintain rate\n"); 18408c2ecf20Sopenharmony_ci return RS_ACTION_STAY; 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if ((low_tpt != IWL_INVALID_VALUE) && 18448c2ecf20Sopenharmony_ci (low_tpt > current_tpt)) { 18458c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 18468c2ecf20Sopenharmony_ci "Lower rate is better\n"); 18478c2ecf20Sopenharmony_ci action = RS_ACTION_DOWNSCALE; 18488c2ecf20Sopenharmony_ci goto out; 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci if ((low_tpt == IWL_INVALID_VALUE) && 18528c2ecf20Sopenharmony_ci (low != IWL_RATE_INVALID)) { 18538c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 18548c2ecf20Sopenharmony_ci "No data about lower rate\n"); 18558c2ecf20Sopenharmony_ci action = RS_ACTION_DOWNSCALE; 18568c2ecf20Sopenharmony_ci goto out; 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Maintain rate\n"); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ciout: 18628c2ecf20Sopenharmony_ci if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) { 18638c2ecf20Sopenharmony_ci if (sr >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { 18648c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 18658c2ecf20Sopenharmony_ci "SR is above NO DECREASE. Avoid downscale\n"); 18668c2ecf20Sopenharmony_ci action = RS_ACTION_STAY; 18678c2ecf20Sopenharmony_ci } else if (current_tpt > (100 * tbl->expected_tpt[low])) { 18688c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 18698c2ecf20Sopenharmony_ci "Current TPT is higher than max expected in low rate. Avoid downscale\n"); 18708c2ecf20Sopenharmony_ci action = RS_ACTION_STAY; 18718c2ecf20Sopenharmony_ci } else { 18728c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Decrease rate\n"); 18738c2ecf20Sopenharmony_ci } 18748c2ecf20Sopenharmony_ci } 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci return action; 18778c2ecf20Sopenharmony_ci} 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_cistatic bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 18808c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci /* Our chip supports Tx STBC and the peer is an HT/VHT STA which 18838c2ecf20Sopenharmony_ci * supports STBC of at least 1*SS 18848c2ecf20Sopenharmony_ci */ 18858c2ecf20Sopenharmony_ci if (!lq_sta->stbc_capable) 18868c2ecf20Sopenharmony_ci return false; 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) 18898c2ecf20Sopenharmony_ci return false; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci return true; 18928c2ecf20Sopenharmony_ci} 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_cistatic void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index, 18958c2ecf20Sopenharmony_ci int *weaker, int *stronger) 18968c2ecf20Sopenharmony_ci{ 18978c2ecf20Sopenharmony_ci *weaker = index + IWL_MVM_RS_TPC_TX_POWER_STEP; 18988c2ecf20Sopenharmony_ci if (*weaker > TPC_MAX_REDUCTION) 18998c2ecf20Sopenharmony_ci *weaker = TPC_INVALID; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci *stronger = index - IWL_MVM_RS_TPC_TX_POWER_STEP; 19028c2ecf20Sopenharmony_ci if (*stronger < 0) 19038c2ecf20Sopenharmony_ci *stronger = TPC_INVALID; 19048c2ecf20Sopenharmony_ci} 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_cistatic bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, 19078c2ecf20Sopenharmony_ci struct rs_rate *rate, enum nl80211_band band) 19088c2ecf20Sopenharmony_ci{ 19098c2ecf20Sopenharmony_ci int index = rate->index; 19108c2ecf20Sopenharmony_ci bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); 19118c2ecf20Sopenharmony_ci bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION && 19128c2ecf20Sopenharmony_ci !vif->bss_conf.ps); 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n", 19158c2ecf20Sopenharmony_ci cam, sta_ps_disabled); 19168c2ecf20Sopenharmony_ci /* 19178c2ecf20Sopenharmony_ci * allow tpc only if power management is enabled, or bt coex 19188c2ecf20Sopenharmony_ci * activity grade allows it and we are on 2.4Ghz. 19198c2ecf20Sopenharmony_ci */ 19208c2ecf20Sopenharmony_ci if ((cam || sta_ps_disabled) && 19218c2ecf20Sopenharmony_ci !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band)) 19228c2ecf20Sopenharmony_ci return false; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type); 19258c2ecf20Sopenharmony_ci if (is_legacy(rate)) 19268c2ecf20Sopenharmony_ci return index == IWL_RATE_54M_INDEX; 19278c2ecf20Sopenharmony_ci if (is_ht(rate)) 19288c2ecf20Sopenharmony_ci return index == IWL_RATE_MCS_7_INDEX; 19298c2ecf20Sopenharmony_ci if (is_vht(rate)) 19308c2ecf20Sopenharmony_ci return index == IWL_RATE_MCS_7_INDEX || 19318c2ecf20Sopenharmony_ci index == IWL_RATE_MCS_8_INDEX || 19328c2ecf20Sopenharmony_ci index == IWL_RATE_MCS_9_INDEX; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 19358c2ecf20Sopenharmony_ci return false; 19368c2ecf20Sopenharmony_ci} 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_cienum tpc_action { 19398c2ecf20Sopenharmony_ci TPC_ACTION_STAY, 19408c2ecf20Sopenharmony_ci TPC_ACTION_DECREASE, 19418c2ecf20Sopenharmony_ci TPC_ACTION_INCREASE, 19428c2ecf20Sopenharmony_ci TPC_ACTION_NO_RESTIRCTION, 19438c2ecf20Sopenharmony_ci}; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_cistatic enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, 19468c2ecf20Sopenharmony_ci s32 sr, int weak, int strong, 19478c2ecf20Sopenharmony_ci int current_tpt, 19488c2ecf20Sopenharmony_ci int weak_tpt, int strong_tpt) 19498c2ecf20Sopenharmony_ci{ 19508c2ecf20Sopenharmony_ci /* stay until we have valid tpt */ 19518c2ecf20Sopenharmony_ci if (current_tpt == IWL_INVALID_VALUE) { 19528c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n"); 19538c2ecf20Sopenharmony_ci return TPC_ACTION_STAY; 19548c2ecf20Sopenharmony_ci } 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci /* Too many failures, increase txp */ 19578c2ecf20Sopenharmony_ci if (sr <= RS_PERCENT(IWL_MVM_RS_TPC_SR_FORCE_INCREASE) || 19588c2ecf20Sopenharmony_ci current_tpt == 0) { 19598c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n"); 19608c2ecf20Sopenharmony_ci return TPC_ACTION_NO_RESTIRCTION; 19618c2ecf20Sopenharmony_ci } 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci /* try decreasing first if applicable */ 19648c2ecf20Sopenharmony_ci if (sr >= RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) && 19658c2ecf20Sopenharmony_ci weak != TPC_INVALID) { 19668c2ecf20Sopenharmony_ci if (weak_tpt == IWL_INVALID_VALUE && 19678c2ecf20Sopenharmony_ci (strong_tpt == IWL_INVALID_VALUE || 19688c2ecf20Sopenharmony_ci current_tpt >= strong_tpt)) { 19698c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 19708c2ecf20Sopenharmony_ci "no weak txp measurement. decrease txp\n"); 19718c2ecf20Sopenharmony_ci return TPC_ACTION_DECREASE; 19728c2ecf20Sopenharmony_ci } 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci if (weak_tpt > current_tpt) { 19758c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 19768c2ecf20Sopenharmony_ci "lower txp has better tpt. decrease txp\n"); 19778c2ecf20Sopenharmony_ci return TPC_ACTION_DECREASE; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci } 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci /* next, increase if needed */ 19828c2ecf20Sopenharmony_ci if (sr < RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) && 19838c2ecf20Sopenharmony_ci strong != TPC_INVALID) { 19848c2ecf20Sopenharmony_ci if (weak_tpt == IWL_INVALID_VALUE && 19858c2ecf20Sopenharmony_ci strong_tpt != IWL_INVALID_VALUE && 19868c2ecf20Sopenharmony_ci current_tpt < strong_tpt) { 19878c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 19888c2ecf20Sopenharmony_ci "higher txp has better tpt. increase txp\n"); 19898c2ecf20Sopenharmony_ci return TPC_ACTION_INCREASE; 19908c2ecf20Sopenharmony_ci } 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (weak_tpt < current_tpt && 19938c2ecf20Sopenharmony_ci (strong_tpt == IWL_INVALID_VALUE || 19948c2ecf20Sopenharmony_ci strong_tpt > current_tpt)) { 19958c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 19968c2ecf20Sopenharmony_ci "lower txp has worse tpt. increase txp\n"); 19978c2ecf20Sopenharmony_ci return TPC_ACTION_INCREASE; 19988c2ecf20Sopenharmony_ci } 19998c2ecf20Sopenharmony_ci } 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n"); 20028c2ecf20Sopenharmony_ci return TPC_ACTION_STAY; 20038c2ecf20Sopenharmony_ci} 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_cistatic bool rs_tpc_perform(struct iwl_mvm *mvm, 20068c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 20078c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 20088c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl) 20098c2ecf20Sopenharmony_ci{ 20108c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); 20118c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = mvm_sta->vif; 20128c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 20138c2ecf20Sopenharmony_ci enum nl80211_band band; 20148c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window; 20158c2ecf20Sopenharmony_ci struct rs_rate *rate = &tbl->rate; 20168c2ecf20Sopenharmony_ci enum tpc_action action; 20178c2ecf20Sopenharmony_ci s32 sr; 20188c2ecf20Sopenharmony_ci u8 cur = lq_sta->lq.reduced_tpc; 20198c2ecf20Sopenharmony_ci int current_tpt; 20208c2ecf20Sopenharmony_ci int weak, strong; 20218c2ecf20Sopenharmony_ci int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 20248c2ecf20Sopenharmony_ci if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) { 20258c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n", 20268c2ecf20Sopenharmony_ci lq_sta->pers.dbg_fixed_txp_reduction); 20278c2ecf20Sopenharmony_ci lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction; 20288c2ecf20Sopenharmony_ci return cur != lq_sta->pers.dbg_fixed_txp_reduction; 20298c2ecf20Sopenharmony_ci } 20308c2ecf20Sopenharmony_ci#endif 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci rcu_read_lock(); 20338c2ecf20Sopenharmony_ci chanctx_conf = rcu_dereference(vif->chanctx_conf); 20348c2ecf20Sopenharmony_ci if (WARN_ON(!chanctx_conf)) 20358c2ecf20Sopenharmony_ci band = NUM_NL80211_BANDS; 20368c2ecf20Sopenharmony_ci else 20378c2ecf20Sopenharmony_ci band = chanctx_conf->def.chan->band; 20388c2ecf20Sopenharmony_ci rcu_read_unlock(); 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci if (!rs_tpc_allowed(mvm, vif, rate, band)) { 20418c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 20428c2ecf20Sopenharmony_ci "tpc is not allowed. remove txp restrictions\n"); 20438c2ecf20Sopenharmony_ci lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; 20448c2ecf20Sopenharmony_ci return cur != TPC_NO_REDUCTION; 20458c2ecf20Sopenharmony_ci } 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci rs_get_adjacent_txp(mvm, cur, &weak, &strong); 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci /* Collect measured throughputs for current and adjacent rates */ 20508c2ecf20Sopenharmony_ci window = tbl->tpc_win; 20518c2ecf20Sopenharmony_ci sr = window[cur].success_ratio; 20528c2ecf20Sopenharmony_ci current_tpt = window[cur].average_tpt; 20538c2ecf20Sopenharmony_ci if (weak != TPC_INVALID) 20548c2ecf20Sopenharmony_ci weak_tpt = window[weak].average_tpt; 20558c2ecf20Sopenharmony_ci if (strong != TPC_INVALID) 20568c2ecf20Sopenharmony_ci strong_tpt = window[strong].average_tpt; 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 20598c2ecf20Sopenharmony_ci "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n", 20608c2ecf20Sopenharmony_ci cur, current_tpt, sr, weak, strong, 20618c2ecf20Sopenharmony_ci weak_tpt, strong_tpt); 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci action = rs_get_tpc_action(mvm, sr, weak, strong, 20648c2ecf20Sopenharmony_ci current_tpt, weak_tpt, strong_tpt); 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci /* override actions if we are on the edge */ 20678c2ecf20Sopenharmony_ci if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) { 20688c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n"); 20698c2ecf20Sopenharmony_ci action = TPC_ACTION_STAY; 20708c2ecf20Sopenharmony_ci } else if (strong == TPC_INVALID && 20718c2ecf20Sopenharmony_ci (action == TPC_ACTION_INCREASE || 20728c2ecf20Sopenharmony_ci action == TPC_ACTION_NO_RESTIRCTION)) { 20738c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n"); 20748c2ecf20Sopenharmony_ci action = TPC_ACTION_STAY; 20758c2ecf20Sopenharmony_ci } 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci switch (action) { 20788c2ecf20Sopenharmony_ci case TPC_ACTION_DECREASE: 20798c2ecf20Sopenharmony_ci lq_sta->lq.reduced_tpc = weak; 20808c2ecf20Sopenharmony_ci return true; 20818c2ecf20Sopenharmony_ci case TPC_ACTION_INCREASE: 20828c2ecf20Sopenharmony_ci lq_sta->lq.reduced_tpc = strong; 20838c2ecf20Sopenharmony_ci return true; 20848c2ecf20Sopenharmony_ci case TPC_ACTION_NO_RESTIRCTION: 20858c2ecf20Sopenharmony_ci lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; 20868c2ecf20Sopenharmony_ci return true; 20878c2ecf20Sopenharmony_ci case TPC_ACTION_STAY: 20888c2ecf20Sopenharmony_ci /* do nothing */ 20898c2ecf20Sopenharmony_ci break; 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci return false; 20928c2ecf20Sopenharmony_ci} 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci/* 20958c2ecf20Sopenharmony_ci * Do rate scaling and search for new modulation mode. 20968c2ecf20Sopenharmony_ci */ 20978c2ecf20Sopenharmony_cistatic void rs_rate_scale_perform(struct iwl_mvm *mvm, 20988c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 20998c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 21008c2ecf20Sopenharmony_ci int tid, bool ndp) 21018c2ecf20Sopenharmony_ci{ 21028c2ecf20Sopenharmony_ci int low = IWL_RATE_INVALID; 21038c2ecf20Sopenharmony_ci int high = IWL_RATE_INVALID; 21048c2ecf20Sopenharmony_ci int index; 21058c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window = NULL; 21068c2ecf20Sopenharmony_ci int current_tpt = IWL_INVALID_VALUE; 21078c2ecf20Sopenharmony_ci int low_tpt = IWL_INVALID_VALUE; 21088c2ecf20Sopenharmony_ci int high_tpt = IWL_INVALID_VALUE; 21098c2ecf20Sopenharmony_ci u32 fail_count; 21108c2ecf20Sopenharmony_ci enum rs_action scale_action = RS_ACTION_STAY; 21118c2ecf20Sopenharmony_ci u16 rate_mask; 21128c2ecf20Sopenharmony_ci u8 update_lq = 0; 21138c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, *tbl1; 21148c2ecf20Sopenharmony_ci u8 active_tbl = 0; 21158c2ecf20Sopenharmony_ci u8 done_search = 0; 21168c2ecf20Sopenharmony_ci u16 high_low; 21178c2ecf20Sopenharmony_ci s32 sr; 21188c2ecf20Sopenharmony_ci u8 prev_agg = lq_sta->is_agg; 21198c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 21208c2ecf20Sopenharmony_ci struct rs_rate *rate; 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci lq_sta->is_agg = !!mvmsta->agg_tids; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci /* 21258c2ecf20Sopenharmony_ci * Select rate-scale / modulation-mode table to work with in 21268c2ecf20Sopenharmony_ci * the rest of this function: "search" if searching for better 21278c2ecf20Sopenharmony_ci * modulation mode, or "active" if doing rate scaling within a mode. 21288c2ecf20Sopenharmony_ci */ 21298c2ecf20Sopenharmony_ci if (!lq_sta->search_better_tbl) 21308c2ecf20Sopenharmony_ci active_tbl = lq_sta->active_tbl; 21318c2ecf20Sopenharmony_ci else 21328c2ecf20Sopenharmony_ci active_tbl = rs_search_tbl(lq_sta->active_tbl); 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[active_tbl]); 21358c2ecf20Sopenharmony_ci rate = &tbl->rate; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci if (prev_agg != lq_sta->is_agg) { 21388c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 21398c2ecf20Sopenharmony_ci "Aggregation changed: prev %d current %d. Update expected TPT table\n", 21408c2ecf20Sopenharmony_ci prev_agg, lq_sta->is_agg); 21418c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, tbl); 21428c2ecf20Sopenharmony_ci rs_rate_scale_clear_tbl_windows(mvm, tbl); 21438c2ecf20Sopenharmony_ci } 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci /* current tx rate */ 21468c2ecf20Sopenharmony_ci index = rate->index; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci /* rates available for this association, and for modulation mode */ 21498c2ecf20Sopenharmony_ci rate_mask = rs_get_supported_rates(lq_sta, rate); 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci if (!(BIT(index) & rate_mask)) { 21528c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Current Rate is not valid\n"); 21538c2ecf20Sopenharmony_ci if (lq_sta->search_better_tbl) { 21548c2ecf20Sopenharmony_ci /* revert to active table if search table is not valid*/ 21558c2ecf20Sopenharmony_ci rate->type = LQ_NONE; 21568c2ecf20Sopenharmony_ci lq_sta->search_better_tbl = 0; 21578c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 21588c2ecf20Sopenharmony_ci rs_update_rate_tbl(mvm, sta, lq_sta, tbl); 21598c2ecf20Sopenharmony_ci } 21608c2ecf20Sopenharmony_ci return; 21618c2ecf20Sopenharmony_ci } 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci /* Get expected throughput table and history window for current rate */ 21648c2ecf20Sopenharmony_ci if (!tbl->expected_tpt) { 21658c2ecf20Sopenharmony_ci IWL_ERR(mvm, "tbl->expected_tpt is NULL\n"); 21668c2ecf20Sopenharmony_ci return; 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ 21708c2ecf20Sopenharmony_ci window = &(tbl->win[index]); 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci /* 21738c2ecf20Sopenharmony_ci * If there is not enough history to calculate actual average 21748c2ecf20Sopenharmony_ci * throughput, keep analyzing results of more tx frames, without 21758c2ecf20Sopenharmony_ci * changing rate or mode (bypass most of the rest of this function). 21768c2ecf20Sopenharmony_ci * Set up new rate table in uCode only if old rate is not supported 21778c2ecf20Sopenharmony_ci * in current association (use new rate found above). 21788c2ecf20Sopenharmony_ci */ 21798c2ecf20Sopenharmony_ci fail_count = window->counter - window->success_counter; 21808c2ecf20Sopenharmony_ci if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) && 21818c2ecf20Sopenharmony_ci (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) { 21828c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 21838c2ecf20Sopenharmony_ci "%s: Test Window: succ %d total %d\n", 21848c2ecf20Sopenharmony_ci rs_pretty_rate(rate), 21858c2ecf20Sopenharmony_ci window->success_counter, window->counter); 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci /* Can't calculate this yet; not enough history */ 21888c2ecf20Sopenharmony_ci window->average_tpt = IWL_INVALID_VALUE; 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci /* Should we stay with this modulation mode, 21918c2ecf20Sopenharmony_ci * or search for a new one? */ 21928c2ecf20Sopenharmony_ci rs_stay_in_table(lq_sta, false); 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci return; 21958c2ecf20Sopenharmony_ci } 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci /* If we are searching for better modulation mode, check success. */ 21988c2ecf20Sopenharmony_ci if (lq_sta->search_better_tbl) { 21998c2ecf20Sopenharmony_ci /* If good success, continue using the "search" mode; 22008c2ecf20Sopenharmony_ci * no need to send new link quality command, since we're 22018c2ecf20Sopenharmony_ci * continuing to use the setup that we've been trying. */ 22028c2ecf20Sopenharmony_ci if (window->average_tpt > lq_sta->last_tpt) { 22038c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 22048c2ecf20Sopenharmony_ci "SWITCHING TO NEW TABLE SR: %d " 22058c2ecf20Sopenharmony_ci "cur-tpt %d old-tpt %d\n", 22068c2ecf20Sopenharmony_ci window->success_ratio, 22078c2ecf20Sopenharmony_ci window->average_tpt, 22088c2ecf20Sopenharmony_ci lq_sta->last_tpt); 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci /* Swap tables; "search" becomes "active" */ 22118c2ecf20Sopenharmony_ci lq_sta->active_tbl = active_tbl; 22128c2ecf20Sopenharmony_ci current_tpt = window->average_tpt; 22138c2ecf20Sopenharmony_ci /* Else poor success; go back to mode in "active" table */ 22148c2ecf20Sopenharmony_ci } else { 22158c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 22168c2ecf20Sopenharmony_ci "GOING BACK TO THE OLD TABLE: SR %d " 22178c2ecf20Sopenharmony_ci "cur-tpt %d old-tpt %d\n", 22188c2ecf20Sopenharmony_ci window->success_ratio, 22198c2ecf20Sopenharmony_ci window->average_tpt, 22208c2ecf20Sopenharmony_ci lq_sta->last_tpt); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci /* Nullify "search" table */ 22238c2ecf20Sopenharmony_ci rate->type = LQ_NONE; 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci /* Revert to "active" table */ 22268c2ecf20Sopenharmony_ci active_tbl = lq_sta->active_tbl; 22278c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[active_tbl]); 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci /* Revert to "active" rate and throughput info */ 22308c2ecf20Sopenharmony_ci index = tbl->rate.index; 22318c2ecf20Sopenharmony_ci current_tpt = lq_sta->last_tpt; 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci /* Need to set up a new rate table in uCode */ 22348c2ecf20Sopenharmony_ci update_lq = 1; 22358c2ecf20Sopenharmony_ci } 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci /* Either way, we've made a decision; modulation mode 22388c2ecf20Sopenharmony_ci * search is done, allow rate adjustment next time. */ 22398c2ecf20Sopenharmony_ci lq_sta->search_better_tbl = 0; 22408c2ecf20Sopenharmony_ci done_search = 1; /* Don't switch modes below! */ 22418c2ecf20Sopenharmony_ci goto lq_update; 22428c2ecf20Sopenharmony_ci } 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci /* (Else) not in search of better modulation mode, try for better 22458c2ecf20Sopenharmony_ci * starting rate, while staying in this mode. */ 22468c2ecf20Sopenharmony_ci high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type); 22478c2ecf20Sopenharmony_ci low = high_low & 0xff; 22488c2ecf20Sopenharmony_ci high = (high_low >> 8) & 0xff; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci sr = window->success_ratio; 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci /* Collect measured throughputs for current and adjacent rates */ 22558c2ecf20Sopenharmony_ci current_tpt = window->average_tpt; 22568c2ecf20Sopenharmony_ci if (low != IWL_RATE_INVALID) 22578c2ecf20Sopenharmony_ci low_tpt = tbl->win[low].average_tpt; 22588c2ecf20Sopenharmony_ci if (high != IWL_RATE_INVALID) 22598c2ecf20Sopenharmony_ci high_tpt = tbl->win[high].average_tpt; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 22628c2ecf20Sopenharmony_ci "%s: cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n", 22638c2ecf20Sopenharmony_ci rs_pretty_rate(rate), current_tpt, sr, 22648c2ecf20Sopenharmony_ci low, high, low_tpt, high_tpt); 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci scale_action = rs_get_rate_action(mvm, tbl, sr, low, high, 22678c2ecf20Sopenharmony_ci current_tpt, low_tpt, high_tpt); 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci /* Force a search in case BT doesn't like us being in MIMO */ 22708c2ecf20Sopenharmony_ci if (is_mimo(rate) && 22718c2ecf20Sopenharmony_ci !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) { 22728c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 22738c2ecf20Sopenharmony_ci "BT Coex forbids MIMO. Search for new config\n"); 22748c2ecf20Sopenharmony_ci rs_stay_in_table(lq_sta, true); 22758c2ecf20Sopenharmony_ci goto lq_update; 22768c2ecf20Sopenharmony_ci } 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci switch (scale_action) { 22798c2ecf20Sopenharmony_ci case RS_ACTION_DOWNSCALE: 22808c2ecf20Sopenharmony_ci /* Decrease starting rate, update uCode's rate table */ 22818c2ecf20Sopenharmony_ci if (low != IWL_RATE_INVALID) { 22828c2ecf20Sopenharmony_ci update_lq = 1; 22838c2ecf20Sopenharmony_ci index = low; 22848c2ecf20Sopenharmony_ci } else { 22858c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 22868c2ecf20Sopenharmony_ci "At the bottom rate. Can't decrease\n"); 22878c2ecf20Sopenharmony_ci } 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci break; 22908c2ecf20Sopenharmony_ci case RS_ACTION_UPSCALE: 22918c2ecf20Sopenharmony_ci /* Increase starting rate, update uCode's rate table */ 22928c2ecf20Sopenharmony_ci if (high != IWL_RATE_INVALID) { 22938c2ecf20Sopenharmony_ci update_lq = 1; 22948c2ecf20Sopenharmony_ci index = high; 22958c2ecf20Sopenharmony_ci } else { 22968c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 22978c2ecf20Sopenharmony_ci "At the top rate. Can't increase\n"); 22988c2ecf20Sopenharmony_ci } 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci break; 23018c2ecf20Sopenharmony_ci case RS_ACTION_STAY: 23028c2ecf20Sopenharmony_ci /* No change */ 23038c2ecf20Sopenharmony_ci if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) 23048c2ecf20Sopenharmony_ci update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl); 23058c2ecf20Sopenharmony_ci break; 23068c2ecf20Sopenharmony_ci default: 23078c2ecf20Sopenharmony_ci break; 23088c2ecf20Sopenharmony_ci } 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_cilq_update: 23118c2ecf20Sopenharmony_ci /* Replace uCode's rate table for the destination station. */ 23128c2ecf20Sopenharmony_ci if (update_lq) { 23138c2ecf20Sopenharmony_ci tbl->rate.index = index; 23148c2ecf20Sopenharmony_ci if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK) 23158c2ecf20Sopenharmony_ci rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action); 23168c2ecf20Sopenharmony_ci rs_set_amsdu_len(mvm, sta, tbl, scale_action); 23178c2ecf20Sopenharmony_ci rs_update_rate_tbl(mvm, sta, lq_sta, tbl); 23188c2ecf20Sopenharmony_ci } 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci rs_stay_in_table(lq_sta, false); 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci /* 23238c2ecf20Sopenharmony_ci * Search for new modulation mode if we're: 23248c2ecf20Sopenharmony_ci * 1) Not changing rates right now 23258c2ecf20Sopenharmony_ci * 2) Not just finishing up a search 23268c2ecf20Sopenharmony_ci * 3) Allowing a new search 23278c2ecf20Sopenharmony_ci */ 23288c2ecf20Sopenharmony_ci if (!update_lq && !done_search && 23298c2ecf20Sopenharmony_ci lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED 23308c2ecf20Sopenharmony_ci && window->counter) { 23318c2ecf20Sopenharmony_ci enum rs_column next_column; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci /* Save current throughput to compare with "search" throughput*/ 23348c2ecf20Sopenharmony_ci lq_sta->last_tpt = current_tpt; 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 23378c2ecf20Sopenharmony_ci "Start Search: update_lq %d done_search %d rs_state %d win->counter %d\n", 23388c2ecf20Sopenharmony_ci update_lq, done_search, lq_sta->rs_state, 23398c2ecf20Sopenharmony_ci window->counter); 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci next_column = rs_get_next_column(mvm, lq_sta, sta, tbl); 23428c2ecf20Sopenharmony_ci if (next_column != RS_COLUMN_INVALID) { 23438c2ecf20Sopenharmony_ci int ret = rs_switch_to_column(mvm, lq_sta, sta, 23448c2ecf20Sopenharmony_ci next_column); 23458c2ecf20Sopenharmony_ci if (!ret) 23468c2ecf20Sopenharmony_ci lq_sta->search_better_tbl = 1; 23478c2ecf20Sopenharmony_ci } else { 23488c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 23498c2ecf20Sopenharmony_ci "No more columns to explore in search cycle. Go to RS_STATE_SEARCH_CYCLE_ENDED\n"); 23508c2ecf20Sopenharmony_ci lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_ENDED; 23518c2ecf20Sopenharmony_ci } 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci /* If new "search" mode was selected, set up in uCode table */ 23548c2ecf20Sopenharmony_ci if (lq_sta->search_better_tbl) { 23558c2ecf20Sopenharmony_ci /* Access the "search" table, clear its history. */ 23568c2ecf20Sopenharmony_ci tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; 23578c2ecf20Sopenharmony_ci rs_rate_scale_clear_tbl_windows(mvm, tbl); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci /* Use new "search" start rate */ 23608c2ecf20Sopenharmony_ci index = tbl->rate.index; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci rs_dump_rate(mvm, &tbl->rate, 23638c2ecf20Sopenharmony_ci "Switch to SEARCH TABLE:"); 23648c2ecf20Sopenharmony_ci rs_update_rate_tbl(mvm, sta, lq_sta, tbl); 23658c2ecf20Sopenharmony_ci } else { 23668c2ecf20Sopenharmony_ci done_search = 1; 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ci } 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci if (!ndp) 23718c2ecf20Sopenharmony_ci rs_tl_turn_on_agg(mvm, mvmsta, tid, lq_sta, sta); 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci if (done_search && lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_ENDED) { 23748c2ecf20Sopenharmony_ci tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); 23758c2ecf20Sopenharmony_ci rs_set_stay_in_table(mvm, is_legacy(&tbl1->rate), lq_sta); 23768c2ecf20Sopenharmony_ci } 23778c2ecf20Sopenharmony_ci} 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_cistruct rs_init_rate_info { 23808c2ecf20Sopenharmony_ci s8 rssi; 23818c2ecf20Sopenharmony_ci u8 rate_idx; 23828c2ecf20Sopenharmony_ci}; 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_cistatic const struct rs_init_rate_info rs_optimal_rates_24ghz_legacy[] = { 23858c2ecf20Sopenharmony_ci { -60, IWL_RATE_54M_INDEX }, 23868c2ecf20Sopenharmony_ci { -64, IWL_RATE_48M_INDEX }, 23878c2ecf20Sopenharmony_ci { -68, IWL_RATE_36M_INDEX }, 23888c2ecf20Sopenharmony_ci { -80, IWL_RATE_24M_INDEX }, 23898c2ecf20Sopenharmony_ci { -84, IWL_RATE_18M_INDEX }, 23908c2ecf20Sopenharmony_ci { -85, IWL_RATE_12M_INDEX }, 23918c2ecf20Sopenharmony_ci { -86, IWL_RATE_11M_INDEX }, 23928c2ecf20Sopenharmony_ci { -88, IWL_RATE_5M_INDEX }, 23938c2ecf20Sopenharmony_ci { -90, IWL_RATE_2M_INDEX }, 23948c2ecf20Sopenharmony_ci { S8_MIN, IWL_RATE_1M_INDEX }, 23958c2ecf20Sopenharmony_ci}; 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_cistatic const struct rs_init_rate_info rs_optimal_rates_5ghz_legacy[] = { 23988c2ecf20Sopenharmony_ci { -60, IWL_RATE_54M_INDEX }, 23998c2ecf20Sopenharmony_ci { -64, IWL_RATE_48M_INDEX }, 24008c2ecf20Sopenharmony_ci { -72, IWL_RATE_36M_INDEX }, 24018c2ecf20Sopenharmony_ci { -80, IWL_RATE_24M_INDEX }, 24028c2ecf20Sopenharmony_ci { -84, IWL_RATE_18M_INDEX }, 24038c2ecf20Sopenharmony_ci { -85, IWL_RATE_12M_INDEX }, 24048c2ecf20Sopenharmony_ci { -87, IWL_RATE_9M_INDEX }, 24058c2ecf20Sopenharmony_ci { S8_MIN, IWL_RATE_6M_INDEX }, 24068c2ecf20Sopenharmony_ci}; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_cistatic const struct rs_init_rate_info rs_optimal_rates_ht[] = { 24098c2ecf20Sopenharmony_ci { -60, IWL_RATE_MCS_7_INDEX }, 24108c2ecf20Sopenharmony_ci { -64, IWL_RATE_MCS_6_INDEX }, 24118c2ecf20Sopenharmony_ci { -68, IWL_RATE_MCS_5_INDEX }, 24128c2ecf20Sopenharmony_ci { -72, IWL_RATE_MCS_4_INDEX }, 24138c2ecf20Sopenharmony_ci { -80, IWL_RATE_MCS_3_INDEX }, 24148c2ecf20Sopenharmony_ci { -84, IWL_RATE_MCS_2_INDEX }, 24158c2ecf20Sopenharmony_ci { -85, IWL_RATE_MCS_1_INDEX }, 24168c2ecf20Sopenharmony_ci { S8_MIN, IWL_RATE_MCS_0_INDEX}, 24178c2ecf20Sopenharmony_ci}; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci/* MCS index 9 is not valid for 20MHz VHT channel width, 24208c2ecf20Sopenharmony_ci * but is ok for 40, 80 and 160MHz channels. 24218c2ecf20Sopenharmony_ci */ 24228c2ecf20Sopenharmony_cistatic const struct rs_init_rate_info rs_optimal_rates_vht_20mhz[] = { 24238c2ecf20Sopenharmony_ci { -60, IWL_RATE_MCS_8_INDEX }, 24248c2ecf20Sopenharmony_ci { -64, IWL_RATE_MCS_7_INDEX }, 24258c2ecf20Sopenharmony_ci { -68, IWL_RATE_MCS_6_INDEX }, 24268c2ecf20Sopenharmony_ci { -72, IWL_RATE_MCS_5_INDEX }, 24278c2ecf20Sopenharmony_ci { -80, IWL_RATE_MCS_4_INDEX }, 24288c2ecf20Sopenharmony_ci { -84, IWL_RATE_MCS_3_INDEX }, 24298c2ecf20Sopenharmony_ci { -85, IWL_RATE_MCS_2_INDEX }, 24308c2ecf20Sopenharmony_ci { -87, IWL_RATE_MCS_1_INDEX }, 24318c2ecf20Sopenharmony_ci { S8_MIN, IWL_RATE_MCS_0_INDEX}, 24328c2ecf20Sopenharmony_ci}; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_cistatic const struct rs_init_rate_info rs_optimal_rates_vht[] = { 24358c2ecf20Sopenharmony_ci { -60, IWL_RATE_MCS_9_INDEX }, 24368c2ecf20Sopenharmony_ci { -64, IWL_RATE_MCS_8_INDEX }, 24378c2ecf20Sopenharmony_ci { -68, IWL_RATE_MCS_7_INDEX }, 24388c2ecf20Sopenharmony_ci { -72, IWL_RATE_MCS_6_INDEX }, 24398c2ecf20Sopenharmony_ci { -80, IWL_RATE_MCS_5_INDEX }, 24408c2ecf20Sopenharmony_ci { -84, IWL_RATE_MCS_4_INDEX }, 24418c2ecf20Sopenharmony_ci { -85, IWL_RATE_MCS_3_INDEX }, 24428c2ecf20Sopenharmony_ci { -87, IWL_RATE_MCS_2_INDEX }, 24438c2ecf20Sopenharmony_ci { -88, IWL_RATE_MCS_1_INDEX }, 24448c2ecf20Sopenharmony_ci { S8_MIN, IWL_RATE_MCS_0_INDEX }, 24458c2ecf20Sopenharmony_ci}; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci#define IWL_RS_LOW_RSSI_THRESHOLD (-76) /* dBm */ 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci/* Init the optimal rate based on STA caps 24508c2ecf20Sopenharmony_ci * This combined with rssi is used to report the last tx rate 24518c2ecf20Sopenharmony_ci * to userspace when we haven't transmitted enough frames. 24528c2ecf20Sopenharmony_ci */ 24538c2ecf20Sopenharmony_cistatic void rs_init_optimal_rate(struct iwl_mvm *mvm, 24548c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 24558c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 24568c2ecf20Sopenharmony_ci{ 24578c2ecf20Sopenharmony_ci struct rs_rate *rate = &lq_sta->optimal_rate; 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci if (lq_sta->max_mimo2_rate_idx != IWL_RATE_INVALID) 24608c2ecf20Sopenharmony_ci rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; 24618c2ecf20Sopenharmony_ci else if (lq_sta->max_siso_rate_idx != IWL_RATE_INVALID) 24628c2ecf20Sopenharmony_ci rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; 24638c2ecf20Sopenharmony_ci else if (lq_sta->band == NL80211_BAND_5GHZ) 24648c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_A; 24658c2ecf20Sopenharmony_ci else 24668c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_G; 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci rate->bw = rs_bw_from_sta_bw(sta); 24698c2ecf20Sopenharmony_ci rate->sgi = rs_sgi_allow(mvm, sta, rate, NULL); 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci /* ANT/LDPC/STBC aren't relevant for the rate reported to userspace */ 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci if (is_mimo(rate)) { 24748c2ecf20Sopenharmony_ci lq_sta->optimal_rate_mask = lq_sta->active_mimo2_rate; 24758c2ecf20Sopenharmony_ci } else if (is_siso(rate)) { 24768c2ecf20Sopenharmony_ci lq_sta->optimal_rate_mask = lq_sta->active_siso_rate; 24778c2ecf20Sopenharmony_ci } else { 24788c2ecf20Sopenharmony_ci lq_sta->optimal_rate_mask = lq_sta->active_legacy_rate; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci if (lq_sta->band == NL80211_BAND_5GHZ) { 24818c2ecf20Sopenharmony_ci lq_sta->optimal_rates = rs_optimal_rates_5ghz_legacy; 24828c2ecf20Sopenharmony_ci lq_sta->optimal_nentries = 24838c2ecf20Sopenharmony_ci ARRAY_SIZE(rs_optimal_rates_5ghz_legacy); 24848c2ecf20Sopenharmony_ci } else { 24858c2ecf20Sopenharmony_ci lq_sta->optimal_rates = rs_optimal_rates_24ghz_legacy; 24868c2ecf20Sopenharmony_ci lq_sta->optimal_nentries = 24878c2ecf20Sopenharmony_ci ARRAY_SIZE(rs_optimal_rates_24ghz_legacy); 24888c2ecf20Sopenharmony_ci } 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci if (is_vht(rate)) { 24928c2ecf20Sopenharmony_ci if (rate->bw == RATE_MCS_CHAN_WIDTH_20) { 24938c2ecf20Sopenharmony_ci lq_sta->optimal_rates = rs_optimal_rates_vht_20mhz; 24948c2ecf20Sopenharmony_ci lq_sta->optimal_nentries = 24958c2ecf20Sopenharmony_ci ARRAY_SIZE(rs_optimal_rates_vht_20mhz); 24968c2ecf20Sopenharmony_ci } else { 24978c2ecf20Sopenharmony_ci lq_sta->optimal_rates = rs_optimal_rates_vht; 24988c2ecf20Sopenharmony_ci lq_sta->optimal_nentries = 24998c2ecf20Sopenharmony_ci ARRAY_SIZE(rs_optimal_rates_vht); 25008c2ecf20Sopenharmony_ci } 25018c2ecf20Sopenharmony_ci } else if (is_ht(rate)) { 25028c2ecf20Sopenharmony_ci lq_sta->optimal_rates = rs_optimal_rates_ht; 25038c2ecf20Sopenharmony_ci lq_sta->optimal_nentries = ARRAY_SIZE(rs_optimal_rates_ht); 25048c2ecf20Sopenharmony_ci } 25058c2ecf20Sopenharmony_ci} 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci/* Compute the optimal rate index based on RSSI */ 25088c2ecf20Sopenharmony_cistatic struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm, 25098c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 25108c2ecf20Sopenharmony_ci{ 25118c2ecf20Sopenharmony_ci struct rs_rate *rate = &lq_sta->optimal_rate; 25128c2ecf20Sopenharmony_ci int i; 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci rate->index = find_first_bit(&lq_sta->optimal_rate_mask, 25158c2ecf20Sopenharmony_ci BITS_PER_LONG); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci for (i = 0; i < lq_sta->optimal_nentries; i++) { 25188c2ecf20Sopenharmony_ci int rate_idx = lq_sta->optimal_rates[i].rate_idx; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci if ((lq_sta->pers.last_rssi >= lq_sta->optimal_rates[i].rssi) && 25218c2ecf20Sopenharmony_ci (BIT(rate_idx) & lq_sta->optimal_rate_mask)) { 25228c2ecf20Sopenharmony_ci rate->index = rate_idx; 25238c2ecf20Sopenharmony_ci break; 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_ci } 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci return rate; 25288c2ecf20Sopenharmony_ci} 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci/* Choose an initial legacy rate and antenna to use based on the RSSI 25318c2ecf20Sopenharmony_ci * of last Rx 25328c2ecf20Sopenharmony_ci */ 25338c2ecf20Sopenharmony_cistatic void rs_get_initial_rate(struct iwl_mvm *mvm, 25348c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 25358c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 25368c2ecf20Sopenharmony_ci enum nl80211_band band, 25378c2ecf20Sopenharmony_ci struct rs_rate *rate) 25388c2ecf20Sopenharmony_ci{ 25398c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 25408c2ecf20Sopenharmony_ci int i, nentries; 25418c2ecf20Sopenharmony_ci unsigned long active_rate; 25428c2ecf20Sopenharmony_ci s8 best_rssi = S8_MIN; 25438c2ecf20Sopenharmony_ci u8 best_ant = ANT_NONE; 25448c2ecf20Sopenharmony_ci u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); 25458c2ecf20Sopenharmony_ci const struct rs_init_rate_info *initial_rates; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { 25488c2ecf20Sopenharmony_ci if (!(lq_sta->pers.chains & BIT(i))) 25498c2ecf20Sopenharmony_ci continue; 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci if (lq_sta->pers.chain_signal[i] > best_rssi) { 25528c2ecf20Sopenharmony_ci best_rssi = lq_sta->pers.chain_signal[i]; 25538c2ecf20Sopenharmony_ci best_ant = BIT(i); 25548c2ecf20Sopenharmony_ci } 25558c2ecf20Sopenharmony_ci } 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Best ANT: %s Best RSSI: %d\n", 25588c2ecf20Sopenharmony_ci rs_pretty_ant(best_ant), best_rssi); 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci if (best_ant != ANT_A && best_ant != ANT_B) 25618c2ecf20Sopenharmony_ci rate->ant = first_antenna(valid_tx_ant); 25628c2ecf20Sopenharmony_ci else 25638c2ecf20Sopenharmony_ci rate->ant = best_ant; 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci rate->sgi = false; 25668c2ecf20Sopenharmony_ci rate->ldpc = false; 25678c2ecf20Sopenharmony_ci rate->bw = RATE_MCS_CHAN_WIDTH_20; 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci rate->index = find_first_bit(&lq_sta->active_legacy_rate, 25708c2ecf20Sopenharmony_ci BITS_PER_LONG); 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci if (band == NL80211_BAND_5GHZ) { 25738c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_A; 25748c2ecf20Sopenharmony_ci initial_rates = rs_optimal_rates_5ghz_legacy; 25758c2ecf20Sopenharmony_ci nentries = ARRAY_SIZE(rs_optimal_rates_5ghz_legacy); 25768c2ecf20Sopenharmony_ci } else { 25778c2ecf20Sopenharmony_ci rate->type = LQ_LEGACY_G; 25788c2ecf20Sopenharmony_ci initial_rates = rs_optimal_rates_24ghz_legacy; 25798c2ecf20Sopenharmony_ci nentries = ARRAY_SIZE(rs_optimal_rates_24ghz_legacy); 25808c2ecf20Sopenharmony_ci } 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci if (!IWL_MVM_RS_RSSI_BASED_INIT_RATE) 25838c2ecf20Sopenharmony_ci goto out; 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci /* Start from a higher rate if the corresponding debug capability 25868c2ecf20Sopenharmony_ci * is enabled. The rate is chosen according to AP capabilities. 25878c2ecf20Sopenharmony_ci * In case of VHT/HT when the rssi is low fallback to the case of 25888c2ecf20Sopenharmony_ci * legacy rates. 25898c2ecf20Sopenharmony_ci */ 25908c2ecf20Sopenharmony_ci if (sta->vht_cap.vht_supported && 25918c2ecf20Sopenharmony_ci best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) { 25928c2ecf20Sopenharmony_ci /* 25938c2ecf20Sopenharmony_ci * In AP mode, when a new station associates, rs is initialized 25948c2ecf20Sopenharmony_ci * immediately upon association completion, before the phy 25958c2ecf20Sopenharmony_ci * context is updated with the association parameters, so the 25968c2ecf20Sopenharmony_ci * sta bandwidth might be wider than the phy context allows. 25978c2ecf20Sopenharmony_ci * To avoid this issue, always initialize rs with 20mhz 25988c2ecf20Sopenharmony_ci * bandwidth rate, and after authorization, when the phy context 25998c2ecf20Sopenharmony_ci * is already up-to-date, re-init rs with the correct bw. 26008c2ecf20Sopenharmony_ci */ 26018c2ecf20Sopenharmony_ci u32 bw = mvmsta->sta_state < IEEE80211_STA_AUTHORIZED ? 26028c2ecf20Sopenharmony_ci RATE_MCS_CHAN_WIDTH_20 : rs_bw_from_sta_bw(sta); 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci switch (bw) { 26058c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_40: 26068c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_80: 26078c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_160: 26088c2ecf20Sopenharmony_ci initial_rates = rs_optimal_rates_vht; 26098c2ecf20Sopenharmony_ci nentries = ARRAY_SIZE(rs_optimal_rates_vht); 26108c2ecf20Sopenharmony_ci break; 26118c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_20: 26128c2ecf20Sopenharmony_ci initial_rates = rs_optimal_rates_vht_20mhz; 26138c2ecf20Sopenharmony_ci nentries = ARRAY_SIZE(rs_optimal_rates_vht_20mhz); 26148c2ecf20Sopenharmony_ci break; 26158c2ecf20Sopenharmony_ci default: 26168c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Invalid BW %d\n", sta->bandwidth); 26178c2ecf20Sopenharmony_ci goto out; 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci active_rate = lq_sta->active_siso_rate; 26218c2ecf20Sopenharmony_ci rate->type = LQ_VHT_SISO; 26228c2ecf20Sopenharmony_ci rate->bw = bw; 26238c2ecf20Sopenharmony_ci } else if (sta->ht_cap.ht_supported && 26248c2ecf20Sopenharmony_ci best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) { 26258c2ecf20Sopenharmony_ci initial_rates = rs_optimal_rates_ht; 26268c2ecf20Sopenharmony_ci nentries = ARRAY_SIZE(rs_optimal_rates_ht); 26278c2ecf20Sopenharmony_ci active_rate = lq_sta->active_siso_rate; 26288c2ecf20Sopenharmony_ci rate->type = LQ_HT_SISO; 26298c2ecf20Sopenharmony_ci } else { 26308c2ecf20Sopenharmony_ci active_rate = lq_sta->active_legacy_rate; 26318c2ecf20Sopenharmony_ci } 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_ci for (i = 0; i < nentries; i++) { 26348c2ecf20Sopenharmony_ci int rate_idx = initial_rates[i].rate_idx; 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci if ((best_rssi >= initial_rates[i].rssi) && 26378c2ecf20Sopenharmony_ci (BIT(rate_idx) & active_rate)) { 26388c2ecf20Sopenharmony_ci rate->index = rate_idx; 26398c2ecf20Sopenharmony_ci break; 26408c2ecf20Sopenharmony_ci } 26418c2ecf20Sopenharmony_ci } 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ciout: 26448c2ecf20Sopenharmony_ci rs_dump_rate(mvm, rate, "INITIAL"); 26458c2ecf20Sopenharmony_ci} 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci/* Save info about RSSI of last Rx */ 26488c2ecf20Sopenharmony_civoid rs_update_last_rssi(struct iwl_mvm *mvm, 26498c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta, 26508c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status) 26518c2ecf20Sopenharmony_ci{ 26528c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; 26538c2ecf20Sopenharmony_ci int i; 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci lq_sta->pers.chains = rx_status->chains; 26568c2ecf20Sopenharmony_ci lq_sta->pers.chain_signal[0] = rx_status->chain_signal[0]; 26578c2ecf20Sopenharmony_ci lq_sta->pers.chain_signal[1] = rx_status->chain_signal[1]; 26588c2ecf20Sopenharmony_ci lq_sta->pers.chain_signal[2] = rx_status->chain_signal[2]; 26598c2ecf20Sopenharmony_ci lq_sta->pers.last_rssi = S8_MIN; 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { 26628c2ecf20Sopenharmony_ci if (!(lq_sta->pers.chains & BIT(i))) 26638c2ecf20Sopenharmony_ci continue; 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci if (lq_sta->pers.chain_signal[i] > lq_sta->pers.last_rssi) 26668c2ecf20Sopenharmony_ci lq_sta->pers.last_rssi = lq_sta->pers.chain_signal[i]; 26678c2ecf20Sopenharmony_ci } 26688c2ecf20Sopenharmony_ci} 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_ci/** 26718c2ecf20Sopenharmony_ci * rs_initialize_lq - Initialize a station's hardware rate table 26728c2ecf20Sopenharmony_ci * 26738c2ecf20Sopenharmony_ci * The uCode's station table contains a table of fallback rates 26748c2ecf20Sopenharmony_ci * for automatic fallback during transmission. 26758c2ecf20Sopenharmony_ci * 26768c2ecf20Sopenharmony_ci * NOTE: This sets up a default set of values. These will be replaced later 26778c2ecf20Sopenharmony_ci * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of 26788c2ecf20Sopenharmony_ci * rc80211_simple. 26798c2ecf20Sopenharmony_ci * 26808c2ecf20Sopenharmony_ci * NOTE: Run REPLY_ADD_STA command to set up station table entry, before 26818c2ecf20Sopenharmony_ci * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, 26828c2ecf20Sopenharmony_ci * which requires station table entry to exist). 26838c2ecf20Sopenharmony_ci */ 26848c2ecf20Sopenharmony_cistatic void rs_initialize_lq(struct iwl_mvm *mvm, 26858c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 26868c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 26878c2ecf20Sopenharmony_ci enum nl80211_band band) 26888c2ecf20Sopenharmony_ci{ 26898c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl; 26908c2ecf20Sopenharmony_ci struct rs_rate *rate; 26918c2ecf20Sopenharmony_ci u8 active_tbl = 0; 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci if (!sta || !lq_sta) 26948c2ecf20Sopenharmony_ci return; 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci if (!lq_sta->search_better_tbl) 26978c2ecf20Sopenharmony_ci active_tbl = lq_sta->active_tbl; 26988c2ecf20Sopenharmony_ci else 26998c2ecf20Sopenharmony_ci active_tbl = rs_search_tbl(lq_sta->active_tbl); 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[active_tbl]); 27028c2ecf20Sopenharmony_ci rate = &tbl->rate; 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_ci rs_get_initial_rate(mvm, sta, lq_sta, band, rate); 27058c2ecf20Sopenharmony_ci rs_init_optimal_rate(mvm, sta, lq_sta); 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_ci WARN_ONCE(rate->ant != ANT_A && rate->ant != ANT_B, 27088c2ecf20Sopenharmony_ci "ant: 0x%x, chains 0x%x, fw tx ant: 0x%x, nvm tx ant: 0x%x\n", 27098c2ecf20Sopenharmony_ci rate->ant, lq_sta->pers.chains, mvm->fw->valid_tx_ant, 27108c2ecf20Sopenharmony_ci mvm->nvm_data ? mvm->nvm_data->valid_tx_ant : ANT_INVALID); 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_ci tbl->column = rs_get_column_from_rate(rate); 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, tbl); 27158c2ecf20Sopenharmony_ci rs_fill_lq_cmd(mvm, sta, lq_sta, rate); 27168c2ecf20Sopenharmony_ci /* TODO restore station should remember the lq cmd */ 27178c2ecf20Sopenharmony_ci iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq); 27188c2ecf20Sopenharmony_ci} 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_cistatic void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta, 27218c2ecf20Sopenharmony_ci void *mvm_sta, 27228c2ecf20Sopenharmony_ci struct ieee80211_tx_rate_control *txrc) 27238c2ecf20Sopenharmony_ci{ 27248c2ecf20Sopenharmony_ci struct iwl_op_mode *op_mode = mvm_r; 27258c2ecf20Sopenharmony_ci struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); 27268c2ecf20Sopenharmony_ci struct sk_buff *skb = txrc->skb; 27278c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 27288c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta; 27298c2ecf20Sopenharmony_ci struct rs_rate *optimal_rate; 27308c2ecf20Sopenharmony_ci u32 last_ucode_rate; 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) { 27338c2ecf20Sopenharmony_ci /* if vif isn't initialized mvm doesn't know about 27348c2ecf20Sopenharmony_ci * this station, so don't do anything with the it 27358c2ecf20Sopenharmony_ci */ 27368c2ecf20Sopenharmony_ci sta = NULL; 27378c2ecf20Sopenharmony_ci mvm_sta = NULL; 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci if (!mvm_sta) 27418c2ecf20Sopenharmony_ci return; 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci lq_sta = mvm_sta; 27448c2ecf20Sopenharmony_ci iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags, 27458c2ecf20Sopenharmony_ci info->band, &info->control.rates[0]); 27468c2ecf20Sopenharmony_ci info->control.rates[0].count = 1; 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_ci /* Report the optimal rate based on rssi and STA caps if we haven't 27498c2ecf20Sopenharmony_ci * converged yet (too little traffic) or exploring other modulations 27508c2ecf20Sopenharmony_ci */ 27518c2ecf20Sopenharmony_ci if (lq_sta->rs_state != RS_STATE_STAY_IN_COLUMN) { 27528c2ecf20Sopenharmony_ci optimal_rate = rs_get_optimal_rate(mvm, lq_sta); 27538c2ecf20Sopenharmony_ci last_ucode_rate = ucode_rate_from_rs_rate(mvm, 27548c2ecf20Sopenharmony_ci optimal_rate); 27558c2ecf20Sopenharmony_ci iwl_mvm_hwrate_to_tx_rate(last_ucode_rate, info->band, 27568c2ecf20Sopenharmony_ci &txrc->reported_rate); 27578c2ecf20Sopenharmony_ci } 27588c2ecf20Sopenharmony_ci} 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_cistatic void *rs_drv_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, 27618c2ecf20Sopenharmony_ci gfp_t gfp) 27628c2ecf20Sopenharmony_ci{ 27638c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 27648c2ecf20Sopenharmony_ci struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate; 27658c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); 27668c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; 27678c2ecf20Sopenharmony_ci 27688c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_ci lq_sta->pers.drv = mvm; 27718c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 27728c2ecf20Sopenharmony_ci lq_sta->pers.dbg_fixed_rate = 0; 27738c2ecf20Sopenharmony_ci lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID; 27748c2ecf20Sopenharmony_ci lq_sta->pers.ss_force = RS_SS_FORCE_NONE; 27758c2ecf20Sopenharmony_ci#endif 27768c2ecf20Sopenharmony_ci lq_sta->pers.chains = 0; 27778c2ecf20Sopenharmony_ci memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal)); 27788c2ecf20Sopenharmony_ci lq_sta->pers.last_rssi = S8_MIN; 27798c2ecf20Sopenharmony_ci 27808c2ecf20Sopenharmony_ci return lq_sta; 27818c2ecf20Sopenharmony_ci} 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_cistatic int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, 27848c2ecf20Sopenharmony_ci int nss) 27858c2ecf20Sopenharmony_ci{ 27868c2ecf20Sopenharmony_ci u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & 27878c2ecf20Sopenharmony_ci (0x3 << (2 * (nss - 1))); 27888c2ecf20Sopenharmony_ci rx_mcs >>= (2 * (nss - 1)); 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7) 27918c2ecf20Sopenharmony_ci return IWL_RATE_MCS_7_INDEX; 27928c2ecf20Sopenharmony_ci else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8) 27938c2ecf20Sopenharmony_ci return IWL_RATE_MCS_8_INDEX; 27948c2ecf20Sopenharmony_ci else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9) 27958c2ecf20Sopenharmony_ci return IWL_RATE_MCS_9_INDEX; 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED); 27988c2ecf20Sopenharmony_ci return -1; 27998c2ecf20Sopenharmony_ci} 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_cistatic void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, 28028c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap, 28038c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 28048c2ecf20Sopenharmony_ci{ 28058c2ecf20Sopenharmony_ci int i; 28068c2ecf20Sopenharmony_ci int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1); 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_ci if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { 28098c2ecf20Sopenharmony_ci for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { 28108c2ecf20Sopenharmony_ci if (i == IWL_RATE_9M_INDEX) 28118c2ecf20Sopenharmony_ci continue; 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ 28148c2ecf20Sopenharmony_ci if (i == IWL_RATE_MCS_9_INDEX && 28158c2ecf20Sopenharmony_ci sta->bandwidth == IEEE80211_STA_RX_BW_20) 28168c2ecf20Sopenharmony_ci continue; 28178c2ecf20Sopenharmony_ci 28188c2ecf20Sopenharmony_ci lq_sta->active_siso_rate |= BIT(i); 28198c2ecf20Sopenharmony_ci } 28208c2ecf20Sopenharmony_ci } 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci if (sta->rx_nss < 2) 28238c2ecf20Sopenharmony_ci return; 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2); 28268c2ecf20Sopenharmony_ci if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { 28278c2ecf20Sopenharmony_ci for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { 28288c2ecf20Sopenharmony_ci if (i == IWL_RATE_9M_INDEX) 28298c2ecf20Sopenharmony_ci continue; 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ 28328c2ecf20Sopenharmony_ci if (i == IWL_RATE_MCS_9_INDEX && 28338c2ecf20Sopenharmony_ci sta->bandwidth == IEEE80211_STA_RX_BW_20) 28348c2ecf20Sopenharmony_ci continue; 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate |= BIT(i); 28378c2ecf20Sopenharmony_ci } 28388c2ecf20Sopenharmony_ci } 28398c2ecf20Sopenharmony_ci} 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_cistatic void rs_ht_init(struct iwl_mvm *mvm, 28428c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 28438c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 28448c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap) 28458c2ecf20Sopenharmony_ci{ 28468c2ecf20Sopenharmony_ci /* active_siso_rate mask includes 9 MBits (bit 5), 28478c2ecf20Sopenharmony_ci * and CCK (bits 0-3), supp_rates[] does not; 28488c2ecf20Sopenharmony_ci * shift to convert format, force 9 MBits off. 28498c2ecf20Sopenharmony_ci */ 28508c2ecf20Sopenharmony_ci lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; 28518c2ecf20Sopenharmony_ci lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; 28528c2ecf20Sopenharmony_ci lq_sta->active_siso_rate &= ~((u16)0x2); 28538c2ecf20Sopenharmony_ci lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; 28568c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; 28578c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate &= ~((u16)0x2); 28588c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ci if (mvm->cfg->ht_params->ldpc && 28618c2ecf20Sopenharmony_ci (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) 28628c2ecf20Sopenharmony_ci lq_sta->ldpc = true; 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci if (mvm->cfg->ht_params->stbc && 28658c2ecf20Sopenharmony_ci (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && 28668c2ecf20Sopenharmony_ci (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) 28678c2ecf20Sopenharmony_ci lq_sta->stbc_capable = true; 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci lq_sta->is_vht = false; 28708c2ecf20Sopenharmony_ci} 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_cistatic void rs_vht_init(struct iwl_mvm *mvm, 28738c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 28748c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 28758c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap) 28768c2ecf20Sopenharmony_ci{ 28778c2ecf20Sopenharmony_ci rs_vht_set_enabled_rates(sta, vht_cap, lq_sta); 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci if (mvm->cfg->ht_params->ldpc && 28808c2ecf20Sopenharmony_ci (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)) 28818c2ecf20Sopenharmony_ci lq_sta->ldpc = true; 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci if (mvm->cfg->ht_params->stbc && 28848c2ecf20Sopenharmony_ci (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && 28858c2ecf20Sopenharmony_ci (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) 28868c2ecf20Sopenharmony_ci lq_sta->stbc_capable = true; 28878c2ecf20Sopenharmony_ci 28888c2ecf20Sopenharmony_ci if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) && 28898c2ecf20Sopenharmony_ci (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && 28908c2ecf20Sopenharmony_ci (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) 28918c2ecf20Sopenharmony_ci lq_sta->bfer_capable = true; 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci lq_sta->is_vht = true; 28948c2ecf20Sopenharmony_ci} 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 28978c2ecf20Sopenharmony_civoid iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm) 28988c2ecf20Sopenharmony_ci{ 28998c2ecf20Sopenharmony_ci spin_lock_bh(&mvm->drv_stats_lock); 29008c2ecf20Sopenharmony_ci memset(&mvm->drv_rx_stats, 0, sizeof(mvm->drv_rx_stats)); 29018c2ecf20Sopenharmony_ci spin_unlock_bh(&mvm->drv_stats_lock); 29028c2ecf20Sopenharmony_ci} 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_civoid iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) 29058c2ecf20Sopenharmony_ci{ 29068c2ecf20Sopenharmony_ci u8 nss = 0; 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci spin_lock(&mvm->drv_stats_lock); 29098c2ecf20Sopenharmony_ci 29108c2ecf20Sopenharmony_ci if (agg) 29118c2ecf20Sopenharmony_ci mvm->drv_rx_stats.agg_frames++; 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci mvm->drv_rx_stats.success_frames++; 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { 29168c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_20: 29178c2ecf20Sopenharmony_ci mvm->drv_rx_stats.bw_20_frames++; 29188c2ecf20Sopenharmony_ci break; 29198c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_40: 29208c2ecf20Sopenharmony_ci mvm->drv_rx_stats.bw_40_frames++; 29218c2ecf20Sopenharmony_ci break; 29228c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_80: 29238c2ecf20Sopenharmony_ci mvm->drv_rx_stats.bw_80_frames++; 29248c2ecf20Sopenharmony_ci break; 29258c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_160: 29268c2ecf20Sopenharmony_ci mvm->drv_rx_stats.bw_160_frames++; 29278c2ecf20Sopenharmony_ci break; 29288c2ecf20Sopenharmony_ci default: 29298c2ecf20Sopenharmony_ci WARN_ONCE(1, "bad BW. rate 0x%x", rate); 29308c2ecf20Sopenharmony_ci } 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci if (rate & RATE_MCS_HT_MSK) { 29338c2ecf20Sopenharmony_ci mvm->drv_rx_stats.ht_frames++; 29348c2ecf20Sopenharmony_ci nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1; 29358c2ecf20Sopenharmony_ci } else if (rate & RATE_MCS_VHT_MSK) { 29368c2ecf20Sopenharmony_ci mvm->drv_rx_stats.vht_frames++; 29378c2ecf20Sopenharmony_ci nss = ((rate & RATE_VHT_MCS_NSS_MSK) >> 29388c2ecf20Sopenharmony_ci RATE_VHT_MCS_NSS_POS) + 1; 29398c2ecf20Sopenharmony_ci } else { 29408c2ecf20Sopenharmony_ci mvm->drv_rx_stats.legacy_frames++; 29418c2ecf20Sopenharmony_ci } 29428c2ecf20Sopenharmony_ci 29438c2ecf20Sopenharmony_ci if (nss == 1) 29448c2ecf20Sopenharmony_ci mvm->drv_rx_stats.siso_frames++; 29458c2ecf20Sopenharmony_ci else if (nss == 2) 29468c2ecf20Sopenharmony_ci mvm->drv_rx_stats.mimo2_frames++; 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci if (rate & RATE_MCS_SGI_MSK) 29498c2ecf20Sopenharmony_ci mvm->drv_rx_stats.sgi_frames++; 29508c2ecf20Sopenharmony_ci else 29518c2ecf20Sopenharmony_ci mvm->drv_rx_stats.ngi_frames++; 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci mvm->drv_rx_stats.last_rates[mvm->drv_rx_stats.last_frame_idx] = rate; 29548c2ecf20Sopenharmony_ci mvm->drv_rx_stats.last_frame_idx = 29558c2ecf20Sopenharmony_ci (mvm->drv_rx_stats.last_frame_idx + 1) % 29568c2ecf20Sopenharmony_ci ARRAY_SIZE(mvm->drv_rx_stats.last_rates); 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci spin_unlock(&mvm->drv_stats_lock); 29598c2ecf20Sopenharmony_ci} 29608c2ecf20Sopenharmony_ci#endif 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_ci/* 29638c2ecf20Sopenharmony_ci * Called after adding a new station to initialize rate scaling 29648c2ecf20Sopenharmony_ci */ 29658c2ecf20Sopenharmony_cistatic void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 29668c2ecf20Sopenharmony_ci enum nl80211_band band) 29678c2ecf20Sopenharmony_ci{ 29688c2ecf20Sopenharmony_ci int i, j; 29698c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = mvm->hw; 29708c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 29718c2ecf20Sopenharmony_ci struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; 29728c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 29738c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; 29748c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 29758c2ecf20Sopenharmony_ci unsigned long supp; /* must be unsigned long for for_each_set_bit */ 29768c2ecf20Sopenharmony_ci 29778c2ecf20Sopenharmony_ci lockdep_assert_held(&mvmsta->lq_sta.rs_drv.pers.lock); 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_ci /* clear all non-persistent lq data */ 29808c2ecf20Sopenharmony_ci memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci sband = hw->wiphy->bands[band]; 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci lq_sta->lq.sta_id = mvmsta->sta_id; 29858c2ecf20Sopenharmony_ci mvmsta->amsdu_enabled = 0; 29868c2ecf20Sopenharmony_ci mvmsta->max_amsdu_len = sta->max_amsdu_len; 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci for (j = 0; j < LQ_SIZE; j++) 29898c2ecf20Sopenharmony_ci rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]); 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci lq_sta->flush_timer = 0; 29928c2ecf20Sopenharmony_ci lq_sta->last_tx = jiffies; 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 29958c2ecf20Sopenharmony_ci "LQ: *** rate scale station global init for station %d ***\n", 29968c2ecf20Sopenharmony_ci mvmsta->sta_id); 29978c2ecf20Sopenharmony_ci /* TODO: what is a good starting rate for STA? About middle? Maybe not 29988c2ecf20Sopenharmony_ci * the lowest or the highest rate.. Could consider using RSSI from 29998c2ecf20Sopenharmony_ci * previous packets? Need to have IEEE 802.1X auth succeed immediately 30008c2ecf20Sopenharmony_ci * after assoc.. */ 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci lq_sta->missed_rate_counter = IWL_MVM_RS_MISSED_RATE_MAX; 30038c2ecf20Sopenharmony_ci lq_sta->band = sband->band; 30048c2ecf20Sopenharmony_ci /* 30058c2ecf20Sopenharmony_ci * active legacy rates as per supported rates bitmap 30068c2ecf20Sopenharmony_ci */ 30078c2ecf20Sopenharmony_ci supp = sta->supp_rates[sband->band]; 30088c2ecf20Sopenharmony_ci lq_sta->active_legacy_rate = 0; 30098c2ecf20Sopenharmony_ci for_each_set_bit(i, &supp, BITS_PER_LONG) 30108c2ecf20Sopenharmony_ci lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci /* TODO: should probably account for rx_highest for both HT/VHT */ 30138c2ecf20Sopenharmony_ci if (!vht_cap || !vht_cap->vht_supported) 30148c2ecf20Sopenharmony_ci rs_ht_init(mvm, sta, lq_sta, ht_cap); 30158c2ecf20Sopenharmony_ci else 30168c2ecf20Sopenharmony_ci rs_vht_init(mvm, sta, lq_sta, vht_cap); 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_ci lq_sta->max_legacy_rate_idx = 30198c2ecf20Sopenharmony_ci rs_get_max_rate_from_mask(lq_sta->active_legacy_rate); 30208c2ecf20Sopenharmony_ci lq_sta->max_siso_rate_idx = 30218c2ecf20Sopenharmony_ci rs_get_max_rate_from_mask(lq_sta->active_siso_rate); 30228c2ecf20Sopenharmony_ci lq_sta->max_mimo2_rate_idx = 30238c2ecf20Sopenharmony_ci rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate); 30248c2ecf20Sopenharmony_ci 30258c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 30268c2ecf20Sopenharmony_ci "LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d BFER=%d\n", 30278c2ecf20Sopenharmony_ci lq_sta->active_legacy_rate, 30288c2ecf20Sopenharmony_ci lq_sta->active_siso_rate, 30298c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate, 30308c2ecf20Sopenharmony_ci lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc_capable, 30318c2ecf20Sopenharmony_ci lq_sta->bfer_capable); 30328c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n", 30338c2ecf20Sopenharmony_ci lq_sta->max_legacy_rate_idx, 30348c2ecf20Sopenharmony_ci lq_sta->max_siso_rate_idx, 30358c2ecf20Sopenharmony_ci lq_sta->max_mimo2_rate_idx); 30368c2ecf20Sopenharmony_ci 30378c2ecf20Sopenharmony_ci /* These values will be overridden later */ 30388c2ecf20Sopenharmony_ci lq_sta->lq.single_stream_ant_msk = 30398c2ecf20Sopenharmony_ci iwl_mvm_bt_coex_get_single_ant_msk(mvm, iwl_mvm_get_valid_tx_ant(mvm)); 30408c2ecf20Sopenharmony_ci lq_sta->lq.dual_stream_ant_msk = ANT_AB; 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci /* as default allow aggregation for all tids */ 30438c2ecf20Sopenharmony_ci lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; 30448c2ecf20Sopenharmony_ci lq_sta->is_agg = 0; 30458c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 30468c2ecf20Sopenharmony_ci iwl_mvm_reset_frame_stats(mvm); 30478c2ecf20Sopenharmony_ci#endif 30488c2ecf20Sopenharmony_ci rs_initialize_lq(mvm, sta, lq_sta, band); 30498c2ecf20Sopenharmony_ci} 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_cistatic void rs_drv_rate_update(void *mvm_r, 30528c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband, 30538c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef, 30548c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 30558c2ecf20Sopenharmony_ci void *priv_sta, u32 changed) 30568c2ecf20Sopenharmony_ci{ 30578c2ecf20Sopenharmony_ci struct iwl_op_mode *op_mode = mvm_r; 30588c2ecf20Sopenharmony_ci struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); 30598c2ecf20Sopenharmony_ci u8 tid; 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci if (!iwl_mvm_sta_from_mac80211(sta)->vif) 30628c2ecf20Sopenharmony_ci return; 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci /* Stop any ongoing aggregations as rs starts off assuming no agg */ 30658c2ecf20Sopenharmony_ci for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) 30668c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_session(sta, tid); 30678c2ecf20Sopenharmony_ci 30688c2ecf20Sopenharmony_ci iwl_mvm_rs_rate_init(mvm, sta, sband->band, true); 30698c2ecf20Sopenharmony_ci} 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_cistatic void __iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, 30728c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 30738c2ecf20Sopenharmony_ci int tid, struct ieee80211_tx_info *info, 30748c2ecf20Sopenharmony_ci bool ndp) 30758c2ecf20Sopenharmony_ci{ 30768c2ecf20Sopenharmony_ci int legacy_success; 30778c2ecf20Sopenharmony_ci int retries; 30788c2ecf20Sopenharmony_ci int i; 30798c2ecf20Sopenharmony_ci struct iwl_lq_cmd *table; 30808c2ecf20Sopenharmony_ci u32 lq_hwrate; 30818c2ecf20Sopenharmony_ci struct rs_rate lq_rate, tx_resp_rate; 30828c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; 30838c2ecf20Sopenharmony_ci u32 tlc_info = (uintptr_t)info->status.status_driver_data[0]; 30848c2ecf20Sopenharmony_ci u8 reduced_txp = tlc_info & RS_DRV_DATA_TXP_MSK; 30858c2ecf20Sopenharmony_ci u8 lq_color = RS_DRV_DATA_LQ_COLOR_GET(tlc_info); 30868c2ecf20Sopenharmony_ci u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1]; 30878c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 30888c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ci if (!lq_sta->pers.drv) { 30918c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); 30928c2ecf20Sopenharmony_ci return; 30938c2ecf20Sopenharmony_ci } 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci /* This packet was aggregated but doesn't carry status info */ 30968c2ecf20Sopenharmony_ci if ((info->flags & IEEE80211_TX_CTL_AMPDU) && 30978c2ecf20Sopenharmony_ci !(info->flags & IEEE80211_TX_STAT_AMPDU)) 30988c2ecf20Sopenharmony_ci return; 30998c2ecf20Sopenharmony_ci 31008c2ecf20Sopenharmony_ci if (rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, 31018c2ecf20Sopenharmony_ci &tx_resp_rate)) { 31028c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 31038c2ecf20Sopenharmony_ci return; 31048c2ecf20Sopenharmony_ci } 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 31078c2ecf20Sopenharmony_ci /* Disable last tx check if we are debugging with fixed rate but 31088c2ecf20Sopenharmony_ci * update tx stats 31098c2ecf20Sopenharmony_ci */ 31108c2ecf20Sopenharmony_ci if (lq_sta->pers.dbg_fixed_rate) { 31118c2ecf20Sopenharmony_ci int index = tx_resp_rate.index; 31128c2ecf20Sopenharmony_ci enum rs_column column; 31138c2ecf20Sopenharmony_ci int attempts, success; 31148c2ecf20Sopenharmony_ci 31158c2ecf20Sopenharmony_ci column = rs_get_column_from_rate(&tx_resp_rate); 31168c2ecf20Sopenharmony_ci if (WARN_ONCE(column == RS_COLUMN_INVALID, 31178c2ecf20Sopenharmony_ci "Can't map rate 0x%x to column", 31188c2ecf20Sopenharmony_ci tx_resp_hwrate)) 31198c2ecf20Sopenharmony_ci return; 31208c2ecf20Sopenharmony_ci 31218c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_STAT_AMPDU) { 31228c2ecf20Sopenharmony_ci attempts = info->status.ampdu_len; 31238c2ecf20Sopenharmony_ci success = info->status.ampdu_ack_len; 31248c2ecf20Sopenharmony_ci } else { 31258c2ecf20Sopenharmony_ci attempts = info->status.rates[0].count; 31268c2ecf20Sopenharmony_ci success = !!(info->flags & IEEE80211_TX_STAT_ACK); 31278c2ecf20Sopenharmony_ci } 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci lq_sta->pers.tx_stats[column][index].total += attempts; 31308c2ecf20Sopenharmony_ci lq_sta->pers.tx_stats[column][index].success += success; 31318c2ecf20Sopenharmony_ci 31328c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n", 31338c2ecf20Sopenharmony_ci tx_resp_hwrate, success, attempts); 31348c2ecf20Sopenharmony_ci return; 31358c2ecf20Sopenharmony_ci } 31368c2ecf20Sopenharmony_ci#endif 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ci if (time_after(jiffies, 31398c2ecf20Sopenharmony_ci (unsigned long)(lq_sta->last_tx + 31408c2ecf20Sopenharmony_ci (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { 31418c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); 31428c2ecf20Sopenharmony_ci /* reach here only in case of driver RS, call directly 31438c2ecf20Sopenharmony_ci * the unlocked version 31448c2ecf20Sopenharmony_ci */ 31458c2ecf20Sopenharmony_ci rs_drv_rate_init(mvm, sta, info->band); 31468c2ecf20Sopenharmony_ci return; 31478c2ecf20Sopenharmony_ci } 31488c2ecf20Sopenharmony_ci lq_sta->last_tx = jiffies; 31498c2ecf20Sopenharmony_ci 31508c2ecf20Sopenharmony_ci /* Ignore this Tx frame response if its initial rate doesn't match 31518c2ecf20Sopenharmony_ci * that of latest Link Quality command. There may be stragglers 31528c2ecf20Sopenharmony_ci * from a previous Link Quality command, but we're no longer interested 31538c2ecf20Sopenharmony_ci * in those; they're either from the "active" mode while we're trying 31548c2ecf20Sopenharmony_ci * to check "search" mode, or a prior "search" mode after we've moved 31558c2ecf20Sopenharmony_ci * to a new "search" mode (which might become the new "active" mode). 31568c2ecf20Sopenharmony_ci */ 31578c2ecf20Sopenharmony_ci table = &lq_sta->lq; 31588c2ecf20Sopenharmony_ci lq_hwrate = le32_to_cpu(table->rs_table[0]); 31598c2ecf20Sopenharmony_ci if (rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate)) { 31608c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 31618c2ecf20Sopenharmony_ci return; 31628c2ecf20Sopenharmony_ci } 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci /* Here we actually compare this rate to the latest LQ command */ 31658c2ecf20Sopenharmony_ci if (lq_color != LQ_FLAG_COLOR_GET(table->flags)) { 31668c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 31678c2ecf20Sopenharmony_ci "tx resp color 0x%x does not match 0x%x\n", 31688c2ecf20Sopenharmony_ci lq_color, LQ_FLAG_COLOR_GET(table->flags)); 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_ci /* Since rates mis-match, the last LQ command may have failed. 31718c2ecf20Sopenharmony_ci * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with 31728c2ecf20Sopenharmony_ci * ... driver. 31738c2ecf20Sopenharmony_ci */ 31748c2ecf20Sopenharmony_ci lq_sta->missed_rate_counter++; 31758c2ecf20Sopenharmony_ci if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) { 31768c2ecf20Sopenharmony_ci lq_sta->missed_rate_counter = 0; 31778c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 31788c2ecf20Sopenharmony_ci "Too many rates mismatch. Send sync LQ. rs_state %d\n", 31798c2ecf20Sopenharmony_ci lq_sta->rs_state); 31808c2ecf20Sopenharmony_ci iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq); 31818c2ecf20Sopenharmony_ci } 31828c2ecf20Sopenharmony_ci /* Regardless, ignore this status info for outdated rate */ 31838c2ecf20Sopenharmony_ci return; 31848c2ecf20Sopenharmony_ci } 31858c2ecf20Sopenharmony_ci 31868c2ecf20Sopenharmony_ci /* Rate did match, so reset the missed_rate_counter */ 31878c2ecf20Sopenharmony_ci lq_sta->missed_rate_counter = 0; 31888c2ecf20Sopenharmony_ci 31898c2ecf20Sopenharmony_ci if (!lq_sta->search_better_tbl) { 31908c2ecf20Sopenharmony_ci curr_tbl = &lq_sta->lq_info[lq_sta->active_tbl]; 31918c2ecf20Sopenharmony_ci other_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; 31928c2ecf20Sopenharmony_ci } else { 31938c2ecf20Sopenharmony_ci curr_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; 31948c2ecf20Sopenharmony_ci other_tbl = &lq_sta->lq_info[lq_sta->active_tbl]; 31958c2ecf20Sopenharmony_ci } 31968c2ecf20Sopenharmony_ci 31978c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) { 31988c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 31998c2ecf20Sopenharmony_ci "Neither active nor search matches tx rate\n"); 32008c2ecf20Sopenharmony_ci tmp_tbl = &lq_sta->lq_info[lq_sta->active_tbl]; 32018c2ecf20Sopenharmony_ci rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE"); 32028c2ecf20Sopenharmony_ci tmp_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; 32038c2ecf20Sopenharmony_ci rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH"); 32048c2ecf20Sopenharmony_ci rs_dump_rate(mvm, &lq_rate, "ACTUAL"); 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci /* no matching table found, let's by-pass the data collection 32078c2ecf20Sopenharmony_ci * and continue to perform rate scale to find the rate table 32088c2ecf20Sopenharmony_ci */ 32098c2ecf20Sopenharmony_ci rs_stay_in_table(lq_sta, true); 32108c2ecf20Sopenharmony_ci goto done; 32118c2ecf20Sopenharmony_ci } 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci /* Updating the frame history depends on whether packets were 32148c2ecf20Sopenharmony_ci * aggregated. 32158c2ecf20Sopenharmony_ci * 32168c2ecf20Sopenharmony_ci * For aggregation, all packets were transmitted at the same rate, the 32178c2ecf20Sopenharmony_ci * first index into rate scale table. 32188c2ecf20Sopenharmony_ci */ 32198c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_STAT_AMPDU) { 32208c2ecf20Sopenharmony_ci rs_collect_tpc_data(mvm, lq_sta, curr_tbl, tx_resp_rate.index, 32218c2ecf20Sopenharmony_ci info->status.ampdu_len, 32228c2ecf20Sopenharmony_ci info->status.ampdu_ack_len, 32238c2ecf20Sopenharmony_ci reduced_txp); 32248c2ecf20Sopenharmony_ci 32258c2ecf20Sopenharmony_ci /* ampdu_ack_len = 0 marks no BA was received. For TLC, treat 32268c2ecf20Sopenharmony_ci * it as a single frame loss as we don't want the success ratio 32278c2ecf20Sopenharmony_ci * to dip too quickly because a BA wasn't received. 32288c2ecf20Sopenharmony_ci * For TPC, there's no need for this optimisation since we want 32298c2ecf20Sopenharmony_ci * to recover very quickly from a bad power reduction and, 32308c2ecf20Sopenharmony_ci * therefore we'd like the success ratio to get an immediate hit 32318c2ecf20Sopenharmony_ci * when failing to get a BA, so we'd switch back to a lower or 32328c2ecf20Sopenharmony_ci * zero power reduction. When FW transmits agg with a rate 32338c2ecf20Sopenharmony_ci * different from the initial rate, it will not use reduced txp 32348c2ecf20Sopenharmony_ci * and will send BA notification twice (one empty with reduced 32358c2ecf20Sopenharmony_ci * txp equal to the value from LQ and one with reduced txp 0). 32368c2ecf20Sopenharmony_ci * We need to update counters for each txp level accordingly. 32378c2ecf20Sopenharmony_ci */ 32388c2ecf20Sopenharmony_ci if (info->status.ampdu_ack_len == 0) 32398c2ecf20Sopenharmony_ci info->status.ampdu_len = 1; 32408c2ecf20Sopenharmony_ci 32418c2ecf20Sopenharmony_ci rs_collect_tlc_data(mvm, mvmsta, tid, curr_tbl, 32428c2ecf20Sopenharmony_ci tx_resp_rate.index, 32438c2ecf20Sopenharmony_ci info->status.ampdu_len, 32448c2ecf20Sopenharmony_ci info->status.ampdu_ack_len); 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_ci /* Update success/fail counts if not searching for new mode */ 32478c2ecf20Sopenharmony_ci if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { 32488c2ecf20Sopenharmony_ci lq_sta->total_success += info->status.ampdu_ack_len; 32498c2ecf20Sopenharmony_ci lq_sta->total_failed += (info->status.ampdu_len - 32508c2ecf20Sopenharmony_ci info->status.ampdu_ack_len); 32518c2ecf20Sopenharmony_ci } 32528c2ecf20Sopenharmony_ci } else { 32538c2ecf20Sopenharmony_ci /* For legacy, update frame history with for each Tx retry. */ 32548c2ecf20Sopenharmony_ci retries = info->status.rates[0].count - 1; 32558c2ecf20Sopenharmony_ci /* HW doesn't send more than 15 retries */ 32568c2ecf20Sopenharmony_ci retries = min(retries, 15); 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci /* The last transmission may have been successful */ 32598c2ecf20Sopenharmony_ci legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); 32608c2ecf20Sopenharmony_ci /* Collect data for each rate used during failed TX attempts */ 32618c2ecf20Sopenharmony_ci for (i = 0; i <= retries; ++i) { 32628c2ecf20Sopenharmony_ci lq_hwrate = le32_to_cpu(table->rs_table[i]); 32638c2ecf20Sopenharmony_ci if (rs_rate_from_ucode_rate(lq_hwrate, info->band, 32648c2ecf20Sopenharmony_ci &lq_rate)) { 32658c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 32668c2ecf20Sopenharmony_ci return; 32678c2ecf20Sopenharmony_ci } 32688c2ecf20Sopenharmony_ci 32698c2ecf20Sopenharmony_ci /* Only collect stats if retried rate is in the same RS 32708c2ecf20Sopenharmony_ci * table as active/search. 32718c2ecf20Sopenharmony_ci */ 32728c2ecf20Sopenharmony_ci if (rs_rate_column_match(&lq_rate, &curr_tbl->rate)) 32738c2ecf20Sopenharmony_ci tmp_tbl = curr_tbl; 32748c2ecf20Sopenharmony_ci else if (rs_rate_column_match(&lq_rate, 32758c2ecf20Sopenharmony_ci &other_tbl->rate)) 32768c2ecf20Sopenharmony_ci tmp_tbl = other_tbl; 32778c2ecf20Sopenharmony_ci else 32788c2ecf20Sopenharmony_ci continue; 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_ci rs_collect_tpc_data(mvm, lq_sta, tmp_tbl, 32818c2ecf20Sopenharmony_ci tx_resp_rate.index, 1, 32828c2ecf20Sopenharmony_ci i < retries ? 0 : legacy_success, 32838c2ecf20Sopenharmony_ci reduced_txp); 32848c2ecf20Sopenharmony_ci rs_collect_tlc_data(mvm, mvmsta, tid, tmp_tbl, 32858c2ecf20Sopenharmony_ci tx_resp_rate.index, 1, 32868c2ecf20Sopenharmony_ci i < retries ? 0 : legacy_success); 32878c2ecf20Sopenharmony_ci } 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_ci /* Update success/fail counts if not searching for new mode */ 32908c2ecf20Sopenharmony_ci if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { 32918c2ecf20Sopenharmony_ci lq_sta->total_success += legacy_success; 32928c2ecf20Sopenharmony_ci lq_sta->total_failed += retries + (1 - legacy_success); 32938c2ecf20Sopenharmony_ci } 32948c2ecf20Sopenharmony_ci } 32958c2ecf20Sopenharmony_ci /* The last TX rate is cached in lq_sta; it's set in if/else above */ 32968c2ecf20Sopenharmony_ci lq_sta->last_rate_n_flags = lq_hwrate; 32978c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp); 32988c2ecf20Sopenharmony_cidone: 32998c2ecf20Sopenharmony_ci /* See if there's a better rate or modulation mode to try. */ 33008c2ecf20Sopenharmony_ci if (sta->supp_rates[info->band]) 33018c2ecf20Sopenharmony_ci rs_rate_scale_perform(mvm, sta, lq_sta, tid, ndp); 33028c2ecf20Sopenharmony_ci} 33038c2ecf20Sopenharmony_ci 33048c2ecf20Sopenharmony_civoid iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 33058c2ecf20Sopenharmony_ci int tid, struct ieee80211_tx_info *info, bool ndp) 33068c2ecf20Sopenharmony_ci{ 33078c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 33088c2ecf20Sopenharmony_ci 33098c2ecf20Sopenharmony_ci /* If it's locked we are in middle of init flow 33108c2ecf20Sopenharmony_ci * just wait for next tx status to update the lq_sta data 33118c2ecf20Sopenharmony_ci */ 33128c2ecf20Sopenharmony_ci if (!spin_trylock(&mvmsta->lq_sta.rs_drv.pers.lock)) 33138c2ecf20Sopenharmony_ci return; 33148c2ecf20Sopenharmony_ci 33158c2ecf20Sopenharmony_ci __iwl_mvm_rs_tx_status(mvm, sta, tid, info, ndp); 33168c2ecf20Sopenharmony_ci spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock); 33178c2ecf20Sopenharmony_ci} 33188c2ecf20Sopenharmony_ci 33198c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 33208c2ecf20Sopenharmony_cistatic void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, 33218c2ecf20Sopenharmony_ci struct iwl_lq_cmd *lq_cmd, 33228c2ecf20Sopenharmony_ci enum nl80211_band band, 33238c2ecf20Sopenharmony_ci u32 ucode_rate) 33248c2ecf20Sopenharmony_ci{ 33258c2ecf20Sopenharmony_ci struct rs_rate rate; 33268c2ecf20Sopenharmony_ci int i; 33278c2ecf20Sopenharmony_ci int num_rates = ARRAY_SIZE(lq_cmd->rs_table); 33288c2ecf20Sopenharmony_ci __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate); 33298c2ecf20Sopenharmony_ci u8 ant = (ucode_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_ci for (i = 0; i < num_rates; i++) 33328c2ecf20Sopenharmony_ci lq_cmd->rs_table[i] = ucode_rate_le32; 33338c2ecf20Sopenharmony_ci 33348c2ecf20Sopenharmony_ci if (rs_rate_from_ucode_rate(ucode_rate, band, &rate)) { 33358c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 33368c2ecf20Sopenharmony_ci return; 33378c2ecf20Sopenharmony_ci } 33388c2ecf20Sopenharmony_ci 33398c2ecf20Sopenharmony_ci if (is_mimo(&rate)) 33408c2ecf20Sopenharmony_ci lq_cmd->mimo_delim = num_rates - 1; 33418c2ecf20Sopenharmony_ci else 33428c2ecf20Sopenharmony_ci lq_cmd->mimo_delim = 0; 33438c2ecf20Sopenharmony_ci 33448c2ecf20Sopenharmony_ci lq_cmd->reduced_tpc = 0; 33458c2ecf20Sopenharmony_ci 33468c2ecf20Sopenharmony_ci if (num_of_ant(ant) == 1) 33478c2ecf20Sopenharmony_ci lq_cmd->single_stream_ant_msk = ant; 33488c2ecf20Sopenharmony_ci 33498c2ecf20Sopenharmony_ci if (!mvm->trans->trans_cfg->gen2) 33508c2ecf20Sopenharmony_ci lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; 33518c2ecf20Sopenharmony_ci else 33528c2ecf20Sopenharmony_ci lq_cmd->agg_frame_cnt_limit = 33538c2ecf20Sopenharmony_ci LINK_QUAL_AGG_FRAME_LIMIT_GEN2_DEF; 33548c2ecf20Sopenharmony_ci} 33558c2ecf20Sopenharmony_ci#endif /* CONFIG_MAC80211_DEBUGFS */ 33568c2ecf20Sopenharmony_ci 33578c2ecf20Sopenharmony_cistatic void rs_fill_rates_for_column(struct iwl_mvm *mvm, 33588c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 33598c2ecf20Sopenharmony_ci struct rs_rate *rate, 33608c2ecf20Sopenharmony_ci __le32 *rs_table, int *rs_table_index, 33618c2ecf20Sopenharmony_ci int num_rates, int num_retries, 33628c2ecf20Sopenharmony_ci u8 valid_tx_ant, bool toggle_ant) 33638c2ecf20Sopenharmony_ci{ 33648c2ecf20Sopenharmony_ci int i, j; 33658c2ecf20Sopenharmony_ci __le32 ucode_rate; 33668c2ecf20Sopenharmony_ci bool bottom_reached = false; 33678c2ecf20Sopenharmony_ci int prev_rate_idx = rate->index; 33688c2ecf20Sopenharmony_ci int end = LINK_QUAL_MAX_RETRY_NUM; 33698c2ecf20Sopenharmony_ci int index = *rs_table_index; 33708c2ecf20Sopenharmony_ci 33718c2ecf20Sopenharmony_ci for (i = 0; i < num_rates && index < end; i++) { 33728c2ecf20Sopenharmony_ci for (j = 0; j < num_retries && index < end; j++, index++) { 33738c2ecf20Sopenharmony_ci ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, 33748c2ecf20Sopenharmony_ci rate)); 33758c2ecf20Sopenharmony_ci rs_table[index] = ucode_rate; 33768c2ecf20Sopenharmony_ci if (toggle_ant) 33778c2ecf20Sopenharmony_ci rs_toggle_antenna(valid_tx_ant, rate); 33788c2ecf20Sopenharmony_ci } 33798c2ecf20Sopenharmony_ci 33808c2ecf20Sopenharmony_ci prev_rate_idx = rate->index; 33818c2ecf20Sopenharmony_ci bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate); 33828c2ecf20Sopenharmony_ci if (bottom_reached && !is_legacy(rate)) 33838c2ecf20Sopenharmony_ci break; 33848c2ecf20Sopenharmony_ci } 33858c2ecf20Sopenharmony_ci 33868c2ecf20Sopenharmony_ci if (!bottom_reached && !is_legacy(rate)) 33878c2ecf20Sopenharmony_ci rate->index = prev_rate_idx; 33888c2ecf20Sopenharmony_ci 33898c2ecf20Sopenharmony_ci *rs_table_index = index; 33908c2ecf20Sopenharmony_ci} 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI 33938c2ecf20Sopenharmony_ci * column the rate table should look like this: 33948c2ecf20Sopenharmony_ci * 33958c2ecf20Sopenharmony_ci * rate[0] 0x400F019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI 33968c2ecf20Sopenharmony_ci * rate[1] 0x400F019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI 33978c2ecf20Sopenharmony_ci * rate[2] 0x400F018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI 33988c2ecf20Sopenharmony_ci * rate[3] 0x400F018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI 33998c2ecf20Sopenharmony_ci * rate[4] 0x400F017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI 34008c2ecf20Sopenharmony_ci * rate[5] 0x400F017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI 34018c2ecf20Sopenharmony_ci * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI 34028c2ecf20Sopenharmony_ci * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI 34038c2ecf20Sopenharmony_ci * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI 34048c2ecf20Sopenharmony_ci * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps 34058c2ecf20Sopenharmony_ci * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps 34068c2ecf20Sopenharmony_ci * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps 34078c2ecf20Sopenharmony_ci * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps 34088c2ecf20Sopenharmony_ci * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps 34098c2ecf20Sopenharmony_ci * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps 34108c2ecf20Sopenharmony_ci * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps 34118c2ecf20Sopenharmony_ci */ 34128c2ecf20Sopenharmony_cistatic void rs_build_rates_table(struct iwl_mvm *mvm, 34138c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 34148c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 34158c2ecf20Sopenharmony_ci const struct rs_rate *initial_rate) 34168c2ecf20Sopenharmony_ci{ 34178c2ecf20Sopenharmony_ci struct rs_rate rate; 34188c2ecf20Sopenharmony_ci int num_rates, num_retries, index = 0; 34198c2ecf20Sopenharmony_ci u8 valid_tx_ant = 0; 34208c2ecf20Sopenharmony_ci struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; 34218c2ecf20Sopenharmony_ci bool toggle_ant = false; 34228c2ecf20Sopenharmony_ci u32 color; 34238c2ecf20Sopenharmony_ci 34248c2ecf20Sopenharmony_ci memcpy(&rate, initial_rate, sizeof(rate)); 34258c2ecf20Sopenharmony_ci 34268c2ecf20Sopenharmony_ci valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_ci /* TODO: remove old API when min FW API hits 14 */ 34298c2ecf20Sopenharmony_ci if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS) && 34308c2ecf20Sopenharmony_ci rs_stbc_allow(mvm, sta, lq_sta)) 34318c2ecf20Sopenharmony_ci rate.stbc = true; 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_ci if (is_siso(&rate)) { 34348c2ecf20Sopenharmony_ci num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES; 34358c2ecf20Sopenharmony_ci num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; 34368c2ecf20Sopenharmony_ci } else if (is_mimo(&rate)) { 34378c2ecf20Sopenharmony_ci num_rates = IWL_MVM_RS_INITIAL_MIMO_NUM_RATES; 34388c2ecf20Sopenharmony_ci num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; 34398c2ecf20Sopenharmony_ci } else { 34408c2ecf20Sopenharmony_ci num_rates = IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES; 34418c2ecf20Sopenharmony_ci num_retries = IWL_MVM_RS_INITIAL_LEGACY_RETRIES; 34428c2ecf20Sopenharmony_ci toggle_ant = true; 34438c2ecf20Sopenharmony_ci } 34448c2ecf20Sopenharmony_ci 34458c2ecf20Sopenharmony_ci rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, 34468c2ecf20Sopenharmony_ci num_rates, num_retries, valid_tx_ant, 34478c2ecf20Sopenharmony_ci toggle_ant); 34488c2ecf20Sopenharmony_ci 34498c2ecf20Sopenharmony_ci rs_get_lower_rate_down_column(lq_sta, &rate); 34508c2ecf20Sopenharmony_ci 34518c2ecf20Sopenharmony_ci if (is_siso(&rate)) { 34528c2ecf20Sopenharmony_ci num_rates = IWL_MVM_RS_SECONDARY_SISO_NUM_RATES; 34538c2ecf20Sopenharmony_ci num_retries = IWL_MVM_RS_SECONDARY_SISO_RETRIES; 34548c2ecf20Sopenharmony_ci lq_cmd->mimo_delim = index; 34558c2ecf20Sopenharmony_ci } else if (is_legacy(&rate)) { 34568c2ecf20Sopenharmony_ci num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; 34578c2ecf20Sopenharmony_ci num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; 34588c2ecf20Sopenharmony_ci } else { 34598c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 34608c2ecf20Sopenharmony_ci } 34618c2ecf20Sopenharmony_ci 34628c2ecf20Sopenharmony_ci toggle_ant = true; 34638c2ecf20Sopenharmony_ci 34648c2ecf20Sopenharmony_ci rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, 34658c2ecf20Sopenharmony_ci num_rates, num_retries, valid_tx_ant, 34668c2ecf20Sopenharmony_ci toggle_ant); 34678c2ecf20Sopenharmony_ci 34688c2ecf20Sopenharmony_ci rs_get_lower_rate_down_column(lq_sta, &rate); 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_ci num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; 34718c2ecf20Sopenharmony_ci num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, 34748c2ecf20Sopenharmony_ci num_rates, num_retries, valid_tx_ant, 34758c2ecf20Sopenharmony_ci toggle_ant); 34768c2ecf20Sopenharmony_ci 34778c2ecf20Sopenharmony_ci /* update the color of the LQ command (as a counter at bits 1-3) */ 34788c2ecf20Sopenharmony_ci color = LQ_FLAGS_COLOR_INC(LQ_FLAG_COLOR_GET(lq_cmd->flags)); 34798c2ecf20Sopenharmony_ci lq_cmd->flags = LQ_FLAG_COLOR_SET(lq_cmd->flags, color); 34808c2ecf20Sopenharmony_ci} 34818c2ecf20Sopenharmony_ci 34828c2ecf20Sopenharmony_cistruct rs_bfer_active_iter_data { 34838c2ecf20Sopenharmony_ci struct ieee80211_sta *exclude_sta; 34848c2ecf20Sopenharmony_ci struct iwl_mvm_sta *bfer_mvmsta; 34858c2ecf20Sopenharmony_ci}; 34868c2ecf20Sopenharmony_ci 34878c2ecf20Sopenharmony_cistatic void rs_bfer_active_iter(void *_data, 34888c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 34898c2ecf20Sopenharmony_ci{ 34908c2ecf20Sopenharmony_ci struct rs_bfer_active_iter_data *data = _data; 34918c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 34928c2ecf20Sopenharmony_ci struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.rs_drv.lq; 34938c2ecf20Sopenharmony_ci u32 ss_params = le32_to_cpu(lq_cmd->ss_params); 34948c2ecf20Sopenharmony_ci 34958c2ecf20Sopenharmony_ci if (sta == data->exclude_sta) 34968c2ecf20Sopenharmony_ci return; 34978c2ecf20Sopenharmony_ci 34988c2ecf20Sopenharmony_ci /* The current sta has BFER allowed */ 34998c2ecf20Sopenharmony_ci if (ss_params & LQ_SS_BFER_ALLOWED) { 35008c2ecf20Sopenharmony_ci WARN_ON_ONCE(data->bfer_mvmsta != NULL); 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_ci data->bfer_mvmsta = mvmsta; 35038c2ecf20Sopenharmony_ci } 35048c2ecf20Sopenharmony_ci} 35058c2ecf20Sopenharmony_ci 35068c2ecf20Sopenharmony_cistatic int rs_bfer_priority(struct iwl_mvm_sta *sta) 35078c2ecf20Sopenharmony_ci{ 35088c2ecf20Sopenharmony_ci int prio = -1; 35098c2ecf20Sopenharmony_ci enum nl80211_iftype viftype = ieee80211_vif_type_p2p(sta->vif); 35108c2ecf20Sopenharmony_ci 35118c2ecf20Sopenharmony_ci switch (viftype) { 35128c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 35138c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 35148c2ecf20Sopenharmony_ci prio = 3; 35158c2ecf20Sopenharmony_ci break; 35168c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 35178c2ecf20Sopenharmony_ci prio = 2; 35188c2ecf20Sopenharmony_ci break; 35198c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 35208c2ecf20Sopenharmony_ci prio = 1; 35218c2ecf20Sopenharmony_ci break; 35228c2ecf20Sopenharmony_ci default: 35238c2ecf20Sopenharmony_ci WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id); 35248c2ecf20Sopenharmony_ci prio = -1; 35258c2ecf20Sopenharmony_ci } 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_ci return prio; 35288c2ecf20Sopenharmony_ci} 35298c2ecf20Sopenharmony_ci 35308c2ecf20Sopenharmony_ci/* Returns >0 if sta1 has a higher BFER priority compared to sta2 */ 35318c2ecf20Sopenharmony_cistatic int rs_bfer_priority_cmp(struct iwl_mvm_sta *sta1, 35328c2ecf20Sopenharmony_ci struct iwl_mvm_sta *sta2) 35338c2ecf20Sopenharmony_ci{ 35348c2ecf20Sopenharmony_ci int prio1 = rs_bfer_priority(sta1); 35358c2ecf20Sopenharmony_ci int prio2 = rs_bfer_priority(sta2); 35368c2ecf20Sopenharmony_ci 35378c2ecf20Sopenharmony_ci if (prio1 > prio2) 35388c2ecf20Sopenharmony_ci return 1; 35398c2ecf20Sopenharmony_ci if (prio1 < prio2) 35408c2ecf20Sopenharmony_ci return -1; 35418c2ecf20Sopenharmony_ci return 0; 35428c2ecf20Sopenharmony_ci} 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_cistatic void rs_set_lq_ss_params(struct iwl_mvm *mvm, 35458c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 35468c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 35478c2ecf20Sopenharmony_ci const struct rs_rate *initial_rate) 35488c2ecf20Sopenharmony_ci{ 35498c2ecf20Sopenharmony_ci struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; 35508c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 35518c2ecf20Sopenharmony_ci struct rs_bfer_active_iter_data data = { 35528c2ecf20Sopenharmony_ci .exclude_sta = sta, 35538c2ecf20Sopenharmony_ci .bfer_mvmsta = NULL, 35548c2ecf20Sopenharmony_ci }; 35558c2ecf20Sopenharmony_ci struct iwl_mvm_sta *bfer_mvmsta = NULL; 35568c2ecf20Sopenharmony_ci u32 ss_params = LQ_SS_PARAMS_VALID; 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_ci if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) 35598c2ecf20Sopenharmony_ci goto out; 35608c2ecf20Sopenharmony_ci 35618c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 35628c2ecf20Sopenharmony_ci /* Check if forcing the decision is configured. 35638c2ecf20Sopenharmony_ci * Note that SISO is forced by not allowing STBC or BFER 35648c2ecf20Sopenharmony_ci */ 35658c2ecf20Sopenharmony_ci if (lq_sta->pers.ss_force == RS_SS_FORCE_STBC) 35668c2ecf20Sopenharmony_ci ss_params |= (LQ_SS_STBC_1SS_ALLOWED | LQ_SS_FORCE); 35678c2ecf20Sopenharmony_ci else if (lq_sta->pers.ss_force == RS_SS_FORCE_BFER) 35688c2ecf20Sopenharmony_ci ss_params |= (LQ_SS_BFER_ALLOWED | LQ_SS_FORCE); 35698c2ecf20Sopenharmony_ci 35708c2ecf20Sopenharmony_ci if (lq_sta->pers.ss_force != RS_SS_FORCE_NONE) { 35718c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Forcing single stream Tx decision %d\n", 35728c2ecf20Sopenharmony_ci lq_sta->pers.ss_force); 35738c2ecf20Sopenharmony_ci goto out; 35748c2ecf20Sopenharmony_ci } 35758c2ecf20Sopenharmony_ci#endif 35768c2ecf20Sopenharmony_ci 35778c2ecf20Sopenharmony_ci if (lq_sta->stbc_capable) 35788c2ecf20Sopenharmony_ci ss_params |= LQ_SS_STBC_1SS_ALLOWED; 35798c2ecf20Sopenharmony_ci 35808c2ecf20Sopenharmony_ci if (!lq_sta->bfer_capable) 35818c2ecf20Sopenharmony_ci goto out; 35828c2ecf20Sopenharmony_ci 35838c2ecf20Sopenharmony_ci ieee80211_iterate_stations_atomic(mvm->hw, 35848c2ecf20Sopenharmony_ci rs_bfer_active_iter, 35858c2ecf20Sopenharmony_ci &data); 35868c2ecf20Sopenharmony_ci bfer_mvmsta = data.bfer_mvmsta; 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci /* This code is safe as it doesn't run concurrently for different 35898c2ecf20Sopenharmony_ci * stations. This is guaranteed by the fact that calls to 35908c2ecf20Sopenharmony_ci * ieee80211_tx_status wouldn't run concurrently for a single HW. 35918c2ecf20Sopenharmony_ci */ 35928c2ecf20Sopenharmony_ci if (!bfer_mvmsta) { 35938c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n"); 35948c2ecf20Sopenharmony_ci 35958c2ecf20Sopenharmony_ci ss_params |= LQ_SS_BFER_ALLOWED; 35968c2ecf20Sopenharmony_ci goto out; 35978c2ecf20Sopenharmony_ci } 35988c2ecf20Sopenharmony_ci 35998c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n", 36008c2ecf20Sopenharmony_ci bfer_mvmsta->sta_id); 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_ci /* Disallow BFER on another STA if active and we're a higher priority */ 36038c2ecf20Sopenharmony_ci if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) { 36048c2ecf20Sopenharmony_ci struct iwl_lq_cmd *bfersta_lq_cmd = 36058c2ecf20Sopenharmony_ci &bfer_mvmsta->lq_sta.rs_drv.lq; 36068c2ecf20Sopenharmony_ci u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params); 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED; 36098c2ecf20Sopenharmony_ci bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params); 36108c2ecf20Sopenharmony_ci iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd); 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci ss_params |= LQ_SS_BFER_ALLOWED; 36138c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, 36148c2ecf20Sopenharmony_ci "Lower priority BFER sta found (%d). Switch BFER\n", 36158c2ecf20Sopenharmony_ci bfer_mvmsta->sta_id); 36168c2ecf20Sopenharmony_ci } 36178c2ecf20Sopenharmony_ciout: 36188c2ecf20Sopenharmony_ci lq_cmd->ss_params = cpu_to_le32(ss_params); 36198c2ecf20Sopenharmony_ci} 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_cistatic void rs_fill_lq_cmd(struct iwl_mvm *mvm, 36228c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 36238c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 36248c2ecf20Sopenharmony_ci const struct rs_rate *initial_rate) 36258c2ecf20Sopenharmony_ci{ 36268c2ecf20Sopenharmony_ci struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; 36278c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 36288c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif; 36298c2ecf20Sopenharmony_ci 36308c2ecf20Sopenharmony_ci lq_cmd->agg_disable_start_th = IWL_MVM_RS_AGG_DISABLE_START; 36318c2ecf20Sopenharmony_ci lq_cmd->agg_time_limit = 36328c2ecf20Sopenharmony_ci cpu_to_le16(IWL_MVM_RS_AGG_TIME_LIMIT); 36338c2ecf20Sopenharmony_ci 36348c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 36358c2ecf20Sopenharmony_ci if (lq_sta->pers.dbg_fixed_rate) { 36368c2ecf20Sopenharmony_ci rs_build_rates_table_from_fixed(mvm, lq_cmd, 36378c2ecf20Sopenharmony_ci lq_sta->band, 36388c2ecf20Sopenharmony_ci lq_sta->pers.dbg_fixed_rate); 36398c2ecf20Sopenharmony_ci return; 36408c2ecf20Sopenharmony_ci } 36418c2ecf20Sopenharmony_ci#endif 36428c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!sta || !initial_rate)) 36438c2ecf20Sopenharmony_ci return; 36448c2ecf20Sopenharmony_ci 36458c2ecf20Sopenharmony_ci rs_build_rates_table(mvm, sta, lq_sta, initial_rate); 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_ci if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS)) 36488c2ecf20Sopenharmony_ci rs_set_lq_ss_params(mvm, sta, lq_sta, initial_rate); 36498c2ecf20Sopenharmony_ci 36508c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 36518c2ecf20Sopenharmony_ci mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); 36528c2ecf20Sopenharmony_ci 36538c2ecf20Sopenharmony_ci if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2) && 36548c2ecf20Sopenharmony_ci num_of_ant(initial_rate->ant) == 1) 36558c2ecf20Sopenharmony_ci lq_cmd->single_stream_ant_msk = initial_rate->ant; 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_ci lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize; 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci /* 36608c2ecf20Sopenharmony_ci * In case of low latency, tell the firmware to leave a frame in the 36618c2ecf20Sopenharmony_ci * Tx Fifo so that it can start a transaction in the same TxOP. This 36628c2ecf20Sopenharmony_ci * basically allows the firmware to send bursts. 36638c2ecf20Sopenharmony_ci */ 36648c2ecf20Sopenharmony_ci if (iwl_mvm_vif_low_latency(mvmvif)) 36658c2ecf20Sopenharmony_ci lq_cmd->agg_frame_cnt_limit--; 36668c2ecf20Sopenharmony_ci 36678c2ecf20Sopenharmony_ci if (mvmsta->vif->p2p) 36688c2ecf20Sopenharmony_ci lq_cmd->flags |= LQ_FLAG_USE_RTS_MSK; 36698c2ecf20Sopenharmony_ci 36708c2ecf20Sopenharmony_ci lq_cmd->agg_time_limit = 36718c2ecf20Sopenharmony_ci cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta)); 36728c2ecf20Sopenharmony_ci} 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_cistatic void *rs_alloc(struct ieee80211_hw *hw) 36758c2ecf20Sopenharmony_ci{ 36768c2ecf20Sopenharmony_ci return hw->priv; 36778c2ecf20Sopenharmony_ci} 36788c2ecf20Sopenharmony_ci 36798c2ecf20Sopenharmony_ci/* rate scale requires free function to be implemented */ 36808c2ecf20Sopenharmony_cistatic void rs_free(void *mvm_rate) 36818c2ecf20Sopenharmony_ci{ 36828c2ecf20Sopenharmony_ci return; 36838c2ecf20Sopenharmony_ci} 36848c2ecf20Sopenharmony_ci 36858c2ecf20Sopenharmony_cistatic void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta) 36868c2ecf20Sopenharmony_ci{ 36878c2ecf20Sopenharmony_ci struct iwl_op_mode *op_mode __maybe_unused = mvm_r; 36888c2ecf20Sopenharmony_ci struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); 36898c2ecf20Sopenharmony_ci 36908c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "enter\n"); 36918c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "leave\n"); 36928c2ecf20Sopenharmony_ci} 36938c2ecf20Sopenharmony_ci 36948c2ecf20Sopenharmony_ciint rs_pretty_print_rate(char *buf, int bufsz, const u32 rate) 36958c2ecf20Sopenharmony_ci{ 36968c2ecf20Sopenharmony_ci 36978c2ecf20Sopenharmony_ci char *type, *bw; 36988c2ecf20Sopenharmony_ci u8 mcs = 0, nss = 0; 36998c2ecf20Sopenharmony_ci u8 ant = (rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; 37008c2ecf20Sopenharmony_ci 37018c2ecf20Sopenharmony_ci if (!(rate & RATE_MCS_HT_MSK) && 37028c2ecf20Sopenharmony_ci !(rate & RATE_MCS_VHT_MSK) && 37038c2ecf20Sopenharmony_ci !(rate & RATE_MCS_HE_MSK)) { 37048c2ecf20Sopenharmony_ci int index = iwl_hwrate_to_plcp_idx(rate); 37058c2ecf20Sopenharmony_ci 37068c2ecf20Sopenharmony_ci return scnprintf(buf, bufsz, "Legacy | ANT: %s Rate: %s Mbps", 37078c2ecf20Sopenharmony_ci rs_pretty_ant(ant), 37088c2ecf20Sopenharmony_ci index == IWL_RATE_INVALID ? "BAD" : 37098c2ecf20Sopenharmony_ci iwl_rate_mcs[index].mbps); 37108c2ecf20Sopenharmony_ci } 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ci if (rate & RATE_MCS_VHT_MSK) { 37138c2ecf20Sopenharmony_ci type = "VHT"; 37148c2ecf20Sopenharmony_ci mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; 37158c2ecf20Sopenharmony_ci nss = ((rate & RATE_VHT_MCS_NSS_MSK) 37168c2ecf20Sopenharmony_ci >> RATE_VHT_MCS_NSS_POS) + 1; 37178c2ecf20Sopenharmony_ci } else if (rate & RATE_MCS_HT_MSK) { 37188c2ecf20Sopenharmony_ci type = "HT"; 37198c2ecf20Sopenharmony_ci mcs = rate & RATE_HT_MCS_INDEX_MSK; 37208c2ecf20Sopenharmony_ci nss = ((rate & RATE_HT_MCS_NSS_MSK) 37218c2ecf20Sopenharmony_ci >> RATE_HT_MCS_NSS_POS) + 1; 37228c2ecf20Sopenharmony_ci } else if (rate & RATE_MCS_HE_MSK) { 37238c2ecf20Sopenharmony_ci type = "HE"; 37248c2ecf20Sopenharmony_ci mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; 37258c2ecf20Sopenharmony_ci nss = ((rate & RATE_VHT_MCS_NSS_MSK) 37268c2ecf20Sopenharmony_ci >> RATE_VHT_MCS_NSS_POS) + 1; 37278c2ecf20Sopenharmony_ci } else { 37288c2ecf20Sopenharmony_ci type = "Unknown"; /* shouldn't happen */ 37298c2ecf20Sopenharmony_ci } 37308c2ecf20Sopenharmony_ci 37318c2ecf20Sopenharmony_ci switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { 37328c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_20: 37338c2ecf20Sopenharmony_ci bw = "20Mhz"; 37348c2ecf20Sopenharmony_ci break; 37358c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_40: 37368c2ecf20Sopenharmony_ci bw = "40Mhz"; 37378c2ecf20Sopenharmony_ci break; 37388c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_80: 37398c2ecf20Sopenharmony_ci bw = "80Mhz"; 37408c2ecf20Sopenharmony_ci break; 37418c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_160: 37428c2ecf20Sopenharmony_ci bw = "160Mhz"; 37438c2ecf20Sopenharmony_ci break; 37448c2ecf20Sopenharmony_ci default: 37458c2ecf20Sopenharmony_ci bw = "BAD BW"; 37468c2ecf20Sopenharmony_ci } 37478c2ecf20Sopenharmony_ci 37488c2ecf20Sopenharmony_ci return scnprintf(buf, bufsz, 37498c2ecf20Sopenharmony_ci "0x%x: %s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s", 37508c2ecf20Sopenharmony_ci rate, type, rs_pretty_ant(ant), bw, mcs, nss, 37518c2ecf20Sopenharmony_ci (rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ", 37528c2ecf20Sopenharmony_ci (rate & RATE_MCS_STBC_MSK) ? "STBC " : "", 37538c2ecf20Sopenharmony_ci (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "", 37548c2ecf20Sopenharmony_ci (rate & RATE_HE_DUAL_CARRIER_MODE_MSK) ? "DCM " : "", 37558c2ecf20Sopenharmony_ci (rate & RATE_MCS_BF_MSK) ? "BF " : ""); 37568c2ecf20Sopenharmony_ci} 37578c2ecf20Sopenharmony_ci 37588c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 37598c2ecf20Sopenharmony_ci/** 37608c2ecf20Sopenharmony_ci * Program the device to use fixed rate for frame transmit 37618c2ecf20Sopenharmony_ci * This is for debugging/testing only 37628c2ecf20Sopenharmony_ci * once the device start use fixed rate, we need to reload the module 37638c2ecf20Sopenharmony_ci * to being back the normal operation. 37648c2ecf20Sopenharmony_ci */ 37658c2ecf20Sopenharmony_cistatic void rs_program_fix_rate(struct iwl_mvm *mvm, 37668c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 37678c2ecf20Sopenharmony_ci{ 37688c2ecf20Sopenharmony_ci lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ 37698c2ecf20Sopenharmony_ci lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ 37708c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ 37718c2ecf20Sopenharmony_ci 37728c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", 37738c2ecf20Sopenharmony_ci lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate); 37748c2ecf20Sopenharmony_ci 37758c2ecf20Sopenharmony_ci if (lq_sta->pers.dbg_fixed_rate) { 37768c2ecf20Sopenharmony_ci rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL); 37778c2ecf20Sopenharmony_ci iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq); 37788c2ecf20Sopenharmony_ci } 37798c2ecf20Sopenharmony_ci} 37808c2ecf20Sopenharmony_ci 37818c2ecf20Sopenharmony_cistatic ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, 37828c2ecf20Sopenharmony_ci const char __user *user_buf, size_t count, loff_t *ppos) 37838c2ecf20Sopenharmony_ci{ 37848c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 37858c2ecf20Sopenharmony_ci struct iwl_mvm *mvm; 37868c2ecf20Sopenharmony_ci char buf[64]; 37878c2ecf20Sopenharmony_ci size_t buf_size; 37888c2ecf20Sopenharmony_ci u32 parsed_rate; 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ci mvm = lq_sta->pers.drv; 37918c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(buf)); 37928c2ecf20Sopenharmony_ci buf_size = min(count, sizeof(buf) - 1); 37938c2ecf20Sopenharmony_ci if (copy_from_user(buf, user_buf, buf_size)) 37948c2ecf20Sopenharmony_ci return -EFAULT; 37958c2ecf20Sopenharmony_ci 37968c2ecf20Sopenharmony_ci if (sscanf(buf, "%x", &parsed_rate) == 1) 37978c2ecf20Sopenharmony_ci lq_sta->pers.dbg_fixed_rate = parsed_rate; 37988c2ecf20Sopenharmony_ci else 37998c2ecf20Sopenharmony_ci lq_sta->pers.dbg_fixed_rate = 0; 38008c2ecf20Sopenharmony_ci 38018c2ecf20Sopenharmony_ci rs_program_fix_rate(mvm, lq_sta); 38028c2ecf20Sopenharmony_ci 38038c2ecf20Sopenharmony_ci return count; 38048c2ecf20Sopenharmony_ci} 38058c2ecf20Sopenharmony_ci 38068c2ecf20Sopenharmony_cistatic ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, 38078c2ecf20Sopenharmony_ci char __user *user_buf, size_t count, loff_t *ppos) 38088c2ecf20Sopenharmony_ci{ 38098c2ecf20Sopenharmony_ci char *buff; 38108c2ecf20Sopenharmony_ci int desc = 0; 38118c2ecf20Sopenharmony_ci int i = 0; 38128c2ecf20Sopenharmony_ci ssize_t ret; 38138c2ecf20Sopenharmony_ci static const size_t bufsz = 2048; 38148c2ecf20Sopenharmony_ci 38158c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 38168c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = 38178c2ecf20Sopenharmony_ci container_of(lq_sta, struct iwl_mvm_sta, lq_sta.rs_drv); 38188c2ecf20Sopenharmony_ci struct iwl_mvm *mvm; 38198c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 38208c2ecf20Sopenharmony_ci struct rs_rate *rate = &tbl->rate; 38218c2ecf20Sopenharmony_ci u32 ss_params; 38228c2ecf20Sopenharmony_ci 38238c2ecf20Sopenharmony_ci mvm = lq_sta->pers.drv; 38248c2ecf20Sopenharmony_ci buff = kmalloc(bufsz, GFP_KERNEL); 38258c2ecf20Sopenharmony_ci if (!buff) 38268c2ecf20Sopenharmony_ci return -ENOMEM; 38278c2ecf20Sopenharmony_ci 38288c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, 38298c2ecf20Sopenharmony_ci "sta_id %d\n", lq_sta->lq.sta_id); 38308c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, 38318c2ecf20Sopenharmony_ci "failed=%d success=%d rate=0%lX\n", 38328c2ecf20Sopenharmony_ci lq_sta->total_failed, lq_sta->total_success, 38338c2ecf20Sopenharmony_ci lq_sta->active_legacy_rate); 38348c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, "fixed rate 0x%X\n", 38358c2ecf20Sopenharmony_ci lq_sta->pers.dbg_fixed_rate); 38368c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, "valid_tx_ant %s%s%s\n", 38378c2ecf20Sopenharmony_ci (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "", 38388c2ecf20Sopenharmony_ci (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "", 38398c2ecf20Sopenharmony_ci (iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : ""); 38408c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, "lq type %s\n", 38418c2ecf20Sopenharmony_ci (is_legacy(rate)) ? "legacy" : 38428c2ecf20Sopenharmony_ci is_vht(rate) ? "VHT" : "HT"); 38438c2ecf20Sopenharmony_ci if (!is_legacy(rate)) { 38448c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, " %s", 38458c2ecf20Sopenharmony_ci (is_siso(rate)) ? "SISO" : "MIMO2"); 38468c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, " %s", 38478c2ecf20Sopenharmony_ci (is_ht20(rate)) ? "20MHz" : 38488c2ecf20Sopenharmony_ci (is_ht40(rate)) ? "40MHz" : 38498c2ecf20Sopenharmony_ci (is_ht80(rate)) ? "80MHz" : 38508c2ecf20Sopenharmony_ci (is_ht160(rate)) ? "160MHz" : "BAD BW"); 38518c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, " %s %s %s %s\n", 38528c2ecf20Sopenharmony_ci (rate->sgi) ? "SGI" : "NGI", 38538c2ecf20Sopenharmony_ci (rate->ldpc) ? "LDPC" : "BCC", 38548c2ecf20Sopenharmony_ci (lq_sta->is_agg) ? "AGG on" : "", 38558c2ecf20Sopenharmony_ci (mvmsta->amsdu_enabled) ? "AMSDU on" : ""); 38568c2ecf20Sopenharmony_ci } 38578c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, "last tx rate=0x%X\n", 38588c2ecf20Sopenharmony_ci lq_sta->last_rate_n_flags); 38598c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, 38608c2ecf20Sopenharmony_ci "general: flags=0x%X mimo-d=%d s-ant=0x%x d-ant=0x%x\n", 38618c2ecf20Sopenharmony_ci lq_sta->lq.flags, 38628c2ecf20Sopenharmony_ci lq_sta->lq.mimo_delim, 38638c2ecf20Sopenharmony_ci lq_sta->lq.single_stream_ant_msk, 38648c2ecf20Sopenharmony_ci lq_sta->lq.dual_stream_ant_msk); 38658c2ecf20Sopenharmony_ci 38668c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, 38678c2ecf20Sopenharmony_ci "agg: time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", 38688c2ecf20Sopenharmony_ci le16_to_cpu(lq_sta->lq.agg_time_limit), 38698c2ecf20Sopenharmony_ci lq_sta->lq.agg_disable_start_th, 38708c2ecf20Sopenharmony_ci lq_sta->lq.agg_frame_cnt_limit); 38718c2ecf20Sopenharmony_ci 38728c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, "reduced tpc=%d\n", 38738c2ecf20Sopenharmony_ci lq_sta->lq.reduced_tpc); 38748c2ecf20Sopenharmony_ci ss_params = le32_to_cpu(lq_sta->lq.ss_params); 38758c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, 38768c2ecf20Sopenharmony_ci "single stream params: %s%s%s%s\n", 38778c2ecf20Sopenharmony_ci (ss_params & LQ_SS_PARAMS_VALID) ? 38788c2ecf20Sopenharmony_ci "VALID" : "INVALID", 38798c2ecf20Sopenharmony_ci (ss_params & LQ_SS_BFER_ALLOWED) ? 38808c2ecf20Sopenharmony_ci ", BFER" : "", 38818c2ecf20Sopenharmony_ci (ss_params & LQ_SS_STBC_1SS_ALLOWED) ? 38828c2ecf20Sopenharmony_ci ", STBC" : "", 38838c2ecf20Sopenharmony_ci (ss_params & LQ_SS_FORCE) ? 38848c2ecf20Sopenharmony_ci ", FORCE" : ""); 38858c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, 38868c2ecf20Sopenharmony_ci "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", 38878c2ecf20Sopenharmony_ci lq_sta->lq.initial_rate_index[0], 38888c2ecf20Sopenharmony_ci lq_sta->lq.initial_rate_index[1], 38898c2ecf20Sopenharmony_ci lq_sta->lq.initial_rate_index[2], 38908c2ecf20Sopenharmony_ci lq_sta->lq.initial_rate_index[3]); 38918c2ecf20Sopenharmony_ci 38928c2ecf20Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { 38938c2ecf20Sopenharmony_ci u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]); 38948c2ecf20Sopenharmony_ci 38958c2ecf20Sopenharmony_ci desc += scnprintf(buff + desc, bufsz - desc, 38968c2ecf20Sopenharmony_ci " rate[%d] 0x%X ", i, r); 38978c2ecf20Sopenharmony_ci desc += rs_pretty_print_rate(buff + desc, bufsz - desc, r); 38988c2ecf20Sopenharmony_ci if (desc < bufsz - 1) 38998c2ecf20Sopenharmony_ci buff[desc++] = '\n'; 39008c2ecf20Sopenharmony_ci } 39018c2ecf20Sopenharmony_ci 39028c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); 39038c2ecf20Sopenharmony_ci kfree(buff); 39048c2ecf20Sopenharmony_ci return ret; 39058c2ecf20Sopenharmony_ci} 39068c2ecf20Sopenharmony_ci 39078c2ecf20Sopenharmony_cistatic const struct file_operations rs_sta_dbgfs_scale_table_ops = { 39088c2ecf20Sopenharmony_ci .write = rs_sta_dbgfs_scale_table_write, 39098c2ecf20Sopenharmony_ci .read = rs_sta_dbgfs_scale_table_read, 39108c2ecf20Sopenharmony_ci .open = simple_open, 39118c2ecf20Sopenharmony_ci .llseek = default_llseek, 39128c2ecf20Sopenharmony_ci}; 39138c2ecf20Sopenharmony_cistatic ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, 39148c2ecf20Sopenharmony_ci char __user *user_buf, size_t count, loff_t *ppos) 39158c2ecf20Sopenharmony_ci{ 39168c2ecf20Sopenharmony_ci char *buff; 39178c2ecf20Sopenharmony_ci int desc = 0; 39188c2ecf20Sopenharmony_ci int i, j; 39198c2ecf20Sopenharmony_ci ssize_t ret; 39208c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl; 39218c2ecf20Sopenharmony_ci struct rs_rate *rate; 39228c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 39238c2ecf20Sopenharmony_ci 39248c2ecf20Sopenharmony_ci buff = kmalloc(1024, GFP_KERNEL); 39258c2ecf20Sopenharmony_ci if (!buff) 39268c2ecf20Sopenharmony_ci return -ENOMEM; 39278c2ecf20Sopenharmony_ci 39288c2ecf20Sopenharmony_ci for (i = 0; i < LQ_SIZE; i++) { 39298c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[i]); 39308c2ecf20Sopenharmony_ci rate = &tbl->rate; 39318c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, 39328c2ecf20Sopenharmony_ci "%s type=%d SGI=%d BW=%s DUP=0\n" 39338c2ecf20Sopenharmony_ci "index=%d\n", 39348c2ecf20Sopenharmony_ci lq_sta->active_tbl == i ? "*" : "x", 39358c2ecf20Sopenharmony_ci rate->type, 39368c2ecf20Sopenharmony_ci rate->sgi, 39378c2ecf20Sopenharmony_ci is_ht20(rate) ? "20MHz" : 39388c2ecf20Sopenharmony_ci is_ht40(rate) ? "40MHz" : 39398c2ecf20Sopenharmony_ci is_ht80(rate) ? "80MHz" : 39408c2ecf20Sopenharmony_ci is_ht160(rate) ? "160MHz" : "ERR", 39418c2ecf20Sopenharmony_ci rate->index); 39428c2ecf20Sopenharmony_ci for (j = 0; j < IWL_RATE_COUNT; j++) { 39438c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, 39448c2ecf20Sopenharmony_ci "counter=%d success=%d %%=%d\n", 39458c2ecf20Sopenharmony_ci tbl->win[j].counter, 39468c2ecf20Sopenharmony_ci tbl->win[j].success_counter, 39478c2ecf20Sopenharmony_ci tbl->win[j].success_ratio); 39488c2ecf20Sopenharmony_ci } 39498c2ecf20Sopenharmony_ci } 39508c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); 39518c2ecf20Sopenharmony_ci kfree(buff); 39528c2ecf20Sopenharmony_ci return ret; 39538c2ecf20Sopenharmony_ci} 39548c2ecf20Sopenharmony_ci 39558c2ecf20Sopenharmony_cistatic const struct file_operations rs_sta_dbgfs_stats_table_ops = { 39568c2ecf20Sopenharmony_ci .read = rs_sta_dbgfs_stats_table_read, 39578c2ecf20Sopenharmony_ci .open = simple_open, 39588c2ecf20Sopenharmony_ci .llseek = default_llseek, 39598c2ecf20Sopenharmony_ci}; 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_cistatic ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file, 39628c2ecf20Sopenharmony_ci char __user *user_buf, 39638c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 39648c2ecf20Sopenharmony_ci{ 39658c2ecf20Sopenharmony_ci static const char * const column_name[] = { 39668c2ecf20Sopenharmony_ci [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A", 39678c2ecf20Sopenharmony_ci [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B", 39688c2ecf20Sopenharmony_ci [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A", 39698c2ecf20Sopenharmony_ci [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B", 39708c2ecf20Sopenharmony_ci [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI", 39718c2ecf20Sopenharmony_ci [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI", 39728c2ecf20Sopenharmony_ci [RS_COLUMN_MIMO2] = "MIMO2", 39738c2ecf20Sopenharmony_ci [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI", 39748c2ecf20Sopenharmony_ci }; 39758c2ecf20Sopenharmony_ci 39768c2ecf20Sopenharmony_ci static const char * const rate_name[] = { 39778c2ecf20Sopenharmony_ci [IWL_RATE_1M_INDEX] = "1M", 39788c2ecf20Sopenharmony_ci [IWL_RATE_2M_INDEX] = "2M", 39798c2ecf20Sopenharmony_ci [IWL_RATE_5M_INDEX] = "5.5M", 39808c2ecf20Sopenharmony_ci [IWL_RATE_11M_INDEX] = "11M", 39818c2ecf20Sopenharmony_ci [IWL_RATE_6M_INDEX] = "6M|MCS0", 39828c2ecf20Sopenharmony_ci [IWL_RATE_9M_INDEX] = "9M", 39838c2ecf20Sopenharmony_ci [IWL_RATE_12M_INDEX] = "12M|MCS1", 39848c2ecf20Sopenharmony_ci [IWL_RATE_18M_INDEX] = "18M|MCS2", 39858c2ecf20Sopenharmony_ci [IWL_RATE_24M_INDEX] = "24M|MCS3", 39868c2ecf20Sopenharmony_ci [IWL_RATE_36M_INDEX] = "36M|MCS4", 39878c2ecf20Sopenharmony_ci [IWL_RATE_48M_INDEX] = "48M|MCS5", 39888c2ecf20Sopenharmony_ci [IWL_RATE_54M_INDEX] = "54M|MCS6", 39898c2ecf20Sopenharmony_ci [IWL_RATE_MCS_7_INDEX] = "MCS7", 39908c2ecf20Sopenharmony_ci [IWL_RATE_MCS_8_INDEX] = "MCS8", 39918c2ecf20Sopenharmony_ci [IWL_RATE_MCS_9_INDEX] = "MCS9", 39928c2ecf20Sopenharmony_ci [IWL_RATE_MCS_10_INDEX] = "MCS10", 39938c2ecf20Sopenharmony_ci [IWL_RATE_MCS_11_INDEX] = "MCS11", 39948c2ecf20Sopenharmony_ci }; 39958c2ecf20Sopenharmony_ci 39968c2ecf20Sopenharmony_ci char *buff, *pos, *endpos; 39978c2ecf20Sopenharmony_ci int col, rate; 39988c2ecf20Sopenharmony_ci ssize_t ret; 39998c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 40008c2ecf20Sopenharmony_ci struct rs_rate_stats *stats; 40018c2ecf20Sopenharmony_ci static const size_t bufsz = 1024; 40028c2ecf20Sopenharmony_ci 40038c2ecf20Sopenharmony_ci buff = kmalloc(bufsz, GFP_KERNEL); 40048c2ecf20Sopenharmony_ci if (!buff) 40058c2ecf20Sopenharmony_ci return -ENOMEM; 40068c2ecf20Sopenharmony_ci 40078c2ecf20Sopenharmony_ci pos = buff; 40088c2ecf20Sopenharmony_ci endpos = pos + bufsz; 40098c2ecf20Sopenharmony_ci 40108c2ecf20Sopenharmony_ci pos += scnprintf(pos, endpos - pos, "COLUMN,"); 40118c2ecf20Sopenharmony_ci for (rate = 0; rate < IWL_RATE_COUNT; rate++) 40128c2ecf20Sopenharmony_ci pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]); 40138c2ecf20Sopenharmony_ci pos += scnprintf(pos, endpos - pos, "\n"); 40148c2ecf20Sopenharmony_ci 40158c2ecf20Sopenharmony_ci for (col = 0; col < RS_COLUMN_COUNT; col++) { 40168c2ecf20Sopenharmony_ci pos += scnprintf(pos, endpos - pos, 40178c2ecf20Sopenharmony_ci "%s,", column_name[col]); 40188c2ecf20Sopenharmony_ci 40198c2ecf20Sopenharmony_ci for (rate = 0; rate < IWL_RATE_COUNT; rate++) { 40208c2ecf20Sopenharmony_ci stats = &(lq_sta->pers.tx_stats[col][rate]); 40218c2ecf20Sopenharmony_ci pos += scnprintf(pos, endpos - pos, 40228c2ecf20Sopenharmony_ci "%llu/%llu,", 40238c2ecf20Sopenharmony_ci stats->success, 40248c2ecf20Sopenharmony_ci stats->total); 40258c2ecf20Sopenharmony_ci } 40268c2ecf20Sopenharmony_ci pos += scnprintf(pos, endpos - pos, "\n"); 40278c2ecf20Sopenharmony_ci } 40288c2ecf20Sopenharmony_ci 40298c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); 40308c2ecf20Sopenharmony_ci kfree(buff); 40318c2ecf20Sopenharmony_ci return ret; 40328c2ecf20Sopenharmony_ci} 40338c2ecf20Sopenharmony_ci 40348c2ecf20Sopenharmony_cistatic ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file, 40358c2ecf20Sopenharmony_ci const char __user *user_buf, 40368c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 40378c2ecf20Sopenharmony_ci{ 40388c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 40398c2ecf20Sopenharmony_ci memset(lq_sta->pers.tx_stats, 0, sizeof(lq_sta->pers.tx_stats)); 40408c2ecf20Sopenharmony_ci 40418c2ecf20Sopenharmony_ci return count; 40428c2ecf20Sopenharmony_ci} 40438c2ecf20Sopenharmony_ci 40448c2ecf20Sopenharmony_cistatic const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = { 40458c2ecf20Sopenharmony_ci .read = rs_sta_dbgfs_drv_tx_stats_read, 40468c2ecf20Sopenharmony_ci .write = rs_sta_dbgfs_drv_tx_stats_write, 40478c2ecf20Sopenharmony_ci .open = simple_open, 40488c2ecf20Sopenharmony_ci .llseek = default_llseek, 40498c2ecf20Sopenharmony_ci}; 40508c2ecf20Sopenharmony_ci 40518c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_ss_force_read(struct file *file, 40528c2ecf20Sopenharmony_ci char __user *user_buf, 40538c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 40548c2ecf20Sopenharmony_ci{ 40558c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 40568c2ecf20Sopenharmony_ci char buf[12]; 40578c2ecf20Sopenharmony_ci int bufsz = sizeof(buf); 40588c2ecf20Sopenharmony_ci int pos = 0; 40598c2ecf20Sopenharmony_ci static const char * const ss_force_name[] = { 40608c2ecf20Sopenharmony_ci [RS_SS_FORCE_NONE] = "none", 40618c2ecf20Sopenharmony_ci [RS_SS_FORCE_STBC] = "stbc", 40628c2ecf20Sopenharmony_ci [RS_SS_FORCE_BFER] = "bfer", 40638c2ecf20Sopenharmony_ci [RS_SS_FORCE_SISO] = "siso", 40648c2ecf20Sopenharmony_ci }; 40658c2ecf20Sopenharmony_ci 40668c2ecf20Sopenharmony_ci pos += scnprintf(buf+pos, bufsz-pos, "%s\n", 40678c2ecf20Sopenharmony_ci ss_force_name[lq_sta->pers.ss_force]); 40688c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, pos); 40698c2ecf20Sopenharmony_ci} 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_cistatic ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf, 40728c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 40738c2ecf20Sopenharmony_ci{ 40748c2ecf20Sopenharmony_ci struct iwl_mvm *mvm = lq_sta->pers.drv; 40758c2ecf20Sopenharmony_ci int ret = 0; 40768c2ecf20Sopenharmony_ci 40778c2ecf20Sopenharmony_ci if (!strncmp("none", buf, 4)) { 40788c2ecf20Sopenharmony_ci lq_sta->pers.ss_force = RS_SS_FORCE_NONE; 40798c2ecf20Sopenharmony_ci } else if (!strncmp("siso", buf, 4)) { 40808c2ecf20Sopenharmony_ci lq_sta->pers.ss_force = RS_SS_FORCE_SISO; 40818c2ecf20Sopenharmony_ci } else if (!strncmp("stbc", buf, 4)) { 40828c2ecf20Sopenharmony_ci if (lq_sta->stbc_capable) { 40838c2ecf20Sopenharmony_ci lq_sta->pers.ss_force = RS_SS_FORCE_STBC; 40848c2ecf20Sopenharmony_ci } else { 40858c2ecf20Sopenharmony_ci IWL_ERR(mvm, 40868c2ecf20Sopenharmony_ci "can't force STBC. peer doesn't support\n"); 40878c2ecf20Sopenharmony_ci ret = -EINVAL; 40888c2ecf20Sopenharmony_ci } 40898c2ecf20Sopenharmony_ci } else if (!strncmp("bfer", buf, 4)) { 40908c2ecf20Sopenharmony_ci if (lq_sta->bfer_capable) { 40918c2ecf20Sopenharmony_ci lq_sta->pers.ss_force = RS_SS_FORCE_BFER; 40928c2ecf20Sopenharmony_ci } else { 40938c2ecf20Sopenharmony_ci IWL_ERR(mvm, 40948c2ecf20Sopenharmony_ci "can't force BFER. peer doesn't support\n"); 40958c2ecf20Sopenharmony_ci ret = -EINVAL; 40968c2ecf20Sopenharmony_ci } 40978c2ecf20Sopenharmony_ci } else { 40988c2ecf20Sopenharmony_ci IWL_ERR(mvm, "valid values none|siso|stbc|bfer\n"); 40998c2ecf20Sopenharmony_ci ret = -EINVAL; 41008c2ecf20Sopenharmony_ci } 41018c2ecf20Sopenharmony_ci return ret ?: count; 41028c2ecf20Sopenharmony_ci} 41038c2ecf20Sopenharmony_ci 41048c2ecf20Sopenharmony_ci#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ 41058c2ecf20Sopenharmony_ci _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_lq_sta) 41068c2ecf20Sopenharmony_ci#define MVM_DEBUGFS_ADD_FILE_RS(name, parent, mode) do { \ 41078c2ecf20Sopenharmony_ci debugfs_create_file(#name, mode, parent, lq_sta, \ 41088c2ecf20Sopenharmony_ci &iwl_dbgfs_##name##_ops); \ 41098c2ecf20Sopenharmony_ci } while (0) 41108c2ecf20Sopenharmony_ci 41118c2ecf20Sopenharmony_ciMVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32); 41128c2ecf20Sopenharmony_ci 41138c2ecf20Sopenharmony_cistatic void rs_drv_add_sta_debugfs(void *mvm, void *priv_sta, 41148c2ecf20Sopenharmony_ci struct dentry *dir) 41158c2ecf20Sopenharmony_ci{ 41168c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = priv_sta; 41178c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 41188c2ecf20Sopenharmony_ci 41198c2ecf20Sopenharmony_ci mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta.rs_drv); 41208c2ecf20Sopenharmony_ci 41218c2ecf20Sopenharmony_ci if (!mvmsta->vif) 41228c2ecf20Sopenharmony_ci return; 41238c2ecf20Sopenharmony_ci 41248c2ecf20Sopenharmony_ci debugfs_create_file("rate_scale_table", 0600, dir, 41258c2ecf20Sopenharmony_ci lq_sta, &rs_sta_dbgfs_scale_table_ops); 41268c2ecf20Sopenharmony_ci debugfs_create_file("rate_stats_table", 0400, dir, 41278c2ecf20Sopenharmony_ci lq_sta, &rs_sta_dbgfs_stats_table_ops); 41288c2ecf20Sopenharmony_ci debugfs_create_file("drv_tx_stats", 0600, dir, 41298c2ecf20Sopenharmony_ci lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops); 41308c2ecf20Sopenharmony_ci debugfs_create_u8("tx_agg_tid_enable", 0600, dir, 41318c2ecf20Sopenharmony_ci &lq_sta->tx_agg_tid_en); 41328c2ecf20Sopenharmony_ci debugfs_create_u8("reduced_tpc", 0600, dir, 41338c2ecf20Sopenharmony_ci &lq_sta->pers.dbg_fixed_txp_reduction); 41348c2ecf20Sopenharmony_ci 41358c2ecf20Sopenharmony_ci MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, 0600); 41368c2ecf20Sopenharmony_ci} 41378c2ecf20Sopenharmony_ci#endif 41388c2ecf20Sopenharmony_ci 41398c2ecf20Sopenharmony_ci/* 41408c2ecf20Sopenharmony_ci * Initialization of rate scaling information is done by driver after 41418c2ecf20Sopenharmony_ci * the station is added. Since mac80211 calls this function before a 41428c2ecf20Sopenharmony_ci * station is added we ignore it. 41438c2ecf20Sopenharmony_ci */ 41448c2ecf20Sopenharmony_cistatic void rs_rate_init_ops(void *mvm_r, 41458c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband, 41468c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef, 41478c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, void *mvm_sta) 41488c2ecf20Sopenharmony_ci{ 41498c2ecf20Sopenharmony_ci} 41508c2ecf20Sopenharmony_ci 41518c2ecf20Sopenharmony_ci/* ops for rate scaling implemented in the driver */ 41528c2ecf20Sopenharmony_cistatic const struct rate_control_ops rs_mvm_ops_drv = { 41538c2ecf20Sopenharmony_ci .name = RS_NAME, 41548c2ecf20Sopenharmony_ci .tx_status = rs_drv_mac80211_tx_status, 41558c2ecf20Sopenharmony_ci .get_rate = rs_drv_get_rate, 41568c2ecf20Sopenharmony_ci .rate_init = rs_rate_init_ops, 41578c2ecf20Sopenharmony_ci .alloc = rs_alloc, 41588c2ecf20Sopenharmony_ci .free = rs_free, 41598c2ecf20Sopenharmony_ci .alloc_sta = rs_drv_alloc_sta, 41608c2ecf20Sopenharmony_ci .free_sta = rs_free_sta, 41618c2ecf20Sopenharmony_ci .rate_update = rs_drv_rate_update, 41628c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 41638c2ecf20Sopenharmony_ci .add_sta_debugfs = rs_drv_add_sta_debugfs, 41648c2ecf20Sopenharmony_ci#endif 41658c2ecf20Sopenharmony_ci .capa = RATE_CTRL_CAPA_VHT_EXT_NSS_BW, 41668c2ecf20Sopenharmony_ci}; 41678c2ecf20Sopenharmony_ci 41688c2ecf20Sopenharmony_civoid iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, 41698c2ecf20Sopenharmony_ci enum nl80211_band band, bool update) 41708c2ecf20Sopenharmony_ci{ 41718c2ecf20Sopenharmony_ci if (iwl_mvm_has_tlc_offload(mvm)) { 41728c2ecf20Sopenharmony_ci rs_fw_rate_init(mvm, sta, band, update); 41738c2ecf20Sopenharmony_ci } else { 41748c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 41758c2ecf20Sopenharmony_ci 41768c2ecf20Sopenharmony_ci spin_lock(&mvmsta->lq_sta.rs_drv.pers.lock); 41778c2ecf20Sopenharmony_ci rs_drv_rate_init(mvm, sta, band); 41788c2ecf20Sopenharmony_ci spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock); 41798c2ecf20Sopenharmony_ci } 41808c2ecf20Sopenharmony_ci} 41818c2ecf20Sopenharmony_ci 41828c2ecf20Sopenharmony_ciint iwl_mvm_rate_control_register(void) 41838c2ecf20Sopenharmony_ci{ 41848c2ecf20Sopenharmony_ci return ieee80211_rate_control_register(&rs_mvm_ops_drv); 41858c2ecf20Sopenharmony_ci} 41868c2ecf20Sopenharmony_ci 41878c2ecf20Sopenharmony_civoid iwl_mvm_rate_control_unregister(void) 41888c2ecf20Sopenharmony_ci{ 41898c2ecf20Sopenharmony_ci ieee80211_rate_control_unregister(&rs_mvm_ops_drv); 41908c2ecf20Sopenharmony_ci} 41918c2ecf20Sopenharmony_ci 41928c2ecf20Sopenharmony_cistatic int rs_drv_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, 41938c2ecf20Sopenharmony_ci bool enable) 41948c2ecf20Sopenharmony_ci{ 41958c2ecf20Sopenharmony_ci struct iwl_lq_cmd *lq = &mvmsta->lq_sta.rs_drv.lq; 41968c2ecf20Sopenharmony_ci 41978c2ecf20Sopenharmony_ci lockdep_assert_held(&mvm->mutex); 41988c2ecf20Sopenharmony_ci 41998c2ecf20Sopenharmony_ci if (enable) { 42008c2ecf20Sopenharmony_ci if (mvmsta->tx_protection == 0) 42018c2ecf20Sopenharmony_ci lq->flags |= LQ_FLAG_USE_RTS_MSK; 42028c2ecf20Sopenharmony_ci mvmsta->tx_protection++; 42038c2ecf20Sopenharmony_ci } else { 42048c2ecf20Sopenharmony_ci mvmsta->tx_protection--; 42058c2ecf20Sopenharmony_ci if (mvmsta->tx_protection == 0) 42068c2ecf20Sopenharmony_ci lq->flags &= ~LQ_FLAG_USE_RTS_MSK; 42078c2ecf20Sopenharmony_ci } 42088c2ecf20Sopenharmony_ci 42098c2ecf20Sopenharmony_ci return iwl_mvm_send_lq_cmd(mvm, lq); 42108c2ecf20Sopenharmony_ci} 42118c2ecf20Sopenharmony_ci 42128c2ecf20Sopenharmony_ci/** 42138c2ecf20Sopenharmony_ci * iwl_mvm_tx_protection - ask FW to enable RTS/CTS protection 42148c2ecf20Sopenharmony_ci * @mvmsta: The station 42158c2ecf20Sopenharmony_ci * @enable: Enable Tx protection? 42168c2ecf20Sopenharmony_ci */ 42178c2ecf20Sopenharmony_ciint iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, 42188c2ecf20Sopenharmony_ci bool enable) 42198c2ecf20Sopenharmony_ci{ 42208c2ecf20Sopenharmony_ci if (iwl_mvm_has_tlc_offload(mvm)) 42218c2ecf20Sopenharmony_ci return rs_fw_tx_protection(mvm, mvmsta, enable); 42228c2ecf20Sopenharmony_ci else 42238c2ecf20Sopenharmony_ci return rs_drv_tx_protection(mvm, mvmsta, enable); 42248c2ecf20Sopenharmony_ci} 4225