18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * NXP Wireless LAN device driver: WMM 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2011-2020 NXP 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by NXP 78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License Version 2, June 1991 88c2ecf20Sopenharmony_ci * (the "License"). You may use, redistribute and/or modify this File in 98c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which 108c2ecf20Sopenharmony_ci * is available by writing to the Free Software Foundation, Inc., 118c2ecf20Sopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 128c2ecf20Sopenharmony_ci * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 158c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 168c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 178c2ecf20Sopenharmony_ci * this warranty disclaimer. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "decl.h" 218c2ecf20Sopenharmony_ci#include "ioctl.h" 228c2ecf20Sopenharmony_ci#include "util.h" 238c2ecf20Sopenharmony_ci#include "fw.h" 248c2ecf20Sopenharmony_ci#include "main.h" 258c2ecf20Sopenharmony_ci#include "wmm.h" 268c2ecf20Sopenharmony_ci#include "11n.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* Maximum value FW can accept for driver delay in packet transmission */ 308c2ecf20Sopenharmony_ci#define DRV_PKT_DELAY_TO_FW_MAX 512 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define WMM_QUEUED_PACKET_LOWER_LIMIT 180 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define WMM_QUEUED_PACKET_UPPER_LIMIT 200 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Offset for TOS field in the IP header */ 388c2ecf20Sopenharmony_ci#define IPTOS_OFFSET 5 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic bool disable_tx_amsdu; 418c2ecf20Sopenharmony_cimodule_param(disable_tx_amsdu, bool, 0644); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* This table inverses the tos_to_tid operation to get a priority 448c2ecf20Sopenharmony_ci * which is in sequential order, and can be compared. 458c2ecf20Sopenharmony_ci * Use this to compare the priority of two different TIDs. 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ciconst u8 tos_to_tid_inv[] = { 488c2ecf20Sopenharmony_ci 0x02, /* from tos_to_tid[2] = 0 */ 498c2ecf20Sopenharmony_ci 0x00, /* from tos_to_tid[0] = 1 */ 508c2ecf20Sopenharmony_ci 0x01, /* from tos_to_tid[1] = 2 */ 518c2ecf20Sopenharmony_ci 0x03, 528c2ecf20Sopenharmony_ci 0x04, 538c2ecf20Sopenharmony_ci 0x05, 548c2ecf20Sopenharmony_ci 0x06, 558c2ecf20Sopenharmony_ci 0x07 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* WMM information IE */ 598c2ecf20Sopenharmony_cistatic const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, 608c2ecf20Sopenharmony_ci 0x00, 0x50, 0xf2, 0x02, 618c2ecf20Sopenharmony_ci 0x00, 0x01, 0x00 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE, 658c2ecf20Sopenharmony_ci WMM_AC_BK, 668c2ecf20Sopenharmony_ci WMM_AC_VI, 678c2ecf20Sopenharmony_ci WMM_AC_VO 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic u8 tos_to_tid[] = { 718c2ecf20Sopenharmony_ci /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */ 728c2ecf20Sopenharmony_ci 0x01, /* 0 1 0 AC_BK */ 738c2ecf20Sopenharmony_ci 0x02, /* 0 0 0 AC_BK */ 748c2ecf20Sopenharmony_ci 0x00, /* 0 0 1 AC_BE */ 758c2ecf20Sopenharmony_ci 0x03, /* 0 1 1 AC_BE */ 768c2ecf20Sopenharmony_ci 0x04, /* 1 0 0 AC_VI */ 778c2ecf20Sopenharmony_ci 0x05, /* 1 0 1 AC_VI */ 788c2ecf20Sopenharmony_ci 0x06, /* 1 1 0 AC_VO */ 798c2ecf20Sopenharmony_ci 0x07 /* 1 1 1 AC_VO */ 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* 858c2ecf20Sopenharmony_ci * This function debug prints the priority parameters for a WMM AC. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_cistatic void 888c2ecf20Sopenharmony_cimwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci const char *ac_str[] = { "BK", "BE", "VI", "VO" }; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, " 938c2ecf20Sopenharmony_ci "EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", 948c2ecf20Sopenharmony_ci ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn_bitmap 958c2ecf20Sopenharmony_ci & MWIFIEX_ACI) >> 5]], 968c2ecf20Sopenharmony_ci (ac_param->aci_aifsn_bitmap & MWIFIEX_ACI) >> 5, 978c2ecf20Sopenharmony_ci (ac_param->aci_aifsn_bitmap & MWIFIEX_ACM) >> 4, 988c2ecf20Sopenharmony_ci ac_param->aci_aifsn_bitmap & MWIFIEX_AIFSN, 998c2ecf20Sopenharmony_ci ac_param->ecw_bitmap & MWIFIEX_ECW_MIN, 1008c2ecf20Sopenharmony_ci (ac_param->ecw_bitmap & MWIFIEX_ECW_MAX) >> 4, 1018c2ecf20Sopenharmony_ci le16_to_cpu(ac_param->tx_op_limit)); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * This function allocates a route address list. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * The function also initializes the list with the provided RA. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_cistatic struct mwifiex_ra_list_tbl * 1108c2ecf20Sopenharmony_cimwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ra_list = kzalloc(sizeof(struct mwifiex_ra_list_tbl), GFP_ATOMIC); 1158c2ecf20Sopenharmony_ci if (!ra_list) 1168c2ecf20Sopenharmony_ci return NULL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ra_list->list); 1198c2ecf20Sopenharmony_ci skb_queue_head_init(&ra_list->skb_head); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci memcpy(ra_list->ra, ra, ETH_ALEN); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ra_list->total_pkt_count = 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: allocated ra_list %p\n", ra_list); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return ra_list; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* This function returns random no between 16 and 32 to be used as threshold 1318c2ecf20Sopenharmony_ci * for no of packets after which BA setup is initiated. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_cistatic u8 mwifiex_get_random_ba_threshold(void) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci u64 ns; 1368c2ecf20Sopenharmony_ci /* setup ba_packet_threshold here random number between 1378c2ecf20Sopenharmony_ci * [BA_SETUP_PACKET_OFFSET, 1388c2ecf20Sopenharmony_ci * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci ns = ktime_get_ns(); 1418c2ecf20Sopenharmony_ci ns += (ns >> 32) + (ns >> 16); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* 1478c2ecf20Sopenharmony_ci * This function allocates and adds a RA list for all TIDs 1488c2ecf20Sopenharmony_ci * with the given RA. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_civoid mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int i; 1538c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list; 1548c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 1558c2ecf20Sopenharmony_ci struct mwifiex_sta_node *node; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NUM_TID; ++i) { 1598c2ecf20Sopenharmony_ci ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra); 1608c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 1618c2ecf20Sopenharmony_ci "info: created ra_list %p\n", ra_list); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (!ra_list) 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci ra_list->is_11n_enabled = 0; 1678c2ecf20Sopenharmony_ci ra_list->tdls_link = false; 1688c2ecf20Sopenharmony_ci ra_list->ba_status = BA_SETUP_NONE; 1698c2ecf20Sopenharmony_ci ra_list->amsdu_in_ampdu = false; 1708c2ecf20Sopenharmony_ci if (!mwifiex_queuing_ra_based(priv)) { 1718c2ecf20Sopenharmony_ci if (mwifiex_is_tdls_link_setup 1728c2ecf20Sopenharmony_ci (mwifiex_get_tdls_link_status(priv, ra))) { 1738c2ecf20Sopenharmony_ci ra_list->tdls_link = true; 1748c2ecf20Sopenharmony_ci ra_list->is_11n_enabled = 1758c2ecf20Sopenharmony_ci mwifiex_tdls_peer_11n_enabled(priv, ra); 1768c2ecf20Sopenharmony_ci } else { 1778c2ecf20Sopenharmony_ci ra_list->is_11n_enabled = IS_11N_ENABLED(priv); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci spin_lock_bh(&priv->sta_list_spinlock); 1818c2ecf20Sopenharmony_ci node = mwifiex_get_sta_entry(priv, ra); 1828c2ecf20Sopenharmony_ci if (node) 1838c2ecf20Sopenharmony_ci ra_list->tx_paused = node->tx_pause; 1848c2ecf20Sopenharmony_ci ra_list->is_11n_enabled = 1858c2ecf20Sopenharmony_ci mwifiex_is_sta_11n_enabled(priv, node); 1868c2ecf20Sopenharmony_ci if (ra_list->is_11n_enabled) 1878c2ecf20Sopenharmony_ci ra_list->max_amsdu = node->max_amsdu; 1888c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->sta_list_spinlock); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=%d\n", 1928c2ecf20Sopenharmony_ci ra_list, ra_list->is_11n_enabled); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (ra_list->is_11n_enabled) { 1958c2ecf20Sopenharmony_ci ra_list->ba_pkt_count = 0; 1968c2ecf20Sopenharmony_ci ra_list->ba_packet_thr = 1978c2ecf20Sopenharmony_ci mwifiex_get_random_ba_threshold(); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci list_add_tail(&ra_list->list, 2008c2ecf20Sopenharmony_ci &priv->wmm.tid_tbl_ptr[i].ra_list); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* 2058c2ecf20Sopenharmony_ci * This function sets the WMM queue priorities to their default values. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_cistatic void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci /* Default queue priorities: VO->VI->BE->BK */ 2108c2ecf20Sopenharmony_ci priv->wmm.queue_priority[0] = WMM_AC_VO; 2118c2ecf20Sopenharmony_ci priv->wmm.queue_priority[1] = WMM_AC_VI; 2128c2ecf20Sopenharmony_ci priv->wmm.queue_priority[2] = WMM_AC_BE; 2138c2ecf20Sopenharmony_ci priv->wmm.queue_priority[3] = WMM_AC_BK; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * This function map ACs to TIDs. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cistatic void 2208c2ecf20Sopenharmony_cimwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct mwifiex_wmm_desc *wmm = &priv->wmm; 2238c2ecf20Sopenharmony_ci u8 *queue_priority = wmm->queue_priority; 2248c2ecf20Sopenharmony_ci int i; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci for (i = 0; i < 4; ++i) { 2278c2ecf20Sopenharmony_ci tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1]; 2288c2ecf20Sopenharmony_ci tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0]; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NUM_TID; ++i) 2328c2ecf20Sopenharmony_ci priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/* 2388c2ecf20Sopenharmony_ci * This function initializes WMM priority queues. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_civoid 2418c2ecf20Sopenharmony_cimwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, 2428c2ecf20Sopenharmony_ci struct ieee_types_wmm_parameter *wmm_ie) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci u16 cw_min, avg_back_off, tmp[4]; 2458c2ecf20Sopenharmony_ci u32 i, j, num_ac; 2468c2ecf20Sopenharmony_ci u8 ac_idx; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!wmm_ie || !priv->wmm_enabled) { 2498c2ecf20Sopenharmony_ci /* WMM is not enabled, just set the defaults and return */ 2508c2ecf20Sopenharmony_ci mwifiex_wmm_default_queue_priorities(priv); 2518c2ecf20Sopenharmony_ci return; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, 2558c2ecf20Sopenharmony_ci "info: WMM Parameter IE: version=%d,\t" 2568c2ecf20Sopenharmony_ci "qos_info Parameter Set Count=%d, Reserved=%#x\n", 2578c2ecf20Sopenharmony_ci wmm_ie->version, wmm_ie->qos_info_bitmap & 2588c2ecf20Sopenharmony_ci IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, 2598c2ecf20Sopenharmony_ci wmm_ie->reserved); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac_params); num_ac++) { 2628c2ecf20Sopenharmony_ci u8 ecw = wmm_ie->ac_params[num_ac].ecw_bitmap; 2638c2ecf20Sopenharmony_ci u8 aci_aifsn = wmm_ie->ac_params[num_ac].aci_aifsn_bitmap; 2648c2ecf20Sopenharmony_ci cw_min = (1 << (ecw & MWIFIEX_ECW_MIN)) - 1; 2658c2ecf20Sopenharmony_ci avg_back_off = (cw_min >> 1) + (aci_aifsn & MWIFIEX_AIFSN); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ac_idx = wmm_aci_to_qidx_map[(aci_aifsn & MWIFIEX_ACI) >> 5]; 2688c2ecf20Sopenharmony_ci priv->wmm.queue_priority[ac_idx] = ac_idx; 2698c2ecf20Sopenharmony_ci tmp[ac_idx] = avg_back_off; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, 2728c2ecf20Sopenharmony_ci "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", 2738c2ecf20Sopenharmony_ci (1 << ((ecw & MWIFIEX_ECW_MAX) >> 4)) - 1, 2748c2ecf20Sopenharmony_ci cw_min, avg_back_off); 2758c2ecf20Sopenharmony_ci mwifiex_wmm_ac_debug_print(&wmm_ie->ac_params[num_ac]); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Bubble sort */ 2798c2ecf20Sopenharmony_ci for (i = 0; i < num_ac; i++) { 2808c2ecf20Sopenharmony_ci for (j = 1; j < num_ac - i; j++) { 2818c2ecf20Sopenharmony_ci if (tmp[j - 1] > tmp[j]) { 2828c2ecf20Sopenharmony_ci swap(tmp[j - 1], tmp[j]); 2838c2ecf20Sopenharmony_ci swap(priv->wmm.queue_priority[j - 1], 2848c2ecf20Sopenharmony_ci priv->wmm.queue_priority[j]); 2858c2ecf20Sopenharmony_ci } else if (tmp[j - 1] == tmp[j]) { 2868c2ecf20Sopenharmony_ci if (priv->wmm.queue_priority[j - 1] 2878c2ecf20Sopenharmony_ci < priv->wmm.queue_priority[j]) 2888c2ecf20Sopenharmony_ci swap(priv->wmm.queue_priority[j - 1], 2898c2ecf20Sopenharmony_ci priv->wmm.queue_priority[j]); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci mwifiex_wmm_queue_priorities_tid(priv); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * This function evaluates whether or not an AC is to be downgraded. 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * In case the AC is not enabled, the highest AC is returned that is 3018c2ecf20Sopenharmony_ci * enabled and does not require admission control. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic enum mwifiex_wmm_ac_e 3048c2ecf20Sopenharmony_cimwifiex_wmm_eval_downgrade_ac(struct mwifiex_private *priv, 3058c2ecf20Sopenharmony_ci enum mwifiex_wmm_ac_e eval_ac) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci int down_ac; 3088c2ecf20Sopenharmony_ci enum mwifiex_wmm_ac_e ret_ac; 3098c2ecf20Sopenharmony_ci struct mwifiex_wmm_ac_status *ac_status; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci ac_status = &priv->wmm.ac_status[eval_ac]; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (!ac_status->disabled) 3148c2ecf20Sopenharmony_ci /* Okay to use this AC, its enabled */ 3158c2ecf20Sopenharmony_ci return eval_ac; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Setup a default return value of the lowest priority */ 3188c2ecf20Sopenharmony_ci ret_ac = WMM_AC_BK; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* 3218c2ecf20Sopenharmony_ci * Find the highest AC that is enabled and does not require 3228c2ecf20Sopenharmony_ci * admission control. The spec disallows downgrading to an AC, 3238c2ecf20Sopenharmony_ci * which is enabled due to a completed admission control. 3248c2ecf20Sopenharmony_ci * Unadmitted traffic is not to be sent on an AC with admitted 3258c2ecf20Sopenharmony_ci * traffic. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) { 3288c2ecf20Sopenharmony_ci ac_status = &priv->wmm.ac_status[down_ac]; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (!ac_status->disabled && !ac_status->flow_required) 3318c2ecf20Sopenharmony_ci /* AC is enabled and does not require admission 3328c2ecf20Sopenharmony_ci control */ 3338c2ecf20Sopenharmony_ci ret_ac = (enum mwifiex_wmm_ac_e) down_ac; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return ret_ac; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/* 3408c2ecf20Sopenharmony_ci * This function downgrades WMM priority queue. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_civoid 3438c2ecf20Sopenharmony_cimwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci int ac_val; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, "info: WMM: AC Priorities:\t" 3488c2ecf20Sopenharmony_ci "BK(0), BE(1), VI(2), VO(3)\n"); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (!priv->wmm_enabled) { 3518c2ecf20Sopenharmony_ci /* WMM is not enabled, default priorities */ 3528c2ecf20Sopenharmony_ci for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) 3538c2ecf20Sopenharmony_ci priv->wmm.ac_down_graded_vals[ac_val] = 3548c2ecf20Sopenharmony_ci (enum mwifiex_wmm_ac_e) ac_val; 3558c2ecf20Sopenharmony_ci } else { 3568c2ecf20Sopenharmony_ci for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { 3578c2ecf20Sopenharmony_ci priv->wmm.ac_down_graded_vals[ac_val] 3588c2ecf20Sopenharmony_ci = mwifiex_wmm_eval_downgrade_ac(priv, 3598c2ecf20Sopenharmony_ci (enum mwifiex_wmm_ac_e) ac_val); 3608c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, 3618c2ecf20Sopenharmony_ci "info: WMM: AC PRIO %d maps to %d\n", 3628c2ecf20Sopenharmony_ci ac_val, 3638c2ecf20Sopenharmony_ci priv->wmm.ac_down_graded_vals[ac_val]); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* 3698c2ecf20Sopenharmony_ci * This function converts the IP TOS field to an WMM AC 3708c2ecf20Sopenharmony_ci * Queue assignment. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_cistatic enum mwifiex_wmm_ac_e 3738c2ecf20Sopenharmony_cimwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci /* Map of TOS UP values to WMM AC */ 3768c2ecf20Sopenharmony_ci static const enum mwifiex_wmm_ac_e tos_to_ac[] = { 3778c2ecf20Sopenharmony_ci WMM_AC_BE, 3788c2ecf20Sopenharmony_ci WMM_AC_BK, 3798c2ecf20Sopenharmony_ci WMM_AC_BK, 3808c2ecf20Sopenharmony_ci WMM_AC_BE, 3818c2ecf20Sopenharmony_ci WMM_AC_VI, 3828c2ecf20Sopenharmony_ci WMM_AC_VI, 3838c2ecf20Sopenharmony_ci WMM_AC_VO, 3848c2ecf20Sopenharmony_ci WMM_AC_VO 3858c2ecf20Sopenharmony_ci }; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (tos >= ARRAY_SIZE(tos_to_ac)) 3888c2ecf20Sopenharmony_ci return WMM_AC_BE; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return tos_to_ac[tos]; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* 3948c2ecf20Sopenharmony_ci * This function evaluates a given TID and downgrades it to a lower 3958c2ecf20Sopenharmony_ci * TID if the WMM Parameter IE received from the AP indicates that the 3968c2ecf20Sopenharmony_ci * AP is disabled (due to call admission control (ACM bit). Mapping 3978c2ecf20Sopenharmony_ci * of TID to AC is taken care of internally. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ciu8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci enum mwifiex_wmm_ac_e ac, ac_down; 4028c2ecf20Sopenharmony_ci u8 new_tid; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci ac = mwifiex_wmm_convert_tos_to_ac(priv->adapter, tid); 4058c2ecf20Sopenharmony_ci ac_down = priv->wmm.ac_down_graded_vals[ac]; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* Send the index to tid array, picking from the array will be 4088c2ecf20Sopenharmony_ci * taken care by dequeuing function 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci new_tid = ac_to_tid[ac_down][tid % 2]; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return new_tid; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci/* 4168c2ecf20Sopenharmony_ci * This function initializes the WMM state information and the 4178c2ecf20Sopenharmony_ci * WMM data path queues. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_civoid 4208c2ecf20Sopenharmony_cimwifiex_wmm_init(struct mwifiex_adapter *adapter) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci int i, j; 4238c2ecf20Sopenharmony_ci struct mwifiex_private *priv; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci for (j = 0; j < adapter->priv_num; ++j) { 4268c2ecf20Sopenharmony_ci priv = adapter->priv[j]; 4278c2ecf20Sopenharmony_ci if (!priv) 4288c2ecf20Sopenharmony_ci continue; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NUM_TID; ++i) { 4318c2ecf20Sopenharmony_ci if (!disable_tx_amsdu && 4328c2ecf20Sopenharmony_ci adapter->tx_buf_size > MWIFIEX_TX_DATA_BUF_SIZE_2K) 4338c2ecf20Sopenharmony_ci priv->aggr_prio_tbl[i].amsdu = 4348c2ecf20Sopenharmony_ci priv->tos_to_tid_inv[i]; 4358c2ecf20Sopenharmony_ci else 4368c2ecf20Sopenharmony_ci priv->aggr_prio_tbl[i].amsdu = 4378c2ecf20Sopenharmony_ci BA_STREAM_NOT_ALLOWED; 4388c2ecf20Sopenharmony_ci priv->aggr_prio_tbl[i].ampdu_ap = 4398c2ecf20Sopenharmony_ci priv->tos_to_tid_inv[i]; 4408c2ecf20Sopenharmony_ci priv->aggr_prio_tbl[i].ampdu_user = 4418c2ecf20Sopenharmony_ci priv->tos_to_tid_inv[i]; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci priv->aggr_prio_tbl[6].amsdu 4458c2ecf20Sopenharmony_ci = priv->aggr_prio_tbl[6].ampdu_ap 4468c2ecf20Sopenharmony_ci = priv->aggr_prio_tbl[6].ampdu_user 4478c2ecf20Sopenharmony_ci = BA_STREAM_NOT_ALLOWED; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap 4508c2ecf20Sopenharmony_ci = priv->aggr_prio_tbl[7].ampdu_user 4518c2ecf20Sopenharmony_ci = BA_STREAM_NOT_ALLOWED; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mwifiex_set_ba_params(priv); 4548c2ecf20Sopenharmony_ci mwifiex_reset_11n_rx_seq_num(priv); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci priv->wmm.drv_pkt_delay_max = MWIFIEX_WMM_DRV_DELAY_MAX; 4578c2ecf20Sopenharmony_ci atomic_set(&priv->wmm.tx_pkts_queued, 0); 4588c2ecf20Sopenharmony_ci atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciint mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct mwifiex_private *priv; 4658c2ecf20Sopenharmony_ci int i; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci for (i = 0; i < adapter->priv_num; i++) { 4688c2ecf20Sopenharmony_ci priv = adapter->priv[i]; 4698c2ecf20Sopenharmony_ci if (!priv) 4708c2ecf20Sopenharmony_ci continue; 4718c2ecf20Sopenharmony_ci if (adapter->if_ops.is_port_ready && 4728c2ecf20Sopenharmony_ci !adapter->if_ops.is_port_ready(priv)) 4738c2ecf20Sopenharmony_ci continue; 4748c2ecf20Sopenharmony_ci if (!skb_queue_empty(&priv->bypass_txq)) 4758c2ecf20Sopenharmony_ci return false; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return true; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci/* 4828c2ecf20Sopenharmony_ci * This function checks if WMM Tx queue is empty. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ciint 4858c2ecf20Sopenharmony_cimwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci int i; 4888c2ecf20Sopenharmony_ci struct mwifiex_private *priv; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci for (i = 0; i < adapter->priv_num; ++i) { 4918c2ecf20Sopenharmony_ci priv = adapter->priv[i]; 4928c2ecf20Sopenharmony_ci if (!priv) 4938c2ecf20Sopenharmony_ci continue; 4948c2ecf20Sopenharmony_ci if (!priv->port_open && 4958c2ecf20Sopenharmony_ci (priv->bss_mode != NL80211_IFTYPE_ADHOC)) 4968c2ecf20Sopenharmony_ci continue; 4978c2ecf20Sopenharmony_ci if (adapter->if_ops.is_port_ready && 4988c2ecf20Sopenharmony_ci !adapter->if_ops.is_port_ready(priv)) 4998c2ecf20Sopenharmony_ci continue; 5008c2ecf20Sopenharmony_ci if (atomic_read(&priv->wmm.tx_pkts_queued)) 5018c2ecf20Sopenharmony_ci return false; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return true; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci/* 5088c2ecf20Sopenharmony_ci * This function deletes all packets in an RA list node. 5098c2ecf20Sopenharmony_ci * 5108c2ecf20Sopenharmony_ci * The packet sent completion callback handler are called with 5118c2ecf20Sopenharmony_ci * status failure, after they are dequeued to ensure proper 5128c2ecf20Sopenharmony_ci * cleanup. The RA list node itself is freed at the end. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_cistatic void 5158c2ecf20Sopenharmony_cimwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, 5168c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 5198c2ecf20Sopenharmony_ci struct sk_buff *skb, *tmp; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) { 5228c2ecf20Sopenharmony_ci skb_unlink(skb, &ra_list->skb_head); 5238c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb, 0, -1); 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/* 5288c2ecf20Sopenharmony_ci * This function deletes all packets in an RA list. 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * Each nodes in the RA list are freed individually first, and then 5318c2ecf20Sopenharmony_ci * the RA list itself is freed. 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_cistatic void 5348c2ecf20Sopenharmony_cimwifiex_wmm_del_pkts_in_ralist(struct mwifiex_private *priv, 5358c2ecf20Sopenharmony_ci struct list_head *ra_list_head) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci list_for_each_entry(ra_list, ra_list_head, list) 5408c2ecf20Sopenharmony_ci mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/* 5448c2ecf20Sopenharmony_ci * This function deletes all packets in all RA lists. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_cistatic void mwifiex_wmm_cleanup_queues(struct mwifiex_private *priv) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci int i; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NUM_TID; i++) 5518c2ecf20Sopenharmony_ci mwifiex_wmm_del_pkts_in_ralist(priv, &priv->wmm.tid_tbl_ptr[i]. 5528c2ecf20Sopenharmony_ci ra_list); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci atomic_set(&priv->wmm.tx_pkts_queued, 0); 5558c2ecf20Sopenharmony_ci atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci/* 5598c2ecf20Sopenharmony_ci * This function deletes all route addresses from all RA lists. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_cistatic void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list, *tmp_node; 5648c2ecf20Sopenharmony_ci int i; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NUM_TID; ++i) { 5678c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, 5688c2ecf20Sopenharmony_ci "info: ra_list: freeing buf for tid %d\n", i); 5698c2ecf20Sopenharmony_ci list_for_each_entry_safe(ra_list, tmp_node, 5708c2ecf20Sopenharmony_ci &priv->wmm.tid_tbl_ptr[i].ra_list, 5718c2ecf20Sopenharmony_ci list) { 5728c2ecf20Sopenharmony_ci list_del(&ra_list->list); 5738c2ecf20Sopenharmony_ci kfree(ra_list); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list); 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic int mwifiex_free_ack_frame(int id, void *p, void *data) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci pr_warn("Have pending ack frames!\n"); 5838c2ecf20Sopenharmony_ci kfree_skb(p); 5848c2ecf20Sopenharmony_ci return 0; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/* 5888c2ecf20Sopenharmony_ci * This function cleans up the Tx and Rx queues. 5898c2ecf20Sopenharmony_ci * 5908c2ecf20Sopenharmony_ci * Cleanup includes - 5918c2ecf20Sopenharmony_ci * - All packets in RA lists 5928c2ecf20Sopenharmony_ci * - All entries in Rx reorder table 5938c2ecf20Sopenharmony_ci * - All entries in Tx BA stream table 5948c2ecf20Sopenharmony_ci * - MPA buffer (if required) 5958c2ecf20Sopenharmony_ci * - All RA lists 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_civoid 5988c2ecf20Sopenharmony_cimwifiex_clean_txrx(struct mwifiex_private *priv) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct sk_buff *skb, *tmp; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci mwifiex_11n_cleanup_reorder_tbl(priv); 6038c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci mwifiex_wmm_cleanup_queues(priv); 6068c2ecf20Sopenharmony_ci mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (priv->adapter->if_ops.cleanup_mpa_buf) 6098c2ecf20Sopenharmony_ci priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci mwifiex_wmm_delete_all_ralist(priv); 6128c2ecf20Sopenharmony_ci memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (priv->adapter->if_ops.clean_pcie_ring && 6158c2ecf20Sopenharmony_ci !test_bit(MWIFIEX_SURPRISE_REMOVED, &priv->adapter->work_flags)) 6168c2ecf20Sopenharmony_ci priv->adapter->if_ops.clean_pcie_ring(priv->adapter); 6178c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { 6208c2ecf20Sopenharmony_ci skb_unlink(skb, &priv->tdls_txq); 6218c2ecf20Sopenharmony_ci mwifiex_write_data_complete(priv->adapter, skb, 0, -1); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) { 6258c2ecf20Sopenharmony_ci skb_unlink(skb, &priv->bypass_txq); 6268c2ecf20Sopenharmony_ci mwifiex_write_data_complete(priv->adapter, skb, 0, -1); 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci atomic_set(&priv->adapter->bypass_tx_pending, 0); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL); 6318c2ecf20Sopenharmony_ci idr_destroy(&priv->ack_status_frames); 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci/* 6358c2ecf20Sopenharmony_ci * This function retrieves a particular RA list node, matching with the 6368c2ecf20Sopenharmony_ci * given TID and RA address. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_cistruct mwifiex_ra_list_tbl * 6398c2ecf20Sopenharmony_cimwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, 6408c2ecf20Sopenharmony_ci const u8 *ra_addr) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list, 6458c2ecf20Sopenharmony_ci list) { 6468c2ecf20Sopenharmony_ci if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN)) 6478c2ecf20Sopenharmony_ci return ra_list; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return NULL; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_civoid mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, 6548c2ecf20Sopenharmony_ci u8 tx_pause) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list; 6578c2ecf20Sopenharmony_ci u32 pkt_cnt = 0, tx_pkts_queued; 6588c2ecf20Sopenharmony_ci int i; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NUM_TID; ++i) { 6638c2ecf20Sopenharmony_ci ra_list = mwifiex_wmm_get_ralist_node(priv, i, mac); 6648c2ecf20Sopenharmony_ci if (ra_list && ra_list->tx_paused != tx_pause) { 6658c2ecf20Sopenharmony_ci pkt_cnt += ra_list->total_pkt_count; 6668c2ecf20Sopenharmony_ci ra_list->tx_paused = tx_pause; 6678c2ecf20Sopenharmony_ci if (tx_pause) 6688c2ecf20Sopenharmony_ci priv->wmm.pkts_paused[i] += 6698c2ecf20Sopenharmony_ci ra_list->total_pkt_count; 6708c2ecf20Sopenharmony_ci else 6718c2ecf20Sopenharmony_ci priv->wmm.pkts_paused[i] -= 6728c2ecf20Sopenharmony_ci ra_list->total_pkt_count; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (pkt_cnt) { 6778c2ecf20Sopenharmony_ci tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); 6788c2ecf20Sopenharmony_ci if (tx_pause) 6798c2ecf20Sopenharmony_ci tx_pkts_queued -= pkt_cnt; 6808c2ecf20Sopenharmony_ci else 6818c2ecf20Sopenharmony_ci tx_pkts_queued += pkt_cnt; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); 6848c2ecf20Sopenharmony_ci atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/* This function updates non-tdls peer ralist tx_pause while 6908c2ecf20Sopenharmony_ci * tdls channel switching 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_civoid mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, 6938c2ecf20Sopenharmony_ci u8 *mac, u8 tx_pause) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list; 6968c2ecf20Sopenharmony_ci u32 pkt_cnt = 0, tx_pkts_queued; 6978c2ecf20Sopenharmony_ci int i; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NUM_TID; ++i) { 7028c2ecf20Sopenharmony_ci list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[i].ra_list, 7038c2ecf20Sopenharmony_ci list) { 7048c2ecf20Sopenharmony_ci if (!memcmp(ra_list->ra, mac, ETH_ALEN)) 7058c2ecf20Sopenharmony_ci continue; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (ra_list->tx_paused != tx_pause) { 7088c2ecf20Sopenharmony_ci pkt_cnt += ra_list->total_pkt_count; 7098c2ecf20Sopenharmony_ci ra_list->tx_paused = tx_pause; 7108c2ecf20Sopenharmony_ci if (tx_pause) 7118c2ecf20Sopenharmony_ci priv->wmm.pkts_paused[i] += 7128c2ecf20Sopenharmony_ci ra_list->total_pkt_count; 7138c2ecf20Sopenharmony_ci else 7148c2ecf20Sopenharmony_ci priv->wmm.pkts_paused[i] -= 7158c2ecf20Sopenharmony_ci ra_list->total_pkt_count; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (pkt_cnt) { 7218c2ecf20Sopenharmony_ci tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); 7228c2ecf20Sopenharmony_ci if (tx_pause) 7238c2ecf20Sopenharmony_ci tx_pkts_queued -= pkt_cnt; 7248c2ecf20Sopenharmony_ci else 7258c2ecf20Sopenharmony_ci tx_pkts_queued += pkt_cnt; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); 7288c2ecf20Sopenharmony_ci atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/* 7348c2ecf20Sopenharmony_ci * This function retrieves an RA list node for a given TID and 7358c2ecf20Sopenharmony_ci * RA address pair. 7368c2ecf20Sopenharmony_ci * 7378c2ecf20Sopenharmony_ci * If no such node is found, a new node is added first and then 7388c2ecf20Sopenharmony_ci * retrieved. 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_cistruct mwifiex_ra_list_tbl * 7418c2ecf20Sopenharmony_cimwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, 7428c2ecf20Sopenharmony_ci const u8 *ra_addr) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); 7478c2ecf20Sopenharmony_ci if (ra_list) 7488c2ecf20Sopenharmony_ci return ra_list; 7498c2ecf20Sopenharmony_ci mwifiex_ralist_add(priv, ra_addr); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci/* 7558c2ecf20Sopenharmony_ci * This function deletes RA list nodes for given mac for all TIDs. 7568c2ecf20Sopenharmony_ci * Function also decrements TX pending count accordingly. 7578c2ecf20Sopenharmony_ci */ 7588c2ecf20Sopenharmony_civoid 7598c2ecf20Sopenharmony_cimwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list; 7628c2ecf20Sopenharmony_ci int i; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci for (i = 0; i < MAX_NUM_TID; ++i) { 7678c2ecf20Sopenharmony_ci ra_list = mwifiex_wmm_get_ralist_node(priv, i, ra_addr); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (!ra_list) 7708c2ecf20Sopenharmony_ci continue; 7718c2ecf20Sopenharmony_ci mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); 7728c2ecf20Sopenharmony_ci if (ra_list->tx_paused) 7738c2ecf20Sopenharmony_ci priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count; 7748c2ecf20Sopenharmony_ci else 7758c2ecf20Sopenharmony_ci atomic_sub(ra_list->total_pkt_count, 7768c2ecf20Sopenharmony_ci &priv->wmm.tx_pkts_queued); 7778c2ecf20Sopenharmony_ci list_del(&ra_list->list); 7788c2ecf20Sopenharmony_ci kfree(ra_list); 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci/* 7848c2ecf20Sopenharmony_ci * This function checks if a particular RA list node exists in a given TID 7858c2ecf20Sopenharmony_ci * table index. 7868c2ecf20Sopenharmony_ci */ 7878c2ecf20Sopenharmony_ciint 7888c2ecf20Sopenharmony_cimwifiex_is_ralist_valid(struct mwifiex_private *priv, 7898c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list, int ptr_index) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *rlist; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list, 7948c2ecf20Sopenharmony_ci list) { 7958c2ecf20Sopenharmony_ci if (rlist == ra_list) 7968c2ecf20Sopenharmony_ci return true; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci return false; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci/* 8038c2ecf20Sopenharmony_ci * This function adds a packet to bypass TX queue. 8048c2ecf20Sopenharmony_ci * This is special TX queue for packets which can be sent even when port_open 8058c2ecf20Sopenharmony_ci * is false. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_civoid 8088c2ecf20Sopenharmony_cimwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv, 8098c2ecf20Sopenharmony_ci struct sk_buff *skb) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci skb_queue_tail(&priv->bypass_txq, skb); 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci/* 8158c2ecf20Sopenharmony_ci * This function adds a packet to WMM queue. 8168c2ecf20Sopenharmony_ci * 8178c2ecf20Sopenharmony_ci * In disconnected state the packet is immediately dropped and the 8188c2ecf20Sopenharmony_ci * packet send completion callback is called with status failure. 8198c2ecf20Sopenharmony_ci * 8208c2ecf20Sopenharmony_ci * Otherwise, the correct RA list node is located and the packet 8218c2ecf20Sopenharmony_ci * is queued at the list tail. 8228c2ecf20Sopenharmony_ci */ 8238c2ecf20Sopenharmony_civoid 8248c2ecf20Sopenharmony_cimwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, 8258c2ecf20Sopenharmony_ci struct sk_buff *skb) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 8288c2ecf20Sopenharmony_ci u32 tid; 8298c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra_list; 8308c2ecf20Sopenharmony_ci u8 ra[ETH_ALEN], tid_down; 8318c2ecf20Sopenharmony_ci struct list_head list_head; 8328c2ecf20Sopenharmony_ci int tdls_status = TDLS_NOT_SETUP; 8338c2ecf20Sopenharmony_ci struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; 8348c2ecf20Sopenharmony_ci struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci memcpy(ra, eth_hdr->h_dest, ETH_ALEN); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && 8398c2ecf20Sopenharmony_ci ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) { 8408c2ecf20Sopenharmony_ci if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS) 8418c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 8428c2ecf20Sopenharmony_ci "TDLS setup packet for %pM.\t" 8438c2ecf20Sopenharmony_ci "Don't block\n", ra); 8448c2ecf20Sopenharmony_ci else if (memcmp(priv->cfg_bssid, ra, ETH_ALEN)) 8458c2ecf20Sopenharmony_ci tdls_status = mwifiex_get_tdls_link_status(priv, ra); 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) { 8498c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, "data: drop packet in disconnect\n"); 8508c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb, 0, -1); 8518c2ecf20Sopenharmony_ci return; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci tid = skb->priority; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci tid_down = mwifiex_wmm_downgrade_tid(priv, tid); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci /* In case of infra as we have already created the list during 8618c2ecf20Sopenharmony_ci association we just don't have to call get_queue_raptr, we will 8628c2ecf20Sopenharmony_ci have only 1 raptr for a tid in case of infra */ 8638c2ecf20Sopenharmony_ci if (!mwifiex_queuing_ra_based(priv) && 8648c2ecf20Sopenharmony_ci !mwifiex_is_skb_mgmt_frame(skb)) { 8658c2ecf20Sopenharmony_ci switch (tdls_status) { 8668c2ecf20Sopenharmony_ci case TDLS_SETUP_COMPLETE: 8678c2ecf20Sopenharmony_ci case TDLS_CHAN_SWITCHING: 8688c2ecf20Sopenharmony_ci case TDLS_IN_BASE_CHAN: 8698c2ecf20Sopenharmony_ci case TDLS_IN_OFF_CHAN: 8708c2ecf20Sopenharmony_ci ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, 8718c2ecf20Sopenharmony_ci ra); 8728c2ecf20Sopenharmony_ci tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; 8738c2ecf20Sopenharmony_ci break; 8748c2ecf20Sopenharmony_ci case TDLS_SETUP_INPROGRESS: 8758c2ecf20Sopenharmony_ci skb_queue_tail(&priv->tdls_txq, skb); 8768c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 8778c2ecf20Sopenharmony_ci return; 8788c2ecf20Sopenharmony_ci default: 8798c2ecf20Sopenharmony_ci list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list; 8808c2ecf20Sopenharmony_ci ra_list = list_first_entry_or_null(&list_head, 8818c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl, list); 8828c2ecf20Sopenharmony_ci break; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci } else { 8858c2ecf20Sopenharmony_ci memcpy(ra, skb->data, ETH_ALEN); 8868c2ecf20Sopenharmony_ci if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb)) 8878c2ecf20Sopenharmony_ci eth_broadcast_addr(ra); 8888c2ecf20Sopenharmony_ci ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra); 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (!ra_list) { 8928c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 8938c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb, 0, -1); 8948c2ecf20Sopenharmony_ci return; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci skb_queue_tail(&ra_list->skb_head, skb); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci ra_list->ba_pkt_count++; 9008c2ecf20Sopenharmony_ci ra_list->total_pkt_count++; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (atomic_read(&priv->wmm.highest_queued_prio) < 9038c2ecf20Sopenharmony_ci priv->tos_to_tid_inv[tid_down]) 9048c2ecf20Sopenharmony_ci atomic_set(&priv->wmm.highest_queued_prio, 9058c2ecf20Sopenharmony_ci priv->tos_to_tid_inv[tid_down]); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (ra_list->tx_paused) 9088c2ecf20Sopenharmony_ci priv->wmm.pkts_paused[tid_down]++; 9098c2ecf20Sopenharmony_ci else 9108c2ecf20Sopenharmony_ci atomic_inc(&priv->wmm.tx_pkts_queued); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci/* 9168c2ecf20Sopenharmony_ci * This function processes the get WMM status command response from firmware. 9178c2ecf20Sopenharmony_ci * 9188c2ecf20Sopenharmony_ci * The response may contain multiple TLVs - 9198c2ecf20Sopenharmony_ci * - AC Queue status TLVs 9208c2ecf20Sopenharmony_ci * - Current WMM Parameter IE TLV 9218c2ecf20Sopenharmony_ci * - Admission Control action frame TLVs 9228c2ecf20Sopenharmony_ci * 9238c2ecf20Sopenharmony_ci * This function parses the TLVs and then calls further specific functions 9248c2ecf20Sopenharmony_ci * to process any changes in the queue prioritize or state. 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_ciint mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, 9278c2ecf20Sopenharmony_ci const struct host_cmd_ds_command *resp) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci u8 *curr = (u8 *) &resp->params.get_wmm_status; 9308c2ecf20Sopenharmony_ci uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; 9318c2ecf20Sopenharmony_ci int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; 9328c2ecf20Sopenharmony_ci bool valid = true; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci struct mwifiex_ie_types_data *tlv_hdr; 9358c2ecf20Sopenharmony_ci struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; 9368c2ecf20Sopenharmony_ci struct ieee_types_wmm_parameter *wmm_param_ie = NULL; 9378c2ecf20Sopenharmony_ci struct mwifiex_wmm_ac_status *ac_status; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, 9408c2ecf20Sopenharmony_ci "info: WMM: WMM_GET_STATUS cmdresp received: %d\n", 9418c2ecf20Sopenharmony_ci resp_len); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci while ((resp_len >= sizeof(tlv_hdr->header)) && valid) { 9448c2ecf20Sopenharmony_ci tlv_hdr = (struct mwifiex_ie_types_data *) curr; 9458c2ecf20Sopenharmony_ci tlv_len = le16_to_cpu(tlv_hdr->header.len); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (resp_len < tlv_len + sizeof(tlv_hdr->header)) 9488c2ecf20Sopenharmony_ci break; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci switch (le16_to_cpu(tlv_hdr->header.type)) { 9518c2ecf20Sopenharmony_ci case TLV_TYPE_WMMQSTATUS: 9528c2ecf20Sopenharmony_ci tlv_wmm_qstatus = 9538c2ecf20Sopenharmony_ci (struct mwifiex_ie_types_wmm_queue_status *) 9548c2ecf20Sopenharmony_ci tlv_hdr; 9558c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, CMD, 9568c2ecf20Sopenharmony_ci "info: CMD_RESP: WMM_GET_STATUS:\t" 9578c2ecf20Sopenharmony_ci "QSTATUS TLV: %d, %d, %d\n", 9588c2ecf20Sopenharmony_ci tlv_wmm_qstatus->queue_index, 9598c2ecf20Sopenharmony_ci tlv_wmm_qstatus->flow_required, 9608c2ecf20Sopenharmony_ci tlv_wmm_qstatus->disabled); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci ac_status = &priv->wmm.ac_status[tlv_wmm_qstatus-> 9638c2ecf20Sopenharmony_ci queue_index]; 9648c2ecf20Sopenharmony_ci ac_status->disabled = tlv_wmm_qstatus->disabled; 9658c2ecf20Sopenharmony_ci ac_status->flow_required = 9668c2ecf20Sopenharmony_ci tlv_wmm_qstatus->flow_required; 9678c2ecf20Sopenharmony_ci ac_status->flow_created = tlv_wmm_qstatus->flow_created; 9688c2ecf20Sopenharmony_ci break; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci case WLAN_EID_VENDOR_SPECIFIC: 9718c2ecf20Sopenharmony_ci /* 9728c2ecf20Sopenharmony_ci * Point the regular IEEE IE 2 bytes into the Marvell IE 9738c2ecf20Sopenharmony_ci * and setup the IEEE IE type and length byte fields 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci wmm_param_ie = 9778c2ecf20Sopenharmony_ci (struct ieee_types_wmm_parameter *) (curr + 9788c2ecf20Sopenharmony_ci 2); 9798c2ecf20Sopenharmony_ci wmm_param_ie->vend_hdr.len = (u8) tlv_len; 9808c2ecf20Sopenharmony_ci wmm_param_ie->vend_hdr.element_id = 9818c2ecf20Sopenharmony_ci WLAN_EID_VENDOR_SPECIFIC; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, CMD, 9848c2ecf20Sopenharmony_ci "info: CMD_RESP: WMM_GET_STATUS:\t" 9858c2ecf20Sopenharmony_ci "WMM Parameter Set Count: %d\n", 9868c2ecf20Sopenharmony_ci wmm_param_ie->qos_info_bitmap & mask); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (wmm_param_ie->vend_hdr.len + 2 > 9898c2ecf20Sopenharmony_ci sizeof(struct ieee_types_wmm_parameter)) 9908c2ecf20Sopenharmony_ci break; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. 9938c2ecf20Sopenharmony_ci wmm_ie, wmm_param_ie, 9948c2ecf20Sopenharmony_ci wmm_param_ie->vend_hdr.len + 2); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci break; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci default: 9998c2ecf20Sopenharmony_ci valid = false; 10008c2ecf20Sopenharmony_ci break; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci curr += (tlv_len + sizeof(tlv_hdr->header)); 10048c2ecf20Sopenharmony_ci resp_len -= (tlv_len + sizeof(tlv_hdr->header)); 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); 10088c2ecf20Sopenharmony_ci mwifiex_wmm_setup_ac_downgrade(priv); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci return 0; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci/* 10148c2ecf20Sopenharmony_ci * Callback handler from the command module to allow insertion of a WMM TLV. 10158c2ecf20Sopenharmony_ci * 10168c2ecf20Sopenharmony_ci * If the BSS we are associating to supports WMM, this function adds the 10178c2ecf20Sopenharmony_ci * required WMM Information IE to the association request command buffer in 10188c2ecf20Sopenharmony_ci * the form of a Marvell extended IEEE IE. 10198c2ecf20Sopenharmony_ci */ 10208c2ecf20Sopenharmony_ciu32 10218c2ecf20Sopenharmony_cimwifiex_wmm_process_association_req(struct mwifiex_private *priv, 10228c2ecf20Sopenharmony_ci u8 **assoc_buf, 10238c2ecf20Sopenharmony_ci struct ieee_types_wmm_parameter *wmm_ie, 10248c2ecf20Sopenharmony_ci struct ieee80211_ht_cap *ht_cap) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci struct mwifiex_ie_types_wmm_param_set *wmm_tlv; 10278c2ecf20Sopenharmony_ci u32 ret_len = 0; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* Null checks */ 10308c2ecf20Sopenharmony_ci if (!assoc_buf) 10318c2ecf20Sopenharmony_ci return 0; 10328c2ecf20Sopenharmony_ci if (!(*assoc_buf)) 10338c2ecf20Sopenharmony_ci return 0; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci if (!wmm_ie) 10368c2ecf20Sopenharmony_ci return 0; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, INFO, 10398c2ecf20Sopenharmony_ci "info: WMM: process assoc req: bss->wmm_ie=%#x\n", 10408c2ecf20Sopenharmony_ci wmm_ie->vend_hdr.element_id); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if ((priv->wmm_required || 10438c2ecf20Sopenharmony_ci (ht_cap && (priv->adapter->config_bands & BAND_GN || 10448c2ecf20Sopenharmony_ci priv->adapter->config_bands & BAND_AN))) && 10458c2ecf20Sopenharmony_ci wmm_ie->vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) { 10468c2ecf20Sopenharmony_ci wmm_tlv = (struct mwifiex_ie_types_wmm_param_set *) *assoc_buf; 10478c2ecf20Sopenharmony_ci wmm_tlv->header.type = cpu_to_le16((u16) wmm_info_ie[0]); 10488c2ecf20Sopenharmony_ci wmm_tlv->header.len = cpu_to_le16((u16) wmm_info_ie[1]); 10498c2ecf20Sopenharmony_ci memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2], 10508c2ecf20Sopenharmony_ci le16_to_cpu(wmm_tlv->header.len)); 10518c2ecf20Sopenharmony_ci if (wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) 10528c2ecf20Sopenharmony_ci memcpy((u8 *) (wmm_tlv->wmm_ie 10538c2ecf20Sopenharmony_ci + le16_to_cpu(wmm_tlv->header.len) 10548c2ecf20Sopenharmony_ci - sizeof(priv->wmm_qosinfo)), 10558c2ecf20Sopenharmony_ci &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo)); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci ret_len = sizeof(wmm_tlv->header) 10588c2ecf20Sopenharmony_ci + le16_to_cpu(wmm_tlv->header.len); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci *assoc_buf += ret_len; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci return ret_len; 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci/* 10678c2ecf20Sopenharmony_ci * This function computes the time delay in the driver queues for a 10688c2ecf20Sopenharmony_ci * given packet. 10698c2ecf20Sopenharmony_ci * 10708c2ecf20Sopenharmony_ci * When the packet is received at the OS/Driver interface, the current 10718c2ecf20Sopenharmony_ci * time is set in the packet structure. The difference between the present 10728c2ecf20Sopenharmony_ci * time and that received time is computed in this function and limited 10738c2ecf20Sopenharmony_ci * based on pre-compiled limits in the driver. 10748c2ecf20Sopenharmony_ci */ 10758c2ecf20Sopenharmony_ciu8 10768c2ecf20Sopenharmony_cimwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, 10778c2ecf20Sopenharmony_ci const struct sk_buff *skb) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp)); 10808c2ecf20Sopenharmony_ci u8 ret_val; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* 10838c2ecf20Sopenharmony_ci * Queue delay is passed as a uint8 in units of 2ms (ms shifted 10848c2ecf20Sopenharmony_ci * by 1). Min value (other than 0) is therefore 2ms, max is 510ms. 10858c2ecf20Sopenharmony_ci * 10868c2ecf20Sopenharmony_ci * Pass max value if queue_delay is beyond the uint8 range 10878c2ecf20Sopenharmony_ci */ 10888c2ecf20Sopenharmony_ci ret_val = (u8) (min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci mwifiex_dbg(priv->adapter, DATA, "data: WMM: Pkt Delay: %d ms,\t" 10918c2ecf20Sopenharmony_ci "%d ms sent to FW\n", queue_delay, ret_val); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci return ret_val; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci/* 10978c2ecf20Sopenharmony_ci * This function retrieves the highest priority RA list table pointer. 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_cistatic struct mwifiex_ra_list_tbl * 11008c2ecf20Sopenharmony_cimwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, 11018c2ecf20Sopenharmony_ci struct mwifiex_private **priv, int *tid) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci struct mwifiex_private *priv_tmp; 11048c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ptr; 11058c2ecf20Sopenharmony_ci struct mwifiex_tid_tbl *tid_ptr; 11068c2ecf20Sopenharmony_ci atomic_t *hqp; 11078c2ecf20Sopenharmony_ci int i, j; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* check the BSS with highest priority first */ 11108c2ecf20Sopenharmony_ci for (j = adapter->priv_num - 1; j >= 0; --j) { 11118c2ecf20Sopenharmony_ci /* iterate over BSS with the equal priority */ 11128c2ecf20Sopenharmony_ci list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur, 11138c2ecf20Sopenharmony_ci &adapter->bss_prio_tbl[j].bss_prio_head, 11148c2ecf20Sopenharmony_ci list) { 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_citry_again: 11178c2ecf20Sopenharmony_ci priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (((priv_tmp->bss_mode != NL80211_IFTYPE_ADHOC) && 11208c2ecf20Sopenharmony_ci !priv_tmp->port_open) || 11218c2ecf20Sopenharmony_ci (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)) 11228c2ecf20Sopenharmony_ci continue; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (adapter->if_ops.is_port_ready && 11258c2ecf20Sopenharmony_ci !adapter->if_ops.is_port_ready(priv_tmp)) 11268c2ecf20Sopenharmony_ci continue; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci /* iterate over the WMM queues of the BSS */ 11298c2ecf20Sopenharmony_ci hqp = &priv_tmp->wmm.highest_queued_prio; 11308c2ecf20Sopenharmony_ci for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci spin_lock_bh(&priv_tmp->wmm.ra_list_spinlock); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci tid_ptr = &(priv_tmp)->wmm. 11358c2ecf20Sopenharmony_ci tid_tbl_ptr[tos_to_tid[i]]; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci /* iterate over receiver addresses */ 11388c2ecf20Sopenharmony_ci list_for_each_entry(ptr, &tid_ptr->ra_list, 11398c2ecf20Sopenharmony_ci list) { 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (!ptr->tx_paused && 11428c2ecf20Sopenharmony_ci !skb_queue_empty(&ptr->skb_head)) 11438c2ecf20Sopenharmony_ci /* holds both locks */ 11448c2ecf20Sopenharmony_ci goto found; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock); 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci if (atomic_read(&priv_tmp->wmm.tx_pkts_queued) != 0) { 11518c2ecf20Sopenharmony_ci atomic_set(&priv_tmp->wmm.highest_queued_prio, 11528c2ecf20Sopenharmony_ci HIGH_PRIO_TID); 11538c2ecf20Sopenharmony_ci /* Iterate current private once more, since 11548c2ecf20Sopenharmony_ci * there still exist packets in data queue 11558c2ecf20Sopenharmony_ci */ 11568c2ecf20Sopenharmony_ci goto try_again; 11578c2ecf20Sopenharmony_ci } else 11588c2ecf20Sopenharmony_ci atomic_set(&priv_tmp->wmm.highest_queued_prio, 11598c2ecf20Sopenharmony_ci NO_PKT_PRIO_TID); 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return NULL; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cifound: 11668c2ecf20Sopenharmony_ci /* holds ra_list_spinlock */ 11678c2ecf20Sopenharmony_ci if (atomic_read(hqp) > i) 11688c2ecf20Sopenharmony_ci atomic_set(hqp, i); 11698c2ecf20Sopenharmony_ci spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci *priv = priv_tmp; 11728c2ecf20Sopenharmony_ci *tid = tos_to_tid[i]; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci return ptr; 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci/* This functions rotates ra and bss lists so packets are picked round robin. 11788c2ecf20Sopenharmony_ci * 11798c2ecf20Sopenharmony_ci * After a packet is successfully transmitted, rotate the ra list, so the ra 11808c2ecf20Sopenharmony_ci * next to the one transmitted, will come first in the list. This way we pick 11818c2ecf20Sopenharmony_ci * the ra' in a round robin fashion. Same applies to bss nodes of equal 11828c2ecf20Sopenharmony_ci * priority. 11838c2ecf20Sopenharmony_ci * 11848c2ecf20Sopenharmony_ci * Function also increments wmm.packets_out counter. 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_civoid mwifiex_rotate_priolists(struct mwifiex_private *priv, 11878c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ra, 11888c2ecf20Sopenharmony_ci int tid) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 11918c2ecf20Sopenharmony_ci struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; 11928c2ecf20Sopenharmony_ci struct mwifiex_tid_tbl *tid_ptr = &priv->wmm.tid_tbl_ptr[tid]; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock); 11958c2ecf20Sopenharmony_ci /* 11968c2ecf20Sopenharmony_ci * dirty trick: we remove 'head' temporarily and reinsert it after 11978c2ecf20Sopenharmony_ci * curr bss node. imagine list to stay fixed while head is moved 11988c2ecf20Sopenharmony_ci */ 11998c2ecf20Sopenharmony_ci list_move(&tbl[priv->bss_priority].bss_prio_head, 12008c2ecf20Sopenharmony_ci &tbl[priv->bss_priority].bss_prio_cur->list); 12018c2ecf20Sopenharmony_ci spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 12048c2ecf20Sopenharmony_ci if (mwifiex_is_ralist_valid(priv, ra, tid)) { 12058c2ecf20Sopenharmony_ci priv->wmm.packets_out[tid]++; 12068c2ecf20Sopenharmony_ci /* same as above */ 12078c2ecf20Sopenharmony_ci list_move(&tid_ptr->ra_list, &ra->list); 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci/* 12138c2ecf20Sopenharmony_ci * This function checks if 11n aggregation is possible. 12148c2ecf20Sopenharmony_ci */ 12158c2ecf20Sopenharmony_cistatic int 12168c2ecf20Sopenharmony_cimwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv, 12178c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ptr, 12188c2ecf20Sopenharmony_ci int max_buf_size) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci int count = 0, total_size = 0; 12218c2ecf20Sopenharmony_ci struct sk_buff *skb, *tmp; 12228c2ecf20Sopenharmony_ci int max_amsdu_size; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP && priv->ap_11n_enabled && 12258c2ecf20Sopenharmony_ci ptr->is_11n_enabled) 12268c2ecf20Sopenharmony_ci max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size); 12278c2ecf20Sopenharmony_ci else 12288c2ecf20Sopenharmony_ci max_amsdu_size = max_buf_size; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { 12318c2ecf20Sopenharmony_ci total_size += skb->len; 12328c2ecf20Sopenharmony_ci if (total_size >= max_amsdu_size) 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci if (++count >= MIN_NUM_AMSDU) 12358c2ecf20Sopenharmony_ci return true; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci return false; 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci/* 12428c2ecf20Sopenharmony_ci * This function sends a single packet to firmware for transmission. 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_cistatic void 12458c2ecf20Sopenharmony_cimwifiex_send_single_packet(struct mwifiex_private *priv, 12468c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ptr, int ptr_index) 12478c2ecf20Sopenharmony_ci __releases(&priv->wmm.ra_list_spinlock) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci struct sk_buff *skb, *skb_next; 12508c2ecf20Sopenharmony_ci struct mwifiex_tx_param tx_param; 12518c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 12528c2ecf20Sopenharmony_ci struct mwifiex_txinfo *tx_info; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (skb_queue_empty(&ptr->skb_head)) { 12558c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 12568c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, "data: nothing to send\n"); 12578c2ecf20Sopenharmony_ci return; 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci skb = skb_dequeue(&ptr->skb_head); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci tx_info = MWIFIEX_SKB_TXCB(skb); 12638c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 12648c2ecf20Sopenharmony_ci "data: dequeuing the packet %p %p\n", ptr, skb); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci ptr->total_pkt_count--; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci if (!skb_queue_empty(&ptr->skb_head)) 12698c2ecf20Sopenharmony_ci skb_next = skb_peek(&ptr->skb_head); 12708c2ecf20Sopenharmony_ci else 12718c2ecf20Sopenharmony_ci skb_next = NULL; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci tx_param.next_pkt_len = ((skb_next) ? skb_next->len + 12768c2ecf20Sopenharmony_ci sizeof(struct txpd) : 0); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { 12798c2ecf20Sopenharmony_ci /* Queue the packet back at the head */ 12808c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { 12838c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 12848c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb, 0, -1); 12858c2ecf20Sopenharmony_ci return; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci skb_queue_tail(&ptr->skb_head, skb); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci ptr->total_pkt_count++; 12918c2ecf20Sopenharmony_ci ptr->ba_pkt_count++; 12928c2ecf20Sopenharmony_ci tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; 12938c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 12948c2ecf20Sopenharmony_ci } else { 12958c2ecf20Sopenharmony_ci mwifiex_rotate_priolists(priv, ptr, ptr_index); 12968c2ecf20Sopenharmony_ci atomic_dec(&priv->wmm.tx_pkts_queued); 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci} 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci/* 13018c2ecf20Sopenharmony_ci * This function checks if the first packet in the given RA list 13028c2ecf20Sopenharmony_ci * is already processed or not. 13038c2ecf20Sopenharmony_ci */ 13048c2ecf20Sopenharmony_cistatic int 13058c2ecf20Sopenharmony_cimwifiex_is_ptr_processed(struct mwifiex_private *priv, 13068c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ptr) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci struct sk_buff *skb; 13098c2ecf20Sopenharmony_ci struct mwifiex_txinfo *tx_info; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (skb_queue_empty(&ptr->skb_head)) 13128c2ecf20Sopenharmony_ci return false; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci skb = skb_peek(&ptr->skb_head); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci tx_info = MWIFIEX_SKB_TXCB(skb); 13178c2ecf20Sopenharmony_ci if (tx_info->flags & MWIFIEX_BUF_FLAG_REQUEUED_PKT) 13188c2ecf20Sopenharmony_ci return true; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci return false; 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci/* 13248c2ecf20Sopenharmony_ci * This function sends a single processed packet to firmware for 13258c2ecf20Sopenharmony_ci * transmission. 13268c2ecf20Sopenharmony_ci */ 13278c2ecf20Sopenharmony_cistatic void 13288c2ecf20Sopenharmony_cimwifiex_send_processed_packet(struct mwifiex_private *priv, 13298c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ptr, int ptr_index) 13308c2ecf20Sopenharmony_ci __releases(&priv->wmm.ra_list_spinlock) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci struct mwifiex_tx_param tx_param; 13338c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = priv->adapter; 13348c2ecf20Sopenharmony_ci int ret = -1; 13358c2ecf20Sopenharmony_ci struct sk_buff *skb, *skb_next; 13368c2ecf20Sopenharmony_ci struct mwifiex_txinfo *tx_info; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (skb_queue_empty(&ptr->skb_head)) { 13398c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 13408c2ecf20Sopenharmony_ci return; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci skb = skb_dequeue(&ptr->skb_head); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (adapter->data_sent || adapter->tx_lock_flag) { 13468c2ecf20Sopenharmony_ci ptr->total_pkt_count--; 13478c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 13488c2ecf20Sopenharmony_ci skb_queue_tail(&adapter->tx_data_q, skb); 13498c2ecf20Sopenharmony_ci atomic_dec(&priv->wmm.tx_pkts_queued); 13508c2ecf20Sopenharmony_ci atomic_inc(&adapter->tx_queued); 13518c2ecf20Sopenharmony_ci return; 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci if (!skb_queue_empty(&ptr->skb_head)) 13558c2ecf20Sopenharmony_ci skb_next = skb_peek(&ptr->skb_head); 13568c2ecf20Sopenharmony_ci else 13578c2ecf20Sopenharmony_ci skb_next = NULL; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci tx_info = MWIFIEX_SKB_TXCB(skb); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci tx_param.next_pkt_len = 13648c2ecf20Sopenharmony_ci ((skb_next) ? skb_next->len + 13658c2ecf20Sopenharmony_ci sizeof(struct txpd) : 0); 13668c2ecf20Sopenharmony_ci if (adapter->iface_type == MWIFIEX_USB) { 13678c2ecf20Sopenharmony_ci ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, 13688c2ecf20Sopenharmony_ci skb, &tx_param); 13698c2ecf20Sopenharmony_ci } else { 13708c2ecf20Sopenharmony_ci ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, 13718c2ecf20Sopenharmony_ci skb, &tx_param); 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci switch (ret) { 13758c2ecf20Sopenharmony_ci case -EBUSY: 13768c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); 13778c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { 13808c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 13818c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb, 0, -1); 13828c2ecf20Sopenharmony_ci return; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci skb_queue_tail(&ptr->skb_head, skb); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; 13888c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 13898c2ecf20Sopenharmony_ci break; 13908c2ecf20Sopenharmony_ci case -1: 13918c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret); 13928c2ecf20Sopenharmony_ci adapter->dbg.num_tx_host_to_card_failure++; 13938c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb, 0, ret); 13948c2ecf20Sopenharmony_ci break; 13958c2ecf20Sopenharmony_ci case -EINPROGRESS: 13968c2ecf20Sopenharmony_ci break; 13978c2ecf20Sopenharmony_ci case 0: 13988c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb, 0, ret); 13998c2ecf20Sopenharmony_ci default: 14008c2ecf20Sopenharmony_ci break; 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci if (ret != -EBUSY) { 14038c2ecf20Sopenharmony_ci mwifiex_rotate_priolists(priv, ptr, ptr_index); 14048c2ecf20Sopenharmony_ci atomic_dec(&priv->wmm.tx_pkts_queued); 14058c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 14068c2ecf20Sopenharmony_ci ptr->total_pkt_count--; 14078c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci/* 14128c2ecf20Sopenharmony_ci * This function dequeues a packet from the highest priority list 14138c2ecf20Sopenharmony_ci * and transmits it. 14148c2ecf20Sopenharmony_ci */ 14158c2ecf20Sopenharmony_cistatic int 14168c2ecf20Sopenharmony_cimwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) 14178c2ecf20Sopenharmony_ci{ 14188c2ecf20Sopenharmony_ci struct mwifiex_ra_list_tbl *ptr; 14198c2ecf20Sopenharmony_ci struct mwifiex_private *priv = NULL; 14208c2ecf20Sopenharmony_ci int ptr_index = 0; 14218c2ecf20Sopenharmony_ci u8 ra[ETH_ALEN]; 14228c2ecf20Sopenharmony_ci int tid_del = 0, tid = 0; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci ptr = mwifiex_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); 14258c2ecf20Sopenharmony_ci if (!ptr) 14268c2ecf20Sopenharmony_ci return -1; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci tid = mwifiex_get_tid(ptr); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, "data: tid=%d\n", tid); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci spin_lock_bh(&priv->wmm.ra_list_spinlock); 14338c2ecf20Sopenharmony_ci if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { 14348c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->wmm.ra_list_spinlock); 14358c2ecf20Sopenharmony_ci return -1; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci if (mwifiex_is_ptr_processed(priv, ptr)) { 14398c2ecf20Sopenharmony_ci mwifiex_send_processed_packet(priv, ptr, ptr_index); 14408c2ecf20Sopenharmony_ci /* ra_list_spinlock has been freed in 14418c2ecf20Sopenharmony_ci mwifiex_send_processed_packet() */ 14428c2ecf20Sopenharmony_ci return 0; 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci if (!ptr->is_11n_enabled || 14468c2ecf20Sopenharmony_ci ptr->ba_status || 14478c2ecf20Sopenharmony_ci priv->wps.session_enable) { 14488c2ecf20Sopenharmony_ci if (ptr->is_11n_enabled && 14498c2ecf20Sopenharmony_ci ptr->ba_status && 14508c2ecf20Sopenharmony_ci ptr->amsdu_in_ampdu && 14518c2ecf20Sopenharmony_ci mwifiex_is_amsdu_allowed(priv, tid) && 14528c2ecf20Sopenharmony_ci mwifiex_is_11n_aggragation_possible(priv, ptr, 14538c2ecf20Sopenharmony_ci adapter->tx_buf_size)) 14548c2ecf20Sopenharmony_ci mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index); 14558c2ecf20Sopenharmony_ci /* ra_list_spinlock has been freed in 14568c2ecf20Sopenharmony_ci * mwifiex_11n_aggregate_pkt() 14578c2ecf20Sopenharmony_ci */ 14588c2ecf20Sopenharmony_ci else 14598c2ecf20Sopenharmony_ci mwifiex_send_single_packet(priv, ptr, ptr_index); 14608c2ecf20Sopenharmony_ci /* ra_list_spinlock has been freed in 14618c2ecf20Sopenharmony_ci * mwifiex_send_single_packet() 14628c2ecf20Sopenharmony_ci */ 14638c2ecf20Sopenharmony_ci } else { 14648c2ecf20Sopenharmony_ci if (mwifiex_is_ampdu_allowed(priv, ptr, tid) && 14658c2ecf20Sopenharmony_ci ptr->ba_pkt_count > ptr->ba_packet_thr) { 14668c2ecf20Sopenharmony_ci if (mwifiex_space_avail_for_new_ba_stream(adapter)) { 14678c2ecf20Sopenharmony_ci mwifiex_create_ba_tbl(priv, ptr->ra, tid, 14688c2ecf20Sopenharmony_ci BA_SETUP_INPROGRESS); 14698c2ecf20Sopenharmony_ci mwifiex_send_addba(priv, tid, ptr->ra); 14708c2ecf20Sopenharmony_ci } else if (mwifiex_find_stream_to_delete 14718c2ecf20Sopenharmony_ci (priv, tid, &tid_del, ra)) { 14728c2ecf20Sopenharmony_ci mwifiex_create_ba_tbl(priv, ptr->ra, tid, 14738c2ecf20Sopenharmony_ci BA_SETUP_INPROGRESS); 14748c2ecf20Sopenharmony_ci mwifiex_send_delba(priv, tid_del, ra, 1); 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci if (mwifiex_is_amsdu_allowed(priv, tid) && 14788c2ecf20Sopenharmony_ci mwifiex_is_11n_aggragation_possible(priv, ptr, 14798c2ecf20Sopenharmony_ci adapter->tx_buf_size)) 14808c2ecf20Sopenharmony_ci mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index); 14818c2ecf20Sopenharmony_ci /* ra_list_spinlock has been freed in 14828c2ecf20Sopenharmony_ci mwifiex_11n_aggregate_pkt() */ 14838c2ecf20Sopenharmony_ci else 14848c2ecf20Sopenharmony_ci mwifiex_send_single_packet(priv, ptr, ptr_index); 14858c2ecf20Sopenharmony_ci /* ra_list_spinlock has been freed in 14868c2ecf20Sopenharmony_ci mwifiex_send_single_packet() */ 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci return 0; 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_civoid mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci struct mwifiex_tx_param tx_param; 14948c2ecf20Sopenharmony_ci struct sk_buff *skb; 14958c2ecf20Sopenharmony_ci struct mwifiex_txinfo *tx_info; 14968c2ecf20Sopenharmony_ci struct mwifiex_private *priv; 14978c2ecf20Sopenharmony_ci int i; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci if (adapter->data_sent || adapter->tx_lock_flag) 15008c2ecf20Sopenharmony_ci return; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci for (i = 0; i < adapter->priv_num; ++i) { 15038c2ecf20Sopenharmony_ci priv = adapter->priv[i]; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci if (!priv) 15068c2ecf20Sopenharmony_ci continue; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (adapter->if_ops.is_port_ready && 15098c2ecf20Sopenharmony_ci !adapter->if_ops.is_port_ready(priv)) 15108c2ecf20Sopenharmony_ci continue; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (skb_queue_empty(&priv->bypass_txq)) 15138c2ecf20Sopenharmony_ci continue; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci skb = skb_dequeue(&priv->bypass_txq); 15168c2ecf20Sopenharmony_ci tx_info = MWIFIEX_SKB_TXCB(skb); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci /* no aggregation for bypass packets */ 15198c2ecf20Sopenharmony_ci tx_param.next_pkt_len = 0; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { 15228c2ecf20Sopenharmony_ci skb_queue_head(&priv->bypass_txq, skb); 15238c2ecf20Sopenharmony_ci tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; 15248c2ecf20Sopenharmony_ci } else { 15258c2ecf20Sopenharmony_ci atomic_dec(&adapter->bypass_tx_pending); 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci} 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci/* 15318c2ecf20Sopenharmony_ci * This function transmits the highest priority packet awaiting in the 15328c2ecf20Sopenharmony_ci * WMM Queues. 15338c2ecf20Sopenharmony_ci */ 15348c2ecf20Sopenharmony_civoid 15358c2ecf20Sopenharmony_cimwifiex_wmm_process_tx(struct mwifiex_adapter *adapter) 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci do { 15388c2ecf20Sopenharmony_ci if (mwifiex_dequeue_tx_packet(adapter)) 15398c2ecf20Sopenharmony_ci break; 15408c2ecf20Sopenharmony_ci if (adapter->iface_type != MWIFIEX_SDIO) { 15418c2ecf20Sopenharmony_ci if (adapter->data_sent || 15428c2ecf20Sopenharmony_ci adapter->tx_lock_flag) 15438c2ecf20Sopenharmony_ci break; 15448c2ecf20Sopenharmony_ci } else { 15458c2ecf20Sopenharmony_ci if (atomic_read(&adapter->tx_queued) >= 15468c2ecf20Sopenharmony_ci MWIFIEX_MAX_PKTS_TXQ) 15478c2ecf20Sopenharmony_ci break; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci } while (!mwifiex_wmm_lists_empty(adapter)); 15508c2ecf20Sopenharmony_ci} 1551