Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/ath/ath11k/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 156 kB image not shown  

Quelle  dp_rx.c   Sprache: C

 
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
 * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
 * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
 */


#include <linux/ieee80211.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <crypto/hash.h>
#include "core.h"
#include "debug.h"
#include "debugfs_htt_stats.h"
#include "debugfs_sta.h"
#include "hal_desc.h"
#include "hw.h"
#include "dp_rx.h"
#include "hal_rx.h"
#include "dp_tx.h"
#include "peer.h"

#define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ)

static inline
u8 *ath11k_dp_rx_h_80211_hdr(struct ath11k_base *ab, struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_hdr_status(desc);
}

static inline
enum hal_encrypt_type ath11k_dp_rx_h_mpdu_start_enctype(struct ath11k_base *ab,
       struct hal_rx_desc *desc)
{
 if (!ab->hw_params.hw_ops->rx_desc_encrypt_valid(desc))
  return HAL_ENCRYPT_TYPE_OPEN;

 return ab->hw_params.hw_ops->rx_desc_get_encrypt_type(desc);
}

static inline u8 ath11k_dp_rx_h_msdu_start_decap_type(struct ath11k_base *ab,
            struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_decap_type(desc);
}

static inline
bool ath11k_dp_rx_h_msdu_start_ldpc_support(struct ath11k_base *ab,
         struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_ldpc_support(desc);
}

static inline
u8 ath11k_dp_rx_h_msdu_start_mesh_ctl_present(struct ath11k_base *ab,
           struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_mesh_ctl(desc);
}

static inline
bool ath11k_dp_rx_h_mpdu_start_seq_ctrl_valid(struct ath11k_base *ab,
           struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_mpdu_seq_ctl_vld(desc);
}

static inline bool ath11k_dp_rx_h_mpdu_start_fc_valid(struct ath11k_base *ab,
            struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_mpdu_fc_valid(desc);
}

static inline bool ath11k_dp_rx_h_mpdu_start_more_frags(struct ath11k_base *ab,
       struct sk_buff *skb)
{
 struct ieee80211_hdr *hdr;

 hdr = (struct ieee80211_hdr *)(skb->data + ab->hw_params.hal_desc_sz);
 return ieee80211_has_morefrags(hdr->frame_control);
}

static inline u16 ath11k_dp_rx_h_mpdu_start_frag_no(struct ath11k_base *ab,
          struct sk_buff *skb)
{
 struct ieee80211_hdr *hdr;

 hdr = (struct ieee80211_hdr *)(skb->data + ab->hw_params.hal_desc_sz);
 return le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
}

static inline u16 ath11k_dp_rx_h_mpdu_start_seq_no(struct ath11k_base *ab,
         struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_mpdu_start_seq_no(desc);
}

static inline void *ath11k_dp_rx_get_attention(struct ath11k_base *ab,
            struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_attention(desc);
}

static inline bool ath11k_dp_rx_h_attn_msdu_done(struct rx_attention *attn)
{
 return !!FIELD_GET(RX_ATTENTION_INFO2_MSDU_DONE,
      __le32_to_cpu(attn->info2));
}

static inline bool ath11k_dp_rx_h_attn_l4_cksum_fail(struct rx_attention *attn)
{
 return !!FIELD_GET(RX_ATTENTION_INFO1_TCP_UDP_CKSUM_FAIL,
      __le32_to_cpu(attn->info1));
}

static inline bool ath11k_dp_rx_h_attn_ip_cksum_fail(struct rx_attention *attn)
{
 return !!FIELD_GET(RX_ATTENTION_INFO1_IP_CKSUM_FAIL,
      __le32_to_cpu(attn->info1));
}

static inline bool ath11k_dp_rx_h_attn_is_decrypted(struct rx_attention *attn)
{
 return (FIELD_GET(RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE,
     __le32_to_cpu(attn->info2)) ==
  RX_DESC_DECRYPT_STATUS_CODE_OK);
}

static u32 ath11k_dp_rx_h_attn_mpdu_err(struct rx_attention *attn)
{
 u32 info = __le32_to_cpu(attn->info1);
 u32 errmap = 0;

 if (info & RX_ATTENTION_INFO1_FCS_ERR)
  errmap |= DP_RX_MPDU_ERR_FCS;

 if (info & RX_ATTENTION_INFO1_DECRYPT_ERR)
  errmap |= DP_RX_MPDU_ERR_DECRYPT;

 if (info & RX_ATTENTION_INFO1_TKIP_MIC_ERR)
  errmap |= DP_RX_MPDU_ERR_TKIP_MIC;

 if (info & RX_ATTENTION_INFO1_A_MSDU_ERROR)
  errmap |= DP_RX_MPDU_ERR_AMSDU_ERR;

 if (info & RX_ATTENTION_INFO1_OVERFLOW_ERR)
  errmap |= DP_RX_MPDU_ERR_OVERFLOW;

 if (info & RX_ATTENTION_INFO1_MSDU_LEN_ERR)
  errmap |= DP_RX_MPDU_ERR_MSDU_LEN;

 if (info & RX_ATTENTION_INFO1_MPDU_LEN_ERR)
  errmap |= DP_RX_MPDU_ERR_MPDU_LEN;

 return errmap;
}

static bool ath11k_dp_rx_h_attn_msdu_len_err(struct ath11k_base *ab,
          struct hal_rx_desc *desc)
{
 struct rx_attention *rx_attention;
 u32 errmap;

 rx_attention = ath11k_dp_rx_get_attention(ab, desc);
 errmap = ath11k_dp_rx_h_attn_mpdu_err(rx_attention);

 return errmap & DP_RX_MPDU_ERR_MSDU_LEN;
}

static inline u16 ath11k_dp_rx_h_msdu_start_msdu_len(struct ath11k_base *ab,
           struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_msdu_len(desc);
}

static inline u8 ath11k_dp_rx_h_msdu_start_sgi(struct ath11k_base *ab,
            struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_msdu_sgi(desc);
}

static inline u8 ath11k_dp_rx_h_msdu_start_rate_mcs(struct ath11k_base *ab,
          struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_msdu_rate_mcs(desc);
}

static inline u8 ath11k_dp_rx_h_msdu_start_rx_bw(struct ath11k_base *ab,
       struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_msdu_rx_bw(desc);
}

static inline u32 ath11k_dp_rx_h_msdu_start_freq(struct ath11k_base *ab,
       struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_msdu_freq(desc);
}

static inline u8 ath11k_dp_rx_h_msdu_start_pkt_type(struct ath11k_base *ab,
          struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_msdu_pkt_type(desc);
}

static inline u8 ath11k_dp_rx_h_msdu_start_nss(struct ath11k_base *ab,
            struct hal_rx_desc *desc)
{
 return hweight8(ab->hw_params.hw_ops->rx_desc_get_msdu_nss(desc));
}

static inline u8 ath11k_dp_rx_h_mpdu_start_tid(struct ath11k_base *ab,
            struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_mpdu_tid(desc);
}

static inline u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab,
          struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_mpdu_peer_id(desc);
}

static inline u8 ath11k_dp_rx_h_msdu_end_l3pad(struct ath11k_base *ab,
            struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_l3_pad_bytes(desc);
}

static inline bool ath11k_dp_rx_h_msdu_end_first_msdu(struct ath11k_base *ab,
            struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_first_msdu(desc);
}

static bool ath11k_dp_rx_h_msdu_end_last_msdu(struct ath11k_base *ab,
           struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_last_msdu(desc);
}

static void ath11k_dp_rx_desc_end_tlv_copy(struct ath11k_base *ab,
        struct hal_rx_desc *fdesc,
        struct hal_rx_desc *ldesc)
{
 ab->hw_params.hw_ops->rx_desc_copy_attn_end_tlv(fdesc, ldesc);
}

static inline u32 ath11k_dp_rxdesc_get_mpdulen_err(struct rx_attention *attn)
{
 return FIELD_GET(RX_ATTENTION_INFO1_MPDU_LEN_ERR,
    __le32_to_cpu(attn->info1));
}

static inline u8 *ath11k_dp_rxdesc_get_80211hdr(struct ath11k_base *ab,
      struct hal_rx_desc *rx_desc)
{
 u8 *rx_pkt_hdr;

 rx_pkt_hdr = ab->hw_params.hw_ops->rx_desc_get_msdu_payload(rx_desc);

 return rx_pkt_hdr;
}

static inline bool ath11k_dp_rxdesc_mpdu_valid(struct ath11k_base *ab,
            struct hal_rx_desc *rx_desc)
{
 u32 tlv_tag;

 tlv_tag = ab->hw_params.hw_ops->rx_desc_get_mpdu_start_tag(rx_desc);

 return tlv_tag == HAL_RX_MPDU_START;
}

static inline u32 ath11k_dp_rxdesc_get_ppduid(struct ath11k_base *ab,
           struct hal_rx_desc *rx_desc)
{
 return ab->hw_params.hw_ops->rx_desc_get_mpdu_ppdu_id(rx_desc);
}

static inline void ath11k_dp_rxdesc_set_msdu_len(struct ath11k_base *ab,
       struct hal_rx_desc *desc,
       u16 len)
{
 ab->hw_params.hw_ops->rx_desc_set_msdu_len(desc, len);
}

static bool ath11k_dp_rx_h_attn_is_mcbc(struct ath11k_base *ab,
     struct hal_rx_desc *desc)
{
 struct rx_attention *attn = ath11k_dp_rx_get_attention(ab, desc);

 return ath11k_dp_rx_h_msdu_end_first_msdu(ab, desc) &&
  (!!FIELD_GET(RX_ATTENTION_INFO1_MCAST_BCAST,
   __le32_to_cpu(attn->info1)));
}

static bool ath11k_dp_rxdesc_mac_addr2_valid(struct ath11k_base *ab,
          struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_mac_addr2_valid(desc);
}

static u8 *ath11k_dp_rxdesc_mpdu_start_addr2(struct ath11k_base *ab,
          struct hal_rx_desc *desc)
{
 return ab->hw_params.hw_ops->rx_desc_mpdu_start_addr2(desc);
}

