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