18c2ecf20Sopenharmony_ci/****************************************************************************** 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 98c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 108c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 118c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 148c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 158c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 188c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 198c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 208c2ecf20Sopenharmony_ci * General Public License for more details. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution 238c2ecf20Sopenharmony_ci * in the file called COPYING. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Contact Information: 268c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 278c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * BSD LICENSE 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 328c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 338c2ecf20Sopenharmony_ci * Copyright(c) 2016 - 2017 Intel Deutschland GmbH 348c2ecf20Sopenharmony_ci * Copyright(c) 2018 - 2020 Intel Corporation 358c2ecf20Sopenharmony_ci * All rights reserved. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 388c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 398c2ecf20Sopenharmony_ci * are met: 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 428c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 438c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 448c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 458c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 468c2ecf20Sopenharmony_ci * distribution. 478c2ecf20Sopenharmony_ci * * Neither the name Intel Corporation nor the names of its 488c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 498c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 528c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 538c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 548c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 558c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 568c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 578c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 588c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 598c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 608c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 618c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci *****************************************************************************/ 648c2ecf20Sopenharmony_ci#include <linux/ieee80211.h> 658c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 668c2ecf20Sopenharmony_ci#include <linux/tcp.h> 678c2ecf20Sopenharmony_ci#include <net/ip.h> 688c2ecf20Sopenharmony_ci#include <net/ipv6.h> 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#include "iwl-trans.h" 718c2ecf20Sopenharmony_ci#include "iwl-eeprom-parse.h" 728c2ecf20Sopenharmony_ci#include "mvm.h" 738c2ecf20Sopenharmony_ci#include "sta.h" 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void 768c2ecf20Sopenharmony_ciiwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, 778c2ecf20Sopenharmony_ci u16 tid, u16 ssn) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_tlv *trig; 808c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_ba *ba_trig; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, NULL, FW_DBG_TRIGGER_BA); 838c2ecf20Sopenharmony_ci if (!trig) 848c2ecf20Sopenharmony_ci return; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ba_trig = (void *)trig->data; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!(le16_to_cpu(ba_trig->tx_bar) & BIT(tid))) 898c2ecf20Sopenharmony_ci return; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, 928c2ecf20Sopenharmony_ci "BAR sent to %pM, tid %d, ssn %d", 938c2ecf20Sopenharmony_ci addr, tid, ssn); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define OPT_HDR(type, skb, off) \ 978c2ecf20Sopenharmony_ci (type *)(skb_network_header(skb) + (off)) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic u16 iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb, 1008c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr, 1018c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 1028c2ecf20Sopenharmony_ci u16 offload_assist) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INET) 1058c2ecf20Sopenharmony_ci u16 mh_len = ieee80211_hdrlen(hdr->frame_control); 1068c2ecf20Sopenharmony_ci u8 protocol = 0; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * Do not compute checksum if already computed or if transport will 1108c2ecf20Sopenharmony_ci * compute it 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL || IWL_MVM_SW_TX_CSUM_OFFLOAD) 1138c2ecf20Sopenharmony_ci goto out; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* We do not expect to be requested to csum stuff we do not support */ 1168c2ecf20Sopenharmony_ci if (WARN_ONCE(!(mvm->hw->netdev_features & IWL_TX_CSUM_NETIF_FLAGS) || 1178c2ecf20Sopenharmony_ci (skb->protocol != htons(ETH_P_IP) && 1188c2ecf20Sopenharmony_ci skb->protocol != htons(ETH_P_IPV6)), 1198c2ecf20Sopenharmony_ci "No support for requested checksum\n")) { 1208c2ecf20Sopenharmony_ci skb_checksum_help(skb); 1218c2ecf20Sopenharmony_ci goto out; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 1258c2ecf20Sopenharmony_ci protocol = ip_hdr(skb)->protocol; 1268c2ecf20Sopenharmony_ci } else { 1278c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 1288c2ecf20Sopenharmony_ci struct ipv6hdr *ipv6h = 1298c2ecf20Sopenharmony_ci (struct ipv6hdr *)skb_network_header(skb); 1308c2ecf20Sopenharmony_ci unsigned int off = sizeof(*ipv6h); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci protocol = ipv6h->nexthdr; 1338c2ecf20Sopenharmony_ci while (protocol != NEXTHDR_NONE && ipv6_ext_hdr(protocol)) { 1348c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *hp; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* only supported extension headers */ 1378c2ecf20Sopenharmony_ci if (protocol != NEXTHDR_ROUTING && 1388c2ecf20Sopenharmony_ci protocol != NEXTHDR_HOP && 1398c2ecf20Sopenharmony_ci protocol != NEXTHDR_DEST) { 1408c2ecf20Sopenharmony_ci skb_checksum_help(skb); 1418c2ecf20Sopenharmony_ci goto out; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci hp = OPT_HDR(struct ipv6_opt_hdr, skb, off); 1458c2ecf20Sopenharmony_ci protocol = hp->nexthdr; 1468c2ecf20Sopenharmony_ci off += ipv6_optlen(hp); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci /* if we get here - protocol now should be TCP/UDP */ 1498c2ecf20Sopenharmony_ci#endif 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) { 1538c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 1548c2ecf20Sopenharmony_ci skb_checksum_help(skb); 1558c2ecf20Sopenharmony_ci goto out; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* enable L4 csum */ 1598c2ecf20Sopenharmony_ci offload_assist |= BIT(TX_CMD_OFFLD_L4_EN); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * Set offset to IP header (snap). 1638c2ecf20Sopenharmony_ci * We don't support tunneling so no need to take care of inner header. 1648c2ecf20Sopenharmony_ci * Size is in words. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci offload_assist |= (4 << TX_CMD_OFFLD_IP_HDR); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Do IPv4 csum for AMSDU only (no IP csum for Ipv6) */ 1698c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP) && 1708c2ecf20Sopenharmony_ci (offload_assist & BIT(TX_CMD_OFFLD_AMSDU))) { 1718c2ecf20Sopenharmony_ci ip_hdr(skb)->check = 0; 1728c2ecf20Sopenharmony_ci offload_assist |= BIT(TX_CMD_OFFLD_L3_EN); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* reset UDP/TCP header csum */ 1768c2ecf20Sopenharmony_ci if (protocol == IPPROTO_TCP) 1778c2ecf20Sopenharmony_ci tcp_hdr(skb)->check = 0; 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci udp_hdr(skb)->check = 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * mac header len should include IV, size is in words unless 1838c2ecf20Sopenharmony_ci * the IV is added by the firmware like in WEP. 1848c2ecf20Sopenharmony_ci * In new Tx API, the IV is always added by the firmware. 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm) && info->control.hw_key && 1878c2ecf20Sopenharmony_ci info->control.hw_key->cipher != WLAN_CIPHER_SUITE_WEP40 && 1888c2ecf20Sopenharmony_ci info->control.hw_key->cipher != WLAN_CIPHER_SUITE_WEP104) 1898c2ecf20Sopenharmony_ci mh_len += info->control.hw_key->iv_len; 1908c2ecf20Sopenharmony_ci mh_len /= 2; 1918c2ecf20Sopenharmony_ci offload_assist |= mh_len << TX_CMD_OFFLD_MH_SIZE; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciout: 1948c2ecf20Sopenharmony_ci#endif 1958c2ecf20Sopenharmony_ci return offload_assist; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * Sets most of the Tx cmd's fields 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_civoid iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, 2028c2ecf20Sopenharmony_ci struct iwl_tx_cmd *tx_cmd, 2038c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, u8 sta_id) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (void *)skb->data; 2068c2ecf20Sopenharmony_ci __le16 fc = hdr->frame_control; 2078c2ecf20Sopenharmony_ci u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags); 2088c2ecf20Sopenharmony_ci u32 len = skb->len + FCS_LEN; 2098c2ecf20Sopenharmony_ci u16 offload_assist = 0; 2108c2ecf20Sopenharmony_ci u8 ac; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) || 2138c2ecf20Sopenharmony_ci (ieee80211_is_probe_resp(fc) && 2148c2ecf20Sopenharmony_ci !is_multicast_ether_addr(hdr->addr1))) 2158c2ecf20Sopenharmony_ci tx_flags |= TX_CMD_FLG_ACK; 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci tx_flags &= ~TX_CMD_FLG_ACK; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (ieee80211_is_probe_resp(fc)) 2208c2ecf20Sopenharmony_ci tx_flags |= TX_CMD_FLG_TSF; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (ieee80211_has_morefrags(fc)) 2238c2ecf20Sopenharmony_ci tx_flags |= TX_CMD_FLG_MORE_FRAG; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (ieee80211_is_data_qos(fc)) { 2268c2ecf20Sopenharmony_ci u8 *qc = ieee80211_get_qos_ctl(hdr); 2278c2ecf20Sopenharmony_ci tx_cmd->tid_tspec = qc[0] & 0xf; 2288c2ecf20Sopenharmony_ci tx_flags &= ~TX_CMD_FLG_SEQ_CTL; 2298c2ecf20Sopenharmony_ci if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) 2308c2ecf20Sopenharmony_ci offload_assist |= BIT(TX_CMD_OFFLD_AMSDU); 2318c2ecf20Sopenharmony_ci } else if (ieee80211_is_back_req(fc)) { 2328c2ecf20Sopenharmony_ci struct ieee80211_bar *bar = (void *)skb->data; 2338c2ecf20Sopenharmony_ci u16 control = le16_to_cpu(bar->control); 2348c2ecf20Sopenharmony_ci u16 ssn = le16_to_cpu(bar->start_seq_num); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; 2378c2ecf20Sopenharmony_ci tx_cmd->tid_tspec = (control & 2388c2ecf20Sopenharmony_ci IEEE80211_BAR_CTRL_TID_INFO_MASK) >> 2398c2ecf20Sopenharmony_ci IEEE80211_BAR_CTRL_TID_INFO_SHIFT; 2408c2ecf20Sopenharmony_ci WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT); 2418c2ecf20Sopenharmony_ci iwl_mvm_bar_check_trigger(mvm, bar->ra, tx_cmd->tid_tspec, 2428c2ecf20Sopenharmony_ci ssn); 2438c2ecf20Sopenharmony_ci } else { 2448c2ecf20Sopenharmony_ci if (ieee80211_is_data(fc)) 2458c2ecf20Sopenharmony_ci tx_cmd->tid_tspec = IWL_TID_NON_QOS; 2468c2ecf20Sopenharmony_ci else 2478c2ecf20Sopenharmony_ci tx_cmd->tid_tspec = IWL_MAX_TID_COUNT; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) 2508c2ecf20Sopenharmony_ci tx_flags |= TX_CMD_FLG_SEQ_CTL; 2518c2ecf20Sopenharmony_ci else 2528c2ecf20Sopenharmony_ci tx_flags &= ~TX_CMD_FLG_SEQ_CTL; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Default to 0 (BE) when tid_spec is set to IWL_MAX_TID_COUNT */ 2568c2ecf20Sopenharmony_ci if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT) 2578c2ecf20Sopenharmony_ci ac = tid_to_mac80211_ac[tx_cmd->tid_tspec]; 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci ac = tid_to_mac80211_ac[0]; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) << 2628c2ecf20Sopenharmony_ci TX_CMD_FLG_BT_PRIO_POS; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (ieee80211_is_mgmt(fc)) { 2658c2ecf20Sopenharmony_ci if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) 2668c2ecf20Sopenharmony_ci tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_ASSOC); 2678c2ecf20Sopenharmony_ci else if (ieee80211_is_action(fc)) 2688c2ecf20Sopenharmony_ci tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE); 2698c2ecf20Sopenharmony_ci else 2708c2ecf20Sopenharmony_ci tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* The spec allows Action frames in A-MPDU, we don't support 2738c2ecf20Sopenharmony_ci * it 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_ci WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU); 2768c2ecf20Sopenharmony_ci } else if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { 2778c2ecf20Sopenharmony_ci tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT); 2788c2ecf20Sopenharmony_ci } else { 2798c2ecf20Sopenharmony_ci tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (ieee80211_is_data(fc) && len > mvm->rts_threshold && 2838c2ecf20Sopenharmony_ci !is_multicast_ether_addr(hdr->addr1)) 2848c2ecf20Sopenharmony_ci tx_flags |= TX_CMD_FLG_PROT_REQUIRE; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (fw_has_capa(&mvm->fw->ucode_capa, 2878c2ecf20Sopenharmony_ci IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT) && 2888c2ecf20Sopenharmony_ci ieee80211_action_contains_tpc(skb)) 2898c2ecf20Sopenharmony_ci tx_flags |= TX_CMD_FLG_WRITE_TX_POWER; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci tx_cmd->tx_flags = cpu_to_le32(tx_flags); 2928c2ecf20Sopenharmony_ci /* Total # bytes to be transmitted - PCIe code will adjust for A-MSDU */ 2938c2ecf20Sopenharmony_ci tx_cmd->len = cpu_to_le16((u16)skb->len); 2948c2ecf20Sopenharmony_ci tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); 2958c2ecf20Sopenharmony_ci tx_cmd->sta_id = sta_id; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* padding is inserted later in transport */ 2988c2ecf20Sopenharmony_ci if (ieee80211_hdrlen(fc) % 4 && 2998c2ecf20Sopenharmony_ci !(offload_assist & BIT(TX_CMD_OFFLD_AMSDU))) 3008c2ecf20Sopenharmony_ci offload_assist |= BIT(TX_CMD_OFFLD_PAD); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci tx_cmd->offload_assist |= 3038c2ecf20Sopenharmony_ci cpu_to_le16(iwl_mvm_tx_csum(mvm, skb, hdr, info, 3048c2ecf20Sopenharmony_ci offload_assist)); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic u32 iwl_mvm_get_tx_ant(struct iwl_mvm *mvm, 3088c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 3098c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, __le16 fc) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci if (info->band == NL80211_BAND_2GHZ && 3128c2ecf20Sopenharmony_ci !iwl_mvm_bt_coex_is_shared_ant_avail(mvm)) 3138c2ecf20Sopenharmony_ci return mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (sta && ieee80211_is_data(fc)) { 3168c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return BIT(mvmsta->tx_ant) << RATE_MCS_ANT_POS; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm, 3258c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 3268c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci int rate_idx; 3298c2ecf20Sopenharmony_ci u8 rate_plcp; 3308c2ecf20Sopenharmony_ci u32 rate_flags = 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* HT rate doesn't make sense for a non data frame */ 3338c2ecf20Sopenharmony_ci WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS, 3348c2ecf20Sopenharmony_ci "Got an HT rate (flags:0x%x/mcs:%d) for a non data frame\n", 3358c2ecf20Sopenharmony_ci info->control.rates[0].flags, 3368c2ecf20Sopenharmony_ci info->control.rates[0].idx); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci rate_idx = info->control.rates[0].idx; 3398c2ecf20Sopenharmony_ci /* if the rate isn't a well known legacy rate, take the lowest one */ 3408c2ecf20Sopenharmony_ci if (rate_idx < 0 || rate_idx >= IWL_RATE_COUNT_LEGACY) 3418c2ecf20Sopenharmony_ci rate_idx = rate_lowest_index( 3428c2ecf20Sopenharmony_ci &mvm->nvm_data->bands[info->band], sta); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* 3458c2ecf20Sopenharmony_ci * For non 2 GHZ band, remap mac80211 rate 3468c2ecf20Sopenharmony_ci * indices into driver indices 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ci if (info->band != NL80211_BAND_2GHZ) 3498c2ecf20Sopenharmony_ci rate_idx += IWL_FIRST_OFDM_RATE; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* For 2.4 GHZ band, check that there is no need to remap */ 3528c2ecf20Sopenharmony_ci BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Get PLCP rate for tx_cmd->rate_n_flags */ 3558c2ecf20Sopenharmony_ci rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* Set CCK flag as needed */ 3588c2ecf20Sopenharmony_ci if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) 3598c2ecf20Sopenharmony_ci rate_flags |= RATE_MCS_CCK_MSK; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return (u32)rate_plcp | rate_flags; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic u32 iwl_mvm_get_tx_rate_n_flags(struct iwl_mvm *mvm, 3658c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 3668c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, __le16 fc) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci return iwl_mvm_get_tx_rate(mvm, info, sta) | 3698c2ecf20Sopenharmony_ci iwl_mvm_get_tx_ant(mvm, info, sta, fc); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/* 3738c2ecf20Sopenharmony_ci * Sets the fields in the Tx cmd that are rate related 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_civoid iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, 3768c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 3778c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, __le16 fc) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci /* Set retry limit on RTS packets */ 3808c2ecf20Sopenharmony_ci tx_cmd->rts_retry_limit = IWL_RTS_DFAULT_RETRY_LIMIT; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* Set retry limit on DATA packets and Probe Responses*/ 3838c2ecf20Sopenharmony_ci if (ieee80211_is_probe_resp(fc)) { 3848c2ecf20Sopenharmony_ci tx_cmd->data_retry_limit = IWL_MGMT_DFAULT_RETRY_LIMIT; 3858c2ecf20Sopenharmony_ci tx_cmd->rts_retry_limit = 3868c2ecf20Sopenharmony_ci min(tx_cmd->data_retry_limit, tx_cmd->rts_retry_limit); 3878c2ecf20Sopenharmony_ci } else if (ieee80211_is_back_req(fc)) { 3888c2ecf20Sopenharmony_ci tx_cmd->data_retry_limit = IWL_BAR_DFAULT_RETRY_LIMIT; 3898c2ecf20Sopenharmony_ci } else { 3908c2ecf20Sopenharmony_ci tx_cmd->data_retry_limit = IWL_DEFAULT_TX_RETRY; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * for data packets, rate info comes from the table inside the fw. This 3958c2ecf20Sopenharmony_ci * table is controlled by LINK_QUALITY commands 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (ieee80211_is_data(fc) && sta) { 3998c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (mvmsta->sta_state >= IEEE80211_STA_AUTHORIZED) { 4028c2ecf20Sopenharmony_ci tx_cmd->initial_rate_index = 0; 4038c2ecf20Sopenharmony_ci tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE); 4048c2ecf20Sopenharmony_ci return; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci } else if (ieee80211_is_back_req(fc)) { 4078c2ecf20Sopenharmony_ci tx_cmd->tx_flags |= 4088c2ecf20Sopenharmony_ci cpu_to_le32(TX_CMD_FLG_ACK | TX_CMD_FLG_BAR); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Set the rate in the TX cmd */ 4128c2ecf20Sopenharmony_ci tx_cmd->rate_n_flags = 4138c2ecf20Sopenharmony_ci cpu_to_le32(iwl_mvm_get_tx_rate_n_flags(mvm, info, sta, fc)); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic inline void iwl_mvm_set_tx_cmd_pn(struct ieee80211_tx_info *info, 4178c2ecf20Sopenharmony_ci u8 *crypto_hdr) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf = info->control.hw_key; 4208c2ecf20Sopenharmony_ci u64 pn; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci pn = atomic64_inc_return(&keyconf->tx_pn); 4238c2ecf20Sopenharmony_ci crypto_hdr[0] = pn; 4248c2ecf20Sopenharmony_ci crypto_hdr[2] = 0; 4258c2ecf20Sopenharmony_ci crypto_hdr[3] = 0x20 | (keyconf->keyidx << 6); 4268c2ecf20Sopenharmony_ci crypto_hdr[1] = pn >> 8; 4278c2ecf20Sopenharmony_ci crypto_hdr[4] = pn >> 16; 4288c2ecf20Sopenharmony_ci crypto_hdr[5] = pn >> 24; 4298c2ecf20Sopenharmony_ci crypto_hdr[6] = pn >> 32; 4308c2ecf20Sopenharmony_ci crypto_hdr[7] = pn >> 40; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/* 4348c2ecf20Sopenharmony_ci * Sets the fields in the Tx cmd that are crypto related 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_cistatic void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, 4378c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 4388c2ecf20Sopenharmony_ci struct iwl_tx_cmd *tx_cmd, 4398c2ecf20Sopenharmony_ci struct sk_buff *skb_frag, 4408c2ecf20Sopenharmony_ci int hdrlen) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct ieee80211_key_conf *keyconf = info->control.hw_key; 4438c2ecf20Sopenharmony_ci u8 *crypto_hdr = skb_frag->data + hdrlen; 4448c2ecf20Sopenharmony_ci enum iwl_tx_cmd_sec_ctrl type = TX_CMD_SEC_CCM; 4458c2ecf20Sopenharmony_ci u64 pn; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci switch (keyconf->cipher) { 4488c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 4498c2ecf20Sopenharmony_ci iwl_mvm_set_tx_cmd_ccmp(info, tx_cmd); 4508c2ecf20Sopenharmony_ci iwl_mvm_set_tx_cmd_pn(info, crypto_hdr); 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 4548c2ecf20Sopenharmony_ci tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; 4558c2ecf20Sopenharmony_ci pn = atomic64_inc_return(&keyconf->tx_pn); 4568c2ecf20Sopenharmony_ci ieee80211_tkip_add_iv(crypto_hdr, keyconf, pn); 4578c2ecf20Sopenharmony_ci ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 4618c2ecf20Sopenharmony_ci tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; 4628c2ecf20Sopenharmony_ci /* fall through */ 4638c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 4648c2ecf20Sopenharmony_ci tx_cmd->sec_ctl |= TX_CMD_SEC_WEP | 4658c2ecf20Sopenharmony_ci ((keyconf->keyidx << TX_CMD_SEC_WEP_KEY_IDX_POS) & 4668c2ecf20Sopenharmony_ci TX_CMD_SEC_WEP_KEY_IDX_MSK); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_GCMP: 4718c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_GCMP_256: 4728c2ecf20Sopenharmony_ci type = TX_CMD_SEC_GCMP; 4738c2ecf20Sopenharmony_ci /* Fall through */ 4748c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP_256: 4758c2ecf20Sopenharmony_ci /* TODO: Taking the key from the table might introduce a race 4768c2ecf20Sopenharmony_ci * when PTK rekeying is done, having an old packets with a PN 4778c2ecf20Sopenharmony_ci * based on the old key but the message encrypted with a new 4788c2ecf20Sopenharmony_ci * one. 4798c2ecf20Sopenharmony_ci * Need to handle this. 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci tx_cmd->sec_ctl |= type | TX_CMD_SEC_KEY_FROM_TABLE; 4828c2ecf20Sopenharmony_ci tx_cmd->key[0] = keyconf->hw_key_idx; 4838c2ecf20Sopenharmony_ci iwl_mvm_set_tx_cmd_pn(info, crypto_hdr); 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci default: 4868c2ecf20Sopenharmony_ci tx_cmd->sec_ctl |= TX_CMD_SEC_EXT; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci/* 4918c2ecf20Sopenharmony_ci * Allocates and sets the Tx cmd the driver data pointers in the skb 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_cistatic struct iwl_device_tx_cmd * 4948c2ecf20Sopenharmony_ciiwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, 4958c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, int hdrlen, 4968c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u8 sta_id) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 4998c2ecf20Sopenharmony_ci struct iwl_device_tx_cmd *dev_cmd; 5008c2ecf20Sopenharmony_ci struct iwl_tx_cmd *tx_cmd; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (unlikely(!dev_cmd)) 5058c2ecf20Sopenharmony_ci return NULL; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci dev_cmd->hdr.cmd = TX_CMD; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 5108c2ecf20Sopenharmony_ci u16 offload_assist = 0; 5118c2ecf20Sopenharmony_ci u32 rate_n_flags = 0; 5128c2ecf20Sopenharmony_ci u16 flags = 0; 5138c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = sta ? 5148c2ecf20Sopenharmony_ci iwl_mvm_sta_from_mac80211(sta) : NULL; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (ieee80211_is_data_qos(hdr->frame_control)) { 5178c2ecf20Sopenharmony_ci u8 *qc = ieee80211_get_qos_ctl(hdr); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) 5208c2ecf20Sopenharmony_ci offload_assist |= BIT(TX_CMD_OFFLD_AMSDU); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci offload_assist = iwl_mvm_tx_csum(mvm, skb, hdr, info, 5248c2ecf20Sopenharmony_ci offload_assist); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* padding is inserted later in transport */ 5278c2ecf20Sopenharmony_ci if (ieee80211_hdrlen(hdr->frame_control) % 4 && 5288c2ecf20Sopenharmony_ci !(offload_assist & BIT(TX_CMD_OFFLD_AMSDU))) 5298c2ecf20Sopenharmony_ci offload_assist |= BIT(TX_CMD_OFFLD_PAD); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (!info->control.hw_key) 5328c2ecf20Sopenharmony_ci flags |= IWL_TX_FLAGS_ENCRYPT_DIS; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* 5358c2ecf20Sopenharmony_ci * For data and mgmt packets rate info comes from the fw. Only 5368c2ecf20Sopenharmony_ci * set rate/antenna for injected frames with fixed rate, or 5378c2ecf20Sopenharmony_ci * when no sta is given. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci if (unlikely(!sta || 5408c2ecf20Sopenharmony_ci info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) { 5418c2ecf20Sopenharmony_ci flags |= IWL_TX_FLAGS_CMD_RATE; 5428c2ecf20Sopenharmony_ci rate_n_flags = 5438c2ecf20Sopenharmony_ci iwl_mvm_get_tx_rate_n_flags(mvm, info, sta, 5448c2ecf20Sopenharmony_ci hdr->frame_control); 5458c2ecf20Sopenharmony_ci } else if (!ieee80211_is_data(hdr->frame_control) || 5468c2ecf20Sopenharmony_ci mvmsta->sta_state < IEEE80211_STA_AUTHORIZED) { 5478c2ecf20Sopenharmony_ci /* These are important frames */ 5488c2ecf20Sopenharmony_ci flags |= IWL_TX_FLAGS_HIGH_PRI; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (mvm->trans->trans_cfg->device_family >= 5528c2ecf20Sopenharmony_ci IWL_DEVICE_FAMILY_AX210) { 5538c2ecf20Sopenharmony_ci struct iwl_tx_cmd_gen3 *cmd = (void *)dev_cmd->payload; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci cmd->offload_assist |= cpu_to_le32(offload_assist); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Total # bytes to be transmitted */ 5588c2ecf20Sopenharmony_ci cmd->len = cpu_to_le16((u16)skb->len); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* Copy MAC header from skb into command buffer */ 5618c2ecf20Sopenharmony_ci memcpy(cmd->hdr, hdr, hdrlen); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci cmd->flags = cpu_to_le16(flags); 5648c2ecf20Sopenharmony_ci cmd->rate_n_flags = cpu_to_le32(rate_n_flags); 5658c2ecf20Sopenharmony_ci } else { 5668c2ecf20Sopenharmony_ci struct iwl_tx_cmd_gen2 *cmd = (void *)dev_cmd->payload; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci cmd->offload_assist |= cpu_to_le16(offload_assist); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* Total # bytes to be transmitted */ 5718c2ecf20Sopenharmony_ci cmd->len = cpu_to_le16((u16)skb->len); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* Copy MAC header from skb into command buffer */ 5748c2ecf20Sopenharmony_ci memcpy(cmd->hdr, hdr, hdrlen); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci cmd->flags = cpu_to_le32(flags); 5778c2ecf20Sopenharmony_ci cmd->rate_n_flags = cpu_to_le32(rate_n_flags); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci goto out; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (info->control.hw_key) 5858c2ecf20Sopenharmony_ci iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb, hdrlen); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci iwl_mvm_set_tx_cmd(mvm, skb, tx_cmd, info, sta_id); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* Copy MAC header from skb into command buffer */ 5928c2ecf20Sopenharmony_ci memcpy(tx_cmd->hdr, hdr, hdrlen); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ciout: 5958c2ecf20Sopenharmony_ci return dev_cmd; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void iwl_mvm_skb_prepare_status(struct sk_buff *skb, 5998c2ecf20Sopenharmony_ci struct iwl_device_tx_cmd *cmd) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci memset(&skb_info->status, 0, sizeof(skb_info->status)); 6048c2ecf20Sopenharmony_ci memset(skb_info->driver_data, 0, sizeof(skb_info->driver_data)); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci skb_info->driver_data[1] = cmd; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_cistatic int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm, 6108c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 6118c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = 6148c2ecf20Sopenharmony_ci iwl_mvm_vif_from_mac80211(info->control.vif); 6158c2ecf20Sopenharmony_ci __le16 fc = hdr->frame_control; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci switch (info->control.vif->type) { 6188c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 6198c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 6208c2ecf20Sopenharmony_ci /* 6218c2ecf20Sopenharmony_ci * Non-bufferable frames use the broadcast station, thus they 6228c2ecf20Sopenharmony_ci * use the probe queue. 6238c2ecf20Sopenharmony_ci * Also take care of the case where we send a deauth to a 6248c2ecf20Sopenharmony_ci * station that we don't have, or similarly an association 6258c2ecf20Sopenharmony_ci * response (with non-success status) for a station we can't 6268c2ecf20Sopenharmony_ci * accept. 6278c2ecf20Sopenharmony_ci * Also, disassociate frames might happen, particular with 6288c2ecf20Sopenharmony_ci * reason 7 ("Class 3 frame received from nonassociated STA"). 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_ci if (ieee80211_is_mgmt(fc) && 6318c2ecf20Sopenharmony_ci (!ieee80211_is_bufferable_mmpdu(fc) || 6328c2ecf20Sopenharmony_ci ieee80211_is_deauth(fc) || ieee80211_is_disassoc(fc))) 6338c2ecf20Sopenharmony_ci return mvm->probe_queue; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (!ieee80211_has_order(fc) && !ieee80211_is_probe_req(fc) && 6368c2ecf20Sopenharmony_ci is_multicast_ether_addr(hdr->addr1)) 6378c2ecf20Sopenharmony_ci return mvmvif->cab_queue; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci WARN_ONCE(info->control.vif->type != NL80211_IFTYPE_ADHOC, 6408c2ecf20Sopenharmony_ci "fc=0x%02x", le16_to_cpu(fc)); 6418c2ecf20Sopenharmony_ci return mvm->probe_queue; 6428c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 6438c2ecf20Sopenharmony_ci if (ieee80211_is_mgmt(fc)) 6448c2ecf20Sopenharmony_ci return mvm->p2p_dev_queue; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 6478c2ecf20Sopenharmony_ci return mvm->p2p_dev_queue; 6488c2ecf20Sopenharmony_ci default: 6498c2ecf20Sopenharmony_ci WARN_ONCE(1, "Not a ctrl vif, no available queue\n"); 6508c2ecf20Sopenharmony_ci return -1; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void iwl_mvm_probe_resp_set_noa(struct iwl_mvm *mvm, 6558c2ecf20Sopenharmony_ci struct sk_buff *skb) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 6588c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = 6598c2ecf20Sopenharmony_ci iwl_mvm_vif_from_mac80211(info->control.vif); 6608c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; 6618c2ecf20Sopenharmony_ci int base_len = (u8 *)mgmt->u.probe_resp.variable - (u8 *)mgmt; 6628c2ecf20Sopenharmony_ci struct iwl_probe_resp_data *resp_data; 6638c2ecf20Sopenharmony_ci u8 *ie, *pos; 6648c2ecf20Sopenharmony_ci u8 match[] = { 6658c2ecf20Sopenharmony_ci (WLAN_OUI_WFA >> 16) & 0xff, 6668c2ecf20Sopenharmony_ci (WLAN_OUI_WFA >> 8) & 0xff, 6678c2ecf20Sopenharmony_ci WLAN_OUI_WFA & 0xff, 6688c2ecf20Sopenharmony_ci WLAN_OUI_TYPE_WFA_P2P, 6698c2ecf20Sopenharmony_ci }; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci rcu_read_lock(); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci resp_data = rcu_dereference(mvmvif->probe_resp_data); 6748c2ecf20Sopenharmony_ci if (!resp_data) 6758c2ecf20Sopenharmony_ci goto out; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (!resp_data->notif.noa_active) 6788c2ecf20Sopenharmony_ci goto out; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci ie = (u8 *)cfg80211_find_ie_match(WLAN_EID_VENDOR_SPECIFIC, 6818c2ecf20Sopenharmony_ci mgmt->u.probe_resp.variable, 6828c2ecf20Sopenharmony_ci skb->len - base_len, 6838c2ecf20Sopenharmony_ci match, 4, 2); 6848c2ecf20Sopenharmony_ci if (!ie) { 6858c2ecf20Sopenharmony_ci IWL_DEBUG_TX(mvm, "probe resp doesn't have P2P IE\n"); 6868c2ecf20Sopenharmony_ci goto out; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (skb_tailroom(skb) < resp_data->noa_len) { 6908c2ecf20Sopenharmony_ci if (pskb_expand_head(skb, 0, resp_data->noa_len, GFP_ATOMIC)) { 6918c2ecf20Sopenharmony_ci IWL_ERR(mvm, 6928c2ecf20Sopenharmony_ci "Failed to reallocate probe resp\n"); 6938c2ecf20Sopenharmony_ci goto out; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci pos = skb_put(skb, resp_data->noa_len); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_VENDOR_SPECIFIC; 7008c2ecf20Sopenharmony_ci /* Set length of IE body (not including ID and length itself) */ 7018c2ecf20Sopenharmony_ci *pos++ = resp_data->noa_len - 2; 7028c2ecf20Sopenharmony_ci *pos++ = (WLAN_OUI_WFA >> 16) & 0xff; 7038c2ecf20Sopenharmony_ci *pos++ = (WLAN_OUI_WFA >> 8) & 0xff; 7048c2ecf20Sopenharmony_ci *pos++ = WLAN_OUI_WFA & 0xff; 7058c2ecf20Sopenharmony_ci *pos++ = WLAN_OUI_TYPE_WFA_P2P; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci memcpy(pos, &resp_data->notif.noa_attr, 7088c2ecf20Sopenharmony_ci resp_data->noa_len - sizeof(struct ieee80211_vendor_ie)); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ciout: 7118c2ecf20Sopenharmony_ci rcu_read_unlock(); 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ciint iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 7178c2ecf20Sopenharmony_ci struct ieee80211_tx_info info; 7188c2ecf20Sopenharmony_ci struct iwl_device_tx_cmd *dev_cmd; 7198c2ecf20Sopenharmony_ci u8 sta_id; 7208c2ecf20Sopenharmony_ci int hdrlen = ieee80211_hdrlen(hdr->frame_control); 7218c2ecf20Sopenharmony_ci __le16 fc = hdr->frame_control; 7228c2ecf20Sopenharmony_ci bool offchannel = IEEE80211_SKB_CB(skb)->flags & 7238c2ecf20Sopenharmony_ci IEEE80211_TX_CTL_TX_OFFCHAN; 7248c2ecf20Sopenharmony_ci int queue = -1; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (IWL_MVM_NON_TRANSMITTING_AP && ieee80211_is_probe_resp(fc)) 7278c2ecf20Sopenharmony_ci return -1; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci memcpy(&info, skb->cb, sizeof(info)); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(skb->len > IEEE80211_MAX_DATA_LEN + hdrlen)) 7328c2ecf20Sopenharmony_ci return -1; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(info.flags & IEEE80211_TX_CTL_AMPDU)) 7358c2ecf20Sopenharmony_ci return -1; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (info.control.vif) { 7388c2ecf20Sopenharmony_ci struct iwl_mvm_vif *mvmvif = 7398c2ecf20Sopenharmony_ci iwl_mvm_vif_from_mac80211(info.control.vif); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (info.control.vif->type == NL80211_IFTYPE_P2P_DEVICE || 7428c2ecf20Sopenharmony_ci info.control.vif->type == NL80211_IFTYPE_AP || 7438c2ecf20Sopenharmony_ci info.control.vif->type == NL80211_IFTYPE_ADHOC) { 7448c2ecf20Sopenharmony_ci if (!ieee80211_is_data(hdr->frame_control)) 7458c2ecf20Sopenharmony_ci sta_id = mvmvif->bcast_sta.sta_id; 7468c2ecf20Sopenharmony_ci else 7478c2ecf20Sopenharmony_ci sta_id = mvmvif->mcast_sta.sta_id; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci queue = iwl_mvm_get_ctrl_vif_queue(mvm, &info, hdr); 7508c2ecf20Sopenharmony_ci } else if (info.control.vif->type == NL80211_IFTYPE_MONITOR) { 7518c2ecf20Sopenharmony_ci queue = mvm->snif_queue; 7528c2ecf20Sopenharmony_ci sta_id = mvm->snif_sta.sta_id; 7538c2ecf20Sopenharmony_ci } else if (info.control.vif->type == NL80211_IFTYPE_STATION && 7548c2ecf20Sopenharmony_ci offchannel) { 7558c2ecf20Sopenharmony_ci /* 7568c2ecf20Sopenharmony_ci * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets 7578c2ecf20Sopenharmony_ci * that can be used in 2 different types of vifs, P2P & 7588c2ecf20Sopenharmony_ci * STATION. 7598c2ecf20Sopenharmony_ci * P2P uses the offchannel queue. 7608c2ecf20Sopenharmony_ci * STATION (HS2.0) uses the auxiliary context of the FW, 7618c2ecf20Sopenharmony_ci * and hence needs to be sent on the aux queue. 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_ci sta_id = mvm->aux_sta.sta_id; 7648c2ecf20Sopenharmony_ci queue = mvm->aux_queue; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (queue < 0) { 7698c2ecf20Sopenharmony_ci IWL_ERR(mvm, "No queue was found. Dropping TX\n"); 7708c2ecf20Sopenharmony_ci return -1; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (unlikely(ieee80211_is_probe_resp(fc))) 7748c2ecf20Sopenharmony_ci iwl_mvm_probe_resp_set_noa(mvm, skb); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, queue); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci dev_cmd = iwl_mvm_set_tx_params(mvm, skb, &info, hdrlen, NULL, sta_id); 7798c2ecf20Sopenharmony_ci if (!dev_cmd) 7808c2ecf20Sopenharmony_ci return -1; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* From now on, we cannot access info->control */ 7838c2ecf20Sopenharmony_ci iwl_mvm_skb_prepare_status(skb, dev_cmd); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (iwl_trans_tx(mvm->trans, skb, dev_cmd, queue)) { 7868c2ecf20Sopenharmony_ci iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); 7878c2ecf20Sopenharmony_ci return -1; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return 0; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ciunsigned int iwl_mvm_max_amsdu_size(struct iwl_mvm *mvm, 7948c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, unsigned int tid) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 7978c2ecf20Sopenharmony_ci enum nl80211_band band = mvmsta->vif->bss_conf.chandef.chan->band; 7988c2ecf20Sopenharmony_ci u8 ac = tid_to_mac80211_ac[tid]; 7998c2ecf20Sopenharmony_ci unsigned int txf; 8008c2ecf20Sopenharmony_ci int lmac = iwl_mvm_get_lmac_id(mvm->fw, band); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* For HE redirect to trigger based fifos */ 8038c2ecf20Sopenharmony_ci if (sta->he_cap.has_he && !WARN_ON(!iwl_mvm_has_new_tx_api(mvm))) 8048c2ecf20Sopenharmony_ci ac += 4; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, ac); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* 8098c2ecf20Sopenharmony_ci * Don't send an AMSDU that will be longer than the TXF. 8108c2ecf20Sopenharmony_ci * Add a security margin of 256 for the TX command + headers. 8118c2ecf20Sopenharmony_ci * We also want to have the start of the next packet inside the 8128c2ecf20Sopenharmony_ci * fifo to be able to send bursts. 8138c2ecf20Sopenharmony_ci */ 8148c2ecf20Sopenharmony_ci return min_t(unsigned int, mvmsta->max_amsdu_len, 8158c2ecf20Sopenharmony_ci mvm->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci#ifdef CONFIG_INET 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic int 8218c2ecf20Sopenharmony_ciiwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, 8228c2ecf20Sopenharmony_ci netdev_features_t netdev_flags, 8238c2ecf20Sopenharmony_ci struct sk_buff_head *mpdus_skb) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct sk_buff *tmp, *next; 8268c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (void *)skb->data; 8278c2ecf20Sopenharmony_ci char cb[sizeof(skb->cb)]; 8288c2ecf20Sopenharmony_ci u16 i = 0; 8298c2ecf20Sopenharmony_ci unsigned int tcp_payload_len; 8308c2ecf20Sopenharmony_ci unsigned int mss = skb_shinfo(skb)->gso_size; 8318c2ecf20Sopenharmony_ci bool ipv4 = (skb->protocol == htons(ETH_P_IP)); 8328c2ecf20Sopenharmony_ci bool qos = ieee80211_is_data_qos(hdr->frame_control); 8338c2ecf20Sopenharmony_ci u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_size = num_subframes * mss; 8368c2ecf20Sopenharmony_ci memcpy(cb, skb->cb, sizeof(cb)); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci next = skb_gso_segment(skb, netdev_flags); 8398c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_size = mss; 8408c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6; 8418c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(IS_ERR(next))) 8428c2ecf20Sopenharmony_ci return -EINVAL; 8438c2ecf20Sopenharmony_ci else if (next) 8448c2ecf20Sopenharmony_ci consume_skb(skb); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci skb_list_walk_safe(next, tmp, next) { 8478c2ecf20Sopenharmony_ci memcpy(tmp->cb, cb, sizeof(tmp->cb)); 8488c2ecf20Sopenharmony_ci /* 8498c2ecf20Sopenharmony_ci * Compute the length of all the data added for the A-MSDU. 8508c2ecf20Sopenharmony_ci * This will be used to compute the length to write in the TX 8518c2ecf20Sopenharmony_ci * command. We have: SNAP + IP + TCP for n -1 subframes and 8528c2ecf20Sopenharmony_ci * ETH header for n subframes. 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_ci tcp_payload_len = skb_tail_pointer(tmp) - 8558c2ecf20Sopenharmony_ci skb_transport_header(tmp) - 8568c2ecf20Sopenharmony_ci tcp_hdrlen(tmp) + tmp->data_len; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (ipv4) 8598c2ecf20Sopenharmony_ci ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (tcp_payload_len > mss) { 8628c2ecf20Sopenharmony_ci skb_shinfo(tmp)->gso_size = mss; 8638c2ecf20Sopenharmony_ci skb_shinfo(tmp)->gso_type = ipv4 ? SKB_GSO_TCPV4 : 8648c2ecf20Sopenharmony_ci SKB_GSO_TCPV6; 8658c2ecf20Sopenharmony_ci } else { 8668c2ecf20Sopenharmony_ci if (qos) { 8678c2ecf20Sopenharmony_ci u8 *qc; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (ipv4) 8708c2ecf20Sopenharmony_ci ip_send_check(ip_hdr(tmp)); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci qc = ieee80211_get_qos_ctl((void *)tmp->data); 8738c2ecf20Sopenharmony_ci *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci skb_shinfo(tmp)->gso_size = 0; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci skb_mark_not_on_list(tmp); 8798c2ecf20Sopenharmony_ci __skb_queue_tail(mpdus_skb, tmp); 8808c2ecf20Sopenharmony_ci i++; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, 8878c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 8888c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 8898c2ecf20Sopenharmony_ci struct sk_buff_head *mpdus_skb) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 8928c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (void *)skb->data; 8938c2ecf20Sopenharmony_ci unsigned int mss = skb_shinfo(skb)->gso_size; 8948c2ecf20Sopenharmony_ci unsigned int num_subframes, tcp_payload_len, subf_len, max_amsdu_len; 8958c2ecf20Sopenharmony_ci u16 snap_ip_tcp, pad; 8968c2ecf20Sopenharmony_ci netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG; 8978c2ecf20Sopenharmony_ci u8 tid; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) + 9008c2ecf20Sopenharmony_ci tcp_hdrlen(skb); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (!mvmsta->max_amsdu_len || 9038c2ecf20Sopenharmony_ci !ieee80211_is_data_qos(hdr->frame_control) || 9048c2ecf20Sopenharmony_ci !mvmsta->amsdu_enabled) 9058c2ecf20Sopenharmony_ci return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* 9088c2ecf20Sopenharmony_ci * Do not build AMSDU for IPv6 with extension headers. 9098c2ecf20Sopenharmony_ci * ask stack to segment and checkum the generated MPDUs for us. 9108c2ecf20Sopenharmony_ci */ 9118c2ecf20Sopenharmony_ci if (skb->protocol == htons(ETH_P_IPV6) && 9128c2ecf20Sopenharmony_ci ((struct ipv6hdr *)skb_network_header(skb))->nexthdr != 9138c2ecf20Sopenharmony_ci IPPROTO_TCP) { 9148c2ecf20Sopenharmony_ci netdev_flags &= ~NETIF_F_CSUM_MASK; 9158c2ecf20Sopenharmony_ci return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci tid = ieee80211_get_tid(hdr); 9198c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) 9208c2ecf20Sopenharmony_ci return -EINVAL; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* 9238c2ecf20Sopenharmony_ci * No need to lock amsdu_in_ampdu_allowed since it can't be modified 9248c2ecf20Sopenharmony_ci * during an BA session. 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_ci if ((info->flags & IEEE80211_TX_CTL_AMPDU && 9278c2ecf20Sopenharmony_ci !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed) || 9288c2ecf20Sopenharmony_ci !(mvmsta->amsdu_enabled & BIT(tid))) 9298c2ecf20Sopenharmony_ci return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* 9328c2ecf20Sopenharmony_ci * Take the min of ieee80211 station and mvm station 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci max_amsdu_len = 9358c2ecf20Sopenharmony_ci min_t(unsigned int, sta->max_amsdu_len, 9368c2ecf20Sopenharmony_ci iwl_mvm_max_amsdu_size(mvm, sta, tid)); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* 9398c2ecf20Sopenharmony_ci * Limit A-MSDU in A-MPDU to 4095 bytes when VHT is not 9408c2ecf20Sopenharmony_ci * supported. This is a spec requirement (IEEE 802.11-2015 9418c2ecf20Sopenharmony_ci * section 8.7.3 NOTE 3). 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_AMPDU && 9448c2ecf20Sopenharmony_ci !sta->vht_cap.vht_supported) 9458c2ecf20Sopenharmony_ci max_amsdu_len = min_t(unsigned int, max_amsdu_len, 4095); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* Sub frame header + SNAP + IP header + TCP header + MSS */ 9488c2ecf20Sopenharmony_ci subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss; 9498c2ecf20Sopenharmony_ci pad = (4 - subf_len) & 0x3; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* 9528c2ecf20Sopenharmony_ci * If we have N subframes in the A-MSDU, then the A-MSDU's size is 9538c2ecf20Sopenharmony_ci * N * subf_len + (N - 1) * pad. 9548c2ecf20Sopenharmony_ci */ 9558c2ecf20Sopenharmony_ci num_subframes = (max_amsdu_len + pad) / (subf_len + pad); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (sta->max_amsdu_subframes && 9588c2ecf20Sopenharmony_ci num_subframes > sta->max_amsdu_subframes) 9598c2ecf20Sopenharmony_ci num_subframes = sta->max_amsdu_subframes; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - 9628c2ecf20Sopenharmony_ci tcp_hdrlen(skb) + skb->data_len; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* 9658c2ecf20Sopenharmony_ci * Make sure we have enough TBs for the A-MSDU: 9668c2ecf20Sopenharmony_ci * 2 for each subframe 9678c2ecf20Sopenharmony_ci * 1 more for each fragment 9688c2ecf20Sopenharmony_ci * 1 more for the potential data in the header 9698c2ecf20Sopenharmony_ci */ 9708c2ecf20Sopenharmony_ci if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) > 9718c2ecf20Sopenharmony_ci mvm->trans->max_skb_frags) 9728c2ecf20Sopenharmony_ci num_subframes = 1; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (num_subframes > 1) 9758c2ecf20Sopenharmony_ci *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* This skb fits in one single A-MSDU */ 9788c2ecf20Sopenharmony_ci if (num_subframes * mss >= tcp_payload_len) { 9798c2ecf20Sopenharmony_ci __skb_queue_tail(mpdus_skb, skb); 9808c2ecf20Sopenharmony_ci return 0; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* 9848c2ecf20Sopenharmony_ci * Trick the segmentation function to make it 9858c2ecf20Sopenharmony_ci * create SKBs that can fit into one A-MSDU. 9868c2ecf20Sopenharmony_ci */ 9878c2ecf20Sopenharmony_ci return iwl_mvm_tx_tso_segment(skb, num_subframes, netdev_flags, 9888c2ecf20Sopenharmony_ci mpdus_skb); 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci#else /* CONFIG_INET */ 9918c2ecf20Sopenharmony_cistatic int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, 9928c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 9938c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 9948c2ecf20Sopenharmony_ci struct sk_buff_head *mpdus_skb) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci /* Impossible to get TSO with CONFIG_INET */ 9978c2ecf20Sopenharmony_ci WARN_ON(1); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci return -1; 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci#endif 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci/* Check if there are any timed-out TIDs on a given shared TXQ */ 10048c2ecf20Sopenharmony_cistatic bool iwl_mvm_txq_should_update(struct iwl_mvm *mvm, int txq_id) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci unsigned long queue_tid_bitmap = mvm->queue_info[txq_id].tid_bitmap; 10078c2ecf20Sopenharmony_ci unsigned long now = jiffies; 10088c2ecf20Sopenharmony_ci int tid; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) 10118c2ecf20Sopenharmony_ci return false; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci for_each_set_bit(tid, &queue_tid_bitmap, IWL_MAX_TID_COUNT + 1) { 10148c2ecf20Sopenharmony_ci if (time_before(mvm->queue_info[txq_id].last_frame_time[tid] + 10158c2ecf20Sopenharmony_ci IWL_MVM_DQA_QUEUE_TIMEOUT, now)) 10168c2ecf20Sopenharmony_ci return true; 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci return false; 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic void iwl_mvm_tx_airtime(struct iwl_mvm *mvm, 10238c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta, 10248c2ecf20Sopenharmony_ci int airtime) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci int mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK; 10278c2ecf20Sopenharmony_ci struct iwl_mvm_tcm_mac *mdata; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (mac >= NUM_MAC_INDEX_DRIVER) 10308c2ecf20Sopenharmony_ci return; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci mdata = &mvm->tcm.data[mac]; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci if (mvm->tcm.paused) 10358c2ecf20Sopenharmony_ci return; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci if (time_after(jiffies, mvm->tcm.ts + MVM_TCM_PERIOD)) 10388c2ecf20Sopenharmony_ci schedule_delayed_work(&mvm->tcm.work, 0); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci mdata->tx.airtime += airtime; 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_cistatic int iwl_mvm_tx_pkt_queued(struct iwl_mvm *mvm, 10448c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta, int tid) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci u32 ac = tid_to_mac80211_ac[tid]; 10478c2ecf20Sopenharmony_ci int mac = mvmsta->mac_id_n_color & FW_CTXT_ID_MSK; 10488c2ecf20Sopenharmony_ci struct iwl_mvm_tcm_mac *mdata; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (mac >= NUM_MAC_INDEX_DRIVER) 10518c2ecf20Sopenharmony_ci return -EINVAL; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci mdata = &mvm->tcm.data[mac]; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci mdata->tx.pkts[ac]++; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci return 0; 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci/* 10618c2ecf20Sopenharmony_ci * Sets the fields in the Tx cmd that are crypto related. 10628c2ecf20Sopenharmony_ci * 10638c2ecf20Sopenharmony_ci * This function must be called with BHs disabled. 10648c2ecf20Sopenharmony_ci */ 10658c2ecf20Sopenharmony_cistatic int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, 10668c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info, 10678c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; 10708c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 10718c2ecf20Sopenharmony_ci struct iwl_device_tx_cmd *dev_cmd; 10728c2ecf20Sopenharmony_ci __le16 fc; 10738c2ecf20Sopenharmony_ci u16 seq_number = 0; 10748c2ecf20Sopenharmony_ci u8 tid = IWL_MAX_TID_COUNT; 10758c2ecf20Sopenharmony_ci u16 txq_id; 10768c2ecf20Sopenharmony_ci bool is_ampdu = false; 10778c2ecf20Sopenharmony_ci int hdrlen; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 10808c2ecf20Sopenharmony_ci fc = hdr->frame_control; 10818c2ecf20Sopenharmony_ci hdrlen = ieee80211_hdrlen(fc); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (IWL_MVM_NON_TRANSMITTING_AP && ieee80211_is_probe_resp(fc)) 10848c2ecf20Sopenharmony_ci return -1; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!mvmsta)) 10878c2ecf20Sopenharmony_ci return -1; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_INVALID_STA)) 10908c2ecf20Sopenharmony_ci return -1; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (unlikely(ieee80211_is_any_nullfunc(fc)) && sta->he_cap.has_he) 10938c2ecf20Sopenharmony_ci return -1; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (unlikely(ieee80211_is_probe_resp(fc))) 10968c2ecf20Sopenharmony_ci iwl_mvm_probe_resp_set_noa(mvm, skb); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci dev_cmd = iwl_mvm_set_tx_params(mvm, skb, info, hdrlen, 10998c2ecf20Sopenharmony_ci sta, mvmsta->sta_id); 11008c2ecf20Sopenharmony_ci if (!dev_cmd) 11018c2ecf20Sopenharmony_ci goto drop; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* 11048c2ecf20Sopenharmony_ci * we handle that entirely ourselves -- for uAPSD the firmware 11058c2ecf20Sopenharmony_ci * will always send a notification, and for PS-Poll responses 11068c2ecf20Sopenharmony_ci * we'll notify mac80211 when getting frame status 11078c2ecf20Sopenharmony_ci */ 11088c2ecf20Sopenharmony_ci info->flags &= ~IEEE80211_TX_STATUS_EOSP; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci spin_lock(&mvmsta->lock); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* nullfunc frames should go to the MGMT queue regardless of QOS, 11138c2ecf20Sopenharmony_ci * the condition of !ieee80211_is_qos_nullfunc(fc) keeps the default 11148c2ecf20Sopenharmony_ci * assignment of MGMT TID 11158c2ecf20Sopenharmony_ci */ 11168c2ecf20Sopenharmony_ci if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { 11178c2ecf20Sopenharmony_ci tid = ieee80211_get_tid(hdr); 11188c2ecf20Sopenharmony_ci if (WARN_ONCE(tid >= IWL_MAX_TID_COUNT, "Invalid TID %d", tid)) 11198c2ecf20Sopenharmony_ci goto drop_unlock_sta; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; 11228c2ecf20Sopenharmony_ci if (WARN_ONCE(is_ampdu && 11238c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].state != IWL_AGG_ON, 11248c2ecf20Sopenharmony_ci "Invalid internal agg state %d for TID %d", 11258c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].state, tid)) 11268c2ecf20Sopenharmony_ci goto drop_unlock_sta; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci seq_number = mvmsta->tid_data[tid].seq_number; 11298c2ecf20Sopenharmony_ci seq_number &= IEEE80211_SCTL_SEQ; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm)) { 11328c2ecf20Sopenharmony_ci struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); 11358c2ecf20Sopenharmony_ci hdr->seq_ctrl |= cpu_to_le16(seq_number); 11368c2ecf20Sopenharmony_ci /* update the tx_cmd hdr as it was already copied */ 11378c2ecf20Sopenharmony_ci tx_cmd->hdr->seq_ctrl = hdr->seq_ctrl; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci } else if (ieee80211_is_data(fc) && !ieee80211_is_data_qos(fc)) { 11408c2ecf20Sopenharmony_ci tid = IWL_TID_NON_QOS; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci txq_id = mvmsta->tid_data[tid].txq_id; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (WARN_ONCE(txq_id == IWL_MVM_INVALID_QUEUE, "Invalid TXQ id")) { 11488c2ecf20Sopenharmony_ci iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); 11498c2ecf20Sopenharmony_ci spin_unlock(&mvmsta->lock); 11508c2ecf20Sopenharmony_ci return -1; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (!iwl_mvm_has_new_tx_api(mvm)) { 11548c2ecf20Sopenharmony_ci /* Keep track of the time of the last frame for this RA/TID */ 11558c2ecf20Sopenharmony_ci mvm->queue_info[txq_id].last_frame_time[tid] = jiffies; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci /* 11588c2ecf20Sopenharmony_ci * If we have timed-out TIDs - schedule the worker that will 11598c2ecf20Sopenharmony_ci * reconfig the queues and update them 11608c2ecf20Sopenharmony_ci * 11618c2ecf20Sopenharmony_ci * Note that the no lock is taken here in order to not serialize 11628c2ecf20Sopenharmony_ci * the TX flow. This isn't dangerous because scheduling 11638c2ecf20Sopenharmony_ci * mvm->add_stream_wk can't ruin the state, and if we DON'T 11648c2ecf20Sopenharmony_ci * schedule it due to some race condition then next TX we get 11658c2ecf20Sopenharmony_ci * here we will. 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_ci if (unlikely(mvm->queue_info[txq_id].status == 11688c2ecf20Sopenharmony_ci IWL_MVM_QUEUE_SHARED && 11698c2ecf20Sopenharmony_ci iwl_mvm_txq_should_update(mvm, txq_id))) 11708c2ecf20Sopenharmony_ci schedule_work(&mvm->add_stream_wk); 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x len %d\n", 11748c2ecf20Sopenharmony_ci mvmsta->sta_id, tid, txq_id, 11758c2ecf20Sopenharmony_ci IEEE80211_SEQ_TO_SN(seq_number), skb->len); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci /* From now on, we cannot access info->control */ 11788c2ecf20Sopenharmony_ci iwl_mvm_skb_prepare_status(skb, dev_cmd); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id)) 11818c2ecf20Sopenharmony_ci goto drop_unlock_sta; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (tid < IWL_MAX_TID_COUNT && !ieee80211_has_morefrags(fc)) 11848c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].seq_number = seq_number + 0x10; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci spin_unlock(&mvmsta->lock); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (iwl_mvm_tx_pkt_queued(mvm, mvmsta, 11898c2ecf20Sopenharmony_ci tid == IWL_MAX_TID_COUNT ? 0 : tid)) 11908c2ecf20Sopenharmony_ci goto drop; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci return 0; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cidrop_unlock_sta: 11958c2ecf20Sopenharmony_ci iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); 11968c2ecf20Sopenharmony_ci spin_unlock(&mvmsta->lock); 11978c2ecf20Sopenharmony_cidrop: 11988c2ecf20Sopenharmony_ci IWL_DEBUG_TX(mvm, "TX to [%d|%d] dropped\n", mvmsta->sta_id, tid); 11998c2ecf20Sopenharmony_ci return -1; 12008c2ecf20Sopenharmony_ci} 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ciint iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, 12038c2ecf20Sopenharmony_ci struct ieee80211_sta *sta) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 12068c2ecf20Sopenharmony_ci struct ieee80211_tx_info info; 12078c2ecf20Sopenharmony_ci struct sk_buff_head mpdus_skbs; 12088c2ecf20Sopenharmony_ci unsigned int payload_len; 12098c2ecf20Sopenharmony_ci int ret; 12108c2ecf20Sopenharmony_ci struct sk_buff *orig_skb = skb; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!mvmsta)) 12138c2ecf20Sopenharmony_ci return -1; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_INVALID_STA)) 12168c2ecf20Sopenharmony_ci return -1; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci memcpy(&info, skb->cb, sizeof(info)); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (!skb_is_gso(skb)) 12218c2ecf20Sopenharmony_ci return iwl_mvm_tx_mpdu(mvm, skb, &info, sta); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - 12248c2ecf20Sopenharmony_ci tcp_hdrlen(skb) + skb->data_len; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (payload_len <= skb_shinfo(skb)->gso_size) 12278c2ecf20Sopenharmony_ci return iwl_mvm_tx_mpdu(mvm, skb, &info, sta); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci __skb_queue_head_init(&mpdus_skbs); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci ret = iwl_mvm_tx_tso(mvm, skb, &info, sta, &mpdus_skbs); 12328c2ecf20Sopenharmony_ci if (ret) 12338c2ecf20Sopenharmony_ci return ret; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (WARN_ON(skb_queue_empty(&mpdus_skbs))) 12368c2ecf20Sopenharmony_ci return ret; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci while (!skb_queue_empty(&mpdus_skbs)) { 12398c2ecf20Sopenharmony_ci skb = __skb_dequeue(&mpdus_skbs); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci ret = iwl_mvm_tx_mpdu(mvm, skb, &info, sta); 12428c2ecf20Sopenharmony_ci if (ret) { 12438c2ecf20Sopenharmony_ci /* Free skbs created as part of TSO logic that have not yet been dequeued */ 12448c2ecf20Sopenharmony_ci __skb_queue_purge(&mpdus_skbs); 12458c2ecf20Sopenharmony_ci /* skb here is not necessarily same as skb that entered this method, 12468c2ecf20Sopenharmony_ci * so free it explicitly. 12478c2ecf20Sopenharmony_ci */ 12488c2ecf20Sopenharmony_ci if (skb == orig_skb) 12498c2ecf20Sopenharmony_ci ieee80211_free_txskb(mvm->hw, skb); 12508c2ecf20Sopenharmony_ci else 12518c2ecf20Sopenharmony_ci kfree_skb(skb); 12528c2ecf20Sopenharmony_ci /* there was error, but we consumed skb one way or another, so return 0 */ 12538c2ecf20Sopenharmony_ci return 0; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci return 0; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, 12618c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, u8 tid) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 12648c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; 12658c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = mvmsta->vif; 12668c2ecf20Sopenharmony_ci u16 normalized_ssn; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci lockdep_assert_held(&mvmsta->lock); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if ((tid_data->state == IWL_AGG_ON || 12718c2ecf20Sopenharmony_ci tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && 12728c2ecf20Sopenharmony_ci iwl_mvm_tid_queued(mvm, tid_data) == 0) { 12738c2ecf20Sopenharmony_ci /* 12748c2ecf20Sopenharmony_ci * Now that this aggregation or DQA queue is empty tell 12758c2ecf20Sopenharmony_ci * mac80211 so it knows we no longer have frames buffered for 12768c2ecf20Sopenharmony_ci * the station on this TID (for the TIM bitmap calculation.) 12778c2ecf20Sopenharmony_ci */ 12788c2ecf20Sopenharmony_ci ieee80211_sta_set_buffered(sta, tid, false); 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci /* 12828c2ecf20Sopenharmony_ci * In 22000 HW, the next_reclaimed index is only 8 bit, so we'll need 12838c2ecf20Sopenharmony_ci * to align the wrap around of ssn so we compare relevant values. 12848c2ecf20Sopenharmony_ci */ 12858c2ecf20Sopenharmony_ci normalized_ssn = tid_data->ssn; 12868c2ecf20Sopenharmony_ci if (mvm->trans->trans_cfg->gen2) 12878c2ecf20Sopenharmony_ci normalized_ssn &= 0xff; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (normalized_ssn != tid_data->next_reclaimed) 12908c2ecf20Sopenharmony_ci return; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci switch (tid_data->state) { 12938c2ecf20Sopenharmony_ci case IWL_EMPTYING_HW_QUEUE_ADDBA: 12948c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 12958c2ecf20Sopenharmony_ci "Can continue addBA flow ssn = next_recl = %d\n", 12968c2ecf20Sopenharmony_ci tid_data->next_reclaimed); 12978c2ecf20Sopenharmony_ci tid_data->state = IWL_AGG_STARTING; 12988c2ecf20Sopenharmony_ci ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); 12998c2ecf20Sopenharmony_ci break; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci case IWL_EMPTYING_HW_QUEUE_DELBA: 13028c2ecf20Sopenharmony_ci IWL_DEBUG_TX_QUEUES(mvm, 13038c2ecf20Sopenharmony_ci "Can continue DELBA flow ssn = next_recl = %d\n", 13048c2ecf20Sopenharmony_ci tid_data->next_reclaimed); 13058c2ecf20Sopenharmony_ci tid_data->state = IWL_AGG_OFF; 13068c2ecf20Sopenharmony_ci ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 13078c2ecf20Sopenharmony_ci break; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci default: 13108c2ecf20Sopenharmony_ci break; 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 13158c2ecf20Sopenharmony_ciconst char *iwl_mvm_get_tx_fail_reason(u32 status) 13168c2ecf20Sopenharmony_ci{ 13178c2ecf20Sopenharmony_ci#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x 13188c2ecf20Sopenharmony_ci#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci switch (status & TX_STATUS_MSK) { 13218c2ecf20Sopenharmony_ci case TX_STATUS_SUCCESS: 13228c2ecf20Sopenharmony_ci return "SUCCESS"; 13238c2ecf20Sopenharmony_ci TX_STATUS_POSTPONE(DELAY); 13248c2ecf20Sopenharmony_ci TX_STATUS_POSTPONE(FEW_BYTES); 13258c2ecf20Sopenharmony_ci TX_STATUS_POSTPONE(BT_PRIO); 13268c2ecf20Sopenharmony_ci TX_STATUS_POSTPONE(QUIET_PERIOD); 13278c2ecf20Sopenharmony_ci TX_STATUS_POSTPONE(CALC_TTAK); 13288c2ecf20Sopenharmony_ci TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); 13298c2ecf20Sopenharmony_ci TX_STATUS_FAIL(SHORT_LIMIT); 13308c2ecf20Sopenharmony_ci TX_STATUS_FAIL(LONG_LIMIT); 13318c2ecf20Sopenharmony_ci TX_STATUS_FAIL(UNDERRUN); 13328c2ecf20Sopenharmony_ci TX_STATUS_FAIL(DRAIN_FLOW); 13338c2ecf20Sopenharmony_ci TX_STATUS_FAIL(RFKILL_FLUSH); 13348c2ecf20Sopenharmony_ci TX_STATUS_FAIL(LIFE_EXPIRE); 13358c2ecf20Sopenharmony_ci TX_STATUS_FAIL(DEST_PS); 13368c2ecf20Sopenharmony_ci TX_STATUS_FAIL(HOST_ABORTED); 13378c2ecf20Sopenharmony_ci TX_STATUS_FAIL(BT_RETRY); 13388c2ecf20Sopenharmony_ci TX_STATUS_FAIL(STA_INVALID); 13398c2ecf20Sopenharmony_ci TX_STATUS_FAIL(FRAG_DROPPED); 13408c2ecf20Sopenharmony_ci TX_STATUS_FAIL(TID_DISABLE); 13418c2ecf20Sopenharmony_ci TX_STATUS_FAIL(FIFO_FLUSHED); 13428c2ecf20Sopenharmony_ci TX_STATUS_FAIL(SMALL_CF_POLL); 13438c2ecf20Sopenharmony_ci TX_STATUS_FAIL(FW_DROP); 13448c2ecf20Sopenharmony_ci TX_STATUS_FAIL(STA_COLOR_MISMATCH); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci return "UNKNOWN"; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci#undef TX_STATUS_FAIL 13508c2ecf20Sopenharmony_ci#undef TX_STATUS_POSTPONE 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci#endif /* CONFIG_IWLWIFI_DEBUG */ 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_civoid iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, 13558c2ecf20Sopenharmony_ci enum nl80211_band band, 13568c2ecf20Sopenharmony_ci struct ieee80211_tx_rate *r) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_HT_MCS_GF_MSK) 13598c2ecf20Sopenharmony_ci r->flags |= IEEE80211_TX_RC_GREEN_FIELD; 13608c2ecf20Sopenharmony_ci switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { 13618c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_20: 13628c2ecf20Sopenharmony_ci break; 13638c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_40: 13648c2ecf20Sopenharmony_ci r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; 13658c2ecf20Sopenharmony_ci break; 13668c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_80: 13678c2ecf20Sopenharmony_ci r->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; 13688c2ecf20Sopenharmony_ci break; 13698c2ecf20Sopenharmony_ci case RATE_MCS_CHAN_WIDTH_160: 13708c2ecf20Sopenharmony_ci r->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH; 13718c2ecf20Sopenharmony_ci break; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_SGI_MSK) 13748c2ecf20Sopenharmony_ci r->flags |= IEEE80211_TX_RC_SHORT_GI; 13758c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) { 13768c2ecf20Sopenharmony_ci r->flags |= IEEE80211_TX_RC_MCS; 13778c2ecf20Sopenharmony_ci r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; 13788c2ecf20Sopenharmony_ci } else if (rate_n_flags & RATE_MCS_VHT_MSK) { 13798c2ecf20Sopenharmony_ci ieee80211_rate_set_vht( 13808c2ecf20Sopenharmony_ci r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK, 13818c2ecf20Sopenharmony_ci ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> 13828c2ecf20Sopenharmony_ci RATE_VHT_MCS_NSS_POS) + 1); 13838c2ecf20Sopenharmony_ci r->flags |= IEEE80211_TX_RC_VHT_MCS; 13848c2ecf20Sopenharmony_ci } else { 13858c2ecf20Sopenharmony_ci r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, 13868c2ecf20Sopenharmony_ci band); 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci/* 13918c2ecf20Sopenharmony_ci * translate ucode response to mac80211 tx status control values 13928c2ecf20Sopenharmony_ci */ 13938c2ecf20Sopenharmony_cistatic void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags, 13948c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci struct ieee80211_tx_rate *r = &info->status.rates[0]; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci info->status.antenna = 13998c2ecf20Sopenharmony_ci ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); 14008c2ecf20Sopenharmony_ci iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r); 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_cistatic void iwl_mvm_tx_status_check_trigger(struct iwl_mvm *mvm, 14048c2ecf20Sopenharmony_ci u32 status) 14058c2ecf20Sopenharmony_ci{ 14068c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_tlv *trig; 14078c2ecf20Sopenharmony_ci struct iwl_fw_dbg_trigger_tx_status *status_trig; 14088c2ecf20Sopenharmony_ci int i; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci trig = iwl_fw_dbg_trigger_on(&mvm->fwrt, NULL, 14118c2ecf20Sopenharmony_ci FW_DBG_TRIGGER_TX_STATUS); 14128c2ecf20Sopenharmony_ci if (!trig) 14138c2ecf20Sopenharmony_ci return; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci status_trig = (void *)trig->data; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(status_trig->statuses); i++) { 14188c2ecf20Sopenharmony_ci /* don't collect on status 0 */ 14198c2ecf20Sopenharmony_ci if (!status_trig->statuses[i].status) 14208c2ecf20Sopenharmony_ci break; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci if (status_trig->statuses[i].status != (status & TX_STATUS_MSK)) 14238c2ecf20Sopenharmony_ci continue; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci iwl_fw_dbg_collect_trig(&mvm->fwrt, trig, 14268c2ecf20Sopenharmony_ci "Tx status %d was received", 14278c2ecf20Sopenharmony_ci status & TX_STATUS_MSK); 14288c2ecf20Sopenharmony_ci break; 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci} 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci/* 14338c2ecf20Sopenharmony_ci * iwl_mvm_get_scd_ssn - returns the SSN of the SCD 14348c2ecf20Sopenharmony_ci * @tx_resp: the Tx response from the fw (agg or non-agg) 14358c2ecf20Sopenharmony_ci * 14368c2ecf20Sopenharmony_ci * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since 14378c2ecf20Sopenharmony_ci * it can't know that everything will go well until the end of the AMPDU, it 14388c2ecf20Sopenharmony_ci * can't know in advance the number of MPDUs that will be sent in the current 14398c2ecf20Sopenharmony_ci * batch. This is why it writes the agg Tx response while it fetches the MPDUs. 14408c2ecf20Sopenharmony_ci * Hence, it can't know in advance what the SSN of the SCD will be at the end 14418c2ecf20Sopenharmony_ci * of the batch. This is why the SSN of the SCD is written at the end of the 14428c2ecf20Sopenharmony_ci * whole struct at a variable offset. This function knows how to cope with the 14438c2ecf20Sopenharmony_ci * variable offset and returns the SSN of the SCD. 14448c2ecf20Sopenharmony_ci */ 14458c2ecf20Sopenharmony_cistatic inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm *mvm, 14468c2ecf20Sopenharmony_ci struct iwl_mvm_tx_resp *tx_resp) 14478c2ecf20Sopenharmony_ci{ 14488c2ecf20Sopenharmony_ci return le32_to_cpup((__le32 *)iwl_mvm_get_agg_status(mvm, tx_resp) + 14498c2ecf20Sopenharmony_ci tx_resp->frame_count) & 0xfff; 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, 14538c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 14568c2ecf20Sopenharmony_ci u16 sequence = le16_to_cpu(pkt->hdr.sequence); 14578c2ecf20Sopenharmony_ci int txq_id = SEQ_TO_QUEUE(sequence); 14588c2ecf20Sopenharmony_ci /* struct iwl_mvm_tx_resp_v3 is almost the same */ 14598c2ecf20Sopenharmony_ci struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; 14608c2ecf20Sopenharmony_ci int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); 14618c2ecf20Sopenharmony_ci int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); 14628c2ecf20Sopenharmony_ci struct agg_tx_status *agg_status = 14638c2ecf20Sopenharmony_ci iwl_mvm_get_agg_status(mvm, tx_resp); 14648c2ecf20Sopenharmony_ci u32 status = le16_to_cpu(agg_status->status); 14658c2ecf20Sopenharmony_ci u16 ssn = iwl_mvm_get_scd_ssn(mvm, tx_resp); 14668c2ecf20Sopenharmony_ci struct sk_buff_head skbs; 14678c2ecf20Sopenharmony_ci u8 skb_freed = 0; 14688c2ecf20Sopenharmony_ci u8 lq_color; 14698c2ecf20Sopenharmony_ci u16 next_reclaimed, seq_ctl; 14708c2ecf20Sopenharmony_ci bool is_ndp = false; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci __skb_queue_head_init(&skbs); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) 14758c2ecf20Sopenharmony_ci txq_id = le16_to_cpu(tx_resp->tx_queue); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci seq_ctl = le16_to_cpu(tx_resp->seq_ctl); 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci /* we can free until ssn % q.n_bd not inclusive */ 14808c2ecf20Sopenharmony_ci iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci while (!skb_queue_empty(&skbs)) { 14838c2ecf20Sopenharmony_ci struct sk_buff *skb = __skb_dequeue(&skbs); 14848c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 14858c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (void *)skb->data; 14868c2ecf20Sopenharmony_ci bool flushed = false; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci skb_freed++; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci memset(&info->status, 0, sizeof(info->status)); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* inform mac80211 about what happened with the frame */ 14958c2ecf20Sopenharmony_ci switch (status & TX_STATUS_MSK) { 14968c2ecf20Sopenharmony_ci case TX_STATUS_SUCCESS: 14978c2ecf20Sopenharmony_ci case TX_STATUS_DIRECT_DONE: 14988c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 14998c2ecf20Sopenharmony_ci break; 15008c2ecf20Sopenharmony_ci case TX_STATUS_FAIL_FIFO_FLUSHED: 15018c2ecf20Sopenharmony_ci case TX_STATUS_FAIL_DRAIN_FLOW: 15028c2ecf20Sopenharmony_ci flushed = true; 15038c2ecf20Sopenharmony_ci break; 15048c2ecf20Sopenharmony_ci case TX_STATUS_FAIL_DEST_PS: 15058c2ecf20Sopenharmony_ci /* the FW should have stopped the queue and not 15068c2ecf20Sopenharmony_ci * return this status 15078c2ecf20Sopenharmony_ci */ 15088c2ecf20Sopenharmony_ci WARN_ON(1); 15098c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_TX_FILTERED; 15108c2ecf20Sopenharmony_ci break; 15118c2ecf20Sopenharmony_ci default: 15128c2ecf20Sopenharmony_ci break; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if ((status & TX_STATUS_MSK) != TX_STATUS_SUCCESS && 15168c2ecf20Sopenharmony_ci ieee80211_is_mgmt(hdr->frame_control)) 15178c2ecf20Sopenharmony_ci iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci /* 15208c2ecf20Sopenharmony_ci * If we are freeing multiple frames, mark all the frames 15218c2ecf20Sopenharmony_ci * but the first one as acked, since they were acknowledged 15228c2ecf20Sopenharmony_ci * before 15238c2ecf20Sopenharmony_ci * */ 15248c2ecf20Sopenharmony_ci if (skb_freed > 1) 15258c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci iwl_mvm_tx_status_check_trigger(mvm, status); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci info->status.rates[0].count = tx_resp->failure_frame + 1; 15308c2ecf20Sopenharmony_ci iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate), 15318c2ecf20Sopenharmony_ci info); 15328c2ecf20Sopenharmony_ci info->status.status_driver_data[1] = 15338c2ecf20Sopenharmony_ci (void *)(uintptr_t)le32_to_cpu(tx_resp->initial_rate); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci /* Single frame failure in an AMPDU queue => send BAR */ 15368c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_AMPDU && 15378c2ecf20Sopenharmony_ci !(info->flags & IEEE80211_TX_STAT_ACK) && 15388c2ecf20Sopenharmony_ci !(info->flags & IEEE80211_TX_STAT_TX_FILTERED) && !flushed) 15398c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; 15408c2ecf20Sopenharmony_ci info->flags &= ~IEEE80211_TX_CTL_AMPDU; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci /* W/A FW bug: seq_ctl is wrong upon failure / BAR frame */ 15438c2ecf20Sopenharmony_ci if (ieee80211_is_back_req(hdr->frame_control)) 15448c2ecf20Sopenharmony_ci seq_ctl = 0; 15458c2ecf20Sopenharmony_ci else if (status != TX_STATUS_SUCCESS) 15468c2ecf20Sopenharmony_ci seq_ctl = le16_to_cpu(hdr->seq_ctrl); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci if (unlikely(!seq_ctl)) { 15498c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (void *)skb->data; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci /* 15528c2ecf20Sopenharmony_ci * If it is an NDP, we can't update next_reclaim since 15538c2ecf20Sopenharmony_ci * its sequence control is 0. Note that for that same 15548c2ecf20Sopenharmony_ci * reason, NDPs are never sent to A-MPDU'able queues 15558c2ecf20Sopenharmony_ci * so that we can never have more than one freed frame 15568c2ecf20Sopenharmony_ci * for a single Tx resonse (see WARN_ON below). 15578c2ecf20Sopenharmony_ci */ 15588c2ecf20Sopenharmony_ci if (ieee80211_is_qos_nullfunc(hdr->frame_control)) 15598c2ecf20Sopenharmony_ci is_ndp = true; 15608c2ecf20Sopenharmony_ci } 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci /* 15638c2ecf20Sopenharmony_ci * TODO: this is not accurate if we are freeing more than one 15648c2ecf20Sopenharmony_ci * packet. 15658c2ecf20Sopenharmony_ci */ 15668c2ecf20Sopenharmony_ci info->status.tx_time = 15678c2ecf20Sopenharmony_ci le16_to_cpu(tx_resp->wireless_media_time); 15688c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1); 15698c2ecf20Sopenharmony_ci lq_color = TX_RES_RATE_TABLE_COL_GET(tx_resp->tlc_info); 15708c2ecf20Sopenharmony_ci info->status.status_driver_data[0] = 15718c2ecf20Sopenharmony_ci RS_DRV_DATA_PACK(lq_color, tx_resp->reduced_tpc); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci ieee80211_tx_status(mvm->hw, skb); 15748c2ecf20Sopenharmony_ci } 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* This is an aggregation queue or might become one, so we use 15778c2ecf20Sopenharmony_ci * the ssn since: ssn = wifi seq_num % 256. 15788c2ecf20Sopenharmony_ci * The seq_ctl is the sequence control of the packet to which 15798c2ecf20Sopenharmony_ci * this Tx response relates. But if there is a hole in the 15808c2ecf20Sopenharmony_ci * bitmap of the BA we received, this Tx response may allow to 15818c2ecf20Sopenharmony_ci * reclaim the hole and all the subsequent packets that were 15828c2ecf20Sopenharmony_ci * already acked. In that case, seq_ctl != ssn, and the next 15838c2ecf20Sopenharmony_ci * packet to be reclaimed will be ssn and not seq_ctl. In that 15848c2ecf20Sopenharmony_ci * case, several packets will be reclaimed even if 15858c2ecf20Sopenharmony_ci * frame_count = 1. 15868c2ecf20Sopenharmony_ci * 15878c2ecf20Sopenharmony_ci * The ssn is the index (% 256) of the latest packet that has 15888c2ecf20Sopenharmony_ci * treated (acked / dropped) + 1. 15898c2ecf20Sopenharmony_ci */ 15908c2ecf20Sopenharmony_ci next_reclaimed = ssn; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, 15938c2ecf20Sopenharmony_ci "TXQ %d status %s (0x%08x)\n", 15948c2ecf20Sopenharmony_ci txq_id, iwl_mvm_get_tx_fail_reason(status), status); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, 15978c2ecf20Sopenharmony_ci "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n", 15988c2ecf20Sopenharmony_ci le32_to_cpu(tx_resp->initial_rate), 15998c2ecf20Sopenharmony_ci tx_resp->failure_frame, SEQ_TO_INDEX(sequence), 16008c2ecf20Sopenharmony_ci ssn, next_reclaimed, seq_ctl); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci rcu_read_lock(); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); 16058c2ecf20Sopenharmony_ci /* 16068c2ecf20Sopenharmony_ci * sta can't be NULL otherwise it'd mean that the sta has been freed in 16078c2ecf20Sopenharmony_ci * the firmware while we still have packets for it in the Tx queues. 16088c2ecf20Sopenharmony_ci */ 16098c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!sta)) 16108c2ecf20Sopenharmony_ci goto out; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (!IS_ERR(sta)) { 16138c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci iwl_mvm_tx_airtime(mvm, mvmsta, 16168c2ecf20Sopenharmony_ci le16_to_cpu(tx_resp->wireless_media_time)); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci if ((status & TX_STATUS_MSK) != TX_STATUS_SUCCESS && 16198c2ecf20Sopenharmony_ci mvmsta->sta_state < IEEE80211_STA_AUTHORIZED) 16208c2ecf20Sopenharmony_ci iwl_mvm_toggle_tx_ant(mvm, &mvmsta->tx_ant); 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci if (sta->wme && tid != IWL_MGMT_TID) { 16238c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data = 16248c2ecf20Sopenharmony_ci &mvmsta->tid_data[tid]; 16258c2ecf20Sopenharmony_ci bool send_eosp_ndp = false; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci if (!is_ndp) { 16308c2ecf20Sopenharmony_ci tid_data->next_reclaimed = next_reclaimed; 16318c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, 16328c2ecf20Sopenharmony_ci "Next reclaimed packet:%d\n", 16338c2ecf20Sopenharmony_ci next_reclaimed); 16348c2ecf20Sopenharmony_ci } else { 16358c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, 16368c2ecf20Sopenharmony_ci "NDP - don't update next_reclaimed\n"); 16378c2ecf20Sopenharmony_ci } 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci iwl_mvm_check_ratid_empty(mvm, sta, tid); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (mvmsta->sleep_tx_count) { 16428c2ecf20Sopenharmony_ci mvmsta->sleep_tx_count--; 16438c2ecf20Sopenharmony_ci if (mvmsta->sleep_tx_count && 16448c2ecf20Sopenharmony_ci !iwl_mvm_tid_queued(mvm, tid_data)) { 16458c2ecf20Sopenharmony_ci /* 16468c2ecf20Sopenharmony_ci * The number of frames in the queue 16478c2ecf20Sopenharmony_ci * dropped to 0 even if we sent less 16488c2ecf20Sopenharmony_ci * frames than we thought we had on the 16498c2ecf20Sopenharmony_ci * Tx queue. 16508c2ecf20Sopenharmony_ci * This means we had holes in the BA 16518c2ecf20Sopenharmony_ci * window that we just filled, ask 16528c2ecf20Sopenharmony_ci * mac80211 to send EOSP since the 16538c2ecf20Sopenharmony_ci * firmware won't know how to do that. 16548c2ecf20Sopenharmony_ci * Send NDP and the firmware will send 16558c2ecf20Sopenharmony_ci * EOSP notification that will trigger 16568c2ecf20Sopenharmony_ci * a call to ieee80211_sta_eosp(). 16578c2ecf20Sopenharmony_ci */ 16588c2ecf20Sopenharmony_ci send_eosp_ndp = true; 16598c2ecf20Sopenharmony_ci } 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 16638c2ecf20Sopenharmony_ci if (send_eosp_ndp) { 16648c2ecf20Sopenharmony_ci iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, 16658c2ecf20Sopenharmony_ci IEEE80211_FRAME_RELEASE_UAPSD, 16668c2ecf20Sopenharmony_ci 1, tid, false, false); 16678c2ecf20Sopenharmony_ci mvmsta->sleep_tx_count = 0; 16688c2ecf20Sopenharmony_ci ieee80211_send_eosp_nullfunc(sta, tid); 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci if (mvmsta->next_status_eosp) { 16738c2ecf20Sopenharmony_ci mvmsta->next_status_eosp = false; 16748c2ecf20Sopenharmony_ci ieee80211_sta_eosp(sta); 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ciout: 16788c2ecf20Sopenharmony_ci rcu_read_unlock(); 16798c2ecf20Sopenharmony_ci} 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 16828c2ecf20Sopenharmony_ci#define AGG_TX_STATE_(x) case AGG_TX_STATE_ ## x: return #x 16838c2ecf20Sopenharmony_cistatic const char *iwl_get_agg_tx_status(u16 status) 16848c2ecf20Sopenharmony_ci{ 16858c2ecf20Sopenharmony_ci switch (status & AGG_TX_STATE_STATUS_MSK) { 16868c2ecf20Sopenharmony_ci AGG_TX_STATE_(TRANSMITTED); 16878c2ecf20Sopenharmony_ci AGG_TX_STATE_(UNDERRUN); 16888c2ecf20Sopenharmony_ci AGG_TX_STATE_(BT_PRIO); 16898c2ecf20Sopenharmony_ci AGG_TX_STATE_(FEW_BYTES); 16908c2ecf20Sopenharmony_ci AGG_TX_STATE_(ABORT); 16918c2ecf20Sopenharmony_ci AGG_TX_STATE_(TX_ON_AIR_DROP); 16928c2ecf20Sopenharmony_ci AGG_TX_STATE_(LAST_SENT_TRY_CNT); 16938c2ecf20Sopenharmony_ci AGG_TX_STATE_(LAST_SENT_BT_KILL); 16948c2ecf20Sopenharmony_ci AGG_TX_STATE_(SCD_QUERY); 16958c2ecf20Sopenharmony_ci AGG_TX_STATE_(TEST_BAD_CRC32); 16968c2ecf20Sopenharmony_ci AGG_TX_STATE_(RESPONSE); 16978c2ecf20Sopenharmony_ci AGG_TX_STATE_(DUMP_TX); 16988c2ecf20Sopenharmony_ci AGG_TX_STATE_(DELAY_TX); 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci return "UNKNOWN"; 17028c2ecf20Sopenharmony_ci} 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_cistatic void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, 17058c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt) 17068c2ecf20Sopenharmony_ci{ 17078c2ecf20Sopenharmony_ci struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; 17088c2ecf20Sopenharmony_ci struct agg_tx_status *frame_status = 17098c2ecf20Sopenharmony_ci iwl_mvm_get_agg_status(mvm, tx_resp); 17108c2ecf20Sopenharmony_ci int i; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci for (i = 0; i < tx_resp->frame_count; i++) { 17138c2ecf20Sopenharmony_ci u16 fstatus = le16_to_cpu(frame_status[i].status); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, 17168c2ecf20Sopenharmony_ci "status %s (0x%04x), try-count (%d) seq (0x%x)\n", 17178c2ecf20Sopenharmony_ci iwl_get_agg_tx_status(fstatus), 17188c2ecf20Sopenharmony_ci fstatus & AGG_TX_STATE_STATUS_MSK, 17198c2ecf20Sopenharmony_ci (fstatus & AGG_TX_STATE_TRY_CNT_MSK) >> 17208c2ecf20Sopenharmony_ci AGG_TX_STATE_TRY_CNT_POS, 17218c2ecf20Sopenharmony_ci le16_to_cpu(frame_status[i].sequence)); 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci} 17248c2ecf20Sopenharmony_ci#else 17258c2ecf20Sopenharmony_cistatic void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, 17268c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt) 17278c2ecf20Sopenharmony_ci{} 17288c2ecf20Sopenharmony_ci#endif /* CONFIG_IWLWIFI_DEBUG */ 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_cistatic void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, 17318c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt) 17328c2ecf20Sopenharmony_ci{ 17338c2ecf20Sopenharmony_ci struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; 17348c2ecf20Sopenharmony_ci int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); 17358c2ecf20Sopenharmony_ci int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); 17368c2ecf20Sopenharmony_ci u16 sequence = le16_to_cpu(pkt->hdr.sequence); 17378c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 17388c2ecf20Sopenharmony_ci int queue = SEQ_TO_QUEUE(sequence); 17398c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(queue < IWL_MVM_DQA_MIN_DATA_QUEUE && 17428c2ecf20Sopenharmony_ci (queue != IWL_MVM_DQA_BSS_CLIENT_QUEUE))) 17438c2ecf20Sopenharmony_ci return; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt); 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci rcu_read_lock(); 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, sta_id); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); 17528c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!sta || !sta->wme)) { 17538c2ecf20Sopenharmony_ci rcu_read_unlock(); 17548c2ecf20Sopenharmony_ci return; 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci if (!WARN_ON_ONCE(!mvmsta)) { 17588c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].rate_n_flags = 17598c2ecf20Sopenharmony_ci le32_to_cpu(tx_resp->initial_rate); 17608c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].tx_time = 17618c2ecf20Sopenharmony_ci le16_to_cpu(tx_resp->wireless_media_time); 17628c2ecf20Sopenharmony_ci mvmsta->tid_data[tid].lq_color = 17638c2ecf20Sopenharmony_ci TX_RES_RATE_TABLE_COL_GET(tx_resp->tlc_info); 17648c2ecf20Sopenharmony_ci iwl_mvm_tx_airtime(mvm, mvmsta, 17658c2ecf20Sopenharmony_ci le16_to_cpu(tx_resp->wireless_media_time)); 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci rcu_read_unlock(); 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_civoid iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 17728c2ecf20Sopenharmony_ci{ 17738c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 17748c2ecf20Sopenharmony_ci struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci if (tx_resp->frame_count == 1) 17778c2ecf20Sopenharmony_ci iwl_mvm_rx_tx_cmd_single(mvm, pkt); 17788c2ecf20Sopenharmony_ci else 17798c2ecf20Sopenharmony_ci iwl_mvm_rx_tx_cmd_agg(mvm, pkt); 17808c2ecf20Sopenharmony_ci} 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_cistatic void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, 17838c2ecf20Sopenharmony_ci int txq, int index, 17848c2ecf20Sopenharmony_ci struct ieee80211_tx_info *ba_info, u32 rate) 17858c2ecf20Sopenharmony_ci{ 17868c2ecf20Sopenharmony_ci struct sk_buff_head reclaimed_skbs; 17878c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data = NULL; 17888c2ecf20Sopenharmony_ci struct ieee80211_sta *sta; 17898c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta = NULL; 17908c2ecf20Sopenharmony_ci struct sk_buff *skb; 17918c2ecf20Sopenharmony_ci int freed; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci if (WARN_ONCE(sta_id >= mvm->fw->ucode_capa.num_stations || 17948c2ecf20Sopenharmony_ci tid > IWL_MAX_TID_COUNT, 17958c2ecf20Sopenharmony_ci "sta_id %d tid %d", sta_id, tid)) 17968c2ecf20Sopenharmony_ci return; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci rcu_read_lock(); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci /* Reclaiming frames for a station that has been deleted ? */ 18038c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!sta)) { 18048c2ecf20Sopenharmony_ci rcu_read_unlock(); 18058c2ecf20Sopenharmony_ci return; 18068c2ecf20Sopenharmony_ci } 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci __skb_queue_head_init(&reclaimed_skbs); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci /* 18118c2ecf20Sopenharmony_ci * Release all TFDs before the SSN, i.e. all TFDs in front of 18128c2ecf20Sopenharmony_ci * block-ack window (we assume that they've been successfully 18138c2ecf20Sopenharmony_ci * transmitted ... if not, it's too late anyway). 18148c2ecf20Sopenharmony_ci */ 18158c2ecf20Sopenharmony_ci iwl_trans_reclaim(mvm->trans, txq, index, &reclaimed_skbs); 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci skb_queue_walk(&reclaimed_skbs, skb) { 18188c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci memset(&info->status, 0, sizeof(info->status)); 18238c2ecf20Sopenharmony_ci /* Packet was transmitted successfully, failures come as single 18248c2ecf20Sopenharmony_ci * frames because before failing a frame the firmware transmits 18258c2ecf20Sopenharmony_ci * it without aggregation at least once. 18268c2ecf20Sopenharmony_ci */ 18278c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci /* 18318c2ecf20Sopenharmony_ci * It's possible to get a BA response after invalidating the rcu (rcu is 18328c2ecf20Sopenharmony_ci * invalidated in order to prevent new Tx from being sent, but there may 18338c2ecf20Sopenharmony_ci * be some frames already in-flight). 18348c2ecf20Sopenharmony_ci * In this case we just want to reclaim, and could skip all the 18358c2ecf20Sopenharmony_ci * sta-dependent stuff since it's in the middle of being removed 18368c2ecf20Sopenharmony_ci * anyways. 18378c2ecf20Sopenharmony_ci */ 18388c2ecf20Sopenharmony_ci if (IS_ERR(sta)) 18398c2ecf20Sopenharmony_ci goto out; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_mac80211(sta); 18428c2ecf20Sopenharmony_ci tid_data = &mvmsta->tid_data[tid]; 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci if (tid_data->txq_id != txq) { 18458c2ecf20Sopenharmony_ci IWL_ERR(mvm, 18468c2ecf20Sopenharmony_ci "invalid BA notification: Q %d, tid %d\n", 18478c2ecf20Sopenharmony_ci tid_data->txq_id, tid); 18488c2ecf20Sopenharmony_ci rcu_read_unlock(); 18498c2ecf20Sopenharmony_ci return; 18508c2ecf20Sopenharmony_ci } 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci spin_lock_bh(&mvmsta->lock); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci tid_data->next_reclaimed = index; 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci iwl_mvm_check_ratid_empty(mvm, sta, tid); 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci freed = 0; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci /* pack lq color from tid_data along the reduced txp */ 18618c2ecf20Sopenharmony_ci ba_info->status.status_driver_data[0] = 18628c2ecf20Sopenharmony_ci RS_DRV_DATA_PACK(tid_data->lq_color, 18638c2ecf20Sopenharmony_ci ba_info->status.status_driver_data[0]); 18648c2ecf20Sopenharmony_ci ba_info->status.status_driver_data[1] = (void *)(uintptr_t)rate; 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci skb_queue_walk(&reclaimed_skbs, skb) { 18678c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (void *)skb->data; 18688c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci if (ieee80211_is_data_qos(hdr->frame_control)) 18718c2ecf20Sopenharmony_ci freed++; 18728c2ecf20Sopenharmony_ci else 18738c2ecf20Sopenharmony_ci WARN_ON_ONCE(tid != IWL_MAX_TID_COUNT); 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci /* this is the first skb we deliver in this batch */ 18768c2ecf20Sopenharmony_ci /* put the rate scaling data there */ 18778c2ecf20Sopenharmony_ci if (freed == 1) { 18788c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_AMPDU; 18798c2ecf20Sopenharmony_ci memcpy(&info->status, &ba_info->status, 18808c2ecf20Sopenharmony_ci sizeof(ba_info->status)); 18818c2ecf20Sopenharmony_ci iwl_mvm_hwrate_to_tx_status(rate, info); 18828c2ecf20Sopenharmony_ci } 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci spin_unlock_bh(&mvmsta->lock); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci /* We got a BA notif with 0 acked or scd_ssn didn't progress which is 18888c2ecf20Sopenharmony_ci * possible (i.e. first MPDU in the aggregation wasn't acked) 18898c2ecf20Sopenharmony_ci * Still it's important to update RS about sent vs. acked. 18908c2ecf20Sopenharmony_ci */ 18918c2ecf20Sopenharmony_ci if (skb_queue_empty(&reclaimed_skbs)) { 18928c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf = NULL; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci if (mvmsta->vif) 18958c2ecf20Sopenharmony_ci chanctx_conf = 18968c2ecf20Sopenharmony_ci rcu_dereference(mvmsta->vif->chanctx_conf); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!chanctx_conf)) 18998c2ecf20Sopenharmony_ci goto out; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci ba_info->band = chanctx_conf->def.chan->band; 19028c2ecf20Sopenharmony_ci iwl_mvm_hwrate_to_tx_status(rate, ba_info); 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci if (!iwl_mvm_has_tlc_offload(mvm)) { 19058c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, 19068c2ecf20Sopenharmony_ci "No reclaim. Update rs directly\n"); 19078c2ecf20Sopenharmony_ci iwl_mvm_rs_tx_status(mvm, sta, tid, ba_info, false); 19088c2ecf20Sopenharmony_ci } 19098c2ecf20Sopenharmony_ci } 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ciout: 19128c2ecf20Sopenharmony_ci rcu_read_unlock(); 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci while (!skb_queue_empty(&reclaimed_skbs)) { 19158c2ecf20Sopenharmony_ci skb = __skb_dequeue(&reclaimed_skbs); 19168c2ecf20Sopenharmony_ci ieee80211_tx_status(mvm->hw, skb); 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci} 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_civoid iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) 19218c2ecf20Sopenharmony_ci{ 19228c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 19238c2ecf20Sopenharmony_ci int sta_id, tid, txq, index; 19248c2ecf20Sopenharmony_ci struct ieee80211_tx_info ba_info = {}; 19258c2ecf20Sopenharmony_ci struct iwl_mvm_ba_notif *ba_notif; 19268c2ecf20Sopenharmony_ci struct iwl_mvm_tid_data *tid_data; 19278c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvmsta; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci ba_info.flags = IEEE80211_TX_STAT_AMPDU; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) { 19328c2ecf20Sopenharmony_ci struct iwl_mvm_compressed_ba_notif *ba_res = 19338c2ecf20Sopenharmony_ci (void *)pkt->data; 19348c2ecf20Sopenharmony_ci u8 lq_color = TX_RES_RATE_TABLE_COL_GET(ba_res->tlc_rate_info); 19358c2ecf20Sopenharmony_ci int i; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci sta_id = ba_res->sta_id; 19388c2ecf20Sopenharmony_ci ba_info.status.ampdu_ack_len = (u8)le16_to_cpu(ba_res->done); 19398c2ecf20Sopenharmony_ci ba_info.status.ampdu_len = (u8)le16_to_cpu(ba_res->txed); 19408c2ecf20Sopenharmony_ci ba_info.status.tx_time = 19418c2ecf20Sopenharmony_ci (u16)le32_to_cpu(ba_res->wireless_time); 19428c2ecf20Sopenharmony_ci ba_info.status.status_driver_data[0] = 19438c2ecf20Sopenharmony_ci (void *)(uintptr_t)ba_res->reduced_txp; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci if (!le16_to_cpu(ba_res->tfd_cnt)) 19468c2ecf20Sopenharmony_ci goto out; 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci rcu_read_lock(); 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, sta_id); 19518c2ecf20Sopenharmony_ci /* 19528c2ecf20Sopenharmony_ci * It's possible to get a BA response after invalidating the rcu 19538c2ecf20Sopenharmony_ci * (rcu is invalidated in order to prevent new Tx from being 19548c2ecf20Sopenharmony_ci * sent, but there may be some frames already in-flight). 19558c2ecf20Sopenharmony_ci * In this case we just want to reclaim, and could skip all the 19568c2ecf20Sopenharmony_ci * sta-dependent stuff since it's in the middle of being removed 19578c2ecf20Sopenharmony_ci * anyways. 19588c2ecf20Sopenharmony_ci */ 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci /* Free per TID */ 19618c2ecf20Sopenharmony_ci for (i = 0; i < le16_to_cpu(ba_res->tfd_cnt); i++) { 19628c2ecf20Sopenharmony_ci struct iwl_mvm_compressed_ba_tfd *ba_tfd = 19638c2ecf20Sopenharmony_ci &ba_res->tfd[i]; 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci tid = ba_tfd->tid; 19668c2ecf20Sopenharmony_ci if (tid == IWL_MGMT_TID) 19678c2ecf20Sopenharmony_ci tid = IWL_MAX_TID_COUNT; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci if (mvmsta) 19708c2ecf20Sopenharmony_ci mvmsta->tid_data[i].lq_color = lq_color; 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci iwl_mvm_tx_reclaim(mvm, sta_id, tid, 19738c2ecf20Sopenharmony_ci (int)(le16_to_cpu(ba_tfd->q_num)), 19748c2ecf20Sopenharmony_ci le16_to_cpu(ba_tfd->tfd_index), 19758c2ecf20Sopenharmony_ci &ba_info, 19768c2ecf20Sopenharmony_ci le32_to_cpu(ba_res->tx_rate)); 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci if (mvmsta) 19808c2ecf20Sopenharmony_ci iwl_mvm_tx_airtime(mvm, mvmsta, 19818c2ecf20Sopenharmony_ci le32_to_cpu(ba_res->wireless_time)); 19828c2ecf20Sopenharmony_ci rcu_read_unlock(); 19838c2ecf20Sopenharmony_ciout: 19848c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, 19858c2ecf20Sopenharmony_ci "BA_NOTIFICATION Received from sta_id = %d, flags %x, sent:%d, acked:%d\n", 19868c2ecf20Sopenharmony_ci sta_id, le32_to_cpu(ba_res->flags), 19878c2ecf20Sopenharmony_ci le16_to_cpu(ba_res->txed), 19888c2ecf20Sopenharmony_ci le16_to_cpu(ba_res->done)); 19898c2ecf20Sopenharmony_ci return; 19908c2ecf20Sopenharmony_ci } 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci ba_notif = (void *)pkt->data; 19938c2ecf20Sopenharmony_ci sta_id = ba_notif->sta_id; 19948c2ecf20Sopenharmony_ci tid = ba_notif->tid; 19958c2ecf20Sopenharmony_ci /* "flow" corresponds to Tx queue */ 19968c2ecf20Sopenharmony_ci txq = le16_to_cpu(ba_notif->scd_flow); 19978c2ecf20Sopenharmony_ci /* "ssn" is start of block-ack Tx window, corresponds to index 19988c2ecf20Sopenharmony_ci * (in Tx queue's circular buffer) of first TFD/frame in window */ 19998c2ecf20Sopenharmony_ci index = le16_to_cpu(ba_notif->scd_ssn); 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci rcu_read_lock(); 20028c2ecf20Sopenharmony_ci mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, sta_id); 20038c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!mvmsta)) { 20048c2ecf20Sopenharmony_ci rcu_read_unlock(); 20058c2ecf20Sopenharmony_ci return; 20068c2ecf20Sopenharmony_ci } 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci tid_data = &mvmsta->tid_data[tid]; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci ba_info.status.ampdu_ack_len = ba_notif->txed_2_done; 20118c2ecf20Sopenharmony_ci ba_info.status.ampdu_len = ba_notif->txed; 20128c2ecf20Sopenharmony_ci ba_info.status.tx_time = tid_data->tx_time; 20138c2ecf20Sopenharmony_ci ba_info.status.status_driver_data[0] = 20148c2ecf20Sopenharmony_ci (void *)(uintptr_t)ba_notif->reduced_txp; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci rcu_read_unlock(); 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci iwl_mvm_tx_reclaim(mvm, sta_id, tid, txq, index, &ba_info, 20198c2ecf20Sopenharmony_ci tid_data->rate_n_flags); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, 20228c2ecf20Sopenharmony_ci "BA_NOTIFICATION Received from %pM, sta_id = %d\n", 20238c2ecf20Sopenharmony_ci ba_notif->sta_addr, ba_notif->sta_id); 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, 20268c2ecf20Sopenharmony_ci "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", 20278c2ecf20Sopenharmony_ci ba_notif->tid, le16_to_cpu(ba_notif->seq_ctl), 20288c2ecf20Sopenharmony_ci le64_to_cpu(ba_notif->bitmap), txq, index, 20298c2ecf20Sopenharmony_ci ba_notif->txed, ba_notif->txed_2_done); 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci IWL_DEBUG_TX_REPLY(mvm, "reduced txp from ba notif %d\n", 20328c2ecf20Sopenharmony_ci ba_notif->reduced_txp); 20338c2ecf20Sopenharmony_ci} 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci/* 20368c2ecf20Sopenharmony_ci * Note that there are transports that buffer frames before they reach 20378c2ecf20Sopenharmony_ci * the firmware. This means that after flush_tx_path is called, the 20388c2ecf20Sopenharmony_ci * queue might not be empty. The race-free way to handle this is to: 20398c2ecf20Sopenharmony_ci * 1) set the station as draining 20408c2ecf20Sopenharmony_ci * 2) flush the Tx path 20418c2ecf20Sopenharmony_ci * 3) wait for the transport queues to be empty 20428c2ecf20Sopenharmony_ci */ 20438c2ecf20Sopenharmony_ciint iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags) 20448c2ecf20Sopenharmony_ci{ 20458c2ecf20Sopenharmony_ci int ret; 20468c2ecf20Sopenharmony_ci struct iwl_tx_path_flush_cmd_v1 flush_cmd = { 20478c2ecf20Sopenharmony_ci .queues_ctl = cpu_to_le32(tfd_msk), 20488c2ecf20Sopenharmony_ci .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH), 20498c2ecf20Sopenharmony_ci }; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci WARN_ON(iwl_mvm_has_new_tx_api(mvm)); 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, 20548c2ecf20Sopenharmony_ci sizeof(flush_cmd), &flush_cmd); 20558c2ecf20Sopenharmony_ci if (ret) 20568c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret); 20578c2ecf20Sopenharmony_ci return ret; 20588c2ecf20Sopenharmony_ci} 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ciint iwl_mvm_flush_sta_tids(struct iwl_mvm *mvm, u32 sta_id, 20618c2ecf20Sopenharmony_ci u16 tids, u32 flags) 20628c2ecf20Sopenharmony_ci{ 20638c2ecf20Sopenharmony_ci int ret; 20648c2ecf20Sopenharmony_ci struct iwl_tx_path_flush_cmd flush_cmd = { 20658c2ecf20Sopenharmony_ci .sta_id = cpu_to_le32(sta_id), 20668c2ecf20Sopenharmony_ci .tid_mask = cpu_to_le16(tids), 20678c2ecf20Sopenharmony_ci }; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci WARN_ON(!iwl_mvm_has_new_tx_api(mvm)); 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, 20728c2ecf20Sopenharmony_ci sizeof(flush_cmd), &flush_cmd); 20738c2ecf20Sopenharmony_ci if (ret) 20748c2ecf20Sopenharmony_ci IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret); 20758c2ecf20Sopenharmony_ci return ret; 20768c2ecf20Sopenharmony_ci} 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ciint iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool internal) 20798c2ecf20Sopenharmony_ci{ 20808c2ecf20Sopenharmony_ci struct iwl_mvm_int_sta *int_sta = sta; 20818c2ecf20Sopenharmony_ci struct iwl_mvm_sta *mvm_sta = sta; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct iwl_mvm_int_sta, sta_id) != 20848c2ecf20Sopenharmony_ci offsetof(struct iwl_mvm_sta, sta_id)); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci if (iwl_mvm_has_new_tx_api(mvm)) 20878c2ecf20Sopenharmony_ci return iwl_mvm_flush_sta_tids(mvm, mvm_sta->sta_id, 0xffff, 0); 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci if (internal) 20908c2ecf20Sopenharmony_ci return iwl_mvm_flush_tx_path(mvm, int_sta->tfd_queue_msk, 0); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci return iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, 0); 20938c2ecf20Sopenharmony_ci} 2094