162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2005-2011 Atheros Communications Inc.
462306a36Sopenharmony_ci * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
562306a36Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "core.h"
962306a36Sopenharmony_ci#include "htc.h"
1062306a36Sopenharmony_ci#include "htt.h"
1162306a36Sopenharmony_ci#include "txrx.h"
1262306a36Sopenharmony_ci#include "debug.h"
1362306a36Sopenharmony_ci#include "trace.h"
1462306a36Sopenharmony_ci#include "mac.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/log2.h>
1762306a36Sopenharmony_ci#include <linux/bitfield.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* when under memory pressure rx ring refill may fail and needs a retry */
2062306a36Sopenharmony_ci#define HTT_RX_RING_REFILL_RETRY_MS 50
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define HTT_RX_RING_REFILL_RESCHED_MS 5
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* shortcut to interpret a raw memory buffer as a rx descriptor */
2562306a36Sopenharmony_ci#define HTT_RX_BUF_TO_RX_DESC(hw, buf) ath10k_htt_rx_desc_from_raw_buffer(hw, buf)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int ath10k_htt_rx_get_csum_state(struct ath10k_hw_params *hw, struct sk_buff *skb);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic struct sk_buff *
3062306a36Sopenharmony_ciath10k_htt_rx_find_skb_paddr(struct ath10k *ar, u64 paddr)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct ath10k_skb_rxcb *rxcb;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	hash_for_each_possible(ar->htt.rx_ring.skb_table, rxcb, hlist, paddr)
3562306a36Sopenharmony_ci		if (rxcb->paddr == paddr)
3662306a36Sopenharmony_ci			return ATH10K_RXCB_SKB(rxcb);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	WARN_ON_ONCE(1);
3962306a36Sopenharmony_ci	return NULL;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct sk_buff *skb;
4562306a36Sopenharmony_ci	struct ath10k_skb_rxcb *rxcb;
4662306a36Sopenharmony_ci	struct hlist_node *n;
4762306a36Sopenharmony_ci	int i;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (htt->rx_ring.in_ord_rx) {
5062306a36Sopenharmony_ci		hash_for_each_safe(htt->rx_ring.skb_table, i, n, rxcb, hlist) {
5162306a36Sopenharmony_ci			skb = ATH10K_RXCB_SKB(rxcb);
5262306a36Sopenharmony_ci			dma_unmap_single(htt->ar->dev, rxcb->paddr,
5362306a36Sopenharmony_ci					 skb->len + skb_tailroom(skb),
5462306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
5562306a36Sopenharmony_ci			hash_del(&rxcb->hlist);
5662306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
5762306a36Sopenharmony_ci		}
5862306a36Sopenharmony_ci	} else {
5962306a36Sopenharmony_ci		for (i = 0; i < htt->rx_ring.size; i++) {
6062306a36Sopenharmony_ci			skb = htt->rx_ring.netbufs_ring[i];
6162306a36Sopenharmony_ci			if (!skb)
6262306a36Sopenharmony_ci				continue;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci			rxcb = ATH10K_SKB_RXCB(skb);
6562306a36Sopenharmony_ci			dma_unmap_single(htt->ar->dev, rxcb->paddr,
6662306a36Sopenharmony_ci					 skb->len + skb_tailroom(skb),
6762306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
6862306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	htt->rx_ring.fill_cnt = 0;
7362306a36Sopenharmony_ci	hash_init(htt->rx_ring.skb_table);
7462306a36Sopenharmony_ci	memset(htt->rx_ring.netbufs_ring, 0,
7562306a36Sopenharmony_ci	       htt->rx_ring.size * sizeof(htt->rx_ring.netbufs_ring[0]));
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic size_t ath10k_htt_get_rx_ring_size_32(struct ath10k_htt *htt)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	return htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring_32);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic size_t ath10k_htt_get_rx_ring_size_64(struct ath10k_htt *htt)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	return htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring_64);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void ath10k_htt_config_paddrs_ring_32(struct ath10k_htt *htt,
8962306a36Sopenharmony_ci					     void *vaddr)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	htt->rx_ring.paddrs_ring_32 = vaddr;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void ath10k_htt_config_paddrs_ring_64(struct ath10k_htt *htt,
9562306a36Sopenharmony_ci					     void *vaddr)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	htt->rx_ring.paddrs_ring_64 = vaddr;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void ath10k_htt_set_paddrs_ring_32(struct ath10k_htt *htt,
10162306a36Sopenharmony_ci					  dma_addr_t paddr, int idx)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	htt->rx_ring.paddrs_ring_32[idx] = __cpu_to_le32(paddr);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void ath10k_htt_set_paddrs_ring_64(struct ath10k_htt *htt,
10762306a36Sopenharmony_ci					  dma_addr_t paddr, int idx)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	htt->rx_ring.paddrs_ring_64[idx] = __cpu_to_le64(paddr);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void ath10k_htt_reset_paddrs_ring_32(struct ath10k_htt *htt, int idx)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	htt->rx_ring.paddrs_ring_32[idx] = 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void ath10k_htt_reset_paddrs_ring_64(struct ath10k_htt *htt, int idx)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	htt->rx_ring.paddrs_ring_64[idx] = 0;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void *ath10k_htt_get_vaddr_ring_32(struct ath10k_htt *htt)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	return (void *)htt->rx_ring.paddrs_ring_32;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void *ath10k_htt_get_vaddr_ring_64(struct ath10k_htt *htt)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	return (void *)htt->rx_ring.paddrs_ring_64;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &htt->ar->hw_params;
13562306a36Sopenharmony_ci	struct htt_rx_desc *rx_desc;
13662306a36Sopenharmony_ci	struct ath10k_skb_rxcb *rxcb;
13762306a36Sopenharmony_ci	struct sk_buff *skb;
13862306a36Sopenharmony_ci	dma_addr_t paddr;
13962306a36Sopenharmony_ci	int ret = 0, idx;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* The Full Rx Reorder firmware has no way of telling the host
14262306a36Sopenharmony_ci	 * implicitly when it copied HTT Rx Ring buffers to MAC Rx Ring.
14362306a36Sopenharmony_ci	 * To keep things simple make sure ring is always half empty. This
14462306a36Sopenharmony_ci	 * guarantees there'll be no replenishment overruns possible.
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	BUILD_BUG_ON(HTT_RX_RING_FILL_LEVEL >= HTT_RX_RING_SIZE / 2);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (idx < 0 || idx >= htt->rx_ring.size) {
15162306a36Sopenharmony_ci		ath10k_err(htt->ar, "rx ring index is not valid, firmware malfunctioning?\n");
15262306a36Sopenharmony_ci		idx &= htt->rx_ring.size_mask;
15362306a36Sopenharmony_ci		ret = -ENOMEM;
15462306a36Sopenharmony_ci		goto fail;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	while (num > 0) {
15862306a36Sopenharmony_ci		skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN);
15962306a36Sopenharmony_ci		if (!skb) {
16062306a36Sopenharmony_ci			ret = -ENOMEM;
16162306a36Sopenharmony_ci			goto fail;
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		if (!IS_ALIGNED((unsigned long)skb->data, HTT_RX_DESC_ALIGN))
16562306a36Sopenharmony_ci			skb_pull(skb,
16662306a36Sopenharmony_ci				 PTR_ALIGN(skb->data, HTT_RX_DESC_ALIGN) -
16762306a36Sopenharmony_ci				 skb->data);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci		/* Clear rx_desc attention word before posting to Rx ring */
17062306a36Sopenharmony_ci		rx_desc = HTT_RX_BUF_TO_RX_DESC(hw, skb->data);
17162306a36Sopenharmony_ci		ath10k_htt_rx_desc_get_attention(hw, rx_desc)->flags = __cpu_to_le32(0);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		paddr = dma_map_single(htt->ar->dev, skb->data,
17462306a36Sopenharmony_ci				       skb->len + skb_tailroom(skb),
17562306a36Sopenharmony_ci				       DMA_FROM_DEVICE);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(htt->ar->dev, paddr))) {
17862306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
17962306a36Sopenharmony_ci			ret = -ENOMEM;
18062306a36Sopenharmony_ci			goto fail;
18162306a36Sopenharmony_ci		}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		rxcb = ATH10K_SKB_RXCB(skb);
18462306a36Sopenharmony_ci		rxcb->paddr = paddr;
18562306a36Sopenharmony_ci		htt->rx_ring.netbufs_ring[idx] = skb;
18662306a36Sopenharmony_ci		ath10k_htt_set_paddrs_ring(htt, paddr, idx);
18762306a36Sopenharmony_ci		htt->rx_ring.fill_cnt++;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		if (htt->rx_ring.in_ord_rx) {
19062306a36Sopenharmony_ci			hash_add(htt->rx_ring.skb_table,
19162306a36Sopenharmony_ci				 &ATH10K_SKB_RXCB(skb)->hlist,
19262306a36Sopenharmony_ci				 paddr);
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		num--;
19662306a36Sopenharmony_ci		idx++;
19762306a36Sopenharmony_ci		idx &= htt->rx_ring.size_mask;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cifail:
20162306a36Sopenharmony_ci	/*
20262306a36Sopenharmony_ci	 * Make sure the rx buffer is updated before available buffer
20362306a36Sopenharmony_ci	 * index to avoid any potential rx ring corruption.
20462306a36Sopenharmony_ci	 */
20562306a36Sopenharmony_ci	mb();
20662306a36Sopenharmony_ci	*htt->rx_ring.alloc_idx.vaddr = __cpu_to_le32(idx);
20762306a36Sopenharmony_ci	return ret;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	lockdep_assert_held(&htt->rx_ring.lock);
21362306a36Sopenharmony_ci	return __ath10k_htt_rx_ring_fill_n(htt, num);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	int ret, num_deficit, num_to_fill;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Refilling the whole RX ring buffer proves to be a bad idea. The
22162306a36Sopenharmony_ci	 * reason is RX may take up significant amount of CPU cycles and starve
22262306a36Sopenharmony_ci	 * other tasks, e.g. TX on an ethernet device while acting as a bridge
22362306a36Sopenharmony_ci	 * with ath10k wlan interface. This ended up with very poor performance
22462306a36Sopenharmony_ci	 * once CPU the host system was overwhelmed with RX on ath10k.
22562306a36Sopenharmony_ci	 *
22662306a36Sopenharmony_ci	 * By limiting the number of refills the replenishing occurs
22762306a36Sopenharmony_ci	 * progressively. This in turns makes use of the fact tasklets are
22862306a36Sopenharmony_ci	 * processed in FIFO order. This means actual RX processing can starve
22962306a36Sopenharmony_ci	 * out refilling. If there's not enough buffers on RX ring FW will not
23062306a36Sopenharmony_ci	 * report RX until it is refilled with enough buffers. This
23162306a36Sopenharmony_ci	 * automatically balances load wrt to CPU power.
23262306a36Sopenharmony_ci	 *
23362306a36Sopenharmony_ci	 * This probably comes at a cost of lower maximum throughput but
23462306a36Sopenharmony_ci	 * improves the average and stability.
23562306a36Sopenharmony_ci	 */
23662306a36Sopenharmony_ci	spin_lock_bh(&htt->rx_ring.lock);
23762306a36Sopenharmony_ci	num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt;
23862306a36Sopenharmony_ci	num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit);
23962306a36Sopenharmony_ci	num_deficit -= num_to_fill;
24062306a36Sopenharmony_ci	ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill);
24162306a36Sopenharmony_ci	if (ret == -ENOMEM) {
24262306a36Sopenharmony_ci		/*
24362306a36Sopenharmony_ci		 * Failed to fill it to the desired level -
24462306a36Sopenharmony_ci		 * we'll start a timer and try again next time.
24562306a36Sopenharmony_ci		 * As long as enough buffers are left in the ring for
24662306a36Sopenharmony_ci		 * another A-MPDU rx, no special recovery is needed.
24762306a36Sopenharmony_ci		 */
24862306a36Sopenharmony_ci		mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
24962306a36Sopenharmony_ci			  msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
25062306a36Sopenharmony_ci	} else if (num_deficit > 0) {
25162306a36Sopenharmony_ci		mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
25262306a36Sopenharmony_ci			  msecs_to_jiffies(HTT_RX_RING_REFILL_RESCHED_MS));
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci	spin_unlock_bh(&htt->rx_ring.lock);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void ath10k_htt_rx_ring_refill_retry(struct timer_list *t)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct ath10k_htt *htt = from_timer(htt, t, rx_ring.refill_retry_timer);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	ath10k_htt_rx_msdu_buff_replenish(htt);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ciint ath10k_htt_rx_ring_refill(struct ath10k *ar)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
26762306a36Sopenharmony_ci	int ret;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
27062306a36Sopenharmony_ci		return 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	spin_lock_bh(&htt->rx_ring.lock);
27362306a36Sopenharmony_ci	ret = ath10k_htt_rx_ring_fill_n(htt, (htt->rx_ring.fill_level -
27462306a36Sopenharmony_ci					      htt->rx_ring.fill_cnt));
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (ret)
27762306a36Sopenharmony_ci		ath10k_htt_rx_ring_free(htt);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	spin_unlock_bh(&htt->rx_ring.lock);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	return ret;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_civoid ath10k_htt_rx_free(struct ath10k_htt *htt)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	if (htt->ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
28762306a36Sopenharmony_ci		return;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	del_timer_sync(&htt->rx_ring.refill_retry_timer);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	skb_queue_purge(&htt->rx_msdus_q);
29262306a36Sopenharmony_ci	skb_queue_purge(&htt->rx_in_ord_compl_q);
29362306a36Sopenharmony_ci	skb_queue_purge(&htt->tx_fetch_ind_q);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	spin_lock_bh(&htt->rx_ring.lock);
29662306a36Sopenharmony_ci	ath10k_htt_rx_ring_free(htt);
29762306a36Sopenharmony_ci	spin_unlock_bh(&htt->rx_ring.lock);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	dma_free_coherent(htt->ar->dev,
30062306a36Sopenharmony_ci			  ath10k_htt_get_rx_ring_size(htt),
30162306a36Sopenharmony_ci			  ath10k_htt_get_vaddr_ring(htt),
30262306a36Sopenharmony_ci			  htt->rx_ring.base_paddr);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	ath10k_htt_config_paddrs_ring(htt, NULL);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	dma_free_coherent(htt->ar->dev,
30762306a36Sopenharmony_ci			  sizeof(*htt->rx_ring.alloc_idx.vaddr),
30862306a36Sopenharmony_ci			  htt->rx_ring.alloc_idx.vaddr,
30962306a36Sopenharmony_ci			  htt->rx_ring.alloc_idx.paddr);
31062306a36Sopenharmony_ci	htt->rx_ring.alloc_idx.vaddr = NULL;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	kfree(htt->rx_ring.netbufs_ring);
31362306a36Sopenharmony_ci	htt->rx_ring.netbufs_ring = NULL;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
31962306a36Sopenharmony_ci	int idx;
32062306a36Sopenharmony_ci	struct sk_buff *msdu;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	lockdep_assert_held(&htt->rx_ring.lock);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (htt->rx_ring.fill_cnt == 0) {
32562306a36Sopenharmony_ci		ath10k_warn(ar, "tried to pop sk_buff from an empty rx ring\n");
32662306a36Sopenharmony_ci		return NULL;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	idx = htt->rx_ring.sw_rd_idx.msdu_payld;
33062306a36Sopenharmony_ci	msdu = htt->rx_ring.netbufs_ring[idx];
33162306a36Sopenharmony_ci	htt->rx_ring.netbufs_ring[idx] = NULL;
33262306a36Sopenharmony_ci	ath10k_htt_reset_paddrs_ring(htt, idx);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	idx++;
33562306a36Sopenharmony_ci	idx &= htt->rx_ring.size_mask;
33662306a36Sopenharmony_ci	htt->rx_ring.sw_rd_idx.msdu_payld = idx;
33762306a36Sopenharmony_ci	htt->rx_ring.fill_cnt--;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	dma_unmap_single(htt->ar->dev,
34062306a36Sopenharmony_ci			 ATH10K_SKB_RXCB(msdu)->paddr,
34162306a36Sopenharmony_ci			 msdu->len + skb_tailroom(msdu),
34262306a36Sopenharmony_ci			 DMA_FROM_DEVICE);
34362306a36Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
34462306a36Sopenharmony_ci			msdu->data, msdu->len + skb_tailroom(msdu));
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	return msdu;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */
35062306a36Sopenharmony_cistatic int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
35162306a36Sopenharmony_ci				   struct sk_buff_head *amsdu)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
35462306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
35562306a36Sopenharmony_ci	int msdu_len, msdu_chaining = 0;
35662306a36Sopenharmony_ci	struct sk_buff *msdu;
35762306a36Sopenharmony_ci	struct htt_rx_desc *rx_desc;
35862306a36Sopenharmony_ci	struct rx_attention *rx_desc_attention;
35962306a36Sopenharmony_ci	struct rx_frag_info_common *rx_desc_frag_info_common;
36062306a36Sopenharmony_ci	struct rx_msdu_start_common *rx_desc_msdu_start_common;
36162306a36Sopenharmony_ci	struct rx_msdu_end_common *rx_desc_msdu_end_common;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	lockdep_assert_held(&htt->rx_ring.lock);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	for (;;) {
36662306a36Sopenharmony_ci		int last_msdu, msdu_len_invalid, msdu_chained;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		msdu = ath10k_htt_rx_netbuf_pop(htt);
36962306a36Sopenharmony_ci		if (!msdu) {
37062306a36Sopenharmony_ci			__skb_queue_purge(amsdu);
37162306a36Sopenharmony_ci			return -ENOENT;
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		__skb_queue_tail(amsdu, msdu);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		rx_desc = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data);
37762306a36Sopenharmony_ci		rx_desc_attention = ath10k_htt_rx_desc_get_attention(hw, rx_desc);
37862306a36Sopenharmony_ci		rx_desc_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw,
37962306a36Sopenharmony_ci									      rx_desc);
38062306a36Sopenharmony_ci		rx_desc_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rx_desc);
38162306a36Sopenharmony_ci		rx_desc_frag_info_common = ath10k_htt_rx_desc_get_frag_info(hw, rx_desc);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		/* FIXME: we must report msdu payload since this is what caller
38462306a36Sopenharmony_ci		 * expects now
38562306a36Sopenharmony_ci		 */
38662306a36Sopenharmony_ci		skb_put(msdu, hw->rx_desc_ops->rx_desc_msdu_payload_offset);
38762306a36Sopenharmony_ci		skb_pull(msdu, hw->rx_desc_ops->rx_desc_msdu_payload_offset);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		/*
39062306a36Sopenharmony_ci		 * Sanity check - confirm the HW is finished filling in the
39162306a36Sopenharmony_ci		 * rx data.
39262306a36Sopenharmony_ci		 * If the HW and SW are working correctly, then it's guaranteed
39362306a36Sopenharmony_ci		 * that the HW's MAC DMA is done before this point in the SW.
39462306a36Sopenharmony_ci		 * To prevent the case that we handle a stale Rx descriptor,
39562306a36Sopenharmony_ci		 * just assert for now until we have a way to recover.
39662306a36Sopenharmony_ci		 */
39762306a36Sopenharmony_ci		if (!(__le32_to_cpu(rx_desc_attention->flags)
39862306a36Sopenharmony_ci				& RX_ATTENTION_FLAGS_MSDU_DONE)) {
39962306a36Sopenharmony_ci			__skb_queue_purge(amsdu);
40062306a36Sopenharmony_ci			return -EIO;
40162306a36Sopenharmony_ci		}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		msdu_len_invalid = !!(__le32_to_cpu(rx_desc_attention->flags)
40462306a36Sopenharmony_ci					& (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR |
40562306a36Sopenharmony_ci					   RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR));
40662306a36Sopenharmony_ci		msdu_len = MS(__le32_to_cpu(rx_desc_msdu_start_common->info0),
40762306a36Sopenharmony_ci			      RX_MSDU_START_INFO0_MSDU_LENGTH);
40862306a36Sopenharmony_ci		msdu_chained = rx_desc_frag_info_common->ring2_more_count;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		if (msdu_len_invalid)
41162306a36Sopenharmony_ci			msdu_len = 0;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		skb_trim(msdu, 0);
41462306a36Sopenharmony_ci		skb_put(msdu, min(msdu_len, ath10k_htt_rx_msdu_size(hw)));
41562306a36Sopenharmony_ci		msdu_len -= msdu->len;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		/* Note: Chained buffers do not contain rx descriptor */
41862306a36Sopenharmony_ci		while (msdu_chained--) {
41962306a36Sopenharmony_ci			msdu = ath10k_htt_rx_netbuf_pop(htt);
42062306a36Sopenharmony_ci			if (!msdu) {
42162306a36Sopenharmony_ci				__skb_queue_purge(amsdu);
42262306a36Sopenharmony_ci				return -ENOENT;
42362306a36Sopenharmony_ci			}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci			__skb_queue_tail(amsdu, msdu);
42662306a36Sopenharmony_ci			skb_trim(msdu, 0);
42762306a36Sopenharmony_ci			skb_put(msdu, min(msdu_len, HTT_RX_BUF_SIZE));
42862306a36Sopenharmony_ci			msdu_len -= msdu->len;
42962306a36Sopenharmony_ci			msdu_chaining = 1;
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		last_msdu = __le32_to_cpu(rx_desc_msdu_end_common->info0) &
43362306a36Sopenharmony_ci				RX_MSDU_END_INFO0_LAST_MSDU;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		/* FIXME: why are we skipping the first part of the rx_desc? */
43662306a36Sopenharmony_ci		trace_ath10k_htt_rx_desc(ar, (void *)rx_desc + sizeof(u32),
43762306a36Sopenharmony_ci					 hw->rx_desc_ops->rx_desc_size - sizeof(u32));
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		if (last_msdu)
44062306a36Sopenharmony_ci			break;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (skb_queue_empty(amsdu))
44462306a36Sopenharmony_ci		msdu_chaining = -1;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/*
44762306a36Sopenharmony_ci	 * Don't refill the ring yet.
44862306a36Sopenharmony_ci	 *
44962306a36Sopenharmony_ci	 * First, the elements popped here are still in use - it is not
45062306a36Sopenharmony_ci	 * safe to overwrite them until the matching call to
45162306a36Sopenharmony_ci	 * mpdu_desc_list_next. Second, for efficiency it is preferable to
45262306a36Sopenharmony_ci	 * refill the rx ring with 1 PPDU's worth of rx buffers (something
45362306a36Sopenharmony_ci	 * like 32 x 3 buffers), rather than one MPDU's worth of rx buffers
45462306a36Sopenharmony_ci	 * (something like 3 buffers). Consequently, we'll rely on the txrx
45562306a36Sopenharmony_ci	 * SW to tell us when it is done pulling all the PPDU's rx buffers
45662306a36Sopenharmony_ci	 * out of the rx ring, and then refill it just once.
45762306a36Sopenharmony_ci	 */
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return msdu_chaining;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic struct sk_buff *ath10k_htt_rx_pop_paddr(struct ath10k_htt *htt,
46362306a36Sopenharmony_ci					       u64 paddr)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
46662306a36Sopenharmony_ci	struct ath10k_skb_rxcb *rxcb;
46762306a36Sopenharmony_ci	struct sk_buff *msdu;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	lockdep_assert_held(&htt->rx_ring.lock);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	msdu = ath10k_htt_rx_find_skb_paddr(ar, paddr);
47262306a36Sopenharmony_ci	if (!msdu)
47362306a36Sopenharmony_ci		return NULL;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	rxcb = ATH10K_SKB_RXCB(msdu);
47662306a36Sopenharmony_ci	hash_del(&rxcb->hlist);
47762306a36Sopenharmony_ci	htt->rx_ring.fill_cnt--;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	dma_unmap_single(htt->ar->dev, rxcb->paddr,
48062306a36Sopenharmony_ci			 msdu->len + skb_tailroom(msdu),
48162306a36Sopenharmony_ci			 DMA_FROM_DEVICE);
48262306a36Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
48362306a36Sopenharmony_ci			msdu->data, msdu->len + skb_tailroom(msdu));
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return msdu;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic inline void ath10k_htt_append_frag_list(struct sk_buff *skb_head,
48962306a36Sopenharmony_ci					       struct sk_buff *frag_list,
49062306a36Sopenharmony_ci					       unsigned int frag_len)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	skb_shinfo(skb_head)->frag_list = frag_list;
49362306a36Sopenharmony_ci	skb_head->data_len = frag_len;
49462306a36Sopenharmony_ci	skb_head->len += skb_head->data_len;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int ath10k_htt_rx_handle_amsdu_mon_32(struct ath10k_htt *htt,
49862306a36Sopenharmony_ci					     struct sk_buff *msdu,
49962306a36Sopenharmony_ci					     struct htt_rx_in_ord_msdu_desc **msdu_desc)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
50262306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
50362306a36Sopenharmony_ci	u32 paddr;
50462306a36Sopenharmony_ci	struct sk_buff *frag_buf;
50562306a36Sopenharmony_ci	struct sk_buff *prev_frag_buf;
50662306a36Sopenharmony_ci	u8 last_frag;
50762306a36Sopenharmony_ci	struct htt_rx_in_ord_msdu_desc *ind_desc = *msdu_desc;
50862306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
50962306a36Sopenharmony_ci	int amsdu_len = __le16_to_cpu(ind_desc->msdu_len);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data);
51262306a36Sopenharmony_ci	trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	skb_put(msdu, hw->rx_desc_ops->rx_desc_size);
51562306a36Sopenharmony_ci	skb_pull(msdu, hw->rx_desc_ops->rx_desc_size);
51662306a36Sopenharmony_ci	skb_put(msdu, min(amsdu_len, ath10k_htt_rx_msdu_size(hw)));
51762306a36Sopenharmony_ci	amsdu_len -= msdu->len;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	last_frag = ind_desc->reserved;
52062306a36Sopenharmony_ci	if (last_frag) {
52162306a36Sopenharmony_ci		if (amsdu_len) {
52262306a36Sopenharmony_ci			ath10k_warn(ar, "invalid amsdu len %u, left %d",
52362306a36Sopenharmony_ci				    __le16_to_cpu(ind_desc->msdu_len),
52462306a36Sopenharmony_ci				    amsdu_len);
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci		return 0;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	ind_desc++;
53062306a36Sopenharmony_ci	paddr = __le32_to_cpu(ind_desc->msdu_paddr);
53162306a36Sopenharmony_ci	frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
53262306a36Sopenharmony_ci	if (!frag_buf) {
53362306a36Sopenharmony_ci		ath10k_warn(ar, "failed to pop frag-1 paddr: 0x%x", paddr);
53462306a36Sopenharmony_ci		return -ENOENT;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
53862306a36Sopenharmony_ci	ath10k_htt_append_frag_list(msdu, frag_buf, amsdu_len);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	amsdu_len -= frag_buf->len;
54162306a36Sopenharmony_ci	prev_frag_buf = frag_buf;
54262306a36Sopenharmony_ci	last_frag = ind_desc->reserved;
54362306a36Sopenharmony_ci	while (!last_frag) {
54462306a36Sopenharmony_ci		ind_desc++;
54562306a36Sopenharmony_ci		paddr = __le32_to_cpu(ind_desc->msdu_paddr);
54662306a36Sopenharmony_ci		frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
54762306a36Sopenharmony_ci		if (!frag_buf) {
54862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to pop frag-n paddr: 0x%x",
54962306a36Sopenharmony_ci				    paddr);
55062306a36Sopenharmony_ci			prev_frag_buf->next = NULL;
55162306a36Sopenharmony_ci			return -ENOENT;
55262306a36Sopenharmony_ci		}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
55562306a36Sopenharmony_ci		last_frag = ind_desc->reserved;
55662306a36Sopenharmony_ci		amsdu_len -= frag_buf->len;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		prev_frag_buf->next = frag_buf;
55962306a36Sopenharmony_ci		prev_frag_buf = frag_buf;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (amsdu_len) {
56362306a36Sopenharmony_ci		ath10k_warn(ar, "invalid amsdu len %u, left %d",
56462306a36Sopenharmony_ci			    __le16_to_cpu(ind_desc->msdu_len), amsdu_len);
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	*msdu_desc = ind_desc;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	prev_frag_buf->next = NULL;
57062306a36Sopenharmony_ci	return 0;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int
57462306a36Sopenharmony_ciath10k_htt_rx_handle_amsdu_mon_64(struct ath10k_htt *htt,
57562306a36Sopenharmony_ci				  struct sk_buff *msdu,
57662306a36Sopenharmony_ci				  struct htt_rx_in_ord_msdu_desc_ext **msdu_desc)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
57962306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
58062306a36Sopenharmony_ci	u64 paddr;
58162306a36Sopenharmony_ci	struct sk_buff *frag_buf;
58262306a36Sopenharmony_ci	struct sk_buff *prev_frag_buf;
58362306a36Sopenharmony_ci	u8 last_frag;
58462306a36Sopenharmony_ci	struct htt_rx_in_ord_msdu_desc_ext *ind_desc = *msdu_desc;
58562306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
58662306a36Sopenharmony_ci	int amsdu_len = __le16_to_cpu(ind_desc->msdu_len);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data);
58962306a36Sopenharmony_ci	trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	skb_put(msdu, hw->rx_desc_ops->rx_desc_size);
59262306a36Sopenharmony_ci	skb_pull(msdu, hw->rx_desc_ops->rx_desc_size);
59362306a36Sopenharmony_ci	skb_put(msdu, min(amsdu_len, ath10k_htt_rx_msdu_size(hw)));
59462306a36Sopenharmony_ci	amsdu_len -= msdu->len;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	last_frag = ind_desc->reserved;
59762306a36Sopenharmony_ci	if (last_frag) {
59862306a36Sopenharmony_ci		if (amsdu_len) {
59962306a36Sopenharmony_ci			ath10k_warn(ar, "invalid amsdu len %u, left %d",
60062306a36Sopenharmony_ci				    __le16_to_cpu(ind_desc->msdu_len),
60162306a36Sopenharmony_ci				    amsdu_len);
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci		return 0;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	ind_desc++;
60762306a36Sopenharmony_ci	paddr = __le64_to_cpu(ind_desc->msdu_paddr);
60862306a36Sopenharmony_ci	frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
60962306a36Sopenharmony_ci	if (!frag_buf) {
61062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to pop frag-1 paddr: 0x%llx", paddr);
61162306a36Sopenharmony_ci		return -ENOENT;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
61562306a36Sopenharmony_ci	ath10k_htt_append_frag_list(msdu, frag_buf, amsdu_len);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	amsdu_len -= frag_buf->len;
61862306a36Sopenharmony_ci	prev_frag_buf = frag_buf;
61962306a36Sopenharmony_ci	last_frag = ind_desc->reserved;
62062306a36Sopenharmony_ci	while (!last_frag) {
62162306a36Sopenharmony_ci		ind_desc++;
62262306a36Sopenharmony_ci		paddr = __le64_to_cpu(ind_desc->msdu_paddr);
62362306a36Sopenharmony_ci		frag_buf = ath10k_htt_rx_pop_paddr(htt, paddr);
62462306a36Sopenharmony_ci		if (!frag_buf) {
62562306a36Sopenharmony_ci			ath10k_warn(ar, "failed to pop frag-n paddr: 0x%llx",
62662306a36Sopenharmony_ci				    paddr);
62762306a36Sopenharmony_ci			prev_frag_buf->next = NULL;
62862306a36Sopenharmony_ci			return -ENOENT;
62962306a36Sopenharmony_ci		}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		skb_put(frag_buf, min(amsdu_len, HTT_RX_BUF_SIZE));
63262306a36Sopenharmony_ci		last_frag = ind_desc->reserved;
63362306a36Sopenharmony_ci		amsdu_len -= frag_buf->len;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		prev_frag_buf->next = frag_buf;
63662306a36Sopenharmony_ci		prev_frag_buf = frag_buf;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (amsdu_len) {
64062306a36Sopenharmony_ci		ath10k_warn(ar, "invalid amsdu len %u, left %d",
64162306a36Sopenharmony_ci			    __le16_to_cpu(ind_desc->msdu_len), amsdu_len);
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	*msdu_desc = ind_desc;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	prev_frag_buf->next = NULL;
64762306a36Sopenharmony_ci	return 0;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic int ath10k_htt_rx_pop_paddr32_list(struct ath10k_htt *htt,
65162306a36Sopenharmony_ci					  struct htt_rx_in_ord_ind *ev,
65262306a36Sopenharmony_ci					  struct sk_buff_head *list)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
65562306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
65662306a36Sopenharmony_ci	struct htt_rx_in_ord_msdu_desc *msdu_desc = ev->msdu_descs32;
65762306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
65862306a36Sopenharmony_ci	struct rx_attention *rxd_attention;
65962306a36Sopenharmony_ci	struct sk_buff *msdu;
66062306a36Sopenharmony_ci	int msdu_count, ret;
66162306a36Sopenharmony_ci	bool is_offload;
66262306a36Sopenharmony_ci	u32 paddr;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	lockdep_assert_held(&htt->rx_ring.lock);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	msdu_count = __le16_to_cpu(ev->msdu_count);
66762306a36Sopenharmony_ci	is_offload = !!(ev->info & HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	while (msdu_count--) {
67062306a36Sopenharmony_ci		paddr = __le32_to_cpu(msdu_desc->msdu_paddr);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		msdu = ath10k_htt_rx_pop_paddr(htt, paddr);
67362306a36Sopenharmony_ci		if (!msdu) {
67462306a36Sopenharmony_ci			__skb_queue_purge(list);
67562306a36Sopenharmony_ci			return -ENOENT;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		if (!is_offload && ar->monitor_arvif) {
67962306a36Sopenharmony_ci			ret = ath10k_htt_rx_handle_amsdu_mon_32(htt, msdu,
68062306a36Sopenharmony_ci								&msdu_desc);
68162306a36Sopenharmony_ci			if (ret) {
68262306a36Sopenharmony_ci				__skb_queue_purge(list);
68362306a36Sopenharmony_ci				return ret;
68462306a36Sopenharmony_ci			}
68562306a36Sopenharmony_ci			__skb_queue_tail(list, msdu);
68662306a36Sopenharmony_ci			msdu_desc++;
68762306a36Sopenharmony_ci			continue;
68862306a36Sopenharmony_ci		}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		__skb_queue_tail(list, msdu);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci		if (!is_offload) {
69362306a36Sopenharmony_ci			rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data);
69462306a36Sopenharmony_ci			rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci			trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci			skb_put(msdu, hw->rx_desc_ops->rx_desc_size);
69962306a36Sopenharmony_ci			skb_pull(msdu, hw->rx_desc_ops->rx_desc_size);
70062306a36Sopenharmony_ci			skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len));
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci			if (!(__le32_to_cpu(rxd_attention->flags) &
70362306a36Sopenharmony_ci			      RX_ATTENTION_FLAGS_MSDU_DONE)) {
70462306a36Sopenharmony_ci				ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n");
70562306a36Sopenharmony_ci				return -EIO;
70662306a36Sopenharmony_ci			}
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		msdu_desc++;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return 0;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic int ath10k_htt_rx_pop_paddr64_list(struct ath10k_htt *htt,
71662306a36Sopenharmony_ci					  struct htt_rx_in_ord_ind *ev,
71762306a36Sopenharmony_ci					  struct sk_buff_head *list)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
72062306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
72162306a36Sopenharmony_ci	struct htt_rx_in_ord_msdu_desc_ext *msdu_desc = ev->msdu_descs64;
72262306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
72362306a36Sopenharmony_ci	struct rx_attention *rxd_attention;
72462306a36Sopenharmony_ci	struct sk_buff *msdu;
72562306a36Sopenharmony_ci	int msdu_count, ret;
72662306a36Sopenharmony_ci	bool is_offload;
72762306a36Sopenharmony_ci	u64 paddr;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	lockdep_assert_held(&htt->rx_ring.lock);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	msdu_count = __le16_to_cpu(ev->msdu_count);
73262306a36Sopenharmony_ci	is_offload = !!(ev->info & HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	while (msdu_count--) {
73562306a36Sopenharmony_ci		paddr = __le64_to_cpu(msdu_desc->msdu_paddr);
73662306a36Sopenharmony_ci		msdu = ath10k_htt_rx_pop_paddr(htt, paddr);
73762306a36Sopenharmony_ci		if (!msdu) {
73862306a36Sopenharmony_ci			__skb_queue_purge(list);
73962306a36Sopenharmony_ci			return -ENOENT;
74062306a36Sopenharmony_ci		}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		if (!is_offload && ar->monitor_arvif) {
74362306a36Sopenharmony_ci			ret = ath10k_htt_rx_handle_amsdu_mon_64(htt, msdu,
74462306a36Sopenharmony_ci								&msdu_desc);
74562306a36Sopenharmony_ci			if (ret) {
74662306a36Sopenharmony_ci				__skb_queue_purge(list);
74762306a36Sopenharmony_ci				return ret;
74862306a36Sopenharmony_ci			}
74962306a36Sopenharmony_ci			__skb_queue_tail(list, msdu);
75062306a36Sopenharmony_ci			msdu_desc++;
75162306a36Sopenharmony_ci			continue;
75262306a36Sopenharmony_ci		}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		__skb_queue_tail(list, msdu);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci		if (!is_offload) {
75762306a36Sopenharmony_ci			rxd = HTT_RX_BUF_TO_RX_DESC(hw, msdu->data);
75862306a36Sopenharmony_ci			rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci			trace_ath10k_htt_rx_desc(ar, rxd, hw->rx_desc_ops->rx_desc_size);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci			skb_put(msdu, hw->rx_desc_ops->rx_desc_size);
76362306a36Sopenharmony_ci			skb_pull(msdu, hw->rx_desc_ops->rx_desc_size);
76462306a36Sopenharmony_ci			skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len));
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci			if (!(__le32_to_cpu(rxd_attention->flags) &
76762306a36Sopenharmony_ci			      RX_ATTENTION_FLAGS_MSDU_DONE)) {
76862306a36Sopenharmony_ci				ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n");
76962306a36Sopenharmony_ci				return -EIO;
77062306a36Sopenharmony_ci			}
77162306a36Sopenharmony_ci		}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		msdu_desc++;
77462306a36Sopenharmony_ci	}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	return 0;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ciint ath10k_htt_rx_alloc(struct ath10k_htt *htt)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
78262306a36Sopenharmony_ci	dma_addr_t paddr;
78362306a36Sopenharmony_ci	void *vaddr, *vaddr_ring;
78462306a36Sopenharmony_ci	size_t size;
78562306a36Sopenharmony_ci	struct timer_list *timer = &htt->rx_ring.refill_retry_timer;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
78862306a36Sopenharmony_ci		return 0;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	htt->rx_confused = false;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/* XXX: The fill level could be changed during runtime in response to
79362306a36Sopenharmony_ci	 * the host processing latency. Is this really worth it?
79462306a36Sopenharmony_ci	 */
79562306a36Sopenharmony_ci	htt->rx_ring.size = HTT_RX_RING_SIZE;
79662306a36Sopenharmony_ci	htt->rx_ring.size_mask = htt->rx_ring.size - 1;
79762306a36Sopenharmony_ci	htt->rx_ring.fill_level = ar->hw_params.rx_ring_fill_level;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (!is_power_of_2(htt->rx_ring.size)) {
80062306a36Sopenharmony_ci		ath10k_warn(ar, "htt rx ring size is not power of 2\n");
80162306a36Sopenharmony_ci		return -EINVAL;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	htt->rx_ring.netbufs_ring =
80562306a36Sopenharmony_ci		kcalloc(htt->rx_ring.size, sizeof(struct sk_buff *),
80662306a36Sopenharmony_ci			GFP_KERNEL);
80762306a36Sopenharmony_ci	if (!htt->rx_ring.netbufs_ring)
80862306a36Sopenharmony_ci		goto err_netbuf;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	size = ath10k_htt_get_rx_ring_size(htt);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	vaddr_ring = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_KERNEL);
81362306a36Sopenharmony_ci	if (!vaddr_ring)
81462306a36Sopenharmony_ci		goto err_dma_ring;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	ath10k_htt_config_paddrs_ring(htt, vaddr_ring);
81762306a36Sopenharmony_ci	htt->rx_ring.base_paddr = paddr;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	vaddr = dma_alloc_coherent(htt->ar->dev,
82062306a36Sopenharmony_ci				   sizeof(*htt->rx_ring.alloc_idx.vaddr),
82162306a36Sopenharmony_ci				   &paddr, GFP_KERNEL);
82262306a36Sopenharmony_ci	if (!vaddr)
82362306a36Sopenharmony_ci		goto err_dma_idx;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	htt->rx_ring.alloc_idx.vaddr = vaddr;
82662306a36Sopenharmony_ci	htt->rx_ring.alloc_idx.paddr = paddr;
82762306a36Sopenharmony_ci	htt->rx_ring.sw_rd_idx.msdu_payld = htt->rx_ring.size_mask;
82862306a36Sopenharmony_ci	*htt->rx_ring.alloc_idx.vaddr = 0;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/* Initialize the Rx refill retry timer */
83162306a36Sopenharmony_ci	timer_setup(timer, ath10k_htt_rx_ring_refill_retry, 0);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	spin_lock_init(&htt->rx_ring.lock);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	htt->rx_ring.fill_cnt = 0;
83662306a36Sopenharmony_ci	htt->rx_ring.sw_rd_idx.msdu_payld = 0;
83762306a36Sopenharmony_ci	hash_init(htt->rx_ring.skb_table);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	skb_queue_head_init(&htt->rx_msdus_q);
84062306a36Sopenharmony_ci	skb_queue_head_init(&htt->rx_in_ord_compl_q);
84162306a36Sopenharmony_ci	skb_queue_head_init(&htt->tx_fetch_ind_q);
84262306a36Sopenharmony_ci	atomic_set(&htt->num_mpdus_ready, 0);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
84562306a36Sopenharmony_ci		   htt->rx_ring.size, htt->rx_ring.fill_level);
84662306a36Sopenharmony_ci	return 0;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cierr_dma_idx:
84962306a36Sopenharmony_ci	dma_free_coherent(htt->ar->dev,
85062306a36Sopenharmony_ci			  ath10k_htt_get_rx_ring_size(htt),
85162306a36Sopenharmony_ci			  vaddr_ring,
85262306a36Sopenharmony_ci			  htt->rx_ring.base_paddr);
85362306a36Sopenharmony_ci	ath10k_htt_config_paddrs_ring(htt, NULL);
85462306a36Sopenharmony_cierr_dma_ring:
85562306a36Sopenharmony_ci	kfree(htt->rx_ring.netbufs_ring);
85662306a36Sopenharmony_ci	htt->rx_ring.netbufs_ring = NULL;
85762306a36Sopenharmony_cierr_netbuf:
85862306a36Sopenharmony_ci	return -ENOMEM;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic int ath10k_htt_rx_crypto_param_len(struct ath10k *ar,
86262306a36Sopenharmony_ci					  enum htt_rx_mpdu_encrypt_type type)
86362306a36Sopenharmony_ci{
86462306a36Sopenharmony_ci	switch (type) {
86562306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_NONE:
86662306a36Sopenharmony_ci		return 0;
86762306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WEP40:
86862306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WEP104:
86962306a36Sopenharmony_ci		return IEEE80211_WEP_IV_LEN;
87062306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
87162306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
87262306a36Sopenharmony_ci		return IEEE80211_TKIP_IV_LEN;
87362306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
87462306a36Sopenharmony_ci		return IEEE80211_CCMP_HDR_LEN;
87562306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2:
87662306a36Sopenharmony_ci		return IEEE80211_CCMP_256_HDR_LEN;
87762306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2:
87862306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2:
87962306a36Sopenharmony_ci		return IEEE80211_GCMP_HDR_LEN;
88062306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WEP128:
88162306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WAPI:
88262306a36Sopenharmony_ci		break;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	ath10k_warn(ar, "unsupported encryption type %d\n", type);
88662306a36Sopenharmony_ci	return 0;
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci#define MICHAEL_MIC_LEN 8
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic int ath10k_htt_rx_crypto_mic_len(struct ath10k *ar,
89262306a36Sopenharmony_ci					enum htt_rx_mpdu_encrypt_type type)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	switch (type) {
89562306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_NONE:
89662306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WEP40:
89762306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WEP104:
89862306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
89962306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
90062306a36Sopenharmony_ci		return 0;
90162306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
90262306a36Sopenharmony_ci		return IEEE80211_CCMP_MIC_LEN;
90362306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2:
90462306a36Sopenharmony_ci		return IEEE80211_CCMP_256_MIC_LEN;
90562306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2:
90662306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2:
90762306a36Sopenharmony_ci		return IEEE80211_GCMP_MIC_LEN;
90862306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WEP128:
90962306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WAPI:
91062306a36Sopenharmony_ci		break;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	ath10k_warn(ar, "unsupported encryption type %d\n", type);
91462306a36Sopenharmony_ci	return 0;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_cistatic int ath10k_htt_rx_crypto_icv_len(struct ath10k *ar,
91862306a36Sopenharmony_ci					enum htt_rx_mpdu_encrypt_type type)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	switch (type) {
92162306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_NONE:
92262306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
92362306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2:
92462306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2:
92562306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2:
92662306a36Sopenharmony_ci		return 0;
92762306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WEP40:
92862306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WEP104:
92962306a36Sopenharmony_ci		return IEEE80211_WEP_ICV_LEN;
93062306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
93162306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
93262306a36Sopenharmony_ci		return IEEE80211_TKIP_ICV_LEN;
93362306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WEP128:
93462306a36Sopenharmony_ci	case HTT_RX_MPDU_ENCRYPT_WAPI:
93562306a36Sopenharmony_ci		break;
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	ath10k_warn(ar, "unsupported encryption type %d\n", type);
93962306a36Sopenharmony_ci	return 0;
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistruct amsdu_subframe_hdr {
94362306a36Sopenharmony_ci	u8 dst[ETH_ALEN];
94462306a36Sopenharmony_ci	u8 src[ETH_ALEN];
94562306a36Sopenharmony_ci	__be16 len;
94662306a36Sopenharmony_ci} __packed;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci#define GROUP_ID_IS_SU_MIMO(x) ((x) == 0 || (x) == 63)
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic inline u8 ath10k_bw_to_mac80211_bw(u8 bw)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	u8 ret = 0;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	switch (bw) {
95562306a36Sopenharmony_ci	case 0:
95662306a36Sopenharmony_ci		ret = RATE_INFO_BW_20;
95762306a36Sopenharmony_ci		break;
95862306a36Sopenharmony_ci	case 1:
95962306a36Sopenharmony_ci		ret = RATE_INFO_BW_40;
96062306a36Sopenharmony_ci		break;
96162306a36Sopenharmony_ci	case 2:
96262306a36Sopenharmony_ci		ret = RATE_INFO_BW_80;
96362306a36Sopenharmony_ci		break;
96462306a36Sopenharmony_ci	case 3:
96562306a36Sopenharmony_ci		ret = RATE_INFO_BW_160;
96662306a36Sopenharmony_ci		break;
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	return ret;
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic void ath10k_htt_rx_h_rates(struct ath10k *ar,
97362306a36Sopenharmony_ci				  struct ieee80211_rx_status *status,
97462306a36Sopenharmony_ci				  struct htt_rx_desc *rxd)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
97762306a36Sopenharmony_ci	struct rx_attention *rxd_attention;
97862306a36Sopenharmony_ci	struct rx_mpdu_start *rxd_mpdu_start;
97962306a36Sopenharmony_ci	struct rx_mpdu_end *rxd_mpdu_end;
98062306a36Sopenharmony_ci	struct rx_msdu_start_common *rxd_msdu_start_common;
98162306a36Sopenharmony_ci	struct rx_msdu_end_common *rxd_msdu_end_common;
98262306a36Sopenharmony_ci	struct rx_ppdu_start *rxd_ppdu_start;
98362306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
98462306a36Sopenharmony_ci	u8 cck, rate, bw, sgi, mcs, nss;
98562306a36Sopenharmony_ci	u8 *rxd_msdu_payload;
98662306a36Sopenharmony_ci	u8 preamble = 0;
98762306a36Sopenharmony_ci	u8 group_id;
98862306a36Sopenharmony_ci	u32 info1, info2, info3;
98962306a36Sopenharmony_ci	u32 stbc, nsts_su;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd);
99262306a36Sopenharmony_ci	rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd);
99362306a36Sopenharmony_ci	rxd_mpdu_end = ath10k_htt_rx_desc_get_mpdu_end(hw, rxd);
99462306a36Sopenharmony_ci	rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd);
99562306a36Sopenharmony_ci	rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd);
99662306a36Sopenharmony_ci	rxd_ppdu_start = ath10k_htt_rx_desc_get_ppdu_start(hw, rxd);
99762306a36Sopenharmony_ci	rxd_msdu_payload = ath10k_htt_rx_desc_get_msdu_payload(hw, rxd);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	info1 = __le32_to_cpu(rxd_ppdu_start->info1);
100062306a36Sopenharmony_ci	info2 = __le32_to_cpu(rxd_ppdu_start->info2);
100162306a36Sopenharmony_ci	info3 = __le32_to_cpu(rxd_ppdu_start->info3);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	preamble = MS(info1, RX_PPDU_START_INFO1_PREAMBLE_TYPE);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	switch (preamble) {
100662306a36Sopenharmony_ci	case HTT_RX_LEGACY:
100762306a36Sopenharmony_ci		/* To get legacy rate index band is required. Since band can't
100862306a36Sopenharmony_ci		 * be undefined check if freq is non-zero.
100962306a36Sopenharmony_ci		 */
101062306a36Sopenharmony_ci		if (!status->freq)
101162306a36Sopenharmony_ci			return;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci		cck = info1 & RX_PPDU_START_INFO1_L_SIG_RATE_SELECT;
101462306a36Sopenharmony_ci		rate = MS(info1, RX_PPDU_START_INFO1_L_SIG_RATE);
101562306a36Sopenharmony_ci		rate &= ~RX_PPDU_START_RATE_FLAG;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci		sband = &ar->mac.sbands[status->band];
101862306a36Sopenharmony_ci		status->rate_idx = ath10k_mac_hw_rate_to_idx(sband, rate, cck);
101962306a36Sopenharmony_ci		break;
102062306a36Sopenharmony_ci	case HTT_RX_HT:
102162306a36Sopenharmony_ci	case HTT_RX_HT_WITH_TXBF:
102262306a36Sopenharmony_ci		/* HT-SIG - Table 20-11 in info2 and info3 */
102362306a36Sopenharmony_ci		mcs = info2 & 0x1F;
102462306a36Sopenharmony_ci		nss = mcs >> 3;
102562306a36Sopenharmony_ci		bw = (info2 >> 7) & 1;
102662306a36Sopenharmony_ci		sgi = (info3 >> 7) & 1;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		status->rate_idx = mcs;
102962306a36Sopenharmony_ci		status->encoding = RX_ENC_HT;
103062306a36Sopenharmony_ci		if (sgi)
103162306a36Sopenharmony_ci			status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
103262306a36Sopenharmony_ci		if (bw)
103362306a36Sopenharmony_ci			status->bw = RATE_INFO_BW_40;
103462306a36Sopenharmony_ci		break;
103562306a36Sopenharmony_ci	case HTT_RX_VHT:
103662306a36Sopenharmony_ci	case HTT_RX_VHT_WITH_TXBF:
103762306a36Sopenharmony_ci		/* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3
103862306a36Sopenharmony_ci		 * TODO check this
103962306a36Sopenharmony_ci		 */
104062306a36Sopenharmony_ci		bw = info2 & 3;
104162306a36Sopenharmony_ci		sgi = info3 & 1;
104262306a36Sopenharmony_ci		stbc = (info2 >> 3) & 1;
104362306a36Sopenharmony_ci		group_id = (info2 >> 4) & 0x3F;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		if (GROUP_ID_IS_SU_MIMO(group_id)) {
104662306a36Sopenharmony_ci			mcs = (info3 >> 4) & 0x0F;
104762306a36Sopenharmony_ci			nsts_su = ((info2 >> 10) & 0x07);
104862306a36Sopenharmony_ci			if (stbc)
104962306a36Sopenharmony_ci				nss = (nsts_su >> 2) + 1;
105062306a36Sopenharmony_ci			else
105162306a36Sopenharmony_ci				nss = (nsts_su + 1);
105262306a36Sopenharmony_ci		} else {
105362306a36Sopenharmony_ci			/* Hardware doesn't decode VHT-SIG-B into Rx descriptor
105462306a36Sopenharmony_ci			 * so it's impossible to decode MCS. Also since
105562306a36Sopenharmony_ci			 * firmware consumes Group Id Management frames host
105662306a36Sopenharmony_ci			 * has no knowledge regarding group/user position
105762306a36Sopenharmony_ci			 * mapping so it's impossible to pick the correct Nsts
105862306a36Sopenharmony_ci			 * from VHT-SIG-A1.
105962306a36Sopenharmony_ci			 *
106062306a36Sopenharmony_ci			 * Bandwidth and SGI are valid so report the rateinfo
106162306a36Sopenharmony_ci			 * on best-effort basis.
106262306a36Sopenharmony_ci			 */
106362306a36Sopenharmony_ci			mcs = 0;
106462306a36Sopenharmony_ci			nss = 1;
106562306a36Sopenharmony_ci		}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci		if (mcs > 0x09) {
106862306a36Sopenharmony_ci			ath10k_warn(ar, "invalid MCS received %u\n", mcs);
106962306a36Sopenharmony_ci			ath10k_warn(ar, "rxd %08x mpdu start %08x %08x msdu start %08x %08x ppdu start %08x %08x %08x %08x %08x\n",
107062306a36Sopenharmony_ci				    __le32_to_cpu(rxd_attention->flags),
107162306a36Sopenharmony_ci				    __le32_to_cpu(rxd_mpdu_start->info0),
107262306a36Sopenharmony_ci				    __le32_to_cpu(rxd_mpdu_start->info1),
107362306a36Sopenharmony_ci				    __le32_to_cpu(rxd_msdu_start_common->info0),
107462306a36Sopenharmony_ci				    __le32_to_cpu(rxd_msdu_start_common->info1),
107562306a36Sopenharmony_ci				    rxd_ppdu_start->info0,
107662306a36Sopenharmony_ci				    __le32_to_cpu(rxd_ppdu_start->info1),
107762306a36Sopenharmony_ci				    __le32_to_cpu(rxd_ppdu_start->info2),
107862306a36Sopenharmony_ci				    __le32_to_cpu(rxd_ppdu_start->info3),
107962306a36Sopenharmony_ci				    __le32_to_cpu(rxd_ppdu_start->info4));
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci			ath10k_warn(ar, "msdu end %08x mpdu end %08x\n",
108262306a36Sopenharmony_ci				    __le32_to_cpu(rxd_msdu_end_common->info0),
108362306a36Sopenharmony_ci				    __le32_to_cpu(rxd_mpdu_end->info0));
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci			ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL,
108662306a36Sopenharmony_ci					"rx desc msdu payload: ",
108762306a36Sopenharmony_ci					rxd_msdu_payload, 50);
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		status->rate_idx = mcs;
109162306a36Sopenharmony_ci		status->nss = nss;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		if (sgi)
109462306a36Sopenharmony_ci			status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci		status->bw = ath10k_bw_to_mac80211_bw(bw);
109762306a36Sopenharmony_ci		status->encoding = RX_ENC_VHT;
109862306a36Sopenharmony_ci		break;
109962306a36Sopenharmony_ci	default:
110062306a36Sopenharmony_ci		break;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic struct ieee80211_channel *
110562306a36Sopenharmony_ciath10k_htt_rx_h_peer_channel(struct ath10k *ar, struct htt_rx_desc *rxd)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
110862306a36Sopenharmony_ci	struct rx_attention *rxd_attention;
110962306a36Sopenharmony_ci	struct rx_msdu_end_common *rxd_msdu_end_common;
111062306a36Sopenharmony_ci	struct rx_mpdu_start *rxd_mpdu_start;
111162306a36Sopenharmony_ci	struct ath10k_peer *peer;
111262306a36Sopenharmony_ci	struct ath10k_vif *arvif;
111362306a36Sopenharmony_ci	struct cfg80211_chan_def def;
111462306a36Sopenharmony_ci	u16 peer_id;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	if (!rxd)
111962306a36Sopenharmony_ci		return NULL;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd);
112262306a36Sopenharmony_ci	rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd);
112362306a36Sopenharmony_ci	rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	if (rxd_attention->flags &
112662306a36Sopenharmony_ci	    __cpu_to_le32(RX_ATTENTION_FLAGS_PEER_IDX_INVALID))
112762306a36Sopenharmony_ci		return NULL;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (!(rxd_msdu_end_common->info0 &
113062306a36Sopenharmony_ci	      __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU)))
113162306a36Sopenharmony_ci		return NULL;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	peer_id = MS(__le32_to_cpu(rxd_mpdu_start->info0),
113462306a36Sopenharmony_ci		     RX_MPDU_START_INFO0_PEER_IDX);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	peer = ath10k_peer_find_by_id(ar, peer_id);
113762306a36Sopenharmony_ci	if (!peer)
113862306a36Sopenharmony_ci		return NULL;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	arvif = ath10k_get_arvif(ar, peer->vdev_id);
114162306a36Sopenharmony_ci	if (WARN_ON_ONCE(!arvif))
114262306a36Sopenharmony_ci		return NULL;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (ath10k_mac_vif_chan(arvif->vif, &def))
114562306a36Sopenharmony_ci		return NULL;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	return def.chan;
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_cistatic struct ieee80211_channel *
115162306a36Sopenharmony_ciath10k_htt_rx_h_vdev_channel(struct ath10k *ar, u32 vdev_id)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	struct ath10k_vif *arvif;
115462306a36Sopenharmony_ci	struct cfg80211_chan_def def;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	list_for_each_entry(arvif, &ar->arvifs, list) {
115962306a36Sopenharmony_ci		if (arvif->vdev_id == vdev_id &&
116062306a36Sopenharmony_ci		    ath10k_mac_vif_chan(arvif->vif, &def) == 0)
116162306a36Sopenharmony_ci			return def.chan;
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	return NULL;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_cistatic void
116862306a36Sopenharmony_ciath10k_htt_rx_h_any_chan_iter(struct ieee80211_hw *hw,
116962306a36Sopenharmony_ci			      struct ieee80211_chanctx_conf *conf,
117062306a36Sopenharmony_ci			      void *data)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	struct cfg80211_chan_def *def = data;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	*def = conf->def;
117562306a36Sopenharmony_ci}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic struct ieee80211_channel *
117862306a36Sopenharmony_ciath10k_htt_rx_h_any_channel(struct ath10k *ar)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	struct cfg80211_chan_def def = {};
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	ieee80211_iter_chan_contexts_atomic(ar->hw,
118362306a36Sopenharmony_ci					    ath10k_htt_rx_h_any_chan_iter,
118462306a36Sopenharmony_ci					    &def);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	return def.chan;
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cistatic bool ath10k_htt_rx_h_channel(struct ath10k *ar,
119062306a36Sopenharmony_ci				    struct ieee80211_rx_status *status,
119162306a36Sopenharmony_ci				    struct htt_rx_desc *rxd,
119262306a36Sopenharmony_ci				    u32 vdev_id)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	struct ieee80211_channel *ch;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
119762306a36Sopenharmony_ci	ch = ar->scan_channel;
119862306a36Sopenharmony_ci	if (!ch)
119962306a36Sopenharmony_ci		ch = ar->rx_channel;
120062306a36Sopenharmony_ci	if (!ch)
120162306a36Sopenharmony_ci		ch = ath10k_htt_rx_h_peer_channel(ar, rxd);
120262306a36Sopenharmony_ci	if (!ch)
120362306a36Sopenharmony_ci		ch = ath10k_htt_rx_h_vdev_channel(ar, vdev_id);
120462306a36Sopenharmony_ci	if (!ch)
120562306a36Sopenharmony_ci		ch = ath10k_htt_rx_h_any_channel(ar);
120662306a36Sopenharmony_ci	if (!ch)
120762306a36Sopenharmony_ci		ch = ar->tgt_oper_chan;
120862306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	if (!ch)
121162306a36Sopenharmony_ci		return false;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	status->band = ch->band;
121462306a36Sopenharmony_ci	status->freq = ch->center_freq;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	return true;
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_cistatic void ath10k_htt_rx_h_signal(struct ath10k *ar,
122062306a36Sopenharmony_ci				   struct ieee80211_rx_status *status,
122162306a36Sopenharmony_ci				   struct htt_rx_desc *rxd)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
122462306a36Sopenharmony_ci	struct rx_ppdu_start *rxd_ppdu_start = ath10k_htt_rx_desc_get_ppdu_start(hw, rxd);
122562306a36Sopenharmony_ci	int i;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	for (i = 0; i < IEEE80211_MAX_CHAINS ; i++) {
122862306a36Sopenharmony_ci		status->chains &= ~BIT(i);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci		if (rxd_ppdu_start->rssi_chains[i].pri20_mhz != 0x80) {
123162306a36Sopenharmony_ci			status->chain_signal[i] = ATH10K_DEFAULT_NOISE_FLOOR +
123262306a36Sopenharmony_ci				rxd_ppdu_start->rssi_chains[i].pri20_mhz;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci			status->chains |= BIT(i);
123562306a36Sopenharmony_ci		}
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	/* FIXME: Get real NF */
123962306a36Sopenharmony_ci	status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
124062306a36Sopenharmony_ci			 rxd_ppdu_start->rssi_comb;
124162306a36Sopenharmony_ci	status->flag &= ~RX_FLAG_NO_SIGNAL_VAL;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic void ath10k_htt_rx_h_mactime(struct ath10k *ar,
124562306a36Sopenharmony_ci				    struct ieee80211_rx_status *status,
124662306a36Sopenharmony_ci				    struct htt_rx_desc *rxd)
124762306a36Sopenharmony_ci{
124862306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
124962306a36Sopenharmony_ci	struct rx_ppdu_end_common *rxd_ppdu_end_common;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	rxd_ppdu_end_common = ath10k_htt_rx_desc_get_ppdu_end(hw, rxd);
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	/* FIXME: TSF is known only at the end of PPDU, in the last MPDU. This
125462306a36Sopenharmony_ci	 * means all prior MSDUs in a PPDU are reported to mac80211 without the
125562306a36Sopenharmony_ci	 * TSF. Is it worth holding frames until end of PPDU is known?
125662306a36Sopenharmony_ci	 *
125762306a36Sopenharmony_ci	 * FIXME: Can we get/compute 64bit TSF?
125862306a36Sopenharmony_ci	 */
125962306a36Sopenharmony_ci	status->mactime = __le32_to_cpu(rxd_ppdu_end_common->tsf_timestamp);
126062306a36Sopenharmony_ci	status->flag |= RX_FLAG_MACTIME_END;
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic void ath10k_htt_rx_h_ppdu(struct ath10k *ar,
126462306a36Sopenharmony_ci				 struct sk_buff_head *amsdu,
126562306a36Sopenharmony_ci				 struct ieee80211_rx_status *status,
126662306a36Sopenharmony_ci				 u32 vdev_id)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	struct sk_buff *first;
126962306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
127062306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
127162306a36Sopenharmony_ci	struct rx_attention *rxd_attention;
127262306a36Sopenharmony_ci	bool is_first_ppdu;
127362306a36Sopenharmony_ci	bool is_last_ppdu;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	if (skb_queue_empty(amsdu))
127662306a36Sopenharmony_ci		return;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	first = skb_peek(amsdu);
127962306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
128062306a36Sopenharmony_ci				    (void *)first->data - hw->rx_desc_ops->rx_desc_size);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	is_first_ppdu = !!(rxd_attention->flags &
128562306a36Sopenharmony_ci			   __cpu_to_le32(RX_ATTENTION_FLAGS_FIRST_MPDU));
128662306a36Sopenharmony_ci	is_last_ppdu = !!(rxd_attention->flags &
128762306a36Sopenharmony_ci			  __cpu_to_le32(RX_ATTENTION_FLAGS_LAST_MPDU));
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (is_first_ppdu) {
129062306a36Sopenharmony_ci		/* New PPDU starts so clear out the old per-PPDU status. */
129162306a36Sopenharmony_ci		status->freq = 0;
129262306a36Sopenharmony_ci		status->rate_idx = 0;
129362306a36Sopenharmony_ci		status->nss = 0;
129462306a36Sopenharmony_ci		status->encoding = RX_ENC_LEGACY;
129562306a36Sopenharmony_ci		status->bw = RATE_INFO_BW_20;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci		status->flag &= ~RX_FLAG_MACTIME_END;
129862306a36Sopenharmony_ci		status->flag |= RX_FLAG_NO_SIGNAL_VAL;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci		status->flag &= ~(RX_FLAG_AMPDU_IS_LAST);
130162306a36Sopenharmony_ci		status->flag |= RX_FLAG_AMPDU_DETAILS | RX_FLAG_AMPDU_LAST_KNOWN;
130262306a36Sopenharmony_ci		status->ampdu_reference = ar->ampdu_reference;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci		ath10k_htt_rx_h_signal(ar, status, rxd);
130562306a36Sopenharmony_ci		ath10k_htt_rx_h_channel(ar, status, rxd, vdev_id);
130662306a36Sopenharmony_ci		ath10k_htt_rx_h_rates(ar, status, rxd);
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	if (is_last_ppdu) {
131062306a36Sopenharmony_ci		ath10k_htt_rx_h_mactime(ar, status, rxd);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci		/* set ampdu last segment flag */
131362306a36Sopenharmony_ci		status->flag |= RX_FLAG_AMPDU_IS_LAST;
131462306a36Sopenharmony_ci		ar->ampdu_reference++;
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_cistatic const char * const tid_to_ac[] = {
131962306a36Sopenharmony_ci	"BE",
132062306a36Sopenharmony_ci	"BK",
132162306a36Sopenharmony_ci	"BK",
132262306a36Sopenharmony_ci	"BE",
132362306a36Sopenharmony_ci	"VI",
132462306a36Sopenharmony_ci	"VI",
132562306a36Sopenharmony_ci	"VO",
132662306a36Sopenharmony_ci	"VO",
132762306a36Sopenharmony_ci};
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cistatic char *ath10k_get_tid(struct ieee80211_hdr *hdr, char *out, size_t size)
133062306a36Sopenharmony_ci{
133162306a36Sopenharmony_ci	u8 *qc;
133262306a36Sopenharmony_ci	int tid;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	if (!ieee80211_is_data_qos(hdr->frame_control))
133562306a36Sopenharmony_ci		return "";
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	qc = ieee80211_get_qos_ctl(hdr);
133862306a36Sopenharmony_ci	tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
133962306a36Sopenharmony_ci	if (tid < 8)
134062306a36Sopenharmony_ci		snprintf(out, size, "tid %d (%s)", tid, tid_to_ac[tid]);
134162306a36Sopenharmony_ci	else
134262306a36Sopenharmony_ci		snprintf(out, size, "tid %d", tid);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	return out;
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cistatic void ath10k_htt_rx_h_queue_msdu(struct ath10k *ar,
134862306a36Sopenharmony_ci				       struct ieee80211_rx_status *rx_status,
134962306a36Sopenharmony_ci				       struct sk_buff *skb)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	struct ieee80211_rx_status *status;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	status = IEEE80211_SKB_RXCB(skb);
135462306a36Sopenharmony_ci	*status = *rx_status;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	skb_queue_tail(&ar->htt.rx_msdus_q, skb);
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_cistatic void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	struct ieee80211_rx_status *status;
136262306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
136362306a36Sopenharmony_ci	char tid[32];
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	status = IEEE80211_SKB_RXCB(skb);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	if (!(ar->filter_flags & FIF_FCSFAIL) &&
136862306a36Sopenharmony_ci	    status->flag & RX_FLAG_FAILED_FCS_CRC) {
136962306a36Sopenharmony_ci		ar->stats.rx_crc_err_drop++;
137062306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
137162306a36Sopenharmony_ci		return;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_DATA,
137562306a36Sopenharmony_ci		   "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
137662306a36Sopenharmony_ci		   skb,
137762306a36Sopenharmony_ci		   skb->len,
137862306a36Sopenharmony_ci		   ieee80211_get_SA(hdr),
137962306a36Sopenharmony_ci		   ath10k_get_tid(hdr, tid, sizeof(tid)),
138062306a36Sopenharmony_ci		   is_multicast_ether_addr(ieee80211_get_DA(hdr)) ?
138162306a36Sopenharmony_ci							"mcast" : "ucast",
138262306a36Sopenharmony_ci		   IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)),
138362306a36Sopenharmony_ci		   (status->encoding == RX_ENC_LEGACY) ? "legacy" : "",
138462306a36Sopenharmony_ci		   (status->encoding == RX_ENC_HT) ? "ht" : "",
138562306a36Sopenharmony_ci		   (status->encoding == RX_ENC_VHT) ? "vht" : "",
138662306a36Sopenharmony_ci		   (status->bw == RATE_INFO_BW_40) ? "40" : "",
138762306a36Sopenharmony_ci		   (status->bw == RATE_INFO_BW_80) ? "80" : "",
138862306a36Sopenharmony_ci		   (status->bw == RATE_INFO_BW_160) ? "160" : "",
138962306a36Sopenharmony_ci		   status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "",
139062306a36Sopenharmony_ci		   status->rate_idx,
139162306a36Sopenharmony_ci		   status->nss,
139262306a36Sopenharmony_ci		   status->freq,
139362306a36Sopenharmony_ci		   status->band, status->flag,
139462306a36Sopenharmony_ci		   !!(status->flag & RX_FLAG_FAILED_FCS_CRC),
139562306a36Sopenharmony_ci		   !!(status->flag & RX_FLAG_MMIC_ERROR),
139662306a36Sopenharmony_ci		   !!(status->flag & RX_FLAG_AMSDU_MORE));
139762306a36Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
139862306a36Sopenharmony_ci			skb->data, skb->len);
139962306a36Sopenharmony_ci	trace_ath10k_rx_hdr(ar, skb->data, skb->len);
140062306a36Sopenharmony_ci	trace_ath10k_rx_payload(ar, skb->data, skb->len);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	ieee80211_rx_napi(ar->hw, NULL, skb, &ar->napi);
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic int ath10k_htt_rx_nwifi_hdrlen(struct ath10k *ar,
140662306a36Sopenharmony_ci				      struct ieee80211_hdr *hdr)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	int len = ieee80211_hdrlen(hdr->frame_control);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	if (!test_bit(ATH10K_FW_FEATURE_NO_NWIFI_DECAP_4ADDR_PADDING,
141162306a36Sopenharmony_ci		      ar->running_fw->fw_file.fw_features))
141262306a36Sopenharmony_ci		len = round_up(len, 4);
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	return len;
141562306a36Sopenharmony_ci}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_cistatic void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
141862306a36Sopenharmony_ci					struct sk_buff *msdu,
141962306a36Sopenharmony_ci					struct ieee80211_rx_status *status,
142062306a36Sopenharmony_ci					enum htt_rx_mpdu_encrypt_type enctype,
142162306a36Sopenharmony_ci					bool is_decrypted,
142262306a36Sopenharmony_ci					const u8 first_hdr[64])
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
142562306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
142662306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
142762306a36Sopenharmony_ci	struct rx_msdu_end_common *rxd_msdu_end_common;
142862306a36Sopenharmony_ci	size_t hdr_len;
142962306a36Sopenharmony_ci	size_t crypto_len;
143062306a36Sopenharmony_ci	bool is_first;
143162306a36Sopenharmony_ci	bool is_last;
143262306a36Sopenharmony_ci	bool msdu_limit_err;
143362306a36Sopenharmony_ci	int bytes_aligned = ar->hw_params.decap_align_bytes;
143462306a36Sopenharmony_ci	u8 *qos;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
143762306a36Sopenharmony_ci				    (void *)msdu->data - hw->rx_desc_ops->rx_desc_size);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd);
144062306a36Sopenharmony_ci	is_first = !!(rxd_msdu_end_common->info0 &
144162306a36Sopenharmony_ci		      __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
144262306a36Sopenharmony_ci	is_last = !!(rxd_msdu_end_common->info0 &
144362306a36Sopenharmony_ci		     __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	/* Delivered decapped frame:
144662306a36Sopenharmony_ci	 * [802.11 header]
144762306a36Sopenharmony_ci	 * [crypto param] <-- can be trimmed if !fcs_err &&
144862306a36Sopenharmony_ci	 *                    !decrypt_err && !peer_idx_invalid
144962306a36Sopenharmony_ci	 * [amsdu header] <-- only if A-MSDU
145062306a36Sopenharmony_ci	 * [rfc1042/llc]
145162306a36Sopenharmony_ci	 * [payload]
145262306a36Sopenharmony_ci	 * [FCS] <-- at end, needs to be trimmed
145362306a36Sopenharmony_ci	 */
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	/* Some hardwares(QCA99x0 variants) limit number of msdus in a-msdu when
145662306a36Sopenharmony_ci	 * deaggregate, so that unwanted MSDU-deaggregation is avoided for
145762306a36Sopenharmony_ci	 * error packets. If limit exceeds, hw sends all remaining MSDUs as
145862306a36Sopenharmony_ci	 * a single last MSDU with this msdu limit error set.
145962306a36Sopenharmony_ci	 */
146062306a36Sopenharmony_ci	msdu_limit_err = ath10k_htt_rx_desc_msdu_limit_error(hw, rxd);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	/* If MSDU limit error happens, then don't warn on, the partial raw MSDU
146362306a36Sopenharmony_ci	 * without first MSDU is expected in that case, and handled later here.
146462306a36Sopenharmony_ci	 */
146562306a36Sopenharmony_ci	/* This probably shouldn't happen but warn just in case */
146662306a36Sopenharmony_ci	if (WARN_ON_ONCE(!is_first && !msdu_limit_err))
146762306a36Sopenharmony_ci		return;
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	/* This probably shouldn't happen but warn just in case */
147062306a36Sopenharmony_ci	if (WARN_ON_ONCE(!(is_first && is_last) && !msdu_limit_err))
147162306a36Sopenharmony_ci		return;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	skb_trim(msdu, msdu->len - FCS_LEN);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	/* Push original 80211 header */
147662306a36Sopenharmony_ci	if (unlikely(msdu_limit_err)) {
147762306a36Sopenharmony_ci		hdr = (struct ieee80211_hdr *)first_hdr;
147862306a36Sopenharmony_ci		hdr_len = ieee80211_hdrlen(hdr->frame_control);
147962306a36Sopenharmony_ci		crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci		if (ieee80211_is_data_qos(hdr->frame_control)) {
148262306a36Sopenharmony_ci			qos = ieee80211_get_qos_ctl(hdr);
148362306a36Sopenharmony_ci			qos[0] |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
148462306a36Sopenharmony_ci		}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci		if (crypto_len)
148762306a36Sopenharmony_ci			memcpy(skb_push(msdu, crypto_len),
148862306a36Sopenharmony_ci			       (void *)hdr + round_up(hdr_len, bytes_aligned),
148962306a36Sopenharmony_ci			       crypto_len);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci		memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	/* In most cases this will be true for sniffed frames. It makes sense
149562306a36Sopenharmony_ci	 * to deliver them as-is without stripping the crypto param. This is
149662306a36Sopenharmony_ci	 * necessary for software based decryption.
149762306a36Sopenharmony_ci	 *
149862306a36Sopenharmony_ci	 * If there's no error then the frame is decrypted. At least that is
149962306a36Sopenharmony_ci	 * the case for frames that come in via fragmented rx indication.
150062306a36Sopenharmony_ci	 */
150162306a36Sopenharmony_ci	if (!is_decrypted)
150262306a36Sopenharmony_ci		return;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	/* The payload is decrypted so strip crypto params. Start from tail
150562306a36Sopenharmony_ci	 * since hdr is used to compute some stuff.
150662306a36Sopenharmony_ci	 */
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	hdr = (void *)msdu->data;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	/* Tail */
151162306a36Sopenharmony_ci	if (status->flag & RX_FLAG_IV_STRIPPED) {
151262306a36Sopenharmony_ci		skb_trim(msdu, msdu->len -
151362306a36Sopenharmony_ci			 ath10k_htt_rx_crypto_mic_len(ar, enctype));
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci		skb_trim(msdu, msdu->len -
151662306a36Sopenharmony_ci			 ath10k_htt_rx_crypto_icv_len(ar, enctype));
151762306a36Sopenharmony_ci	} else {
151862306a36Sopenharmony_ci		/* MIC */
151962306a36Sopenharmony_ci		if (status->flag & RX_FLAG_MIC_STRIPPED)
152062306a36Sopenharmony_ci			skb_trim(msdu, msdu->len -
152162306a36Sopenharmony_ci				 ath10k_htt_rx_crypto_mic_len(ar, enctype));
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci		/* ICV */
152462306a36Sopenharmony_ci		if (status->flag & RX_FLAG_ICV_STRIPPED)
152562306a36Sopenharmony_ci			skb_trim(msdu, msdu->len -
152662306a36Sopenharmony_ci				 ath10k_htt_rx_crypto_icv_len(ar, enctype));
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	/* MMIC */
153062306a36Sopenharmony_ci	if ((status->flag & RX_FLAG_MMIC_STRIPPED) &&
153162306a36Sopenharmony_ci	    !ieee80211_has_morefrags(hdr->frame_control) &&
153262306a36Sopenharmony_ci	    enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
153362306a36Sopenharmony_ci		skb_trim(msdu, msdu->len - MICHAEL_MIC_LEN);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	/* Head */
153662306a36Sopenharmony_ci	if (status->flag & RX_FLAG_IV_STRIPPED) {
153762306a36Sopenharmony_ci		hdr_len = ieee80211_hdrlen(hdr->frame_control);
153862306a36Sopenharmony_ci		crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci		memmove((void *)msdu->data + crypto_len,
154162306a36Sopenharmony_ci			(void *)msdu->data, hdr_len);
154262306a36Sopenharmony_ci		skb_pull(msdu, crypto_len);
154362306a36Sopenharmony_ci	}
154462306a36Sopenharmony_ci}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_cistatic void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
154762306a36Sopenharmony_ci					  struct sk_buff *msdu,
154862306a36Sopenharmony_ci					  struct ieee80211_rx_status *status,
154962306a36Sopenharmony_ci					  const u8 first_hdr[64],
155062306a36Sopenharmony_ci					  enum htt_rx_mpdu_encrypt_type enctype)
155162306a36Sopenharmony_ci{
155262306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
155362306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
155462306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
155562306a36Sopenharmony_ci	size_t hdr_len;
155662306a36Sopenharmony_ci	u8 da[ETH_ALEN];
155762306a36Sopenharmony_ci	u8 sa[ETH_ALEN];
155862306a36Sopenharmony_ci	int l3_pad_bytes;
155962306a36Sopenharmony_ci	int bytes_aligned = ar->hw_params.decap_align_bytes;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	/* Delivered decapped frame:
156262306a36Sopenharmony_ci	 * [nwifi 802.11 header] <-- replaced with 802.11 hdr
156362306a36Sopenharmony_ci	 * [rfc1042/llc]
156462306a36Sopenharmony_ci	 *
156562306a36Sopenharmony_ci	 * Note: The nwifi header doesn't have QoS Control and is
156662306a36Sopenharmony_ci	 * (always?) a 3addr frame.
156762306a36Sopenharmony_ci	 *
156862306a36Sopenharmony_ci	 * Note2: There's no A-MSDU subframe header. Even if it's part
156962306a36Sopenharmony_ci	 * of an A-MSDU.
157062306a36Sopenharmony_ci	 */
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	/* pull decapped header and copy SA & DA */
157362306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw, (void *)msdu->data -
157462306a36Sopenharmony_ci				    hw->rx_desc_ops->rx_desc_size);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	l3_pad_bytes = ath10k_htt_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd);
157762306a36Sopenharmony_ci	skb_put(msdu, l3_pad_bytes);
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)(msdu->data + l3_pad_bytes);
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	hdr_len = ath10k_htt_rx_nwifi_hdrlen(ar, hdr);
158262306a36Sopenharmony_ci	ether_addr_copy(da, ieee80211_get_DA(hdr));
158362306a36Sopenharmony_ci	ether_addr_copy(sa, ieee80211_get_SA(hdr));
158462306a36Sopenharmony_ci	skb_pull(msdu, hdr_len);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	/* push original 802.11 header */
158762306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)first_hdr;
158862306a36Sopenharmony_ci	hdr_len = ieee80211_hdrlen(hdr->frame_control);
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
159162306a36Sopenharmony_ci		memcpy(skb_push(msdu,
159262306a36Sopenharmony_ci				ath10k_htt_rx_crypto_param_len(ar, enctype)),
159362306a36Sopenharmony_ci		       (void *)hdr + round_up(hdr_len, bytes_aligned),
159462306a36Sopenharmony_ci			ath10k_htt_rx_crypto_param_len(ar, enctype));
159562306a36Sopenharmony_ci	}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/* original 802.11 header has a different DA and in
160062306a36Sopenharmony_ci	 * case of 4addr it may also have different SA
160162306a36Sopenharmony_ci	 */
160262306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)msdu->data;
160362306a36Sopenharmony_ci	ether_addr_copy(ieee80211_get_DA(hdr), da);
160462306a36Sopenharmony_ci	ether_addr_copy(ieee80211_get_SA(hdr), sa);
160562306a36Sopenharmony_ci}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_cistatic void *ath10k_htt_rx_h_find_rfc1042(struct ath10k *ar,
160862306a36Sopenharmony_ci					  struct sk_buff *msdu,
160962306a36Sopenharmony_ci					  enum htt_rx_mpdu_encrypt_type enctype)
161062306a36Sopenharmony_ci{
161162306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
161262306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
161362306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
161462306a36Sopenharmony_ci	struct rx_msdu_end_common *rxd_msdu_end_common;
161562306a36Sopenharmony_ci	u8 *rxd_rx_hdr_status;
161662306a36Sopenharmony_ci	size_t hdr_len, crypto_len;
161762306a36Sopenharmony_ci	void *rfc1042;
161862306a36Sopenharmony_ci	bool is_first, is_last, is_amsdu;
161962306a36Sopenharmony_ci	int bytes_aligned = ar->hw_params.decap_align_bytes;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
162262306a36Sopenharmony_ci				    (void *)msdu->data - hw->rx_desc_ops->rx_desc_size);
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd);
162562306a36Sopenharmony_ci	rxd_rx_hdr_status = ath10k_htt_rx_desc_get_rx_hdr_status(hw, rxd);
162662306a36Sopenharmony_ci	hdr = (void *)rxd_rx_hdr_status;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	is_first = !!(rxd_msdu_end_common->info0 &
162962306a36Sopenharmony_ci		      __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
163062306a36Sopenharmony_ci	is_last = !!(rxd_msdu_end_common->info0 &
163162306a36Sopenharmony_ci		     __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
163262306a36Sopenharmony_ci	is_amsdu = !(is_first && is_last);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	rfc1042 = hdr;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	if (is_first) {
163762306a36Sopenharmony_ci		hdr_len = ieee80211_hdrlen(hdr->frame_control);
163862306a36Sopenharmony_ci		crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci		rfc1042 += round_up(hdr_len, bytes_aligned) +
164162306a36Sopenharmony_ci			   round_up(crypto_len, bytes_aligned);
164262306a36Sopenharmony_ci	}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	if (is_amsdu)
164562306a36Sopenharmony_ci		rfc1042 += sizeof(struct amsdu_subframe_hdr);
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	return rfc1042;
164862306a36Sopenharmony_ci}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistatic void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
165162306a36Sopenharmony_ci					struct sk_buff *msdu,
165262306a36Sopenharmony_ci					struct ieee80211_rx_status *status,
165362306a36Sopenharmony_ci					const u8 first_hdr[64],
165462306a36Sopenharmony_ci					enum htt_rx_mpdu_encrypt_type enctype)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
165762306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
165862306a36Sopenharmony_ci	struct ethhdr *eth;
165962306a36Sopenharmony_ci	size_t hdr_len;
166062306a36Sopenharmony_ci	void *rfc1042;
166162306a36Sopenharmony_ci	u8 da[ETH_ALEN];
166262306a36Sopenharmony_ci	u8 sa[ETH_ALEN];
166362306a36Sopenharmony_ci	int l3_pad_bytes;
166462306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
166562306a36Sopenharmony_ci	int bytes_aligned = ar->hw_params.decap_align_bytes;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	/* Delivered decapped frame:
166862306a36Sopenharmony_ci	 * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
166962306a36Sopenharmony_ci	 * [payload]
167062306a36Sopenharmony_ci	 */
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	rfc1042 = ath10k_htt_rx_h_find_rfc1042(ar, msdu, enctype);
167362306a36Sopenharmony_ci	if (WARN_ON_ONCE(!rfc1042))
167462306a36Sopenharmony_ci		return;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
167762306a36Sopenharmony_ci				    (void *)msdu->data - hw->rx_desc_ops->rx_desc_size);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	l3_pad_bytes = ath10k_htt_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd);
168062306a36Sopenharmony_ci	skb_put(msdu, l3_pad_bytes);
168162306a36Sopenharmony_ci	skb_pull(msdu, l3_pad_bytes);
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	/* pull decapped header and copy SA & DA */
168462306a36Sopenharmony_ci	eth = (struct ethhdr *)msdu->data;
168562306a36Sopenharmony_ci	ether_addr_copy(da, eth->h_dest);
168662306a36Sopenharmony_ci	ether_addr_copy(sa, eth->h_source);
168762306a36Sopenharmony_ci	skb_pull(msdu, sizeof(struct ethhdr));
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	/* push rfc1042/llc/snap */
169062306a36Sopenharmony_ci	memcpy(skb_push(msdu, sizeof(struct rfc1042_hdr)), rfc1042,
169162306a36Sopenharmony_ci	       sizeof(struct rfc1042_hdr));
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	/* push original 802.11 header */
169462306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)first_hdr;
169562306a36Sopenharmony_ci	hdr_len = ieee80211_hdrlen(hdr->frame_control);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
169862306a36Sopenharmony_ci		memcpy(skb_push(msdu,
169962306a36Sopenharmony_ci				ath10k_htt_rx_crypto_param_len(ar, enctype)),
170062306a36Sopenharmony_ci		       (void *)hdr + round_up(hdr_len, bytes_aligned),
170162306a36Sopenharmony_ci			ath10k_htt_rx_crypto_param_len(ar, enctype));
170262306a36Sopenharmony_ci	}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	/* original 802.11 header has a different DA and in
170762306a36Sopenharmony_ci	 * case of 4addr it may also have different SA
170862306a36Sopenharmony_ci	 */
170962306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)msdu->data;
171062306a36Sopenharmony_ci	ether_addr_copy(ieee80211_get_DA(hdr), da);
171162306a36Sopenharmony_ci	ether_addr_copy(ieee80211_get_SA(hdr), sa);
171262306a36Sopenharmony_ci}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_cistatic void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
171562306a36Sopenharmony_ci					 struct sk_buff *msdu,
171662306a36Sopenharmony_ci					 struct ieee80211_rx_status *status,
171762306a36Sopenharmony_ci					 const u8 first_hdr[64],
171862306a36Sopenharmony_ci					 enum htt_rx_mpdu_encrypt_type enctype)
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
172162306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
172262306a36Sopenharmony_ci	size_t hdr_len;
172362306a36Sopenharmony_ci	int l3_pad_bytes;
172462306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
172562306a36Sopenharmony_ci	int bytes_aligned = ar->hw_params.decap_align_bytes;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	/* Delivered decapped frame:
172862306a36Sopenharmony_ci	 * [amsdu header] <-- replaced with 802.11 hdr
172962306a36Sopenharmony_ci	 * [rfc1042/llc]
173062306a36Sopenharmony_ci	 * [payload]
173162306a36Sopenharmony_ci	 */
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
173462306a36Sopenharmony_ci				    (void *)msdu->data - hw->rx_desc_ops->rx_desc_size);
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	l3_pad_bytes = ath10k_htt_rx_desc_get_l3_pad_bytes(&ar->hw_params, rxd);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	skb_put(msdu, l3_pad_bytes);
173962306a36Sopenharmony_ci	skb_pull(msdu, sizeof(struct amsdu_subframe_hdr) + l3_pad_bytes);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)first_hdr;
174262306a36Sopenharmony_ci	hdr_len = ieee80211_hdrlen(hdr->frame_control);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
174562306a36Sopenharmony_ci		memcpy(skb_push(msdu,
174662306a36Sopenharmony_ci				ath10k_htt_rx_crypto_param_len(ar, enctype)),
174762306a36Sopenharmony_ci		       (void *)hdr + round_up(hdr_len, bytes_aligned),
174862306a36Sopenharmony_ci			ath10k_htt_rx_crypto_param_len(ar, enctype));
174962306a36Sopenharmony_ci	}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
175262306a36Sopenharmony_ci}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_cistatic void ath10k_htt_rx_h_undecap(struct ath10k *ar,
175562306a36Sopenharmony_ci				    struct sk_buff *msdu,
175662306a36Sopenharmony_ci				    struct ieee80211_rx_status *status,
175762306a36Sopenharmony_ci				    u8 first_hdr[64],
175862306a36Sopenharmony_ci				    enum htt_rx_mpdu_encrypt_type enctype,
175962306a36Sopenharmony_ci				    bool is_decrypted)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
176262306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
176362306a36Sopenharmony_ci	struct rx_msdu_start_common *rxd_msdu_start_common;
176462306a36Sopenharmony_ci	enum rx_msdu_decap_format decap;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	/* First msdu's decapped header:
176762306a36Sopenharmony_ci	 * [802.11 header] <-- padded to 4 bytes long
176862306a36Sopenharmony_ci	 * [crypto param] <-- padded to 4 bytes long
176962306a36Sopenharmony_ci	 * [amsdu header] <-- only if A-MSDU
177062306a36Sopenharmony_ci	 * [rfc1042/llc]
177162306a36Sopenharmony_ci	 *
177262306a36Sopenharmony_ci	 * Other (2nd, 3rd, ..) msdu's decapped header:
177362306a36Sopenharmony_ci	 * [amsdu header] <-- only if A-MSDU
177462306a36Sopenharmony_ci	 * [rfc1042/llc]
177562306a36Sopenharmony_ci	 */
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
177862306a36Sopenharmony_ci				    (void *)msdu->data - hw->rx_desc_ops->rx_desc_size);
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd);
178162306a36Sopenharmony_ci	decap = MS(__le32_to_cpu(rxd_msdu_start_common->info1),
178262306a36Sopenharmony_ci		   RX_MSDU_START_INFO1_DECAP_FORMAT);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	switch (decap) {
178562306a36Sopenharmony_ci	case RX_MSDU_DECAP_RAW:
178662306a36Sopenharmony_ci		ath10k_htt_rx_h_undecap_raw(ar, msdu, status, enctype,
178762306a36Sopenharmony_ci					    is_decrypted, first_hdr);
178862306a36Sopenharmony_ci		break;
178962306a36Sopenharmony_ci	case RX_MSDU_DECAP_NATIVE_WIFI:
179062306a36Sopenharmony_ci		ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr,
179162306a36Sopenharmony_ci					      enctype);
179262306a36Sopenharmony_ci		break;
179362306a36Sopenharmony_ci	case RX_MSDU_DECAP_ETHERNET2_DIX:
179462306a36Sopenharmony_ci		ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
179562306a36Sopenharmony_ci		break;
179662306a36Sopenharmony_ci	case RX_MSDU_DECAP_8023_SNAP_LLC:
179762306a36Sopenharmony_ci		ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr,
179862306a36Sopenharmony_ci					     enctype);
179962306a36Sopenharmony_ci		break;
180062306a36Sopenharmony_ci	}
180162306a36Sopenharmony_ci}
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_cistatic int ath10k_htt_rx_get_csum_state(struct ath10k_hw_params *hw, struct sk_buff *skb)
180462306a36Sopenharmony_ci{
180562306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
180662306a36Sopenharmony_ci	struct rx_attention *rxd_attention;
180762306a36Sopenharmony_ci	struct rx_msdu_start_common *rxd_msdu_start_common;
180862306a36Sopenharmony_ci	u32 flags, info;
180962306a36Sopenharmony_ci	bool is_ip4, is_ip6;
181062306a36Sopenharmony_ci	bool is_tcp, is_udp;
181162306a36Sopenharmony_ci	bool ip_csum_ok, tcpudp_csum_ok;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
181462306a36Sopenharmony_ci				    (void *)skb->data - hw->rx_desc_ops->rx_desc_size);
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd);
181762306a36Sopenharmony_ci	rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd);
181862306a36Sopenharmony_ci	flags = __le32_to_cpu(rxd_attention->flags);
181962306a36Sopenharmony_ci	info = __le32_to_cpu(rxd_msdu_start_common->info1);
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	is_ip4 = !!(info & RX_MSDU_START_INFO1_IPV4_PROTO);
182262306a36Sopenharmony_ci	is_ip6 = !!(info & RX_MSDU_START_INFO1_IPV6_PROTO);
182362306a36Sopenharmony_ci	is_tcp = !!(info & RX_MSDU_START_INFO1_TCP_PROTO);
182462306a36Sopenharmony_ci	is_udp = !!(info & RX_MSDU_START_INFO1_UDP_PROTO);
182562306a36Sopenharmony_ci	ip_csum_ok = !(flags & RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL);
182662306a36Sopenharmony_ci	tcpudp_csum_ok = !(flags & RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL);
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	if (!is_ip4 && !is_ip6)
182962306a36Sopenharmony_ci		return CHECKSUM_NONE;
183062306a36Sopenharmony_ci	if (!is_tcp && !is_udp)
183162306a36Sopenharmony_ci		return CHECKSUM_NONE;
183262306a36Sopenharmony_ci	if (!ip_csum_ok)
183362306a36Sopenharmony_ci		return CHECKSUM_NONE;
183462306a36Sopenharmony_ci	if (!tcpudp_csum_ok)
183562306a36Sopenharmony_ci		return CHECKSUM_NONE;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	return CHECKSUM_UNNECESSARY;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic void ath10k_htt_rx_h_csum_offload(struct ath10k_hw_params *hw,
184162306a36Sopenharmony_ci					 struct sk_buff *msdu)
184262306a36Sopenharmony_ci{
184362306a36Sopenharmony_ci	msdu->ip_summed = ath10k_htt_rx_get_csum_state(hw, msdu);
184462306a36Sopenharmony_ci}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb,
184762306a36Sopenharmony_ci				  enum htt_rx_mpdu_encrypt_type enctype)
184862306a36Sopenharmony_ci{
184962306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
185062306a36Sopenharmony_ci	u64 pn = 0;
185162306a36Sopenharmony_ci	u8 *ehdr;
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)skb->data;
185462306a36Sopenharmony_ci	ehdr = skb->data + ieee80211_hdrlen(hdr->frame_control);
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	if (enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) {
185762306a36Sopenharmony_ci		pn = ehdr[0];
185862306a36Sopenharmony_ci		pn |= (u64)ehdr[1] << 8;
185962306a36Sopenharmony_ci		pn |= (u64)ehdr[4] << 16;
186062306a36Sopenharmony_ci		pn |= (u64)ehdr[5] << 24;
186162306a36Sopenharmony_ci		pn |= (u64)ehdr[6] << 32;
186262306a36Sopenharmony_ci		pn |= (u64)ehdr[7] << 40;
186362306a36Sopenharmony_ci	}
186462306a36Sopenharmony_ci	return pn;
186562306a36Sopenharmony_ci}
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_cistatic bool ath10k_htt_rx_h_frag_multicast_check(struct ath10k *ar,
186862306a36Sopenharmony_ci						 struct sk_buff *skb)
186962306a36Sopenharmony_ci{
187062306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)skb->data;
187362306a36Sopenharmony_ci	return !is_multicast_ether_addr(hdr->addr1);
187462306a36Sopenharmony_ci}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_cistatic bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar,
187762306a36Sopenharmony_ci					  struct sk_buff *skb,
187862306a36Sopenharmony_ci					  u16 peer_id,
187962306a36Sopenharmony_ci					  enum htt_rx_mpdu_encrypt_type enctype)
188062306a36Sopenharmony_ci{
188162306a36Sopenharmony_ci	struct ath10k_peer *peer;
188262306a36Sopenharmony_ci	union htt_rx_pn_t *last_pn, new_pn = {0};
188362306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
188462306a36Sopenharmony_ci	u8 tid, frag_number;
188562306a36Sopenharmony_ci	u32 seq;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	peer = ath10k_peer_find_by_id(ar, peer_id);
188862306a36Sopenharmony_ci	if (!peer) {
188962306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer for frag pn check\n");
189062306a36Sopenharmony_ci		return false;
189162306a36Sopenharmony_ci	}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)skb->data;
189462306a36Sopenharmony_ci	if (ieee80211_is_data_qos(hdr->frame_control))
189562306a36Sopenharmony_ci		tid = ieee80211_get_tid(hdr);
189662306a36Sopenharmony_ci	else
189762306a36Sopenharmony_ci		tid = ATH10K_TXRX_NON_QOS_TID;
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	last_pn = &peer->frag_tids_last_pn[tid];
190062306a36Sopenharmony_ci	new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, enctype);
190162306a36Sopenharmony_ci	frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
190262306a36Sopenharmony_ci	seq = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	if (frag_number == 0) {
190562306a36Sopenharmony_ci		last_pn->pn48 = new_pn.pn48;
190662306a36Sopenharmony_ci		peer->frag_tids_seq[tid] = seq;
190762306a36Sopenharmony_ci	} else {
190862306a36Sopenharmony_ci		if (seq != peer->frag_tids_seq[tid])
190962306a36Sopenharmony_ci			return false;
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci		if (new_pn.pn48 != last_pn->pn48 + 1)
191262306a36Sopenharmony_ci			return false;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci		last_pn->pn48 = new_pn.pn48;
191562306a36Sopenharmony_ci	}
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	return true;
191862306a36Sopenharmony_ci}
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_cistatic void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
192162306a36Sopenharmony_ci				 struct sk_buff_head *amsdu,
192262306a36Sopenharmony_ci				 struct ieee80211_rx_status *status,
192362306a36Sopenharmony_ci				 bool fill_crypt_header,
192462306a36Sopenharmony_ci				 u8 *rx_hdr,
192562306a36Sopenharmony_ci				 enum ath10k_pkt_rx_err *err,
192662306a36Sopenharmony_ci				 u16 peer_id,
192762306a36Sopenharmony_ci				 bool frag)
192862306a36Sopenharmony_ci{
192962306a36Sopenharmony_ci	struct sk_buff *first;
193062306a36Sopenharmony_ci	struct sk_buff *last;
193162306a36Sopenharmony_ci	struct sk_buff *msdu, *temp;
193262306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
193362306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
193462306a36Sopenharmony_ci	struct rx_attention *rxd_attention;
193562306a36Sopenharmony_ci	struct rx_mpdu_start *rxd_mpdu_start;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
193862306a36Sopenharmony_ci	enum htt_rx_mpdu_encrypt_type enctype;
193962306a36Sopenharmony_ci	u8 first_hdr[64];
194062306a36Sopenharmony_ci	u8 *qos;
194162306a36Sopenharmony_ci	bool has_fcs_err;
194262306a36Sopenharmony_ci	bool has_crypto_err;
194362306a36Sopenharmony_ci	bool has_tkip_err;
194462306a36Sopenharmony_ci	bool has_peer_idx_invalid;
194562306a36Sopenharmony_ci	bool is_decrypted;
194662306a36Sopenharmony_ci	bool is_mgmt;
194762306a36Sopenharmony_ci	u32 attention;
194862306a36Sopenharmony_ci	bool frag_pn_check = true, multicast_check = true;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	if (skb_queue_empty(amsdu))
195162306a36Sopenharmony_ci		return;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	first = skb_peek(amsdu);
195462306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
195562306a36Sopenharmony_ci				    (void *)first->data - hw->rx_desc_ops->rx_desc_size);
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd);
195862306a36Sopenharmony_ci	rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd);
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	is_mgmt = !!(rxd_attention->flags &
196162306a36Sopenharmony_ci		     __cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE));
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	enctype = MS(__le32_to_cpu(rxd_mpdu_start->info0),
196462306a36Sopenharmony_ci		     RX_MPDU_START_INFO0_ENCRYPT_TYPE);
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	/* First MSDU's Rx descriptor in an A-MSDU contains full 802.11
196762306a36Sopenharmony_ci	 * decapped header. It'll be used for undecapping of each MSDU.
196862306a36Sopenharmony_ci	 */
196962306a36Sopenharmony_ci	hdr = (void *)ath10k_htt_rx_desc_get_rx_hdr_status(hw, rxd);
197062306a36Sopenharmony_ci	memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	if (rx_hdr)
197362306a36Sopenharmony_ci		memcpy(rx_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	/* Each A-MSDU subframe will use the original header as the base and be
197662306a36Sopenharmony_ci	 * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
197762306a36Sopenharmony_ci	 */
197862306a36Sopenharmony_ci	hdr = (void *)first_hdr;
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	if (ieee80211_is_data_qos(hdr->frame_control)) {
198162306a36Sopenharmony_ci		qos = ieee80211_get_qos_ctl(hdr);
198262306a36Sopenharmony_ci		qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
198362306a36Sopenharmony_ci	}
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	/* Some attention flags are valid only in the last MSDU. */
198662306a36Sopenharmony_ci	last = skb_peek_tail(amsdu);
198762306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
198862306a36Sopenharmony_ci				    (void *)last->data - hw->rx_desc_ops->rx_desc_size);
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	rxd_attention = ath10k_htt_rx_desc_get_attention(hw, rxd);
199162306a36Sopenharmony_ci	attention = __le32_to_cpu(rxd_attention->flags);
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	has_fcs_err = !!(attention & RX_ATTENTION_FLAGS_FCS_ERR);
199462306a36Sopenharmony_ci	has_crypto_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
199562306a36Sopenharmony_ci	has_tkip_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
199662306a36Sopenharmony_ci	has_peer_idx_invalid = !!(attention & RX_ATTENTION_FLAGS_PEER_IDX_INVALID);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	/* Note: If hardware captures an encrypted frame that it can't decrypt,
199962306a36Sopenharmony_ci	 * e.g. due to fcs error, missing peer or invalid key data it will
200062306a36Sopenharmony_ci	 * report the frame as raw.
200162306a36Sopenharmony_ci	 */
200262306a36Sopenharmony_ci	is_decrypted = (enctype != HTT_RX_MPDU_ENCRYPT_NONE &&
200362306a36Sopenharmony_ci			!has_fcs_err &&
200462306a36Sopenharmony_ci			!has_crypto_err &&
200562306a36Sopenharmony_ci			!has_peer_idx_invalid);
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	/* Clear per-MPDU flags while leaving per-PPDU flags intact. */
200862306a36Sopenharmony_ci	status->flag &= ~(RX_FLAG_FAILED_FCS_CRC |
200962306a36Sopenharmony_ci			  RX_FLAG_MMIC_ERROR |
201062306a36Sopenharmony_ci			  RX_FLAG_DECRYPTED |
201162306a36Sopenharmony_ci			  RX_FLAG_IV_STRIPPED |
201262306a36Sopenharmony_ci			  RX_FLAG_ONLY_MONITOR |
201362306a36Sopenharmony_ci			  RX_FLAG_MMIC_STRIPPED);
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	if (has_fcs_err)
201662306a36Sopenharmony_ci		status->flag |= RX_FLAG_FAILED_FCS_CRC;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	if (has_tkip_err)
201962306a36Sopenharmony_ci		status->flag |= RX_FLAG_MMIC_ERROR;
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	if (err) {
202262306a36Sopenharmony_ci		if (has_fcs_err)
202362306a36Sopenharmony_ci			*err = ATH10K_PKT_RX_ERR_FCS;
202462306a36Sopenharmony_ci		else if (has_tkip_err)
202562306a36Sopenharmony_ci			*err = ATH10K_PKT_RX_ERR_TKIP;
202662306a36Sopenharmony_ci		else if (has_crypto_err)
202762306a36Sopenharmony_ci			*err = ATH10K_PKT_RX_ERR_CRYPT;
202862306a36Sopenharmony_ci		else if (has_peer_idx_invalid)
202962306a36Sopenharmony_ci			*err = ATH10K_PKT_RX_ERR_PEER_IDX_INVAL;
203062306a36Sopenharmony_ci	}
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	/* Firmware reports all necessary management frames via WMI already.
203362306a36Sopenharmony_ci	 * They are not reported to monitor interfaces at all so pass the ones
203462306a36Sopenharmony_ci	 * coming via HTT to monitor interfaces instead. This simplifies
203562306a36Sopenharmony_ci	 * matters a lot.
203662306a36Sopenharmony_ci	 */
203762306a36Sopenharmony_ci	if (is_mgmt)
203862306a36Sopenharmony_ci		status->flag |= RX_FLAG_ONLY_MONITOR;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	if (is_decrypted) {
204162306a36Sopenharmony_ci		status->flag |= RX_FLAG_DECRYPTED;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci		if (likely(!is_mgmt))
204462306a36Sopenharmony_ci			status->flag |= RX_FLAG_MMIC_STRIPPED;
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci		if (fill_crypt_header)
204762306a36Sopenharmony_ci			status->flag |= RX_FLAG_MIC_STRIPPED |
204862306a36Sopenharmony_ci					RX_FLAG_ICV_STRIPPED;
204962306a36Sopenharmony_ci		else
205062306a36Sopenharmony_ci			status->flag |= RX_FLAG_IV_STRIPPED;
205162306a36Sopenharmony_ci	}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	skb_queue_walk(amsdu, msdu) {
205462306a36Sopenharmony_ci		if (frag && !fill_crypt_header && is_decrypted &&
205562306a36Sopenharmony_ci		    enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
205662306a36Sopenharmony_ci			frag_pn_check = ath10k_htt_rx_h_frag_pn_check(ar,
205762306a36Sopenharmony_ci								      msdu,
205862306a36Sopenharmony_ci								      peer_id,
205962306a36Sopenharmony_ci								      enctype);
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci		if (frag)
206262306a36Sopenharmony_ci			multicast_check = ath10k_htt_rx_h_frag_multicast_check(ar,
206362306a36Sopenharmony_ci									       msdu);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci		if (!frag_pn_check || !multicast_check) {
206662306a36Sopenharmony_ci			/* Discard the fragment with invalid PN or multicast DA
206762306a36Sopenharmony_ci			 */
206862306a36Sopenharmony_ci			temp = msdu->prev;
206962306a36Sopenharmony_ci			__skb_unlink(msdu, amsdu);
207062306a36Sopenharmony_ci			dev_kfree_skb_any(msdu);
207162306a36Sopenharmony_ci			msdu = temp;
207262306a36Sopenharmony_ci			frag_pn_check = true;
207362306a36Sopenharmony_ci			multicast_check = true;
207462306a36Sopenharmony_ci			continue;
207562306a36Sopenharmony_ci		}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci		ath10k_htt_rx_h_csum_offload(&ar->hw_params, msdu);
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci		if (frag && !fill_crypt_header &&
208062306a36Sopenharmony_ci		    enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
208162306a36Sopenharmony_ci			status->flag &= ~RX_FLAG_MMIC_STRIPPED;
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci		ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
208462306a36Sopenharmony_ci					is_decrypted);
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci		/* Undecapping involves copying the original 802.11 header back
208762306a36Sopenharmony_ci		 * to sk_buff. If frame is protected and hardware has decrypted
208862306a36Sopenharmony_ci		 * it then remove the protected bit.
208962306a36Sopenharmony_ci		 */
209062306a36Sopenharmony_ci		if (!is_decrypted)
209162306a36Sopenharmony_ci			continue;
209262306a36Sopenharmony_ci		if (is_mgmt)
209362306a36Sopenharmony_ci			continue;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci		if (fill_crypt_header)
209662306a36Sopenharmony_ci			continue;
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci		hdr = (void *)msdu->data;
209962306a36Sopenharmony_ci		hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci		if (frag && !fill_crypt_header &&
210262306a36Sopenharmony_ci		    enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
210362306a36Sopenharmony_ci			status->flag &= ~RX_FLAG_IV_STRIPPED &
210462306a36Sopenharmony_ci					~RX_FLAG_MMIC_STRIPPED;
210562306a36Sopenharmony_ci	}
210662306a36Sopenharmony_ci}
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_cistatic void ath10k_htt_rx_h_enqueue(struct ath10k *ar,
210962306a36Sopenharmony_ci				    struct sk_buff_head *amsdu,
211062306a36Sopenharmony_ci				    struct ieee80211_rx_status *status)
211162306a36Sopenharmony_ci{
211262306a36Sopenharmony_ci	struct sk_buff *msdu;
211362306a36Sopenharmony_ci	struct sk_buff *first_subframe;
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	first_subframe = skb_peek(amsdu);
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	while ((msdu = __skb_dequeue(amsdu))) {
211862306a36Sopenharmony_ci		/* Setup per-MSDU flags */
211962306a36Sopenharmony_ci		if (skb_queue_empty(amsdu))
212062306a36Sopenharmony_ci			status->flag &= ~RX_FLAG_AMSDU_MORE;
212162306a36Sopenharmony_ci		else
212262306a36Sopenharmony_ci			status->flag |= RX_FLAG_AMSDU_MORE;
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci		if (msdu == first_subframe) {
212562306a36Sopenharmony_ci			first_subframe = NULL;
212662306a36Sopenharmony_ci			status->flag &= ~RX_FLAG_ALLOW_SAME_PN;
212762306a36Sopenharmony_ci		} else {
212862306a36Sopenharmony_ci			status->flag |= RX_FLAG_ALLOW_SAME_PN;
212962306a36Sopenharmony_ci		}
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci		ath10k_htt_rx_h_queue_msdu(ar, status, msdu);
213262306a36Sopenharmony_ci	}
213362306a36Sopenharmony_ci}
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_cistatic int ath10k_unchain_msdu(struct sk_buff_head *amsdu,
213662306a36Sopenharmony_ci			       unsigned long *unchain_cnt)
213762306a36Sopenharmony_ci{
213862306a36Sopenharmony_ci	struct sk_buff *skb, *first;
213962306a36Sopenharmony_ci	int space;
214062306a36Sopenharmony_ci	int total_len = 0;
214162306a36Sopenharmony_ci	int amsdu_len = skb_queue_len(amsdu);
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	/* TODO:  Might could optimize this by using
214462306a36Sopenharmony_ci	 * skb_try_coalesce or similar method to
214562306a36Sopenharmony_ci	 * decrease copying, or maybe get mac80211 to
214662306a36Sopenharmony_ci	 * provide a way to just receive a list of
214762306a36Sopenharmony_ci	 * skb?
214862306a36Sopenharmony_ci	 */
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	first = __skb_dequeue(amsdu);
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	/* Allocate total length all at once. */
215362306a36Sopenharmony_ci	skb_queue_walk(amsdu, skb)
215462306a36Sopenharmony_ci		total_len += skb->len;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	space = total_len - skb_tailroom(first);
215762306a36Sopenharmony_ci	if ((space > 0) &&
215862306a36Sopenharmony_ci	    (pskb_expand_head(first, 0, space, GFP_ATOMIC) < 0)) {
215962306a36Sopenharmony_ci		/* TODO:  bump some rx-oom error stat */
216062306a36Sopenharmony_ci		/* put it back together so we can free the
216162306a36Sopenharmony_ci		 * whole list at once.
216262306a36Sopenharmony_ci		 */
216362306a36Sopenharmony_ci		__skb_queue_head(amsdu, first);
216462306a36Sopenharmony_ci		return -1;
216562306a36Sopenharmony_ci	}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	/* Walk list again, copying contents into
216862306a36Sopenharmony_ci	 * msdu_head
216962306a36Sopenharmony_ci	 */
217062306a36Sopenharmony_ci	while ((skb = __skb_dequeue(amsdu))) {
217162306a36Sopenharmony_ci		skb_copy_from_linear_data(skb, skb_put(first, skb->len),
217262306a36Sopenharmony_ci					  skb->len);
217362306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	__skb_queue_head(amsdu, first);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	*unchain_cnt += amsdu_len - 1;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	return 0;
218162306a36Sopenharmony_ci}
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_cistatic void ath10k_htt_rx_h_unchain(struct ath10k *ar,
218462306a36Sopenharmony_ci				    struct sk_buff_head *amsdu,
218562306a36Sopenharmony_ci				    unsigned long *drop_cnt,
218662306a36Sopenharmony_ci				    unsigned long *unchain_cnt)
218762306a36Sopenharmony_ci{
218862306a36Sopenharmony_ci	struct sk_buff *first;
218962306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
219062306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
219162306a36Sopenharmony_ci	struct rx_msdu_start_common *rxd_msdu_start_common;
219262306a36Sopenharmony_ci	struct rx_frag_info_common *rxd_frag_info;
219362306a36Sopenharmony_ci	enum rx_msdu_decap_format decap;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	first = skb_peek(amsdu);
219662306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
219762306a36Sopenharmony_ci				    (void *)first->data - hw->rx_desc_ops->rx_desc_size);
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci	rxd_msdu_start_common = ath10k_htt_rx_desc_get_msdu_start(hw, rxd);
220062306a36Sopenharmony_ci	rxd_frag_info = ath10k_htt_rx_desc_get_frag_info(hw, rxd);
220162306a36Sopenharmony_ci	decap = MS(__le32_to_cpu(rxd_msdu_start_common->info1),
220262306a36Sopenharmony_ci		   RX_MSDU_START_INFO1_DECAP_FORMAT);
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	/* FIXME: Current unchaining logic can only handle simple case of raw
220562306a36Sopenharmony_ci	 * msdu chaining. If decapping is other than raw the chaining may be
220662306a36Sopenharmony_ci	 * more complex and this isn't handled by the current code. Don't even
220762306a36Sopenharmony_ci	 * try re-constructing such frames - it'll be pretty much garbage.
220862306a36Sopenharmony_ci	 */
220962306a36Sopenharmony_ci	if (decap != RX_MSDU_DECAP_RAW ||
221062306a36Sopenharmony_ci	    skb_queue_len(amsdu) != 1 + rxd_frag_info->ring2_more_count) {
221162306a36Sopenharmony_ci		*drop_cnt += skb_queue_len(amsdu);
221262306a36Sopenharmony_ci		__skb_queue_purge(amsdu);
221362306a36Sopenharmony_ci		return;
221462306a36Sopenharmony_ci	}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	ath10k_unchain_msdu(amsdu, unchain_cnt);
221762306a36Sopenharmony_ci}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_cistatic bool ath10k_htt_rx_validate_amsdu(struct ath10k *ar,
222062306a36Sopenharmony_ci					 struct sk_buff_head *amsdu)
222162306a36Sopenharmony_ci{
222262306a36Sopenharmony_ci	u8 *subframe_hdr;
222362306a36Sopenharmony_ci	struct sk_buff *first;
222462306a36Sopenharmony_ci	bool is_first, is_last;
222562306a36Sopenharmony_ci	struct ath10k_hw_params *hw = &ar->hw_params;
222662306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
222762306a36Sopenharmony_ci	struct rx_msdu_end_common *rxd_msdu_end_common;
222862306a36Sopenharmony_ci	struct rx_mpdu_start *rxd_mpdu_start;
222962306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
223062306a36Sopenharmony_ci	size_t hdr_len, crypto_len;
223162306a36Sopenharmony_ci	enum htt_rx_mpdu_encrypt_type enctype;
223262306a36Sopenharmony_ci	int bytes_aligned = ar->hw_params.decap_align_bytes;
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci	first = skb_peek(amsdu);
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
223762306a36Sopenharmony_ci				    (void *)first->data - hw->rx_desc_ops->rx_desc_size);
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd);
224062306a36Sopenharmony_ci	rxd_mpdu_start = ath10k_htt_rx_desc_get_mpdu_start(hw, rxd);
224162306a36Sopenharmony_ci	hdr = (void *)ath10k_htt_rx_desc_get_rx_hdr_status(hw, rxd);
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	is_first = !!(rxd_msdu_end_common->info0 &
224462306a36Sopenharmony_ci		      __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
224562306a36Sopenharmony_ci	is_last = !!(rxd_msdu_end_common->info0 &
224662306a36Sopenharmony_ci		     __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	/* Return in case of non-aggregated msdu */
224962306a36Sopenharmony_ci	if (is_first && is_last)
225062306a36Sopenharmony_ci		return true;
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	/* First msdu flag is not set for the first msdu of the list */
225362306a36Sopenharmony_ci	if (!is_first)
225462306a36Sopenharmony_ci		return false;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	enctype = MS(__le32_to_cpu(rxd_mpdu_start->info0),
225762306a36Sopenharmony_ci		     RX_MPDU_START_INFO0_ENCRYPT_TYPE);
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	hdr_len = ieee80211_hdrlen(hdr->frame_control);
226062306a36Sopenharmony_ci	crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	subframe_hdr = (u8 *)hdr + round_up(hdr_len, bytes_aligned) +
226362306a36Sopenharmony_ci		       crypto_len;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	/* Validate if the amsdu has a proper first subframe.
226662306a36Sopenharmony_ci	 * There are chances a single msdu can be received as amsdu when
226762306a36Sopenharmony_ci	 * the unauthenticated amsdu flag of a QoS header
226862306a36Sopenharmony_ci	 * gets flipped in non-SPP AMSDU's, in such cases the first
226962306a36Sopenharmony_ci	 * subframe has llc/snap header in place of a valid da.
227062306a36Sopenharmony_ci	 * return false if the da matches rfc1042 pattern
227162306a36Sopenharmony_ci	 */
227262306a36Sopenharmony_ci	if (ether_addr_equal(subframe_hdr, rfc1042_header))
227362306a36Sopenharmony_ci		return false;
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	return true;
227662306a36Sopenharmony_ci}
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_cistatic bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
227962306a36Sopenharmony_ci					struct sk_buff_head *amsdu,
228062306a36Sopenharmony_ci					struct ieee80211_rx_status *rx_status)
228162306a36Sopenharmony_ci{
228262306a36Sopenharmony_ci	if (!rx_status->freq) {
228362306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "no channel configured; ignoring frame(s)!\n");
228462306a36Sopenharmony_ci		return false;
228562306a36Sopenharmony_ci	}
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci	if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
228862306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx cac running\n");
228962306a36Sopenharmony_ci		return false;
229062306a36Sopenharmony_ci	}
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	if (!ath10k_htt_rx_validate_amsdu(ar, amsdu)) {
229362306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid amsdu received\n");
229462306a36Sopenharmony_ci		return false;
229562306a36Sopenharmony_ci	}
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	return true;
229862306a36Sopenharmony_ci}
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_cistatic void ath10k_htt_rx_h_filter(struct ath10k *ar,
230162306a36Sopenharmony_ci				   struct sk_buff_head *amsdu,
230262306a36Sopenharmony_ci				   struct ieee80211_rx_status *rx_status,
230362306a36Sopenharmony_ci				   unsigned long *drop_cnt)
230462306a36Sopenharmony_ci{
230562306a36Sopenharmony_ci	if (skb_queue_empty(amsdu))
230662306a36Sopenharmony_ci		return;
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status))
230962306a36Sopenharmony_ci		return;
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	if (drop_cnt)
231262306a36Sopenharmony_ci		*drop_cnt += skb_queue_len(amsdu);
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	__skb_queue_purge(amsdu);
231562306a36Sopenharmony_ci}
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_cistatic int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
231862306a36Sopenharmony_ci{
231962306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
232062306a36Sopenharmony_ci	struct ieee80211_rx_status *rx_status = &htt->rx_status;
232162306a36Sopenharmony_ci	struct sk_buff_head amsdu;
232262306a36Sopenharmony_ci	int ret;
232362306a36Sopenharmony_ci	unsigned long drop_cnt = 0;
232462306a36Sopenharmony_ci	unsigned long unchain_cnt = 0;
232562306a36Sopenharmony_ci	unsigned long drop_cnt_filter = 0;
232662306a36Sopenharmony_ci	unsigned long msdus_to_queue, num_msdus;
232762306a36Sopenharmony_ci	enum ath10k_pkt_rx_err err = ATH10K_PKT_RX_ERR_MAX;
232862306a36Sopenharmony_ci	u8 first_hdr[RX_HTT_HDR_STATUS_LEN];
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	__skb_queue_head_init(&amsdu);
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	spin_lock_bh(&htt->rx_ring.lock);
233362306a36Sopenharmony_ci	if (htt->rx_confused) {
233462306a36Sopenharmony_ci		spin_unlock_bh(&htt->rx_ring.lock);
233562306a36Sopenharmony_ci		return -EIO;
233662306a36Sopenharmony_ci	}
233762306a36Sopenharmony_ci	ret = ath10k_htt_rx_amsdu_pop(htt, &amsdu);
233862306a36Sopenharmony_ci	spin_unlock_bh(&htt->rx_ring.lock);
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci	if (ret < 0) {
234162306a36Sopenharmony_ci		ath10k_warn(ar, "rx ring became corrupted: %d\n", ret);
234262306a36Sopenharmony_ci		__skb_queue_purge(&amsdu);
234362306a36Sopenharmony_ci		/* FIXME: It's probably a good idea to reboot the
234462306a36Sopenharmony_ci		 * device instead of leaving it inoperable.
234562306a36Sopenharmony_ci		 */
234662306a36Sopenharmony_ci		htt->rx_confused = true;
234762306a36Sopenharmony_ci		return ret;
234862306a36Sopenharmony_ci	}
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	num_msdus = skb_queue_len(&amsdu);
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	/* only for ret = 1 indicates chained msdus */
235562306a36Sopenharmony_ci	if (ret > 0)
235662306a36Sopenharmony_ci		ath10k_htt_rx_h_unchain(ar, &amsdu, &drop_cnt, &unchain_cnt);
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	ath10k_htt_rx_h_filter(ar, &amsdu, rx_status, &drop_cnt_filter);
235962306a36Sopenharmony_ci	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err, 0,
236062306a36Sopenharmony_ci			     false);
236162306a36Sopenharmony_ci	msdus_to_queue = skb_queue_len(&amsdu);
236262306a36Sopenharmony_ci	ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status);
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	ath10k_sta_update_rx_tid_stats(ar, first_hdr, num_msdus, err,
236562306a36Sopenharmony_ci				       unchain_cnt, drop_cnt, drop_cnt_filter,
236662306a36Sopenharmony_ci				       msdus_to_queue);
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	return 0;
236962306a36Sopenharmony_ci}
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_cistatic void ath10k_htt_rx_mpdu_desc_pn_hl(struct htt_hl_rx_desc *rx_desc,
237262306a36Sopenharmony_ci					  union htt_rx_pn_t *pn,
237362306a36Sopenharmony_ci					  int pn_len_bits)
237462306a36Sopenharmony_ci{
237562306a36Sopenharmony_ci	switch (pn_len_bits) {
237662306a36Sopenharmony_ci	case 48:
237762306a36Sopenharmony_ci		pn->pn48 = __le32_to_cpu(rx_desc->pn_31_0) +
237862306a36Sopenharmony_ci			   ((u64)(__le32_to_cpu(rx_desc->u0.pn_63_32) & 0xFFFF) << 32);
237962306a36Sopenharmony_ci		break;
238062306a36Sopenharmony_ci	case 24:
238162306a36Sopenharmony_ci		pn->pn24 = __le32_to_cpu(rx_desc->pn_31_0);
238262306a36Sopenharmony_ci		break;
238362306a36Sopenharmony_ci	}
238462306a36Sopenharmony_ci}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_cistatic bool ath10k_htt_rx_pn_cmp48(union htt_rx_pn_t *new_pn,
238762306a36Sopenharmony_ci				   union htt_rx_pn_t *old_pn)
238862306a36Sopenharmony_ci{
238962306a36Sopenharmony_ci	return ((new_pn->pn48 & 0xffffffffffffULL) <=
239062306a36Sopenharmony_ci		(old_pn->pn48 & 0xffffffffffffULL));
239162306a36Sopenharmony_ci}
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_cistatic bool ath10k_htt_rx_pn_check_replay_hl(struct ath10k *ar,
239462306a36Sopenharmony_ci					     struct ath10k_peer *peer,
239562306a36Sopenharmony_ci					     struct htt_rx_indication_hl *rx)
239662306a36Sopenharmony_ci{
239762306a36Sopenharmony_ci	bool last_pn_valid, pn_invalid = false;
239862306a36Sopenharmony_ci	enum htt_txrx_sec_cast_type sec_index;
239962306a36Sopenharmony_ci	enum htt_security_types sec_type;
240062306a36Sopenharmony_ci	union htt_rx_pn_t new_pn = {0};
240162306a36Sopenharmony_ci	struct htt_hl_rx_desc *rx_desc;
240262306a36Sopenharmony_ci	union htt_rx_pn_t *last_pn;
240362306a36Sopenharmony_ci	u32 rx_desc_info, tid;
240462306a36Sopenharmony_ci	int num_mpdu_ranges;
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	if (!peer)
240962306a36Sopenharmony_ci		return false;
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	if (!(rx->fw_desc.flags & FW_RX_DESC_FLAGS_FIRST_MSDU))
241262306a36Sopenharmony_ci		return false;
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
241562306a36Sopenharmony_ci			     HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	rx_desc = (struct htt_hl_rx_desc *)&rx->mpdu_ranges[num_mpdu_ranges];
241862306a36Sopenharmony_ci	rx_desc_info = __le32_to_cpu(rx_desc->info);
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED))
242162306a36Sopenharmony_ci		return false;
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	tid = MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
242462306a36Sopenharmony_ci	last_pn_valid = peer->tids_last_pn_valid[tid];
242562306a36Sopenharmony_ci	last_pn = &peer->tids_last_pn[tid];
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	if (MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST))
242862306a36Sopenharmony_ci		sec_index = HTT_TXRX_SEC_MCAST;
242962306a36Sopenharmony_ci	else
243062306a36Sopenharmony_ci		sec_index = HTT_TXRX_SEC_UCAST;
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	sec_type = peer->rx_pn[sec_index].sec_type;
243362306a36Sopenharmony_ci	ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len);
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	if (sec_type != HTT_SECURITY_AES_CCMP &&
243662306a36Sopenharmony_ci	    sec_type != HTT_SECURITY_TKIP &&
243762306a36Sopenharmony_ci	    sec_type != HTT_SECURITY_TKIP_NOMIC)
243862306a36Sopenharmony_ci		return false;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	if (last_pn_valid)
244162306a36Sopenharmony_ci		pn_invalid = ath10k_htt_rx_pn_cmp48(&new_pn, last_pn);
244262306a36Sopenharmony_ci	else
244362306a36Sopenharmony_ci		peer->tids_last_pn_valid[tid] = true;
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	if (!pn_invalid)
244662306a36Sopenharmony_ci		last_pn->pn48 = new_pn.pn48;
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	return pn_invalid;
244962306a36Sopenharmony_ci}
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_cistatic bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
245262306a36Sopenharmony_ci					 struct htt_rx_indication_hl *rx,
245362306a36Sopenharmony_ci					 struct sk_buff *skb,
245462306a36Sopenharmony_ci					 enum htt_rx_pn_check_type check_pn_type,
245562306a36Sopenharmony_ci					 enum htt_rx_tkip_demic_type tkip_mic_type)
245662306a36Sopenharmony_ci{
245762306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
245862306a36Sopenharmony_ci	struct ath10k_peer *peer;
245962306a36Sopenharmony_ci	struct htt_rx_indication_mpdu_range *mpdu_ranges;
246062306a36Sopenharmony_ci	struct fw_rx_desc_hl *fw_desc;
246162306a36Sopenharmony_ci	enum htt_txrx_sec_cast_type sec_index;
246262306a36Sopenharmony_ci	enum htt_security_types sec_type;
246362306a36Sopenharmony_ci	union htt_rx_pn_t new_pn = {0};
246462306a36Sopenharmony_ci	struct htt_hl_rx_desc *rx_desc;
246562306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
246662306a36Sopenharmony_ci	struct ieee80211_rx_status *rx_status;
246762306a36Sopenharmony_ci	u16 peer_id;
246862306a36Sopenharmony_ci	u8 rx_desc_len;
246962306a36Sopenharmony_ci	int num_mpdu_ranges;
247062306a36Sopenharmony_ci	size_t tot_hdr_len;
247162306a36Sopenharmony_ci	struct ieee80211_channel *ch;
247262306a36Sopenharmony_ci	bool pn_invalid, qos, first_msdu;
247362306a36Sopenharmony_ci	u32 tid, rx_desc_info;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	peer_id = __le16_to_cpu(rx->hdr.peer_id);
247662306a36Sopenharmony_ci	tid = MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
247962306a36Sopenharmony_ci	peer = ath10k_peer_find_by_id(ar, peer_id);
248062306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
248162306a36Sopenharmony_ci	if (!peer && peer_id != HTT_INVALID_PEERID)
248262306a36Sopenharmony_ci		ath10k_warn(ar, "Got RX ind from invalid peer: %u\n", peer_id);
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci	if (!peer)
248562306a36Sopenharmony_ci		return true;
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci	num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
248862306a36Sopenharmony_ci			     HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
248962306a36Sopenharmony_ci	mpdu_ranges = htt_rx_ind_get_mpdu_ranges_hl(rx);
249062306a36Sopenharmony_ci	fw_desc = &rx->fw_desc;
249162306a36Sopenharmony_ci	rx_desc_len = fw_desc->len;
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	if (fw_desc->u.bits.discard) {
249462306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "htt discard mpdu\n");
249562306a36Sopenharmony_ci		goto err;
249662306a36Sopenharmony_ci	}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	/* I have not yet seen any case where num_mpdu_ranges > 1.
249962306a36Sopenharmony_ci	 * qcacld does not seem handle that case either, so we introduce the
250062306a36Sopenharmony_ci	 * same limitation here as well.
250162306a36Sopenharmony_ci	 */
250262306a36Sopenharmony_ci	if (num_mpdu_ranges > 1)
250362306a36Sopenharmony_ci		ath10k_warn(ar,
250462306a36Sopenharmony_ci			    "Unsupported number of MPDU ranges: %d, ignoring all but the first\n",
250562306a36Sopenharmony_ci			    num_mpdu_ranges);
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	if (mpdu_ranges->mpdu_range_status !=
250862306a36Sopenharmony_ci	    HTT_RX_IND_MPDU_STATUS_OK &&
250962306a36Sopenharmony_ci	    mpdu_ranges->mpdu_range_status !=
251062306a36Sopenharmony_ci	    HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) {
251162306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "htt mpdu_range_status %d\n",
251262306a36Sopenharmony_ci			   mpdu_ranges->mpdu_range_status);
251362306a36Sopenharmony_ci		goto err;
251462306a36Sopenharmony_ci	}
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	rx_desc = (struct htt_hl_rx_desc *)&rx->mpdu_ranges[num_mpdu_ranges];
251762306a36Sopenharmony_ci	rx_desc_info = __le32_to_cpu(rx_desc->info);
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	if (MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST))
252062306a36Sopenharmony_ci		sec_index = HTT_TXRX_SEC_MCAST;
252162306a36Sopenharmony_ci	else
252262306a36Sopenharmony_ci		sec_index = HTT_TXRX_SEC_UCAST;
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci	sec_type = peer->rx_pn[sec_index].sec_type;
252562306a36Sopenharmony_ci	first_msdu = rx->fw_desc.flags & FW_RX_DESC_FLAGS_FIRST_MSDU;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len);
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	if (check_pn_type == HTT_RX_PN_CHECK && tid >= IEEE80211_NUM_TIDS) {
253062306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
253162306a36Sopenharmony_ci		pn_invalid = ath10k_htt_rx_pn_check_replay_hl(ar, peer, rx);
253262306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci		if (pn_invalid)
253562306a36Sopenharmony_ci			goto err;
253662306a36Sopenharmony_ci	}
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	/* Strip off all headers before the MAC header before delivery to
253962306a36Sopenharmony_ci	 * mac80211
254062306a36Sopenharmony_ci	 */
254162306a36Sopenharmony_ci	tot_hdr_len = sizeof(struct htt_resp_hdr) + sizeof(rx->hdr) +
254262306a36Sopenharmony_ci		      sizeof(rx->ppdu) + sizeof(rx->prefix) +
254362306a36Sopenharmony_ci		      sizeof(rx->fw_desc) +
254462306a36Sopenharmony_ci		      sizeof(*mpdu_ranges) * num_mpdu_ranges + rx_desc_len;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	skb_pull(skb, tot_hdr_len);
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)skb->data;
254962306a36Sopenharmony_ci	qos = ieee80211_is_data_qos(hdr->frame_control);
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	rx_status = IEEE80211_SKB_RXCB(skb);
255262306a36Sopenharmony_ci	memset(rx_status, 0, sizeof(*rx_status));
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	if (rx->ppdu.combined_rssi == 0) {
255562306a36Sopenharmony_ci		/* SDIO firmware does not provide signal */
255662306a36Sopenharmony_ci		rx_status->signal = 0;
255762306a36Sopenharmony_ci		rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
255862306a36Sopenharmony_ci	} else {
255962306a36Sopenharmony_ci		rx_status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
256062306a36Sopenharmony_ci			rx->ppdu.combined_rssi;
256162306a36Sopenharmony_ci		rx_status->flag &= ~RX_FLAG_NO_SIGNAL_VAL;
256262306a36Sopenharmony_ci	}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
256562306a36Sopenharmony_ci	ch = ar->scan_channel;
256662306a36Sopenharmony_ci	if (!ch)
256762306a36Sopenharmony_ci		ch = ar->rx_channel;
256862306a36Sopenharmony_ci	if (!ch)
256962306a36Sopenharmony_ci		ch = ath10k_htt_rx_h_any_channel(ar);
257062306a36Sopenharmony_ci	if (!ch)
257162306a36Sopenharmony_ci		ch = ar->tgt_oper_chan;
257262306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	if (ch) {
257562306a36Sopenharmony_ci		rx_status->band = ch->band;
257662306a36Sopenharmony_ci		rx_status->freq = ch->center_freq;
257762306a36Sopenharmony_ci	}
257862306a36Sopenharmony_ci	if (rx->fw_desc.flags & FW_RX_DESC_FLAGS_LAST_MSDU)
257962306a36Sopenharmony_ci		rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
258062306a36Sopenharmony_ci	else
258162306a36Sopenharmony_ci		rx_status->flag |= RX_FLAG_AMSDU_MORE;
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	/* Not entirely sure about this, but all frames from the chipset has
258462306a36Sopenharmony_ci	 * the protected flag set even though they have already been decrypted.
258562306a36Sopenharmony_ci	 * Unmasking this flag is necessary in order for mac80211 not to drop
258662306a36Sopenharmony_ci	 * the frame.
258762306a36Sopenharmony_ci	 * TODO: Verify this is always the case or find out a way to check
258862306a36Sopenharmony_ci	 * if there has been hw decryption.
258962306a36Sopenharmony_ci	 */
259062306a36Sopenharmony_ci	if (ieee80211_has_protected(hdr->frame_control)) {
259162306a36Sopenharmony_ci		hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
259262306a36Sopenharmony_ci		rx_status->flag |= RX_FLAG_DECRYPTED |
259362306a36Sopenharmony_ci				   RX_FLAG_IV_STRIPPED |
259462306a36Sopenharmony_ci				   RX_FLAG_MMIC_STRIPPED;
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci		if (tid < IEEE80211_NUM_TIDS &&
259762306a36Sopenharmony_ci		    first_msdu &&
259862306a36Sopenharmony_ci		    check_pn_type == HTT_RX_PN_CHECK &&
259962306a36Sopenharmony_ci		   (sec_type == HTT_SECURITY_AES_CCMP ||
260062306a36Sopenharmony_ci		    sec_type == HTT_SECURITY_TKIP ||
260162306a36Sopenharmony_ci		    sec_type == HTT_SECURITY_TKIP_NOMIC)) {
260262306a36Sopenharmony_ci			u8 offset, *ivp, i;
260362306a36Sopenharmony_ci			s8 keyidx = 0;
260462306a36Sopenharmony_ci			__le64 pn48 = cpu_to_le64(new_pn.pn48);
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci			hdr = (struct ieee80211_hdr *)skb->data;
260762306a36Sopenharmony_ci			offset = ieee80211_hdrlen(hdr->frame_control);
260862306a36Sopenharmony_ci			hdr->frame_control |= __cpu_to_le16(IEEE80211_FCTL_PROTECTED);
260962306a36Sopenharmony_ci			rx_status->flag &= ~RX_FLAG_IV_STRIPPED;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci			memmove(skb->data - IEEE80211_CCMP_HDR_LEN,
261262306a36Sopenharmony_ci				skb->data, offset);
261362306a36Sopenharmony_ci			skb_push(skb, IEEE80211_CCMP_HDR_LEN);
261462306a36Sopenharmony_ci			ivp = skb->data + offset;
261562306a36Sopenharmony_ci			memset(skb->data + offset, 0, IEEE80211_CCMP_HDR_LEN);
261662306a36Sopenharmony_ci			/* Ext IV */
261762306a36Sopenharmony_ci			ivp[IEEE80211_WEP_IV_LEN - 1] |= ATH10K_IEEE80211_EXTIV;
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci			for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
262062306a36Sopenharmony_ci				if (peer->keys[i] &&
262162306a36Sopenharmony_ci				    peer->keys[i]->flags & IEEE80211_KEY_FLAG_PAIRWISE)
262262306a36Sopenharmony_ci					keyidx = peer->keys[i]->keyidx;
262362306a36Sopenharmony_ci			}
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci			/* Key ID */
262662306a36Sopenharmony_ci			ivp[IEEE80211_WEP_IV_LEN - 1] |= keyidx << 6;
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci			if (sec_type == HTT_SECURITY_AES_CCMP) {
262962306a36Sopenharmony_ci				rx_status->flag |= RX_FLAG_MIC_STRIPPED;
263062306a36Sopenharmony_ci				/* pn 0, pn 1 */
263162306a36Sopenharmony_ci				memcpy(skb->data + offset, &pn48, 2);
263262306a36Sopenharmony_ci				/* pn 1, pn 3 , pn 34 , pn 5 */
263362306a36Sopenharmony_ci				memcpy(skb->data + offset + 4, ((u8 *)&pn48) + 2, 4);
263462306a36Sopenharmony_ci			} else {
263562306a36Sopenharmony_ci				rx_status->flag |= RX_FLAG_ICV_STRIPPED;
263662306a36Sopenharmony_ci				/* TSC 0 */
263762306a36Sopenharmony_ci				memcpy(skb->data + offset + 2, &pn48, 1);
263862306a36Sopenharmony_ci				/* TSC 1 */
263962306a36Sopenharmony_ci				memcpy(skb->data + offset, ((u8 *)&pn48) + 1, 1);
264062306a36Sopenharmony_ci				/* TSC 2 , TSC 3 , TSC 4 , TSC 5*/
264162306a36Sopenharmony_ci				memcpy(skb->data + offset + 4, ((u8 *)&pn48) + 2, 4);
264262306a36Sopenharmony_ci			}
264362306a36Sopenharmony_ci		}
264462306a36Sopenharmony_ci	}
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	if (tkip_mic_type == HTT_RX_TKIP_MIC)
264762306a36Sopenharmony_ci		rx_status->flag &= ~RX_FLAG_IV_STRIPPED &
264862306a36Sopenharmony_ci				   ~RX_FLAG_MMIC_STRIPPED;
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	if (mpdu_ranges->mpdu_range_status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR)
265162306a36Sopenharmony_ci		rx_status->flag |= RX_FLAG_MMIC_ERROR;
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci	if (!qos && tid < IEEE80211_NUM_TIDS) {
265462306a36Sopenharmony_ci		u8 offset;
265562306a36Sopenharmony_ci		__le16 qos_ctrl = 0;
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci		hdr = (struct ieee80211_hdr *)skb->data;
265862306a36Sopenharmony_ci		offset = ieee80211_hdrlen(hdr->frame_control);
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_ci		hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
266162306a36Sopenharmony_ci		memmove(skb->data - IEEE80211_QOS_CTL_LEN, skb->data, offset);
266262306a36Sopenharmony_ci		skb_push(skb, IEEE80211_QOS_CTL_LEN);
266362306a36Sopenharmony_ci		qos_ctrl = cpu_to_le16(tid);
266462306a36Sopenharmony_ci		memcpy(skb->data + offset, &qos_ctrl, IEEE80211_QOS_CTL_LEN);
266562306a36Sopenharmony_ci	}
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	if (ar->napi.dev)
266862306a36Sopenharmony_ci		ieee80211_rx_napi(ar->hw, NULL, skb, &ar->napi);
266962306a36Sopenharmony_ci	else
267062306a36Sopenharmony_ci		ieee80211_rx_ni(ar->hw, skb);
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	/* We have delivered the skb to the upper layers (mac80211) so we
267362306a36Sopenharmony_ci	 * must not free it.
267462306a36Sopenharmony_ci	 */
267562306a36Sopenharmony_ci	return false;
267662306a36Sopenharmony_cierr:
267762306a36Sopenharmony_ci	/* Tell the caller that it must free the skb since we have not
267862306a36Sopenharmony_ci	 * consumed it
267962306a36Sopenharmony_ci	 */
268062306a36Sopenharmony_ci	return true;
268162306a36Sopenharmony_ci}
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_cistatic int ath10k_htt_rx_frag_tkip_decap_nomic(struct sk_buff *skb,
268462306a36Sopenharmony_ci					       u16 head_len,
268562306a36Sopenharmony_ci					       u16 hdr_len)
268662306a36Sopenharmony_ci{
268762306a36Sopenharmony_ci	u8 *ivp, *orig_hdr;
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	orig_hdr = skb->data;
269062306a36Sopenharmony_ci	ivp = orig_hdr + hdr_len + head_len;
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci	/* the ExtIV bit is always set to 1 for TKIP */
269362306a36Sopenharmony_ci	if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV))
269462306a36Sopenharmony_ci		return -EINVAL;
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	memmove(orig_hdr + IEEE80211_TKIP_IV_LEN, orig_hdr, head_len + hdr_len);
269762306a36Sopenharmony_ci	skb_pull(skb, IEEE80211_TKIP_IV_LEN);
269862306a36Sopenharmony_ci	skb_trim(skb, skb->len - ATH10K_IEEE80211_TKIP_MICLEN);
269962306a36Sopenharmony_ci	return 0;
270062306a36Sopenharmony_ci}
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_cistatic int ath10k_htt_rx_frag_tkip_decap_withmic(struct sk_buff *skb,
270362306a36Sopenharmony_ci						 u16 head_len,
270462306a36Sopenharmony_ci						 u16 hdr_len)
270562306a36Sopenharmony_ci{
270662306a36Sopenharmony_ci	u8 *ivp, *orig_hdr;
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_ci	orig_hdr = skb->data;
270962306a36Sopenharmony_ci	ivp = orig_hdr + hdr_len + head_len;
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	/* the ExtIV bit is always set to 1 for TKIP */
271262306a36Sopenharmony_ci	if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV))
271362306a36Sopenharmony_ci		return -EINVAL;
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	memmove(orig_hdr + IEEE80211_TKIP_IV_LEN, orig_hdr, head_len + hdr_len);
271662306a36Sopenharmony_ci	skb_pull(skb, IEEE80211_TKIP_IV_LEN);
271762306a36Sopenharmony_ci	skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
271862306a36Sopenharmony_ci	return 0;
271962306a36Sopenharmony_ci}
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_cistatic int ath10k_htt_rx_frag_ccmp_decap(struct sk_buff *skb,
272262306a36Sopenharmony_ci					 u16 head_len,
272362306a36Sopenharmony_ci					 u16 hdr_len)
272462306a36Sopenharmony_ci{
272562306a36Sopenharmony_ci	u8 *ivp, *orig_hdr;
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	orig_hdr = skb->data;
272862306a36Sopenharmony_ci	ivp = orig_hdr + hdr_len + head_len;
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	/* the ExtIV bit is always set to 1 for CCMP */
273162306a36Sopenharmony_ci	if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV))
273262306a36Sopenharmony_ci		return -EINVAL;
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci	skb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN);
273562306a36Sopenharmony_ci	memmove(orig_hdr + IEEE80211_CCMP_HDR_LEN, orig_hdr, head_len + hdr_len);
273662306a36Sopenharmony_ci	skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
273762306a36Sopenharmony_ci	return 0;
273862306a36Sopenharmony_ci}
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_cistatic int ath10k_htt_rx_frag_wep_decap(struct sk_buff *skb,
274162306a36Sopenharmony_ci					u16 head_len,
274262306a36Sopenharmony_ci					u16 hdr_len)
274362306a36Sopenharmony_ci{
274462306a36Sopenharmony_ci	u8 *orig_hdr;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	orig_hdr = skb->data;
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	memmove(orig_hdr + IEEE80211_WEP_IV_LEN,
274962306a36Sopenharmony_ci		orig_hdr, head_len + hdr_len);
275062306a36Sopenharmony_ci	skb_pull(skb, IEEE80211_WEP_IV_LEN);
275162306a36Sopenharmony_ci	skb_trim(skb, skb->len - IEEE80211_WEP_ICV_LEN);
275262306a36Sopenharmony_ci	return 0;
275362306a36Sopenharmony_ci}
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_cistatic bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt,
275662306a36Sopenharmony_ci					      struct htt_rx_fragment_indication *rx,
275762306a36Sopenharmony_ci					      struct sk_buff *skb)
275862306a36Sopenharmony_ci{
275962306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
276062306a36Sopenharmony_ci	enum htt_rx_tkip_demic_type tkip_mic = HTT_RX_NON_TKIP_MIC;
276162306a36Sopenharmony_ci	enum htt_txrx_sec_cast_type sec_index;
276262306a36Sopenharmony_ci	struct htt_rx_indication_hl *rx_hl;
276362306a36Sopenharmony_ci	enum htt_security_types sec_type;
276462306a36Sopenharmony_ci	u32 tid, frag, seq, rx_desc_info;
276562306a36Sopenharmony_ci	union htt_rx_pn_t new_pn = {0};
276662306a36Sopenharmony_ci	struct htt_hl_rx_desc *rx_desc;
276762306a36Sopenharmony_ci	u16 peer_id, sc, hdr_space;
276862306a36Sopenharmony_ci	union htt_rx_pn_t *last_pn;
276962306a36Sopenharmony_ci	struct ieee80211_hdr *hdr;
277062306a36Sopenharmony_ci	int ret, num_mpdu_ranges;
277162306a36Sopenharmony_ci	struct ath10k_peer *peer;
277262306a36Sopenharmony_ci	struct htt_resp *resp;
277362306a36Sopenharmony_ci	size_t tot_hdr_len;
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	resp = (struct htt_resp *)(skb->data + HTT_RX_FRAG_IND_INFO0_HEADER_LEN);
277662306a36Sopenharmony_ci	skb_pull(skb, HTT_RX_FRAG_IND_INFO0_HEADER_LEN);
277762306a36Sopenharmony_ci	skb_trim(skb, skb->len - FCS_LEN);
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci	peer_id = __le16_to_cpu(rx->peer_id);
278062306a36Sopenharmony_ci	rx_hl = (struct htt_rx_indication_hl *)(&resp->rx_ind_hl);
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
278362306a36Sopenharmony_ci	peer = ath10k_peer_find_by_id(ar, peer_id);
278462306a36Sopenharmony_ci	if (!peer) {
278562306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer: %u\n", peer_id);
278662306a36Sopenharmony_ci		goto err;
278762306a36Sopenharmony_ci	}
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	num_mpdu_ranges = MS(__le32_to_cpu(rx_hl->hdr.info1),
279062306a36Sopenharmony_ci			     HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci	tot_hdr_len = sizeof(struct htt_resp_hdr) +
279362306a36Sopenharmony_ci		      sizeof(rx_hl->hdr) +
279462306a36Sopenharmony_ci		      sizeof(rx_hl->ppdu) +
279562306a36Sopenharmony_ci		      sizeof(rx_hl->prefix) +
279662306a36Sopenharmony_ci		      sizeof(rx_hl->fw_desc) +
279762306a36Sopenharmony_ci		      sizeof(struct htt_rx_indication_mpdu_range) * num_mpdu_ranges;
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	tid =  MS(rx_hl->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
280062306a36Sopenharmony_ci	rx_desc = (struct htt_hl_rx_desc *)(skb->data + tot_hdr_len);
280162306a36Sopenharmony_ci	rx_desc_info = __le32_to_cpu(rx_desc->info);
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len);
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	if (is_multicast_ether_addr(hdr->addr1)) {
280662306a36Sopenharmony_ci		/* Discard the fragment with multicast DA */
280762306a36Sopenharmony_ci		goto err;
280862306a36Sopenharmony_ci	}
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED)) {
281162306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
281262306a36Sopenharmony_ci		return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
281362306a36Sopenharmony_ci						    HTT_RX_NON_PN_CHECK,
281462306a36Sopenharmony_ci						    HTT_RX_NON_TKIP_MIC);
281562306a36Sopenharmony_ci	}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	if (ieee80211_has_retry(hdr->frame_control))
281862306a36Sopenharmony_ci		goto err;
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	hdr_space = ieee80211_hdrlen(hdr->frame_control);
282162306a36Sopenharmony_ci	sc = __le16_to_cpu(hdr->seq_ctrl);
282262306a36Sopenharmony_ci	seq = IEEE80211_SEQ_TO_SN(sc);
282362306a36Sopenharmony_ci	frag = sc & IEEE80211_SCTL_FRAG;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	sec_index = MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST) ?
282662306a36Sopenharmony_ci		    HTT_TXRX_SEC_MCAST : HTT_TXRX_SEC_UCAST;
282762306a36Sopenharmony_ci	sec_type = peer->rx_pn[sec_index].sec_type;
282862306a36Sopenharmony_ci	ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len);
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci	switch (sec_type) {
283162306a36Sopenharmony_ci	case HTT_SECURITY_TKIP:
283262306a36Sopenharmony_ci		tkip_mic = HTT_RX_TKIP_MIC;
283362306a36Sopenharmony_ci		ret = ath10k_htt_rx_frag_tkip_decap_withmic(skb,
283462306a36Sopenharmony_ci							    tot_hdr_len +
283562306a36Sopenharmony_ci							    rx_hl->fw_desc.len,
283662306a36Sopenharmony_ci							    hdr_space);
283762306a36Sopenharmony_ci		if (ret)
283862306a36Sopenharmony_ci			goto err;
283962306a36Sopenharmony_ci		break;
284062306a36Sopenharmony_ci	case HTT_SECURITY_TKIP_NOMIC:
284162306a36Sopenharmony_ci		ret = ath10k_htt_rx_frag_tkip_decap_nomic(skb,
284262306a36Sopenharmony_ci							  tot_hdr_len +
284362306a36Sopenharmony_ci							  rx_hl->fw_desc.len,
284462306a36Sopenharmony_ci							  hdr_space);
284562306a36Sopenharmony_ci		if (ret)
284662306a36Sopenharmony_ci			goto err;
284762306a36Sopenharmony_ci		break;
284862306a36Sopenharmony_ci	case HTT_SECURITY_AES_CCMP:
284962306a36Sopenharmony_ci		ret = ath10k_htt_rx_frag_ccmp_decap(skb,
285062306a36Sopenharmony_ci						    tot_hdr_len + rx_hl->fw_desc.len,
285162306a36Sopenharmony_ci						    hdr_space);
285262306a36Sopenharmony_ci		if (ret)
285362306a36Sopenharmony_ci			goto err;
285462306a36Sopenharmony_ci		break;
285562306a36Sopenharmony_ci	case HTT_SECURITY_WEP128:
285662306a36Sopenharmony_ci	case HTT_SECURITY_WEP104:
285762306a36Sopenharmony_ci	case HTT_SECURITY_WEP40:
285862306a36Sopenharmony_ci		ret = ath10k_htt_rx_frag_wep_decap(skb,
285962306a36Sopenharmony_ci						   tot_hdr_len + rx_hl->fw_desc.len,
286062306a36Sopenharmony_ci						   hdr_space);
286162306a36Sopenharmony_ci		if (ret)
286262306a36Sopenharmony_ci			goto err;
286362306a36Sopenharmony_ci		break;
286462306a36Sopenharmony_ci	default:
286562306a36Sopenharmony_ci		break;
286662306a36Sopenharmony_ci	}
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	resp = (struct htt_resp *)(skb->data);
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	if (sec_type != HTT_SECURITY_AES_CCMP &&
287162306a36Sopenharmony_ci	    sec_type != HTT_SECURITY_TKIP &&
287262306a36Sopenharmony_ci	    sec_type != HTT_SECURITY_TKIP_NOMIC) {
287362306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
287462306a36Sopenharmony_ci		return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
287562306a36Sopenharmony_ci						    HTT_RX_NON_PN_CHECK,
287662306a36Sopenharmony_ci						    HTT_RX_NON_TKIP_MIC);
287762306a36Sopenharmony_ci	}
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_ci	last_pn = &peer->frag_tids_last_pn[tid];
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci	if (frag == 0) {
288262306a36Sopenharmony_ci		if (ath10k_htt_rx_pn_check_replay_hl(ar, peer, &resp->rx_ind_hl))
288362306a36Sopenharmony_ci			goto err;
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci		last_pn->pn48 = new_pn.pn48;
288662306a36Sopenharmony_ci		peer->frag_tids_seq[tid] = seq;
288762306a36Sopenharmony_ci	} else if (sec_type == HTT_SECURITY_AES_CCMP) {
288862306a36Sopenharmony_ci		if (seq != peer->frag_tids_seq[tid])
288962306a36Sopenharmony_ci			goto err;
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_ci		if (new_pn.pn48 != last_pn->pn48 + 1)
289262306a36Sopenharmony_ci			goto err;
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci		last_pn->pn48 = new_pn.pn48;
289562306a36Sopenharmony_ci		last_pn = &peer->tids_last_pn[tid];
289662306a36Sopenharmony_ci		last_pn->pn48 = new_pn.pn48;
289762306a36Sopenharmony_ci	}
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
290262306a36Sopenharmony_ci					    HTT_RX_NON_PN_CHECK, tkip_mic);
290362306a36Sopenharmony_ci
290462306a36Sopenharmony_cierr:
290562306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
290662306a36Sopenharmony_ci
290762306a36Sopenharmony_ci	/* Tell the caller that it must free the skb since we have not
290862306a36Sopenharmony_ci	 * consumed it
290962306a36Sopenharmony_ci	 */
291062306a36Sopenharmony_ci	return true;
291162306a36Sopenharmony_ci}
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_cistatic void ath10k_htt_rx_proc_rx_ind_ll(struct ath10k_htt *htt,
291462306a36Sopenharmony_ci					 struct htt_rx_indication *rx)
291562306a36Sopenharmony_ci{
291662306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
291762306a36Sopenharmony_ci	struct htt_rx_indication_mpdu_range *mpdu_ranges;
291862306a36Sopenharmony_ci	int num_mpdu_ranges;
291962306a36Sopenharmony_ci	int i, mpdu_count = 0;
292062306a36Sopenharmony_ci	u16 peer_id;
292162306a36Sopenharmony_ci	u8 tid;
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci	num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
292462306a36Sopenharmony_ci			     HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
292562306a36Sopenharmony_ci	peer_id = __le16_to_cpu(rx->hdr.peer_id);
292662306a36Sopenharmony_ci	tid =  MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
293162306a36Sopenharmony_ci			rx, struct_size(rx, mpdu_ranges, num_mpdu_ranges));
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	for (i = 0; i < num_mpdu_ranges; i++)
293462306a36Sopenharmony_ci		mpdu_count += mpdu_ranges[i].mpdu_count;
293562306a36Sopenharmony_ci
293662306a36Sopenharmony_ci	atomic_add(mpdu_count, &htt->num_mpdus_ready);
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	ath10k_sta_update_rx_tid_stats_ampdu(ar, peer_id, tid, mpdu_ranges,
293962306a36Sopenharmony_ci					     num_mpdu_ranges);
294062306a36Sopenharmony_ci}
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_cistatic void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar,
294362306a36Sopenharmony_ci				       struct sk_buff *skb)
294462306a36Sopenharmony_ci{
294562306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
294662306a36Sopenharmony_ci	struct htt_resp *resp = (struct htt_resp *)skb->data;
294762306a36Sopenharmony_ci	struct htt_tx_done tx_done = {};
294862306a36Sopenharmony_ci	int status = MS(resp->data_tx_completion.flags, HTT_DATA_TX_STATUS);
294962306a36Sopenharmony_ci	__le16 msdu_id, *msdus;
295062306a36Sopenharmony_ci	bool rssi_enabled = false;
295162306a36Sopenharmony_ci	u8 msdu_count = 0, num_airtime_records, tid;
295262306a36Sopenharmony_ci	int i, htt_pad = 0;
295362306a36Sopenharmony_ci	struct htt_data_tx_compl_ppdu_dur *ppdu_info;
295462306a36Sopenharmony_ci	struct ath10k_peer *peer;
295562306a36Sopenharmony_ci	u16 ppdu_info_offset = 0, peer_id;
295662306a36Sopenharmony_ci	u32 tx_duration;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	switch (status) {
295962306a36Sopenharmony_ci	case HTT_DATA_TX_STATUS_NO_ACK:
296062306a36Sopenharmony_ci		tx_done.status = HTT_TX_COMPL_STATE_NOACK;
296162306a36Sopenharmony_ci		break;
296262306a36Sopenharmony_ci	case HTT_DATA_TX_STATUS_OK:
296362306a36Sopenharmony_ci		tx_done.status = HTT_TX_COMPL_STATE_ACK;
296462306a36Sopenharmony_ci		break;
296562306a36Sopenharmony_ci	case HTT_DATA_TX_STATUS_DISCARD:
296662306a36Sopenharmony_ci	case HTT_DATA_TX_STATUS_POSTPONE:
296762306a36Sopenharmony_ci	case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
296862306a36Sopenharmony_ci		tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
296962306a36Sopenharmony_ci		break;
297062306a36Sopenharmony_ci	default:
297162306a36Sopenharmony_ci		ath10k_warn(ar, "unhandled tx completion status %d\n", status);
297262306a36Sopenharmony_ci		tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
297362306a36Sopenharmony_ci		break;
297462306a36Sopenharmony_ci	}
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n",
297762306a36Sopenharmony_ci		   resp->data_tx_completion.num_msdus);
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	msdu_count = resp->data_tx_completion.num_msdus;
298062306a36Sopenharmony_ci	msdus = resp->data_tx_completion.msdus;
298162306a36Sopenharmony_ci	rssi_enabled = ath10k_is_rssi_enable(&ar->hw_params, resp);
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci	if (rssi_enabled)
298462306a36Sopenharmony_ci		htt_pad = ath10k_tx_data_rssi_get_pad_bytes(&ar->hw_params,
298562306a36Sopenharmony_ci							    resp);
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci	for (i = 0; i < msdu_count; i++) {
298862306a36Sopenharmony_ci		msdu_id = msdus[i];
298962306a36Sopenharmony_ci		tx_done.msdu_id = __le16_to_cpu(msdu_id);
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci		if (rssi_enabled) {
299262306a36Sopenharmony_ci			/* Total no of MSDUs should be even,
299362306a36Sopenharmony_ci			 * if odd MSDUs are sent firmware fills
299462306a36Sopenharmony_ci			 * last msdu id with 0xffff
299562306a36Sopenharmony_ci			 */
299662306a36Sopenharmony_ci			if (msdu_count & 0x01) {
299762306a36Sopenharmony_ci				msdu_id = msdus[msdu_count +  i + 1 + htt_pad];
299862306a36Sopenharmony_ci				tx_done.ack_rssi = __le16_to_cpu(msdu_id);
299962306a36Sopenharmony_ci			} else {
300062306a36Sopenharmony_ci				msdu_id = msdus[msdu_count +  i + htt_pad];
300162306a36Sopenharmony_ci				tx_done.ack_rssi = __le16_to_cpu(msdu_id);
300262306a36Sopenharmony_ci			}
300362306a36Sopenharmony_ci		}
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci		/* kfifo_put: In practice firmware shouldn't fire off per-CE
300662306a36Sopenharmony_ci		 * interrupt and main interrupt (MSI/-X range case) for the same
300762306a36Sopenharmony_ci		 * HTC service so it should be safe to use kfifo_put w/o lock.
300862306a36Sopenharmony_ci		 *
300962306a36Sopenharmony_ci		 * From kfifo_put() documentation:
301062306a36Sopenharmony_ci		 *  Note that with only one concurrent reader and one concurrent
301162306a36Sopenharmony_ci		 *  writer, you don't need extra locking to use these macro.
301262306a36Sopenharmony_ci		 */
301362306a36Sopenharmony_ci		if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) {
301462306a36Sopenharmony_ci			ath10k_txrx_tx_unref(htt, &tx_done);
301562306a36Sopenharmony_ci		} else if (!kfifo_put(&htt->txdone_fifo, tx_done)) {
301662306a36Sopenharmony_ci			ath10k_warn(ar, "txdone fifo overrun, msdu_id %d status %d\n",
301762306a36Sopenharmony_ci				    tx_done.msdu_id, tx_done.status);
301862306a36Sopenharmony_ci			ath10k_txrx_tx_unref(htt, &tx_done);
301962306a36Sopenharmony_ci		}
302062306a36Sopenharmony_ci	}
302162306a36Sopenharmony_ci
302262306a36Sopenharmony_ci	if (!(resp->data_tx_completion.flags2 & HTT_TX_CMPL_FLAG_PPDU_DURATION_PRESENT))
302362306a36Sopenharmony_ci		return;
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	ppdu_info_offset = (msdu_count & 0x01) ? msdu_count + 1 : msdu_count;
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	if (rssi_enabled)
302862306a36Sopenharmony_ci		ppdu_info_offset += ppdu_info_offset;
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ci	if (resp->data_tx_completion.flags2 &
303162306a36Sopenharmony_ci	    (HTT_TX_CMPL_FLAG_PPID_PRESENT | HTT_TX_CMPL_FLAG_PA_PRESENT))
303262306a36Sopenharmony_ci		ppdu_info_offset += 2;
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	ppdu_info = (struct htt_data_tx_compl_ppdu_dur *)&msdus[ppdu_info_offset];
303562306a36Sopenharmony_ci	num_airtime_records = FIELD_GET(HTT_TX_COMPL_PPDU_DUR_INFO0_NUM_ENTRIES_MASK,
303662306a36Sopenharmony_ci					__le32_to_cpu(ppdu_info->info0));
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci	for (i = 0; i < num_airtime_records; i++) {
303962306a36Sopenharmony_ci		struct htt_data_tx_ppdu_dur *ppdu_dur;
304062306a36Sopenharmony_ci		u32 info0;
304162306a36Sopenharmony_ci
304262306a36Sopenharmony_ci		ppdu_dur = &ppdu_info->ppdu_dur[i];
304362306a36Sopenharmony_ci		info0 = __le32_to_cpu(ppdu_dur->info0);
304462306a36Sopenharmony_ci
304562306a36Sopenharmony_ci		peer_id = FIELD_GET(HTT_TX_PPDU_DUR_INFO0_PEER_ID_MASK,
304662306a36Sopenharmony_ci				    info0);
304762306a36Sopenharmony_ci		rcu_read_lock();
304862306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci		peer = ath10k_peer_find_by_id(ar, peer_id);
305162306a36Sopenharmony_ci		if (!peer || !peer->sta) {
305262306a36Sopenharmony_ci			spin_unlock_bh(&ar->data_lock);
305362306a36Sopenharmony_ci			rcu_read_unlock();
305462306a36Sopenharmony_ci			continue;
305562306a36Sopenharmony_ci		}
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci		tid = FIELD_GET(HTT_TX_PPDU_DUR_INFO0_TID_MASK, info0) &
305862306a36Sopenharmony_ci						IEEE80211_QOS_CTL_TID_MASK;
305962306a36Sopenharmony_ci		tx_duration = __le32_to_cpu(ppdu_dur->tx_duration);
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci		ieee80211_sta_register_airtime(peer->sta, tid, tx_duration, 0);
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
306462306a36Sopenharmony_ci		rcu_read_unlock();
306562306a36Sopenharmony_ci	}
306662306a36Sopenharmony_ci}
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_cistatic void ath10k_htt_rx_addba(struct ath10k *ar, struct htt_resp *resp)
306962306a36Sopenharmony_ci{
307062306a36Sopenharmony_ci	struct htt_rx_addba *ev = &resp->rx_addba;
307162306a36Sopenharmony_ci	struct ath10k_peer *peer;
307262306a36Sopenharmony_ci	struct ath10k_vif *arvif;
307362306a36Sopenharmony_ci	u16 info0, tid, peer_id;
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_ci	info0 = __le16_to_cpu(ev->info0);
307662306a36Sopenharmony_ci	tid = MS(info0, HTT_RX_BA_INFO0_TID);
307762306a36Sopenharmony_ci	peer_id = MS(info0, HTT_RX_BA_INFO0_PEER_ID);
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT,
308062306a36Sopenharmony_ci		   "htt rx addba tid %u peer_id %u size %u\n",
308162306a36Sopenharmony_ci		   tid, peer_id, ev->window_size);
308262306a36Sopenharmony_ci
308362306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
308462306a36Sopenharmony_ci	peer = ath10k_peer_find_by_id(ar, peer_id);
308562306a36Sopenharmony_ci	if (!peer) {
308662306a36Sopenharmony_ci		ath10k_warn(ar, "received addba event for invalid peer_id: %u\n",
308762306a36Sopenharmony_ci			    peer_id);
308862306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
308962306a36Sopenharmony_ci		return;
309062306a36Sopenharmony_ci	}
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_ci	arvif = ath10k_get_arvif(ar, peer->vdev_id);
309362306a36Sopenharmony_ci	if (!arvif) {
309462306a36Sopenharmony_ci		ath10k_warn(ar, "received addba event for invalid vdev_id: %u\n",
309562306a36Sopenharmony_ci			    peer->vdev_id);
309662306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
309762306a36Sopenharmony_ci		return;
309862306a36Sopenharmony_ci	}
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT,
310162306a36Sopenharmony_ci		   "htt rx start rx ba session sta %pM tid %u size %u\n",
310262306a36Sopenharmony_ci		   peer->addr, tid, ev->window_size);
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci	ieee80211_start_rx_ba_session_offl(arvif->vif, peer->addr, tid);
310562306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
310662306a36Sopenharmony_ci}
310762306a36Sopenharmony_ci
310862306a36Sopenharmony_cistatic void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp)
310962306a36Sopenharmony_ci{
311062306a36Sopenharmony_ci	struct htt_rx_delba *ev = &resp->rx_delba;
311162306a36Sopenharmony_ci	struct ath10k_peer *peer;
311262306a36Sopenharmony_ci	struct ath10k_vif *arvif;
311362306a36Sopenharmony_ci	u16 info0, tid, peer_id;
311462306a36Sopenharmony_ci
311562306a36Sopenharmony_ci	info0 = __le16_to_cpu(ev->info0);
311662306a36Sopenharmony_ci	tid = MS(info0, HTT_RX_BA_INFO0_TID);
311762306a36Sopenharmony_ci	peer_id = MS(info0, HTT_RX_BA_INFO0_PEER_ID);
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT,
312062306a36Sopenharmony_ci		   "htt rx delba tid %u peer_id %u\n",
312162306a36Sopenharmony_ci		   tid, peer_id);
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
312462306a36Sopenharmony_ci	peer = ath10k_peer_find_by_id(ar, peer_id);
312562306a36Sopenharmony_ci	if (!peer) {
312662306a36Sopenharmony_ci		ath10k_warn(ar, "received addba event for invalid peer_id: %u\n",
312762306a36Sopenharmony_ci			    peer_id);
312862306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
312962306a36Sopenharmony_ci		return;
313062306a36Sopenharmony_ci	}
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci	arvif = ath10k_get_arvif(ar, peer->vdev_id);
313362306a36Sopenharmony_ci	if (!arvif) {
313462306a36Sopenharmony_ci		ath10k_warn(ar, "received addba event for invalid vdev_id: %u\n",
313562306a36Sopenharmony_ci			    peer->vdev_id);
313662306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
313762306a36Sopenharmony_ci		return;
313862306a36Sopenharmony_ci	}
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT,
314162306a36Sopenharmony_ci		   "htt rx stop rx ba session sta %pM tid %u\n",
314262306a36Sopenharmony_ci		   peer->addr, tid);
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci	ieee80211_stop_rx_ba_session_offl(arvif->vif, peer->addr, tid);
314562306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
314662306a36Sopenharmony_ci}
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_cistatic int ath10k_htt_rx_extract_amsdu(struct ath10k_hw_params *hw,
314962306a36Sopenharmony_ci				       struct sk_buff_head *list,
315062306a36Sopenharmony_ci				       struct sk_buff_head *amsdu)
315162306a36Sopenharmony_ci{
315262306a36Sopenharmony_ci	struct sk_buff *msdu;
315362306a36Sopenharmony_ci	struct htt_rx_desc *rxd;
315462306a36Sopenharmony_ci	struct rx_msdu_end_common *rxd_msdu_end_common;
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	if (skb_queue_empty(list))
315762306a36Sopenharmony_ci		return -ENOBUFS;
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci	if (WARN_ON(!skb_queue_empty(amsdu)))
316062306a36Sopenharmony_ci		return -EINVAL;
316162306a36Sopenharmony_ci
316262306a36Sopenharmony_ci	while ((msdu = __skb_dequeue(list))) {
316362306a36Sopenharmony_ci		__skb_queue_tail(amsdu, msdu);
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci		rxd = HTT_RX_BUF_TO_RX_DESC(hw,
316662306a36Sopenharmony_ci					    (void *)msdu->data -
316762306a36Sopenharmony_ci					    hw->rx_desc_ops->rx_desc_size);
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci		rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd);
317062306a36Sopenharmony_ci		if (rxd_msdu_end_common->info0 &
317162306a36Sopenharmony_ci		    __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))
317262306a36Sopenharmony_ci			break;
317362306a36Sopenharmony_ci	}
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci	msdu = skb_peek_tail(amsdu);
317662306a36Sopenharmony_ci	rxd = HTT_RX_BUF_TO_RX_DESC(hw,
317762306a36Sopenharmony_ci				    (void *)msdu->data - hw->rx_desc_ops->rx_desc_size);
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci	rxd_msdu_end_common = ath10k_htt_rx_desc_get_msdu_end(hw, rxd);
318062306a36Sopenharmony_ci	if (!(rxd_msdu_end_common->info0 &
318162306a36Sopenharmony_ci	      __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))) {
318262306a36Sopenharmony_ci		skb_queue_splice_init(amsdu, list);
318362306a36Sopenharmony_ci		return -EAGAIN;
318462306a36Sopenharmony_ci	}
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci	return 0;
318762306a36Sopenharmony_ci}
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_cistatic void ath10k_htt_rx_h_rx_offload_prot(struct ieee80211_rx_status *status,
319062306a36Sopenharmony_ci					    struct sk_buff *skb)
319162306a36Sopenharmony_ci{
319262306a36Sopenharmony_ci	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci	if (!ieee80211_has_protected(hdr->frame_control))
319562306a36Sopenharmony_ci		return;
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci	/* Offloaded frames are already decrypted but firmware insists they are
319862306a36Sopenharmony_ci	 * protected in the 802.11 header. Strip the flag.  Otherwise mac80211
319962306a36Sopenharmony_ci	 * will drop the frame.
320062306a36Sopenharmony_ci	 */
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci	hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
320362306a36Sopenharmony_ci	status->flag |= RX_FLAG_DECRYPTED |
320462306a36Sopenharmony_ci			RX_FLAG_IV_STRIPPED |
320562306a36Sopenharmony_ci			RX_FLAG_MMIC_STRIPPED;
320662306a36Sopenharmony_ci}
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_cistatic void ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
320962306a36Sopenharmony_ci				       struct sk_buff_head *list)
321062306a36Sopenharmony_ci{
321162306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
321262306a36Sopenharmony_ci	struct ieee80211_rx_status *status = &htt->rx_status;
321362306a36Sopenharmony_ci	struct htt_rx_offload_msdu *rx;
321462306a36Sopenharmony_ci	struct sk_buff *msdu;
321562306a36Sopenharmony_ci	size_t offset;
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_ci	while ((msdu = __skb_dequeue(list))) {
321862306a36Sopenharmony_ci		/* Offloaded frames don't have Rx descriptor. Instead they have
321962306a36Sopenharmony_ci		 * a short meta information header.
322062306a36Sopenharmony_ci		 */
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci		rx = (void *)msdu->data;
322362306a36Sopenharmony_ci
322462306a36Sopenharmony_ci		skb_put(msdu, sizeof(*rx));
322562306a36Sopenharmony_ci		skb_pull(msdu, sizeof(*rx));
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci		if (skb_tailroom(msdu) < __le16_to_cpu(rx->msdu_len)) {
322862306a36Sopenharmony_ci			ath10k_warn(ar, "dropping frame: offloaded rx msdu is too long!\n");
322962306a36Sopenharmony_ci			dev_kfree_skb_any(msdu);
323062306a36Sopenharmony_ci			continue;
323162306a36Sopenharmony_ci		}
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci		skb_put(msdu, __le16_to_cpu(rx->msdu_len));
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci		/* Offloaded rx header length isn't multiple of 2 nor 4 so the
323662306a36Sopenharmony_ci		 * actual payload is unaligned. Align the frame.  Otherwise
323762306a36Sopenharmony_ci		 * mac80211 complains.  This shouldn't reduce performance much
323862306a36Sopenharmony_ci		 * because these offloaded frames are rare.
323962306a36Sopenharmony_ci		 */
324062306a36Sopenharmony_ci		offset = 4 - ((unsigned long)msdu->data & 3);
324162306a36Sopenharmony_ci		skb_put(msdu, offset);
324262306a36Sopenharmony_ci		memmove(msdu->data + offset, msdu->data, msdu->len);
324362306a36Sopenharmony_ci		skb_pull(msdu, offset);
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci		/* FIXME: The frame is NWifi. Re-construct QoS Control
324662306a36Sopenharmony_ci		 * if possible later.
324762306a36Sopenharmony_ci		 */
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci		memset(status, 0, sizeof(*status));
325062306a36Sopenharmony_ci		status->flag |= RX_FLAG_NO_SIGNAL_VAL;
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci		ath10k_htt_rx_h_rx_offload_prot(status, msdu);
325362306a36Sopenharmony_ci		ath10k_htt_rx_h_channel(ar, status, NULL, rx->vdev_id);
325462306a36Sopenharmony_ci		ath10k_htt_rx_h_queue_msdu(ar, status, msdu);
325562306a36Sopenharmony_ci	}
325662306a36Sopenharmony_ci}
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_cistatic int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
325962306a36Sopenharmony_ci{
326062306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
326162306a36Sopenharmony_ci	struct htt_resp *resp = (void *)skb->data;
326262306a36Sopenharmony_ci	struct ieee80211_rx_status *status = &htt->rx_status;
326362306a36Sopenharmony_ci	struct sk_buff_head list;
326462306a36Sopenharmony_ci	struct sk_buff_head amsdu;
326562306a36Sopenharmony_ci	u16 peer_id;
326662306a36Sopenharmony_ci	u16 msdu_count;
326762306a36Sopenharmony_ci	u8 vdev_id;
326862306a36Sopenharmony_ci	u8 tid;
326962306a36Sopenharmony_ci	bool offload;
327062306a36Sopenharmony_ci	bool frag;
327162306a36Sopenharmony_ci	int ret;
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_ci	lockdep_assert_held(&htt->rx_ring.lock);
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci	if (htt->rx_confused)
327662306a36Sopenharmony_ci		return -EIO;
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci	skb_pull(skb, sizeof(resp->hdr));
327962306a36Sopenharmony_ci	skb_pull(skb, sizeof(resp->rx_in_ord_ind));
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci	peer_id = __le16_to_cpu(resp->rx_in_ord_ind.peer_id);
328262306a36Sopenharmony_ci	msdu_count = __le16_to_cpu(resp->rx_in_ord_ind.msdu_count);
328362306a36Sopenharmony_ci	vdev_id = resp->rx_in_ord_ind.vdev_id;
328462306a36Sopenharmony_ci	tid = SM(resp->rx_in_ord_ind.info, HTT_RX_IN_ORD_IND_INFO_TID);
328562306a36Sopenharmony_ci	offload = !!(resp->rx_in_ord_ind.info &
328662306a36Sopenharmony_ci			HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK);
328762306a36Sopenharmony_ci	frag = !!(resp->rx_in_ord_ind.info & HTT_RX_IN_ORD_IND_INFO_FRAG_MASK);
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT,
329062306a36Sopenharmony_ci		   "htt rx in ord vdev %i peer %i tid %i offload %i frag %i msdu count %i\n",
329162306a36Sopenharmony_ci		   vdev_id, peer_id, tid, offload, frag, msdu_count);
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_ci	if (skb->len < msdu_count * sizeof(*resp->rx_in_ord_ind.msdu_descs32)) {
329462306a36Sopenharmony_ci		ath10k_warn(ar, "dropping invalid in order rx indication\n");
329562306a36Sopenharmony_ci		return -EINVAL;
329662306a36Sopenharmony_ci	}
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	/* The event can deliver more than 1 A-MSDU. Each A-MSDU is later
329962306a36Sopenharmony_ci	 * extracted and processed.
330062306a36Sopenharmony_ci	 */
330162306a36Sopenharmony_ci	__skb_queue_head_init(&list);
330262306a36Sopenharmony_ci	if (ar->hw_params.target_64bit)
330362306a36Sopenharmony_ci		ret = ath10k_htt_rx_pop_paddr64_list(htt, &resp->rx_in_ord_ind,
330462306a36Sopenharmony_ci						     &list);
330562306a36Sopenharmony_ci	else
330662306a36Sopenharmony_ci		ret = ath10k_htt_rx_pop_paddr32_list(htt, &resp->rx_in_ord_ind,
330762306a36Sopenharmony_ci						     &list);
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci	if (ret < 0) {
331062306a36Sopenharmony_ci		ath10k_warn(ar, "failed to pop paddr list: %d\n", ret);
331162306a36Sopenharmony_ci		htt->rx_confused = true;
331262306a36Sopenharmony_ci		return -EIO;
331362306a36Sopenharmony_ci	}
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	/* Offloaded frames are very different and need to be handled
331662306a36Sopenharmony_ci	 * separately.
331762306a36Sopenharmony_ci	 */
331862306a36Sopenharmony_ci	if (offload)
331962306a36Sopenharmony_ci		ath10k_htt_rx_h_rx_offload(ar, &list);
332062306a36Sopenharmony_ci
332162306a36Sopenharmony_ci	while (!skb_queue_empty(&list)) {
332262306a36Sopenharmony_ci		__skb_queue_head_init(&amsdu);
332362306a36Sopenharmony_ci		ret = ath10k_htt_rx_extract_amsdu(&ar->hw_params, &list, &amsdu);
332462306a36Sopenharmony_ci		switch (ret) {
332562306a36Sopenharmony_ci		case 0:
332662306a36Sopenharmony_ci			/* Note: The in-order indication may report interleaved
332762306a36Sopenharmony_ci			 * frames from different PPDUs meaning reported rx rate
332862306a36Sopenharmony_ci			 * to mac80211 isn't accurate/reliable. It's still
332962306a36Sopenharmony_ci			 * better to report something than nothing though. This
333062306a36Sopenharmony_ci			 * should still give an idea about rx rate to the user.
333162306a36Sopenharmony_ci			 */
333262306a36Sopenharmony_ci			ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
333362306a36Sopenharmony_ci			ath10k_htt_rx_h_filter(ar, &amsdu, status, NULL);
333462306a36Sopenharmony_ci			ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false, NULL,
333562306a36Sopenharmony_ci					     NULL, peer_id, frag);
333662306a36Sopenharmony_ci			ath10k_htt_rx_h_enqueue(ar, &amsdu, status);
333762306a36Sopenharmony_ci			break;
333862306a36Sopenharmony_ci		case -EAGAIN:
333962306a36Sopenharmony_ci			fallthrough;
334062306a36Sopenharmony_ci		default:
334162306a36Sopenharmony_ci			/* Should not happen. */
334262306a36Sopenharmony_ci			ath10k_warn(ar, "failed to extract amsdu: %d\n", ret);
334362306a36Sopenharmony_ci			htt->rx_confused = true;
334462306a36Sopenharmony_ci			__skb_queue_purge(&list);
334562306a36Sopenharmony_ci			return -EIO;
334662306a36Sopenharmony_ci		}
334762306a36Sopenharmony_ci	}
334862306a36Sopenharmony_ci	return ret;
334962306a36Sopenharmony_ci}
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_cistatic void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
335262306a36Sopenharmony_ci						   const __le32 *resp_ids,
335362306a36Sopenharmony_ci						   int num_resp_ids)
335462306a36Sopenharmony_ci{
335562306a36Sopenharmony_ci	int i;
335662306a36Sopenharmony_ci	u32 resp_id;
335762306a36Sopenharmony_ci
335862306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm num_resp_ids %d\n",
335962306a36Sopenharmony_ci		   num_resp_ids);
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci	for (i = 0; i < num_resp_ids; i++) {
336262306a36Sopenharmony_ci		resp_id = le32_to_cpu(resp_ids[i]);
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm resp_id %u\n",
336562306a36Sopenharmony_ci			   resp_id);
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ci		/* TODO: free resp_id */
336862306a36Sopenharmony_ci	}
336962306a36Sopenharmony_ci}
337062306a36Sopenharmony_ci
337162306a36Sopenharmony_cistatic void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
337262306a36Sopenharmony_ci{
337362306a36Sopenharmony_ci	struct ieee80211_hw *hw = ar->hw;
337462306a36Sopenharmony_ci	struct ieee80211_txq *txq;
337562306a36Sopenharmony_ci	struct htt_resp *resp = (struct htt_resp *)skb->data;
337662306a36Sopenharmony_ci	struct htt_tx_fetch_record *record;
337762306a36Sopenharmony_ci	size_t len;
337862306a36Sopenharmony_ci	size_t max_num_bytes;
337962306a36Sopenharmony_ci	size_t max_num_msdus;
338062306a36Sopenharmony_ci	size_t num_bytes;
338162306a36Sopenharmony_ci	size_t num_msdus;
338262306a36Sopenharmony_ci	const __le32 *resp_ids;
338362306a36Sopenharmony_ci	u16 num_records;
338462306a36Sopenharmony_ci	u16 num_resp_ids;
338562306a36Sopenharmony_ci	u16 peer_id;
338662306a36Sopenharmony_ci	u8 tid;
338762306a36Sopenharmony_ci	int ret;
338862306a36Sopenharmony_ci	int i;
338962306a36Sopenharmony_ci	bool may_tx;
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci	len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_ind);
339462306a36Sopenharmony_ci	if (unlikely(skb->len < len)) {
339562306a36Sopenharmony_ci		ath10k_warn(ar, "received corrupted tx_fetch_ind event: buffer too short\n");
339662306a36Sopenharmony_ci		return;
339762306a36Sopenharmony_ci	}
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_ci	num_records = le16_to_cpu(resp->tx_fetch_ind.num_records);
340062306a36Sopenharmony_ci	num_resp_ids = le16_to_cpu(resp->tx_fetch_ind.num_resp_ids);
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci	len += sizeof(resp->tx_fetch_ind.records[0]) * num_records;
340362306a36Sopenharmony_ci	len += sizeof(resp->tx_fetch_ind.resp_ids[0]) * num_resp_ids;
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	if (unlikely(skb->len < len)) {
340662306a36Sopenharmony_ci		ath10k_warn(ar, "received corrupted tx_fetch_ind event: too many records/resp_ids\n");
340762306a36Sopenharmony_ci		return;
340862306a36Sopenharmony_ci	}
340962306a36Sopenharmony_ci
341062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind num records %u num resps %u seq %u\n",
341162306a36Sopenharmony_ci		   num_records, num_resp_ids,
341262306a36Sopenharmony_ci		   le16_to_cpu(resp->tx_fetch_ind.fetch_seq_num));
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	if (!ar->htt.tx_q_state.enabled) {
341562306a36Sopenharmony_ci		ath10k_warn(ar, "received unexpected tx_fetch_ind event: not enabled\n");
341662306a36Sopenharmony_ci		return;
341762306a36Sopenharmony_ci	}
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci	if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH) {
342062306a36Sopenharmony_ci		ath10k_warn(ar, "received unexpected tx_fetch_ind event: in push mode\n");
342162306a36Sopenharmony_ci		return;
342262306a36Sopenharmony_ci	}
342362306a36Sopenharmony_ci
342462306a36Sopenharmony_ci	rcu_read_lock();
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_ci	for (i = 0; i < num_records; i++) {
342762306a36Sopenharmony_ci		record = &resp->tx_fetch_ind.records[i];
342862306a36Sopenharmony_ci		peer_id = MS(le16_to_cpu(record->info),
342962306a36Sopenharmony_ci			     HTT_TX_FETCH_RECORD_INFO_PEER_ID);
343062306a36Sopenharmony_ci		tid = MS(le16_to_cpu(record->info),
343162306a36Sopenharmony_ci			 HTT_TX_FETCH_RECORD_INFO_TID);
343262306a36Sopenharmony_ci		max_num_msdus = le16_to_cpu(record->num_msdus);
343362306a36Sopenharmony_ci		max_num_bytes = le32_to_cpu(record->num_bytes);
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch record %i peer_id %u tid %u msdus %zu bytes %zu\n",
343662306a36Sopenharmony_ci			   i, peer_id, tid, max_num_msdus, max_num_bytes);
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci		if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
343962306a36Sopenharmony_ci		    unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
344062306a36Sopenharmony_ci			ath10k_warn(ar, "received out of range peer_id %u tid %u\n",
344162306a36Sopenharmony_ci				    peer_id, tid);
344262306a36Sopenharmony_ci			continue;
344362306a36Sopenharmony_ci		}
344462306a36Sopenharmony_ci
344562306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
344662306a36Sopenharmony_ci		txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
344762306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci		/* It is okay to release the lock and use txq because RCU read
345062306a36Sopenharmony_ci		 * lock is held.
345162306a36Sopenharmony_ci		 */
345262306a36Sopenharmony_ci
345362306a36Sopenharmony_ci		if (unlikely(!txq)) {
345462306a36Sopenharmony_ci			ath10k_warn(ar, "failed to lookup txq for peer_id %u tid %u\n",
345562306a36Sopenharmony_ci				    peer_id, tid);
345662306a36Sopenharmony_ci			continue;
345762306a36Sopenharmony_ci		}
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci		num_msdus = 0;
346062306a36Sopenharmony_ci		num_bytes = 0;
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci		ieee80211_txq_schedule_start(hw, txq->ac);
346362306a36Sopenharmony_ci		may_tx = ieee80211_txq_may_transmit(hw, txq);
346462306a36Sopenharmony_ci		while (num_msdus < max_num_msdus &&
346562306a36Sopenharmony_ci		       num_bytes < max_num_bytes) {
346662306a36Sopenharmony_ci			if (!may_tx)
346762306a36Sopenharmony_ci				break;
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci			ret = ath10k_mac_tx_push_txq(hw, txq);
347062306a36Sopenharmony_ci			if (ret < 0)
347162306a36Sopenharmony_ci				break;
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci			num_msdus++;
347462306a36Sopenharmony_ci			num_bytes += ret;
347562306a36Sopenharmony_ci		}
347662306a36Sopenharmony_ci		ieee80211_return_txq(hw, txq, false);
347762306a36Sopenharmony_ci		ieee80211_txq_schedule_end(hw, txq->ac);
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci		record->num_msdus = cpu_to_le16(num_msdus);
348062306a36Sopenharmony_ci		record->num_bytes = cpu_to_le32(num_bytes);
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci		ath10k_htt_tx_txq_recalc(hw, txq);
348362306a36Sopenharmony_ci	}
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci	rcu_read_unlock();
348662306a36Sopenharmony_ci
348762306a36Sopenharmony_ci	resp_ids = ath10k_htt_get_tx_fetch_ind_resp_ids(&resp->tx_fetch_ind);
348862306a36Sopenharmony_ci	ath10k_htt_rx_tx_fetch_resp_id_confirm(ar, resp_ids, num_resp_ids);
348962306a36Sopenharmony_ci
349062306a36Sopenharmony_ci	ret = ath10k_htt_tx_fetch_resp(ar,
349162306a36Sopenharmony_ci				       resp->tx_fetch_ind.token,
349262306a36Sopenharmony_ci				       resp->tx_fetch_ind.fetch_seq_num,
349362306a36Sopenharmony_ci				       resp->tx_fetch_ind.records,
349462306a36Sopenharmony_ci				       num_records);
349562306a36Sopenharmony_ci	if (unlikely(ret)) {
349662306a36Sopenharmony_ci		ath10k_warn(ar, "failed to submit tx fetch resp for token 0x%08x: %d\n",
349762306a36Sopenharmony_ci			    le32_to_cpu(resp->tx_fetch_ind.token), ret);
349862306a36Sopenharmony_ci		/* FIXME: request fw restart */
349962306a36Sopenharmony_ci	}
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci	ath10k_htt_tx_txq_sync(ar);
350262306a36Sopenharmony_ci}
350362306a36Sopenharmony_ci
350462306a36Sopenharmony_cistatic void ath10k_htt_rx_tx_fetch_confirm(struct ath10k *ar,
350562306a36Sopenharmony_ci					   struct sk_buff *skb)
350662306a36Sopenharmony_ci{
350762306a36Sopenharmony_ci	const struct htt_resp *resp = (void *)skb->data;
350862306a36Sopenharmony_ci	size_t len;
350962306a36Sopenharmony_ci	int num_resp_ids;
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm\n");
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci	len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_confirm);
351462306a36Sopenharmony_ci	if (unlikely(skb->len < len)) {
351562306a36Sopenharmony_ci		ath10k_warn(ar, "received corrupted tx_fetch_confirm event: buffer too short\n");
351662306a36Sopenharmony_ci		return;
351762306a36Sopenharmony_ci	}
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci	num_resp_ids = le16_to_cpu(resp->tx_fetch_confirm.num_resp_ids);
352062306a36Sopenharmony_ci	len += sizeof(resp->tx_fetch_confirm.resp_ids[0]) * num_resp_ids;
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_ci	if (unlikely(skb->len < len)) {
352362306a36Sopenharmony_ci		ath10k_warn(ar, "received corrupted tx_fetch_confirm event: resp_ids buffer overflow\n");
352462306a36Sopenharmony_ci		return;
352562306a36Sopenharmony_ci	}
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci	ath10k_htt_rx_tx_fetch_resp_id_confirm(ar,
352862306a36Sopenharmony_ci					       resp->tx_fetch_confirm.resp_ids,
352962306a36Sopenharmony_ci					       num_resp_ids);
353062306a36Sopenharmony_ci}
353162306a36Sopenharmony_ci
353262306a36Sopenharmony_cistatic void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
353362306a36Sopenharmony_ci					     struct sk_buff *skb)
353462306a36Sopenharmony_ci{
353562306a36Sopenharmony_ci	const struct htt_resp *resp = (void *)skb->data;
353662306a36Sopenharmony_ci	const struct htt_tx_mode_switch_record *record;
353762306a36Sopenharmony_ci	struct ieee80211_txq *txq;
353862306a36Sopenharmony_ci	struct ath10k_txq *artxq;
353962306a36Sopenharmony_ci	size_t len;
354062306a36Sopenharmony_ci	size_t num_records;
354162306a36Sopenharmony_ci	enum htt_tx_mode_switch_mode mode;
354262306a36Sopenharmony_ci	bool enable;
354362306a36Sopenharmony_ci	u16 info0;
354462306a36Sopenharmony_ci	u16 info1;
354562306a36Sopenharmony_ci	u16 threshold;
354662306a36Sopenharmony_ci	u16 peer_id;
354762306a36Sopenharmony_ci	u8 tid;
354862306a36Sopenharmony_ci	int i;
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx mode switch ind\n");
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	len = sizeof(resp->hdr) + sizeof(resp->tx_mode_switch_ind);
355362306a36Sopenharmony_ci	if (unlikely(skb->len < len)) {
355462306a36Sopenharmony_ci		ath10k_warn(ar, "received corrupted tx_mode_switch_ind event: buffer too short\n");
355562306a36Sopenharmony_ci		return;
355662306a36Sopenharmony_ci	}
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci	info0 = le16_to_cpu(resp->tx_mode_switch_ind.info0);
355962306a36Sopenharmony_ci	info1 = le16_to_cpu(resp->tx_mode_switch_ind.info1);
356062306a36Sopenharmony_ci
356162306a36Sopenharmony_ci	enable = !!(info0 & HTT_TX_MODE_SWITCH_IND_INFO0_ENABLE);
356262306a36Sopenharmony_ci	num_records = MS(info0, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
356362306a36Sopenharmony_ci	mode = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_MODE);
356462306a36Sopenharmony_ci	threshold = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
356562306a36Sopenharmony_ci
356662306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT,
356762306a36Sopenharmony_ci		   "htt rx tx mode switch ind info0 0x%04x info1 0x%04x enable %d num records %zd mode %d threshold %u\n",
356862306a36Sopenharmony_ci		   info0, info1, enable, num_records, mode, threshold);
356962306a36Sopenharmony_ci
357062306a36Sopenharmony_ci	len += sizeof(resp->tx_mode_switch_ind.records[0]) * num_records;
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_ci	if (unlikely(skb->len < len)) {
357362306a36Sopenharmony_ci		ath10k_warn(ar, "received corrupted tx_mode_switch_mode_ind event: too many records\n");
357462306a36Sopenharmony_ci		return;
357562306a36Sopenharmony_ci	}
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci	switch (mode) {
357862306a36Sopenharmony_ci	case HTT_TX_MODE_SWITCH_PUSH:
357962306a36Sopenharmony_ci	case HTT_TX_MODE_SWITCH_PUSH_PULL:
358062306a36Sopenharmony_ci		break;
358162306a36Sopenharmony_ci	default:
358262306a36Sopenharmony_ci		ath10k_warn(ar, "received invalid tx_mode_switch_mode_ind mode %d, ignoring\n",
358362306a36Sopenharmony_ci			    mode);
358462306a36Sopenharmony_ci		return;
358562306a36Sopenharmony_ci	}
358662306a36Sopenharmony_ci
358762306a36Sopenharmony_ci	if (!enable)
358862306a36Sopenharmony_ci		return;
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_ci	ar->htt.tx_q_state.enabled = enable;
359162306a36Sopenharmony_ci	ar->htt.tx_q_state.mode = mode;
359262306a36Sopenharmony_ci	ar->htt.tx_q_state.num_push_allowed = threshold;
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci	rcu_read_lock();
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_ci	for (i = 0; i < num_records; i++) {
359762306a36Sopenharmony_ci		record = &resp->tx_mode_switch_ind.records[i];
359862306a36Sopenharmony_ci		info0 = le16_to_cpu(record->info0);
359962306a36Sopenharmony_ci		peer_id = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_PEER_ID);
360062306a36Sopenharmony_ci		tid = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_TID);
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci		if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
360362306a36Sopenharmony_ci		    unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
360462306a36Sopenharmony_ci			ath10k_warn(ar, "received out of range peer_id %u tid %u\n",
360562306a36Sopenharmony_ci				    peer_id, tid);
360662306a36Sopenharmony_ci			continue;
360762306a36Sopenharmony_ci		}
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci		spin_lock_bh(&ar->data_lock);
361062306a36Sopenharmony_ci		txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
361162306a36Sopenharmony_ci		spin_unlock_bh(&ar->data_lock);
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_ci		/* It is okay to release the lock and use txq because RCU read
361462306a36Sopenharmony_ci		 * lock is held.
361562306a36Sopenharmony_ci		 */
361662306a36Sopenharmony_ci
361762306a36Sopenharmony_ci		if (unlikely(!txq)) {
361862306a36Sopenharmony_ci			ath10k_warn(ar, "failed to lookup txq for peer_id %u tid %u\n",
361962306a36Sopenharmony_ci				    peer_id, tid);
362062306a36Sopenharmony_ci			continue;
362162306a36Sopenharmony_ci		}
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci		spin_lock_bh(&ar->htt.tx_lock);
362462306a36Sopenharmony_ci		artxq = (void *)txq->drv_priv;
362562306a36Sopenharmony_ci		artxq->num_push_allowed = le16_to_cpu(record->num_max_msdus);
362662306a36Sopenharmony_ci		spin_unlock_bh(&ar->htt.tx_lock);
362762306a36Sopenharmony_ci	}
362862306a36Sopenharmony_ci
362962306a36Sopenharmony_ci	rcu_read_unlock();
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci	ath10k_mac_tx_push_pending(ar);
363262306a36Sopenharmony_ci}
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_civoid ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
363562306a36Sopenharmony_ci{
363662306a36Sopenharmony_ci	bool release;
363762306a36Sopenharmony_ci
363862306a36Sopenharmony_ci	release = ath10k_htt_t2h_msg_handler(ar, skb);
363962306a36Sopenharmony_ci
364062306a36Sopenharmony_ci	/* Free the indication buffer */
364162306a36Sopenharmony_ci	if (release)
364262306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
364362306a36Sopenharmony_ci}
364462306a36Sopenharmony_ci
364562306a36Sopenharmony_cistatic inline s8 ath10k_get_legacy_rate_idx(struct ath10k *ar, u8 rate)
364662306a36Sopenharmony_ci{
364762306a36Sopenharmony_ci	static const u8 legacy_rates[] = {1, 2, 5, 11, 6, 9, 12,
364862306a36Sopenharmony_ci					  18, 24, 36, 48, 54};
364962306a36Sopenharmony_ci	int i;
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(legacy_rates); i++) {
365262306a36Sopenharmony_ci		if (rate == legacy_rates[i])
365362306a36Sopenharmony_ci			return i;
365462306a36Sopenharmony_ci	}
365562306a36Sopenharmony_ci
365662306a36Sopenharmony_ci	ath10k_warn(ar, "Invalid legacy rate %d peer stats", rate);
365762306a36Sopenharmony_ci	return -EINVAL;
365862306a36Sopenharmony_ci}
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_cistatic void
366162306a36Sopenharmony_ciath10k_accumulate_per_peer_tx_stats(struct ath10k *ar,
366262306a36Sopenharmony_ci				    struct ath10k_sta *arsta,
366362306a36Sopenharmony_ci				    struct ath10k_per_peer_tx_stats *pstats,
366462306a36Sopenharmony_ci				    s8 legacy_rate_idx)
366562306a36Sopenharmony_ci{
366662306a36Sopenharmony_ci	struct rate_info *txrate = &arsta->txrate;
366762306a36Sopenharmony_ci	struct ath10k_htt_tx_stats *tx_stats;
366862306a36Sopenharmony_ci	int idx, ht_idx, gi, mcs, bw, nss;
366962306a36Sopenharmony_ci	unsigned long flags;
367062306a36Sopenharmony_ci
367162306a36Sopenharmony_ci	if (!arsta->tx_stats)
367262306a36Sopenharmony_ci		return;
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_ci	tx_stats = arsta->tx_stats;
367562306a36Sopenharmony_ci	flags = txrate->flags;
367662306a36Sopenharmony_ci	gi = test_bit(ATH10K_RATE_INFO_FLAGS_SGI_BIT, &flags);
367762306a36Sopenharmony_ci	mcs = ATH10K_HW_MCS_RATE(pstats->ratecode);
367862306a36Sopenharmony_ci	bw = txrate->bw;
367962306a36Sopenharmony_ci	nss = txrate->nss;
368062306a36Sopenharmony_ci	ht_idx = mcs + (nss - 1) * 8;
368162306a36Sopenharmony_ci	idx = mcs * 8 + 8 * 10 * (nss - 1);
368262306a36Sopenharmony_ci	idx += bw * 2 + gi;
368362306a36Sopenharmony_ci
368462306a36Sopenharmony_ci#define STATS_OP_FMT(name) tx_stats->stats[ATH10K_STATS_TYPE_##name]
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci	if (txrate->flags & RATE_INFO_FLAGS_VHT_MCS) {
368762306a36Sopenharmony_ci		STATS_OP_FMT(SUCC).vht[0][mcs] += pstats->succ_bytes;
368862306a36Sopenharmony_ci		STATS_OP_FMT(SUCC).vht[1][mcs] += pstats->succ_pkts;
368962306a36Sopenharmony_ci		STATS_OP_FMT(FAIL).vht[0][mcs] += pstats->failed_bytes;
369062306a36Sopenharmony_ci		STATS_OP_FMT(FAIL).vht[1][mcs] += pstats->failed_pkts;
369162306a36Sopenharmony_ci		STATS_OP_FMT(RETRY).vht[0][mcs] += pstats->retry_bytes;
369262306a36Sopenharmony_ci		STATS_OP_FMT(RETRY).vht[1][mcs] += pstats->retry_pkts;
369362306a36Sopenharmony_ci	} else if (txrate->flags & RATE_INFO_FLAGS_MCS) {
369462306a36Sopenharmony_ci		STATS_OP_FMT(SUCC).ht[0][ht_idx] += pstats->succ_bytes;
369562306a36Sopenharmony_ci		STATS_OP_FMT(SUCC).ht[1][ht_idx] += pstats->succ_pkts;
369662306a36Sopenharmony_ci		STATS_OP_FMT(FAIL).ht[0][ht_idx] += pstats->failed_bytes;
369762306a36Sopenharmony_ci		STATS_OP_FMT(FAIL).ht[1][ht_idx] += pstats->failed_pkts;
369862306a36Sopenharmony_ci		STATS_OP_FMT(RETRY).ht[0][ht_idx] += pstats->retry_bytes;
369962306a36Sopenharmony_ci		STATS_OP_FMT(RETRY).ht[1][ht_idx] += pstats->retry_pkts;
370062306a36Sopenharmony_ci	} else {
370162306a36Sopenharmony_ci		mcs = legacy_rate_idx;
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci		STATS_OP_FMT(SUCC).legacy[0][mcs] += pstats->succ_bytes;
370462306a36Sopenharmony_ci		STATS_OP_FMT(SUCC).legacy[1][mcs] += pstats->succ_pkts;
370562306a36Sopenharmony_ci		STATS_OP_FMT(FAIL).legacy[0][mcs] += pstats->failed_bytes;
370662306a36Sopenharmony_ci		STATS_OP_FMT(FAIL).legacy[1][mcs] += pstats->failed_pkts;
370762306a36Sopenharmony_ci		STATS_OP_FMT(RETRY).legacy[0][mcs] += pstats->retry_bytes;
370862306a36Sopenharmony_ci		STATS_OP_FMT(RETRY).legacy[1][mcs] += pstats->retry_pkts;
370962306a36Sopenharmony_ci	}
371062306a36Sopenharmony_ci
371162306a36Sopenharmony_ci	if (ATH10K_HW_AMPDU(pstats->flags)) {
371262306a36Sopenharmony_ci		tx_stats->ba_fails += ATH10K_HW_BA_FAIL(pstats->flags);
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci		if (txrate->flags & RATE_INFO_FLAGS_MCS) {
371562306a36Sopenharmony_ci			STATS_OP_FMT(AMPDU).ht[0][ht_idx] +=
371662306a36Sopenharmony_ci				pstats->succ_bytes + pstats->retry_bytes;
371762306a36Sopenharmony_ci			STATS_OP_FMT(AMPDU).ht[1][ht_idx] +=
371862306a36Sopenharmony_ci				pstats->succ_pkts + pstats->retry_pkts;
371962306a36Sopenharmony_ci		} else {
372062306a36Sopenharmony_ci			STATS_OP_FMT(AMPDU).vht[0][mcs] +=
372162306a36Sopenharmony_ci				pstats->succ_bytes + pstats->retry_bytes;
372262306a36Sopenharmony_ci			STATS_OP_FMT(AMPDU).vht[1][mcs] +=
372362306a36Sopenharmony_ci				pstats->succ_pkts + pstats->retry_pkts;
372462306a36Sopenharmony_ci		}
372562306a36Sopenharmony_ci		STATS_OP_FMT(AMPDU).bw[0][bw] +=
372662306a36Sopenharmony_ci			pstats->succ_bytes + pstats->retry_bytes;
372762306a36Sopenharmony_ci		STATS_OP_FMT(AMPDU).nss[0][nss - 1] +=
372862306a36Sopenharmony_ci			pstats->succ_bytes + pstats->retry_bytes;
372962306a36Sopenharmony_ci		STATS_OP_FMT(AMPDU).gi[0][gi] +=
373062306a36Sopenharmony_ci			pstats->succ_bytes + pstats->retry_bytes;
373162306a36Sopenharmony_ci		STATS_OP_FMT(AMPDU).rate_table[0][idx] +=
373262306a36Sopenharmony_ci			pstats->succ_bytes + pstats->retry_bytes;
373362306a36Sopenharmony_ci		STATS_OP_FMT(AMPDU).bw[1][bw] +=
373462306a36Sopenharmony_ci			pstats->succ_pkts + pstats->retry_pkts;
373562306a36Sopenharmony_ci		STATS_OP_FMT(AMPDU).nss[1][nss - 1] +=
373662306a36Sopenharmony_ci			pstats->succ_pkts + pstats->retry_pkts;
373762306a36Sopenharmony_ci		STATS_OP_FMT(AMPDU).gi[1][gi] +=
373862306a36Sopenharmony_ci			pstats->succ_pkts + pstats->retry_pkts;
373962306a36Sopenharmony_ci		STATS_OP_FMT(AMPDU).rate_table[1][idx] +=
374062306a36Sopenharmony_ci			pstats->succ_pkts + pstats->retry_pkts;
374162306a36Sopenharmony_ci	} else {
374262306a36Sopenharmony_ci		tx_stats->ack_fails +=
374362306a36Sopenharmony_ci				ATH10K_HW_BA_FAIL(pstats->flags);
374462306a36Sopenharmony_ci	}
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	STATS_OP_FMT(SUCC).bw[0][bw] += pstats->succ_bytes;
374762306a36Sopenharmony_ci	STATS_OP_FMT(SUCC).nss[0][nss - 1] += pstats->succ_bytes;
374862306a36Sopenharmony_ci	STATS_OP_FMT(SUCC).gi[0][gi] += pstats->succ_bytes;
374962306a36Sopenharmony_ci
375062306a36Sopenharmony_ci	STATS_OP_FMT(SUCC).bw[1][bw] += pstats->succ_pkts;
375162306a36Sopenharmony_ci	STATS_OP_FMT(SUCC).nss[1][nss - 1] += pstats->succ_pkts;
375262306a36Sopenharmony_ci	STATS_OP_FMT(SUCC).gi[1][gi] += pstats->succ_pkts;
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci	STATS_OP_FMT(FAIL).bw[0][bw] += pstats->failed_bytes;
375562306a36Sopenharmony_ci	STATS_OP_FMT(FAIL).nss[0][nss - 1] += pstats->failed_bytes;
375662306a36Sopenharmony_ci	STATS_OP_FMT(FAIL).gi[0][gi] += pstats->failed_bytes;
375762306a36Sopenharmony_ci
375862306a36Sopenharmony_ci	STATS_OP_FMT(FAIL).bw[1][bw] += pstats->failed_pkts;
375962306a36Sopenharmony_ci	STATS_OP_FMT(FAIL).nss[1][nss - 1] += pstats->failed_pkts;
376062306a36Sopenharmony_ci	STATS_OP_FMT(FAIL).gi[1][gi] += pstats->failed_pkts;
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci	STATS_OP_FMT(RETRY).bw[0][bw] += pstats->retry_bytes;
376362306a36Sopenharmony_ci	STATS_OP_FMT(RETRY).nss[0][nss - 1] += pstats->retry_bytes;
376462306a36Sopenharmony_ci	STATS_OP_FMT(RETRY).gi[0][gi] += pstats->retry_bytes;
376562306a36Sopenharmony_ci
376662306a36Sopenharmony_ci	STATS_OP_FMT(RETRY).bw[1][bw] += pstats->retry_pkts;
376762306a36Sopenharmony_ci	STATS_OP_FMT(RETRY).nss[1][nss - 1] += pstats->retry_pkts;
376862306a36Sopenharmony_ci	STATS_OP_FMT(RETRY).gi[1][gi] += pstats->retry_pkts;
376962306a36Sopenharmony_ci
377062306a36Sopenharmony_ci	if (txrate->flags >= RATE_INFO_FLAGS_MCS) {
377162306a36Sopenharmony_ci		STATS_OP_FMT(SUCC).rate_table[0][idx] += pstats->succ_bytes;
377262306a36Sopenharmony_ci		STATS_OP_FMT(SUCC).rate_table[1][idx] += pstats->succ_pkts;
377362306a36Sopenharmony_ci		STATS_OP_FMT(FAIL).rate_table[0][idx] += pstats->failed_bytes;
377462306a36Sopenharmony_ci		STATS_OP_FMT(FAIL).rate_table[1][idx] += pstats->failed_pkts;
377562306a36Sopenharmony_ci		STATS_OP_FMT(RETRY).rate_table[0][idx] += pstats->retry_bytes;
377662306a36Sopenharmony_ci		STATS_OP_FMT(RETRY).rate_table[1][idx] += pstats->retry_pkts;
377762306a36Sopenharmony_ci	}
377862306a36Sopenharmony_ci
377962306a36Sopenharmony_ci	tx_stats->tx_duration += pstats->duration;
378062306a36Sopenharmony_ci}
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_cistatic void
378362306a36Sopenharmony_ciath10k_update_per_peer_tx_stats(struct ath10k *ar,
378462306a36Sopenharmony_ci				struct ieee80211_sta *sta,
378562306a36Sopenharmony_ci				struct ath10k_per_peer_tx_stats *peer_stats)
378662306a36Sopenharmony_ci{
378762306a36Sopenharmony_ci	struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
378862306a36Sopenharmony_ci	struct ieee80211_chanctx_conf *conf = NULL;
378962306a36Sopenharmony_ci	u8 rate = 0, sgi;
379062306a36Sopenharmony_ci	s8 rate_idx = 0;
379162306a36Sopenharmony_ci	bool skip_auto_rate;
379262306a36Sopenharmony_ci	struct rate_info txrate;
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	lockdep_assert_held(&ar->data_lock);
379562306a36Sopenharmony_ci
379662306a36Sopenharmony_ci	txrate.flags = ATH10K_HW_PREAMBLE(peer_stats->ratecode);
379762306a36Sopenharmony_ci	txrate.bw = ATH10K_HW_BW(peer_stats->flags);
379862306a36Sopenharmony_ci	txrate.nss = ATH10K_HW_NSS(peer_stats->ratecode);
379962306a36Sopenharmony_ci	txrate.mcs = ATH10K_HW_MCS_RATE(peer_stats->ratecode);
380062306a36Sopenharmony_ci	sgi = ATH10K_HW_GI(peer_stats->flags);
380162306a36Sopenharmony_ci	skip_auto_rate = ATH10K_FW_SKIPPED_RATE_CTRL(peer_stats->flags);
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci	/* Firmware's rate control skips broadcast/management frames,
380462306a36Sopenharmony_ci	 * if host has configure fixed rates and in some other special cases.
380562306a36Sopenharmony_ci	 */
380662306a36Sopenharmony_ci	if (skip_auto_rate)
380762306a36Sopenharmony_ci		return;
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	if (txrate.flags == WMI_RATE_PREAMBLE_VHT && txrate.mcs > 9) {
381062306a36Sopenharmony_ci		ath10k_warn(ar, "Invalid VHT mcs %d peer stats",  txrate.mcs);
381162306a36Sopenharmony_ci		return;
381262306a36Sopenharmony_ci	}
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci	if (txrate.flags == WMI_RATE_PREAMBLE_HT &&
381562306a36Sopenharmony_ci	    (txrate.mcs > 7 || txrate.nss < 1)) {
381662306a36Sopenharmony_ci		ath10k_warn(ar, "Invalid HT mcs %d nss %d peer stats",
381762306a36Sopenharmony_ci			    txrate.mcs, txrate.nss);
381862306a36Sopenharmony_ci		return;
381962306a36Sopenharmony_ci	}
382062306a36Sopenharmony_ci
382162306a36Sopenharmony_ci	memset(&arsta->txrate, 0, sizeof(arsta->txrate));
382262306a36Sopenharmony_ci	memset(&arsta->tx_info.status, 0, sizeof(arsta->tx_info.status));
382362306a36Sopenharmony_ci	if (txrate.flags == WMI_RATE_PREAMBLE_CCK ||
382462306a36Sopenharmony_ci	    txrate.flags == WMI_RATE_PREAMBLE_OFDM) {
382562306a36Sopenharmony_ci		rate = ATH10K_HW_LEGACY_RATE(peer_stats->ratecode);
382662306a36Sopenharmony_ci		/* This is hacky, FW sends CCK rate 5.5Mbps as 6 */
382762306a36Sopenharmony_ci		if (rate == 6 && txrate.flags == WMI_RATE_PREAMBLE_CCK)
382862306a36Sopenharmony_ci			rate = 5;
382962306a36Sopenharmony_ci		rate_idx = ath10k_get_legacy_rate_idx(ar, rate);
383062306a36Sopenharmony_ci		if (rate_idx < 0)
383162306a36Sopenharmony_ci			return;
383262306a36Sopenharmony_ci		arsta->txrate.legacy = rate;
383362306a36Sopenharmony_ci	} else if (txrate.flags == WMI_RATE_PREAMBLE_HT) {
383462306a36Sopenharmony_ci		arsta->txrate.flags = RATE_INFO_FLAGS_MCS;
383562306a36Sopenharmony_ci		arsta->txrate.mcs = txrate.mcs + 8 * (txrate.nss - 1);
383662306a36Sopenharmony_ci	} else {
383762306a36Sopenharmony_ci		arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
383862306a36Sopenharmony_ci		arsta->txrate.mcs = txrate.mcs;
383962306a36Sopenharmony_ci	}
384062306a36Sopenharmony_ci
384162306a36Sopenharmony_ci	switch (txrate.flags) {
384262306a36Sopenharmony_ci	case WMI_RATE_PREAMBLE_OFDM:
384362306a36Sopenharmony_ci		if (arsta->arvif && arsta->arvif->vif)
384462306a36Sopenharmony_ci			conf = rcu_dereference(arsta->arvif->vif->bss_conf.chanctx_conf);
384562306a36Sopenharmony_ci		if (conf && conf->def.chan->band == NL80211_BAND_5GHZ)
384662306a36Sopenharmony_ci			arsta->tx_info.status.rates[0].idx = rate_idx - 4;
384762306a36Sopenharmony_ci		break;
384862306a36Sopenharmony_ci	case WMI_RATE_PREAMBLE_CCK:
384962306a36Sopenharmony_ci		arsta->tx_info.status.rates[0].idx = rate_idx;
385062306a36Sopenharmony_ci		if (sgi)
385162306a36Sopenharmony_ci			arsta->tx_info.status.rates[0].flags |=
385262306a36Sopenharmony_ci				(IEEE80211_TX_RC_USE_SHORT_PREAMBLE |
385362306a36Sopenharmony_ci				 IEEE80211_TX_RC_SHORT_GI);
385462306a36Sopenharmony_ci		break;
385562306a36Sopenharmony_ci	case WMI_RATE_PREAMBLE_HT:
385662306a36Sopenharmony_ci		arsta->tx_info.status.rates[0].idx =
385762306a36Sopenharmony_ci				txrate.mcs + ((txrate.nss - 1) * 8);
385862306a36Sopenharmony_ci		if (sgi)
385962306a36Sopenharmony_ci			arsta->tx_info.status.rates[0].flags |=
386062306a36Sopenharmony_ci					IEEE80211_TX_RC_SHORT_GI;
386162306a36Sopenharmony_ci		arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_MCS;
386262306a36Sopenharmony_ci		break;
386362306a36Sopenharmony_ci	case WMI_RATE_PREAMBLE_VHT:
386462306a36Sopenharmony_ci		ieee80211_rate_set_vht(&arsta->tx_info.status.rates[0],
386562306a36Sopenharmony_ci				       txrate.mcs, txrate.nss);
386662306a36Sopenharmony_ci		if (sgi)
386762306a36Sopenharmony_ci			arsta->tx_info.status.rates[0].flags |=
386862306a36Sopenharmony_ci						IEEE80211_TX_RC_SHORT_GI;
386962306a36Sopenharmony_ci		arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_VHT_MCS;
387062306a36Sopenharmony_ci		break;
387162306a36Sopenharmony_ci	}
387262306a36Sopenharmony_ci
387362306a36Sopenharmony_ci	arsta->txrate.nss = txrate.nss;
387462306a36Sopenharmony_ci	arsta->txrate.bw = ath10k_bw_to_mac80211_bw(txrate.bw);
387562306a36Sopenharmony_ci	arsta->last_tx_bitrate = cfg80211_calculate_bitrate(&arsta->txrate);
387662306a36Sopenharmony_ci	if (sgi)
387762306a36Sopenharmony_ci		arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
387862306a36Sopenharmony_ci
387962306a36Sopenharmony_ci	switch (arsta->txrate.bw) {
388062306a36Sopenharmony_ci	case RATE_INFO_BW_40:
388162306a36Sopenharmony_ci		arsta->tx_info.status.rates[0].flags |=
388262306a36Sopenharmony_ci				IEEE80211_TX_RC_40_MHZ_WIDTH;
388362306a36Sopenharmony_ci		break;
388462306a36Sopenharmony_ci	case RATE_INFO_BW_80:
388562306a36Sopenharmony_ci		arsta->tx_info.status.rates[0].flags |=
388662306a36Sopenharmony_ci				IEEE80211_TX_RC_80_MHZ_WIDTH;
388762306a36Sopenharmony_ci		break;
388862306a36Sopenharmony_ci	case RATE_INFO_BW_160:
388962306a36Sopenharmony_ci		arsta->tx_info.status.rates[0].flags |=
389062306a36Sopenharmony_ci				IEEE80211_TX_RC_160_MHZ_WIDTH;
389162306a36Sopenharmony_ci		break;
389262306a36Sopenharmony_ci	}
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	if (peer_stats->succ_pkts) {
389562306a36Sopenharmony_ci		arsta->tx_info.flags = IEEE80211_TX_STAT_ACK;
389662306a36Sopenharmony_ci		arsta->tx_info.status.rates[0].count = 1;
389762306a36Sopenharmony_ci		ieee80211_tx_rate_update(ar->hw, sta, &arsta->tx_info);
389862306a36Sopenharmony_ci	}
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci	if (ar->htt.disable_tx_comp) {
390162306a36Sopenharmony_ci		arsta->tx_failed += peer_stats->failed_pkts;
390262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "tx failed %d\n",
390362306a36Sopenharmony_ci			   arsta->tx_failed);
390462306a36Sopenharmony_ci	}
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	arsta->tx_retries += peer_stats->retry_pkts;
390762306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx retries %d", arsta->tx_retries);
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	if (ath10k_debug_is_extd_tx_stats_enabled(ar))
391062306a36Sopenharmony_ci		ath10k_accumulate_per_peer_tx_stats(ar, arsta, peer_stats,
391162306a36Sopenharmony_ci						    rate_idx);
391262306a36Sopenharmony_ci}
391362306a36Sopenharmony_ci
391462306a36Sopenharmony_cistatic void ath10k_htt_fetch_peer_stats(struct ath10k *ar,
391562306a36Sopenharmony_ci					struct sk_buff *skb)
391662306a36Sopenharmony_ci{
391762306a36Sopenharmony_ci	struct htt_resp *resp = (struct htt_resp *)skb->data;
391862306a36Sopenharmony_ci	struct ath10k_per_peer_tx_stats *p_tx_stats = &ar->peer_tx_stats;
391962306a36Sopenharmony_ci	struct htt_per_peer_tx_stats_ind *tx_stats;
392062306a36Sopenharmony_ci	struct ieee80211_sta *sta;
392162306a36Sopenharmony_ci	struct ath10k_peer *peer;
392262306a36Sopenharmony_ci	int peer_id, i;
392362306a36Sopenharmony_ci	u8 ppdu_len, num_ppdu;
392462306a36Sopenharmony_ci
392562306a36Sopenharmony_ci	num_ppdu = resp->peer_tx_stats.num_ppdu;
392662306a36Sopenharmony_ci	ppdu_len = resp->peer_tx_stats.ppdu_len * sizeof(__le32);
392762306a36Sopenharmony_ci
392862306a36Sopenharmony_ci	if (skb->len < sizeof(struct htt_resp_hdr) + num_ppdu * ppdu_len) {
392962306a36Sopenharmony_ci		ath10k_warn(ar, "Invalid peer stats buf length %d\n", skb->len);
393062306a36Sopenharmony_ci		return;
393162306a36Sopenharmony_ci	}
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_ci	tx_stats = (struct htt_per_peer_tx_stats_ind *)
393462306a36Sopenharmony_ci			(resp->peer_tx_stats.payload);
393562306a36Sopenharmony_ci	peer_id = __le16_to_cpu(tx_stats->peer_id);
393662306a36Sopenharmony_ci
393762306a36Sopenharmony_ci	rcu_read_lock();
393862306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
393962306a36Sopenharmony_ci	peer = ath10k_peer_find_by_id(ar, peer_id);
394062306a36Sopenharmony_ci	if (!peer || !peer->sta) {
394162306a36Sopenharmony_ci		ath10k_warn(ar, "Invalid peer id %d peer stats buffer\n",
394262306a36Sopenharmony_ci			    peer_id);
394362306a36Sopenharmony_ci		goto out;
394462306a36Sopenharmony_ci	}
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci	sta = peer->sta;
394762306a36Sopenharmony_ci	for (i = 0; i < num_ppdu; i++) {
394862306a36Sopenharmony_ci		tx_stats = (struct htt_per_peer_tx_stats_ind *)
394962306a36Sopenharmony_ci			   (resp->peer_tx_stats.payload + i * ppdu_len);
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_ci		p_tx_stats->succ_bytes = __le32_to_cpu(tx_stats->succ_bytes);
395262306a36Sopenharmony_ci		p_tx_stats->retry_bytes = __le32_to_cpu(tx_stats->retry_bytes);
395362306a36Sopenharmony_ci		p_tx_stats->failed_bytes =
395462306a36Sopenharmony_ci				__le32_to_cpu(tx_stats->failed_bytes);
395562306a36Sopenharmony_ci		p_tx_stats->ratecode = tx_stats->ratecode;
395662306a36Sopenharmony_ci		p_tx_stats->flags = tx_stats->flags;
395762306a36Sopenharmony_ci		p_tx_stats->succ_pkts = __le16_to_cpu(tx_stats->succ_pkts);
395862306a36Sopenharmony_ci		p_tx_stats->retry_pkts = __le16_to_cpu(tx_stats->retry_pkts);
395962306a36Sopenharmony_ci		p_tx_stats->failed_pkts = __le16_to_cpu(tx_stats->failed_pkts);
396062306a36Sopenharmony_ci		p_tx_stats->duration = __le16_to_cpu(tx_stats->tx_duration);
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci		ath10k_update_per_peer_tx_stats(ar, sta, p_tx_stats);
396362306a36Sopenharmony_ci	}
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ciout:
396662306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
396762306a36Sopenharmony_ci	rcu_read_unlock();
396862306a36Sopenharmony_ci}
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_cistatic void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data)
397162306a36Sopenharmony_ci{
397262306a36Sopenharmony_ci	struct ath10k_pktlog_hdr *hdr = (struct ath10k_pktlog_hdr *)data;
397362306a36Sopenharmony_ci	struct ath10k_per_peer_tx_stats *p_tx_stats = &ar->peer_tx_stats;
397462306a36Sopenharmony_ci	struct ath10k_10_2_peer_tx_stats *tx_stats;
397562306a36Sopenharmony_ci	struct ieee80211_sta *sta;
397662306a36Sopenharmony_ci	struct ath10k_peer *peer;
397762306a36Sopenharmony_ci	u16 log_type = __le16_to_cpu(hdr->log_type);
397862306a36Sopenharmony_ci	u32 peer_id = 0, i;
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci	if (log_type != ATH_PKTLOG_TYPE_TX_STAT)
398162306a36Sopenharmony_ci		return;
398262306a36Sopenharmony_ci
398362306a36Sopenharmony_ci	tx_stats = (struct ath10k_10_2_peer_tx_stats *)((hdr->payload) +
398462306a36Sopenharmony_ci		    ATH10K_10_2_TX_STATS_OFFSET);
398562306a36Sopenharmony_ci
398662306a36Sopenharmony_ci	if (!tx_stats->tx_ppdu_cnt)
398762306a36Sopenharmony_ci		return;
398862306a36Sopenharmony_ci
398962306a36Sopenharmony_ci	peer_id = tx_stats->peer_id;
399062306a36Sopenharmony_ci
399162306a36Sopenharmony_ci	rcu_read_lock();
399262306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
399362306a36Sopenharmony_ci	peer = ath10k_peer_find_by_id(ar, peer_id);
399462306a36Sopenharmony_ci	if (!peer || !peer->sta) {
399562306a36Sopenharmony_ci		ath10k_warn(ar, "Invalid peer id %d in peer stats buffer\n",
399662306a36Sopenharmony_ci			    peer_id);
399762306a36Sopenharmony_ci		goto out;
399862306a36Sopenharmony_ci	}
399962306a36Sopenharmony_ci
400062306a36Sopenharmony_ci	sta = peer->sta;
400162306a36Sopenharmony_ci	for (i = 0; i < tx_stats->tx_ppdu_cnt; i++) {
400262306a36Sopenharmony_ci		p_tx_stats->succ_bytes =
400362306a36Sopenharmony_ci			__le16_to_cpu(tx_stats->success_bytes[i]);
400462306a36Sopenharmony_ci		p_tx_stats->retry_bytes =
400562306a36Sopenharmony_ci			__le16_to_cpu(tx_stats->retry_bytes[i]);
400662306a36Sopenharmony_ci		p_tx_stats->failed_bytes =
400762306a36Sopenharmony_ci			__le16_to_cpu(tx_stats->failed_bytes[i]);
400862306a36Sopenharmony_ci		p_tx_stats->ratecode = tx_stats->ratecode[i];
400962306a36Sopenharmony_ci		p_tx_stats->flags = tx_stats->flags[i];
401062306a36Sopenharmony_ci		p_tx_stats->succ_pkts = tx_stats->success_pkts[i];
401162306a36Sopenharmony_ci		p_tx_stats->retry_pkts = tx_stats->retry_pkts[i];
401262306a36Sopenharmony_ci		p_tx_stats->failed_pkts = tx_stats->failed_pkts[i];
401362306a36Sopenharmony_ci
401462306a36Sopenharmony_ci		ath10k_update_per_peer_tx_stats(ar, sta, p_tx_stats);
401562306a36Sopenharmony_ci	}
401662306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
401762306a36Sopenharmony_ci	rcu_read_unlock();
401862306a36Sopenharmony_ci
401962306a36Sopenharmony_ci	return;
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_ciout:
402262306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
402362306a36Sopenharmony_ci	rcu_read_unlock();
402462306a36Sopenharmony_ci}
402562306a36Sopenharmony_ci
402662306a36Sopenharmony_cistatic int ath10k_htt_rx_pn_len(enum htt_security_types sec_type)
402762306a36Sopenharmony_ci{
402862306a36Sopenharmony_ci	switch (sec_type) {
402962306a36Sopenharmony_ci	case HTT_SECURITY_TKIP:
403062306a36Sopenharmony_ci	case HTT_SECURITY_TKIP_NOMIC:
403162306a36Sopenharmony_ci	case HTT_SECURITY_AES_CCMP:
403262306a36Sopenharmony_ci		return 48;
403362306a36Sopenharmony_ci	default:
403462306a36Sopenharmony_ci		return 0;
403562306a36Sopenharmony_ci	}
403662306a36Sopenharmony_ci}
403762306a36Sopenharmony_ci
403862306a36Sopenharmony_cistatic void ath10k_htt_rx_sec_ind_handler(struct ath10k *ar,
403962306a36Sopenharmony_ci					  struct htt_security_indication *ev)
404062306a36Sopenharmony_ci{
404162306a36Sopenharmony_ci	enum htt_txrx_sec_cast_type sec_index;
404262306a36Sopenharmony_ci	enum htt_security_types sec_type;
404362306a36Sopenharmony_ci	struct ath10k_peer *peer;
404462306a36Sopenharmony_ci
404562306a36Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
404662306a36Sopenharmony_ci
404762306a36Sopenharmony_ci	peer = ath10k_peer_find_by_id(ar, __le16_to_cpu(ev->peer_id));
404862306a36Sopenharmony_ci	if (!peer) {
404962306a36Sopenharmony_ci		ath10k_warn(ar, "failed to find peer id %d for security indication",
405062306a36Sopenharmony_ci			    __le16_to_cpu(ev->peer_id));
405162306a36Sopenharmony_ci		goto out;
405262306a36Sopenharmony_ci	}
405362306a36Sopenharmony_ci
405462306a36Sopenharmony_ci	sec_type = MS(ev->flags, HTT_SECURITY_TYPE);
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_ci	if (ev->flags & HTT_SECURITY_IS_UNICAST)
405762306a36Sopenharmony_ci		sec_index = HTT_TXRX_SEC_UCAST;
405862306a36Sopenharmony_ci	else
405962306a36Sopenharmony_ci		sec_index = HTT_TXRX_SEC_MCAST;
406062306a36Sopenharmony_ci
406162306a36Sopenharmony_ci	peer->rx_pn[sec_index].sec_type = sec_type;
406262306a36Sopenharmony_ci	peer->rx_pn[sec_index].pn_len = ath10k_htt_rx_pn_len(sec_type);
406362306a36Sopenharmony_ci
406462306a36Sopenharmony_ci	memset(peer->tids_last_pn_valid, 0, sizeof(peer->tids_last_pn_valid));
406562306a36Sopenharmony_ci	memset(peer->tids_last_pn, 0, sizeof(peer->tids_last_pn));
406662306a36Sopenharmony_ci
406762306a36Sopenharmony_ciout:
406862306a36Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
406962306a36Sopenharmony_ci}
407062306a36Sopenharmony_ci
407162306a36Sopenharmony_cibool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
407262306a36Sopenharmony_ci{
407362306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
407462306a36Sopenharmony_ci	struct htt_resp *resp = (struct htt_resp *)skb->data;
407562306a36Sopenharmony_ci	enum htt_t2h_msg_type type;
407662306a36Sopenharmony_ci
407762306a36Sopenharmony_ci	/* confirm alignment */
407862306a36Sopenharmony_ci	if (!IS_ALIGNED((unsigned long)skb->data, 4))
407962306a36Sopenharmony_ci		ath10k_warn(ar, "unaligned htt message, expect trouble\n");
408062306a36Sopenharmony_ci
408162306a36Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n",
408262306a36Sopenharmony_ci		   resp->hdr.msg_type);
408362306a36Sopenharmony_ci
408462306a36Sopenharmony_ci	if (resp->hdr.msg_type >= ar->htt.t2h_msg_types_max) {
408562306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx, unsupported msg_type: 0x%0X\n max: 0x%0X",
408662306a36Sopenharmony_ci			   resp->hdr.msg_type, ar->htt.t2h_msg_types_max);
408762306a36Sopenharmony_ci		return true;
408862306a36Sopenharmony_ci	}
408962306a36Sopenharmony_ci	type = ar->htt.t2h_msg_types[resp->hdr.msg_type];
409062306a36Sopenharmony_ci
409162306a36Sopenharmony_ci	switch (type) {
409262306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_VERSION_CONF: {
409362306a36Sopenharmony_ci		htt->target_version_major = resp->ver_resp.major;
409462306a36Sopenharmony_ci		htt->target_version_minor = resp->ver_resp.minor;
409562306a36Sopenharmony_ci		complete(&htt->target_version_received);
409662306a36Sopenharmony_ci		break;
409762306a36Sopenharmony_ci	}
409862306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_RX_IND:
409962306a36Sopenharmony_ci		if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL) {
410062306a36Sopenharmony_ci			ath10k_htt_rx_proc_rx_ind_ll(htt, &resp->rx_ind);
410162306a36Sopenharmony_ci		} else {
410262306a36Sopenharmony_ci			skb_queue_tail(&htt->rx_indication_head, skb);
410362306a36Sopenharmony_ci			return false;
410462306a36Sopenharmony_ci		}
410562306a36Sopenharmony_ci		break;
410662306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_PEER_MAP: {
410762306a36Sopenharmony_ci		struct htt_peer_map_event ev = {
410862306a36Sopenharmony_ci			.vdev_id = resp->peer_map.vdev_id,
410962306a36Sopenharmony_ci			.peer_id = __le16_to_cpu(resp->peer_map.peer_id),
411062306a36Sopenharmony_ci		};
411162306a36Sopenharmony_ci		memcpy(ev.addr, resp->peer_map.addr, sizeof(ev.addr));
411262306a36Sopenharmony_ci		ath10k_peer_map_event(htt, &ev);
411362306a36Sopenharmony_ci		break;
411462306a36Sopenharmony_ci	}
411562306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_PEER_UNMAP: {
411662306a36Sopenharmony_ci		struct htt_peer_unmap_event ev = {
411762306a36Sopenharmony_ci			.peer_id = __le16_to_cpu(resp->peer_unmap.peer_id),
411862306a36Sopenharmony_ci		};
411962306a36Sopenharmony_ci		ath10k_peer_unmap_event(htt, &ev);
412062306a36Sopenharmony_ci		break;
412162306a36Sopenharmony_ci	}
412262306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION: {
412362306a36Sopenharmony_ci		struct htt_tx_done tx_done = {};
412462306a36Sopenharmony_ci		struct ath10k_htt *htt = &ar->htt;
412562306a36Sopenharmony_ci		struct ath10k_htc *htc = &ar->htc;
412662306a36Sopenharmony_ci		struct ath10k_htc_ep *ep = &ar->htc.endpoint[htt->eid];
412762306a36Sopenharmony_ci		int status = __le32_to_cpu(resp->mgmt_tx_completion.status);
412862306a36Sopenharmony_ci		int info = __le32_to_cpu(resp->mgmt_tx_completion.info);
412962306a36Sopenharmony_ci
413062306a36Sopenharmony_ci		tx_done.msdu_id = __le32_to_cpu(resp->mgmt_tx_completion.desc_id);
413162306a36Sopenharmony_ci
413262306a36Sopenharmony_ci		switch (status) {
413362306a36Sopenharmony_ci		case HTT_MGMT_TX_STATUS_OK:
413462306a36Sopenharmony_ci			tx_done.status = HTT_TX_COMPL_STATE_ACK;
413562306a36Sopenharmony_ci			if (test_bit(WMI_SERVICE_HTT_MGMT_TX_COMP_VALID_FLAGS,
413662306a36Sopenharmony_ci				     ar->wmi.svc_map) &&
413762306a36Sopenharmony_ci			    (resp->mgmt_tx_completion.flags &
413862306a36Sopenharmony_ci			     HTT_MGMT_TX_CMPL_FLAG_ACK_RSSI)) {
413962306a36Sopenharmony_ci				tx_done.ack_rssi =
414062306a36Sopenharmony_ci				FIELD_GET(HTT_MGMT_TX_CMPL_INFO_ACK_RSSI_MASK,
414162306a36Sopenharmony_ci					  info);
414262306a36Sopenharmony_ci			}
414362306a36Sopenharmony_ci			break;
414462306a36Sopenharmony_ci		case HTT_MGMT_TX_STATUS_RETRY:
414562306a36Sopenharmony_ci			tx_done.status = HTT_TX_COMPL_STATE_NOACK;
414662306a36Sopenharmony_ci			break;
414762306a36Sopenharmony_ci		case HTT_MGMT_TX_STATUS_DROP:
414862306a36Sopenharmony_ci			tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
414962306a36Sopenharmony_ci			break;
415062306a36Sopenharmony_ci		}
415162306a36Sopenharmony_ci
415262306a36Sopenharmony_ci		if (htt->disable_tx_comp) {
415362306a36Sopenharmony_ci			spin_lock_bh(&htc->tx_lock);
415462306a36Sopenharmony_ci			ep->tx_credits++;
415562306a36Sopenharmony_ci			spin_unlock_bh(&htc->tx_lock);
415662306a36Sopenharmony_ci		}
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_ci		status = ath10k_txrx_tx_unref(htt, &tx_done);
415962306a36Sopenharmony_ci		if (!status) {
416062306a36Sopenharmony_ci			spin_lock_bh(&htt->tx_lock);
416162306a36Sopenharmony_ci			ath10k_htt_tx_mgmt_dec_pending(htt);
416262306a36Sopenharmony_ci			spin_unlock_bh(&htt->tx_lock);
416362306a36Sopenharmony_ci		}
416462306a36Sopenharmony_ci		break;
416562306a36Sopenharmony_ci	}
416662306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_TX_COMPL_IND:
416762306a36Sopenharmony_ci		ath10k_htt_rx_tx_compl_ind(htt->ar, skb);
416862306a36Sopenharmony_ci		break;
416962306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_SEC_IND: {
417062306a36Sopenharmony_ci		struct ath10k *ar = htt->ar;
417162306a36Sopenharmony_ci		struct htt_security_indication *ev = &resp->security_indication;
417262306a36Sopenharmony_ci
417362306a36Sopenharmony_ci		ath10k_htt_rx_sec_ind_handler(ar, ev);
417462306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT,
417562306a36Sopenharmony_ci			   "sec ind peer_id %d unicast %d type %d\n",
417662306a36Sopenharmony_ci			  __le16_to_cpu(ev->peer_id),
417762306a36Sopenharmony_ci			  !!(ev->flags & HTT_SECURITY_IS_UNICAST),
417862306a36Sopenharmony_ci			  MS(ev->flags, HTT_SECURITY_TYPE));
417962306a36Sopenharmony_ci		complete(&ar->install_key_done);
418062306a36Sopenharmony_ci		break;
418162306a36Sopenharmony_ci	}
418262306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_RX_FRAG_IND: {
418362306a36Sopenharmony_ci		ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
418462306a36Sopenharmony_ci				skb->data, skb->len);
418562306a36Sopenharmony_ci		atomic_inc(&htt->num_mpdus_ready);
418662306a36Sopenharmony_ci
418762306a36Sopenharmony_ci		return ath10k_htt_rx_proc_rx_frag_ind(htt,
418862306a36Sopenharmony_ci						      &resp->rx_frag_ind,
418962306a36Sopenharmony_ci						      skb);
419062306a36Sopenharmony_ci	}
419162306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_TEST:
419262306a36Sopenharmony_ci		break;
419362306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_STATS_CONF:
419462306a36Sopenharmony_ci		trace_ath10k_htt_stats(ar, skb->data, skb->len);
419562306a36Sopenharmony_ci		break;
419662306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_TX_INSPECT_IND:
419762306a36Sopenharmony_ci		/* Firmware can return tx frames if it's unable to fully
419862306a36Sopenharmony_ci		 * process them and suspects host may be able to fix it. ath10k
419962306a36Sopenharmony_ci		 * sends all tx frames as already inspected so this shouldn't
420062306a36Sopenharmony_ci		 * happen unless fw has a bug.
420162306a36Sopenharmony_ci		 */
420262306a36Sopenharmony_ci		ath10k_warn(ar, "received an unexpected htt tx inspect event\n");
420362306a36Sopenharmony_ci		break;
420462306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_RX_ADDBA:
420562306a36Sopenharmony_ci		ath10k_htt_rx_addba(ar, resp);
420662306a36Sopenharmony_ci		break;
420762306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_RX_DELBA:
420862306a36Sopenharmony_ci		ath10k_htt_rx_delba(ar, resp);
420962306a36Sopenharmony_ci		break;
421062306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_PKTLOG: {
421162306a36Sopenharmony_ci		trace_ath10k_htt_pktlog(ar, resp->pktlog_msg.payload,
421262306a36Sopenharmony_ci					skb->len -
421362306a36Sopenharmony_ci					offsetof(struct htt_resp,
421462306a36Sopenharmony_ci						 pktlog_msg.payload));
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci		if (ath10k_peer_stats_enabled(ar))
421762306a36Sopenharmony_ci			ath10k_fetch_10_2_tx_stats(ar,
421862306a36Sopenharmony_ci						   resp->pktlog_msg.payload);
421962306a36Sopenharmony_ci		break;
422062306a36Sopenharmony_ci	}
422162306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_RX_FLUSH: {
422262306a36Sopenharmony_ci		/* Ignore this event because mac80211 takes care of Rx
422362306a36Sopenharmony_ci		 * aggregation reordering.
422462306a36Sopenharmony_ci		 */
422562306a36Sopenharmony_ci		break;
422662306a36Sopenharmony_ci	}
422762306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: {
422862306a36Sopenharmony_ci		skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
422962306a36Sopenharmony_ci		return false;
423062306a36Sopenharmony_ci	}
423162306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND: {
423262306a36Sopenharmony_ci		struct ath10k_htt *htt = &ar->htt;
423362306a36Sopenharmony_ci		struct ath10k_htc *htc = &ar->htc;
423462306a36Sopenharmony_ci		struct ath10k_htc_ep *ep = &ar->htc.endpoint[htt->eid];
423562306a36Sopenharmony_ci		u32 msg_word = __le32_to_cpu(*(__le32 *)resp);
423662306a36Sopenharmony_ci		int htt_credit_delta;
423762306a36Sopenharmony_ci
423862306a36Sopenharmony_ci		htt_credit_delta = HTT_TX_CREDIT_DELTA_ABS_GET(msg_word);
423962306a36Sopenharmony_ci		if (HTT_TX_CREDIT_SIGN_BIT_GET(msg_word))
424062306a36Sopenharmony_ci			htt_credit_delta = -htt_credit_delta;
424162306a36Sopenharmony_ci
424262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT,
424362306a36Sopenharmony_ci			   "htt credit update delta %d\n",
424462306a36Sopenharmony_ci			   htt_credit_delta);
424562306a36Sopenharmony_ci
424662306a36Sopenharmony_ci		if (htt->disable_tx_comp) {
424762306a36Sopenharmony_ci			spin_lock_bh(&htc->tx_lock);
424862306a36Sopenharmony_ci			ep->tx_credits += htt_credit_delta;
424962306a36Sopenharmony_ci			spin_unlock_bh(&htc->tx_lock);
425062306a36Sopenharmony_ci			ath10k_dbg(ar, ATH10K_DBG_HTT,
425162306a36Sopenharmony_ci				   "htt credit total %d\n",
425262306a36Sopenharmony_ci				   ep->tx_credits);
425362306a36Sopenharmony_ci			ep->ep_ops.ep_tx_credits(htc->ar);
425462306a36Sopenharmony_ci		}
425562306a36Sopenharmony_ci		break;
425662306a36Sopenharmony_ci	}
425762306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_CHAN_CHANGE: {
425862306a36Sopenharmony_ci		u32 phymode = __le32_to_cpu(resp->chan_change.phymode);
425962306a36Sopenharmony_ci		u32 freq = __le32_to_cpu(resp->chan_change.freq);
426062306a36Sopenharmony_ci
426162306a36Sopenharmony_ci		ar->tgt_oper_chan = ieee80211_get_channel(ar->hw->wiphy, freq);
426262306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT,
426362306a36Sopenharmony_ci			   "htt chan change freq %u phymode %s\n",
426462306a36Sopenharmony_ci			   freq, ath10k_wmi_phymode_str(phymode));
426562306a36Sopenharmony_ci		break;
426662306a36Sopenharmony_ci	}
426762306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_AGGR_CONF:
426862306a36Sopenharmony_ci		break;
426962306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_TX_FETCH_IND: {
427062306a36Sopenharmony_ci		struct sk_buff *tx_fetch_ind = skb_copy(skb, GFP_ATOMIC);
427162306a36Sopenharmony_ci
427262306a36Sopenharmony_ci		if (!tx_fetch_ind) {
427362306a36Sopenharmony_ci			ath10k_warn(ar, "failed to copy htt tx fetch ind\n");
427462306a36Sopenharmony_ci			break;
427562306a36Sopenharmony_ci		}
427662306a36Sopenharmony_ci		skb_queue_tail(&htt->tx_fetch_ind_q, tx_fetch_ind);
427762306a36Sopenharmony_ci		break;
427862306a36Sopenharmony_ci	}
427962306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
428062306a36Sopenharmony_ci		ath10k_htt_rx_tx_fetch_confirm(ar, skb);
428162306a36Sopenharmony_ci		break;
428262306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND:
428362306a36Sopenharmony_ci		ath10k_htt_rx_tx_mode_switch_ind(ar, skb);
428462306a36Sopenharmony_ci		break;
428562306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_PEER_STATS:
428662306a36Sopenharmony_ci		ath10k_htt_fetch_peer_stats(ar, skb);
428762306a36Sopenharmony_ci		break;
428862306a36Sopenharmony_ci	case HTT_T2H_MSG_TYPE_EN_STATS:
428962306a36Sopenharmony_ci	default:
429062306a36Sopenharmony_ci		ath10k_warn(ar, "htt event (%d) not handled\n",
429162306a36Sopenharmony_ci			    resp->hdr.msg_type);
429262306a36Sopenharmony_ci		ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
429362306a36Sopenharmony_ci				skb->data, skb->len);
429462306a36Sopenharmony_ci		break;
429562306a36Sopenharmony_ci	}
429662306a36Sopenharmony_ci	return true;
429762306a36Sopenharmony_ci}
429862306a36Sopenharmony_ciEXPORT_SYMBOL(ath10k_htt_t2h_msg_handler);
429962306a36Sopenharmony_ci
430062306a36Sopenharmony_civoid ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
430162306a36Sopenharmony_ci					     struct sk_buff *skb)
430262306a36Sopenharmony_ci{
430362306a36Sopenharmony_ci	trace_ath10k_htt_pktlog(ar, skb->data, skb->len);
430462306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
430562306a36Sopenharmony_ci}
430662306a36Sopenharmony_ciEXPORT_SYMBOL(ath10k_htt_rx_pktlog_completion_handler);
430762306a36Sopenharmony_ci
430862306a36Sopenharmony_cistatic int ath10k_htt_rx_deliver_msdu(struct ath10k *ar, int quota, int budget)
430962306a36Sopenharmony_ci{
431062306a36Sopenharmony_ci	struct sk_buff *skb;
431162306a36Sopenharmony_ci
431262306a36Sopenharmony_ci	while (quota < budget) {
431362306a36Sopenharmony_ci		if (skb_queue_empty(&ar->htt.rx_msdus_q))
431462306a36Sopenharmony_ci			break;
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci		skb = skb_dequeue(&ar->htt.rx_msdus_q);
431762306a36Sopenharmony_ci		if (!skb)
431862306a36Sopenharmony_ci			break;
431962306a36Sopenharmony_ci		ath10k_process_rx(ar, skb);
432062306a36Sopenharmony_ci		quota++;
432162306a36Sopenharmony_ci	}
432262306a36Sopenharmony_ci
432362306a36Sopenharmony_ci	return quota;
432462306a36Sopenharmony_ci}
432562306a36Sopenharmony_ci
432662306a36Sopenharmony_ciint ath10k_htt_rx_hl_indication(struct ath10k *ar, int budget)
432762306a36Sopenharmony_ci{
432862306a36Sopenharmony_ci	struct htt_resp *resp;
432962306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
433062306a36Sopenharmony_ci	struct sk_buff *skb;
433162306a36Sopenharmony_ci	bool release;
433262306a36Sopenharmony_ci	int quota;
433362306a36Sopenharmony_ci
433462306a36Sopenharmony_ci	for (quota = 0; quota < budget; quota++) {
433562306a36Sopenharmony_ci		skb = skb_dequeue(&htt->rx_indication_head);
433662306a36Sopenharmony_ci		if (!skb)
433762306a36Sopenharmony_ci			break;
433862306a36Sopenharmony_ci
433962306a36Sopenharmony_ci		resp = (struct htt_resp *)skb->data;
434062306a36Sopenharmony_ci
434162306a36Sopenharmony_ci		release = ath10k_htt_rx_proc_rx_ind_hl(htt,
434262306a36Sopenharmony_ci						       &resp->rx_ind_hl,
434362306a36Sopenharmony_ci						       skb,
434462306a36Sopenharmony_ci						       HTT_RX_PN_CHECK,
434562306a36Sopenharmony_ci						       HTT_RX_NON_TKIP_MIC);
434662306a36Sopenharmony_ci
434762306a36Sopenharmony_ci		if (release)
434862306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_HTT, "rx indication poll pending count:%d\n",
435162306a36Sopenharmony_ci			   skb_queue_len(&htt->rx_indication_head));
435262306a36Sopenharmony_ci	}
435362306a36Sopenharmony_ci	return quota;
435462306a36Sopenharmony_ci}
435562306a36Sopenharmony_ciEXPORT_SYMBOL(ath10k_htt_rx_hl_indication);
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ciint ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget)
435862306a36Sopenharmony_ci{
435962306a36Sopenharmony_ci	struct ath10k_htt *htt = &ar->htt;
436062306a36Sopenharmony_ci	struct htt_tx_done tx_done = {};
436162306a36Sopenharmony_ci	struct sk_buff_head tx_ind_q;
436262306a36Sopenharmony_ci	struct sk_buff *skb;
436362306a36Sopenharmony_ci	unsigned long flags;
436462306a36Sopenharmony_ci	int quota = 0, done, ret;
436562306a36Sopenharmony_ci	bool resched_napi = false;
436662306a36Sopenharmony_ci
436762306a36Sopenharmony_ci	__skb_queue_head_init(&tx_ind_q);
436862306a36Sopenharmony_ci
436962306a36Sopenharmony_ci	/* Process pending frames before dequeuing more data
437062306a36Sopenharmony_ci	 * from hardware.
437162306a36Sopenharmony_ci	 */
437262306a36Sopenharmony_ci	quota = ath10k_htt_rx_deliver_msdu(ar, quota, budget);
437362306a36Sopenharmony_ci	if (quota == budget) {
437462306a36Sopenharmony_ci		resched_napi = true;
437562306a36Sopenharmony_ci		goto exit;
437662306a36Sopenharmony_ci	}
437762306a36Sopenharmony_ci
437862306a36Sopenharmony_ci	while ((skb = skb_dequeue(&htt->rx_in_ord_compl_q))) {
437962306a36Sopenharmony_ci		spin_lock_bh(&htt->rx_ring.lock);
438062306a36Sopenharmony_ci		ret = ath10k_htt_rx_in_ord_ind(ar, skb);
438162306a36Sopenharmony_ci		spin_unlock_bh(&htt->rx_ring.lock);
438262306a36Sopenharmony_ci
438362306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
438462306a36Sopenharmony_ci		if (ret == -EIO) {
438562306a36Sopenharmony_ci			resched_napi = true;
438662306a36Sopenharmony_ci			goto exit;
438762306a36Sopenharmony_ci		}
438862306a36Sopenharmony_ci	}
438962306a36Sopenharmony_ci
439062306a36Sopenharmony_ci	while (atomic_read(&htt->num_mpdus_ready)) {
439162306a36Sopenharmony_ci		ret = ath10k_htt_rx_handle_amsdu(htt);
439262306a36Sopenharmony_ci		if (ret == -EIO) {
439362306a36Sopenharmony_ci			resched_napi = true;
439462306a36Sopenharmony_ci			goto exit;
439562306a36Sopenharmony_ci		}
439662306a36Sopenharmony_ci		atomic_dec(&htt->num_mpdus_ready);
439762306a36Sopenharmony_ci	}
439862306a36Sopenharmony_ci
439962306a36Sopenharmony_ci	/* Deliver received data after processing data from hardware */
440062306a36Sopenharmony_ci	quota = ath10k_htt_rx_deliver_msdu(ar, quota, budget);
440162306a36Sopenharmony_ci
440262306a36Sopenharmony_ci	/* From NAPI documentation:
440362306a36Sopenharmony_ci	 *  The napi poll() function may also process TX completions, in which
440462306a36Sopenharmony_ci	 *  case if it processes the entire TX ring then it should count that
440562306a36Sopenharmony_ci	 *  work as the rest of the budget.
440662306a36Sopenharmony_ci	 */
440762306a36Sopenharmony_ci	if ((quota < budget) && !kfifo_is_empty(&htt->txdone_fifo))
440862306a36Sopenharmony_ci		quota = budget;
440962306a36Sopenharmony_ci
441062306a36Sopenharmony_ci	/* kfifo_get: called only within txrx_tasklet so it's neatly serialized.
441162306a36Sopenharmony_ci	 * From kfifo_get() documentation:
441262306a36Sopenharmony_ci	 *  Note that with only one concurrent reader and one concurrent writer,
441362306a36Sopenharmony_ci	 *  you don't need extra locking to use these macro.
441462306a36Sopenharmony_ci	 */
441562306a36Sopenharmony_ci	while (kfifo_get(&htt->txdone_fifo, &tx_done))
441662306a36Sopenharmony_ci		ath10k_txrx_tx_unref(htt, &tx_done);
441762306a36Sopenharmony_ci
441862306a36Sopenharmony_ci	ath10k_mac_tx_push_pending(ar);
441962306a36Sopenharmony_ci
442062306a36Sopenharmony_ci	spin_lock_irqsave(&htt->tx_fetch_ind_q.lock, flags);
442162306a36Sopenharmony_ci	skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q);
442262306a36Sopenharmony_ci	spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags);
442362306a36Sopenharmony_ci
442462306a36Sopenharmony_ci	while ((skb = __skb_dequeue(&tx_ind_q))) {
442562306a36Sopenharmony_ci		ath10k_htt_rx_tx_fetch_ind(ar, skb);
442662306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
442762306a36Sopenharmony_ci	}
442862306a36Sopenharmony_ci
442962306a36Sopenharmony_ciexit:
443062306a36Sopenharmony_ci	ath10k_htt_rx_msdu_buff_replenish(htt);
443162306a36Sopenharmony_ci	/* In case of rx failure or more data to read, report budget
443262306a36Sopenharmony_ci	 * to reschedule NAPI poll
443362306a36Sopenharmony_ci	 */
443462306a36Sopenharmony_ci	done = resched_napi ? budget : quota;
443562306a36Sopenharmony_ci
443662306a36Sopenharmony_ci	return done;
443762306a36Sopenharmony_ci}
443862306a36Sopenharmony_ciEXPORT_SYMBOL(ath10k_htt_txrx_compl_task);
443962306a36Sopenharmony_ci
444062306a36Sopenharmony_cistatic const struct ath10k_htt_rx_ops htt_rx_ops_32 = {
444162306a36Sopenharmony_ci	.htt_get_rx_ring_size = ath10k_htt_get_rx_ring_size_32,
444262306a36Sopenharmony_ci	.htt_config_paddrs_ring = ath10k_htt_config_paddrs_ring_32,
444362306a36Sopenharmony_ci	.htt_set_paddrs_ring = ath10k_htt_set_paddrs_ring_32,
444462306a36Sopenharmony_ci	.htt_get_vaddr_ring = ath10k_htt_get_vaddr_ring_32,
444562306a36Sopenharmony_ci	.htt_reset_paddrs_ring = ath10k_htt_reset_paddrs_ring_32,
444662306a36Sopenharmony_ci};
444762306a36Sopenharmony_ci
444862306a36Sopenharmony_cistatic const struct ath10k_htt_rx_ops htt_rx_ops_64 = {
444962306a36Sopenharmony_ci	.htt_get_rx_ring_size = ath10k_htt_get_rx_ring_size_64,
445062306a36Sopenharmony_ci	.htt_config_paddrs_ring = ath10k_htt_config_paddrs_ring_64,
445162306a36Sopenharmony_ci	.htt_set_paddrs_ring = ath10k_htt_set_paddrs_ring_64,
445262306a36Sopenharmony_ci	.htt_get_vaddr_ring = ath10k_htt_get_vaddr_ring_64,
445362306a36Sopenharmony_ci	.htt_reset_paddrs_ring = ath10k_htt_reset_paddrs_ring_64,
445462306a36Sopenharmony_ci};
445562306a36Sopenharmony_ci
445662306a36Sopenharmony_cistatic const struct ath10k_htt_rx_ops htt_rx_ops_hl = {
445762306a36Sopenharmony_ci	.htt_rx_proc_rx_frag_ind = ath10k_htt_rx_proc_rx_frag_ind_hl,
445862306a36Sopenharmony_ci};
445962306a36Sopenharmony_ci
446062306a36Sopenharmony_civoid ath10k_htt_set_rx_ops(struct ath10k_htt *htt)
446162306a36Sopenharmony_ci{
446262306a36Sopenharmony_ci	struct ath10k *ar = htt->ar;
446362306a36Sopenharmony_ci
446462306a36Sopenharmony_ci	if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
446562306a36Sopenharmony_ci		htt->rx_ops = &htt_rx_ops_hl;
446662306a36Sopenharmony_ci	else if (ar->hw_params.target_64bit)
446762306a36Sopenharmony_ci		htt->rx_ops = &htt_rx_ops_64;
446862306a36Sopenharmony_ci	else
446962306a36Sopenharmony_ci		htt->rx_ops = &htt_rx_ops_32;
447062306a36Sopenharmony_ci}
4471