static void ath11k_dp_service_mon_ring(struct timer_list *t)
{
 struct ath11k_base *ab = timer_container_of(ab, t, mon_reap_timer);
 int i;

 for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++)
  ath11k_dp_rx_process_mon_rings(ab, i, NULL, DP_MON_SERVICE_BUDGET);

 mod_timer(&ab->mon_reap_timer, jiffies +
    msecs_to_jiffies(ATH11K_MON_TIMER_INTERVAL));
}

static int ath11k_dp_purge_mon_ring(struct ath11k_base *ab)
{
 int i, reaped = 0;
 unsigned long timeout = jiffies + msecs_to_jiffies(DP_MON_PURGE_TIMEOUT_MS);

 do {
  for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++)
   reaped += ath11k_dp_rx_process_mon_rings(ab, i,
         NULL,
         DP_MON_SERVICE_BUDGET);

  /* nothing more to reap */
  if (reaped < DP_MON_SERVICE_BUDGET)
   return 0;

 } while (time_before(jiffies, timeout));

 ath11k_warn(ab, "dp mon ring purge timeout");

 return -ETIMEDOUT;
}

/* Returns number of Rx buffers replenished */
int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id,
          struct dp_rxdma_ring *rx_ring,
          int req_entries,
          enum hal_rx_buf_return_buf_manager mgr)
{
 struct hal_srng *srng;
 u32 *desc;
 struct sk_buff *skb;
 int num_free;
 int num_remain;
 int buf_id;
 u32 cookie;
 dma_addr_t paddr;

 req_entries = min(req_entries, rx_ring->bufs_max);

 srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id];

 spin_lock_bh(&srng->lock);

 ath11k_hal_srng_access_begin(ab, srng);

 num_free = ath11k_hal_srng_src_num_free(ab, srng, true);
 if (!req_entries && (num_free > (rx_ring->bufs_max * 3) / 4))
  req_entries = num_free;

 req_entries = min(num_free, req_entries);
 num_remain = req_entries;

 while (num_remain > 0) {
  skb = dev_alloc_skb(DP_RX_BUFFER_SIZE +
        DP_RX_BUFFER_ALIGN_SIZE);
  if (!skb)
   break;

  if (!IS_ALIGNED((unsigned long)skb->data,
    DP_RX_BUFFER_ALIGN_SIZE)) {
   skb_pull(skb,
     PTR_ALIGN(skb->data, DP_RX_BUFFER_ALIGN_SIZE) -
     skb->data);
  }

  paddr = dma_map_single(ab->dev, skb->data,
           skb->len + skb_tailroom(skb),
           DMA_FROM_DEVICE);
  if (dma_mapping_error(ab->dev, paddr))
   goto fail_free_skb;

  spin_lock_bh(&rx_ring->idr_lock);
  buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 1,
       (rx_ring->bufs_max * 3) + 1, GFP_ATOMIC);
  spin_unlock_bh(&rx_ring->idr_lock);
  if (buf_id <= 0)
   goto fail_dma_unmap;

  desc = ath11k_hal_srng_src_get_next_entry(ab, srng);
  if (!desc)
   goto fail_idr_remove;

  ATH11K_SKB_RXCB(skb)->paddr = paddr;

  cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) |
    FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id);

  num_remain--;

  ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr);
 }

 ath11k_hal_srng_access_end(ab, srng);

 spin_unlock_bh(&srng->lock);

 return req_entries - num_remain;

fail_idr_remove:
 spin_lock_bh(&rx_ring->idr_lock);
 idr_remove(&rx_ring->bufs_idr, buf_id);
 spin_unlock_bh(&rx_ring->idr_lock);
fail_dma_unmap:
 dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb),
    DMA_FROM_DEVICE);
fail_free_skb:
 dev_kfree_skb_any(skb);

 ath11k_hal_srng_access_end(ab, srng);

 spin_unlock_bh(&srng->lock);

 return req_entries - num_remain;
}

static int ath11k_dp_rxdma_buf_ring_free(struct ath11k *ar,
      struct dp_rxdma_ring *rx_ring)
{
 struct sk_buff *skb;
 int buf_id;

 spin_lock_bh(&rx_ring->idr_lock);
 idr_for_each_entry(&rx_ring->bufs_idr, skb, buf_id) {
  idr_remove(&rx_ring->bufs_idr, buf_id);
  /* TODO: Understand where internal driver does this dma_unmap
 * of rxdma_buffer.
 */

  dma_unmap_single(ar->ab->dev, ATH11K_SKB_RXCB(skb)->paddr,
     skb->len + skb_tailroom(skb), DMA_FROM_DEVICE);
  dev_kfree_skb_any(skb);
 }

 idr_destroy(&rx_ring->bufs_idr);
 spin_unlock_bh(&rx_ring->idr_lock);

 return 0;
}

static int ath11k_dp_rxdma_pdev_buf_free(struct ath11k *ar)
{
 struct ath11k_pdev_dp *dp = &ar->dp;
 struct ath11k_base *ab = ar->ab;
 struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring;
 int i;

 ath11k_dp_rxdma_buf_ring_free(ar, rx_ring);

 rx_ring = &dp->rxdma_mon_buf_ring;
 ath11k_dp_rxdma_buf_ring_free(ar, rx_ring);

 for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) {
  rx_ring = &dp->rx_mon_status_refill_ring[i];
  ath11k_dp_rxdma_buf_ring_free(ar, rx_ring);
 }

 return 0;
}

static int ath11k_dp_rxdma_ring_buf_setup(struct ath11k *ar,
       struct dp_rxdma_ring *rx_ring,
       u32 ringtype)
{
 struct ath11k_pdev_dp *dp = &ar->dp;
 int num_entries;

 num_entries = rx_ring->refill_buf_ring.size /
  ath11k_hal_srng_get_entrysize(ar->ab, ringtype);

 rx_ring->bufs_max = num_entries;
 ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, rx_ring, num_entries,
       ar->ab->hw_params.hal_params->rx_buf_rbm);
 return 0;
}

static int ath11k_dp_rxdma_pdev_buf_setup(struct ath11k *ar)
{
 struct ath11k_pdev_dp *dp = &ar->dp;
 struct ath11k_base *ab = ar->ab;
 struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring;
 int i;

 ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF);

 if (ar->ab->hw_params.rxdma1_enable) {
  rx_ring = &dp->rxdma_mon_buf_ring;
  ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_BUF);
 }

 for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) {
  rx_ring = &dp->rx_mon_status_refill_ring[i];
  ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_STATUS);
 }

 return 0;
}

static void ath11k_dp_rx_pdev_srng_free(struct ath11k *ar)
{
 struct ath11k_pdev_dp *dp = &ar->dp;
 struct ath11k_base *ab = ar->ab;
 int i;

 ath11k_dp_srng_cleanup(ab, &dp->rx_refill_buf_ring.refill_buf_ring);

 for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) {
  if (ab->hw_params.rx_mac_buf_ring)
   ath11k_dp_srng_cleanup(ab, &dp->rx_mac_buf_ring[i]);

  ath11k_dp_srng_cleanup(ab, &dp->rxdma_err_dst_ring[i]);
  ath11k_dp_srng_cleanup(ab,
           &dp->rx_mon_status_refill_ring[i].refill_buf_ring);
 }

 ath11k_dp_srng_cleanup(ab, &dp->rxdma_mon_buf_ring.refill_buf_ring);
}

void ath11k_dp_pdev_reo_cleanup(struct ath11k_base *ab)
{
 struct ath11k_dp *dp = &ab->dp;
 int i;

 for (i = 0; i < DP_REO_DST_RING_MAX; i++)
  ath11k_dp_srng_cleanup(ab, &dp->reo_dst_ring[i]);
}

int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab)
{
 struct ath11k_dp *dp = &ab->dp;
 int ret;
 int i;

 for (i = 0; i < DP_REO_DST_RING_MAX; i++) {
  ret = ath11k_dp_srng_setup(ab, &dp->reo_dst_ring[i],
        HAL_REO_DST, i, 0,
        DP_REO_DST_RING_SIZE);
  if (ret) {
   ath11k_warn(ab, "failed to setup reo_dst_ring\n");
   goto err_reo_cleanup;
  }
 }

 return 0;

err_reo_cleanup:
 ath11k_dp_pdev_reo_cleanup(ab);

 return ret;
}

static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar)
{
 struct ath11k_pdev_dp *dp = &ar->dp;
 struct ath11k_base *ab = ar->ab;
 struct dp_srng *srng = NULL;
 int i;
 int ret;

 ret = ath11k_dp_srng_setup(ar->ab,
       &dp->rx_refill_buf_ring.refill_buf_ring,
       HAL_RXDMA_BUF, 0,
       dp->mac_id, DP_RXDMA_BUF_RING_SIZE);
 if (ret) {
  ath11k_warn(ar->ab, "failed to setup rx_refill_buf_ring\n");
  return ret;
 }

 if (ar->ab->hw_params.rx_mac_buf_ring) {
  for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) {
   ret = ath11k_dp_srng_setup(ar->ab,
         &dp->rx_mac_buf_ring[i],
         HAL_RXDMA_BUF, 1,
         dp->mac_id + i, 1024);
   if (ret) {
    ath11k_warn(ar->ab, "failed to setup rx_mac_buf_ring %d\n",
         i);
    return ret;
   }
  }
 }

 for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) {
  ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_err_dst_ring[i],
        HAL_RXDMA_DST, 0, dp->mac_id + i,
        DP_RXDMA_ERR_DST_RING_SIZE);
  if (ret) {
   ath11k_warn(ar->ab, "failed to setup rxdma_err_dst_ring %d\n", i);
   return ret;
  }
 }

 for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) {
  srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring;
  ret = ath11k_dp_srng_setup(ar->ab,
        srng,
        HAL_RXDMA_MONITOR_STATUS, 0, dp->mac_id + i,
        DP_RXDMA_MON_STATUS_RING_SIZE);
  if (ret) {
   ath11k_warn(ar->ab,
        "failed to setup rx_mon_status_refill_ring %d\n", i);
   return ret;
  }
 }

 /* if rxdma1_enable is false, then it doesn't need
 * to setup rxdam_mon_buf_ring, rxdma_mon_dst_ring
 * and rxdma_mon_desc_ring.
 * init reap timer for QCA6390.
 */

 if (!ar->ab->hw_params.rxdma1_enable) {
  //init mon status buffer reap timer
  timer_setup(&ar->ab->mon_reap_timer,
       ath11k_dp_service_mon_ring, 0);
  return 0;
 }

 ret = ath11k_dp_srng_setup(ar->ab,
       &dp->rxdma_mon_buf_ring.refill_buf_ring,
       HAL_RXDMA_MONITOR_BUF, 0, dp->mac_id,
       DP_RXDMA_MONITOR_BUF_RING_SIZE);
 if (ret) {
  ath11k_warn(ar->ab,
       "failed to setup HAL_RXDMA_MONITOR_BUF\n");
  return ret;
 }

 ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_mon_dst_ring,
       HAL_RXDMA_MONITOR_DST, 0, dp->mac_id,
       DP_RXDMA_MONITOR_DST_RING_SIZE);
 if (ret) {
  ath11k_warn(ar->ab,
       "failed to setup HAL_RXDMA_MONITOR_DST\n");
  return ret;
 }

 ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_mon_desc_ring,
       HAL_RXDMA_MONITOR_DESC, 0, dp->mac_id,
       DP_RXDMA_MONITOR_DESC_RING_SIZE);
 if (ret) {
  ath11k_warn(ar->ab,
       "failed to setup HAL_RXDMA_MONITOR_DESC\n");
  return ret;
 }

 return 0;
}

