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