Files
linux/drivers/net/wireless/ath/ath12k/dp_rx.c
Reshma Immaculate Rajkumar 4242625f27 wifi: ath12k: Pass the correct value of each TID during a stop AMPDU session
With traffic ongoing for data TID [TID 0], an DELBA request to
stop AMPDU for the BA session was received on management TID [TID 4].
The corresponding TID number was incorrectly passed to stop the BA session,
resulting in the BA session for data TIDs being stopped and the BA size
being reduced to 1, causing an overall dip in TCP throughput.

Fix this issue by passing the correct argument from
ath12k_dp_rx_ampdu_stop() to ath12k_dp_arch_peer_rx_tid_reo_update()
during an AMPDU stop session. Instead of passing peer->dp_peer->rx_tid,
which is the base address of the array, corresponding to TID 0, pass
the value of &peer->dp_peer->rx_tid[params->tid]. With this, the
different TID numbers are accounted for.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1

Fixes: d889913205 ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
Signed-off-by: Reshma Immaculate Rajkumar <reshma.rajkumar@oss.qualcomm.com>
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Link: https://patch.msgid.link/20260227110123.3726354-1-reshma.rajkumar@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
2026-03-24 06:44:00 -07:00

1857 lines
49 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/ieee80211.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <crypto/hash.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);
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,
struct hal_rx_desc *rx_desc,
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, rx_info->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 hal_rx_desc *rx_desc,
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 crypto_shash *tfm;
struct ath12k_dp_link_peer *peer;
struct ath12k_dp_rx_tid *rx_tid;
int i;
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
tfm = crypto_alloc_shash("michael_mic", 0, 0);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
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);
crypto_free_shash(tfm);
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);
crypto_free_shash(tfm);
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->tfm_mmic = tfm;
peer->dp_peer->dp_setup_done = true;
spin_unlock_bh(&dp->dp_lock);
return 0;
}
int ath12k_dp_rx_h_michael_mic(struct crypto_shash *tfm, u8 *key,
struct ieee80211_hdr *hdr, u8 *data,
size_t data_len, u8 *mic)
{
SHASH_DESC_ON_STACK(desc, tfm);
u8 mic_hdr[16] = {};
u8 tid = 0;
int ret;
if (!tfm)
return -EINVAL;
desc->tfm = tfm;
ret = crypto_shash_setkey(tfm, key, 8);
if (ret)
goto out;
ret = crypto_shash_init(desc);
if (ret)
goto out;
/* TKIP MIC header */
memcpy(mic_hdr, ieee80211_get_DA(hdr), ETH_ALEN);
memcpy(mic_hdr + ETH_ALEN, ieee80211_get_SA(hdr), ETH_ALEN);
if (ieee80211_is_data_qos(hdr->frame_control))
tid = ieee80211_get_tid(hdr);
mic_hdr[12] = tid;
ret = crypto_shash_update(desc, mic_hdr, 16);
if (ret)
goto out;
ret = crypto_shash_update(desc, data, data_len);
if (ret)
goto out;
ret = crypto_shash_final(desc, mic);
out:
shash_desc_zero(desc);
return ret;
}
EXPORT_SYMBOL(ath12k_dp_rx_h_michael_mic);
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;
}