void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab)
{
 struct ath11k_dp *dp = &ab->dp;
 struct dp_reo_cmd *cmd, *tmp;
 struct dp_reo_cache_flush_elem *cmd_cache, *tmp_cache;
 struct dp_rx_tid *rx_tid;

 spin_lock_bh(&dp->reo_cmd_lock);
 list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) {
  list_del(&cmd->list);
  rx_tid = &cmd->data;
  if (rx_tid->vaddr_unaligned) {
   dma_free_noncoherent(ab->dev, rx_tid->unaligned_size,
          rx_tid->vaddr_unaligned,
          rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
   rx_tid->vaddr_unaligned = NULL;
  }
  kfree(cmd);
 }

 list_for_each_entry_safe(cmd_cache, tmp_cache,
     &dp->reo_cmd_cache_flush_list, list) {
  list_del(&cmd_cache->list);
  dp->reo_cmd_cache_flush_count--;
  rx_tid = &cmd_cache->data;
  if (rx_tid->vaddr_unaligned) {
   dma_free_noncoherent(ab->dev, rx_tid->unaligned_size,
          rx_tid->vaddr_unaligned,
          rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
   rx_tid->vaddr_unaligned = NULL;
  }
  kfree(cmd_cache);
 }
 spin_unlock_bh(&dp->reo_cmd_lock);
}

static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx,
       enum hal_reo_cmd_status status)
{
 struct dp_rx_tid *rx_tid = ctx;

 if (status != HAL_REO_CMD_SUCCESS)
  ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n",
       rx_tid->tid, status);
 if (rx_tid->vaddr_unaligned) {
  dma_free_noncoherent(dp->ab->dev, rx_tid->unaligned_size,
         rx_tid->vaddr_unaligned,
         rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
  rx_tid->vaddr_unaligned = NULL;
 }
}

static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab,
          struct dp_rx_tid *rx_tid)
{
 struct ath11k_hal_reo_cmd cmd = {};
 unsigned long tot_desc_sz, desc_sz;
 int ret;

 tot_desc_sz = rx_tid->size;
 desc_sz = ath11k_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID);

 while (tot_desc_sz > desc_sz) {
  tot_desc_sz -= desc_sz;
  cmd.addr_lo = lower_32_bits(rx_tid->paddr + tot_desc_sz);
  cmd.addr_hi = upper_32_bits(rx_tid->paddr);
  ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid,
      HAL_REO_CMD_FLUSH_CACHE, &cmd,
      NULL);
  if (ret)
   ath11k_warn(ab,
        "failed to send HAL_REO_CMD_FLUSH_CACHE, tid %d (%d)\n",
        rx_tid->tid, ret);
 }

 memset(&cmd, 0, sizeof(cmd));
 cmd.addr_lo = lower_32_bits(rx_tid->paddr);
 cmd.addr_hi = upper_32_bits(rx_tid->paddr);
 cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS;
 ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid,
     HAL_REO_CMD_FLUSH_CACHE,
     &cmd, ath11k_dp_reo_cmd_free);
 if (ret) {
  ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n",
      rx_tid->tid, ret);
  dma_free_noncoherent(ab->dev, rx_tid->unaligned_size,
         rx_tid->vaddr_unaligned,
         rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
  rx_tid->vaddr_unaligned = NULL;
 }
}

static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx,
          enum hal_reo_cmd_status status)
{
 struct ath11k_base *ab = dp->ab;
 struct dp_rx_tid *rx_tid = ctx;
 struct dp_reo_cache_flush_elem *elem, *tmp;

 if (status == HAL_REO_CMD_DRAIN) {
  goto free_desc;
 } else if (status != HAL_REO_CMD_SUCCESS) {
  /* Shouldn't happen! Cleanup in case of other failure? */
  ath11k_warn(ab, "failed to delete rx tid %d hw descriptor %d\n",
       rx_tid->tid, status);
  return;
 }

 elem = kzalloc(sizeof(*elem), GFP_ATOMIC);
 if (!elem)
  goto free_desc;

 elem->ts = jiffies;
 memcpy(&elem->data, rx_tid, sizeof(*rx_tid));

 spin_lock_bh(&dp->reo_cmd_lock);
 list_add_tail(&elem->list, &dp->reo_cmd_cache_flush_list);
 dp->reo_cmd_cache_flush_count++;

 /* Flush and invalidate aged REO desc from HW cache */
 list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_cache_flush_list,
     list) {
  if (dp->reo_cmd_cache_flush_count > DP_REO_DESC_FREE_THRESHOLD ||
      time_after(jiffies, elem->ts +
          msecs_to_jiffies(DP_REO_DESC_FREE_TIMEOUT_MS))) {
   list_del(&elem->list);
   dp->reo_cmd_cache_flush_count--;
   spin_unlock_bh(&dp->reo_cmd_lock);

   ath11k_dp_reo_cache_flush(ab, &elem->data);
   kfree(elem);
   spin_lock_bh(&dp->reo_cmd_lock);
  }
 }
 spin_unlock_bh(&dp->reo_cmd_lock);

 return;
free_desc:
 dma_free_noncoherent(ab->dev, rx_tid->unaligned_size,
        rx_tid->vaddr_unaligned,
        rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
 rx_tid->vaddr_unaligned = NULL;
}

void ath11k_peer_rx_tid_delete(struct ath11k *ar,
          struct ath11k_peer *peer, u8 tid)
{
 struct ath11k_hal_reo_cmd cmd = {};
 struct dp_rx_tid *rx_tid = &peer->rx_tid[tid];
 int ret;

 if (!rx_tid->active)
  return;

 rx_tid->active = false;

 cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
 cmd.addr_lo = lower_32_bits(rx_tid->paddr);
 cmd.addr_hi = upper_32_bits(rx_tid->paddr);
 cmd.upd0 |= HAL_REO_CMD_UPD0_VLD;
 ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid,
     HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd,
     ath11k_dp_rx_tid_del_func);
 if (ret) {
  if (ret != -ESHUTDOWN)
   ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n",
       tid, ret);
  dma_free_noncoherent(ar->ab->dev, rx_tid->unaligned_size,
         rx_tid->vaddr_unaligned,
         rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
  rx_tid->vaddr_unaligned = NULL;
 }

 rx_tid->paddr = 0;
 rx_tid->paddr_unaligned = 0;
 rx_tid->size = 0;
 rx_tid->unaligned_size = 0;
}

static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab,
      u32 *link_desc,
      enum hal_wbm_rel_bm_act action)
{
 struct ath11k_dp *dp = &ab->dp;
 struct hal_srng *srng;
 u32 *desc;
 int ret = 0;

 srng = &ab->hal.srng_list[dp->wbm_desc_rel_ring.ring_id];

 spin_lock_bh(&srng->lock);

 ath11k_hal_srng_access_begin(ab, srng);

 desc = ath11k_hal_srng_src_get_next_entry(ab, srng);
 if (!desc) {
  ret = -ENOBUFS;
  goto exit;
 }

 ath11k_hal_rx_msdu_link_desc_set(ab, (void *)desc, (void *)link_desc,
      action);

exit:
 ath11k_hal_srng_access_end(ab, srng);

 spin_unlock_bh(&srng->lock);

 return ret;
}

static void ath11k_dp_rx_frags_cleanup(struct dp_rx_tid *rx_tid, bool rel_link_desc)
{
 struct ath11k_base *ab = rx_tid->ab;

 lockdep_assert_held(&ab->base_lock);

 if (rx_tid->dst_ring_desc) {
  if (rel_link_desc)
   ath11k_dp_rx_link_desc_return(ab, (u32 *)rx_tid->dst_ring_desc,
            HAL_WBM_REL_BM_ACT_PUT_IN_IDLE);
  kfree(rx_tid->dst_ring_desc);
  rx_tid->dst_ring_desc = NULL;
 }

 rx_tid->cur_sn = 0;
 rx_tid->last_frag_no = 0;
 rx_tid->rx_frag_bitmap = 0;
 __skb_queue_purge(&rx_tid->rx_frags);
}

void ath11k_peer_frags_flush(struct ath11k *ar, struct ath11k_peer *peer)
{
 struct dp_rx_tid *rx_tid;
 int i;

 lockdep_assert_held(&ar->ab->base_lock);

 for (i = 0; i <= IEEE80211_NUM_TIDS; i++) {
  rx_tid = &peer->rx_tid[i];

  spin_unlock_bh(&ar->ab->base_lock);
  timer_delete_sync(&rx_tid->frag_timer);
  spin_lock_bh(&ar->ab->base_lock);

  ath11k_dp_rx_frags_cleanup(rx_tid, true);
 }
}

void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer)
{
 struct dp_rx_tid *rx_tid;
 int i;

 lockdep_assert_held(&ar->ab->base_lock);

 for (i = 0; i <= IEEE80211_NUM_TIDS; i++) {
  rx_tid = &peer->rx_tid[i];

  ath11k_peer_rx_tid_delete(ar, peer, i);
  ath11k_dp_rx_frags_cleanup(rx_tid, true);

  spin_unlock_bh(&ar->ab->base_lock);
  timer_delete_sync(&rx_tid->frag_timer);
  spin_lock_bh(&ar->ab->base_lock);
 }
}

