mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
ath12k_dp_rx_deliver_msdu() currently uses hal_rx_desc_data::peer_id
parsed from mpdu_start descriptor to do peer lookup. However In an A-MSDU
aggregation scenario, hardware only populates mpdu_start descriptor for
the first sub-msdu, but not the following ones. In that case peer_id could
be invalid, leading to peer lookup failure:
ath12k_wifi7_pci 0000:06:00.0: rx skb 00000000c391c041 len 1532 peer (null) 0 ucast sn 0 eht320 rate_idx 12 vht_nss 2 freq 6105 band 3 flag 0x40d1a fcs-err 0 mic-err 0 amsdu-more 0
As a result pubsta is NULL and parts of ieee80211_rx_status structure are
left uninitialized, which may cause unexpected behavior.
Fix it by switching the normal RX path to use ath12k_skb_rxcb::peer_id
which is parsed from REO ring's rx_mpdu_desc and is always valid.
hal_rx_desc_data::peer_id is still used in
ath12k_wifi7_dp_rx_frag_h_mpdu(), which is safe since A-MSDU
aggregation does not occur for fragmented frames. Similarly,
ath12k_skb_rxcb::peer_id may be overwritten by hal_rx_desc_data::peer_id
in ath12k_wifi7_dp_rx_h_mpdu(), which only handles non-aggregated
multicast/broadcast traffic.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Fixes: 11157e0910 ("wifi: ath12k: Use ath12k_dp_peer in per packet Tx & Rx paths")
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com>
Link: https://patch.msgid.link/20260427-ath12k-fix-peer-id-source-v1-1-b5f701fb8e88@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
1813 lines
48 KiB
C
1813 lines
48 KiB
C
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
/*
|
|
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
|
|
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
*/
|
|
|
|
#include <linux/fips.h>
|
|
#include <linux/ieee80211.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/skbuff.h>
|
|
#include "core.h"
|
|
#include "debug.h"
|
|
#include "hw.h"
|
|
#include "dp_rx.h"
|
|
#include "dp_tx.h"
|
|
#include "peer.h"
|
|
#include "dp_mon.h"
|
|
#include "debugfs_htt_stats.h"
|
|
|
|
static int ath12k_dp_rx_tid_delete_handler(struct ath12k_base *ab,
|
|
struct ath12k_dp_rx_tid_rxq *rx_tid);
|
|
|
|
static size_t ath12k_dp_list_cut_nodes(struct list_head *list,
|
|
struct list_head *head,
|
|
size_t count)
|
|
{
|
|
struct list_head *cur;
|
|
struct ath12k_rx_desc_info *rx_desc;
|
|
size_t nodes = 0;
|
|
|
|
if (!count) {
|
|
INIT_LIST_HEAD(list);
|
|
goto out;
|
|
}
|
|
|
|
list_for_each(cur, head) {
|
|
if (!count)
|
|
break;
|
|
|
|
rx_desc = list_entry(cur, struct ath12k_rx_desc_info, list);
|
|
rx_desc->in_use = true;
|
|
|
|
count--;
|
|
nodes++;
|
|
}
|
|
|
|
list_cut_before(list, head, cur);
|
|
out:
|
|
return nodes;
|
|
}
|
|
|
|
static void ath12k_dp_rx_enqueue_free(struct ath12k_dp *dp,
|
|
struct list_head *used_list)
|
|
{
|
|
struct ath12k_rx_desc_info *rx_desc, *safe;
|
|
|
|
/* Reset the use flag */
|
|
list_for_each_entry_safe(rx_desc, safe, used_list, list)
|
|
rx_desc->in_use = false;
|
|
|
|
spin_lock_bh(&dp->rx_desc_lock);
|
|
list_splice_tail(used_list, &dp->rx_desc_free_list);
|
|
spin_unlock_bh(&dp->rx_desc_lock);
|
|
}
|
|
|
|
/* Returns number of Rx buffers replenished */
|
|
int ath12k_dp_rx_bufs_replenish(struct ath12k_dp *dp,
|
|
struct dp_rxdma_ring *rx_ring,
|
|
struct list_head *used_list,
|
|
int req_entries)
|
|
{
|
|
struct ath12k_base *ab = dp->ab;
|
|
struct ath12k_buffer_addr *desc;
|
|
struct hal_srng *srng;
|
|
struct sk_buff *skb;
|
|
int num_free;
|
|
int num_remain;
|
|
u32 cookie;
|
|
dma_addr_t paddr;
|
|
struct ath12k_rx_desc_info *rx_desc;
|
|
enum hal_rx_buf_return_buf_manager mgr = dp->hal->hal_params->rx_buf_rbm;
|
|
|
|
req_entries = min(req_entries, rx_ring->bufs_max);
|
|
|
|
srng = &dp->hal->srng_list[rx_ring->refill_buf_ring.ring_id];
|
|
|
|
spin_lock_bh(&srng->lock);
|
|
|
|
ath12k_hal_srng_access_begin(ab, srng);
|
|
|
|
num_free = ath12k_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;
|
|
|
|
if (!num_remain)
|
|
goto out;
|
|
|
|
/* Get the descriptor from free list */
|
|
if (list_empty(used_list)) {
|
|
spin_lock_bh(&dp->rx_desc_lock);
|
|
req_entries = ath12k_dp_list_cut_nodes(used_list,
|
|
&dp->rx_desc_free_list,
|
|
num_remain);
|
|
spin_unlock_bh(&dp->rx_desc_lock);
|
|
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(dp->dev, skb->data,
|
|
skb->len + skb_tailroom(skb),
|
|
DMA_FROM_DEVICE);
|
|
if (dma_mapping_error(dp->dev, paddr))
|
|
goto fail_free_skb;
|
|
|
|
rx_desc = list_first_entry_or_null(used_list,
|
|
struct ath12k_rx_desc_info,
|
|
list);
|
|
if (!rx_desc)
|
|
goto fail_dma_unmap;
|
|
|
|
rx_desc->skb = skb;
|
|
cookie = rx_desc->cookie;
|
|
|
|
desc = ath12k_hal_srng_src_get_next_entry(ab, srng);
|
|
if (!desc)
|
|
goto fail_dma_unmap;
|
|
|
|
list_del(&rx_desc->list);
|
|
ATH12K_SKB_RXCB(skb)->paddr = paddr;
|
|
|
|
num_remain--;
|
|
|
|
ath12k_hal_rx_buf_addr_info_set(dp->hal, desc, paddr, cookie,
|
|
mgr);
|
|
}
|
|
|
|
goto out;
|
|
|
|
fail_dma_unmap:
|
|
dma_unmap_single(dp->dev, paddr, skb->len + skb_tailroom(skb),
|
|
DMA_FROM_DEVICE);
|
|
fail_free_skb:
|
|
dev_kfree_skb_any(skb);
|
|
out:
|
|
ath12k_hal_srng_access_end(ab, srng);
|
|
|
|
if (!list_empty(used_list))
|
|
ath12k_dp_rx_enqueue_free(dp, used_list);
|
|
|
|
spin_unlock_bh(&srng->lock);
|
|
|
|
return req_entries - num_remain;
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_bufs_replenish);
|
|
|
|
static int ath12k_dp_rxdma_mon_buf_ring_free(struct ath12k_base *ab,
|
|
struct dp_rxdma_mon_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(ab->dev, ATH12K_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 ath12k_dp_rxdma_buf_free(struct ath12k_base *ab)
|
|
{
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
int i;
|
|
|
|
ath12k_dp_rxdma_mon_buf_ring_free(ab, &dp->rxdma_mon_buf_ring);
|
|
|
|
if (ab->hw_params->rxdma1_enable)
|
|
return 0;
|
|
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++)
|
|
ath12k_dp_rxdma_mon_buf_ring_free(ab,
|
|
&dp->rx_mon_status_refill_ring[i]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ath12k_dp_rxdma_mon_ring_buf_setup(struct ath12k_base *ab,
|
|
struct dp_rxdma_mon_ring *rx_ring,
|
|
u32 ringtype)
|
|
{
|
|
int num_entries;
|
|
|
|
num_entries = rx_ring->refill_buf_ring.size /
|
|
ath12k_hal_srng_get_entrysize(ab, ringtype);
|
|
|
|
rx_ring->bufs_max = num_entries;
|
|
|
|
if (ringtype == HAL_RXDMA_MONITOR_STATUS)
|
|
ath12k_dp_mon_status_bufs_replenish(ab, rx_ring,
|
|
num_entries);
|
|
else
|
|
ath12k_dp_mon_buf_replenish(ab, rx_ring, num_entries);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ath12k_dp_rxdma_ring_buf_setup(struct ath12k_base *ab,
|
|
struct dp_rxdma_ring *rx_ring)
|
|
{
|
|
LIST_HEAD(list);
|
|
|
|
rx_ring->bufs_max = rx_ring->refill_buf_ring.size /
|
|
ath12k_hal_srng_get_entrysize(ab, HAL_RXDMA_BUF);
|
|
|
|
ath12k_dp_rx_bufs_replenish(ath12k_ab_to_dp(ab), rx_ring, &list, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ath12k_dp_rxdma_buf_setup(struct ath12k_base *ab)
|
|
{
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
struct dp_rxdma_mon_ring *mon_ring;
|
|
int ret, i;
|
|
|
|
ret = ath12k_dp_rxdma_ring_buf_setup(ab, &dp->rx_refill_buf_ring);
|
|
if (ret) {
|
|
ath12k_warn(ab,
|
|
"failed to setup HAL_RXDMA_BUF\n");
|
|
return ret;
|
|
}
|
|
|
|
if (ab->hw_params->rxdma1_enable) {
|
|
ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab,
|
|
&dp->rxdma_mon_buf_ring,
|
|
HAL_RXDMA_MONITOR_BUF);
|
|
if (ret)
|
|
ath12k_warn(ab,
|
|
"failed to setup HAL_RXDMA_MONITOR_BUF\n");
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
|
|
mon_ring = &dp->rx_mon_status_refill_ring[i];
|
|
ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab, mon_ring,
|
|
HAL_RXDMA_MONITOR_STATUS);
|
|
if (ret) {
|
|
ath12k_warn(ab,
|
|
"failed to setup HAL_RXDMA_MONITOR_STATUS\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ath12k_dp_rx_pdev_srng_free(struct ath12k *ar)
|
|
{
|
|
struct ath12k_pdev_dp *dp = &ar->dp;
|
|
struct ath12k_base *ab = ar->ab;
|
|
int i;
|
|
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++)
|
|
ath12k_dp_srng_cleanup(ab, &dp->rxdma_mon_dst_ring[i]);
|
|
}
|
|
|
|
void ath12k_dp_rx_pdev_reo_cleanup(struct ath12k_base *ab)
|
|
{
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
int i;
|
|
|
|
for (i = 0; i < DP_REO_DST_RING_MAX; i++)
|
|
ath12k_dp_srng_cleanup(ab, &dp->reo_dst_ring[i]);
|
|
}
|
|
|
|
int ath12k_dp_rx_pdev_reo_setup(struct ath12k_base *ab)
|
|
{
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
int ret;
|
|
int i;
|
|
|
|
for (i = 0; i < DP_REO_DST_RING_MAX; i++) {
|
|
ret = ath12k_dp_srng_setup(ab, &dp->reo_dst_ring[i],
|
|
HAL_REO_DST, i, 0,
|
|
DP_REO_DST_RING_SIZE);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup reo_dst_ring\n");
|
|
goto err_reo_cleanup;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_reo_cleanup:
|
|
ath12k_dp_rx_pdev_reo_cleanup(ab);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ath12k_dp_rx_pdev_srng_alloc(struct ath12k *ar)
|
|
{
|
|
struct ath12k_pdev_dp *dp = &ar->dp;
|
|
struct ath12k_base *ab = ar->ab;
|
|
int i;
|
|
int ret;
|
|
u32 mac_id = dp->mac_id;
|
|
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
|
|
ret = ath12k_dp_srng_setup(ar->ab,
|
|
&dp->rxdma_mon_dst_ring[i],
|
|
HAL_RXDMA_MONITOR_DST,
|
|
0, mac_id + i,
|
|
DP_RXDMA_MONITOR_DST_RING_SIZE(ab));
|
|
if (ret) {
|
|
ath12k_warn(ar->ab,
|
|
"failed to setup HAL_RXDMA_MONITOR_DST\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ath12k_dp_init_rx_tid_rxq(struct ath12k_dp_rx_tid_rxq *rx_tid_rxq,
|
|
struct ath12k_dp_rx_tid *rx_tid,
|
|
bool active)
|
|
{
|
|
rx_tid_rxq->tid = rx_tid->tid;
|
|
rx_tid_rxq->active = active;
|
|
rx_tid_rxq->qbuf = rx_tid->qbuf;
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_init_rx_tid_rxq);
|
|
|
|
static void ath12k_dp_rx_tid_cleanup(struct ath12k_base *ab,
|
|
struct ath12k_reoq_buf *tid_qbuf)
|
|
{
|
|
if (tid_qbuf->vaddr) {
|
|
dma_unmap_single(ab->dev, tid_qbuf->paddr_aligned,
|
|
tid_qbuf->size, DMA_BIDIRECTIONAL);
|
|
kfree(tid_qbuf->vaddr);
|
|
tid_qbuf->vaddr = NULL;
|
|
}
|
|
}
|
|
|
|
void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab)
|
|
{
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
struct ath12k_dp_rx_reo_cmd *cmd, *tmp;
|
|
struct ath12k_dp_rx_reo_cache_flush_elem *cmd_cache, *tmp_cache;
|
|
struct dp_reo_update_rx_queue_elem *cmd_queue, *tmp_queue;
|
|
|
|
spin_lock_bh(&dp->reo_rxq_flush_lock);
|
|
list_for_each_entry_safe(cmd_queue, tmp_queue, &dp->reo_cmd_update_rx_queue_list,
|
|
list) {
|
|
list_del(&cmd_queue->list);
|
|
ath12k_dp_rx_tid_cleanup(ab, &cmd_queue->rx_tid.qbuf);
|
|
kfree(cmd_queue);
|
|
}
|
|
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--;
|
|
ath12k_dp_rx_tid_cleanup(ab, &cmd_cache->data.qbuf);
|
|
kfree(cmd_cache);
|
|
}
|
|
spin_unlock_bh(&dp->reo_rxq_flush_lock);
|
|
|
|
spin_lock_bh(&dp->reo_cmd_lock);
|
|
list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) {
|
|
list_del(&cmd->list);
|
|
ath12k_dp_rx_tid_cleanup(ab, &cmd->data.qbuf);
|
|
kfree(cmd);
|
|
}
|
|
spin_unlock_bh(&dp->reo_cmd_lock);
|
|
}
|
|
|
|
void ath12k_dp_reo_cmd_free(struct ath12k_dp *dp, void *ctx,
|
|
enum hal_reo_cmd_status status)
|
|
{
|
|
struct ath12k_dp_rx_tid_rxq *rx_tid = ctx;
|
|
|
|
if (status != HAL_REO_CMD_SUCCESS)
|
|
ath12k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n",
|
|
rx_tid->tid, status);
|
|
|
|
ath12k_dp_rx_tid_cleanup(dp->ab, &rx_tid->qbuf);
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_reo_cmd_free);
|
|
|
|
void ath12k_dp_rx_process_reo_cmd_update_rx_queue_list(struct ath12k_dp *dp)
|
|
{
|
|
struct ath12k_base *ab = dp->ab;
|
|
struct dp_reo_update_rx_queue_elem *elem, *tmp;
|
|
|
|
spin_lock_bh(&dp->reo_rxq_flush_lock);
|
|
|
|
list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_update_rx_queue_list, list) {
|
|
if (elem->rx_tid.active)
|
|
continue;
|
|
|
|
if (ath12k_dp_rx_tid_delete_handler(ab, &elem->rx_tid))
|
|
break;
|
|
|
|
ath12k_dp_arch_peer_rx_tid_qref_reset(dp,
|
|
elem->is_ml_peer ?
|
|
elem->ml_peer_id : elem->peer_id,
|
|
elem->rx_tid.tid);
|
|
|
|
if (ab->hw_params->reoq_lut_support)
|
|
ath12k_hal_reo_shared_qaddr_cache_clear(ab);
|
|
|
|
list_del(&elem->list);
|
|
kfree(elem);
|
|
}
|
|
|
|
spin_unlock_bh(&dp->reo_rxq_flush_lock);
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_process_reo_cmd_update_rx_queue_list);
|
|
|
|
void ath12k_dp_rx_tid_del_func(struct ath12k_dp *dp, void *ctx,
|
|
enum hal_reo_cmd_status status)
|
|
{
|
|
struct ath12k_base *ab = dp->ab;
|
|
struct ath12k_dp_rx_tid_rxq *rx_tid = ctx;
|
|
struct ath12k_dp_rx_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? */
|
|
ath12k_warn(ab, "failed to delete rx tid %d hw descriptor %d\n",
|
|
rx_tid->tid, status);
|
|
return;
|
|
}
|
|
|
|
/* Retry the HAL_REO_CMD_UPDATE_RX_QUEUE command for entries
|
|
* in the pending queue list marked TID as inactive
|
|
*/
|
|
spin_lock_bh(&dp->dp_lock);
|
|
ath12k_dp_rx_process_reo_cmd_update_rx_queue_list(dp);
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
|
|
elem = kzalloc_obj(*elem, GFP_ATOMIC);
|
|
if (!elem)
|
|
goto free_desc;
|
|
|
|
elem->ts = jiffies;
|
|
memcpy(&elem->data, rx_tid, sizeof(*rx_tid));
|
|
|
|
spin_lock_bh(&dp->reo_rxq_flush_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 > ATH12K_DP_RX_REO_DESC_FREE_THRES ||
|
|
time_after(jiffies, elem->ts +
|
|
msecs_to_jiffies(ATH12K_DP_RX_REO_DESC_FREE_TIMEOUT_MS))) {
|
|
/* The reo_cmd_cache_flush_list is used in only two contexts,
|
|
* one is in this function called from napi and the
|
|
* other in ath12k_dp_free during core destroy.
|
|
* If cache command sent is success, delete the element in
|
|
* the cache list. ath12k_dp_rx_reo_cmd_list_cleanup
|
|
* will be called during core destroy.
|
|
*/
|
|
|
|
if (ath12k_dp_arch_reo_cache_flush(dp, &elem->data))
|
|
break;
|
|
|
|
list_del(&elem->list);
|
|
dp->reo_cmd_cache_flush_count--;
|
|
|
|
kfree(elem);
|
|
}
|
|
}
|
|
spin_unlock_bh(&dp->reo_rxq_flush_lock);
|
|
|
|
return;
|
|
free_desc:
|
|
ath12k_dp_rx_tid_cleanup(ab, &rx_tid->qbuf);
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_tid_del_func);
|
|
|
|
static int ath12k_dp_rx_tid_delete_handler(struct ath12k_base *ab,
|
|
struct ath12k_dp_rx_tid_rxq *rx_tid)
|
|
{
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
|
|
return ath12k_dp_arch_rx_tid_delete_handler(dp, rx_tid);
|
|
}
|
|
|
|
void ath12k_dp_mark_tid_as_inactive(struct ath12k_dp *dp, int peer_id, u8 tid)
|
|
{
|
|
struct dp_reo_update_rx_queue_elem *elem;
|
|
struct ath12k_dp_rx_tid_rxq *rx_tid;
|
|
|
|
spin_lock_bh(&dp->reo_rxq_flush_lock);
|
|
list_for_each_entry(elem, &dp->reo_cmd_update_rx_queue_list, list) {
|
|
if (elem->peer_id == peer_id) {
|
|
rx_tid = &elem->rx_tid;
|
|
if (rx_tid->tid == tid) {
|
|
rx_tid->active = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
spin_unlock_bh(&dp->reo_rxq_flush_lock);
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_mark_tid_as_inactive);
|
|
|
|
void ath12k_dp_rx_peer_tid_cleanup(struct ath12k *ar, struct ath12k_dp_link_peer *peer)
|
|
{
|
|
struct ath12k_dp_rx_tid *rx_tid;
|
|
int i;
|
|
struct ath12k_base *ab = ar->ab;
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
|
|
lockdep_assert_held(&dp->dp_lock);
|
|
|
|
if (!peer->primary_link)
|
|
return;
|
|
|
|
for (i = 0; i <= IEEE80211_NUM_TIDS; i++) {
|
|
rx_tid = &peer->dp_peer->rx_tid[i];
|
|
|
|
ath12k_dp_arch_rx_peer_tid_delete(dp, peer, i);
|
|
ath12k_dp_arch_rx_frags_cleanup(dp, rx_tid, true);
|
|
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
timer_delete_sync(&rx_tid->frag_timer);
|
|
spin_lock_bh(&dp->dp_lock);
|
|
}
|
|
}
|
|
|
|
static int ath12k_dp_prepare_reo_update_elem(struct ath12k_dp *dp,
|
|
struct ath12k_dp_link_peer *peer,
|
|
struct ath12k_dp_rx_tid *rx_tid)
|
|
{
|
|
struct dp_reo_update_rx_queue_elem *elem;
|
|
|
|
lockdep_assert_held(&dp->dp_lock);
|
|
|
|
if (!peer->primary_link)
|
|
return 0;
|
|
|
|
elem = kzalloc_obj(*elem, GFP_ATOMIC);
|
|
if (!elem)
|
|
return -ENOMEM;
|
|
|
|
elem->peer_id = peer->peer_id;
|
|
elem->is_ml_peer = peer->mlo;
|
|
elem->ml_peer_id = peer->ml_id;
|
|
|
|
ath12k_dp_init_rx_tid_rxq(&elem->rx_tid, rx_tid,
|
|
(peer->rx_tid_active_bitmask & (1 << rx_tid->tid)));
|
|
|
|
spin_lock_bh(&dp->reo_rxq_flush_lock);
|
|
list_add_tail(&elem->list, &dp->reo_cmd_update_rx_queue_list);
|
|
spin_unlock_bh(&dp->reo_rxq_flush_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id,
|
|
u8 tid, u32 ba_win_sz, u16 ssn,
|
|
enum hal_pn_type pn_type)
|
|
{
|
|
struct ath12k_base *ab = ar->ab;
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
struct ath12k_dp_link_peer *peer;
|
|
struct ath12k_dp_rx_tid *rx_tid;
|
|
dma_addr_t paddr_aligned;
|
|
int ret;
|
|
|
|
spin_lock_bh(&dp->dp_lock);
|
|
|
|
peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, vdev_id, peer_mac);
|
|
if (!peer || !peer->dp_peer) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
ath12k_warn(ab, "failed to find the peer to set up rx tid\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (ab->hw_params->dp_primary_link_only &&
|
|
!peer->primary_link) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
return 0;
|
|
}
|
|
|
|
if (ab->hw_params->reoq_lut_support &&
|
|
(!dp->reoq_lut.vaddr || !dp->ml_reoq_lut.vaddr)) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
ath12k_warn(ab, "reo qref table is not setup\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (peer->peer_id > DP_MAX_PEER_ID || tid > IEEE80211_NUM_TIDS) {
|
|
ath12k_warn(ab, "peer id of peer %d or tid %d doesn't allow reoq setup\n",
|
|
peer->peer_id, tid);
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rx_tid = &peer->dp_peer->rx_tid[tid];
|
|
/* Update the tid queue if it is already setup */
|
|
if (peer->rx_tid_active_bitmask & (1 << tid)) {
|
|
ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, rx_tid,
|
|
ba_win_sz, ssn, true);
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to update reo for rx tid %d\n", tid);
|
|
return ret;
|
|
}
|
|
|
|
if (!ab->hw_params->reoq_lut_support) {
|
|
paddr_aligned = rx_tid->qbuf.paddr_aligned;
|
|
ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id,
|
|
peer_mac,
|
|
paddr_aligned, tid,
|
|
1, ba_win_sz);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup peer rx reorder queuefor tid %d: %d\n",
|
|
tid, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
rx_tid->tid = tid;
|
|
|
|
rx_tid->ba_win_sz = ba_win_sz;
|
|
|
|
ret = ath12k_dp_arch_rx_assign_reoq(dp, peer->dp_peer, rx_tid, ssn, pn_type);
|
|
if (ret) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
ath12k_warn(ab, "failed to assign reoq buf for rx tid %u\n", tid);
|
|
return ret;
|
|
}
|
|
|
|
peer->rx_tid_active_bitmask |= (1 << tid);
|
|
|
|
/* Pre-allocate the update_rxq_list for the corresponding tid
|
|
* This will be used during the tid delete. The reason we are not
|
|
* allocating during tid delete is that, if any alloc fail in update_rxq_list
|
|
* we may not be able to delete the tid vaddr/paddr and may lead to leak
|
|
*/
|
|
ret = ath12k_dp_prepare_reo_update_elem(dp, peer, rx_tid);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to alloc update_rxq_list for rx tid %u\n", tid);
|
|
ath12k_dp_rx_tid_cleanup(ab, &rx_tid->qbuf);
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
return ret;
|
|
}
|
|
|
|
paddr_aligned = rx_tid->qbuf.paddr_aligned;
|
|
if (ab->hw_params->reoq_lut_support) {
|
|
/* Update the REO queue LUT at the corresponding peer id
|
|
* and tid with qaddr.
|
|
*/
|
|
if (peer->mlo)
|
|
ath12k_dp_arch_peer_rx_tid_qref_setup(dp, peer->ml_id, tid,
|
|
paddr_aligned);
|
|
else
|
|
ath12k_dp_arch_peer_rx_tid_qref_setup(dp, peer->peer_id, tid,
|
|
paddr_aligned);
|
|
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
} else {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac,
|
|
paddr_aligned, tid, 1,
|
|
ba_win_sz);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ath12k_dp_rx_ampdu_start(struct ath12k *ar,
|
|
struct ieee80211_ampdu_params *params,
|
|
u8 link_id)
|
|
{
|
|
struct ath12k_base *ab = ar->ab;
|
|
struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(params->sta);
|
|
struct ath12k_link_sta *arsta;
|
|
int vdev_id;
|
|
int ret;
|
|
|
|
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
|
|
|
|
arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy,
|
|
ahsta->link[link_id]);
|
|
if (!arsta)
|
|
return -ENOLINK;
|
|
|
|
vdev_id = arsta->arvif->vdev_id;
|
|
|
|
ret = ath12k_dp_rx_peer_tid_setup(ar, arsta->addr, vdev_id,
|
|
params->tid, params->buf_size,
|
|
params->ssn, arsta->ahsta->pn_type);
|
|
if (ret)
|
|
ath12k_warn(ab, "failed to setup rx tid %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ath12k_dp_rx_ampdu_stop(struct ath12k *ar,
|
|
struct ieee80211_ampdu_params *params,
|
|
u8 link_id)
|
|
{
|
|
struct ath12k_base *ab = ar->ab;
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
struct ath12k_dp_link_peer *peer;
|
|
struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(params->sta);
|
|
struct ath12k_dp_rx_tid *rx_tid;
|
|
struct ath12k_link_sta *arsta;
|
|
int vdev_id;
|
|
bool active;
|
|
int ret;
|
|
|
|
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
|
|
|
|
arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy,
|
|
ahsta->link[link_id]);
|
|
if (!arsta)
|
|
return -ENOLINK;
|
|
|
|
vdev_id = arsta->arvif->vdev_id;
|
|
|
|
spin_lock_bh(&dp->dp_lock);
|
|
|
|
peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, vdev_id, arsta->addr);
|
|
if (!peer || !peer->dp_peer) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
ath12k_warn(ab, "failed to find the peer to stop rx aggregation\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (ab->hw_params->dp_primary_link_only &&
|
|
!peer->primary_link) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
return 0;
|
|
}
|
|
|
|
active = peer->rx_tid_active_bitmask & (1 << params->tid);
|
|
if (!active) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
return 0;
|
|
}
|
|
|
|
rx_tid = &peer->dp_peer->rx_tid[params->tid];
|
|
ret = ath12k_dp_arch_peer_rx_tid_reo_update(dp, peer, rx_tid,
|
|
1, 0, false);
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to update reo for rx tid %d: %d\n",
|
|
params->tid, ret);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_link_vif *arvif,
|
|
const u8 *peer_addr,
|
|
enum set_key_cmd key_cmd,
|
|
struct ieee80211_key_conf *key)
|
|
{
|
|
struct ath12k *ar = arvif->ar;
|
|
struct ath12k_base *ab = ar->ab;
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
struct ath12k_hal_reo_cmd cmd = {};
|
|
struct ath12k_dp_link_peer *peer;
|
|
struct ath12k_dp_rx_tid *rx_tid;
|
|
struct ath12k_dp_rx_tid_rxq rx_tid_rxq;
|
|
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;
|
|
|
|
spin_lock_bh(&dp->dp_lock);
|
|
|
|
peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arvif->vdev_id,
|
|
peer_addr);
|
|
if (!peer || !peer->dp_peer) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
ath12k_warn(ab, "failed to find the peer %pM to configure pn replay detection\n",
|
|
peer_addr);
|
|
return -ENOENT;
|
|
}
|
|
|
|
for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) {
|
|
if (!(peer->rx_tid_active_bitmask & (1 << tid)))
|
|
continue;
|
|
|
|
rx_tid = &peer->dp_peer->rx_tid[tid];
|
|
ath12k_dp_init_rx_tid_rxq(&rx_tid_rxq, rx_tid,
|
|
(peer->rx_tid_active_bitmask & (1 << tid)));
|
|
ath12k_dp_arch_setup_pn_check_reo_cmd(dp, &cmd, rx_tid, key->cipher,
|
|
key_cmd);
|
|
ret = ath12k_dp_arch_reo_cmd_send(dp, &rx_tid_rxq,
|
|
HAL_REO_CMD_UPDATE_RX_QUEUE,
|
|
&cmd, NULL);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to configure rx tid %d queue of peer %pM for pn replay detection %d\n",
|
|
tid, peer_addr, ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_get_msdu_last_buf);
|
|
|
|
struct sk_buff *ath12k_dp_rx_get_msdu_last_buf(struct sk_buff_head *msdu_list,
|
|
struct sk_buff *first)
|
|
{
|
|
struct sk_buff *skb;
|
|
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(first);
|
|
|
|
if (!rxcb->is_continuation)
|
|
return first;
|
|
|
|
skb_queue_walk(msdu_list, skb) {
|
|
rxcb = ATH12K_SKB_RXCB(skb);
|
|
if (!rxcb->is_continuation)
|
|
return skb;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int ath12k_dp_rx_crypto_mic_len(struct ath12k_dp *dp, 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;
|
|
}
|
|
|
|
ath12k_warn(dp->ab, "unsupported encryption type %d for mic len\n", enctype);
|
|
return 0;
|
|
}
|
|
|
|
static int ath12k_dp_rx_crypto_param_len(struct ath12k_pdev_dp *dp_pdev,
|
|
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;
|
|
}
|
|
|
|
ath12k_warn(dp_pdev->dp->ab, "unsupported encryption type %d\n", enctype);
|
|
return 0;
|
|
}
|
|
|
|
static int ath12k_dp_rx_crypto_icv_len(struct ath12k_pdev_dp *dp_pdev,
|
|
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;
|
|
}
|
|
|
|
ath12k_warn(dp_pdev->dp->ab, "unsupported encryption type %d\n", enctype);
|
|
return 0;
|
|
}
|
|
|
|
static void ath12k_dp_rx_h_undecap_nwifi(struct ath12k_pdev_dp *dp_pdev,
|
|
struct sk_buff *msdu,
|
|
enum hal_encrypt_type enctype,
|
|
struct hal_rx_desc_data *rx_info)
|
|
{
|
|
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
|
|
u8 decap_hdr[DP_MAX_NWIFI_HDR_LEN];
|
|
struct ieee80211_hdr *hdr;
|
|
size_t hdr_len;
|
|
u8 *crypto_hdr;
|
|
u16 qos_ctl;
|
|
|
|
/* pull decapped header */
|
|
hdr = (struct ieee80211_hdr *)msdu->data;
|
|
hdr_len = ieee80211_hdrlen(hdr->frame_control);
|
|
skb_pull(msdu, hdr_len);
|
|
|
|
/* Rebuild qos header */
|
|
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 (rx_info->mesh_ctrl_present)
|
|
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, hdr, hdr_len);
|
|
|
|
/* Rebuild crypto header for mac80211 use */
|
|
if (!(rx_info->rx_status->flag & RX_FLAG_IV_STRIPPED)) {
|
|
crypto_hdr = skb_push(msdu,
|
|
ath12k_dp_rx_crypto_param_len(dp_pdev, enctype));
|
|
ath12k_dp_rx_desc_get_crypto_header(dp_pdev->dp->hal,
|
|
rxcb->rx_desc, crypto_hdr,
|
|
enctype);
|
|
}
|
|
|
|
memcpy(skb_push(msdu,
|
|
IEEE80211_QOS_CTL_LEN), &qos_ctl,
|
|
IEEE80211_QOS_CTL_LEN);
|
|
memcpy(skb_push(msdu, hdr_len), decap_hdr, hdr_len);
|
|
}
|
|
|
|
static void ath12k_dp_rx_h_undecap_raw(struct ath12k_pdev_dp *dp_pdev,
|
|
struct sk_buff *msdu,
|
|
enum hal_encrypt_type enctype,
|
|
struct ieee80211_rx_status *status,
|
|
bool decrypted)
|
|
{
|
|
struct ath12k_dp *dp = dp_pdev->dp;
|
|
struct ath12k_skb_rxcb *rxcb = ATH12K_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 -
|
|
ath12k_dp_rx_crypto_mic_len(dp, enctype));
|
|
|
|
skb_trim(msdu, msdu->len -
|
|
ath12k_dp_rx_crypto_icv_len(dp_pdev, enctype));
|
|
} else {
|
|
/* MIC */
|
|
if (status->flag & RX_FLAG_MIC_STRIPPED)
|
|
skb_trim(msdu, msdu->len -
|
|
ath12k_dp_rx_crypto_mic_len(dp, enctype));
|
|
|
|
/* ICV */
|
|
if (status->flag & RX_FLAG_ICV_STRIPPED)
|
|
skb_trim(msdu, msdu->len -
|
|
ath12k_dp_rx_crypto_icv_len(dp_pdev, 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 = ath12k_dp_rx_crypto_param_len(dp_pdev, enctype);
|
|
|
|
memmove(msdu->data + crypto_len, msdu->data, hdr_len);
|
|
skb_pull(msdu, crypto_len);
|
|
}
|
|
}
|
|
|
|
static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k_pdev_dp *dp_pdev,
|
|
struct sk_buff *msdu,
|
|
struct ath12k_skb_rxcb *rxcb,
|
|
enum hal_encrypt_type enctype,
|
|
struct hal_rx_desc_data *rx_info)
|
|
{
|
|
struct hal_rx_desc *rx_desc = rxcb->rx_desc;
|
|
struct ath12k_dp *dp = dp_pdev->dp;
|
|
struct ath12k_hal *hal = dp->hal;
|
|
size_t hdr_len, crypto_len;
|
|
struct ieee80211_hdr hdr;
|
|
__le16 qos_ctl;
|
|
u8 *crypto_hdr;
|
|
|
|
ath12k_dp_rx_desc_get_dot11_hdr(hal, rx_desc, &hdr);
|
|
hdr_len = ieee80211_hdrlen(hdr.frame_control);
|
|
|
|
if (!(rx_info->rx_status->flag & RX_FLAG_IV_STRIPPED)) {
|
|
crypto_len = ath12k_dp_rx_crypto_param_len(dp_pdev, enctype);
|
|
crypto_hdr = skb_push(msdu, crypto_len);
|
|
ath12k_dp_rx_desc_get_crypto_header(dp->hal, rx_desc, crypto_hdr,
|
|
enctype);
|
|
}
|
|
|
|
skb_push(msdu, hdr_len);
|
|
memcpy(msdu->data, &hdr, min(hdr_len, sizeof(hdr)));
|
|
|
|
if (rxcb->is_mcbc)
|
|
rx_info->rx_status->flag &= ~RX_FLAG_PN_VALIDATED;
|
|
|
|
/* Add QOS header */
|
|
if (ieee80211_is_data_qos(hdr.frame_control)) {
|
|
struct ieee80211_hdr *qos_ptr = (struct ieee80211_hdr *)msdu->data;
|
|
|
|
qos_ctl = cpu_to_le16(rxcb->tid & IEEE80211_QOS_CTL_TID_MASK);
|
|
if (rx_info->mesh_ctrl_present)
|
|
qos_ctl |= cpu_to_le16(IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT);
|
|
|
|
memcpy(ieee80211_get_qos_ctl(qos_ptr), &qos_ctl, IEEE80211_QOS_CTL_LEN);
|
|
}
|
|
}
|
|
|
|
static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev,
|
|
struct sk_buff *msdu,
|
|
enum hal_encrypt_type enctype,
|
|
struct hal_rx_desc_data *rx_info)
|
|
{
|
|
struct ieee80211_hdr *hdr;
|
|
struct ethhdr *eth;
|
|
u8 da[ETH_ALEN];
|
|
u8 sa[ETH_ALEN];
|
|
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
|
|
struct ath12k_dp_rx_rfc1042_hdr rfc = {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}};
|
|
|
|
eth = (struct ethhdr *)msdu->data;
|
|
ether_addr_copy(da, eth->h_dest);
|
|
ether_addr_copy(sa, eth->h_source);
|
|
rfc.snap_type = eth->h_proto;
|
|
skb_pull(msdu, sizeof(*eth));
|
|
memcpy(skb_push(msdu, sizeof(rfc)), &rfc,
|
|
sizeof(rfc));
|
|
ath12k_get_dot11_hdr_from_rx_desc(dp_pdev, msdu, rxcb, enctype, rx_info);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu,
|
|
enum hal_encrypt_type enctype,
|
|
bool decrypted,
|
|
struct hal_rx_desc_data *rx_info)
|
|
{
|
|
struct ethhdr *ehdr;
|
|
|
|
switch (rx_info->decap_type) {
|
|
case DP_RX_DECAP_TYPE_NATIVE_WIFI:
|
|
ath12k_dp_rx_h_undecap_nwifi(dp_pdev, msdu, enctype, rx_info);
|
|
break;
|
|
case DP_RX_DECAP_TYPE_RAW:
|
|
ath12k_dp_rx_h_undecap_raw(dp_pdev, msdu, enctype, rx_info->rx_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)) {
|
|
ATH12K_SKB_RXCB(msdu)->is_eapol = true;
|
|
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info);
|
|
break;
|
|
}
|
|
|
|
/* PN for mcast packets will be validated in mac80211;
|
|
* remove eth header and add 802.11 header.
|
|
*/
|
|
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted)
|
|
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info);
|
|
break;
|
|
case DP_RX_DECAP_TYPE_8023:
|
|
/* TODO: Handle undecap for these formats */
|
|
break;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_h_undecap);
|
|
|
|
struct ath12k_dp_link_peer *
|
|
ath12k_dp_rx_h_find_link_peer(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu,
|
|
struct hal_rx_desc_data *rx_info)
|
|
{
|
|
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
|
|
struct ath12k_dp_link_peer *peer = NULL;
|
|
struct ath12k_dp *dp = dp_pdev->dp;
|
|
|
|
lockdep_assert_held(&dp->dp_lock);
|
|
|
|
if (rxcb->peer_id)
|
|
peer = ath12k_dp_link_peer_find_by_peerid(dp_pdev, rxcb->peer_id);
|
|
|
|
if (peer)
|
|
return peer;
|
|
|
|
if (rx_info->addr2_present)
|
|
peer = ath12k_dp_link_peer_find_by_addr(dp, rx_info->addr2);
|
|
|
|
return peer;
|
|
}
|
|
|
|
static void ath12k_dp_rx_h_rate(struct ath12k_pdev_dp *dp_pdev,
|
|
struct hal_rx_desc_data *rx_info)
|
|
{
|
|
struct ath12k_dp *dp = dp_pdev->dp;
|
|
struct ieee80211_supported_band *sband;
|
|
struct ieee80211_rx_status *rx_status = rx_info->rx_status;
|
|
enum rx_msdu_start_pkt_type pkt_type = rx_info->pkt_type;
|
|
u8 bw = rx_info->bw, sgi = rx_info->sgi;
|
|
u8 rate_mcs = rx_info->rate_mcs, nss = rx_info->nss;
|
|
bool is_cck;
|
|
struct ath12k *ar;
|
|
|
|
switch (pkt_type) {
|
|
case RX_MSDU_START_PKT_TYPE_11A:
|
|
case RX_MSDU_START_PKT_TYPE_11B:
|
|
ar = ath12k_pdev_dp_to_ar(dp_pdev);
|
|
is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B);
|
|
sband = &ar->mac.sbands[rx_status->band];
|
|
rx_status->rate_idx = ath12k_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 > ATH12K_HT_MCS_MAX) {
|
|
ath12k_warn(dp->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 = ath12k_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 > ATH12K_VHT_MCS_MAX) {
|
|
ath12k_warn(dp->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;
|
|
rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw);
|
|
break;
|
|
case RX_MSDU_START_PKT_TYPE_11AX:
|
|
rx_status->rate_idx = rate_mcs;
|
|
if (rate_mcs > ATH12K_HE_MCS_MAX) {
|
|
ath12k_warn(dp->ab,
|
|
"Received with invalid mcs in HE mode %d\n",
|
|
rate_mcs);
|
|
break;
|
|
}
|
|
rx_status->encoding = RX_ENC_HE;
|
|
rx_status->nss = nss;
|
|
rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi);
|
|
rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw);
|
|
break;
|
|
case RX_MSDU_START_PKT_TYPE_11BE:
|
|
rx_status->rate_idx = rate_mcs;
|
|
|
|
if (rate_mcs > ATH12K_EHT_MCS_MAX) {
|
|
ath12k_warn(dp->ab,
|
|
"Received with invalid mcs in EHT mode %d\n",
|
|
rate_mcs);
|
|
break;
|
|
}
|
|
|
|
rx_status->encoding = RX_ENC_EHT;
|
|
rx_status->nss = nss;
|
|
rx_status->eht.gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(sgi);
|
|
rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ath12k_dp_rx_h_ppdu(struct ath12k_pdev_dp *dp_pdev,
|
|
struct hal_rx_desc_data *rx_info)
|
|
{
|
|
struct ieee80211_rx_status *rx_status = rx_info->rx_status;
|
|
u8 channel_num;
|
|
u32 center_freq, meta_data;
|
|
struct ieee80211_channel *channel;
|
|
|
|
rx_status->freq = 0;
|
|
rx_status->rate_idx = 0;
|
|
rx_status->nss = 0;
|
|
rx_status->encoding = RX_ENC_LEGACY;
|
|
rx_status->bw = RATE_INFO_BW_20;
|
|
rx_status->enc_flags = 0;
|
|
|
|
rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
|
|
|
|
meta_data = rx_info->phy_meta_data;
|
|
channel_num = meta_data;
|
|
center_freq = meta_data >> 16;
|
|
|
|
rx_status->band = NUM_NL80211_BANDS;
|
|
|
|
if (center_freq >= ATH12K_MIN_6GHZ_FREQ &&
|
|
center_freq <= ATH12K_MAX_6GHZ_FREQ) {
|
|
rx_status->band = NL80211_BAND_6GHZ;
|
|
rx_status->freq = center_freq;
|
|
} else if (channel_num >= 1 && channel_num <= 14) {
|
|
rx_status->band = NL80211_BAND_2GHZ;
|
|
} else if (channel_num >= 36 && channel_num <= 173) {
|
|
rx_status->band = NL80211_BAND_5GHZ;
|
|
}
|
|
|
|
if (unlikely(rx_status->band == NUM_NL80211_BANDS ||
|
|
!ath12k_pdev_dp_to_hw(dp_pdev)->wiphy->bands[rx_status->band])) {
|
|
struct ath12k *ar = ath12k_pdev_dp_to_ar(dp_pdev);
|
|
|
|
ath12k_warn(ar->ab, "sband is NULL for status band %d channel_num %d center_freq %d pdev_id %d\n",
|
|
rx_status->band, channel_num, center_freq, ar->pdev_idx);
|
|
|
|
spin_lock_bh(&ar->data_lock);
|
|
channel = ar->rx_channel;
|
|
if (channel) {
|
|
rx_status->band = channel->band;
|
|
channel_num =
|
|
ieee80211_frequency_to_channel(channel->center_freq);
|
|
rx_status->freq = ieee80211_channel_to_frequency(channel_num,
|
|
rx_status->band);
|
|
} else {
|
|
ath12k_err(ar->ab, "unable to determine channel, band for rx packet");
|
|
}
|
|
spin_unlock_bh(&ar->data_lock);
|
|
goto h_rate;
|
|
}
|
|
|
|
if (rx_status->band != NL80211_BAND_6GHZ)
|
|
rx_status->freq = ieee80211_channel_to_frequency(channel_num,
|
|
rx_status->band);
|
|
|
|
h_rate:
|
|
ath12k_dp_rx_h_rate(dp_pdev, rx_info);
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_h_ppdu);
|
|
|
|
void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struct *napi,
|
|
struct sk_buff *msdu,
|
|
struct hal_rx_desc_data *rx_info)
|
|
{
|
|
struct ath12k_dp *dp = dp_pdev->dp;
|
|
struct ieee80211_rx_status *rx_status;
|
|
struct ieee80211_sta *pubsta;
|
|
struct ath12k_dp_peer *peer;
|
|
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
|
|
struct ieee80211_rx_status *status = rx_info->rx_status;
|
|
u8 decap = rx_info->decap_type;
|
|
bool is_mcbc = rxcb->is_mcbc;
|
|
bool is_eapol = rxcb->is_eapol;
|
|
|
|
peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id);
|
|
|
|
pubsta = peer ? peer->sta : NULL;
|
|
|
|
if (pubsta && pubsta->valid_links) {
|
|
status->link_valid = 1;
|
|
status->link_id = peer->hw_links[rxcb->hw_link_id];
|
|
}
|
|
|
|
ath12k_dbg(dp->ab, ATH12K_DBG_DATA,
|
|
"rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
|
|
msdu,
|
|
msdu->len,
|
|
peer ? peer->addr : NULL,
|
|
rxcb->tid,
|
|
is_mcbc ? "mcast" : "ucast",
|
|
rx_info->seq_no,
|
|
(status->encoding == RX_ENC_LEGACY) ? "legacy" : "",
|
|
(status->encoding == RX_ENC_HT) ? "ht" : "",
|
|
(status->encoding == RX_ENC_VHT) ? "vht" : "",
|
|
(status->encoding == RX_ENC_HE) ? "he" : "",
|
|
(status->encoding == RX_ENC_EHT) ? "eht" : "",
|
|
(status->bw == RATE_INFO_BW_40) ? "40" : "",
|
|
(status->bw == RATE_INFO_BW_80) ? "80" : "",
|
|
(status->bw == RATE_INFO_BW_160) ? "160" : "",
|
|
(status->bw == RATE_INFO_BW_320) ? "320" : "",
|
|
status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "",
|
|
status->rate_idx,
|
|
status->nss,
|
|
status->freq,
|
|
status->band, status->flag,
|
|
!!(status->flag & RX_FLAG_FAILED_FCS_CRC),
|
|
!!(status->flag & RX_FLAG_MMIC_ERROR),
|
|
!!(status->flag & RX_FLAG_AMSDU_MORE));
|
|
|
|
ath12k_dbg_dump(dp->ab, ATH12K_DBG_DP_RX, NULL, "dp rx msdu: ",
|
|
msdu->data, msdu->len);
|
|
|
|
rx_status = IEEE80211_SKB_RXCB(msdu);
|
|
*rx_status = *status;
|
|
|
|
/* TODO: trace rx packet */
|
|
|
|
/* PN for multicast packets are not validate in HW,
|
|
* so skip 802.3 rx path
|
|
* Also, fast_rx expects the STA to be authorized, hence
|
|
* eapol packets are sent in slow path.
|
|
*/
|
|
if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol &&
|
|
!(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
|
|
rx_status->flag |= RX_FLAG_8023;
|
|
|
|
ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);
|
|
|
|
bool ath12k_dp_rx_check_nwifi_hdr_len_valid(struct ath12k_dp *dp,
|
|
struct sk_buff *msdu,
|
|
struct hal_rx_desc_data *rx_info)
|
|
{
|
|
struct ieee80211_hdr *hdr;
|
|
u32 hdr_len;
|
|
|
|
if (rx_info->decap_type != DP_RX_DECAP_TYPE_NATIVE_WIFI)
|
|
return true;
|
|
|
|
hdr = (struct ieee80211_hdr *)msdu->data;
|
|
hdr_len = ieee80211_hdrlen(hdr->frame_control);
|
|
|
|
if ((likely(hdr_len <= DP_MAX_NWIFI_HDR_LEN)))
|
|
return true;
|
|
|
|
dp->device_stats.invalid_rbm++;
|
|
WARN_ON_ONCE(1);
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_check_nwifi_hdr_len_valid);
|
|
|
|
static void ath12k_dp_rx_frag_timer(struct timer_list *timer)
|
|
{
|
|
struct ath12k_dp_rx_tid *rx_tid = timer_container_of(rx_tid, timer,
|
|
frag_timer);
|
|
|
|
spin_lock_bh(&rx_tid->dp->dp_lock);
|
|
if (rx_tid->last_frag_no &&
|
|
rx_tid->rx_frag_bitmap == GENMASK(rx_tid->last_frag_no, 0)) {
|
|
spin_unlock_bh(&rx_tid->dp->dp_lock);
|
|
return;
|
|
}
|
|
ath12k_dp_arch_rx_frags_cleanup(rx_tid->dp, rx_tid, true);
|
|
spin_unlock_bh(&rx_tid->dp->dp_lock);
|
|
}
|
|
|
|
int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id)
|
|
{
|
|
struct ath12k_base *ab = ar->ab;
|
|
struct ath12k_dp_link_peer *peer;
|
|
struct ath12k_dp_rx_tid *rx_tid;
|
|
int i;
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
|
|
if (fips_enabled) {
|
|
ath12k_warn(ab, "This driver is disabled due to FIPS\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
spin_lock_bh(&dp->dp_lock);
|
|
|
|
peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, vdev_id, peer_mac);
|
|
if (!peer || !peer->dp_peer) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
ath12k_warn(ab, "failed to find the peer to set up fragment info\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (!peer->primary_link) {
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i <= IEEE80211_NUM_TIDS; i++) {
|
|
rx_tid = &peer->dp_peer->rx_tid[i];
|
|
rx_tid->dp = dp;
|
|
timer_setup(&rx_tid->frag_timer, ath12k_dp_rx_frag_timer, 0);
|
|
skb_queue_head_init(&rx_tid->rx_frags);
|
|
}
|
|
|
|
peer->dp_peer->dp_setup_done = true;
|
|
spin_unlock_bh(&dp->dp_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ath12k_dp_rx_h_undecap_frag(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu,
|
|
enum hal_encrypt_type enctype, u32 flags)
|
|
{
|
|
struct ath12k_dp *dp = dp_pdev->dp;
|
|
struct ieee80211_hdr *hdr;
|
|
size_t hdr_len;
|
|
size_t crypto_len;
|
|
u32 hal_rx_desc_sz = dp->ab->hal.hal_desc_sz;
|
|
|
|
if (!flags)
|
|
return;
|
|
|
|
hdr = (struct ieee80211_hdr *)(msdu->data + hal_rx_desc_sz);
|
|
|
|
if (flags & RX_FLAG_MIC_STRIPPED)
|
|
skb_trim(msdu, msdu->len -
|
|
ath12k_dp_rx_crypto_mic_len(dp, enctype));
|
|
|
|
if (flags & RX_FLAG_ICV_STRIPPED)
|
|
skb_trim(msdu, msdu->len -
|
|
ath12k_dp_rx_crypto_icv_len(dp_pdev, enctype));
|
|
|
|
if (flags & RX_FLAG_IV_STRIPPED) {
|
|
hdr_len = ieee80211_hdrlen(hdr->frame_control);
|
|
crypto_len = ath12k_dp_rx_crypto_param_len(dp_pdev, enctype);
|
|
|
|
memmove(msdu->data + hal_rx_desc_sz + crypto_len,
|
|
msdu->data + hal_rx_desc_sz, hdr_len);
|
|
skb_pull(msdu, crypto_len);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_h_undecap_frag);
|
|
|
|
static int ath12k_dp_rx_h_cmp_frags(struct ath12k_hal *hal,
|
|
struct sk_buff *a, struct sk_buff *b)
|
|
{
|
|
int frag1, frag2;
|
|
|
|
frag1 = ath12k_dp_rx_h_frag_no(hal, a);
|
|
frag2 = ath12k_dp_rx_h_frag_no(hal, b);
|
|
|
|
return frag1 - frag2;
|
|
}
|
|
|
|
void ath12k_dp_rx_h_sort_frags(struct ath12k_hal *hal,
|
|
struct sk_buff_head *frag_list,
|
|
struct sk_buff *cur_frag)
|
|
{
|
|
struct sk_buff *skb;
|
|
int cmp;
|
|
|
|
skb_queue_walk(frag_list, skb) {
|
|
cmp = ath12k_dp_rx_h_cmp_frags(hal, skb, cur_frag);
|
|
if (cmp < 0)
|
|
continue;
|
|
__skb_queue_before(frag_list, skb, cur_frag);
|
|
return;
|
|
}
|
|
__skb_queue_tail(frag_list, cur_frag);
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_h_sort_frags);
|
|
|
|
u64 ath12k_dp_rx_h_get_pn(struct ath12k_dp *dp, struct sk_buff *skb)
|
|
{
|
|
struct ieee80211_hdr *hdr;
|
|
u64 pn = 0;
|
|
u8 *ehdr;
|
|
u32 hal_rx_desc_sz = dp->ab->hal.hal_desc_sz;
|
|
|
|
hdr = (struct ieee80211_hdr *)(skb->data + hal_rx_desc_sz);
|
|
ehdr = skb->data + hal_rx_desc_sz + ieee80211_hdrlen(hdr->frame_control);
|
|
|
|
pn = ehdr[0];
|
|
pn |= (u64)ehdr[1] << 8;
|
|
pn |= (u64)ehdr[4] << 16;
|
|
pn |= (u64)ehdr[5] << 24;
|
|
pn |= (u64)ehdr[6] << 32;
|
|
pn |= (u64)ehdr[7] << 40;
|
|
|
|
return pn;
|
|
}
|
|
EXPORT_SYMBOL(ath12k_dp_rx_h_get_pn);
|
|
|
|
void ath12k_dp_rx_free(struct ath12k_base *ab)
|
|
{
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
struct dp_srng *srng;
|
|
int i;
|
|
|
|
ath12k_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)
|
|
ath12k_dp_srng_cleanup(ab, &dp->rx_mac_buf_ring[i]);
|
|
if (!ab->hw_params->rxdma1_enable) {
|
|
srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring;
|
|
ath12k_dp_srng_cleanup(ab, srng);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ab->hw_params->num_rxdma_dst_ring; i++)
|
|
ath12k_dp_srng_cleanup(ab, &dp->rxdma_err_dst_ring[i]);
|
|
|
|
ath12k_dp_srng_cleanup(ab, &dp->rxdma_mon_buf_ring.refill_buf_ring);
|
|
|
|
ath12k_dp_rxdma_buf_free(ab);
|
|
}
|
|
|
|
void ath12k_dp_rx_pdev_free(struct ath12k_base *ab, int mac_id)
|
|
{
|
|
struct ath12k *ar = ab->pdevs[mac_id].ar;
|
|
|
|
ath12k_dp_rx_pdev_srng_free(ar);
|
|
}
|
|
|
|
int ath12k_dp_rx_htt_setup(struct ath12k_base *ab)
|
|
{
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
u32 ring_id;
|
|
int i, ret;
|
|
|
|
/* TODO: Need to verify the HTT setup for QCN9224 */
|
|
ring_id = dp->rx_refill_buf_ring.refill_buf_ring.ring_id;
|
|
ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id, 0, HAL_RXDMA_BUF);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to configure rx_refill_buf_ring %d\n",
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
if (ab->hw_params->rx_mac_buf_ring) {
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
|
|
ring_id = dp->rx_mac_buf_ring[i].ring_id;
|
|
ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id,
|
|
i, HAL_RXDMA_BUF);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to configure rx_mac_buf_ring%d %d\n",
|
|
i, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ab->hw_params->num_rxdma_dst_ring; i++) {
|
|
ring_id = dp->rxdma_err_dst_ring[i].ring_id;
|
|
ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id,
|
|
i, HAL_RXDMA_DST);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to configure rxdma_err_dest_ring%d %d\n",
|
|
i, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (ab->hw_params->rxdma1_enable) {
|
|
ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id;
|
|
ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id,
|
|
0, HAL_RXDMA_MONITOR_BUF);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to configure rxdma_mon_buf_ring %d\n",
|
|
ret);
|
|
return ret;
|
|
}
|
|
} else {
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
|
|
ring_id =
|
|
dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;
|
|
ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id, i,
|
|
HAL_RXDMA_MONITOR_STATUS);
|
|
if (ret) {
|
|
ath12k_warn(ab,
|
|
"failed to configure mon_status_refill_ring%d %d\n",
|
|
i, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = ab->hw_params->hw_ops->rxdma_ring_sel_config(ab);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup rxdma ring selection config\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ath12k_dp_rx_alloc(struct ath12k_base *ab)
|
|
{
|
|
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
|
|
struct dp_srng *srng;
|
|
int i, ret;
|
|
|
|
idr_init(&dp->rxdma_mon_buf_ring.bufs_idr);
|
|
spin_lock_init(&dp->rxdma_mon_buf_ring.idr_lock);
|
|
|
|
ret = ath12k_dp_srng_setup(ab,
|
|
&dp->rx_refill_buf_ring.refill_buf_ring,
|
|
HAL_RXDMA_BUF, 0, 0,
|
|
DP_RXDMA_BUF_RING_SIZE);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup rx_refill_buf_ring\n");
|
|
return ret;
|
|
}
|
|
|
|
if (ab->hw_params->rx_mac_buf_ring) {
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
|
|
ret = ath12k_dp_srng_setup(ab,
|
|
&dp->rx_mac_buf_ring[i],
|
|
HAL_RXDMA_BUF, 1,
|
|
i, DP_RX_MAC_BUF_RING_SIZE);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup rx_mac_buf_ring %d\n",
|
|
i);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < ab->hw_params->num_rxdma_dst_ring; i++) {
|
|
ret = ath12k_dp_srng_setup(ab, &dp->rxdma_err_dst_ring[i],
|
|
HAL_RXDMA_DST, 0, i,
|
|
DP_RXDMA_ERR_DST_RING_SIZE);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup rxdma_err_dst_ring %d\n", i);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (ab->hw_params->rxdma1_enable) {
|
|
ret = ath12k_dp_srng_setup(ab,
|
|
&dp->rxdma_mon_buf_ring.refill_buf_ring,
|
|
HAL_RXDMA_MONITOR_BUF, 0, 0,
|
|
DP_RXDMA_MONITOR_BUF_RING_SIZE(ab));
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup HAL_RXDMA_MONITOR_BUF\n");
|
|
return ret;
|
|
}
|
|
} else {
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
|
|
idr_init(&dp->rx_mon_status_refill_ring[i].bufs_idr);
|
|
spin_lock_init(&dp->rx_mon_status_refill_ring[i].idr_lock);
|
|
}
|
|
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
|
|
srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring;
|
|
ret = ath12k_dp_srng_setup(ab, srng,
|
|
HAL_RXDMA_MONITOR_STATUS, 0, i,
|
|
DP_RXDMA_MON_STATUS_RING_SIZE);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup mon status ring %d\n",
|
|
i);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = ath12k_dp_rxdma_buf_setup(ab);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup rxdma ring\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ath12k_dp_rx_pdev_alloc(struct ath12k_base *ab, int mac_id)
|
|
{
|
|
struct ath12k *ar = ab->pdevs[mac_id].ar;
|
|
struct ath12k_pdev_dp *dp = &ar->dp;
|
|
u32 ring_id;
|
|
int i;
|
|
int ret;
|
|
|
|
if (!ab->hw_params->rxdma1_enable)
|
|
goto out;
|
|
|
|
ret = ath12k_dp_rx_pdev_srng_alloc(ar);
|
|
if (ret) {
|
|
ath12k_warn(ab, "failed to setup rx srngs\n");
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
|
|
ring_id = dp->rxdma_mon_dst_ring[i].ring_id;
|
|
ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id,
|
|
mac_id + i,
|
|
HAL_RXDMA_MONITOR_DST);
|
|
if (ret) {
|
|
ath12k_warn(ab,
|
|
"failed to configure rxdma_mon_dst_ring %d %d\n",
|
|
i, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
static int ath12k_dp_rx_pdev_mon_status_attach(struct ath12k *ar)
|
|
{
|
|
struct ath12k_pdev_dp *dp = &ar->dp;
|
|
struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&dp->mon_data;
|
|
|
|
skb_queue_head_init(&pmon->rx_status_q);
|
|
|
|
pmon->mon_ppdu_status = DP_PPDU_STATUS_START;
|
|
|
|
memset(&pmon->rx_mon_stats, 0,
|
|
sizeof(pmon->rx_mon_stats));
|
|
return 0;
|
|
}
|
|
|
|
int ath12k_dp_rx_pdev_mon_attach(struct ath12k *ar)
|
|
{
|
|
struct ath12k_pdev_dp *dp = &ar->dp;
|
|
struct ath12k_mon_data *pmon = &dp->mon_data;
|
|
int ret = 0;
|
|
|
|
ret = ath12k_dp_rx_pdev_mon_status_attach(ar);
|
|
if (ret) {
|
|
ath12k_warn(ar->ab, "pdev_mon_status_attach() failed");
|
|
return ret;
|
|
}
|
|
|
|
pmon->mon_last_linkdesc_paddr = 0;
|
|
pmon->mon_last_buf_cookie = DP_RX_DESC_COOKIE_MAX + 1;
|
|
spin_lock_init(&pmon->mon_lock);
|
|
|
|
if (!ar->ab->hw_params->rxdma1_enable)
|
|
return 0;
|
|
|
|
INIT_LIST_HEAD(&pmon->dp_rx_mon_mpdu_list);
|
|
pmon->mon_mpdu = NULL;
|
|
|
|
return 0;
|
|
}
|