18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. 58c2ecf20Sopenharmony_ci * Copyright (C) 2019 - 2020 Intel Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact Information: 88c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 98c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci *****************************************************************************/ 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <net/mac80211.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 188c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "dev.h" 248c2ecf20Sopenharmony_ci#include "agn.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define RS_NAME "iwl-agn-rs" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define NUM_TRY_BEFORE_ANT_TOGGLE 1 298c2ecf20Sopenharmony_ci#define IWL_NUMBER_TRY 1 308c2ecf20Sopenharmony_ci#define IWL_HT_NUMBER_TRY 3 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ 338c2ecf20Sopenharmony_ci#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ 348c2ecf20Sopenharmony_ci#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* max allowed rate miss before sync LQ cmd */ 378c2ecf20Sopenharmony_ci#define IWL_MISSED_RATE_MAX 15 388c2ecf20Sopenharmony_ci/* max time to accum history 2 seconds */ 398c2ecf20Sopenharmony_ci#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic u8 rs_ht_to_legacy[] = { 428c2ecf20Sopenharmony_ci IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, 438c2ecf20Sopenharmony_ci IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, 448c2ecf20Sopenharmony_ci IWL_RATE_6M_INDEX, 458c2ecf20Sopenharmony_ci IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, 468c2ecf20Sopenharmony_ci IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, 478c2ecf20Sopenharmony_ci IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, 488c2ecf20Sopenharmony_ci IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic const u8 ant_toggle_lookup[] = { 528c2ecf20Sopenharmony_ci /*ANT_NONE -> */ ANT_NONE, 538c2ecf20Sopenharmony_ci /*ANT_A -> */ ANT_B, 548c2ecf20Sopenharmony_ci /*ANT_B -> */ ANT_C, 558c2ecf20Sopenharmony_ci /*ANT_AB -> */ ANT_BC, 568c2ecf20Sopenharmony_ci /*ANT_C -> */ ANT_A, 578c2ecf20Sopenharmony_ci /*ANT_AC -> */ ANT_AB, 588c2ecf20Sopenharmony_ci /*ANT_BC -> */ ANT_AC, 598c2ecf20Sopenharmony_ci /*ANT_ABC -> */ ANT_ABC, 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ 638c2ecf20Sopenharmony_ci [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ 648c2ecf20Sopenharmony_ci IWL_RATE_SISO_##s##M_PLCP, \ 658c2ecf20Sopenharmony_ci IWL_RATE_MIMO2_##s##M_PLCP,\ 668c2ecf20Sopenharmony_ci IWL_RATE_MIMO3_##s##M_PLCP,\ 678c2ecf20Sopenharmony_ci IWL_RATE_##r##M_IEEE, \ 688c2ecf20Sopenharmony_ci IWL_RATE_##ip##M_INDEX, \ 698c2ecf20Sopenharmony_ci IWL_RATE_##in##M_INDEX, \ 708c2ecf20Sopenharmony_ci IWL_RATE_##rp##M_INDEX, \ 718c2ecf20Sopenharmony_ci IWL_RATE_##rn##M_INDEX, \ 728c2ecf20Sopenharmony_ci IWL_RATE_##pp##M_INDEX, \ 738c2ecf20Sopenharmony_ci IWL_RATE_##np##M_INDEX } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * Parameter order: 778c2ecf20Sopenharmony_ci * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg 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_ciconst struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { 848c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ 858c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ 868c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ 878c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ 888c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ 898c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ 908c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ 918c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ 928c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ 938c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ 948c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ 958c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ 968c2ecf20Sopenharmony_ci IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ 978c2ecf20Sopenharmony_ci /* FIXME:RS: ^^ should be INV (legacy) */ 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline u8 rs_extract_rate(u32 rate_n_flags) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci return (u8)(rate_n_flags & RATE_MCS_RATE_MSK); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int idx = 0; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* HT rate format */ 1108c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) { 1118c2ecf20Sopenharmony_ci idx = rs_extract_rate(rate_n_flags); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (idx >= IWL_RATE_MIMO3_6M_PLCP) 1148c2ecf20Sopenharmony_ci idx = idx - IWL_RATE_MIMO3_6M_PLCP; 1158c2ecf20Sopenharmony_ci else if (idx >= IWL_RATE_MIMO2_6M_PLCP) 1168c2ecf20Sopenharmony_ci idx = idx - IWL_RATE_MIMO2_6M_PLCP; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci idx += IWL_FIRST_OFDM_RATE; 1198c2ecf20Sopenharmony_ci /* skip 9M not supported in ht*/ 1208c2ecf20Sopenharmony_ci if (idx >= IWL_RATE_9M_INDEX) 1218c2ecf20Sopenharmony_ci idx += 1; 1228c2ecf20Sopenharmony_ci if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE)) 1238c2ecf20Sopenharmony_ci return idx; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* legacy rate format, search for match in table */ 1268c2ecf20Sopenharmony_ci } else { 1278c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) 1288c2ecf20Sopenharmony_ci if (iwl_rates[idx].plcp == 1298c2ecf20Sopenharmony_ci rs_extract_rate(rate_n_flags)) 1308c2ecf20Sopenharmony_ci return idx; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return -1; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void rs_rate_scale_perform(struct iwl_priv *priv, 1378c2ecf20Sopenharmony_ci struct sk_buff *skb, 1388c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 1398c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta); 1408c2ecf20Sopenharmony_cistatic void rs_fill_link_cmd(struct iwl_priv *priv, 1418c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, u32 rate_n_flags); 1428c2ecf20Sopenharmony_cistatic void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 1468c2ecf20Sopenharmony_cistatic void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, 1478c2ecf20Sopenharmony_ci u32 *rate_n_flags, int index); 1488c2ecf20Sopenharmony_ci#else 1498c2ecf20Sopenharmony_cistatic void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, 1508c2ecf20Sopenharmony_ci u32 *rate_n_flags, int index) 1518c2ecf20Sopenharmony_ci{} 1528c2ecf20Sopenharmony_ci#endif 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * The following tables contain the expected throughput metrics for all rates 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * where invalid entries are zeros. 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * CCK rates are only valid in legacy table and will only be used in G 1628c2ecf20Sopenharmony_ci * (2.4 GHz) band. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic const u16 expected_tpt_legacy[IWL_RATE_COUNT] = { 1668c2ecf20Sopenharmony_ci 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic const u16 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { 1708c2ecf20Sopenharmony_ci {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */ 1718c2ecf20Sopenharmony_ci {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */ 1728c2ecf20Sopenharmony_ci {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */ 1738c2ecf20Sopenharmony_ci {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */ 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic const u16 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { 1778c2ecf20Sopenharmony_ci {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ 1788c2ecf20Sopenharmony_ci {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ 1798c2ecf20Sopenharmony_ci {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */ 1808c2ecf20Sopenharmony_ci {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */ 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { 1848c2ecf20Sopenharmony_ci {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */ 1858c2ecf20Sopenharmony_ci {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */ 1868c2ecf20Sopenharmony_ci {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */ 1878c2ecf20Sopenharmony_ci {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/ 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { 1918c2ecf20Sopenharmony_ci {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ 1928c2ecf20Sopenharmony_ci {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ 1938c2ecf20Sopenharmony_ci {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */ 1948c2ecf20Sopenharmony_ci {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic const u16 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { 1988c2ecf20Sopenharmony_ci {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */ 1998c2ecf20Sopenharmony_ci {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */ 2008c2ecf20Sopenharmony_ci {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */ 2018c2ecf20Sopenharmony_ci {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */ 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic const u16 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { 2058c2ecf20Sopenharmony_ci {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */ 2068c2ecf20Sopenharmony_ci {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */ 2078c2ecf20Sopenharmony_ci {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */ 2088c2ecf20Sopenharmony_ci {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */ 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* mbps, mcs */ 2128c2ecf20Sopenharmony_cistatic const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { 2138c2ecf20Sopenharmony_ci { "1", "BPSK DSSS"}, 2148c2ecf20Sopenharmony_ci { "2", "QPSK DSSS"}, 2158c2ecf20Sopenharmony_ci {"5.5", "BPSK CCK"}, 2168c2ecf20Sopenharmony_ci { "11", "QPSK CCK"}, 2178c2ecf20Sopenharmony_ci { "6", "BPSK 1/2"}, 2188c2ecf20Sopenharmony_ci { "9", "BPSK 1/2"}, 2198c2ecf20Sopenharmony_ci { "12", "QPSK 1/2"}, 2208c2ecf20Sopenharmony_ci { "18", "QPSK 3/4"}, 2218c2ecf20Sopenharmony_ci { "24", "16QAM 1/2"}, 2228c2ecf20Sopenharmony_ci { "36", "16QAM 3/4"}, 2238c2ecf20Sopenharmony_ci { "48", "64QAM 2/3"}, 2248c2ecf20Sopenharmony_ci { "54", "64QAM 3/4"}, 2258c2ecf20Sopenharmony_ci { "60", "64QAM 5/6"}, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci#define MCS_INDEX_PER_STREAM (8) 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci window->data = 0; 2338c2ecf20Sopenharmony_ci window->success_counter = 0; 2348c2ecf20Sopenharmony_ci window->success_ratio = IWL_INVALID_VALUE; 2358c2ecf20Sopenharmony_ci window->counter = 0; 2368c2ecf20Sopenharmony_ci window->average_tpt = IWL_INVALID_VALUE; 2378c2ecf20Sopenharmony_ci window->stamp = 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci return (ant_type & valid_antenna) == ant_type; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* 2468c2ecf20Sopenharmony_ci * removes the old data from the statistics. All data that is older than 2478c2ecf20Sopenharmony_ci * TID_MAX_TIME_DIFF, will be deleted. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_cistatic void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci /* The oldest age we want to keep */ 2528c2ecf20Sopenharmony_ci u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci while (tl->queue_count && 2558c2ecf20Sopenharmony_ci (tl->time_stamp < oldest_time)) { 2568c2ecf20Sopenharmony_ci tl->total -= tl->packet_count[tl->head]; 2578c2ecf20Sopenharmony_ci tl->packet_count[tl->head] = 0; 2588c2ecf20Sopenharmony_ci tl->time_stamp += TID_QUEUE_CELL_SPACING; 2598c2ecf20Sopenharmony_ci tl->queue_count--; 2608c2ecf20Sopenharmony_ci tl->head++; 2618c2ecf20Sopenharmony_ci if (tl->head >= TID_QUEUE_MAX_SIZE) 2628c2ecf20Sopenharmony_ci tl->head = 0; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * increment traffic load value for tid and also remove 2688c2ecf20Sopenharmony_ci * any old values if passed the certain time period 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_cistatic u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, 2718c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci u32 curr_time = jiffies_to_msecs(jiffies); 2748c2ecf20Sopenharmony_ci u32 time_diff; 2758c2ecf20Sopenharmony_ci s32 index; 2768c2ecf20Sopenharmony_ci struct iwl_traffic_load *tl = NULL; 2778c2ecf20Sopenharmony_ci u8 tid; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (ieee80211_is_data_qos(hdr->frame_control)) { 2808c2ecf20Sopenharmony_ci u8 *qc = ieee80211_get_qos_ctl(hdr); 2818c2ecf20Sopenharmony_ci tid = qc[0] & 0xf; 2828c2ecf20Sopenharmony_ci } else 2838c2ecf20Sopenharmony_ci return IWL_MAX_TID_COUNT; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (unlikely(tid >= IWL_MAX_TID_COUNT)) 2868c2ecf20Sopenharmony_ci return IWL_MAX_TID_COUNT; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci tl = &lq_data->load[tid]; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci curr_time -= curr_time % TID_ROUND_VALUE; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Happens only for the first packet. Initialize the data */ 2938c2ecf20Sopenharmony_ci if (!(tl->queue_count)) { 2948c2ecf20Sopenharmony_ci tl->total = 1; 2958c2ecf20Sopenharmony_ci tl->time_stamp = curr_time; 2968c2ecf20Sopenharmony_ci tl->queue_count = 1; 2978c2ecf20Sopenharmony_ci tl->head = 0; 2988c2ecf20Sopenharmony_ci tl->packet_count[0] = 1; 2998c2ecf20Sopenharmony_ci return IWL_MAX_TID_COUNT; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); 3038c2ecf20Sopenharmony_ci index = time_diff / TID_QUEUE_CELL_SPACING; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* The history is too long: remove data that is older than */ 3068c2ecf20Sopenharmony_ci /* TID_MAX_TIME_DIFF */ 3078c2ecf20Sopenharmony_ci if (index >= TID_QUEUE_MAX_SIZE) 3088c2ecf20Sopenharmony_ci rs_tl_rm_old_stats(tl, curr_time); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci index = (tl->head + index) % TID_QUEUE_MAX_SIZE; 3118c2ecf20Sopenharmony_ci tl->packet_count[index] = tl->packet_count[index] + 1; 3128c2ecf20Sopenharmony_ci tl->total = tl->total + 1; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if ((index + 1) > tl->queue_count) 3158c2ecf20Sopenharmony_ci tl->queue_count = index + 1; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return tid; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 3218c2ecf20Sopenharmony_ci/* 3228c2ecf20Sopenharmony_ci * Program the device to use fixed rate for frame transmit 3238c2ecf20Sopenharmony_ci * This is for debugging/testing only 3248c2ecf20Sopenharmony_ci * once the device start use fixed rate, we need to reload the module 3258c2ecf20Sopenharmony_ci * to being back the normal operation. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_cistatic void rs_program_fix_rate(struct iwl_priv *priv, 3288c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv = 3318c2ecf20Sopenharmony_ci container_of(lq_sta, struct iwl_station_priv, lq_sta); 3328c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx = sta_priv->ctx; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ 3358c2ecf20Sopenharmony_ci lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ 3368c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ 3378c2ecf20Sopenharmony_ci lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n", 3408c2ecf20Sopenharmony_ci lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (lq_sta->dbg_fixed_rate) { 3438c2ecf20Sopenharmony_ci rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); 3448c2ecf20Sopenharmony_ci iwl_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC, 3458c2ecf20Sopenharmony_ci false); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci#endif 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* 3518c2ecf20Sopenharmony_ci get the traffic load value for tid 3528c2ecf20Sopenharmony_ci*/ 3538c2ecf20Sopenharmony_cistatic void rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci u32 curr_time = jiffies_to_msecs(jiffies); 3568c2ecf20Sopenharmony_ci u32 time_diff; 3578c2ecf20Sopenharmony_ci s32 index; 3588c2ecf20Sopenharmony_ci struct iwl_traffic_load *tl = NULL; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (tid >= IWL_MAX_TID_COUNT) 3618c2ecf20Sopenharmony_ci return; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci tl = &(lq_data->load[tid]); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci curr_time -= curr_time % TID_ROUND_VALUE; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (!(tl->queue_count)) 3688c2ecf20Sopenharmony_ci return; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); 3718c2ecf20Sopenharmony_ci index = time_diff / TID_QUEUE_CELL_SPACING; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* The history is too long: remove data that is older than */ 3748c2ecf20Sopenharmony_ci /* TID_MAX_TIME_DIFF */ 3758c2ecf20Sopenharmony_ci if (index >= TID_QUEUE_MAX_SIZE) 3768c2ecf20Sopenharmony_ci rs_tl_rm_old_stats(tl, curr_time); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, 3808c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_data, u8 tid, 3818c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci int ret = -EAGAIN; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * Don't create TX aggregation sessions when in high 3878c2ecf20Sopenharmony_ci * BT traffic, as they would just be disrupted by BT. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) { 3908c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, 3918c2ecf20Sopenharmony_ci "BT traffic (%d), no aggregation allowed\n", 3928c2ecf20Sopenharmony_ci priv->bt_traffic_load); 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci rs_tl_get_load(lq_data, tid); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", 3998c2ecf20Sopenharmony_ci sta->addr, tid); 4008c2ecf20Sopenharmony_ci ret = ieee80211_start_tx_ba_session(sta, tid, 5000); 4018c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 4028c2ecf20Sopenharmony_ci /* 4038c2ecf20Sopenharmony_ci * driver and mac80211 is out of sync 4048c2ecf20Sopenharmony_ci * this might be cause by reloading firmware 4058c2ecf20Sopenharmony_ci * stop the tx ba session here 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", 4088c2ecf20Sopenharmony_ci tid); 4098c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_session(sta, tid); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci return ret; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, 4158c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_data, 4168c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci if (tid < IWL_MAX_TID_COUNT) 4198c2ecf20Sopenharmony_ci rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); 4208c2ecf20Sopenharmony_ci else 4218c2ecf20Sopenharmony_ci IWL_ERR(priv, "tid exceeds max TID count: %d/%d\n", 4228c2ecf20Sopenharmony_ci tid, IWL_MAX_TID_COUNT); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic inline int get_num_of_ant_from_rate(u32 rate_n_flags) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + 4288c2ecf20Sopenharmony_ci !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + 4298c2ecf20Sopenharmony_ci !!(rate_n_flags & RATE_MCS_ANT_C_MSK); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* 4338c2ecf20Sopenharmony_ci * Static function to get the expected throughput from an iwl_scale_tbl_info 4348c2ecf20Sopenharmony_ci * that wraps a NULL pointer check 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_cistatic s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci if (tbl->expected_tpt) 4398c2ecf20Sopenharmony_ci return tbl->expected_tpt[rs_index]; 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* 4448c2ecf20Sopenharmony_ci * rs_collect_tx_data - Update the success/failure sliding window 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci * We keep a sliding window of the last 62 packets transmitted 4478c2ecf20Sopenharmony_ci * at this rate. window->data contains the bitmask of successful 4488c2ecf20Sopenharmony_ci * packets. 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_cistatic int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, 4518c2ecf20Sopenharmony_ci int scale_index, int attempts, int successes) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window = NULL; 4548c2ecf20Sopenharmony_ci static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); 4558c2ecf20Sopenharmony_ci s32 fail_count, tpt; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* Select window for current tx bit rate */ 4618c2ecf20Sopenharmony_ci window = &(tbl->win[scale_index]); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Get expected throughput */ 4648c2ecf20Sopenharmony_ci tpt = get_expected_tpt(tbl, scale_index); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* 4678c2ecf20Sopenharmony_ci * Keep track of only the latest 62 tx frame attempts in this rate's 4688c2ecf20Sopenharmony_ci * history window; anything older isn't really relevant any more. 4698c2ecf20Sopenharmony_ci * If we have filled up the sliding window, drop the oldest attempt; 4708c2ecf20Sopenharmony_ci * if the oldest attempt (highest bit in bitmap) shows "success", 4718c2ecf20Sopenharmony_ci * subtract "1" from the success counter (this is the main reason 4728c2ecf20Sopenharmony_ci * we keep these bitmaps!). 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci while (attempts > 0) { 4758c2ecf20Sopenharmony_ci if (window->counter >= IWL_RATE_MAX_WINDOW) { 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* remove earliest */ 4788c2ecf20Sopenharmony_ci window->counter = IWL_RATE_MAX_WINDOW - 1; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (window->data & mask) { 4818c2ecf20Sopenharmony_ci window->data &= ~mask; 4828c2ecf20Sopenharmony_ci window->success_counter--; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* Increment frames-attempted counter */ 4878c2ecf20Sopenharmony_ci window->counter++; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Shift bitmap by one frame to throw away oldest history */ 4908c2ecf20Sopenharmony_ci window->data <<= 1; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* Mark the most recent #successes attempts as successful */ 4938c2ecf20Sopenharmony_ci if (successes > 0) { 4948c2ecf20Sopenharmony_ci window->success_counter++; 4958c2ecf20Sopenharmony_ci window->data |= 0x1; 4968c2ecf20Sopenharmony_ci successes--; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci attempts--; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Calculate current success ratio, avoid divide-by-0! */ 5038c2ecf20Sopenharmony_ci if (window->counter > 0) 5048c2ecf20Sopenharmony_ci window->success_ratio = 128 * (100 * window->success_counter) 5058c2ecf20Sopenharmony_ci / window->counter; 5068c2ecf20Sopenharmony_ci else 5078c2ecf20Sopenharmony_ci window->success_ratio = IWL_INVALID_VALUE; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci fail_count = window->counter - window->success_counter; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Calculate average throughput, if we have enough history. */ 5128c2ecf20Sopenharmony_ci if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || 5138c2ecf20Sopenharmony_ci (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) 5148c2ecf20Sopenharmony_ci window->average_tpt = (window->success_ratio * tpt + 64) / 128; 5158c2ecf20Sopenharmony_ci else 5168c2ecf20Sopenharmony_ci window->average_tpt = IWL_INVALID_VALUE; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Tag this window as having been updated */ 5198c2ecf20Sopenharmony_ci window->stamp = jiffies; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/* 5258c2ecf20Sopenharmony_ci * Fill uCode API rate_n_flags field, based on "search" or "active" table. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci/* FIXME:RS:remove this function and put the flags statically in the table */ 5288c2ecf20Sopenharmony_cistatic u32 rate_n_flags_from_tbl(struct iwl_priv *priv, 5298c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 5308c2ecf20Sopenharmony_ci int index, u8 use_green) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci u32 rate_n_flags = 0; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (is_legacy(tbl->lq_type)) { 5358c2ecf20Sopenharmony_ci rate_n_flags = iwl_rates[index].plcp; 5368c2ecf20Sopenharmony_ci if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) 5378c2ecf20Sopenharmony_ci rate_n_flags |= RATE_MCS_CCK_MSK; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci } else if (is_Ht(tbl->lq_type)) { 5408c2ecf20Sopenharmony_ci if (index > IWL_LAST_OFDM_RATE) { 5418c2ecf20Sopenharmony_ci IWL_ERR(priv, "Invalid HT rate index %d\n", index); 5428c2ecf20Sopenharmony_ci index = IWL_LAST_OFDM_RATE; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci rate_n_flags = RATE_MCS_HT_MSK; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (is_siso(tbl->lq_type)) 5478c2ecf20Sopenharmony_ci rate_n_flags |= iwl_rates[index].plcp_siso; 5488c2ecf20Sopenharmony_ci else if (is_mimo2(tbl->lq_type)) 5498c2ecf20Sopenharmony_ci rate_n_flags |= iwl_rates[index].plcp_mimo2; 5508c2ecf20Sopenharmony_ci else 5518c2ecf20Sopenharmony_ci rate_n_flags |= iwl_rates[index].plcp_mimo3; 5528c2ecf20Sopenharmony_ci } else { 5538c2ecf20Sopenharmony_ci IWL_ERR(priv, "Invalid tbl->lq_type %d\n", tbl->lq_type); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & 5578c2ecf20Sopenharmony_ci RATE_MCS_ANT_ABC_MSK); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (is_Ht(tbl->lq_type)) { 5608c2ecf20Sopenharmony_ci if (tbl->is_ht40) { 5618c2ecf20Sopenharmony_ci if (tbl->is_dup) 5628c2ecf20Sopenharmony_ci rate_n_flags |= RATE_MCS_DUP_MSK; 5638c2ecf20Sopenharmony_ci else 5648c2ecf20Sopenharmony_ci rate_n_flags |= RATE_MCS_HT40_MSK; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci if (tbl->is_SGI) 5678c2ecf20Sopenharmony_ci rate_n_flags |= RATE_MCS_SGI_MSK; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (use_green) { 5708c2ecf20Sopenharmony_ci rate_n_flags |= RATE_MCS_GF_MSK; 5718c2ecf20Sopenharmony_ci if (is_siso(tbl->lq_type) && tbl->is_SGI) { 5728c2ecf20Sopenharmony_ci rate_n_flags &= ~RATE_MCS_SGI_MSK; 5738c2ecf20Sopenharmony_ci IWL_ERR(priv, "GF was set with SGI:SISO\n"); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci return rate_n_flags; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/* 5818c2ecf20Sopenharmony_ci * Interpret uCode API's rate_n_flags format, 5828c2ecf20Sopenharmony_ci * fill "search" or "active" tx mode table. 5838c2ecf20Sopenharmony_ci */ 5848c2ecf20Sopenharmony_cistatic int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, 5858c2ecf20Sopenharmony_ci enum nl80211_band band, 5868c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 5878c2ecf20Sopenharmony_ci int *rate_idx) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); 5908c2ecf20Sopenharmony_ci u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); 5918c2ecf20Sopenharmony_ci u8 mcs; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci memset(tbl, 0, sizeof(struct iwl_scale_tbl_info)); 5948c2ecf20Sopenharmony_ci *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (*rate_idx == IWL_RATE_INVALID) { 5978c2ecf20Sopenharmony_ci *rate_idx = -1; 5988c2ecf20Sopenharmony_ci return -EINVAL; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci tbl->is_SGI = 0; /* default legacy setup */ 6018c2ecf20Sopenharmony_ci tbl->is_ht40 = 0; 6028c2ecf20Sopenharmony_ci tbl->is_dup = 0; 6038c2ecf20Sopenharmony_ci tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); 6048c2ecf20Sopenharmony_ci tbl->lq_type = LQ_NONE; 6058c2ecf20Sopenharmony_ci tbl->max_search = IWL_MAX_SEARCH; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* legacy rate format */ 6088c2ecf20Sopenharmony_ci if (!(rate_n_flags & RATE_MCS_HT_MSK)) { 6098c2ecf20Sopenharmony_ci if (num_of_ant == 1) { 6108c2ecf20Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 6118c2ecf20Sopenharmony_ci tbl->lq_type = LQ_A; 6128c2ecf20Sopenharmony_ci else 6138c2ecf20Sopenharmony_ci tbl->lq_type = LQ_G; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci /* HT rate format */ 6168c2ecf20Sopenharmony_ci } else { 6178c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_SGI_MSK) 6188c2ecf20Sopenharmony_ci tbl->is_SGI = 1; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if ((rate_n_flags & RATE_MCS_HT40_MSK) || 6218c2ecf20Sopenharmony_ci (rate_n_flags & RATE_MCS_DUP_MSK)) 6228c2ecf20Sopenharmony_ci tbl->is_ht40 = 1; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_DUP_MSK) 6258c2ecf20Sopenharmony_ci tbl->is_dup = 1; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci mcs = rs_extract_rate(rate_n_flags); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* SISO */ 6308c2ecf20Sopenharmony_ci if (mcs <= IWL_RATE_SISO_60M_PLCP) { 6318c2ecf20Sopenharmony_ci if (num_of_ant == 1) 6328c2ecf20Sopenharmony_ci tbl->lq_type = LQ_SISO; /*else NONE*/ 6338c2ecf20Sopenharmony_ci /* MIMO2 */ 6348c2ecf20Sopenharmony_ci } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { 6358c2ecf20Sopenharmony_ci if (num_of_ant == 2) 6368c2ecf20Sopenharmony_ci tbl->lq_type = LQ_MIMO2; 6378c2ecf20Sopenharmony_ci /* MIMO3 */ 6388c2ecf20Sopenharmony_ci } else { 6398c2ecf20Sopenharmony_ci if (num_of_ant == 3) { 6408c2ecf20Sopenharmony_ci tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; 6418c2ecf20Sopenharmony_ci tbl->lq_type = LQ_MIMO3; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci/* switch to another antenna/antennas and return 1 */ 6498c2ecf20Sopenharmony_ci/* if no other valid antenna found, return 0 */ 6508c2ecf20Sopenharmony_cistatic int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, 6518c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci u8 new_ant_type; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (!tbl->ant_type || tbl->ant_type > ANT_ABC) 6568c2ecf20Sopenharmony_ci return 0; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci new_ant_type = ant_toggle_lookup[tbl->ant_type]; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci while ((new_ant_type != tbl->ant_type) && 6648c2ecf20Sopenharmony_ci !rs_is_valid_ant(valid_ant, new_ant_type)) 6658c2ecf20Sopenharmony_ci new_ant_type = ant_toggle_lookup[new_ant_type]; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (new_ant_type == tbl->ant_type) 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci tbl->ant_type = new_ant_type; 6718c2ecf20Sopenharmony_ci *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; 6728c2ecf20Sopenharmony_ci *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; 6738c2ecf20Sopenharmony_ci return 1; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/* 6778c2ecf20Sopenharmony_ci * Green-field mode is valid if the station supports it and 6788c2ecf20Sopenharmony_ci * there are no non-GF stations present in the BSS. 6798c2ecf20Sopenharmony_ci */ 6808c2ecf20Sopenharmony_cistatic bool rs_use_green(struct ieee80211_sta *sta) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci /* 6838c2ecf20Sopenharmony_ci * There's a bug somewhere in this code that causes the 6848c2ecf20Sopenharmony_ci * scaling to get stuck because GF+SGI can't be combined 6858c2ecf20Sopenharmony_ci * in SISO rates. Until we find that bug, disable GF, it 6868c2ecf20Sopenharmony_ci * has only limited benefit and we still interoperate with 6878c2ecf20Sopenharmony_ci * GF APs since we can always receive GF transmissions. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ci return false; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/* 6938c2ecf20Sopenharmony_ci * rs_get_supported_rates - get the available rates 6948c2ecf20Sopenharmony_ci * 6958c2ecf20Sopenharmony_ci * if management frame or broadcast frame only return 6968c2ecf20Sopenharmony_ci * basic available rates. 6978c2ecf20Sopenharmony_ci * 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_cistatic u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, 7008c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr, 7018c2ecf20Sopenharmony_ci enum iwl_table_type rate_type) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci if (is_legacy(rate_type)) { 7048c2ecf20Sopenharmony_ci return lq_sta->active_legacy_rate; 7058c2ecf20Sopenharmony_ci } else { 7068c2ecf20Sopenharmony_ci if (is_siso(rate_type)) 7078c2ecf20Sopenharmony_ci return lq_sta->active_siso_rate; 7088c2ecf20Sopenharmony_ci else if (is_mimo2(rate_type)) 7098c2ecf20Sopenharmony_ci return lq_sta->active_mimo2_rate; 7108c2ecf20Sopenharmony_ci else 7118c2ecf20Sopenharmony_ci return lq_sta->active_mimo3_rate; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask, 7168c2ecf20Sopenharmony_ci int rate_type) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci u8 high = IWL_RATE_INVALID; 7198c2ecf20Sopenharmony_ci u8 low = IWL_RATE_INVALID; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* 802.11A or ht walks to the next literal adjacent rate in 7228c2ecf20Sopenharmony_ci * the rate table */ 7238c2ecf20Sopenharmony_ci if (is_a_band(rate_type) || !is_legacy(rate_type)) { 7248c2ecf20Sopenharmony_ci int i; 7258c2ecf20Sopenharmony_ci u32 mask; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Find the previous rate that is in the rate mask */ 7288c2ecf20Sopenharmony_ci i = index - 1; 7298c2ecf20Sopenharmony_ci if (i >= 0) 7308c2ecf20Sopenharmony_ci mask = BIT(i); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci for (; i >= 0; i--, mask >>= 1) { 7338c2ecf20Sopenharmony_ci if (rate_mask & mask) { 7348c2ecf20Sopenharmony_ci low = i; 7358c2ecf20Sopenharmony_ci break; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* Find the next rate that is in the rate mask */ 7408c2ecf20Sopenharmony_ci i = index + 1; 7418c2ecf20Sopenharmony_ci for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { 7428c2ecf20Sopenharmony_ci if (rate_mask & mask) { 7438c2ecf20Sopenharmony_ci high = i; 7448c2ecf20Sopenharmony_ci break; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return (high << 8) | low; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci low = index; 7528c2ecf20Sopenharmony_ci while (low != IWL_RATE_INVALID) { 7538c2ecf20Sopenharmony_ci low = iwl_rates[low].prev_rs; 7548c2ecf20Sopenharmony_ci if (low == IWL_RATE_INVALID) 7558c2ecf20Sopenharmony_ci break; 7568c2ecf20Sopenharmony_ci if (rate_mask & (1 << low)) 7578c2ecf20Sopenharmony_ci break; 7588c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low); 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci high = index; 7628c2ecf20Sopenharmony_ci while (high != IWL_RATE_INVALID) { 7638c2ecf20Sopenharmony_ci high = iwl_rates[high].next_rs; 7648c2ecf20Sopenharmony_ci if (high == IWL_RATE_INVALID) 7658c2ecf20Sopenharmony_ci break; 7668c2ecf20Sopenharmony_ci if (rate_mask & (1 << high)) 7678c2ecf20Sopenharmony_ci break; 7688c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci return (high << 8) | low; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, 7758c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 7768c2ecf20Sopenharmony_ci u8 scale_index, u8 ht_possible) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci s32 low; 7798c2ecf20Sopenharmony_ci u16 rate_mask; 7808c2ecf20Sopenharmony_ci u16 high_low; 7818c2ecf20Sopenharmony_ci u8 switch_to_legacy = 0; 7828c2ecf20Sopenharmony_ci u8 is_green = lq_sta->is_green; 7838c2ecf20Sopenharmony_ci struct iwl_priv *priv = lq_sta->drv; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* check if we need to switch from HT to legacy rates. 7868c2ecf20Sopenharmony_ci * assumption is that mandatory rates (1Mbps or 6Mbps) 7878c2ecf20Sopenharmony_ci * are always supported (spec demand) */ 7888c2ecf20Sopenharmony_ci if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { 7898c2ecf20Sopenharmony_ci switch_to_legacy = 1; 7908c2ecf20Sopenharmony_ci scale_index = rs_ht_to_legacy[scale_index]; 7918c2ecf20Sopenharmony_ci if (lq_sta->band == NL80211_BAND_5GHZ) 7928c2ecf20Sopenharmony_ci tbl->lq_type = LQ_A; 7938c2ecf20Sopenharmony_ci else 7948c2ecf20Sopenharmony_ci tbl->lq_type = LQ_G; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (num_of_ant(tbl->ant_type) > 1) 7978c2ecf20Sopenharmony_ci tbl->ant_type = 7988c2ecf20Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci tbl->is_ht40 = 0; 8018c2ecf20Sopenharmony_ci tbl->is_SGI = 0; 8028c2ecf20Sopenharmony_ci tbl->max_search = IWL_MAX_SEARCH; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* Mask with station rate restriction */ 8088c2ecf20Sopenharmony_ci if (is_legacy(tbl->lq_type)) { 8098c2ecf20Sopenharmony_ci /* supp_rates has no CCK bits in A mode */ 8108c2ecf20Sopenharmony_ci if (lq_sta->band == NL80211_BAND_5GHZ) 8118c2ecf20Sopenharmony_ci rate_mask = (u16)(rate_mask & 8128c2ecf20Sopenharmony_ci (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); 8138c2ecf20Sopenharmony_ci else 8148c2ecf20Sopenharmony_ci rate_mask = (u16)(rate_mask & lq_sta->supp_rates); 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* If we switched from HT to legacy, check current rate */ 8188c2ecf20Sopenharmony_ci if (switch_to_legacy && (rate_mask & (1 << scale_index))) { 8198c2ecf20Sopenharmony_ci low = scale_index; 8208c2ecf20Sopenharmony_ci goto out; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, 8248c2ecf20Sopenharmony_ci tbl->lq_type); 8258c2ecf20Sopenharmony_ci low = high_low & 0xff; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (low == IWL_RATE_INVALID) 8288c2ecf20Sopenharmony_ci low = scale_index; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ciout: 8318c2ecf20Sopenharmony_ci return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci/* 8358c2ecf20Sopenharmony_ci * Simple function to compare two rate scale table types 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_cistatic bool table_type_matches(struct iwl_scale_tbl_info *a, 8388c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *b) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && 8418c2ecf20Sopenharmony_ci (a->is_SGI == b->is_SGI); 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, 8458c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl; 8488c2ecf20Sopenharmony_ci bool full_concurrent = priv->bt_full_concurrent; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if ((priv->bt_traffic_load != priv->last_bt_traffic_load) || 8518c2ecf20Sopenharmony_ci (priv->bt_full_concurrent != full_concurrent)) { 8528c2ecf20Sopenharmony_ci priv->bt_full_concurrent = full_concurrent; 8538c2ecf20Sopenharmony_ci priv->last_bt_traffic_load = priv->bt_traffic_load; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* Update uCode's rate table. */ 8568c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 8578c2ecf20Sopenharmony_ci rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); 8588c2ecf20Sopenharmony_ci iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->bt_full_concurrency); 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci/* 8658c2ecf20Sopenharmony_ci * mac80211 sends us Tx status 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_cistatic void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, 8688c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, void *priv_sta, 8698c2ecf20Sopenharmony_ci struct sk_buff *skb) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci int legacy_success; 8728c2ecf20Sopenharmony_ci int retries; 8738c2ecf20Sopenharmony_ci int rs_index, mac_index, i; 8748c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = priv_sta; 8758c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *table; 8768c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 8778c2ecf20Sopenharmony_ci struct iwl_op_mode *op_mode = (struct iwl_op_mode *)priv_r; 8788c2ecf20Sopenharmony_ci struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); 8798c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 8808c2ecf20Sopenharmony_ci enum mac80211_rate_control_flags mac_flags; 8818c2ecf20Sopenharmony_ci u32 tx_rate; 8828c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info tbl_type; 8838c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; 8848c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; 8858c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx = sta_priv->ctx; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* Treat uninitialized rate scaling data same as non-existing. */ 8908c2ecf20Sopenharmony_ci if (!lq_sta) { 8918c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n"); 8928c2ecf20Sopenharmony_ci return; 8938c2ecf20Sopenharmony_ci } else if (!lq_sta->drv) { 8948c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); 8958c2ecf20Sopenharmony_ci return; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (!ieee80211_is_data(hdr->frame_control) || 8998c2ecf20Sopenharmony_ci info->flags & IEEE80211_TX_CTL_NO_ACK) 9008c2ecf20Sopenharmony_ci return; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* This packet was aggregated but doesn't carry status info */ 9038c2ecf20Sopenharmony_ci if ((info->flags & IEEE80211_TX_CTL_AMPDU) && 9048c2ecf20Sopenharmony_ci !(info->flags & IEEE80211_TX_STAT_AMPDU)) 9058c2ecf20Sopenharmony_ci return; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* 9088c2ecf20Sopenharmony_ci * Ignore this Tx frame response if its initial rate doesn't match 9098c2ecf20Sopenharmony_ci * that of latest Link Quality command. There may be stragglers 9108c2ecf20Sopenharmony_ci * from a previous Link Quality command, but we're no longer interested 9118c2ecf20Sopenharmony_ci * in those; they're either from the "active" mode while we're trying 9128c2ecf20Sopenharmony_ci * to check "search" mode, or a prior "search" mode after we've moved 9138c2ecf20Sopenharmony_ci * to a new "search" mode (which might become the new "active" mode). 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci table = &lq_sta->lq; 9168c2ecf20Sopenharmony_ci tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); 9178c2ecf20Sopenharmony_ci rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); 9188c2ecf20Sopenharmony_ci if (priv->band == NL80211_BAND_5GHZ) 9198c2ecf20Sopenharmony_ci rs_index -= IWL_FIRST_OFDM_RATE; 9208c2ecf20Sopenharmony_ci mac_flags = info->status.rates[0].flags; 9218c2ecf20Sopenharmony_ci mac_index = info->status.rates[0].idx; 9228c2ecf20Sopenharmony_ci /* For HT packets, map MCS to PLCP */ 9238c2ecf20Sopenharmony_ci if (mac_flags & IEEE80211_TX_RC_MCS) { 9248c2ecf20Sopenharmony_ci mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */ 9258c2ecf20Sopenharmony_ci if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) 9268c2ecf20Sopenharmony_ci mac_index++; 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * mac80211 HT index is always zero-indexed; we need to move 9298c2ecf20Sopenharmony_ci * HT OFDM rates after CCK rates in 2.4 GHz band 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci if (priv->band == NL80211_BAND_2GHZ) 9328c2ecf20Sopenharmony_ci mac_index += IWL_FIRST_OFDM_RATE; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci /* Here we actually compare this rate to the latest LQ command */ 9358c2ecf20Sopenharmony_ci if ((mac_index < 0) || 9368c2ecf20Sopenharmony_ci (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || 9378c2ecf20Sopenharmony_ci (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || 9388c2ecf20Sopenharmony_ci (tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) || 9398c2ecf20Sopenharmony_ci (tbl_type.ant_type != info->status.antenna) || 9408c2ecf20Sopenharmony_ci (!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) || 9418c2ecf20Sopenharmony_ci (!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || 9428c2ecf20Sopenharmony_ci (rs_index != mac_index)) { 9438c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate); 9448c2ecf20Sopenharmony_ci /* 9458c2ecf20Sopenharmony_ci * Since rates mis-match, the last LQ command may have failed. 9468c2ecf20Sopenharmony_ci * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with 9478c2ecf20Sopenharmony_ci * ... driver. 9488c2ecf20Sopenharmony_ci */ 9498c2ecf20Sopenharmony_ci lq_sta->missed_rate_counter++; 9508c2ecf20Sopenharmony_ci if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { 9518c2ecf20Sopenharmony_ci lq_sta->missed_rate_counter = 0; 9528c2ecf20Sopenharmony_ci iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci /* Regardless, ignore this status info for outdated rate */ 9558c2ecf20Sopenharmony_ci return; 9568c2ecf20Sopenharmony_ci } else 9578c2ecf20Sopenharmony_ci /* Rate did match, so reset the missed_rate_counter */ 9588c2ecf20Sopenharmony_ci lq_sta->missed_rate_counter = 0; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Figure out if rate scale algorithm is in active or search table */ 9618c2ecf20Sopenharmony_ci if (table_type_matches(&tbl_type, 9628c2ecf20Sopenharmony_ci &(lq_sta->lq_info[lq_sta->active_tbl]))) { 9638c2ecf20Sopenharmony_ci curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 9648c2ecf20Sopenharmony_ci other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); 9658c2ecf20Sopenharmony_ci } else if (table_type_matches(&tbl_type, 9668c2ecf20Sopenharmony_ci &lq_sta->lq_info[1 - lq_sta->active_tbl])) { 9678c2ecf20Sopenharmony_ci curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); 9688c2ecf20Sopenharmony_ci other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 9698c2ecf20Sopenharmony_ci } else { 9708c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n"); 9718c2ecf20Sopenharmony_ci tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 9728c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n", 9738c2ecf20Sopenharmony_ci tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); 9748c2ecf20Sopenharmony_ci tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); 9758c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n", 9768c2ecf20Sopenharmony_ci tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); 9778c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n", 9788c2ecf20Sopenharmony_ci tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI); 9798c2ecf20Sopenharmony_ci /* 9808c2ecf20Sopenharmony_ci * no matching table found, let's by-pass the data collection 9818c2ecf20Sopenharmony_ci * and continue to perform rate scale to find the rate table 9828c2ecf20Sopenharmony_ci */ 9838c2ecf20Sopenharmony_ci rs_stay_in_table(lq_sta, true); 9848c2ecf20Sopenharmony_ci goto done; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci /* 9888c2ecf20Sopenharmony_ci * Updating the frame history depends on whether packets were 9898c2ecf20Sopenharmony_ci * aggregated. 9908c2ecf20Sopenharmony_ci * 9918c2ecf20Sopenharmony_ci * For aggregation, all packets were transmitted at the same rate, the 9928c2ecf20Sopenharmony_ci * first index into rate scale table. 9938c2ecf20Sopenharmony_ci */ 9948c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_STAT_AMPDU) { 9958c2ecf20Sopenharmony_ci tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); 9968c2ecf20Sopenharmony_ci rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, 9978c2ecf20Sopenharmony_ci &rs_index); 9988c2ecf20Sopenharmony_ci rs_collect_tx_data(curr_tbl, rs_index, 9998c2ecf20Sopenharmony_ci info->status.ampdu_len, 10008c2ecf20Sopenharmony_ci info->status.ampdu_ack_len); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* Update success/fail counts if not searching for new mode */ 10038c2ecf20Sopenharmony_ci if (lq_sta->stay_in_tbl) { 10048c2ecf20Sopenharmony_ci lq_sta->total_success += info->status.ampdu_ack_len; 10058c2ecf20Sopenharmony_ci lq_sta->total_failed += (info->status.ampdu_len - 10068c2ecf20Sopenharmony_ci info->status.ampdu_ack_len); 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci } else { 10098c2ecf20Sopenharmony_ci /* 10108c2ecf20Sopenharmony_ci * For legacy, update frame history with for each Tx retry. 10118c2ecf20Sopenharmony_ci */ 10128c2ecf20Sopenharmony_ci retries = info->status.rates[0].count - 1; 10138c2ecf20Sopenharmony_ci /* HW doesn't send more than 15 retries */ 10148c2ecf20Sopenharmony_ci retries = min(retries, 15); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* The last transmission may have been successful */ 10178c2ecf20Sopenharmony_ci legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); 10188c2ecf20Sopenharmony_ci /* Collect data for each rate used during failed TX attempts */ 10198c2ecf20Sopenharmony_ci for (i = 0; i <= retries; ++i) { 10208c2ecf20Sopenharmony_ci tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); 10218c2ecf20Sopenharmony_ci rs_get_tbl_info_from_mcs(tx_rate, priv->band, 10228c2ecf20Sopenharmony_ci &tbl_type, &rs_index); 10238c2ecf20Sopenharmony_ci /* 10248c2ecf20Sopenharmony_ci * Only collect stats if retried rate is in the same RS 10258c2ecf20Sopenharmony_ci * table as active/search. 10268c2ecf20Sopenharmony_ci */ 10278c2ecf20Sopenharmony_ci if (table_type_matches(&tbl_type, curr_tbl)) 10288c2ecf20Sopenharmony_ci tmp_tbl = curr_tbl; 10298c2ecf20Sopenharmony_ci else if (table_type_matches(&tbl_type, other_tbl)) 10308c2ecf20Sopenharmony_ci tmp_tbl = other_tbl; 10318c2ecf20Sopenharmony_ci else 10328c2ecf20Sopenharmony_ci continue; 10338c2ecf20Sopenharmony_ci rs_collect_tx_data(tmp_tbl, rs_index, 1, 10348c2ecf20Sopenharmony_ci i < retries ? 0 : legacy_success); 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* Update success/fail counts if not searching for new mode */ 10388c2ecf20Sopenharmony_ci if (lq_sta->stay_in_tbl) { 10398c2ecf20Sopenharmony_ci lq_sta->total_success += legacy_success; 10408c2ecf20Sopenharmony_ci lq_sta->total_failed += retries + (1 - legacy_success); 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci /* The last TX rate is cached in lq_sta; it's set in if/else above */ 10448c2ecf20Sopenharmony_ci lq_sta->last_rate_n_flags = tx_rate; 10458c2ecf20Sopenharmony_cidone: 10468c2ecf20Sopenharmony_ci /* See if there's a better rate or modulation mode to try. */ 10478c2ecf20Sopenharmony_ci if (sta && sta->supp_rates[sband->band]) 10488c2ecf20Sopenharmony_ci rs_rate_scale_perform(priv, skb, sta, lq_sta); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist) 10518c2ecf20Sopenharmony_ci rs_bt_update_lq(priv, ctx, lq_sta); 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci/* 10558c2ecf20Sopenharmony_ci * Begin a period of staying with a selected modulation mode. 10568c2ecf20Sopenharmony_ci * Set "stay_in_tbl" flag to prevent any mode switches. 10578c2ecf20Sopenharmony_ci * Set frame tx success limits according to legacy vs. high-throughput, 10588c2ecf20Sopenharmony_ci * and reset overall (spanning all rates) tx success history statistics. 10598c2ecf20Sopenharmony_ci * These control how long we stay using same modulation mode before 10608c2ecf20Sopenharmony_ci * searching for a new mode. 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_cistatic void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, 10638c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "we are staying in the same table\n"); 10668c2ecf20Sopenharmony_ci lq_sta->stay_in_tbl = 1; /* only place this gets set */ 10678c2ecf20Sopenharmony_ci if (is_legacy) { 10688c2ecf20Sopenharmony_ci lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; 10698c2ecf20Sopenharmony_ci lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; 10708c2ecf20Sopenharmony_ci lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; 10718c2ecf20Sopenharmony_ci } else { 10728c2ecf20Sopenharmony_ci lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; 10738c2ecf20Sopenharmony_ci lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; 10748c2ecf20Sopenharmony_ci lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci lq_sta->table_count = 0; 10778c2ecf20Sopenharmony_ci lq_sta->total_failed = 0; 10788c2ecf20Sopenharmony_ci lq_sta->total_success = 0; 10798c2ecf20Sopenharmony_ci lq_sta->flush_timer = jiffies; 10808c2ecf20Sopenharmony_ci lq_sta->action_counter = 0; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci/* 10848c2ecf20Sopenharmony_ci * Find correct throughput table for given mode of modulation 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_cistatic void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, 10878c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci /* Used to choose among HT tables */ 10908c2ecf20Sopenharmony_ci const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT]; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* Check for invalid LQ type */ 10938c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { 10948c2ecf20Sopenharmony_ci tbl->expected_tpt = expected_tpt_legacy; 10958c2ecf20Sopenharmony_ci return; 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* Legacy rates have only one table */ 10998c2ecf20Sopenharmony_ci if (is_legacy(tbl->lq_type)) { 11008c2ecf20Sopenharmony_ci tbl->expected_tpt = expected_tpt_legacy; 11018c2ecf20Sopenharmony_ci return; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* Choose among many HT tables depending on number of streams 11058c2ecf20Sopenharmony_ci * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation 11068c2ecf20Sopenharmony_ci * status */ 11078c2ecf20Sopenharmony_ci if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) 11088c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_siso20MHz; 11098c2ecf20Sopenharmony_ci else if (is_siso(tbl->lq_type)) 11108c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_siso40MHz; 11118c2ecf20Sopenharmony_ci else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) 11128c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_mimo2_20MHz; 11138c2ecf20Sopenharmony_ci else if (is_mimo2(tbl->lq_type)) 11148c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_mimo2_40MHz; 11158c2ecf20Sopenharmony_ci else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) 11168c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_mimo3_20MHz; 11178c2ecf20Sopenharmony_ci else /* if (is_mimo3(tbl->lq_type)) <-- must be true */ 11188c2ecf20Sopenharmony_ci ht_tbl_pointer = expected_tpt_mimo3_40MHz; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ 11218c2ecf20Sopenharmony_ci tbl->expected_tpt = ht_tbl_pointer[0]; 11228c2ecf20Sopenharmony_ci else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ 11238c2ecf20Sopenharmony_ci tbl->expected_tpt = ht_tbl_pointer[1]; 11248c2ecf20Sopenharmony_ci else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ 11258c2ecf20Sopenharmony_ci tbl->expected_tpt = ht_tbl_pointer[2]; 11268c2ecf20Sopenharmony_ci else /* AGG+SGI */ 11278c2ecf20Sopenharmony_ci tbl->expected_tpt = ht_tbl_pointer[3]; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci/* 11318c2ecf20Sopenharmony_ci * Find starting rate for new "search" high-throughput mode of modulation. 11328c2ecf20Sopenharmony_ci * Goal is to find lowest expected rate (under perfect conditions) that is 11338c2ecf20Sopenharmony_ci * above the current measured throughput of "active" mode, to give new mode 11348c2ecf20Sopenharmony_ci * a fair chance to prove itself without too many challenges. 11358c2ecf20Sopenharmony_ci * 11368c2ecf20Sopenharmony_ci * This gets called when transitioning to more aggressive modulation 11378c2ecf20Sopenharmony_ci * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive 11388c2ecf20Sopenharmony_ci * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need 11398c2ecf20Sopenharmony_ci * to decrease to match "active" throughput. When moving from MIMO to SISO, 11408c2ecf20Sopenharmony_ci * bit rate will typically need to increase, but not if performance was bad. 11418c2ecf20Sopenharmony_ci */ 11428c2ecf20Sopenharmony_cistatic s32 rs_get_best_rate(struct iwl_priv *priv, 11438c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 11448c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, /* "search" */ 11458c2ecf20Sopenharmony_ci u16 rate_mask, s8 index) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci /* "active" values */ 11488c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *active_tbl = 11498c2ecf20Sopenharmony_ci &(lq_sta->lq_info[lq_sta->active_tbl]); 11508c2ecf20Sopenharmony_ci s32 active_sr = active_tbl->win[index].success_ratio; 11518c2ecf20Sopenharmony_ci s32 active_tpt = active_tbl->expected_tpt[index]; 11528c2ecf20Sopenharmony_ci /* expected "search" throughput */ 11538c2ecf20Sopenharmony_ci const u16 *tpt_tbl = tbl->expected_tpt; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci s32 new_rate, high, low, start_hi; 11568c2ecf20Sopenharmony_ci u16 high_low; 11578c2ecf20Sopenharmony_ci s8 rate = index; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci new_rate = high = low = start_hi = IWL_RATE_INVALID; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci for (; ;) { 11628c2ecf20Sopenharmony_ci high_low = rs_get_adjacent_rate(priv, rate, rate_mask, 11638c2ecf20Sopenharmony_ci tbl->lq_type); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci low = high_low & 0xff; 11668c2ecf20Sopenharmony_ci high = (high_low >> 8) & 0xff; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci /* 11698c2ecf20Sopenharmony_ci * Lower the "search" bit rate, to give new "search" mode 11708c2ecf20Sopenharmony_ci * approximately the same throughput as "active" if: 11718c2ecf20Sopenharmony_ci * 11728c2ecf20Sopenharmony_ci * 1) "Active" mode has been working modestly well (but not 11738c2ecf20Sopenharmony_ci * great), and expected "search" throughput (under perfect 11748c2ecf20Sopenharmony_ci * conditions) at candidate rate is above the actual 11758c2ecf20Sopenharmony_ci * measured "active" throughput (but less than expected 11768c2ecf20Sopenharmony_ci * "active" throughput under perfect conditions). 11778c2ecf20Sopenharmony_ci * OR 11788c2ecf20Sopenharmony_ci * 2) "Active" mode has been working perfectly or very well 11798c2ecf20Sopenharmony_ci * and expected "search" throughput (under perfect 11808c2ecf20Sopenharmony_ci * conditions) at candidate rate is above expected 11818c2ecf20Sopenharmony_ci * "active" throughput (under perfect conditions). 11828c2ecf20Sopenharmony_ci */ 11838c2ecf20Sopenharmony_ci if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && 11848c2ecf20Sopenharmony_ci ((active_sr > IWL_RATE_DECREASE_TH) && 11858c2ecf20Sopenharmony_ci (active_sr <= IWL_RATE_HIGH_TH) && 11868c2ecf20Sopenharmony_ci (tpt_tbl[rate] <= active_tpt))) || 11878c2ecf20Sopenharmony_ci ((active_sr >= IWL_RATE_SCALE_SWITCH) && 11888c2ecf20Sopenharmony_ci (tpt_tbl[rate] > active_tpt))) { 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* (2nd or later pass) 11918c2ecf20Sopenharmony_ci * If we've already tried to raise the rate, and are 11928c2ecf20Sopenharmony_ci * now trying to lower it, use the higher rate. */ 11938c2ecf20Sopenharmony_ci if (start_hi != IWL_RATE_INVALID) { 11948c2ecf20Sopenharmony_ci new_rate = start_hi; 11958c2ecf20Sopenharmony_ci break; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci new_rate = rate; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* Loop again with lower rate */ 12018c2ecf20Sopenharmony_ci if (low != IWL_RATE_INVALID) 12028c2ecf20Sopenharmony_ci rate = low; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci /* Lower rate not available, use the original */ 12058c2ecf20Sopenharmony_ci else 12068c2ecf20Sopenharmony_ci break; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci /* Else try to raise the "search" rate to match "active" */ 12098c2ecf20Sopenharmony_ci } else { 12108c2ecf20Sopenharmony_ci /* (2nd or later pass) 12118c2ecf20Sopenharmony_ci * If we've already tried to lower the rate, and are 12128c2ecf20Sopenharmony_ci * now trying to raise it, use the lower rate. */ 12138c2ecf20Sopenharmony_ci if (new_rate != IWL_RATE_INVALID) 12148c2ecf20Sopenharmony_ci break; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci /* Loop again with higher rate */ 12178c2ecf20Sopenharmony_ci else if (high != IWL_RATE_INVALID) { 12188c2ecf20Sopenharmony_ci start_hi = high; 12198c2ecf20Sopenharmony_ci rate = high; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci /* Higher rate not available, use the original */ 12228c2ecf20Sopenharmony_ci } else { 12238c2ecf20Sopenharmony_ci new_rate = rate; 12248c2ecf20Sopenharmony_ci break; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci return new_rate; 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci/* 12338c2ecf20Sopenharmony_ci * Set up search table for MIMO2 12348c2ecf20Sopenharmony_ci */ 12358c2ecf20Sopenharmony_cistatic int rs_switch_to_mimo2(struct iwl_priv *priv, 12368c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 12378c2ecf20Sopenharmony_ci struct ieee80211_conf *conf, 12388c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 12398c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, int index) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci u16 rate_mask; 12428c2ecf20Sopenharmony_ci s32 rate; 12438c2ecf20Sopenharmony_ci s8 is_green = lq_sta->is_green; 12448c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; 12458c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx = sta_priv->ctx; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) 12488c2ecf20Sopenharmony_ci return -1; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci if (sta->smps_mode == IEEE80211_SMPS_STATIC) 12518c2ecf20Sopenharmony_ci return -1; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci /* Need both Tx chains/antennas to support MIMO */ 12548c2ecf20Sopenharmony_ci if (priv->hw_params.tx_chains_num < 2) 12558c2ecf20Sopenharmony_ci return -1; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n"); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci tbl->lq_type = LQ_MIMO2; 12608c2ecf20Sopenharmony_ci tbl->is_dup = lq_sta->is_dup; 12618c2ecf20Sopenharmony_ci tbl->action = 0; 12628c2ecf20Sopenharmony_ci tbl->max_search = IWL_MAX_SEARCH; 12638c2ecf20Sopenharmony_ci rate_mask = lq_sta->active_mimo2_rate; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) 12668c2ecf20Sopenharmony_ci tbl->is_ht40 = 1; 12678c2ecf20Sopenharmony_ci else 12688c2ecf20Sopenharmony_ci tbl->is_ht40 = 0; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, tbl); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); 12758c2ecf20Sopenharmony_ci if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { 12768c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", 12778c2ecf20Sopenharmony_ci rate, rate_mask); 12788c2ecf20Sopenharmony_ci return -1; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", 12838c2ecf20Sopenharmony_ci tbl->current_rate, is_green); 12848c2ecf20Sopenharmony_ci return 0; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci/* 12888c2ecf20Sopenharmony_ci * Set up search table for MIMO3 12898c2ecf20Sopenharmony_ci */ 12908c2ecf20Sopenharmony_cistatic int rs_switch_to_mimo3(struct iwl_priv *priv, 12918c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 12928c2ecf20Sopenharmony_ci struct ieee80211_conf *conf, 12938c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 12948c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, int index) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci u16 rate_mask; 12978c2ecf20Sopenharmony_ci s32 rate; 12988c2ecf20Sopenharmony_ci s8 is_green = lq_sta->is_green; 12998c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; 13008c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx = sta_priv->ctx; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) 13038c2ecf20Sopenharmony_ci return -1; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci if (sta->smps_mode == IEEE80211_SMPS_STATIC) 13068c2ecf20Sopenharmony_ci return -1; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* Need both Tx chains/antennas to support MIMO */ 13098c2ecf20Sopenharmony_ci if (priv->hw_params.tx_chains_num < 3) 13108c2ecf20Sopenharmony_ci return -1; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO3\n"); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci tbl->lq_type = LQ_MIMO3; 13158c2ecf20Sopenharmony_ci tbl->is_dup = lq_sta->is_dup; 13168c2ecf20Sopenharmony_ci tbl->action = 0; 13178c2ecf20Sopenharmony_ci tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; 13188c2ecf20Sopenharmony_ci rate_mask = lq_sta->active_mimo3_rate; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) 13218c2ecf20Sopenharmony_ci tbl->is_ht40 = 1; 13228c2ecf20Sopenharmony_ci else 13238c2ecf20Sopenharmony_ci tbl->is_ht40 = 0; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, tbl); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO3 best rate %d mask %X\n", 13308c2ecf20Sopenharmony_ci rate, rate_mask); 13318c2ecf20Sopenharmony_ci if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { 13328c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", 13338c2ecf20Sopenharmony_ci rate, rate_mask); 13348c2ecf20Sopenharmony_ci return -1; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", 13398c2ecf20Sopenharmony_ci tbl->current_rate, is_green); 13408c2ecf20Sopenharmony_ci return 0; 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci/* 13448c2ecf20Sopenharmony_ci * Set up search table for SISO 13458c2ecf20Sopenharmony_ci */ 13468c2ecf20Sopenharmony_cistatic int rs_switch_to_siso(struct iwl_priv *priv, 13478c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 13488c2ecf20Sopenharmony_ci struct ieee80211_conf *conf, 13498c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 13508c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, int index) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci u16 rate_mask; 13538c2ecf20Sopenharmony_ci u8 is_green = lq_sta->is_green; 13548c2ecf20Sopenharmony_ci s32 rate; 13558c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; 13568c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx = sta_priv->ctx; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) 13598c2ecf20Sopenharmony_ci return -1; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: try to switch to SISO\n"); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci tbl->is_dup = lq_sta->is_dup; 13648c2ecf20Sopenharmony_ci tbl->lq_type = LQ_SISO; 13658c2ecf20Sopenharmony_ci tbl->action = 0; 13668c2ecf20Sopenharmony_ci tbl->max_search = IWL_MAX_SEARCH; 13678c2ecf20Sopenharmony_ci rate_mask = lq_sta->active_siso_rate; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) 13708c2ecf20Sopenharmony_ci tbl->is_ht40 = 1; 13718c2ecf20Sopenharmony_ci else 13728c2ecf20Sopenharmony_ci tbl->is_ht40 = 0; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci if (is_green) 13758c2ecf20Sopenharmony_ci tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, tbl); 13788c2ecf20Sopenharmony_ci rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: get best rate %d mask %X\n", rate, rate_mask); 13818c2ecf20Sopenharmony_ci if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { 13828c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "can not switch with index %d rate mask %x\n", 13838c2ecf20Sopenharmony_ci rate, rate_mask); 13848c2ecf20Sopenharmony_ci return -1; 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); 13878c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", 13888c2ecf20Sopenharmony_ci tbl->current_rate, is_green); 13898c2ecf20Sopenharmony_ci return 0; 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci/* 13938c2ecf20Sopenharmony_ci * Try to switch to new modulation mode from legacy 13948c2ecf20Sopenharmony_ci */ 13958c2ecf20Sopenharmony_cistatic void rs_move_legacy_other(struct iwl_priv *priv, 13968c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 13978c2ecf20Sopenharmony_ci struct ieee80211_conf *conf, 13988c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 13998c2ecf20Sopenharmony_ci int index) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 14028c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *search_tbl = 14038c2ecf20Sopenharmony_ci &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); 14048c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window = &(tbl->win[index]); 14058c2ecf20Sopenharmony_ci u32 sz = (sizeof(struct iwl_scale_tbl_info) - 14068c2ecf20Sopenharmony_ci (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); 14078c2ecf20Sopenharmony_ci u8 start_action; 14088c2ecf20Sopenharmony_ci u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; 14098c2ecf20Sopenharmony_ci u8 tx_chains_num = priv->hw_params.tx_chains_num; 14108c2ecf20Sopenharmony_ci int ret = 0; 14118c2ecf20Sopenharmony_ci u8 update_search_tbl_counter = 0; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci switch (priv->bt_traffic_load) { 14148c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_NONE: 14158c2ecf20Sopenharmony_ci /* nothing */ 14168c2ecf20Sopenharmony_ci break; 14178c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_LOW: 14188c2ecf20Sopenharmony_ci /* avoid antenna B unless MIMO */ 14198c2ecf20Sopenharmony_ci if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2) 14208c2ecf20Sopenharmony_ci tbl->action = IWL_LEGACY_SWITCH_SISO; 14218c2ecf20Sopenharmony_ci break; 14228c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: 14238c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: 14248c2ecf20Sopenharmony_ci /* avoid antenna B and MIMO */ 14258c2ecf20Sopenharmony_ci valid_tx_ant = 14268c2ecf20Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 14278c2ecf20Sopenharmony_ci if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 && 14288c2ecf20Sopenharmony_ci tbl->action != IWL_LEGACY_SWITCH_SISO) 14298c2ecf20Sopenharmony_ci tbl->action = IWL_LEGACY_SWITCH_SISO; 14308c2ecf20Sopenharmony_ci break; 14318c2ecf20Sopenharmony_ci default: 14328c2ecf20Sopenharmony_ci IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); 14338c2ecf20Sopenharmony_ci break; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci if (!iwl_ht_enabled(priv)) 14378c2ecf20Sopenharmony_ci /* stay in Legacy */ 14388c2ecf20Sopenharmony_ci tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; 14398c2ecf20Sopenharmony_ci else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && 14408c2ecf20Sopenharmony_ci tbl->action > IWL_LEGACY_SWITCH_SISO) 14418c2ecf20Sopenharmony_ci tbl->action = IWL_LEGACY_SWITCH_SISO; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci /* configure as 1x1 if bt full concurrency */ 14448c2ecf20Sopenharmony_ci if (priv->bt_full_concurrent) { 14458c2ecf20Sopenharmony_ci if (!iwl_ht_enabled(priv)) 14468c2ecf20Sopenharmony_ci tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; 14478c2ecf20Sopenharmony_ci else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) 14488c2ecf20Sopenharmony_ci tbl->action = IWL_LEGACY_SWITCH_SISO; 14498c2ecf20Sopenharmony_ci valid_tx_ant = 14508c2ecf20Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci start_action = tbl->action; 14548c2ecf20Sopenharmony_ci for (; ;) { 14558c2ecf20Sopenharmony_ci lq_sta->action_counter++; 14568c2ecf20Sopenharmony_ci switch (tbl->action) { 14578c2ecf20Sopenharmony_ci case IWL_LEGACY_SWITCH_ANTENNA1: 14588c2ecf20Sopenharmony_ci case IWL_LEGACY_SWITCH_ANTENNA2: 14598c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: Legacy toggle Antenna\n"); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 && 14628c2ecf20Sopenharmony_ci tx_chains_num <= 1) || 14638c2ecf20Sopenharmony_ci (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 && 14648c2ecf20Sopenharmony_ci tx_chains_num <= 2)) 14658c2ecf20Sopenharmony_ci break; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci /* Don't change antenna if success has been great */ 14688c2ecf20Sopenharmony_ci if (window->success_ratio >= IWL_RS_GOOD_RATIO && 14698c2ecf20Sopenharmony_ci !priv->bt_full_concurrent && 14708c2ecf20Sopenharmony_ci priv->bt_traffic_load == 14718c2ecf20Sopenharmony_ci IWL_BT_COEX_TRAFFIC_LOAD_NONE) 14728c2ecf20Sopenharmony_ci break; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci /* Set up search table to try other antenna */ 14758c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (rs_toggle_antenna(valid_tx_ant, 14788c2ecf20Sopenharmony_ci &search_tbl->current_rate, search_tbl)) { 14798c2ecf20Sopenharmony_ci update_search_tbl_counter = 1; 14808c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, search_tbl); 14818c2ecf20Sopenharmony_ci goto out; 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci break; 14848c2ecf20Sopenharmony_ci case IWL_LEGACY_SWITCH_SISO: 14858c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: Legacy switch to SISO\n"); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci /* Set up search table to try SISO */ 14888c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 14898c2ecf20Sopenharmony_ci search_tbl->is_SGI = 0; 14908c2ecf20Sopenharmony_ci ret = rs_switch_to_siso(priv, lq_sta, conf, sta, 14918c2ecf20Sopenharmony_ci search_tbl, index); 14928c2ecf20Sopenharmony_ci if (!ret) { 14938c2ecf20Sopenharmony_ci lq_sta->action_counter = 0; 14948c2ecf20Sopenharmony_ci goto out; 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci break; 14988c2ecf20Sopenharmony_ci case IWL_LEGACY_SWITCH_MIMO2_AB: 14998c2ecf20Sopenharmony_ci case IWL_LEGACY_SWITCH_MIMO2_AC: 15008c2ecf20Sopenharmony_ci case IWL_LEGACY_SWITCH_MIMO2_BC: 15018c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO2\n"); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci /* Set up search table to try MIMO */ 15048c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 15058c2ecf20Sopenharmony_ci search_tbl->is_SGI = 0; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB) 15088c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_AB; 15098c2ecf20Sopenharmony_ci else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC) 15108c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_AC; 15118c2ecf20Sopenharmony_ci else 15128c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_BC; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) 15158c2ecf20Sopenharmony_ci break; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, 15188c2ecf20Sopenharmony_ci search_tbl, index); 15198c2ecf20Sopenharmony_ci if (!ret) { 15208c2ecf20Sopenharmony_ci lq_sta->action_counter = 0; 15218c2ecf20Sopenharmony_ci goto out; 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci break; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci case IWL_LEGACY_SWITCH_MIMO3_ABC: 15268c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO3\n"); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci /* Set up search table to try MIMO3 */ 15298c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 15308c2ecf20Sopenharmony_ci search_tbl->is_SGI = 0; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_ABC; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) 15358c2ecf20Sopenharmony_ci break; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, 15388c2ecf20Sopenharmony_ci search_tbl, index); 15398c2ecf20Sopenharmony_ci if (!ret) { 15408c2ecf20Sopenharmony_ci lq_sta->action_counter = 0; 15418c2ecf20Sopenharmony_ci goto out; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci break; 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci tbl->action++; 15468c2ecf20Sopenharmony_ci if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) 15478c2ecf20Sopenharmony_ci tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci if (tbl->action == start_action) 15508c2ecf20Sopenharmony_ci break; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci search_tbl->lq_type = LQ_NONE; 15548c2ecf20Sopenharmony_ci return; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ciout: 15578c2ecf20Sopenharmony_ci lq_sta->search_better_tbl = 1; 15588c2ecf20Sopenharmony_ci tbl->action++; 15598c2ecf20Sopenharmony_ci if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) 15608c2ecf20Sopenharmony_ci tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; 15618c2ecf20Sopenharmony_ci if (update_search_tbl_counter) 15628c2ecf20Sopenharmony_ci search_tbl->action = tbl->action; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci/* 15668c2ecf20Sopenharmony_ci * Try to switch to new modulation mode from SISO 15678c2ecf20Sopenharmony_ci */ 15688c2ecf20Sopenharmony_cistatic void rs_move_siso_to_other(struct iwl_priv *priv, 15698c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 15708c2ecf20Sopenharmony_ci struct ieee80211_conf *conf, 15718c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, int index) 15728c2ecf20Sopenharmony_ci{ 15738c2ecf20Sopenharmony_ci u8 is_green = lq_sta->is_green; 15748c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 15758c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *search_tbl = 15768c2ecf20Sopenharmony_ci &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); 15778c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window = &(tbl->win[index]); 15788c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 15798c2ecf20Sopenharmony_ci u32 sz = (sizeof(struct iwl_scale_tbl_info) - 15808c2ecf20Sopenharmony_ci (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); 15818c2ecf20Sopenharmony_ci u8 start_action; 15828c2ecf20Sopenharmony_ci u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; 15838c2ecf20Sopenharmony_ci u8 tx_chains_num = priv->hw_params.tx_chains_num; 15848c2ecf20Sopenharmony_ci u8 update_search_tbl_counter = 0; 15858c2ecf20Sopenharmony_ci int ret; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci switch (priv->bt_traffic_load) { 15888c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_NONE: 15898c2ecf20Sopenharmony_ci /* nothing */ 15908c2ecf20Sopenharmony_ci break; 15918c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_LOW: 15928c2ecf20Sopenharmony_ci /* avoid antenna B unless MIMO */ 15938c2ecf20Sopenharmony_ci if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) 15948c2ecf20Sopenharmony_ci tbl->action = IWL_SISO_SWITCH_MIMO2_AB; 15958c2ecf20Sopenharmony_ci break; 15968c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: 15978c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: 15988c2ecf20Sopenharmony_ci /* avoid antenna B and MIMO */ 15998c2ecf20Sopenharmony_ci valid_tx_ant = 16008c2ecf20Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 16018c2ecf20Sopenharmony_ci if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) 16028c2ecf20Sopenharmony_ci tbl->action = IWL_SISO_SWITCH_ANTENNA1; 16038c2ecf20Sopenharmony_ci break; 16048c2ecf20Sopenharmony_ci default: 16058c2ecf20Sopenharmony_ci IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); 16068c2ecf20Sopenharmony_ci break; 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && 16108c2ecf20Sopenharmony_ci tbl->action > IWL_SISO_SWITCH_ANTENNA2) { 16118c2ecf20Sopenharmony_ci /* stay in SISO */ 16128c2ecf20Sopenharmony_ci tbl->action = IWL_SISO_SWITCH_ANTENNA1; 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci /* configure as 1x1 if bt full concurrency */ 16168c2ecf20Sopenharmony_ci if (priv->bt_full_concurrent) { 16178c2ecf20Sopenharmony_ci valid_tx_ant = 16188c2ecf20Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 16198c2ecf20Sopenharmony_ci if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) 16208c2ecf20Sopenharmony_ci tbl->action = IWL_SISO_SWITCH_ANTENNA1; 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci start_action = tbl->action; 16248c2ecf20Sopenharmony_ci for (;;) { 16258c2ecf20Sopenharmony_ci lq_sta->action_counter++; 16268c2ecf20Sopenharmony_ci switch (tbl->action) { 16278c2ecf20Sopenharmony_ci case IWL_SISO_SWITCH_ANTENNA1: 16288c2ecf20Sopenharmony_ci case IWL_SISO_SWITCH_ANTENNA2: 16298c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: SISO toggle Antenna\n"); 16308c2ecf20Sopenharmony_ci if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 && 16318c2ecf20Sopenharmony_ci tx_chains_num <= 1) || 16328c2ecf20Sopenharmony_ci (tbl->action == IWL_SISO_SWITCH_ANTENNA2 && 16338c2ecf20Sopenharmony_ci tx_chains_num <= 2)) 16348c2ecf20Sopenharmony_ci break; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci if (window->success_ratio >= IWL_RS_GOOD_RATIO && 16378c2ecf20Sopenharmony_ci !priv->bt_full_concurrent && 16388c2ecf20Sopenharmony_ci priv->bt_traffic_load == 16398c2ecf20Sopenharmony_ci IWL_BT_COEX_TRAFFIC_LOAD_NONE) 16408c2ecf20Sopenharmony_ci break; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 16438c2ecf20Sopenharmony_ci if (rs_toggle_antenna(valid_tx_ant, 16448c2ecf20Sopenharmony_ci &search_tbl->current_rate, search_tbl)) { 16458c2ecf20Sopenharmony_ci update_search_tbl_counter = 1; 16468c2ecf20Sopenharmony_ci goto out; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci break; 16498c2ecf20Sopenharmony_ci case IWL_SISO_SWITCH_MIMO2_AB: 16508c2ecf20Sopenharmony_ci case IWL_SISO_SWITCH_MIMO2_AC: 16518c2ecf20Sopenharmony_ci case IWL_SISO_SWITCH_MIMO2_BC: 16528c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO2\n"); 16538c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 16548c2ecf20Sopenharmony_ci search_tbl->is_SGI = 0; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB) 16578c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_AB; 16588c2ecf20Sopenharmony_ci else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC) 16598c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_AC; 16608c2ecf20Sopenharmony_ci else 16618c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_BC; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) 16648c2ecf20Sopenharmony_ci break; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, 16678c2ecf20Sopenharmony_ci search_tbl, index); 16688c2ecf20Sopenharmony_ci if (!ret) 16698c2ecf20Sopenharmony_ci goto out; 16708c2ecf20Sopenharmony_ci break; 16718c2ecf20Sopenharmony_ci case IWL_SISO_SWITCH_GI: 16728c2ecf20Sopenharmony_ci if (!tbl->is_ht40 && !(ht_cap->cap & 16738c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_20)) 16748c2ecf20Sopenharmony_ci break; 16758c2ecf20Sopenharmony_ci if (tbl->is_ht40 && !(ht_cap->cap & 16768c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_40)) 16778c2ecf20Sopenharmony_ci break; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n"); 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 16828c2ecf20Sopenharmony_ci if (is_green) { 16838c2ecf20Sopenharmony_ci if (!tbl->is_SGI) 16848c2ecf20Sopenharmony_ci break; 16858c2ecf20Sopenharmony_ci else 16868c2ecf20Sopenharmony_ci IWL_ERR(priv, 16878c2ecf20Sopenharmony_ci "SGI was set in GF+SISO\n"); 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci search_tbl->is_SGI = !tbl->is_SGI; 16908c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, search_tbl); 16918c2ecf20Sopenharmony_ci if (tbl->is_SGI) { 16928c2ecf20Sopenharmony_ci s32 tpt = lq_sta->last_tpt / 100; 16938c2ecf20Sopenharmony_ci if (tpt >= search_tbl->expected_tpt[index]) 16948c2ecf20Sopenharmony_ci break; 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci search_tbl->current_rate = 16978c2ecf20Sopenharmony_ci rate_n_flags_from_tbl(priv, search_tbl, 16988c2ecf20Sopenharmony_ci index, is_green); 16998c2ecf20Sopenharmony_ci update_search_tbl_counter = 1; 17008c2ecf20Sopenharmony_ci goto out; 17018c2ecf20Sopenharmony_ci case IWL_SISO_SWITCH_MIMO3_ABC: 17028c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO3\n"); 17038c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 17048c2ecf20Sopenharmony_ci search_tbl->is_SGI = 0; 17058c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_ABC; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) 17088c2ecf20Sopenharmony_ci break; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, 17118c2ecf20Sopenharmony_ci search_tbl, index); 17128c2ecf20Sopenharmony_ci if (!ret) 17138c2ecf20Sopenharmony_ci goto out; 17148c2ecf20Sopenharmony_ci break; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci tbl->action++; 17178c2ecf20Sopenharmony_ci if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) 17188c2ecf20Sopenharmony_ci tbl->action = IWL_SISO_SWITCH_ANTENNA1; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci if (tbl->action == start_action) 17218c2ecf20Sopenharmony_ci break; 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci search_tbl->lq_type = LQ_NONE; 17248c2ecf20Sopenharmony_ci return; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci out: 17278c2ecf20Sopenharmony_ci lq_sta->search_better_tbl = 1; 17288c2ecf20Sopenharmony_ci tbl->action++; 17298c2ecf20Sopenharmony_ci if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC) 17308c2ecf20Sopenharmony_ci tbl->action = IWL_SISO_SWITCH_ANTENNA1; 17318c2ecf20Sopenharmony_ci if (update_search_tbl_counter) 17328c2ecf20Sopenharmony_ci search_tbl->action = tbl->action; 17338c2ecf20Sopenharmony_ci} 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci/* 17368c2ecf20Sopenharmony_ci * Try to switch to new modulation mode from MIMO2 17378c2ecf20Sopenharmony_ci */ 17388c2ecf20Sopenharmony_cistatic void rs_move_mimo2_to_other(struct iwl_priv *priv, 17398c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 17408c2ecf20Sopenharmony_ci struct ieee80211_conf *conf, 17418c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, int index) 17428c2ecf20Sopenharmony_ci{ 17438c2ecf20Sopenharmony_ci s8 is_green = lq_sta->is_green; 17448c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 17458c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *search_tbl = 17468c2ecf20Sopenharmony_ci &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); 17478c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window = &(tbl->win[index]); 17488c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 17498c2ecf20Sopenharmony_ci u32 sz = (sizeof(struct iwl_scale_tbl_info) - 17508c2ecf20Sopenharmony_ci (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); 17518c2ecf20Sopenharmony_ci u8 start_action; 17528c2ecf20Sopenharmony_ci u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; 17538c2ecf20Sopenharmony_ci u8 tx_chains_num = priv->hw_params.tx_chains_num; 17548c2ecf20Sopenharmony_ci u8 update_search_tbl_counter = 0; 17558c2ecf20Sopenharmony_ci int ret; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci switch (priv->bt_traffic_load) { 17588c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_NONE: 17598c2ecf20Sopenharmony_ci /* nothing */ 17608c2ecf20Sopenharmony_ci break; 17618c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: 17628c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: 17638c2ecf20Sopenharmony_ci /* avoid antenna B and MIMO */ 17648c2ecf20Sopenharmony_ci if (tbl->action != IWL_MIMO2_SWITCH_SISO_A) 17658c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO2_SWITCH_SISO_A; 17668c2ecf20Sopenharmony_ci break; 17678c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_LOW: 17688c2ecf20Sopenharmony_ci /* avoid antenna B unless MIMO */ 17698c2ecf20Sopenharmony_ci if (tbl->action == IWL_MIMO2_SWITCH_SISO_B || 17708c2ecf20Sopenharmony_ci tbl->action == IWL_MIMO2_SWITCH_SISO_C) 17718c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO2_SWITCH_SISO_A; 17728c2ecf20Sopenharmony_ci break; 17738c2ecf20Sopenharmony_ci default: 17748c2ecf20Sopenharmony_ci IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); 17758c2ecf20Sopenharmony_ci break; 17768c2ecf20Sopenharmony_ci } 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && 17798c2ecf20Sopenharmony_ci (tbl->action < IWL_MIMO2_SWITCH_SISO_A || 17808c2ecf20Sopenharmony_ci tbl->action > IWL_MIMO2_SWITCH_SISO_C)) { 17818c2ecf20Sopenharmony_ci /* switch in SISO */ 17828c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO2_SWITCH_SISO_A; 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci /* configure as 1x1 if bt full concurrency */ 17868c2ecf20Sopenharmony_ci if (priv->bt_full_concurrent && 17878c2ecf20Sopenharmony_ci (tbl->action < IWL_MIMO2_SWITCH_SISO_A || 17888c2ecf20Sopenharmony_ci tbl->action > IWL_MIMO2_SWITCH_SISO_C)) 17898c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO2_SWITCH_SISO_A; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci start_action = tbl->action; 17928c2ecf20Sopenharmony_ci for (;;) { 17938c2ecf20Sopenharmony_ci lq_sta->action_counter++; 17948c2ecf20Sopenharmony_ci switch (tbl->action) { 17958c2ecf20Sopenharmony_ci case IWL_MIMO2_SWITCH_ANTENNA1: 17968c2ecf20Sopenharmony_ci case IWL_MIMO2_SWITCH_ANTENNA2: 17978c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle Antennas\n"); 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci if (tx_chains_num <= 2) 18008c2ecf20Sopenharmony_ci break; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci if (window->success_ratio >= IWL_RS_GOOD_RATIO) 18038c2ecf20Sopenharmony_ci break; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 18068c2ecf20Sopenharmony_ci if (rs_toggle_antenna(valid_tx_ant, 18078c2ecf20Sopenharmony_ci &search_tbl->current_rate, search_tbl)) { 18088c2ecf20Sopenharmony_ci update_search_tbl_counter = 1; 18098c2ecf20Sopenharmony_ci goto out; 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci break; 18128c2ecf20Sopenharmony_ci case IWL_MIMO2_SWITCH_SISO_A: 18138c2ecf20Sopenharmony_ci case IWL_MIMO2_SWITCH_SISO_B: 18148c2ecf20Sopenharmony_ci case IWL_MIMO2_SWITCH_SISO_C: 18158c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to SISO\n"); 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci /* Set up new search table for SISO */ 18188c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) 18218c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_A; 18228c2ecf20Sopenharmony_ci else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) 18238c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_B; 18248c2ecf20Sopenharmony_ci else 18258c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_C; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) 18288c2ecf20Sopenharmony_ci break; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci ret = rs_switch_to_siso(priv, lq_sta, conf, sta, 18318c2ecf20Sopenharmony_ci search_tbl, index); 18328c2ecf20Sopenharmony_ci if (!ret) 18338c2ecf20Sopenharmony_ci goto out; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci break; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci case IWL_MIMO2_SWITCH_GI: 18388c2ecf20Sopenharmony_ci if (!tbl->is_ht40 && !(ht_cap->cap & 18398c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_20)) 18408c2ecf20Sopenharmony_ci break; 18418c2ecf20Sopenharmony_ci if (tbl->is_ht40 && !(ht_cap->cap & 18428c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_40)) 18438c2ecf20Sopenharmony_ci break; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n"); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci /* Set up new search table for MIMO2 */ 18488c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 18498c2ecf20Sopenharmony_ci search_tbl->is_SGI = !tbl->is_SGI; 18508c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, search_tbl); 18518c2ecf20Sopenharmony_ci /* 18528c2ecf20Sopenharmony_ci * If active table already uses the fastest possible 18538c2ecf20Sopenharmony_ci * modulation (dual stream with short guard interval), 18548c2ecf20Sopenharmony_ci * and it's working well, there's no need to look 18558c2ecf20Sopenharmony_ci * for a better type of modulation! 18568c2ecf20Sopenharmony_ci */ 18578c2ecf20Sopenharmony_ci if (tbl->is_SGI) { 18588c2ecf20Sopenharmony_ci s32 tpt = lq_sta->last_tpt / 100; 18598c2ecf20Sopenharmony_ci if (tpt >= search_tbl->expected_tpt[index]) 18608c2ecf20Sopenharmony_ci break; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci search_tbl->current_rate = 18638c2ecf20Sopenharmony_ci rate_n_flags_from_tbl(priv, search_tbl, 18648c2ecf20Sopenharmony_ci index, is_green); 18658c2ecf20Sopenharmony_ci update_search_tbl_counter = 1; 18668c2ecf20Sopenharmony_ci goto out; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci case IWL_MIMO2_SWITCH_MIMO3_ABC: 18698c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to MIMO3\n"); 18708c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 18718c2ecf20Sopenharmony_ci search_tbl->is_SGI = 0; 18728c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_ABC; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) 18758c2ecf20Sopenharmony_ci break; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, 18788c2ecf20Sopenharmony_ci search_tbl, index); 18798c2ecf20Sopenharmony_ci if (!ret) 18808c2ecf20Sopenharmony_ci goto out; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci break; 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci tbl->action++; 18858c2ecf20Sopenharmony_ci if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) 18868c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci if (tbl->action == start_action) 18898c2ecf20Sopenharmony_ci break; 18908c2ecf20Sopenharmony_ci } 18918c2ecf20Sopenharmony_ci search_tbl->lq_type = LQ_NONE; 18928c2ecf20Sopenharmony_ci return; 18938c2ecf20Sopenharmony_ci out: 18948c2ecf20Sopenharmony_ci lq_sta->search_better_tbl = 1; 18958c2ecf20Sopenharmony_ci tbl->action++; 18968c2ecf20Sopenharmony_ci if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) 18978c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; 18988c2ecf20Sopenharmony_ci if (update_search_tbl_counter) 18998c2ecf20Sopenharmony_ci search_tbl->action = tbl->action; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci} 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci/* 19048c2ecf20Sopenharmony_ci * Try to switch to new modulation mode from MIMO3 19058c2ecf20Sopenharmony_ci */ 19068c2ecf20Sopenharmony_cistatic void rs_move_mimo3_to_other(struct iwl_priv *priv, 19078c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 19088c2ecf20Sopenharmony_ci struct ieee80211_conf *conf, 19098c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, int index) 19108c2ecf20Sopenharmony_ci{ 19118c2ecf20Sopenharmony_ci s8 is_green = lq_sta->is_green; 19128c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 19138c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *search_tbl = 19148c2ecf20Sopenharmony_ci &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); 19158c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window = &(tbl->win[index]); 19168c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 19178c2ecf20Sopenharmony_ci u32 sz = (sizeof(struct iwl_scale_tbl_info) - 19188c2ecf20Sopenharmony_ci (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); 19198c2ecf20Sopenharmony_ci u8 start_action; 19208c2ecf20Sopenharmony_ci u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; 19218c2ecf20Sopenharmony_ci u8 tx_chains_num = priv->hw_params.tx_chains_num; 19228c2ecf20Sopenharmony_ci int ret; 19238c2ecf20Sopenharmony_ci u8 update_search_tbl_counter = 0; 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci switch (priv->bt_traffic_load) { 19268c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_NONE: 19278c2ecf20Sopenharmony_ci /* nothing */ 19288c2ecf20Sopenharmony_ci break; 19298c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: 19308c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: 19318c2ecf20Sopenharmony_ci /* avoid antenna B and MIMO */ 19328c2ecf20Sopenharmony_ci if (tbl->action != IWL_MIMO3_SWITCH_SISO_A) 19338c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO3_SWITCH_SISO_A; 19348c2ecf20Sopenharmony_ci break; 19358c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_LOW: 19368c2ecf20Sopenharmony_ci /* avoid antenna B unless MIMO */ 19378c2ecf20Sopenharmony_ci if (tbl->action == IWL_MIMO3_SWITCH_SISO_B || 19388c2ecf20Sopenharmony_ci tbl->action == IWL_MIMO3_SWITCH_SISO_C) 19398c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO3_SWITCH_SISO_A; 19408c2ecf20Sopenharmony_ci break; 19418c2ecf20Sopenharmony_ci default: 19428c2ecf20Sopenharmony_ci IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); 19438c2ecf20Sopenharmony_ci break; 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && 19478c2ecf20Sopenharmony_ci (tbl->action < IWL_MIMO3_SWITCH_SISO_A || 19488c2ecf20Sopenharmony_ci tbl->action > IWL_MIMO3_SWITCH_SISO_C)) { 19498c2ecf20Sopenharmony_ci /* switch in SISO */ 19508c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO3_SWITCH_SISO_A; 19518c2ecf20Sopenharmony_ci } 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci /* configure as 1x1 if bt full concurrency */ 19548c2ecf20Sopenharmony_ci if (priv->bt_full_concurrent && 19558c2ecf20Sopenharmony_ci (tbl->action < IWL_MIMO3_SWITCH_SISO_A || 19568c2ecf20Sopenharmony_ci tbl->action > IWL_MIMO3_SWITCH_SISO_C)) 19578c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO3_SWITCH_SISO_A; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci start_action = tbl->action; 19608c2ecf20Sopenharmony_ci for (;;) { 19618c2ecf20Sopenharmony_ci lq_sta->action_counter++; 19628c2ecf20Sopenharmony_ci switch (tbl->action) { 19638c2ecf20Sopenharmony_ci case IWL_MIMO3_SWITCH_ANTENNA1: 19648c2ecf20Sopenharmony_ci case IWL_MIMO3_SWITCH_ANTENNA2: 19658c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle Antennas\n"); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci if (tx_chains_num <= 3) 19688c2ecf20Sopenharmony_ci break; 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci if (window->success_ratio >= IWL_RS_GOOD_RATIO) 19718c2ecf20Sopenharmony_ci break; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 19748c2ecf20Sopenharmony_ci if (rs_toggle_antenna(valid_tx_ant, 19758c2ecf20Sopenharmony_ci &search_tbl->current_rate, search_tbl)) 19768c2ecf20Sopenharmony_ci goto out; 19778c2ecf20Sopenharmony_ci break; 19788c2ecf20Sopenharmony_ci case IWL_MIMO3_SWITCH_SISO_A: 19798c2ecf20Sopenharmony_ci case IWL_MIMO3_SWITCH_SISO_B: 19808c2ecf20Sopenharmony_ci case IWL_MIMO3_SWITCH_SISO_C: 19818c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to SISO\n"); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci /* Set up new search table for SISO */ 19848c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci if (tbl->action == IWL_MIMO3_SWITCH_SISO_A) 19878c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_A; 19888c2ecf20Sopenharmony_ci else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B) 19898c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_B; 19908c2ecf20Sopenharmony_ci else 19918c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_C; 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) 19948c2ecf20Sopenharmony_ci break; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci ret = rs_switch_to_siso(priv, lq_sta, conf, sta, 19978c2ecf20Sopenharmony_ci search_tbl, index); 19988c2ecf20Sopenharmony_ci if (!ret) 19998c2ecf20Sopenharmony_ci goto out; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci break; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci case IWL_MIMO3_SWITCH_MIMO2_AB: 20048c2ecf20Sopenharmony_ci case IWL_MIMO3_SWITCH_MIMO2_AC: 20058c2ecf20Sopenharmony_ci case IWL_MIMO3_SWITCH_MIMO2_BC: 20068c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to MIMO2\n"); 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 20098c2ecf20Sopenharmony_ci search_tbl->is_SGI = 0; 20108c2ecf20Sopenharmony_ci if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB) 20118c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_AB; 20128c2ecf20Sopenharmony_ci else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC) 20138c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_AC; 20148c2ecf20Sopenharmony_ci else 20158c2ecf20Sopenharmony_ci search_tbl->ant_type = ANT_BC; 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) 20188c2ecf20Sopenharmony_ci break; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, 20218c2ecf20Sopenharmony_ci search_tbl, index); 20228c2ecf20Sopenharmony_ci if (!ret) 20238c2ecf20Sopenharmony_ci goto out; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci break; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci case IWL_MIMO3_SWITCH_GI: 20288c2ecf20Sopenharmony_ci if (!tbl->is_ht40 && !(ht_cap->cap & 20298c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_20)) 20308c2ecf20Sopenharmony_ci break; 20318c2ecf20Sopenharmony_ci if (tbl->is_ht40 && !(ht_cap->cap & 20328c2ecf20Sopenharmony_ci IEEE80211_HT_CAP_SGI_40)) 20338c2ecf20Sopenharmony_ci break; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n"); 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci /* Set up new search table for MIMO */ 20388c2ecf20Sopenharmony_ci memcpy(search_tbl, tbl, sz); 20398c2ecf20Sopenharmony_ci search_tbl->is_SGI = !tbl->is_SGI; 20408c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, search_tbl); 20418c2ecf20Sopenharmony_ci /* 20428c2ecf20Sopenharmony_ci * If active table already uses the fastest possible 20438c2ecf20Sopenharmony_ci * modulation (dual stream with short guard interval), 20448c2ecf20Sopenharmony_ci * and it's working well, there's no need to look 20458c2ecf20Sopenharmony_ci * for a better type of modulation! 20468c2ecf20Sopenharmony_ci */ 20478c2ecf20Sopenharmony_ci if (tbl->is_SGI) { 20488c2ecf20Sopenharmony_ci s32 tpt = lq_sta->last_tpt / 100; 20498c2ecf20Sopenharmony_ci if (tpt >= search_tbl->expected_tpt[index]) 20508c2ecf20Sopenharmony_ci break; 20518c2ecf20Sopenharmony_ci } 20528c2ecf20Sopenharmony_ci search_tbl->current_rate = 20538c2ecf20Sopenharmony_ci rate_n_flags_from_tbl(priv, search_tbl, 20548c2ecf20Sopenharmony_ci index, is_green); 20558c2ecf20Sopenharmony_ci update_search_tbl_counter = 1; 20568c2ecf20Sopenharmony_ci goto out; 20578c2ecf20Sopenharmony_ci } 20588c2ecf20Sopenharmony_ci tbl->action++; 20598c2ecf20Sopenharmony_ci if (tbl->action > IWL_MIMO3_SWITCH_GI) 20608c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci if (tbl->action == start_action) 20638c2ecf20Sopenharmony_ci break; 20648c2ecf20Sopenharmony_ci } 20658c2ecf20Sopenharmony_ci search_tbl->lq_type = LQ_NONE; 20668c2ecf20Sopenharmony_ci return; 20678c2ecf20Sopenharmony_ci out: 20688c2ecf20Sopenharmony_ci lq_sta->search_better_tbl = 1; 20698c2ecf20Sopenharmony_ci tbl->action++; 20708c2ecf20Sopenharmony_ci if (tbl->action > IWL_MIMO3_SWITCH_GI) 20718c2ecf20Sopenharmony_ci tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; 20728c2ecf20Sopenharmony_ci if (update_search_tbl_counter) 20738c2ecf20Sopenharmony_ci search_tbl->action = tbl->action; 20748c2ecf20Sopenharmony_ci} 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci/* 20778c2ecf20Sopenharmony_ci * Check whether we should continue using same modulation mode, or 20788c2ecf20Sopenharmony_ci * begin search for a new mode, based on: 20798c2ecf20Sopenharmony_ci * 1) # tx successes or failures while using this mode 20808c2ecf20Sopenharmony_ci * 2) # times calling this function 20818c2ecf20Sopenharmony_ci * 3) elapsed time in this mode (not used, for now) 20828c2ecf20Sopenharmony_ci */ 20838c2ecf20Sopenharmony_cistatic void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) 20848c2ecf20Sopenharmony_ci{ 20858c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl; 20868c2ecf20Sopenharmony_ci int i; 20878c2ecf20Sopenharmony_ci int active_tbl; 20888c2ecf20Sopenharmony_ci int flush_interval_passed = 0; 20898c2ecf20Sopenharmony_ci struct iwl_priv *priv; 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci priv = lq_sta->drv; 20928c2ecf20Sopenharmony_ci active_tbl = lq_sta->active_tbl; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[active_tbl]); 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci /* If we've been disallowing search, see if we should now allow it */ 20978c2ecf20Sopenharmony_ci if (lq_sta->stay_in_tbl) { 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci /* Elapsed time using current modulation mode */ 21008c2ecf20Sopenharmony_ci if (lq_sta->flush_timer) 21018c2ecf20Sopenharmony_ci flush_interval_passed = 21028c2ecf20Sopenharmony_ci time_after(jiffies, 21038c2ecf20Sopenharmony_ci (unsigned long)(lq_sta->flush_timer + 21048c2ecf20Sopenharmony_ci IWL_RATE_SCALE_FLUSH_INTVL)); 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci /* 21078c2ecf20Sopenharmony_ci * Check if we should allow search for new modulation mode. 21088c2ecf20Sopenharmony_ci * If many frames have failed or succeeded, or we've used 21098c2ecf20Sopenharmony_ci * this same modulation for a long time, allow search, and 21108c2ecf20Sopenharmony_ci * reset history stats that keep track of whether we should 21118c2ecf20Sopenharmony_ci * allow a new search. Also (below) reset all bitmaps and 21128c2ecf20Sopenharmony_ci * stats in active history. 21138c2ecf20Sopenharmony_ci */ 21148c2ecf20Sopenharmony_ci if (force_search || 21158c2ecf20Sopenharmony_ci (lq_sta->total_failed > lq_sta->max_failure_limit) || 21168c2ecf20Sopenharmony_ci (lq_sta->total_success > lq_sta->max_success_limit) || 21178c2ecf20Sopenharmony_ci ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer) 21188c2ecf20Sopenharmony_ci && (flush_interval_passed))) { 21198c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n", 21208c2ecf20Sopenharmony_ci lq_sta->total_failed, 21218c2ecf20Sopenharmony_ci lq_sta->total_success, 21228c2ecf20Sopenharmony_ci flush_interval_passed); 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci /* Allow search for new mode */ 21258c2ecf20Sopenharmony_ci lq_sta->stay_in_tbl = 0; /* only place reset */ 21268c2ecf20Sopenharmony_ci lq_sta->total_failed = 0; 21278c2ecf20Sopenharmony_ci lq_sta->total_success = 0; 21288c2ecf20Sopenharmony_ci lq_sta->flush_timer = 0; 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci /* 21318c2ecf20Sopenharmony_ci * Else if we've used this modulation mode enough repetitions 21328c2ecf20Sopenharmony_ci * (regardless of elapsed time or success/failure), reset 21338c2ecf20Sopenharmony_ci * history bitmaps and rate-specific stats for all rates in 21348c2ecf20Sopenharmony_ci * active table. 21358c2ecf20Sopenharmony_ci */ 21368c2ecf20Sopenharmony_ci } else { 21378c2ecf20Sopenharmony_ci lq_sta->table_count++; 21388c2ecf20Sopenharmony_ci if (lq_sta->table_count >= 21398c2ecf20Sopenharmony_ci lq_sta->table_count_limit) { 21408c2ecf20Sopenharmony_ci lq_sta->table_count = 0; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: stay in table clear win\n"); 21438c2ecf20Sopenharmony_ci for (i = 0; i < IWL_RATE_COUNT; i++) 21448c2ecf20Sopenharmony_ci rs_rate_scale_clear_window( 21458c2ecf20Sopenharmony_ci &(tbl->win[i])); 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci /* If transitioning to allow "search", reset all history 21508c2ecf20Sopenharmony_ci * bitmaps and stats in active table (this will become the new 21518c2ecf20Sopenharmony_ci * "search" table). */ 21528c2ecf20Sopenharmony_ci if (!lq_sta->stay_in_tbl) { 21538c2ecf20Sopenharmony_ci for (i = 0; i < IWL_RATE_COUNT; i++) 21548c2ecf20Sopenharmony_ci rs_rate_scale_clear_window(&(tbl->win[i])); 21558c2ecf20Sopenharmony_ci } 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci} 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci/* 21608c2ecf20Sopenharmony_ci * setup rate table in uCode 21618c2ecf20Sopenharmony_ci */ 21628c2ecf20Sopenharmony_cistatic void rs_update_rate_tbl(struct iwl_priv *priv, 21638c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, 21648c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, 21658c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, 21668c2ecf20Sopenharmony_ci int index, u8 is_green) 21678c2ecf20Sopenharmony_ci{ 21688c2ecf20Sopenharmony_ci u32 rate; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci /* Update uCode's rate table. */ 21718c2ecf20Sopenharmony_ci rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); 21728c2ecf20Sopenharmony_ci rs_fill_link_cmd(priv, lq_sta, rate); 21738c2ecf20Sopenharmony_ci iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); 21748c2ecf20Sopenharmony_ci} 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci/* 21778c2ecf20Sopenharmony_ci * Do rate scaling and search for new modulation mode. 21788c2ecf20Sopenharmony_ci */ 21798c2ecf20Sopenharmony_cistatic void rs_rate_scale_perform(struct iwl_priv *priv, 21808c2ecf20Sopenharmony_ci struct sk_buff *skb, 21818c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 21828c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 21838c2ecf20Sopenharmony_ci{ 21848c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = priv->hw; 21858c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = &hw->conf; 21868c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 21878c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 21888c2ecf20Sopenharmony_ci int low = IWL_RATE_INVALID; 21898c2ecf20Sopenharmony_ci int high = IWL_RATE_INVALID; 21908c2ecf20Sopenharmony_ci int index; 21918c2ecf20Sopenharmony_ci int i; 21928c2ecf20Sopenharmony_ci struct iwl_rate_scale_data *window = NULL; 21938c2ecf20Sopenharmony_ci int current_tpt = IWL_INVALID_VALUE; 21948c2ecf20Sopenharmony_ci int low_tpt = IWL_INVALID_VALUE; 21958c2ecf20Sopenharmony_ci int high_tpt = IWL_INVALID_VALUE; 21968c2ecf20Sopenharmony_ci u32 fail_count; 21978c2ecf20Sopenharmony_ci s8 scale_action = 0; 21988c2ecf20Sopenharmony_ci u16 rate_mask; 21998c2ecf20Sopenharmony_ci u8 update_lq = 0; 22008c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl, *tbl1; 22018c2ecf20Sopenharmony_ci u16 rate_scale_index_msk = 0; 22028c2ecf20Sopenharmony_ci u8 is_green = 0; 22038c2ecf20Sopenharmony_ci u8 active_tbl = 0; 22048c2ecf20Sopenharmony_ci u8 done_search = 0; 22058c2ecf20Sopenharmony_ci u16 high_low; 22068c2ecf20Sopenharmony_ci s32 sr; 22078c2ecf20Sopenharmony_ci u8 tid = IWL_MAX_TID_COUNT; 22088c2ecf20Sopenharmony_ci struct iwl_tid_data *tid_data; 22098c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; 22108c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx = sta_priv->ctx; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n"); 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci /* Send management frames and NO_ACK data using lowest rate. */ 22158c2ecf20Sopenharmony_ci /* TODO: this could probably be improved.. */ 22168c2ecf20Sopenharmony_ci if (!ieee80211_is_data(hdr->frame_control) || 22178c2ecf20Sopenharmony_ci info->flags & IEEE80211_TX_CTL_NO_ACK) 22188c2ecf20Sopenharmony_ci return; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci tid = rs_tl_add_packet(lq_sta, hdr); 22238c2ecf20Sopenharmony_ci if ((tid != IWL_MAX_TID_COUNT) && 22248c2ecf20Sopenharmony_ci (lq_sta->tx_agg_tid_en & (1 << tid))) { 22258c2ecf20Sopenharmony_ci tid_data = &priv->tid_data[lq_sta->lq.sta_id][tid]; 22268c2ecf20Sopenharmony_ci if (tid_data->agg.state == IWL_AGG_OFF) 22278c2ecf20Sopenharmony_ci lq_sta->is_agg = 0; 22288c2ecf20Sopenharmony_ci else 22298c2ecf20Sopenharmony_ci lq_sta->is_agg = 1; 22308c2ecf20Sopenharmony_ci } else 22318c2ecf20Sopenharmony_ci lq_sta->is_agg = 0; 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci /* 22348c2ecf20Sopenharmony_ci * Select rate-scale / modulation-mode table to work with in 22358c2ecf20Sopenharmony_ci * the rest of this function: "search" if searching for better 22368c2ecf20Sopenharmony_ci * modulation mode, or "active" if doing rate scaling within a mode. 22378c2ecf20Sopenharmony_ci */ 22388c2ecf20Sopenharmony_ci if (!lq_sta->search_better_tbl) 22398c2ecf20Sopenharmony_ci active_tbl = lq_sta->active_tbl; 22408c2ecf20Sopenharmony_ci else 22418c2ecf20Sopenharmony_ci active_tbl = 1 - lq_sta->active_tbl; 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[active_tbl]); 22448c2ecf20Sopenharmony_ci if (is_legacy(tbl->lq_type)) 22458c2ecf20Sopenharmony_ci lq_sta->is_green = 0; 22468c2ecf20Sopenharmony_ci else 22478c2ecf20Sopenharmony_ci lq_sta->is_green = rs_use_green(sta); 22488c2ecf20Sopenharmony_ci is_green = lq_sta->is_green; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci /* current tx rate */ 22518c2ecf20Sopenharmony_ci index = lq_sta->last_txrate_idx; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Rate scale index %d for type %d\n", index, 22548c2ecf20Sopenharmony_ci tbl->lq_type); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci /* rates available for this association, and for modulation mode */ 22578c2ecf20Sopenharmony_ci rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "mask 0x%04X\n", rate_mask); 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci /* mask with station rate restriction */ 22628c2ecf20Sopenharmony_ci if (is_legacy(tbl->lq_type)) { 22638c2ecf20Sopenharmony_ci if (lq_sta->band == NL80211_BAND_5GHZ) 22648c2ecf20Sopenharmony_ci /* supp_rates has no CCK bits in A mode */ 22658c2ecf20Sopenharmony_ci rate_scale_index_msk = (u16) (rate_mask & 22668c2ecf20Sopenharmony_ci (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); 22678c2ecf20Sopenharmony_ci else 22688c2ecf20Sopenharmony_ci rate_scale_index_msk = (u16) (rate_mask & 22698c2ecf20Sopenharmony_ci lq_sta->supp_rates); 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci } else 22728c2ecf20Sopenharmony_ci rate_scale_index_msk = rate_mask; 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci if (!rate_scale_index_msk) 22758c2ecf20Sopenharmony_ci rate_scale_index_msk = rate_mask; 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci if (!((1 << index) & rate_scale_index_msk)) { 22788c2ecf20Sopenharmony_ci IWL_ERR(priv, "Current Rate is not valid\n"); 22798c2ecf20Sopenharmony_ci if (lq_sta->search_better_tbl) { 22808c2ecf20Sopenharmony_ci /* revert to active table if search table is not valid*/ 22818c2ecf20Sopenharmony_ci tbl->lq_type = LQ_NONE; 22828c2ecf20Sopenharmony_ci lq_sta->search_better_tbl = 0; 22838c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 22848c2ecf20Sopenharmony_ci /* get "active" rate info */ 22858c2ecf20Sopenharmony_ci index = iwl_hwrate_to_plcp_idx(tbl->current_rate); 22868c2ecf20Sopenharmony_ci rs_update_rate_tbl(priv, ctx, lq_sta, tbl, 22878c2ecf20Sopenharmony_ci index, is_green); 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci return; 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci /* Get expected throughput table and history window for current rate */ 22938c2ecf20Sopenharmony_ci if (!tbl->expected_tpt) { 22948c2ecf20Sopenharmony_ci IWL_ERR(priv, "tbl->expected_tpt is NULL\n"); 22958c2ecf20Sopenharmony_ci return; 22968c2ecf20Sopenharmony_ci } 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci /* force user max rate if set by user */ 22998c2ecf20Sopenharmony_ci if ((lq_sta->max_rate_idx != -1) && 23008c2ecf20Sopenharmony_ci (lq_sta->max_rate_idx < index)) { 23018c2ecf20Sopenharmony_ci index = lq_sta->max_rate_idx; 23028c2ecf20Sopenharmony_ci update_lq = 1; 23038c2ecf20Sopenharmony_ci window = &(tbl->win[index]); 23048c2ecf20Sopenharmony_ci goto lq_update; 23058c2ecf20Sopenharmony_ci } 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci window = &(tbl->win[index]); 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci /* 23108c2ecf20Sopenharmony_ci * If there is not enough history to calculate actual average 23118c2ecf20Sopenharmony_ci * throughput, keep analyzing results of more tx frames, without 23128c2ecf20Sopenharmony_ci * changing rate or mode (bypass most of the rest of this function). 23138c2ecf20Sopenharmony_ci * Set up new rate table in uCode only if old rate is not supported 23148c2ecf20Sopenharmony_ci * in current association (use new rate found above). 23158c2ecf20Sopenharmony_ci */ 23168c2ecf20Sopenharmony_ci fail_count = window->counter - window->success_counter; 23178c2ecf20Sopenharmony_ci if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && 23188c2ecf20Sopenharmony_ci (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { 23198c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: still below TH. succ=%d total=%d " 23208c2ecf20Sopenharmony_ci "for index %d\n", 23218c2ecf20Sopenharmony_ci window->success_counter, window->counter, index); 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci /* Can't calculate this yet; not enough history */ 23248c2ecf20Sopenharmony_ci window->average_tpt = IWL_INVALID_VALUE; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci /* Should we stay with this modulation mode, 23278c2ecf20Sopenharmony_ci * or search for a new one? */ 23288c2ecf20Sopenharmony_ci rs_stay_in_table(lq_sta, false); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci goto out; 23318c2ecf20Sopenharmony_ci } 23328c2ecf20Sopenharmony_ci /* Else we have enough samples; calculate estimate of 23338c2ecf20Sopenharmony_ci * actual average throughput */ 23348c2ecf20Sopenharmony_ci if (window->average_tpt != ((window->success_ratio * 23358c2ecf20Sopenharmony_ci tbl->expected_tpt[index] + 64) / 128)) { 23368c2ecf20Sopenharmony_ci IWL_ERR(priv, "expected_tpt should have been calculated by now\n"); 23378c2ecf20Sopenharmony_ci window->average_tpt = ((window->success_ratio * 23388c2ecf20Sopenharmony_ci tbl->expected_tpt[index] + 64) / 128); 23398c2ecf20Sopenharmony_ci } 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci /* If we are searching for better modulation mode, check success. */ 23428c2ecf20Sopenharmony_ci if (lq_sta->search_better_tbl && 23438c2ecf20Sopenharmony_ci (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) { 23448c2ecf20Sopenharmony_ci /* If good success, continue using the "search" mode; 23458c2ecf20Sopenharmony_ci * no need to send new link quality command, since we're 23468c2ecf20Sopenharmony_ci * continuing to use the setup that we've been trying. */ 23478c2ecf20Sopenharmony_ci if (window->average_tpt > lq_sta->last_tpt) { 23488c2ecf20Sopenharmony_ci 23498c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: SWITCHING TO NEW TABLE " 23508c2ecf20Sopenharmony_ci "suc=%d cur-tpt=%d old-tpt=%d\n", 23518c2ecf20Sopenharmony_ci window->success_ratio, 23528c2ecf20Sopenharmony_ci window->average_tpt, 23538c2ecf20Sopenharmony_ci lq_sta->last_tpt); 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci if (!is_legacy(tbl->lq_type)) 23568c2ecf20Sopenharmony_ci lq_sta->enable_counter = 1; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci /* Swap tables; "search" becomes "active" */ 23598c2ecf20Sopenharmony_ci lq_sta->active_tbl = active_tbl; 23608c2ecf20Sopenharmony_ci current_tpt = window->average_tpt; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci /* Else poor success; go back to mode in "active" table */ 23638c2ecf20Sopenharmony_ci } else { 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: GOING BACK TO THE OLD TABLE " 23668c2ecf20Sopenharmony_ci "suc=%d cur-tpt=%d old-tpt=%d\n", 23678c2ecf20Sopenharmony_ci window->success_ratio, 23688c2ecf20Sopenharmony_ci window->average_tpt, 23698c2ecf20Sopenharmony_ci lq_sta->last_tpt); 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci /* Nullify "search" table */ 23728c2ecf20Sopenharmony_ci tbl->lq_type = LQ_NONE; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci /* Revert to "active" table */ 23758c2ecf20Sopenharmony_ci active_tbl = lq_sta->active_tbl; 23768c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[active_tbl]); 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci /* Revert to "active" rate and throughput info */ 23798c2ecf20Sopenharmony_ci index = iwl_hwrate_to_plcp_idx(tbl->current_rate); 23808c2ecf20Sopenharmony_ci current_tpt = lq_sta->last_tpt; 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci /* Need to set up a new rate table in uCode */ 23838c2ecf20Sopenharmony_ci update_lq = 1; 23848c2ecf20Sopenharmony_ci } 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci /* Either way, we've made a decision; modulation mode 23878c2ecf20Sopenharmony_ci * search is done, allow rate adjustment next time. */ 23888c2ecf20Sopenharmony_ci lq_sta->search_better_tbl = 0; 23898c2ecf20Sopenharmony_ci done_search = 1; /* Don't switch modes below! */ 23908c2ecf20Sopenharmony_ci goto lq_update; 23918c2ecf20Sopenharmony_ci } 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci /* (Else) not in search of better modulation mode, try for better 23948c2ecf20Sopenharmony_ci * starting rate, while staying in this mode. */ 23958c2ecf20Sopenharmony_ci high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk, 23968c2ecf20Sopenharmony_ci tbl->lq_type); 23978c2ecf20Sopenharmony_ci low = high_low & 0xff; 23988c2ecf20Sopenharmony_ci high = (high_low >> 8) & 0xff; 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci /* If user set max rate, dont allow higher than user constrain */ 24018c2ecf20Sopenharmony_ci if ((lq_sta->max_rate_idx != -1) && 24028c2ecf20Sopenharmony_ci (lq_sta->max_rate_idx < high)) 24038c2ecf20Sopenharmony_ci high = IWL_RATE_INVALID; 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci sr = window->success_ratio; 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci /* Collect measured throughputs for current and adjacent rates */ 24088c2ecf20Sopenharmony_ci current_tpt = window->average_tpt; 24098c2ecf20Sopenharmony_ci if (low != IWL_RATE_INVALID) 24108c2ecf20Sopenharmony_ci low_tpt = tbl->win[low].average_tpt; 24118c2ecf20Sopenharmony_ci if (high != IWL_RATE_INVALID) 24128c2ecf20Sopenharmony_ci high_tpt = tbl->win[high].average_tpt; 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci scale_action = 0; 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci /* Too many failures, decrease rate */ 24178c2ecf20Sopenharmony_ci if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { 24188c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n"); 24198c2ecf20Sopenharmony_ci scale_action = -1; 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci /* No throughput measured yet for adjacent rates; try increase. */ 24228c2ecf20Sopenharmony_ci } else if ((low_tpt == IWL_INVALID_VALUE) && 24238c2ecf20Sopenharmony_ci (high_tpt == IWL_INVALID_VALUE)) { 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) 24268c2ecf20Sopenharmony_ci scale_action = 1; 24278c2ecf20Sopenharmony_ci else if (low != IWL_RATE_INVALID) 24288c2ecf20Sopenharmony_ci scale_action = 0; 24298c2ecf20Sopenharmony_ci } 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci /* Both adjacent throughputs are measured, but neither one has better 24328c2ecf20Sopenharmony_ci * throughput; we're using the best rate, don't change it! */ 24338c2ecf20Sopenharmony_ci else if ((low_tpt != IWL_INVALID_VALUE) && 24348c2ecf20Sopenharmony_ci (high_tpt != IWL_INVALID_VALUE) && 24358c2ecf20Sopenharmony_ci (low_tpt < current_tpt) && 24368c2ecf20Sopenharmony_ci (high_tpt < current_tpt)) 24378c2ecf20Sopenharmony_ci scale_action = 0; 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci /* At least one adjacent rate's throughput is measured, 24408c2ecf20Sopenharmony_ci * and may have better performance. */ 24418c2ecf20Sopenharmony_ci else { 24428c2ecf20Sopenharmony_ci /* Higher adjacent rate's throughput is measured */ 24438c2ecf20Sopenharmony_ci if (high_tpt != IWL_INVALID_VALUE) { 24448c2ecf20Sopenharmony_ci /* Higher rate has better throughput */ 24458c2ecf20Sopenharmony_ci if (high_tpt > current_tpt && 24468c2ecf20Sopenharmony_ci sr >= IWL_RATE_INCREASE_TH) { 24478c2ecf20Sopenharmony_ci scale_action = 1; 24488c2ecf20Sopenharmony_ci } else { 24498c2ecf20Sopenharmony_ci scale_action = 0; 24508c2ecf20Sopenharmony_ci } 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci /* Lower adjacent rate's throughput is measured */ 24538c2ecf20Sopenharmony_ci } else if (low_tpt != IWL_INVALID_VALUE) { 24548c2ecf20Sopenharmony_ci /* Lower rate has better throughput */ 24558c2ecf20Sopenharmony_ci if (low_tpt > current_tpt) { 24568c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, 24578c2ecf20Sopenharmony_ci "decrease rate because of low tpt\n"); 24588c2ecf20Sopenharmony_ci scale_action = -1; 24598c2ecf20Sopenharmony_ci } else if (sr >= IWL_RATE_INCREASE_TH) { 24608c2ecf20Sopenharmony_ci scale_action = 1; 24618c2ecf20Sopenharmony_ci } 24628c2ecf20Sopenharmony_ci } 24638c2ecf20Sopenharmony_ci } 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci /* Sanity check; asked for decrease, but success rate or throughput 24668c2ecf20Sopenharmony_ci * has been good at old rate. Don't change it. */ 24678c2ecf20Sopenharmony_ci if ((scale_action == -1) && (low != IWL_RATE_INVALID) && 24688c2ecf20Sopenharmony_ci ((sr > IWL_RATE_HIGH_TH) || 24698c2ecf20Sopenharmony_ci (current_tpt > (100 * tbl->expected_tpt[low])))) 24708c2ecf20Sopenharmony_ci scale_action = 0; 24718c2ecf20Sopenharmony_ci if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type)) 24728c2ecf20Sopenharmony_ci scale_action = -1; 24738c2ecf20Sopenharmony_ci if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI && 24748c2ecf20Sopenharmony_ci (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) 24758c2ecf20Sopenharmony_ci scale_action = -1; 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && 24788c2ecf20Sopenharmony_ci (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { 24798c2ecf20Sopenharmony_ci if (lq_sta->last_bt_traffic > priv->bt_traffic_load) { 24808c2ecf20Sopenharmony_ci /* 24818c2ecf20Sopenharmony_ci * don't set scale_action, don't want to scale up if 24828c2ecf20Sopenharmony_ci * the rate scale doesn't otherwise think that is a 24838c2ecf20Sopenharmony_ci * good idea. 24848c2ecf20Sopenharmony_ci */ 24858c2ecf20Sopenharmony_ci } else if (lq_sta->last_bt_traffic <= priv->bt_traffic_load) { 24868c2ecf20Sopenharmony_ci scale_action = -1; 24878c2ecf20Sopenharmony_ci } 24888c2ecf20Sopenharmony_ci } 24898c2ecf20Sopenharmony_ci lq_sta->last_bt_traffic = priv->bt_traffic_load; 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && 24928c2ecf20Sopenharmony_ci (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { 24938c2ecf20Sopenharmony_ci /* search for a new modulation */ 24948c2ecf20Sopenharmony_ci rs_stay_in_table(lq_sta, true); 24958c2ecf20Sopenharmony_ci goto lq_update; 24968c2ecf20Sopenharmony_ci } 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci switch (scale_action) { 24998c2ecf20Sopenharmony_ci case -1: 25008c2ecf20Sopenharmony_ci /* Decrease starting rate, update uCode's rate table */ 25018c2ecf20Sopenharmony_ci if (low != IWL_RATE_INVALID) { 25028c2ecf20Sopenharmony_ci update_lq = 1; 25038c2ecf20Sopenharmony_ci index = low; 25048c2ecf20Sopenharmony_ci } 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci break; 25078c2ecf20Sopenharmony_ci case 1: 25088c2ecf20Sopenharmony_ci /* Increase starting rate, update uCode's rate table */ 25098c2ecf20Sopenharmony_ci if (high != IWL_RATE_INVALID) { 25108c2ecf20Sopenharmony_ci update_lq = 1; 25118c2ecf20Sopenharmony_ci index = high; 25128c2ecf20Sopenharmony_ci } 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci break; 25158c2ecf20Sopenharmony_ci case 0: 25168c2ecf20Sopenharmony_ci /* No change */ 25178c2ecf20Sopenharmony_ci default: 25188c2ecf20Sopenharmony_ci break; 25198c2ecf20Sopenharmony_ci } 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "choose rate scale index %d action %d low %d " 25228c2ecf20Sopenharmony_ci "high %d type %d\n", 25238c2ecf20Sopenharmony_ci index, scale_action, low, high, tbl->lq_type); 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_cilq_update: 25268c2ecf20Sopenharmony_ci /* Replace uCode's rate table for the destination station. */ 25278c2ecf20Sopenharmony_ci if (update_lq) 25288c2ecf20Sopenharmony_ci rs_update_rate_tbl(priv, ctx, lq_sta, tbl, index, is_green); 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) { 25318c2ecf20Sopenharmony_ci /* Should we stay with this modulation mode, 25328c2ecf20Sopenharmony_ci * or search for a new one? */ 25338c2ecf20Sopenharmony_ci rs_stay_in_table(lq_sta, false); 25348c2ecf20Sopenharmony_ci } 25358c2ecf20Sopenharmony_ci /* 25368c2ecf20Sopenharmony_ci * Search for new modulation mode if we're: 25378c2ecf20Sopenharmony_ci * 1) Not changing rates right now 25388c2ecf20Sopenharmony_ci * 2) Not just finishing up a search 25398c2ecf20Sopenharmony_ci * 3) Allowing a new search 25408c2ecf20Sopenharmony_ci */ 25418c2ecf20Sopenharmony_ci if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) { 25428c2ecf20Sopenharmony_ci /* Save current throughput to compare with "search" throughput*/ 25438c2ecf20Sopenharmony_ci lq_sta->last_tpt = current_tpt; 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci /* Select a new "search" modulation mode to try. 25468c2ecf20Sopenharmony_ci * If one is found, set up the new "search" table. */ 25478c2ecf20Sopenharmony_ci if (is_legacy(tbl->lq_type)) 25488c2ecf20Sopenharmony_ci rs_move_legacy_other(priv, lq_sta, conf, sta, index); 25498c2ecf20Sopenharmony_ci else if (is_siso(tbl->lq_type)) 25508c2ecf20Sopenharmony_ci rs_move_siso_to_other(priv, lq_sta, conf, sta, index); 25518c2ecf20Sopenharmony_ci else if (is_mimo2(tbl->lq_type)) 25528c2ecf20Sopenharmony_ci rs_move_mimo2_to_other(priv, lq_sta, conf, sta, index); 25538c2ecf20Sopenharmony_ci else 25548c2ecf20Sopenharmony_ci rs_move_mimo3_to_other(priv, lq_sta, conf, sta, index); 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci /* If new "search" mode was selected, set up in uCode table */ 25578c2ecf20Sopenharmony_ci if (lq_sta->search_better_tbl) { 25588c2ecf20Sopenharmony_ci /* Access the "search" table, clear its history. */ 25598c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); 25608c2ecf20Sopenharmony_ci for (i = 0; i < IWL_RATE_COUNT; i++) 25618c2ecf20Sopenharmony_ci rs_rate_scale_clear_window(&(tbl->win[i])); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci /* Use new "search" start rate */ 25648c2ecf20Sopenharmony_ci index = iwl_hwrate_to_plcp_idx(tbl->current_rate); 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n", 25678c2ecf20Sopenharmony_ci tbl->current_rate, index); 25688c2ecf20Sopenharmony_ci rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); 25698c2ecf20Sopenharmony_ci iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); 25708c2ecf20Sopenharmony_ci } else 25718c2ecf20Sopenharmony_ci done_search = 1; 25728c2ecf20Sopenharmony_ci } 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci if (done_search && !lq_sta->stay_in_tbl) { 25758c2ecf20Sopenharmony_ci /* If the "active" (non-search) mode was legacy, 25768c2ecf20Sopenharmony_ci * and we've tried switching antennas, 25778c2ecf20Sopenharmony_ci * but we haven't been able to try HT modes (not available), 25788c2ecf20Sopenharmony_ci * stay with best antenna legacy modulation for a while 25798c2ecf20Sopenharmony_ci * before next round of mode comparisons. */ 25808c2ecf20Sopenharmony_ci tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); 25818c2ecf20Sopenharmony_ci if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && 25828c2ecf20Sopenharmony_ci lq_sta->action_counter > tbl1->max_search) { 25838c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: STAY in legacy table\n"); 25848c2ecf20Sopenharmony_ci rs_set_stay_in_table(priv, 1, lq_sta); 25858c2ecf20Sopenharmony_ci } 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci /* If we're in an HT mode, and all 3 mode switch actions 25888c2ecf20Sopenharmony_ci * have been tried and compared, stay in this best modulation 25898c2ecf20Sopenharmony_ci * mode for a while before next round of mode comparisons. */ 25908c2ecf20Sopenharmony_ci if (lq_sta->enable_counter && 25918c2ecf20Sopenharmony_ci (lq_sta->action_counter >= tbl1->max_search) && 25928c2ecf20Sopenharmony_ci iwl_ht_enabled(priv)) { 25938c2ecf20Sopenharmony_ci if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && 25948c2ecf20Sopenharmony_ci (lq_sta->tx_agg_tid_en & (1 << tid)) && 25958c2ecf20Sopenharmony_ci (tid != IWL_MAX_TID_COUNT)) { 25968c2ecf20Sopenharmony_ci u8 sta_id = lq_sta->lq.sta_id; 25978c2ecf20Sopenharmony_ci tid_data = &priv->tid_data[sta_id][tid]; 25988c2ecf20Sopenharmony_ci if (tid_data->agg.state == IWL_AGG_OFF) { 25998c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, 26008c2ecf20Sopenharmony_ci "try to aggregate tid %d\n", 26018c2ecf20Sopenharmony_ci tid); 26028c2ecf20Sopenharmony_ci rs_tl_turn_on_agg(priv, tid, 26038c2ecf20Sopenharmony_ci lq_sta, sta); 26048c2ecf20Sopenharmony_ci } 26058c2ecf20Sopenharmony_ci } 26068c2ecf20Sopenharmony_ci rs_set_stay_in_table(priv, 0, lq_sta); 26078c2ecf20Sopenharmony_ci } 26088c2ecf20Sopenharmony_ci } 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ciout: 26118c2ecf20Sopenharmony_ci tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); 26128c2ecf20Sopenharmony_ci lq_sta->last_txrate_idx = index; 26138c2ecf20Sopenharmony_ci} 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci/* 26168c2ecf20Sopenharmony_ci * rs_initialize_lq - Initialize a station's hardware rate table 26178c2ecf20Sopenharmony_ci * 26188c2ecf20Sopenharmony_ci * The uCode's station table contains a table of fallback rates 26198c2ecf20Sopenharmony_ci * for automatic fallback during transmission. 26208c2ecf20Sopenharmony_ci * 26218c2ecf20Sopenharmony_ci * NOTE: This sets up a default set of values. These will be replaced later 26228c2ecf20Sopenharmony_ci * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of 26238c2ecf20Sopenharmony_ci * rc80211_simple. 26248c2ecf20Sopenharmony_ci * 26258c2ecf20Sopenharmony_ci * NOTE: Run REPLY_ADD_STA command to set up station table entry, before 26268c2ecf20Sopenharmony_ci * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, 26278c2ecf20Sopenharmony_ci * which requires station table entry to exist). 26288c2ecf20Sopenharmony_ci */ 26298c2ecf20Sopenharmony_cistatic void rs_initialize_lq(struct iwl_priv *priv, 26308c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 26318c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta) 26328c2ecf20Sopenharmony_ci{ 26338c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl; 26348c2ecf20Sopenharmony_ci int rate_idx; 26358c2ecf20Sopenharmony_ci int i; 26368c2ecf20Sopenharmony_ci u32 rate; 26378c2ecf20Sopenharmony_ci u8 use_green = rs_use_green(sta); 26388c2ecf20Sopenharmony_ci u8 active_tbl = 0; 26398c2ecf20Sopenharmony_ci u8 valid_tx_ant; 26408c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv; 26418c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx; 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci if (!sta || !lq_sta) 26448c2ecf20Sopenharmony_ci return; 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci sta_priv = (void *)sta->drv_priv; 26478c2ecf20Sopenharmony_ci ctx = sta_priv->ctx; 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci i = lq_sta->last_txrate_idx; 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci valid_tx_ant = priv->nvm_data->valid_tx_ant; 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci if (!lq_sta->search_better_tbl) 26548c2ecf20Sopenharmony_ci active_tbl = lq_sta->active_tbl; 26558c2ecf20Sopenharmony_ci else 26568c2ecf20Sopenharmony_ci active_tbl = 1 - lq_sta->active_tbl; 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_ci tbl = &(lq_sta->lq_info[active_tbl]); 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci if ((i < 0) || (i >= IWL_RATE_COUNT)) 26618c2ecf20Sopenharmony_ci i = 0; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci rate = iwl_rates[i].plcp; 26648c2ecf20Sopenharmony_ci tbl->ant_type = first_antenna(valid_tx_ant); 26658c2ecf20Sopenharmony_ci rate |= tbl->ant_type << RATE_MCS_ANT_POS; 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) 26688c2ecf20Sopenharmony_ci rate |= RATE_MCS_CCK_MSK; 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_ci rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx); 26718c2ecf20Sopenharmony_ci if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) 26728c2ecf20Sopenharmony_ci rs_toggle_antenna(valid_tx_ant, &rate, tbl); 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci rate = rate_n_flags_from_tbl(priv, tbl, rate_idx, use_green); 26758c2ecf20Sopenharmony_ci tbl->current_rate = rate; 26768c2ecf20Sopenharmony_ci rs_set_expected_tpt_table(lq_sta, tbl); 26778c2ecf20Sopenharmony_ci rs_fill_link_cmd(NULL, lq_sta, rate); 26788c2ecf20Sopenharmony_ci priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; 26798c2ecf20Sopenharmony_ci iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, 0, true); 26808c2ecf20Sopenharmony_ci} 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_cistatic void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, 26838c2ecf20Sopenharmony_ci struct ieee80211_tx_rate_control *txrc) 26848c2ecf20Sopenharmony_ci{ 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_ci struct sk_buff *skb = txrc->skb; 26878c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband = txrc->sband; 26888c2ecf20Sopenharmony_ci struct iwl_op_mode *op_mode __maybe_unused = 26898c2ecf20Sopenharmony_ci (struct iwl_op_mode *)priv_r; 26908c2ecf20Sopenharmony_ci struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); 26918c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 26928c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = priv_sta; 26938c2ecf20Sopenharmony_ci int rate_idx; 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n"); 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci /* Get max rate if user set max rate */ 26988c2ecf20Sopenharmony_ci if (lq_sta) { 26998c2ecf20Sopenharmony_ci lq_sta->max_rate_idx = fls(txrc->rate_idx_mask) - 1; 27008c2ecf20Sopenharmony_ci if ((sband->band == NL80211_BAND_5GHZ) && 27018c2ecf20Sopenharmony_ci (lq_sta->max_rate_idx != -1)) 27028c2ecf20Sopenharmony_ci lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE; 27038c2ecf20Sopenharmony_ci if ((lq_sta->max_rate_idx < 0) || 27048c2ecf20Sopenharmony_ci (lq_sta->max_rate_idx >= IWL_RATE_COUNT)) 27058c2ecf20Sopenharmony_ci lq_sta->max_rate_idx = -1; 27068c2ecf20Sopenharmony_ci } 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci /* Treat uninitialized rate scaling data same as non-existing. */ 27098c2ecf20Sopenharmony_ci if (lq_sta && !lq_sta->drv) { 27108c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); 27118c2ecf20Sopenharmony_ci priv_sta = NULL; 27128c2ecf20Sopenharmony_ci } 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci rate_idx = lq_sta->last_txrate_idx; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { 27178c2ecf20Sopenharmony_ci rate_idx -= IWL_FIRST_OFDM_RATE; 27188c2ecf20Sopenharmony_ci /* 6M and 9M shared same MCS index */ 27198c2ecf20Sopenharmony_ci rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; 27208c2ecf20Sopenharmony_ci if (rs_extract_rate(lq_sta->last_rate_n_flags) >= 27218c2ecf20Sopenharmony_ci IWL_RATE_MIMO3_6M_PLCP) 27228c2ecf20Sopenharmony_ci rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM); 27238c2ecf20Sopenharmony_ci else if (rs_extract_rate(lq_sta->last_rate_n_flags) >= 27248c2ecf20Sopenharmony_ci IWL_RATE_MIMO2_6M_PLCP) 27258c2ecf20Sopenharmony_ci rate_idx = rate_idx + MCS_INDEX_PER_STREAM; 27268c2ecf20Sopenharmony_ci info->control.rates[0].flags = IEEE80211_TX_RC_MCS; 27278c2ecf20Sopenharmony_ci if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) 27288c2ecf20Sopenharmony_ci info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; 27298c2ecf20Sopenharmony_ci if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) 27308c2ecf20Sopenharmony_ci info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA; 27318c2ecf20Sopenharmony_ci if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) 27328c2ecf20Sopenharmony_ci info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; 27338c2ecf20Sopenharmony_ci if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) 27348c2ecf20Sopenharmony_ci info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; 27358c2ecf20Sopenharmony_ci } else { 27368c2ecf20Sopenharmony_ci /* Check for invalid rates */ 27378c2ecf20Sopenharmony_ci if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) || 27388c2ecf20Sopenharmony_ci ((sband->band == NL80211_BAND_5GHZ) && 27398c2ecf20Sopenharmony_ci (rate_idx < IWL_FIRST_OFDM_RATE))) 27408c2ecf20Sopenharmony_ci rate_idx = rate_lowest_index(sband, sta); 27418c2ecf20Sopenharmony_ci /* On valid 5 GHz rate, adjust index */ 27428c2ecf20Sopenharmony_ci else if (sband->band == NL80211_BAND_5GHZ) 27438c2ecf20Sopenharmony_ci rate_idx -= IWL_FIRST_OFDM_RATE; 27448c2ecf20Sopenharmony_ci info->control.rates[0].flags = 0; 27458c2ecf20Sopenharmony_ci } 27468c2ecf20Sopenharmony_ci info->control.rates[0].idx = rate_idx; 27478c2ecf20Sopenharmony_ci info->control.rates[0].count = 1; 27488c2ecf20Sopenharmony_ci} 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_cistatic void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, 27518c2ecf20Sopenharmony_ci gfp_t gfp) 27528c2ecf20Sopenharmony_ci{ 27538c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv; 27548c2ecf20Sopenharmony_ci struct iwl_op_mode *op_mode __maybe_unused = 27558c2ecf20Sopenharmony_ci (struct iwl_op_mode *)priv_rate; 27568c2ecf20Sopenharmony_ci struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "create station rate scale window\n"); 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci return &sta_priv->lq_sta; 27618c2ecf20Sopenharmony_ci} 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci/* 27648c2ecf20Sopenharmony_ci * Called after adding a new station to initialize rate scaling 27658c2ecf20Sopenharmony_ci */ 27668c2ecf20Sopenharmony_civoid iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) 27678c2ecf20Sopenharmony_ci{ 27688c2ecf20Sopenharmony_ci int i, j; 27698c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = priv->hw; 27708c2ecf20Sopenharmony_ci struct ieee80211_conf *conf = &priv->hw->conf; 27718c2ecf20Sopenharmony_ci struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 27728c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv; 27738c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta; 27748c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 27758c2ecf20Sopenharmony_ci unsigned long supp; /* must be unsigned long for for_each_set_bit */ 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci sta_priv = (struct iwl_station_priv *) sta->drv_priv; 27788c2ecf20Sopenharmony_ci lq_sta = &sta_priv->lq_sta; 27798c2ecf20Sopenharmony_ci sband = hw->wiphy->bands[conf->chandef.chan->band]; 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci lq_sta->lq.sta_id = sta_id; 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci for (j = 0; j < LQ_SIZE; j++) 27858c2ecf20Sopenharmony_ci for (i = 0; i < IWL_RATE_COUNT; i++) 27868c2ecf20Sopenharmony_ci rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci lq_sta->flush_timer = 0; 27898c2ecf20Sopenharmony_ci lq_sta->supp_rates = sta->supp_rates[sband->band]; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n", 27928c2ecf20Sopenharmony_ci sta_id); 27938c2ecf20Sopenharmony_ci /* TODO: what is a good starting rate for STA? About middle? Maybe not 27948c2ecf20Sopenharmony_ci * the lowest or the highest rate.. Could consider using RSSI from 27958c2ecf20Sopenharmony_ci * previous packets? Need to have IEEE 802.1X auth succeed immediately 27968c2ecf20Sopenharmony_ci * after assoc.. */ 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci lq_sta->is_dup = 0; 27998c2ecf20Sopenharmony_ci lq_sta->max_rate_idx = -1; 28008c2ecf20Sopenharmony_ci lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; 28018c2ecf20Sopenharmony_ci lq_sta->is_green = rs_use_green(sta); 28028c2ecf20Sopenharmony_ci lq_sta->band = sband->band; 28038c2ecf20Sopenharmony_ci /* 28048c2ecf20Sopenharmony_ci * active legacy rates as per supported rates bitmap 28058c2ecf20Sopenharmony_ci */ 28068c2ecf20Sopenharmony_ci supp = sta->supp_rates[sband->band]; 28078c2ecf20Sopenharmony_ci lq_sta->active_legacy_rate = 0; 28088c2ecf20Sopenharmony_ci for_each_set_bit(i, &supp, BITS_PER_LONG) 28098c2ecf20Sopenharmony_ci lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci /* 28128c2ecf20Sopenharmony_ci * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), 28138c2ecf20Sopenharmony_ci * supp_rates[] does not; shift to convert format, force 9 MBits off. 28148c2ecf20Sopenharmony_ci */ 28158c2ecf20Sopenharmony_ci lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; 28168c2ecf20Sopenharmony_ci lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; 28178c2ecf20Sopenharmony_ci lq_sta->active_siso_rate &= ~((u16)0x2); 28188c2ecf20Sopenharmony_ci lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci /* Same here */ 28218c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; 28228c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; 28238c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate &= ~((u16)0x2); 28248c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1; 28278c2ecf20Sopenharmony_ci lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1; 28288c2ecf20Sopenharmony_ci lq_sta->active_mimo3_rate &= ~((u16)0x2); 28298c2ecf20Sopenharmony_ci lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n", 28328c2ecf20Sopenharmony_ci lq_sta->active_siso_rate, 28338c2ecf20Sopenharmony_ci lq_sta->active_mimo2_rate, 28348c2ecf20Sopenharmony_ci lq_sta->active_mimo3_rate); 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci /* These values will be overridden later */ 28378c2ecf20Sopenharmony_ci lq_sta->lq.general_params.single_stream_ant_msk = 28388c2ecf20Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 28398c2ecf20Sopenharmony_ci lq_sta->lq.general_params.dual_stream_ant_msk = 28408c2ecf20Sopenharmony_ci priv->nvm_data->valid_tx_ant & 28418c2ecf20Sopenharmony_ci ~first_antenna(priv->nvm_data->valid_tx_ant); 28428c2ecf20Sopenharmony_ci if (!lq_sta->lq.general_params.dual_stream_ant_msk) { 28438c2ecf20Sopenharmony_ci lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; 28448c2ecf20Sopenharmony_ci } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) { 28458c2ecf20Sopenharmony_ci lq_sta->lq.general_params.dual_stream_ant_msk = 28468c2ecf20Sopenharmony_ci priv->nvm_data->valid_tx_ant; 28478c2ecf20Sopenharmony_ci } 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci /* as default allow aggregation for all tids */ 28508c2ecf20Sopenharmony_ci lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; 28518c2ecf20Sopenharmony_ci lq_sta->drv = priv; 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci /* Set last_txrate_idx to lowest rate */ 28548c2ecf20Sopenharmony_ci lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); 28558c2ecf20Sopenharmony_ci if (sband->band == NL80211_BAND_5GHZ) 28568c2ecf20Sopenharmony_ci lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; 28578c2ecf20Sopenharmony_ci lq_sta->is_agg = 0; 28588c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 28598c2ecf20Sopenharmony_ci lq_sta->dbg_fixed_rate = 0; 28608c2ecf20Sopenharmony_ci#endif 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci rs_initialize_lq(priv, sta, lq_sta); 28638c2ecf20Sopenharmony_ci} 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_cistatic void rs_fill_link_cmd(struct iwl_priv *priv, 28668c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta, u32 new_rate) 28678c2ecf20Sopenharmony_ci{ 28688c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info tbl_type; 28698c2ecf20Sopenharmony_ci int index = 0; 28708c2ecf20Sopenharmony_ci int rate_idx; 28718c2ecf20Sopenharmony_ci int repeat_rate = 0; 28728c2ecf20Sopenharmony_ci u8 ant_toggle_cnt = 0; 28738c2ecf20Sopenharmony_ci u8 use_ht_possible = 1; 28748c2ecf20Sopenharmony_ci u8 valid_tx_ant = 0; 28758c2ecf20Sopenharmony_ci struct iwl_station_priv *sta_priv = 28768c2ecf20Sopenharmony_ci container_of(lq_sta, struct iwl_station_priv, lq_sta); 28778c2ecf20Sopenharmony_ci struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq; 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci /* Override starting rate (index 0) if needed for debug purposes */ 28808c2ecf20Sopenharmony_ci rs_dbgfs_set_mcs(lq_sta, &new_rate, index); 28818c2ecf20Sopenharmony_ci 28828c2ecf20Sopenharmony_ci /* Interpret new_rate (rate_n_flags) */ 28838c2ecf20Sopenharmony_ci rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, 28848c2ecf20Sopenharmony_ci &tbl_type, &rate_idx); 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ci if (priv && priv->bt_full_concurrent) { 28878c2ecf20Sopenharmony_ci /* 1x1 only */ 28888c2ecf20Sopenharmony_ci tbl_type.ant_type = 28898c2ecf20Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 28908c2ecf20Sopenharmony_ci } 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_ci /* How many times should we repeat the initial rate? */ 28938c2ecf20Sopenharmony_ci if (is_legacy(tbl_type.lq_type)) { 28948c2ecf20Sopenharmony_ci ant_toggle_cnt = 1; 28958c2ecf20Sopenharmony_ci repeat_rate = IWL_NUMBER_TRY; 28968c2ecf20Sopenharmony_ci } else { 28978c2ecf20Sopenharmony_ci repeat_rate = min(IWL_HT_NUMBER_TRY, 28988c2ecf20Sopenharmony_ci LINK_QUAL_AGG_DISABLE_START_DEF - 1); 28998c2ecf20Sopenharmony_ci } 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci lq_cmd->general_params.mimo_delimiter = 29028c2ecf20Sopenharmony_ci is_mimo(tbl_type.lq_type) ? 1 : 0; 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci /* Fill 1st table entry (index 0) */ 29058c2ecf20Sopenharmony_ci lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); 29068c2ecf20Sopenharmony_ci 29078c2ecf20Sopenharmony_ci if (num_of_ant(tbl_type.ant_type) == 1) { 29088c2ecf20Sopenharmony_ci lq_cmd->general_params.single_stream_ant_msk = 29098c2ecf20Sopenharmony_ci tbl_type.ant_type; 29108c2ecf20Sopenharmony_ci } else if (num_of_ant(tbl_type.ant_type) == 2) { 29118c2ecf20Sopenharmony_ci lq_cmd->general_params.dual_stream_ant_msk = 29128c2ecf20Sopenharmony_ci tbl_type.ant_type; 29138c2ecf20Sopenharmony_ci } /* otherwise we don't modify the existing value */ 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci index++; 29168c2ecf20Sopenharmony_ci repeat_rate--; 29178c2ecf20Sopenharmony_ci if (priv) { 29188c2ecf20Sopenharmony_ci if (priv->bt_full_concurrent) 29198c2ecf20Sopenharmony_ci valid_tx_ant = ANT_A; 29208c2ecf20Sopenharmony_ci else 29218c2ecf20Sopenharmony_ci valid_tx_ant = priv->nvm_data->valid_tx_ant; 29228c2ecf20Sopenharmony_ci } 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ci /* Fill rest of rate table */ 29258c2ecf20Sopenharmony_ci while (index < LINK_QUAL_MAX_RETRY_NUM) { 29268c2ecf20Sopenharmony_ci /* Repeat initial/next rate. 29278c2ecf20Sopenharmony_ci * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. 29288c2ecf20Sopenharmony_ci * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ 29298c2ecf20Sopenharmony_ci while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { 29308c2ecf20Sopenharmony_ci if (is_legacy(tbl_type.lq_type)) { 29318c2ecf20Sopenharmony_ci if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) 29328c2ecf20Sopenharmony_ci ant_toggle_cnt++; 29338c2ecf20Sopenharmony_ci else if (priv && 29348c2ecf20Sopenharmony_ci rs_toggle_antenna(valid_tx_ant, 29358c2ecf20Sopenharmony_ci &new_rate, &tbl_type)) 29368c2ecf20Sopenharmony_ci ant_toggle_cnt = 1; 29378c2ecf20Sopenharmony_ci } 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci /* Override next rate if needed for debug purposes */ 29408c2ecf20Sopenharmony_ci rs_dbgfs_set_mcs(lq_sta, &new_rate, index); 29418c2ecf20Sopenharmony_ci 29428c2ecf20Sopenharmony_ci /* Fill next table entry */ 29438c2ecf20Sopenharmony_ci lq_cmd->rs_table[index].rate_n_flags = 29448c2ecf20Sopenharmony_ci cpu_to_le32(new_rate); 29458c2ecf20Sopenharmony_ci repeat_rate--; 29468c2ecf20Sopenharmony_ci index++; 29478c2ecf20Sopenharmony_ci } 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_ci rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, 29508c2ecf20Sopenharmony_ci &rate_idx); 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_ci if (priv && priv->bt_full_concurrent) { 29538c2ecf20Sopenharmony_ci /* 1x1 only */ 29548c2ecf20Sopenharmony_ci tbl_type.ant_type = 29558c2ecf20Sopenharmony_ci first_antenna(priv->nvm_data->valid_tx_ant); 29568c2ecf20Sopenharmony_ci } 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci /* Indicate to uCode which entries might be MIMO. 29598c2ecf20Sopenharmony_ci * If initial rate was MIMO, this will finally end up 29608c2ecf20Sopenharmony_ci * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ 29618c2ecf20Sopenharmony_ci if (is_mimo(tbl_type.lq_type)) 29628c2ecf20Sopenharmony_ci lq_cmd->general_params.mimo_delimiter = index; 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci /* Get next rate */ 29658c2ecf20Sopenharmony_ci new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, 29668c2ecf20Sopenharmony_ci use_ht_possible); 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_ci /* How many times should we repeat the next rate? */ 29698c2ecf20Sopenharmony_ci if (is_legacy(tbl_type.lq_type)) { 29708c2ecf20Sopenharmony_ci if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) 29718c2ecf20Sopenharmony_ci ant_toggle_cnt++; 29728c2ecf20Sopenharmony_ci else if (priv && 29738c2ecf20Sopenharmony_ci rs_toggle_antenna(valid_tx_ant, 29748c2ecf20Sopenharmony_ci &new_rate, &tbl_type)) 29758c2ecf20Sopenharmony_ci ant_toggle_cnt = 1; 29768c2ecf20Sopenharmony_ci 29778c2ecf20Sopenharmony_ci repeat_rate = IWL_NUMBER_TRY; 29788c2ecf20Sopenharmony_ci } else { 29798c2ecf20Sopenharmony_ci repeat_rate = IWL_HT_NUMBER_TRY; 29808c2ecf20Sopenharmony_ci } 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci /* Don't allow HT rates after next pass. 29838c2ecf20Sopenharmony_ci * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ 29848c2ecf20Sopenharmony_ci use_ht_possible = 0; 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_ci /* Override next rate if needed for debug purposes */ 29878c2ecf20Sopenharmony_ci rs_dbgfs_set_mcs(lq_sta, &new_rate, index); 29888c2ecf20Sopenharmony_ci 29898c2ecf20Sopenharmony_ci /* Fill next table entry */ 29908c2ecf20Sopenharmony_ci lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci index++; 29938c2ecf20Sopenharmony_ci repeat_rate--; 29948c2ecf20Sopenharmony_ci } 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci lq_cmd->agg_params.agg_frame_cnt_limit = 29978c2ecf20Sopenharmony_ci sta_priv->max_agg_bufsize ?: LINK_QUAL_AGG_FRAME_LIMIT_DEF; 29988c2ecf20Sopenharmony_ci lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ci lq_cmd->agg_params.agg_time_limit = 30018c2ecf20Sopenharmony_ci cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); 30028c2ecf20Sopenharmony_ci /* 30038c2ecf20Sopenharmony_ci * overwrite if needed, pass aggregation time limit 30048c2ecf20Sopenharmony_ci * to uCode in uSec 30058c2ecf20Sopenharmony_ci */ 30068c2ecf20Sopenharmony_ci if (priv && priv->lib->bt_params && 30078c2ecf20Sopenharmony_ci priv->lib->bt_params->agg_time_limit && 30088c2ecf20Sopenharmony_ci priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) 30098c2ecf20Sopenharmony_ci lq_cmd->agg_params.agg_time_limit = 30108c2ecf20Sopenharmony_ci cpu_to_le16(priv->lib->bt_params->agg_time_limit); 30118c2ecf20Sopenharmony_ci} 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_cistatic void *rs_alloc(struct ieee80211_hw *hw) 30148c2ecf20Sopenharmony_ci{ 30158c2ecf20Sopenharmony_ci return hw->priv; 30168c2ecf20Sopenharmony_ci} 30178c2ecf20Sopenharmony_ci/* rate scale requires free function to be implemented */ 30188c2ecf20Sopenharmony_cistatic void rs_free(void *priv_rate) 30198c2ecf20Sopenharmony_ci{ 30208c2ecf20Sopenharmony_ci return; 30218c2ecf20Sopenharmony_ci} 30228c2ecf20Sopenharmony_ci 30238c2ecf20Sopenharmony_cistatic void rs_free_sta(void *priv_r, struct ieee80211_sta *sta, 30248c2ecf20Sopenharmony_ci void *priv_sta) 30258c2ecf20Sopenharmony_ci{ 30268c2ecf20Sopenharmony_ci struct iwl_op_mode *op_mode __maybe_unused = priv_r; 30278c2ecf20Sopenharmony_ci struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "enter\n"); 30308c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "leave\n"); 30318c2ecf20Sopenharmony_ci} 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 30348c2ecf20Sopenharmony_cistatic void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, 30358c2ecf20Sopenharmony_ci u32 *rate_n_flags, int index) 30368c2ecf20Sopenharmony_ci{ 30378c2ecf20Sopenharmony_ci struct iwl_priv *priv; 30388c2ecf20Sopenharmony_ci u8 valid_tx_ant; 30398c2ecf20Sopenharmony_ci u8 ant_sel_tx; 30408c2ecf20Sopenharmony_ci 30418c2ecf20Sopenharmony_ci priv = lq_sta->drv; 30428c2ecf20Sopenharmony_ci valid_tx_ant = priv->nvm_data->valid_tx_ant; 30438c2ecf20Sopenharmony_ci if (lq_sta->dbg_fixed_rate) { 30448c2ecf20Sopenharmony_ci ant_sel_tx = 30458c2ecf20Sopenharmony_ci ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) 30468c2ecf20Sopenharmony_ci >> RATE_MCS_ANT_POS); 30478c2ecf20Sopenharmony_ci if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { 30488c2ecf20Sopenharmony_ci *rate_n_flags = lq_sta->dbg_fixed_rate; 30498c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Fixed rate ON\n"); 30508c2ecf20Sopenharmony_ci } else { 30518c2ecf20Sopenharmony_ci lq_sta->dbg_fixed_rate = 0; 30528c2ecf20Sopenharmony_ci IWL_ERR(priv, 30538c2ecf20Sopenharmony_ci "Invalid antenna selection 0x%X, Valid is 0x%X\n", 30548c2ecf20Sopenharmony_ci ant_sel_tx, valid_tx_ant); 30558c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); 30568c2ecf20Sopenharmony_ci } 30578c2ecf20Sopenharmony_ci } else { 30588c2ecf20Sopenharmony_ci IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); 30598c2ecf20Sopenharmony_ci } 30608c2ecf20Sopenharmony_ci} 30618c2ecf20Sopenharmony_ci 30628c2ecf20Sopenharmony_cistatic ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, 30638c2ecf20Sopenharmony_ci const char __user *user_buf, size_t count, loff_t *ppos) 30648c2ecf20Sopenharmony_ci{ 30658c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 30668c2ecf20Sopenharmony_ci struct iwl_priv *priv; 30678c2ecf20Sopenharmony_ci char buf[64]; 30688c2ecf20Sopenharmony_ci size_t buf_size; 30698c2ecf20Sopenharmony_ci u32 parsed_rate; 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci 30728c2ecf20Sopenharmony_ci priv = lq_sta->drv; 30738c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(buf)); 30748c2ecf20Sopenharmony_ci buf_size = min(count, sizeof(buf) - 1); 30758c2ecf20Sopenharmony_ci if (copy_from_user(buf, user_buf, buf_size)) 30768c2ecf20Sopenharmony_ci return -EFAULT; 30778c2ecf20Sopenharmony_ci 30788c2ecf20Sopenharmony_ci if (sscanf(buf, "%x", &parsed_rate) == 1) 30798c2ecf20Sopenharmony_ci lq_sta->dbg_fixed_rate = parsed_rate; 30808c2ecf20Sopenharmony_ci else 30818c2ecf20Sopenharmony_ci lq_sta->dbg_fixed_rate = 0; 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci rs_program_fix_rate(priv, lq_sta); 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci return count; 30868c2ecf20Sopenharmony_ci} 30878c2ecf20Sopenharmony_ci 30888c2ecf20Sopenharmony_cistatic ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, 30898c2ecf20Sopenharmony_ci char __user *user_buf, size_t count, loff_t *ppos) 30908c2ecf20Sopenharmony_ci{ 30918c2ecf20Sopenharmony_ci char *buff; 30928c2ecf20Sopenharmony_ci int desc = 0; 30938c2ecf20Sopenharmony_ci int i = 0; 30948c2ecf20Sopenharmony_ci int index = 0; 30958c2ecf20Sopenharmony_ci ssize_t ret; 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 30988c2ecf20Sopenharmony_ci struct iwl_priv *priv; 30998c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci priv = lq_sta->drv; 31028c2ecf20Sopenharmony_ci buff = kmalloc(1024, GFP_KERNEL); 31038c2ecf20Sopenharmony_ci if (!buff) 31048c2ecf20Sopenharmony_ci return -ENOMEM; 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); 31078c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", 31088c2ecf20Sopenharmony_ci lq_sta->total_failed, lq_sta->total_success, 31098c2ecf20Sopenharmony_ci lq_sta->active_legacy_rate); 31108c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, "fixed rate 0x%X\n", 31118c2ecf20Sopenharmony_ci lq_sta->dbg_fixed_rate); 31128c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", 31138c2ecf20Sopenharmony_ci (priv->nvm_data->valid_tx_ant & ANT_A) ? "ANT_A," : "", 31148c2ecf20Sopenharmony_ci (priv->nvm_data->valid_tx_ant & ANT_B) ? "ANT_B," : "", 31158c2ecf20Sopenharmony_ci (priv->nvm_data->valid_tx_ant & ANT_C) ? "ANT_C" : ""); 31168c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, "lq type %s\n", 31178c2ecf20Sopenharmony_ci (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); 31188c2ecf20Sopenharmony_ci if (is_Ht(tbl->lq_type)) { 31198c2ecf20Sopenharmony_ci desc += sprintf(buff + desc, " %s", 31208c2ecf20Sopenharmony_ci (is_siso(tbl->lq_type)) ? "SISO" : 31218c2ecf20Sopenharmony_ci ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); 31228c2ecf20Sopenharmony_ci desc += sprintf(buff + desc, " %s", 31238c2ecf20Sopenharmony_ci (tbl->is_ht40) ? "40MHz" : "20MHz"); 31248c2ecf20Sopenharmony_ci desc += sprintf(buff + desc, " %s %s %s\n", 31258c2ecf20Sopenharmony_ci (tbl->is_SGI) ? "SGI" : "", 31268c2ecf20Sopenharmony_ci (lq_sta->is_green) ? "GF enabled" : "", 31278c2ecf20Sopenharmony_ci (lq_sta->is_agg) ? "AGG on" : ""); 31288c2ecf20Sopenharmony_ci } 31298c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, "last tx rate=0x%X\n", 31308c2ecf20Sopenharmony_ci lq_sta->last_rate_n_flags); 31318c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, "general:" 31328c2ecf20Sopenharmony_ci "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", 31338c2ecf20Sopenharmony_ci lq_sta->lq.general_params.flags, 31348c2ecf20Sopenharmony_ci lq_sta->lq.general_params.mimo_delimiter, 31358c2ecf20Sopenharmony_ci lq_sta->lq.general_params.single_stream_ant_msk, 31368c2ecf20Sopenharmony_ci lq_sta->lq.general_params.dual_stream_ant_msk); 31378c2ecf20Sopenharmony_ci 31388c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, "agg:" 31398c2ecf20Sopenharmony_ci "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", 31408c2ecf20Sopenharmony_ci le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), 31418c2ecf20Sopenharmony_ci lq_sta->lq.agg_params.agg_dis_start_th, 31428c2ecf20Sopenharmony_ci lq_sta->lq.agg_params.agg_frame_cnt_limit); 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, 31458c2ecf20Sopenharmony_ci "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", 31468c2ecf20Sopenharmony_ci lq_sta->lq.general_params.start_rate_index[0], 31478c2ecf20Sopenharmony_ci lq_sta->lq.general_params.start_rate_index[1], 31488c2ecf20Sopenharmony_ci lq_sta->lq.general_params.start_rate_index[2], 31498c2ecf20Sopenharmony_ci lq_sta->lq.general_params.start_rate_index[3]); 31508c2ecf20Sopenharmony_ci 31518c2ecf20Sopenharmony_ci for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { 31528c2ecf20Sopenharmony_ci index = iwl_hwrate_to_plcp_idx( 31538c2ecf20Sopenharmony_ci le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags)); 31548c2ecf20Sopenharmony_ci if (is_legacy(tbl->lq_type)) { 31558c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n", 31568c2ecf20Sopenharmony_ci i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), 31578c2ecf20Sopenharmony_ci iwl_rate_mcs[index].mbps); 31588c2ecf20Sopenharmony_ci } else { 31598c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps (%s)\n", 31608c2ecf20Sopenharmony_ci i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), 31618c2ecf20Sopenharmony_ci iwl_rate_mcs[index].mbps, iwl_rate_mcs[index].mcs); 31628c2ecf20Sopenharmony_ci } 31638c2ecf20Sopenharmony_ci } 31648c2ecf20Sopenharmony_ci 31658c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); 31668c2ecf20Sopenharmony_ci kfree(buff); 31678c2ecf20Sopenharmony_ci return ret; 31688c2ecf20Sopenharmony_ci} 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_cistatic const struct file_operations rs_sta_dbgfs_scale_table_ops = { 31718c2ecf20Sopenharmony_ci .write = rs_sta_dbgfs_scale_table_write, 31728c2ecf20Sopenharmony_ci .read = rs_sta_dbgfs_scale_table_read, 31738c2ecf20Sopenharmony_ci .open = simple_open, 31748c2ecf20Sopenharmony_ci .llseek = default_llseek, 31758c2ecf20Sopenharmony_ci}; 31768c2ecf20Sopenharmony_cistatic ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, 31778c2ecf20Sopenharmony_ci char __user *user_buf, size_t count, loff_t *ppos) 31788c2ecf20Sopenharmony_ci{ 31798c2ecf20Sopenharmony_ci char *buff; 31808c2ecf20Sopenharmony_ci int desc = 0; 31818c2ecf20Sopenharmony_ci int i, j; 31828c2ecf20Sopenharmony_ci ssize_t ret; 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 31858c2ecf20Sopenharmony_ci 31868c2ecf20Sopenharmony_ci buff = kmalloc(1024, GFP_KERNEL); 31878c2ecf20Sopenharmony_ci if (!buff) 31888c2ecf20Sopenharmony_ci return -ENOMEM; 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci for (i = 0; i < LQ_SIZE; i++) { 31918c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, 31928c2ecf20Sopenharmony_ci "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" 31938c2ecf20Sopenharmony_ci "rate=0x%X\n", 31948c2ecf20Sopenharmony_ci lq_sta->active_tbl == i ? "*" : "x", 31958c2ecf20Sopenharmony_ci lq_sta->lq_info[i].lq_type, 31968c2ecf20Sopenharmony_ci lq_sta->lq_info[i].is_SGI, 31978c2ecf20Sopenharmony_ci lq_sta->lq_info[i].is_ht40, 31988c2ecf20Sopenharmony_ci lq_sta->lq_info[i].is_dup, 31998c2ecf20Sopenharmony_ci lq_sta->is_green, 32008c2ecf20Sopenharmony_ci lq_sta->lq_info[i].current_rate); 32018c2ecf20Sopenharmony_ci for (j = 0; j < IWL_RATE_COUNT; j++) { 32028c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, 32038c2ecf20Sopenharmony_ci "counter=%d success=%d %%=%d\n", 32048c2ecf20Sopenharmony_ci lq_sta->lq_info[i].win[j].counter, 32058c2ecf20Sopenharmony_ci lq_sta->lq_info[i].win[j].success_counter, 32068c2ecf20Sopenharmony_ci lq_sta->lq_info[i].win[j].success_ratio); 32078c2ecf20Sopenharmony_ci } 32088c2ecf20Sopenharmony_ci } 32098c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); 32108c2ecf20Sopenharmony_ci kfree(buff); 32118c2ecf20Sopenharmony_ci return ret; 32128c2ecf20Sopenharmony_ci} 32138c2ecf20Sopenharmony_ci 32148c2ecf20Sopenharmony_cistatic const struct file_operations rs_sta_dbgfs_stats_table_ops = { 32158c2ecf20Sopenharmony_ci .read = rs_sta_dbgfs_stats_table_read, 32168c2ecf20Sopenharmony_ci .open = simple_open, 32178c2ecf20Sopenharmony_ci .llseek = default_llseek, 32188c2ecf20Sopenharmony_ci}; 32198c2ecf20Sopenharmony_ci 32208c2ecf20Sopenharmony_cistatic ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file, 32218c2ecf20Sopenharmony_ci char __user *user_buf, size_t count, loff_t *ppos) 32228c2ecf20Sopenharmony_ci{ 32238c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = file->private_data; 32248c2ecf20Sopenharmony_ci struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; 32258c2ecf20Sopenharmony_ci char buff[120]; 32268c2ecf20Sopenharmony_ci int desc = 0; 32278c2ecf20Sopenharmony_ci 32288c2ecf20Sopenharmony_ci if (is_Ht(tbl->lq_type)) 32298c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, 32308c2ecf20Sopenharmony_ci "Bit Rate= %d Mb/s\n", 32318c2ecf20Sopenharmony_ci tbl->expected_tpt[lq_sta->last_txrate_idx]); 32328c2ecf20Sopenharmony_ci else 32338c2ecf20Sopenharmony_ci desc += sprintf(buff+desc, 32348c2ecf20Sopenharmony_ci "Bit Rate= %d Mb/s\n", 32358c2ecf20Sopenharmony_ci iwl_rates[lq_sta->last_txrate_idx].ieee >> 1); 32368c2ecf20Sopenharmony_ci 32378c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buff, desc); 32388c2ecf20Sopenharmony_ci} 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_cistatic const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { 32418c2ecf20Sopenharmony_ci .read = rs_sta_dbgfs_rate_scale_data_read, 32428c2ecf20Sopenharmony_ci .open = simple_open, 32438c2ecf20Sopenharmony_ci .llseek = default_llseek, 32448c2ecf20Sopenharmony_ci}; 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_cistatic void rs_add_debugfs(void *priv, void *priv_sta, 32478c2ecf20Sopenharmony_ci struct dentry *dir) 32488c2ecf20Sopenharmony_ci{ 32498c2ecf20Sopenharmony_ci struct iwl_lq_sta *lq_sta = priv_sta; 32508c2ecf20Sopenharmony_ci 32518c2ecf20Sopenharmony_ci debugfs_create_file("rate_scale_table", 0600, dir, lq_sta, 32528c2ecf20Sopenharmony_ci &rs_sta_dbgfs_scale_table_ops); 32538c2ecf20Sopenharmony_ci debugfs_create_file("rate_stats_table", 0400, dir, lq_sta, 32548c2ecf20Sopenharmony_ci &rs_sta_dbgfs_stats_table_ops); 32558c2ecf20Sopenharmony_ci debugfs_create_file("rate_scale_data", 0400, dir, lq_sta, 32568c2ecf20Sopenharmony_ci &rs_sta_dbgfs_rate_scale_data_ops); 32578c2ecf20Sopenharmony_ci debugfs_create_u8("tx_agg_tid_enable", 0600, dir, 32588c2ecf20Sopenharmony_ci &lq_sta->tx_agg_tid_en); 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_ci} 32618c2ecf20Sopenharmony_ci#endif 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci/* 32648c2ecf20Sopenharmony_ci * Initialization of rate scaling information is done by driver after 32658c2ecf20Sopenharmony_ci * the station is added. Since mac80211 calls this function before a 32668c2ecf20Sopenharmony_ci * station is added we ignore it. 32678c2ecf20Sopenharmony_ci */ 32688c2ecf20Sopenharmony_cistatic void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, 32698c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef, 32708c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, void *priv_sta) 32718c2ecf20Sopenharmony_ci{ 32728c2ecf20Sopenharmony_ci} 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_cistatic const struct rate_control_ops rs_ops = { 32758c2ecf20Sopenharmony_ci .name = RS_NAME, 32768c2ecf20Sopenharmony_ci .tx_status = rs_tx_status, 32778c2ecf20Sopenharmony_ci .get_rate = rs_get_rate, 32788c2ecf20Sopenharmony_ci .rate_init = rs_rate_init_stub, 32798c2ecf20Sopenharmony_ci .alloc = rs_alloc, 32808c2ecf20Sopenharmony_ci .free = rs_free, 32818c2ecf20Sopenharmony_ci .alloc_sta = rs_alloc_sta, 32828c2ecf20Sopenharmony_ci .free_sta = rs_free_sta, 32838c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_DEBUGFS 32848c2ecf20Sopenharmony_ci .add_sta_debugfs = rs_add_debugfs, 32858c2ecf20Sopenharmony_ci#endif 32868c2ecf20Sopenharmony_ci}; 32878c2ecf20Sopenharmony_ci 32888c2ecf20Sopenharmony_ciint iwlagn_rate_control_register(void) 32898c2ecf20Sopenharmony_ci{ 32908c2ecf20Sopenharmony_ci return ieee80211_rate_control_register(&rs_ops); 32918c2ecf20Sopenharmony_ci} 32928c2ecf20Sopenharmony_ci 32938c2ecf20Sopenharmony_civoid iwlagn_rate_control_unregister(void) 32948c2ecf20Sopenharmony_ci{ 32958c2ecf20Sopenharmony_ci ieee80211_rate_control_unregister(&rs_ops); 32968c2ecf20Sopenharmony_ci} 32978c2ecf20Sopenharmony_ci 3298