static int ath11k_peer_rx_tid_reo_update(struct ath11k *ar,
      struct ath11k_peer *peer,
      struct dp_rx_tid *rx_tid,
      u32 ba_win_sz, u16 ssn,
      bool update_ssn)
{
 struct ath11k_hal_reo_cmd cmd = {};
 int ret;

 cmd.addr_lo = lower_32_bits(rx_tid->paddr);
 cmd.addr_hi = upper_32_bits(rx_tid->paddr);
 cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
 cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE;
 cmd.ba_window_size = ba_win_sz;

 if (update_ssn) {
  cmd.upd0 |= HAL_REO_CMD_UPD0_SSN;
  cmd.upd2 = FIELD_PREP(HAL_REO_CMD_UPD2_SSN, ssn);
 }

 ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid,
     HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd,
     NULL);
 if (ret) {
  ath11k_warn(ar->ab, "failed to update rx tid queue, tid %d (%d)\n",
       rx_tid->tid, ret);
  return ret;
 }

 rx_tid->ba_win_sz = ba_win_sz;

 return 0;
}

static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab,
          const u8 *peer_mac, int vdev_id, u8 tid)
{
 struct ath11k_peer *peer;
 struct dp_rx_tid *rx_tid;

 spin_lock_bh(&ab->base_lock);

 peer = ath11k_peer_find(ab, vdev_id, peer_mac);
 if (!peer) {
  ath11k_warn(ab, "failed to find the peer to free up rx tid mem\n");
  goto unlock_exit;
 }

 rx_tid = &peer->rx_tid[tid];
 if (!rx_tid->active)
  goto unlock_exit;

 dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, rx_tid->vaddr_unaligned,
        rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL);
 rx_tid->vaddr_unaligned = NULL;

 rx_tid->active = false;

unlock_exit:
 spin_unlock_bh(&ab->base_lock);
}

int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
        u8 tid, u32 ba_win_sz, u16 ssn,
        enum hal_pn_type pn_type)
{
 struct ath11k_base *ab = ar->ab;
 struct ath11k_peer *peer;
 struct dp_rx_tid *rx_tid;
 u32 hw_desc_sz, *vaddr;
 void *vaddr_unaligned;
 dma_addr_t paddr;
 int ret;

 spin_lock_bh(&ab->base_lock);

 peer = ath11k_peer_find(ab, vdev_id, peer_mac);
 if (!peer) {
  ath11k_warn(ab, "failed to find the peer %pM to set up rx tid\n",
       peer_mac);
  spin_unlock_bh(&ab->base_lock);
  return -ENOENT;
 }

 rx_tid = &peer->rx_tid[tid];
 /* Update the tid queue if it is already setup */
 if (rx_tid->active) {
  paddr = rx_tid->paddr;
  ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid,
          ba_win_sz, ssn, true);
  spin_unlock_bh(&ab->base_lock);
  if (ret) {
   ath11k_warn(ab, "failed to update reo for peer %pM rx tid %d\n: %d",
        peer_mac, tid, ret);
   return ret;
  }

  ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id,
            peer_mac, paddr,
            tid, 1, ba_win_sz);
  if (ret)
   ath11k_warn(ab, "failed to send wmi rx reorder queue for peer %pM tid %d: %d\n",
        peer_mac, tid, ret);
  return ret;
 }

 rx_tid->tid = tid;

 rx_tid->ba_win_sz = ba_win_sz;

 /* TODO: Optimize the memory allocation for qos tid based on
 * the actual BA window size in REO tid update path.
 */

 if (tid == HAL_DESC_REO_NON_QOS_TID)
  hw_desc_sz = ath11k_hal_reo_qdesc_size(ba_win_sz, tid);
 else
  hw_desc_sz = ath11k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid);

 rx_tid->unaligned_size = hw_desc_sz + HAL_LINK_DESC_ALIGN - 1;
 vaddr_unaligned = dma_alloc_noncoherent(ab->dev, rx_tid->unaligned_size, &paddr,
      DMA_BIDIRECTIONAL, GFP_ATOMIC);
 if (!vaddr_unaligned) {
  spin_unlock_bh(&ab->base_lock);
  return -ENOMEM;
 }

 rx_tid->vaddr_unaligned = vaddr_unaligned;
 vaddr = PTR_ALIGN(vaddr_unaligned, HAL_LINK_DESC_ALIGN);
 rx_tid->paddr_unaligned = paddr;
 rx_tid->paddr = rx_tid->paddr_unaligned + ((unsigned long)vaddr -
   (unsigned long)rx_tid->vaddr_unaligned);
 ath11k_hal_reo_qdesc_setup(vaddr, tid, ba_win_sz, ssn, pn_type);
 rx_tid->size = hw_desc_sz;
 rx_tid->active = true;

 /* After dma_alloc_noncoherent, vaddr is being modified for reo qdesc setup.
 * Since these changes are not reflected in the device, driver now needs to
 * explicitly call dma_sync_single_for_device.
 */

 dma_sync_single_for_device(ab->dev, rx_tid->paddr,
       rx_tid->size,
       DMA_TO_DEVICE);
 spin_unlock_bh(&ab->base_lock);

 ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, rx_tid->paddr,
           tid, 1, ba_win_sz);
 if (ret) {
  ath11k_warn(ar->ab, "failed to setup rx reorder queue for peer %pM tid %d: %d\n",
       peer_mac, tid, ret);
  ath11k_dp_rx_tid_mem_free(ab, peer_mac, vdev_id, tid);
 }

 return ret;
}

int ath11k_dp_rx_ampdu_start(struct ath11k *ar,
        struct ieee80211_ampdu_params *params)
{
 struct ath11k_base *ab = ar->ab;
 struct ath11k_sta *arsta = ath11k_sta_to_arsta(params->sta);
 int vdev_id = arsta->arvif->vdev_id;
 int ret;

 ret = ath11k_peer_rx_tid_setup(ar, params->sta->addr, vdev_id,
           params->tid, params->buf_size,
           params->ssn, arsta->pn_type);
 if (ret)
  ath11k_warn(ab, "failed to setup rx tid %d\n", ret);

 return ret;
}

int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
       struct ieee80211_ampdu_params *params)
{
 struct ath11k_base *ab = ar->ab;
 struct ath11k_peer *peer;
 struct ath11k_sta *arsta = ath11k_sta_to_arsta(params->sta);
 int vdev_id = arsta->arvif->vdev_id;
 dma_addr_t paddr;
 bool active;
 int ret;

 spin_lock_bh(&ab->base_lock);

 peer = ath11k_peer_find(ab, vdev_id, params->sta->addr);
 if (!peer) {
  ath11k_warn(ab, "failed to find the peer to stop rx aggregation\n");
  spin_unlock_bh(&ab->base_lock);
  return -ENOENT;
 }

 paddr = peer->rx_tid[params->tid].paddr;
 active = peer->rx_tid[params->tid].active;

 if (!active) {
  spin_unlock_bh(&ab->base_lock);
  return 0;
 }

 ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false);
 spin_unlock_bh(&ab->base_lock);
 if (ret) {
  ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n",
       params->tid, ret);
  return ret;
 }

 ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id,
           params->sta->addr, paddr,
           params->tid, 1, 1);
 if (ret)
  ath11k_warn(ab, "failed to send wmi to delete rx tid %d\n",
       ret);

 return ret;
}

int ath11k_dp_peer_rx_pn_replay_config(struct ath11k_vif *arvif,
           const u8 *peer_addr,
           enum set_key_cmd key_cmd,
           struct ieee80211_key_conf *key)
{
 struct ath11k *ar = arvif->ar;
 struct ath11k_base *ab = ar->ab;
 struct ath11k_hal_reo_cmd cmd = {};
 struct ath11k_peer *peer;
 struct dp_rx_tid *rx_tid;
 u8 tid;
 int ret = 0;

 /* NOTE: Enable PN/TSC replay check offload only for unicast frames.
 * We use mac80211 PN/TSC replay check functionality for bcast/mcast
 * for now.
 */

 if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
  return 0;

 cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS;
 cmd.upd0 |= HAL_REO_CMD_UPD0_PN |
      HAL_REO_CMD_UPD0_PN_SIZE |
      HAL_REO_CMD_UPD0_PN_VALID |
      HAL_REO_CMD_UPD0_PN_CHECK |
      HAL_REO_CMD_UPD0_SVLD;

 switch (key->cipher) {
 case WLAN_CIPHER_SUITE_TKIP:
 case WLAN_CIPHER_SUITE_CCMP:
 case WLAN_CIPHER_SUITE_CCMP_256:
 case WLAN_CIPHER_SUITE_GCMP:
 case WLAN_CIPHER_SUITE_GCMP_256:
  if (key_cmd == SET_KEY) {
   cmd.upd1 |= HAL_REO_CMD_UPD1_PN_CHECK;
   cmd.pn_size = 48;
  }
  break;
 default:
  break;
 }

 spin_lock_bh(&ab->base_lock);

 peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr);
 if (!peer) {
  ath11k_warn(ab, "failed to find the peer to configure pn replay detection\n");
  spin_unlock_bh(&ab->base_lock);
  return -ENOENT;
 }

 for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) {
  rx_tid = &peer->rx_tid[tid];
  if (!rx_tid->active)
   continue;
  cmd.addr_lo = lower_32_bits(rx_tid->paddr);
  cmd.addr_hi = upper_32_bits(rx_tid->paddr);
  ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid,
      HAL_REO_CMD_UPDATE_RX_QUEUE,
      &cmd, NULL);
  if (ret) {
   ath11k_warn(ab, "failed to configure rx tid %d queue for pn replay detection %d\n",
        tid, ret);
   break;
  }
 }

 spin_unlock_bh(&ab->base_lock);

 return ret;
}

static inline int ath11k_get_ppdu_user_index(struct htt_ppdu_stats *ppdu_stats,
          u16 peer_id)
{
 int i;

 for (i = 0; i < HTT_PPDU_STATS_MAX_USERS - 1; i++) {
  if (ppdu_stats->user_stats[i].is_valid_peer_id) {
   if (peer_id == ppdu_stats->user_stats[i].peer_id)
    return i;
  } else {
   return i;
  }
 }

 return -EINVAL;
}

static int ath11k_htt_tlv_ppdu_stats_parse(struct ath11k_base *ab,
        u16 tag, u16 len, const void *ptr,
        void *data)
{
 struct htt_ppdu_stats_info *ppdu_info;
 struct htt_ppdu_user_stats *user_stats;
 int cur_user;
 u16 peer_id;

 ppdu_info = data;

 switch (tag) {
 case HTT_PPDU_STATS_TAG_COMMON:
  if (len < sizeof(struct htt_ppdu_stats_common)) {
   ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n",
        len, tag);
   return -EINVAL;
  }
  memcpy((void *)&ppdu_info->ppdu_stats.common, ptr,
         sizeof(struct htt_ppdu_stats_common));
  break;
 case HTT_PPDU_STATS_TAG_USR_RATE:
  if (len < sizeof(struct htt_ppdu_stats_user_rate)) {
   ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n",
        len, tag);
   return -EINVAL;
  }

  peer_id = ((struct htt_ppdu_stats_user_rate *)ptr)->sw_peer_id;
  cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats,
            peer_id);
  if (cur_user < 0)
   return -EINVAL;
  user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user];
  user_stats->peer_id = peer_id;
  user_stats->is_valid_peer_id = true;
  memcpy((void *)&user_stats->rate, ptr,
         sizeof(struct htt_ppdu_stats_user_rate));
  user_stats->tlv_flags |= BIT(tag);
  break;
 case HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON:
  if (len < sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn)) {
   ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n",
        len, tag);
   return -EINVAL;
  }

  peer_id = ((struct htt_ppdu_stats_usr_cmpltn_cmn *)ptr)->sw_peer_id;
  cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats,
            peer_id);
  if (cur_user < 0)
   return -EINVAL;
  user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user];
  user_stats->peer_id = peer_id;
  user_stats->is_valid_peer_id = true;
  memcpy((void *)&user_stats->cmpltn_cmn, ptr,
         sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn));
  user_stats->tlv_flags |= BIT(tag);
  break;
 case HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS:
  if (len <
      sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)) {
   ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n",
        len, tag);
   return -EINVAL;
  }

  peer_id =
  ((struct htt_ppdu_stats_usr_cmpltn_ack_ba_status *)ptr)->sw_peer_id;
  cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats,
            peer_id);
  if (cur_user < 0)
   return -EINVAL;
  user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user];
  user_stats->peer_id = peer_id;
  user_stats->is_valid_peer_id = true;
  memcpy((void *)&user_stats->ack_ba, ptr,
         sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status));
  user_stats->tlv_flags |= BIT(tag);
  break;
 }
 return 0;
}

int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len,
      int (*iter)(struct ath11k_base *ar, u16 tag, u16 len,
           const void *ptr, void *data),
      void *data)
{
 const struct htt_tlv *tlv;
 const void *begin = ptr;
 u16 tlv_tag, tlv_len;
 int ret = -EINVAL;

 while (len > 0) {
  if (len < sizeof(*tlv)) {
   ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n",
       ptr - begin, len, sizeof(*tlv));
   return -EINVAL;
  }
  tlv = (struct htt_tlv *)ptr;
  tlv_tag = FIELD_GET(HTT_TLV_TAG, tlv->header);
  tlv_len = FIELD_GET(HTT_TLV_LEN, tlv->header);
  ptr += sizeof(*tlv);
  len -= sizeof(*tlv);

  if (tlv_len > len) {
   ath11k_err(ab, "htt tlv parse failure of tag %u at byte %zd (%zu bytes left, %u expected)\n",
       tlv_tag, ptr - begin, len, tlv_len);
   return -EINVAL;
  }
  ret = iter(ab, tlv_tag, tlv_len, ptr, data);
  if (ret == -ENOMEM)
   return ret;

  ptr += tlv_len;
  len -= tlv_len;
 }
 return 0;
}

static void
ath11k_update_per_peer_tx_stats(struct ath11k *ar,
    struct htt_ppdu_stats *ppdu_stats, u8 user)
{
 struct ath11k_base *ab = ar->ab;
 struct ath11k_peer *peer;
 struct ieee80211_sta *sta;
 struct ath11k_sta *arsta;
 struct htt_ppdu_stats_user_rate *user_rate;
 struct ath11k_per_peer_tx_stats *peer_stats = &ar->peer_tx_stats;
 struct htt_ppdu_user_stats *usr_stats = &ppdu_stats->user_stats[user];
 struct htt_ppdu_stats_common *common = &ppdu_stats->common;
 int ret;
 u8 flags, mcs, nss, bw, sgi, dcm, rate_idx = 0;
 u32 succ_bytes = 0;
 u16 rate = 0, succ_pkts = 0;
 u32 tx_duration = 0;
 u8 tid = HTT_PPDU_STATS_NON_QOS_TID;
 bool is_ampdu = false;

 if (!(usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_RATE)))
  return;

 if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON))
  is_ampdu =
   HTT_USR_CMPLTN_IS_AMPDU(usr_stats->cmpltn_cmn.flags);

 if (usr_stats->tlv_flags &
     BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS)) {
  succ_bytes = usr_stats->ack_ba.success_bytes;
  succ_pkts = FIELD_GET(HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M,
          usr_stats->ack_ba.info);
  tid = FIELD_GET(HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM,
    usr_stats->ack_ba.info);
 }

 if (common->fes_duration_us)
  tx_duration = common->fes_duration_us;

 user_rate = &usr_stats->rate;
 flags = HTT_USR_RATE_PREAMBLE(user_rate->rate_flags);
 bw = HTT_USR_RATE_BW(user_rate->rate_flags) - 2;
 nss = HTT_USR_RATE_NSS(user_rate->rate_flags) + 1;
 mcs = HTT_USR_RATE_MCS(user_rate->rate_flags);
 sgi = HTT_USR_RATE_GI(user_rate->rate_flags);
 dcm = HTT_USR_RATE_DCM(user_rate->rate_flags);

 /* Note: If host configured fixed rates and in some other special
 * cases, the broadcast/management frames are sent in different rates.
 * Firmware rate's control to be skipped for this?
 */


 if (flags == WMI_RATE_PREAMBLE_HE && mcs > ATH11K_HE_MCS_MAX) {
  ath11k_warn(ab, "Invalid HE mcs %d peer stats",  mcs);
  return;
 }

 if (flags == WMI_RATE_PREAMBLE_VHT && mcs > ATH11K_VHT_MCS_MAX) {
  ath11k_warn(ab, "Invalid VHT mcs %d peer stats",  mcs);
  return;
 }

 if (flags == WMI_RATE_PREAMBLE_HT && (mcs > ATH11K_HT_MCS_MAX || nss < 1)) {
  ath11k_warn(ab, "Invalid HT mcs %d nss %d peer stats",
       mcs, nss);
  return;
 }

 if (flags == WMI_RATE_PREAMBLE_CCK || flags == WMI_RATE_PREAMBLE_OFDM) {
  ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs,
           flags,
           &rate_idx,
           &rate);
  if (ret < 0)
   return;
 }

 rcu_read_lock();
 spin_lock_bh(&ab->base_lock);
 peer = ath11k_peer_find_by_id(ab, usr_stats->peer_id);

 if (!peer || !peer->sta) {
  spin_unlock_bh(&ab->base_lock);
  rcu_read_unlock();
  return;
 }

 sta = peer->sta;
 arsta = ath11k_sta_to_arsta(sta);

 memset(&arsta->txrate, 0, sizeof(arsta->txrate));

 switch (flags) {
 case WMI_RATE_PREAMBLE_OFDM:
  arsta->txrate.legacy = rate;
  break;
 case WMI_RATE_PREAMBLE_CCK:
  arsta->txrate.legacy = rate;
  break;
 case WMI_RATE_PREAMBLE_HT:
  arsta->txrate.mcs = mcs + 8 * (nss - 1);
  arsta->txrate.flags = RATE_INFO_FLAGS_MCS;
  if (sgi)
   arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
  break;
 case WMI_RATE_PREAMBLE_VHT:
  arsta->txrate.mcs = mcs;
  arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
  if (sgi)
   arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
  break;
 case WMI_RATE_PREAMBLE_HE:
  arsta->txrate.mcs = mcs;
  arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
  arsta->txrate.he_dcm = dcm;
  arsta->txrate.he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi);
  arsta->txrate.he_ru_alloc = ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc
      ((user_rate->ru_end -
       user_rate->ru_start) + 1);
  break;
 }

 arsta->txrate.nss = nss;

 arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw);
 arsta->tx_duration += tx_duration;
 memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info));

 /* PPDU stats reported for mgmt packet doesn't have valid tx bytes.
 * So skip peer stats update for mgmt packets.
 */

 if (tid < HTT_PPDU_STATS_NON_QOS_TID) {
  memset(peer_stats, 0, sizeof(*peer_stats));
  peer_stats->succ_pkts = succ_pkts;
  peer_stats->succ_bytes = succ_bytes;
  peer_stats->is_ampdu = is_ampdu;
  peer_stats->duration = tx_duration;
  peer_stats->ba_fails =
   HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) +
   HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags);

  if (ath11k_debugfs_is_extd_tx_stats_enabled(ar))
   ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx);
 }

 spin_unlock_bh(&ab->base_lock);
 rcu_read_unlock();
}

static void ath11k_htt_update_ppdu_stats(struct ath11k *ar,
      struct htt_ppdu_stats *ppdu_stats)
{
 u8 user;

 for (user = 0; user < HTT_PPDU_STATS_MAX_USERS - 1; user++)
  ath11k_update_per_peer_tx_stats(ar, ppdu_stats, user);
}

static
struct htt_ppdu_stats_info *ath11k_dp_htt_get_ppdu_desc(struct ath11k *ar,
       u32 ppdu_id)
{
 struct htt_ppdu_stats_info *ppdu_info;

 lockdep_assert_held(&ar->data_lock);

 if (!list_empty(&ar->ppdu_stats_info)) {
  list_for_each_entry(ppdu_info, &ar->ppdu_stats_info, list) {
   if (ppdu_info->ppdu_id == ppdu_id)
    return ppdu_info;
  }

  if (ar->ppdu_stat_list_depth > HTT_PPDU_DESC_MAX_DEPTH) {
   ppdu_info = list_first_entry(&ar->ppdu_stats_info,
           typeof(*ppdu_info), list);
   list_del(&ppdu_info->list);
   ar->ppdu_stat_list_depth--;
   ath11k_htt_update_ppdu_stats(ar, &ppdu_info->ppdu_stats);
   kfree(ppdu_info);
  }
 }

 ppdu_info = kzalloc(sizeof(*ppdu_info), GFP_ATOMIC);
 if (!ppdu_info)
  return NULL;

 list_add_tail(&ppdu_info->list, &ar->ppdu_stats_info);
 ar->ppdu_stat_list_depth++;

 return ppdu_info;
}

static int ath11k_htt_pull_ppdu_stats(struct ath11k_base *ab,
          struct sk_buff *skb)
{
 struct ath11k_htt_ppdu_stats_msg *msg;
 struct htt_ppdu_stats_info *ppdu_info;
 struct ath11k *ar;
 int ret;
 u8 pdev_id;
 u32 ppdu_id, len;

 msg = (struct ath11k_htt_ppdu_stats_msg *)skb->data;
 len = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE, msg->info);
 pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, msg->info);
 ppdu_id = msg->ppdu_id;

 rcu_read_lock();
 ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id);
 if (!ar) {
  ret = -EINVAL;
  goto out;
 }

 if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar))
  trace_ath11k_htt_ppdu_stats(ar, skb->data, len);

 spin_lock_bh(&ar->data_lock);
 ppdu_info = ath11k_dp_htt_get_ppdu_desc(ar, ppdu_id);
 if (!ppdu_info) {
  ret = -EINVAL;
  goto out_unlock_data;
 }

 ppdu_info->ppdu_id = ppdu_id;
 ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len,
         ath11k_htt_tlv_ppdu_stats_parse,
         (void *)ppdu_info);
 if (ret) {
  ath11k_warn(ab, "Failed to parse tlv %d\n", ret);
  goto out_unlock_data;
 }

out_unlock_data:
 spin_unlock_bh(&ar->data_lock);

out:
 rcu_read_unlock();

 return ret;
}

static void ath11k_htt_pktlog(struct ath11k_base *ab, struct sk_buff *skb)
{
 struct htt_pktlog_msg *data = (struct htt_pktlog_msg *)skb->data;
 struct ath_pktlog_hdr *hdr = (struct ath_pktlog_hdr *)data;
 struct ath11k *ar;
 u8 pdev_id;

 pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, data->hdr);

 rcu_read_lock();

 ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id);
 if (!ar) {
  ath11k_warn(ab, "invalid pdev id %d on htt pktlog\n", pdev_id);
  goto out;
 }

 trace_ath11k_htt_pktlog(ar, data->payload, hdr->size,
    ar->ab->pktlog_defs_checksum);

out:
 rcu_read_unlock();
}

static void ath11k_htt_backpressure_event_handler(struct ath11k_base *ab,
        struct sk_buff *skb)
{
 u32 *data = (u32 *)skb->data;
 u8 pdev_id, ring_type, ring_id, pdev_idx;
 u16 hp, tp;
 u32 backpressure_time;
 struct ath11k_bp_stats *bp_stats;

 pdev_id = FIELD_GET(HTT_BACKPRESSURE_EVENT_PDEV_ID_M, *data);
 ring_type = FIELD_GET(HTT_BACKPRESSURE_EVENT_RING_TYPE_M, *data);
 ring_id = FIELD_GET(HTT_BACKPRESSURE_EVENT_RING_ID_M, *data);
 ++data;

 hp = FIELD_GET(HTT_BACKPRESSURE_EVENT_HP_M, *data);
 tp = FIELD_GET(HTT_BACKPRESSURE_EVENT_TP_M, *data);
 ++data;

 backpressure_time = *data;

 ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "backpressure event, pdev %d, ring type %d,ring id %d, hp %d tp %d, backpressure time %d\n",
     pdev_id, ring_type, ring_id, hp, tp, backpressure_time);

 if (ring_type == HTT_BACKPRESSURE_UMAC_RING_TYPE) {
  if (ring_id >= HTT_SW_UMAC_RING_IDX_MAX)
   return;

  bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[ring_id];
 } else if (ring_type == HTT_BACKPRESSURE_LMAC_RING_TYPE) {
  pdev_idx = DP_HW2SW_MACID(pdev_id);

  if (ring_id >= HTT_SW_LMAC_RING_IDX_MAX || pdev_idx >= MAX_RADIOS)
   return;

  bp_stats = &ab->soc_stats.bp_stats.lmac_ring_bp_stats[ring_id][pdev_idx];
 } else {
  ath11k_warn(ab, "unknown ring type received in htt bp event %d\n",
       ring_type);
  return;
 }

 spin_lock_bh(&ab->base_lock);
 bp_stats->hp = hp;
 bp_stats->tp = tp;
 bp_stats->count++;
 bp_stats->jiffies = jiffies;
 spin_unlock_bh(&ab->base_lock);
}

void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab,
           struct sk_buff *skb)
{
 struct ath11k_dp *dp = &ab->dp;
 struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data;
 enum htt_t2h_msg_type type = FIELD_GET(HTT_T2H_MSG_TYPE, *(u32 *)resp);
 u16 peer_id;
 u8 vdev_id;
 u8 mac_addr[ETH_ALEN];
 u16 peer_mac_h16;
 u16 ast_hash;
 u16 hw_peer_id;

 ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "dp_htt rx msg type :0x%0x\n", type);

 switch (type) {
 case HTT_T2H_MSG_TYPE_VERSION_CONF:
  dp->htt_tgt_ver_major = FIELD_GET(HTT_T2H_VERSION_CONF_MAJOR,
        resp->version_msg.version);
  dp->htt_tgt_ver_minor = FIELD_GET(HTT_T2H_VERSION_CONF_MINOR,
        resp->version_msg.version);
  complete(&dp->htt_tgt_version_received);
  break;
 case HTT_T2H_MSG_TYPE_PEER_MAP:
  vdev_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_VDEV_ID,
        resp->peer_map_ev.info);
  peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_PEER_ID,
        resp->peer_map_ev.info);
  peer_mac_h16 = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16,
      resp->peer_map_ev.info1);
  ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32,
           peer_mac_h16, mac_addr);
  ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, 0, 0);
  break;
 case HTT_T2H_MSG_TYPE_PEER_MAP2:
  vdev_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_VDEV_ID,
        resp->peer_map_ev.info);
  peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_PEER_ID,
        resp->peer_map_ev.info);
  peer_mac_h16 = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16,
      resp->peer_map_ev.info1);
  ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32,
           peer_mac_h16, mac_addr);
  ast_hash = FIELD_GET(HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL,
         resp->peer_map_ev.info2);
  hw_peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID,
           resp->peer_map_ev.info1);
  ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash,
          hw_peer_id);
  break;
 case HTT_T2H_MSG_TYPE_PEER_UNMAP:
 case HTT_T2H_MSG_TYPE_PEER_UNMAP2:
  peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID,
        resp->peer_unmap_ev.info);
  ath11k_peer_unmap_event(ab, peer_id);
  break;
 case HTT_T2H_MSG_TYPE_PPDU_STATS_IND:
  ath11k_htt_pull_ppdu_stats(ab, skb);
  break;
 case HTT_T2H_MSG_TYPE_EXT_STATS_CONF:
  ath11k_debugfs_htt_ext_stats_handler(ab, skb);
  break;
 case HTT_T2H_MSG_TYPE_PKTLOG:
  ath11k_htt_pktlog(ab, skb);
  break;
 case HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND:
  ath11k_htt_backpressure_event_handler(ab, skb);
  break;
 default:
  ath11k_warn(ab, "htt event %d not handled\n", type);
  break;
 }

 dev_kfree_skb_any(skb);
}

static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
          struct sk_buff_head *msdu_list,
          struct sk_buff *first, struct sk_buff *last,
          u8 l3pad_bytes, int msdu_len)
{
 struct ath11k_base *ab = ar->ab;
 struct sk_buff *skb;
 struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(first);
 int buf_first_hdr_len, buf_first_len;
 struct hal_rx_desc *ldesc;
 int space_extra, rem_len, buf_len;
 u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz;

 /* As the msdu is spread across multiple rx buffers,
 * find the offset to the start of msdu for computing
 * the length of the msdu in the first buffer.
 */

 buf_first_hdr_len = hal_rx_desc_sz + l3pad_bytes;
 buf_first_len = DP_RX_BUFFER_SIZE - buf_first_hdr_len;

 if (WARN_ON_ONCE(msdu_len <= buf_first_len)) {
  skb_put(first, buf_first_hdr_len + msdu_len);
  skb_pull(first, buf_first_hdr_len);
  return 0;
 }

 ldesc = (struct hal_rx_desc *)last->data;
 rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(ab, ldesc);
 rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(ab, ldesc);

 /* MSDU spans over multiple buffers because the length of the MSDU
 * exceeds DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE. So assume the data
 * in the first buf is of length DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE.
 */

 skb_put(first, DP_RX_BUFFER_SIZE);
 skb_pull(first, buf_first_hdr_len);

 /* When an MSDU spread over multiple buffers attention, MSDU_END and
 * MPDU_END tlvs are valid only in the last buffer. Copy those tlvs.
 */

 ath11k_dp_rx_desc_end_tlv_copy(ab, rxcb->rx_desc, ldesc);

 space_extra = msdu_len - (buf_first_len + skb_tailroom(first));
 if (space_extra > 0 &&
     (pskb_expand_head(first, 0, space_extra, GFP_ATOMIC) < 0)) {
  /* Free up all buffers of the MSDU */
  while ((skb = __skb_dequeue(msdu_list)) != NULL) {
   rxcb = ATH11K_SKB_RXCB(skb);
   if (!rxcb->is_continuation) {
    dev_kfree_skb_any(skb);
    break;
   }
   dev_kfree_skb_any(skb);
  }
  return -ENOMEM;
 }

 rem_len = msdu_len - buf_first_len;
 while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) {
  rxcb = ATH11K_SKB_RXCB(skb);
  if (rxcb->is_continuation)
   buf_len = DP_RX_BUFFER_SIZE - hal_rx_desc_sz;
  else
   buf_len = rem_len;

  if (buf_len > (DP_RX_BUFFER_SIZE - hal_rx_desc_sz)) {
   WARN_ON_ONCE(1);
   dev_kfree_skb_any(skb);
   return -EINVAL;
  }

  skb_put(skb, buf_len + hal_rx_desc_sz);
  skb_pull(skb, hal_rx_desc_sz);
  skb_copy_from_linear_data(skb, skb_put(first, buf_len),
       buf_len);
  dev_kfree_skb_any(skb);

  rem_len -= buf_len;
  if (!rxcb->is_continuation)
   break;
 }

 return 0;
}

static struct sk_buff *ath11k_dp_rx_get_msdu_last_buf(struct sk_buff_head *msdu_list,
            struct sk_buff *first)
{
 struct sk_buff *skb;
 struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(first);

 if (!rxcb->is_continuation)
  return first;

 skb_queue_walk(msdu_list, skb) {
  rxcb = ATH11K_SKB_RXCB(skb);
  if (!rxcb->is_continuation)
   return skb;
 }

 return NULL;
}

static void ath11k_dp_rx_h_csum_offload(struct ath11k *ar, struct sk_buff *msdu)
{
 struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
 struct rx_attention *rx_attention;
 bool ip_csum_fail, l4_csum_fail;

 rx_attention = ath11k_dp_rx_get_attention(ar->ab, rxcb->rx_desc);
 ip_csum_fail = ath11k_dp_rx_h_attn_ip_cksum_fail(rx_attention);
 l4_csum_fail = ath11k_dp_rx_h_attn_l4_cksum_fail(rx_attention);

 msdu->ip_summed = (ip_csum_fail || l4_csum_fail) ?
     CHECKSUM_NONE : CHECKSUM_UNNECESSARY;
}

int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, enum hal_encrypt_type enctype)
{
 switch (enctype) {
 case HAL_ENCRYPT_TYPE_OPEN:
 case HAL_ENCRYPT_TYPE_TKIP_NO_MIC:
 case HAL_ENCRYPT_TYPE_TKIP_MIC:
  return 0;
 case HAL_ENCRYPT_TYPE_CCMP_128:
  return IEEE80211_CCMP_MIC_LEN;
 case HAL_ENCRYPT_TYPE_CCMP_256:
  return IEEE80211_CCMP_256_MIC_LEN;
 case HAL_ENCRYPT_TYPE_GCMP_128:
 case HAL_ENCRYPT_TYPE_AES_GCMP_256:
  return IEEE80211_GCMP_MIC_LEN;
 case HAL_ENCRYPT_TYPE_WEP_40:
 case HAL_ENCRYPT_TYPE_WEP_104:
 case HAL_ENCRYPT_TYPE_WEP_128:
 case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4:
 case HAL_ENCRYPT_TYPE_WAPI:
  break;
 }

 ath11k_warn(ar->ab, "unsupported encryption type %d for mic len\n", enctype);
 return 0;
}

static int ath11k_dp_rx_crypto_param_len(struct ath11k *ar,
      enum hal_encrypt_type enctype)
{
 switch (enctype) {
 case HAL_ENCRYPT_TYPE_OPEN:
  return 0;
 case HAL_ENCRYPT_TYPE_TKIP_NO_MIC:
 case HAL_ENCRYPT_TYPE_TKIP_MIC:
  return IEEE80211_TKIP_IV_LEN;
 case HAL_ENCRYPT_TYPE_CCMP_128:
  return IEEE80211_CCMP_HDR_LEN;
 case HAL_ENCRYPT_TYPE_CCMP_256:
  return IEEE80211_CCMP_256_HDR_LEN;
 case HAL_ENCRYPT_TYPE_GCMP_128:
 case HAL_ENCRYPT_TYPE_AES_GCMP_256:
  return IEEE80211_GCMP_HDR_LEN;
 case HAL_ENCRYPT_TYPE_WEP_40:
 case HAL_ENCRYPT_TYPE_WEP_104:
 case HAL_ENCRYPT_TYPE_WEP_128:
 case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4:
 case HAL_ENCRYPT_TYPE_WAPI:
  break;
 }

 ath11k_warn(ar->ab, "unsupported encryption type %d\n", enctype);
 return 0;
}

static int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar,
           enum hal_encrypt_type enctype)
{
 switch (enctype) {
 case HAL_ENCRYPT_TYPE_OPEN:
 case HAL_ENCRYPT_TYPE_CCMP_128:
 case HAL_ENCRYPT_TYPE_CCMP_256:
 case HAL_ENCRYPT_TYPE_GCMP_128:
 case HAL_ENCRYPT_TYPE_AES_GCMP_256:
  return 0;
 case HAL_ENCRYPT_TYPE_TKIP_NO_MIC:
 case HAL_ENCRYPT_TYPE_TKIP_MIC:
  return IEEE80211_TKIP_ICV_LEN;
 case HAL_ENCRYPT_TYPE_WEP_40:
 case HAL_ENCRYPT_TYPE_WEP_104:
 case HAL_ENCRYPT_TYPE_WEP_128:
 case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4:
 case HAL_ENCRYPT_TYPE_WAPI:
  break;
 }

 ath11k_warn(ar->ab, "unsupported encryption type %d\n", enctype);
 return 0;
}

static void ath11k_dp_rx_h_undecap_nwifi(struct ath11k *ar,
      struct sk_buff *msdu,
      u8 *first_hdr,
      enum hal_encrypt_type enctype,
      struct ieee80211_rx_status *status)
{
 struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
 u8 decap_hdr[DP_MAX_NWIFI_HDR_LEN];
 struct ieee80211_hdr *hdr;
 size_t hdr_len;
 u8 da[ETH_ALEN];
 u8 sa[ETH_ALEN];
 u16 qos_ctl = 0;
 u8 *qos;

 /* copy SA & DA and pull decapped header */
 hdr = (struct ieee80211_hdr *)msdu->data;
 hdr_len = ieee80211_hdrlen(hdr->frame_control);
 ether_addr_copy(da, ieee80211_get_DA(hdr));
 ether_addr_copy(sa, ieee80211_get_SA(hdr));
 skb_pull(msdu, ieee80211_hdrlen(hdr->frame_control));

 if (rxcb->is_first_msdu) {
  /* original 802.11 header is valid for the first msdu
 * hence we can reuse the same header
 */

  hdr = (struct ieee80211_hdr *)first_hdr;
  hdr_len = ieee80211_hdrlen(hdr->frame_control);

  /* Each A-MSDU subframe will be reported as a separate MSDU,
 * so strip the A-MSDU bit from QoS Ctl.
 */

  if (ieee80211_is_data_qos(hdr->frame_control)) {
   qos = ieee80211_get_qos_ctl(hdr);
   qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
  }
 } else {
  /*  Rebuild qos header if this is a middle/last msdu */
  hdr->frame_control |= __cpu_to_le16(IEEE80211_STYPE_QOS_DATA);

  /* Reset the order bit as the HT_Control header is stripped */
  hdr->frame_control &= ~(__cpu_to_le16(IEEE80211_FCTL_ORDER));

  qos_ctl = rxcb->tid;

  if (ath11k_dp_rx_h_msdu_start_mesh_ctl_present(ar->ab, rxcb->rx_desc))
   qos_ctl |= IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT;

  /* TODO Add other QoS ctl fields when required */

  /* copy decap header before overwriting for reuse below */
  memcpy(decap_hdr, (uint8_t *)hdr, hdr_len);
 }

 if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
  memcpy(skb_push(msdu,
    ath11k_dp_rx_crypto_param_len(ar, enctype)),
         (void *)hdr + hdr_len,
         ath11k_dp_rx_crypto_param_len(ar, enctype));
 }

 if (!rxcb->is_first_msdu) {
  memcpy(skb_push(msdu,
    IEEE80211_QOS_CTL_LEN), &qos_ctl,
    IEEE80211_QOS_CTL_LEN);
  memcpy(skb_push(msdu, hdr_len), decap_hdr, hdr_len);
  return;
 }

 memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);

 /* original 802.11 header has a different DA and in
 * case of 4addr it may also have different SA
 */

 hdr = (struct ieee80211_hdr *)msdu->data;
 ether_addr_copy(ieee80211_get_DA(hdr), da);
 ether_addr_copy(ieee80211_get_SA(hdr), sa);
}

static void ath11k_dp_rx_h_undecap_raw(struct ath11k *ar, struct sk_buff *msdu,
           enum hal_encrypt_type enctype,
           struct ieee80211_rx_status *status,
           bool decrypted)
{
 struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
 struct ieee80211_hdr *hdr;
 size_t hdr_len;
 size_t crypto_len;

 if (!rxcb->is_first_msdu ||
     !(rxcb->is_first_msdu && rxcb->is_last_msdu)) {
  WARN_ON_ONCE(1);
  return;
 }

 skb_trim(msdu, msdu->len - FCS_LEN);

 if (!decrypted)
  return;

 hdr = (void *)msdu->data;

 /* Tail */
 if (status->flag & RX_FLAG_IV_STRIPPED) {
  skb_trim(msdu, msdu->len -
    ath11k_dp_rx_crypto_mic_len(ar, enctype));

  skb_trim(msdu, msdu->len -
    ath11k_dp_rx_crypto_icv_len(ar, enctype));
 } else {
  /* MIC */
  if (status->flag & RX_FLAG_MIC_STRIPPED)
   skb_trim(msdu, msdu->len -
     ath11k_dp_rx_crypto_mic_len(ar, enctype));

  /* ICV */
  if (status->flag & RX_FLAG_ICV_STRIPPED)
   skb_trim(msdu, msdu->len -
     ath11k_dp_rx_crypto_icv_len(ar, enctype));
 }

 /* MMIC */
 if ((status->flag & RX_FLAG_MMIC_STRIPPED) &&
     !ieee80211_has_morefrags(hdr->frame_control) &&
     enctype == HAL_ENCRYPT_TYPE_TKIP_MIC)
  skb_trim(msdu, msdu->len - IEEE80211_CCMP_MIC_LEN);

 /* Head */
 if (status->flag & RX_FLAG_IV_STRIPPED) {
  hdr_len = ieee80211_hdrlen(hdr->frame_control);
  crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype);

  memmove((void *)msdu->data + crypto_len,
   (void *)msdu->data, hdr_len);
  skb_pull(msdu, crypto_len);
 }
}

static void *ath11k_dp_rx_h_find_rfc1042(struct ath11k *ar,
      struct sk_buff *msdu,
      enum hal_encrypt_type enctype)
{
 struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
 struct ieee80211_hdr *hdr;
 size_t hdr_len, crypto_len;
 void *rfc1042;
 bool is_amsdu;

 is_amsdu = !(rxcb->is_first_msdu && rxcb->is_last_msdu);
 hdr = (struct ieee80211_hdr *)ath11k_dp_rx_h_80211_hdr(ar->ab, rxcb->rx_desc);
 rfc1042 = hdr;

 if (rxcb->is_first_msdu) {
  hdr_len = ieee80211_hdrlen(hdr->frame_control);
  crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype);

  rfc1042 += hdr_len + crypto_len;
 }

 if (is_amsdu)
  rfc1042 += sizeof(struct ath11k_dp_amsdu_subframe_hdr);

 return rfc1042;
}

static void ath11k_dp_rx_h_undecap_eth(struct ath11k *ar,
           struct sk_buff *msdu,
           u8 *first_hdr,
           enum hal_encrypt_type enctype,
           struct ieee80211_rx_status *status)
{
 struct ieee80211_hdr *hdr;
 struct ethhdr *eth;
 size_t hdr_len;
 u8 da[ETH_ALEN];
 u8 sa[ETH_ALEN];
 void *rfc1042;

 rfc1042 = ath11k_dp_rx_h_find_rfc1042(ar, msdu, enctype);
 if (WARN_ON_ONCE(!rfc1042))
  return;

 /* pull decapped header and copy SA & DA */
 eth = (struct ethhdr *)msdu->data;
 ether_addr_copy(da, eth->h_dest);
 ether_addr_copy(sa, eth->h_source);
 skb_pull(msdu, sizeof(struct ethhdr));

 /* push rfc1042/llc/snap */
 memcpy(skb_push(msdu, sizeof(struct ath11k_dp_rfc1042_hdr)), rfc1042,
        sizeof(struct ath11k_dp_rfc1042_hdr));

 /* push original 802.11 header */
 hdr = (struct ieee80211_hdr *)first_hdr;
 hdr_len = ieee80211_hdrlen(hdr->frame_control);

 if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
  memcpy(skb_push(msdu,
    ath11k_dp_rx_crypto_param_len(ar, enctype)),
         (void *)hdr + hdr_len,
         ath11k_dp_rx_crypto_param_len(ar, enctype));
 }

 memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);

 /* original 802.11 header has a different DA and in
 * case of 4addr it may also have different SA
 */

 hdr = (struct ieee80211_hdr *)msdu->data;
 ether_addr_copy(ieee80211_get_DA(hdr), da);
 ether_addr_copy(ieee80211_get_SA(hdr), sa);
}

static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu,
       struct hal_rx_desc *rx_desc,
       enum hal_encrypt_type enctype,
       struct ieee80211_rx_status *status,
       bool decrypted)
{
 u8 *first_hdr;
 u8 decap;
 struct ethhdr *ehdr;

 first_hdr = ath11k_dp_rx_h_80211_hdr(ar->ab, rx_desc);
 decap = ath11k_dp_rx_h_msdu_start_decap_type(ar->ab, rx_desc);

 switch (decap) {
 case DP_RX_DECAP_TYPE_NATIVE_WIFI:
  ath11k_dp_rx_h_undecap_nwifi(ar, msdu, first_hdr,
          enctype, status);
  break;
 case DP_RX_DECAP_TYPE_RAW:
  ath11k_dp_rx_h_undecap_raw(ar, msdu, enctype, status,
        decrypted);
  break;
 case DP_RX_DECAP_TYPE_ETHERNET2_DIX:
  ehdr = (struct ethhdr *)msdu->data;

  /* mac80211 allows fast path only for authorized STA */
  if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE)) {
   ATH11K_SKB_RXCB(msdu)->is_eapol = true;
   ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr,
         enctype, status);
   break;
  }

  /* PN for mcast packets will be validated in mac80211;
 * remove eth header and add 802.11 header.
 */

  if (ATH11K_SKB_RXCB(msdu)->is_mcbc && decrypted)
   ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr,
         enctype, status);
  break;
 case DP_RX_DECAP_TYPE_8023:
  /* TODO: Handle undecap for these formats */
  break;
 }
}

static struct ath11k_peer *
ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu)
{
 struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
 struct hal_rx_desc *rx_desc = rxcb->rx_desc;
 struct ath11k_peer *peer = NULL;

 lockdep_assert_held(&ab->base_lock);

 if (rxcb->peer_id)
  peer = ath11k_peer_find_by_id(ab, rxcb->peer_id);

 if (peer)
  return peer;

 if (!rx_desc || !(ath11k_dp_rxdesc_mac_addr2_valid(ab, rx_desc)))
  return NULL;

 peer = ath11k_peer_find_by_addr(ab,
     ath11k_dp_rxdesc_mpdu_start_addr2(ab, rx_desc));
 return peer;
}

static void ath11k_dp_rx_h_mpdu(struct ath11k *ar,
    struct sk_buff *msdu,
    struct hal_rx_desc *rx_desc,
    struct ieee80211_rx_status *rx_status)
{
 bool  fill_crypto_hdr;
 enum hal_encrypt_type enctype;
 bool is_decrypted = false;
 struct ath11k_skb_rxcb *rxcb;
 struct ieee80211_hdr *hdr;
 struct ath11k_peer *peer;
 struct rx_attention *rx_attention;
 u32 err_bitmap;

 /* PN for multicast packets will be checked in mac80211 */
 rxcb = ATH11K_SKB_RXCB(msdu);
 fill_crypto_hdr = ath11k_dp_rx_h_attn_is_mcbc(ar->ab, rx_desc);
 rxcb->is_mcbc = fill_crypto_hdr;

 if (rxcb->is_mcbc) {
  rxcb->peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(ar->ab, rx_desc);
  rxcb->seq_no = ath11k_dp_rx_h_mpdu_start_seq_no(ar->ab, rx_desc);
 }

 spin_lock_bh(&ar->ab->base_lock);
 peer = ath11k_dp_rx_h_find_peer(ar->ab, msdu);
 if (peer) {
  if (rxcb->is_mcbc)
   enctype = peer->sec_type_grp;
  else
   enctype = peer->sec_type;
 } else {
  enctype = ath11k_dp_rx_h_mpdu_start_enctype(ar->ab, rx_desc);
 }
 spin_unlock_bh(&ar->ab->base_lock);

 rx_attention = ath11k_dp_rx_get_attention(ar->ab, rx_desc);
 err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(rx_attention);
 if (enctype != HAL_ENCRYPT_TYPE_OPEN && !err_bitmap)
  is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_attention);

 /* Clear per-MPDU flags while leaving per-PPDU flags intact */
 rx_status->flag &= ~(RX_FLAG_FAILED_FCS_CRC |
        RX_FLAG_MMIC_ERROR |
        RX_FLAG_DECRYPTED |
        RX_FLAG_IV_STRIPPED |
        RX_FLAG_MMIC_STRIPPED);

 if (err_bitmap & DP_RX_MPDU_ERR_FCS)
  rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
 if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC)
  rx_status->flag |= RX_FLAG_MMIC_ERROR;

 if (is_decrypted) {
  rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED;

  if (fill_crypto_hdr)
   rx_status->flag |= RX_FLAG_MIC_STRIPPED |
     RX_FLAG_ICV_STRIPPED;
  else
   rx_status->flag |= RX_FLAG_IV_STRIPPED |
        RX_FLAG_PN_VALIDATED;
 }

 ath11k_dp_rx_h_csum_offload(ar, msdu);
 ath11k_dp_rx_h_undecap(ar, msdu, rx_desc,
          enctype, rx_status, is_decrypted);

 if (!is_decrypted || fill_crypto_hdr)
  return;

 if (ath11k_dp_rx_h_msdu_start_decap_type(ar->ab, rx_desc) !=
     DP_RX_DECAP_TYPE_ETHERNET2_DIX) {
  hdr = (void *)msdu->data;
  hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
 }
}

static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc,
    struct ieee80211_rx_status *rx_status)
{
 struct ieee80211_supported_band *sband;
 enum rx_msdu_start_pkt_type pkt_type;
 u8 bw;
 u8 rate_mcs, nss;
 u8 sgi;
 bool is_cck, is_ldpc;

 pkt_type = ath11k_dp_rx_h_msdu_start_pkt_type(ar->ab, rx_desc);
 bw = ath11k_dp_rx_h_msdu_start_rx_bw(ar->ab, rx_desc);
 rate_mcs = ath11k_dp_rx_h_msdu_start_rate_mcs(ar->ab, rx_desc);
 nss = ath11k_dp_rx_h_msdu_start_nss(ar->ab, rx_desc);
 sgi = ath11k_dp_rx_h_msdu_start_sgi(ar->ab, rx_desc);

 switch (pkt_type) {
 case RX_MSDU_START_PKT_TYPE_11A:
 case RX_MSDU_START_PKT_TYPE_11B:
  is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B);
  sband = &ar->mac.sbands[rx_status->band];
  rx_status->rate_idx = ath11k_mac_hw_rate_to_idx(sband, rate_mcs,
        is_cck);
  break;
 case RX_MSDU_START_PKT_TYPE_11N:
  rx_status->encoding = RX_ENC_HT;
  if (rate_mcs > ATH11K_HT_MCS_MAX) {
   ath11k_warn(ar->ab,
        "Received with invalid mcs in HT mode %d\n",
         rate_mcs);
   break;
  }
  rx_status->rate_idx = rate_mcs + (8 * (nss - 1));
  if (sgi)
   rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
  rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw);
  break;
 case RX_MSDU_START_PKT_TYPE_11AC:
  rx_status->encoding = RX_ENC_VHT;
  rx_status->rate_idx = rate_mcs;
  if (rate_mcs > ATH11K_VHT_MCS_MAX) {
   ath11k_warn(ar->ab,
        "Received with invalid mcs in VHT mode %d\n",
         rate_mcs);
   break;
  }
  rx_status->nss = nss;
  if (sgi)
   rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=96 G=94

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.