mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-24 21:02:35 +00:00
2045 lines
57 KiB
Diff
2045 lines
57 KiB
Diff
From 5cd2da1a4d9cf177444d3633d9cfdc0a68573587 Mon Sep 17 00:00:00 2001
|
||
From: Venkateswara Naralasetty <vnaralas@codeaurora.org>
|
||
Date: Wed, 10 Feb 2021 18:17:04 +0530
|
||
Subject: [PATCH] ath11k: add CFR capture support for ipq8074
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
This change is to add single shot/periodic CFR capture
|
||
support for ipq8074.
|
||
|
||
To enable/disable cfr feature use command,
|
||
echo <val> > /sys/kernel/debug/ieee80211/phyX/ath11k/enable_cfr
|
||
where, val: 0 to disable CFR and 1 to enable CFR.
|
||
|
||
To enable CFR capture for associated peers,
|
||
echo "<val> <bw> <periodicity> <method>"
|
||
> /sys/kernel/debug/ieee80211/phyX/netdev\:wlanx/stations/<mac>/cfr_capture
|
||
val: 0 - stop CFR capture
|
||
1 - start CFR capture
|
||
bw: CFR capture bandwidth
|
||
0 - 20MHZ
|
||
1 - 40MHZ
|
||
2 - 80MHZ
|
||
Periodicity: Periodicity at which hardware is expceted to collect CFR dump.
|
||
0 - single shot capture.
|
||
non zero - for Periodic captures (value should be multiple of 10).
|
||
method: Method used by hardware to collect the CFR dump.
|
||
0 - from the ACKs of QOS NULL packets.
|
||
|
||
To enable CFR capture for unassociated clients,
|
||
echo “<mac address> <val> <periodicity>”
|
||
> /sys/kernel/debug/ieee80211/phyX/ath11k/cfr_unassoc
|
||
Mac address: mac address of the client.
|
||
Val: 0 - start CFR capture
|
||
1 – stop CFR capture
|
||
Periodicity: Periodicity at which hardware is expceted to collect CFR dump.
|
||
0 - single shot capture.
|
||
non zero - for Periodic captures (value should be multiple of 10).
|
||
|
||
To collect the cfr dump,
|
||
cat /sys/kernel/debug/ieee80211/phy0/ath11k/cfr_capture0 > /tmp/cfr.bin
|
||
|
||
Signed-off-by: Venkateswara Naralasetty <vnaralas@codeaurora.org>
|
||
---
|
||
drivers/net/wireless/ath/ath11k/Kconfig | 9 +
|
||
drivers/net/wireless/ath/ath11k/Makefile | 1 +
|
||
drivers/net/wireless/ath/ath11k/cfr.c | 878 ++++++++++++++++++++++++++
|
||
drivers/net/wireless/ath/ath11k/cfr.h | 282 +++++++++
|
||
drivers/net/wireless/ath/ath11k/core.c | 15 +
|
||
drivers/net/wireless/ath/ath11k/core.h | 16 +-
|
||
drivers/net/wireless/ath/ath11k/dbring.c | 28 +-
|
||
drivers/net/wireless/ath/ath11k/dbring.h | 11 +-
|
||
drivers/net/wireless/ath/ath11k/debugfs.h | 2 +
|
||
drivers/net/wireless/ath/ath11k/debugfs_sta.c | 157 +++++
|
||
drivers/net/wireless/ath/ath11k/hal.c | 2 +-
|
||
drivers/net/wireless/ath/ath11k/hw.h | 4 +
|
||
drivers/net/wireless/ath/ath11k/mac.c | 11 +-
|
||
drivers/net/wireless/ath/ath11k/spectral.c | 2 +-
|
||
drivers/net/wireless/ath/ath11k/wmi.c | 137 +++-
|
||
drivers/net/wireless/ath/ath11k/wmi.h | 91 ++-
|
||
local-symbols | 1 +
|
||
17 files changed, 1629 insertions(+), 18 deletions(-)
|
||
create mode 100644 drivers/net/wireless/ath/ath11k/cfr.c
|
||
create mode 100644 drivers/net/wireless/ath/ath11k/cfr.h
|
||
|
||
--- a/drivers/net/wireless/ath/ath11k/Kconfig
|
||
+++ b/drivers/net/wireless/ath/ath11k/Kconfig
|
||
@@ -84,3 +84,12 @@ config ATH11K_MEM_PROFILE_512M
|
||
default n
|
||
---help---
|
||
Enables 512MB memory profile for ath11k
|
||
+
|
||
+config ATH11K_CFR
|
||
+ bool "QCA ath11k CFR support"
|
||
+ depends on ATH11K_DEBUGFS
|
||
+ depends on RELAY
|
||
+ help
|
||
+ Enable ath11k cfr dump support
|
||
+
|
||
+ Say Y to enable access to collect cfr data dump via debugfs.
|
||
--- a/drivers/net/wireless/ath/ath11k/Makefile
|
||
+++ b/drivers/net/wireless/ath/ath11k/Makefile
|
||
@@ -28,6 +28,7 @@ ath11k-$(CONFIG_WANT_DEV_COREDUMP) += co
|
||
ath11k-$(CPTCFG_ATH11K_SPECTRAL) += spectral.o
|
||
ath11k-$(CPTCFG_ATH11K_PKTLOG) += pktlog.o
|
||
ath11k-$(CPTCFG_ATH11K_NSS_SUPPORT) += nss.o
|
||
+ath11k-$(CPTCFG_ATH11K_CFR) += cfr.o
|
||
|
||
obj-$(CPTCFG_ATH11K_AHB) += ath11k_ahb.o
|
||
ath11k_ahb-y += ahb.o
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/ath/ath11k/cfr.c
|
||
@@ -0,0 +1,878 @@
|
||
+// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||
+/*
|
||
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
||
+ */
|
||
+
|
||
+#include <linux/relay.h>
|
||
+#include "core.h"
|
||
+#include "debug.h"
|
||
+
|
||
+bool peer_is_in_cfr_unassoc_pool(struct ath11k *ar, u8 *peer_mac)
|
||
+{
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+ struct cfr_unassoc_pool_entry *entry;
|
||
+ int i;
|
||
+
|
||
+ if (!ar->cfr_enabled)
|
||
+ return false;
|
||
+
|
||
+ spin_lock_bh(&cfr->lock);
|
||
+ for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
|
||
+ entry = &cfr->unassoc_pool[i];
|
||
+ if (!entry->is_valid)
|
||
+ continue;
|
||
+
|
||
+ if (ether_addr_equal(peer_mac, entry->peer_mac)) {
|
||
+ /* Remove entry if it is single shot */
|
||
+ if (entry->period == 0) {
|
||
+ memset(entry->peer_mac, 0 , ETH_ALEN);
|
||
+ entry->is_valid = false;
|
||
+ cfr->cfr_enabled_peer_cnt--;
|
||
+ }
|
||
+ spin_unlock_bh(&cfr->lock);
|
||
+ return true;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ spin_unlock_bh(&cfr->lock);
|
||
+
|
||
+ return false;
|
||
+}
|
||
+
|
||
+void ath11k_cfr_lut_update_paddr(struct ath11k *ar, dma_addr_t paddr,
|
||
+ u32 buf_id)
|
||
+{
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+ struct ath11k_cfr_look_up_table *lut;
|
||
+
|
||
+ if (cfr->lut) {
|
||
+ lut = &cfr->lut[buf_id];
|
||
+ lut->dbr_address = paddr;
|
||
+ }
|
||
+}
|
||
+
|
||
+void ath11k_cfr_decrement_peer_count(struct ath11k *ar,
|
||
+ struct ath11k_sta *arsta)
|
||
+{
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+
|
||
+ spin_lock_bh(&cfr->lock);
|
||
+
|
||
+ if (arsta->cfr_capture.cfr_enable)
|
||
+ cfr->cfr_enabled_peer_cnt--;
|
||
+
|
||
+ spin_unlock_bh(&cfr->lock);
|
||
+}
|
||
+
|
||
+struct ath11k_dbring *ath11k_cfr_get_dbring(struct ath11k *ar)
|
||
+{
|
||
+ if (ar->cfr_enabled)
|
||
+ return &ar->cfr.rx_ring;
|
||
+ else
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
+static int cfr_calculate_tones_form_dma_hdr(struct ath11k_cfir_dma_hdr *hdr)
|
||
+{
|
||
+ u8 bw = FIELD_GET(CFIR_DMA_HDR_INFO1_UPLOAD_PKT_BW, hdr->info1);
|
||
+ u8 preamble = FIELD_GET(CFIR_DMA_HDR_INFO1_PREABLE_TYPE, hdr->info1);
|
||
+
|
||
+ switch (preamble) {
|
||
+ case ATH11K_CFR_PREAMBLE_TYPE_LEGACY:
|
||
+ case ATH11K_CFR_PREAMBLE_TYPE_VHT:
|
||
+ switch (bw) {
|
||
+ case 0:
|
||
+ return TONES_IN_20MHZ;
|
||
+ case 1: /* DUP40/VHT40 */
|
||
+ return TONES_IN_40MHZ;
|
||
+ case 2: /* DUP80/VHT80 */
|
||
+ return TONES_IN_80MHZ;
|
||
+ case 3: /* DUP160/VHT160 */
|
||
+ return TONES_IN_160MHZ;
|
||
+ }
|
||
+
|
||
+ case ATH11K_CFR_PREAMBLE_TYPE_HT:
|
||
+ switch (bw) {
|
||
+ case 0:
|
||
+ return TONES_IN_20MHZ;
|
||
+ case 1:
|
||
+ return TONES_IN_40MHZ;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return TONES_INVALID;
|
||
+}
|
||
+
|
||
+static inline
|
||
+void ath11k_cfr_release_lut_entry(struct ath11k_cfr_look_up_table *lut)
|
||
+{
|
||
+ memset(lut, 0, sizeof(*lut));
|
||
+}
|
||
+
|
||
+static void ath11k_cfr_rfs_write(struct ath11k *ar, const void *head,
|
||
+ u32 head_len, const void *data, u32 data_len,
|
||
+ const void * tail, int tail_data)
|
||
+{
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+
|
||
+ if (!ar->cfr.rfs_cfr_capture)
|
||
+ return;
|
||
+
|
||
+ relay_write(cfr->rfs_cfr_capture, head, head_len);
|
||
+ relay_write(cfr->rfs_cfr_capture, data, data_len);
|
||
+ relay_write(cfr->rfs_cfr_capture, tail, tail_data);
|
||
+ relay_flush(cfr->rfs_cfr_capture);
|
||
+}
|
||
+
|
||
+static void ath11k_cfr_free_pending_dbr_events(struct ath11k *ar)
|
||
+{
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+ struct ath11k_cfr_look_up_table *lut = NULL;
|
||
+ int i;
|
||
+
|
||
+ if (!cfr->lut)
|
||
+ return;
|
||
+
|
||
+ for (i = 0; i < cfr->lut_num; i++) {
|
||
+ lut = &cfr->lut[i];
|
||
+ if (lut->dbr_recv && !lut->tx_recv &&
|
||
+ (lut->dbr_tstamp < cfr->last_success_tstamp)) {
|
||
+ ath11k_dbring_bufs_replenish(ar, &cfr->rx_ring, lut->buff,
|
||
+ WMI_DIRECT_BUF_CFR, GFP_ATOMIC);
|
||
+ ath11k_cfr_release_lut_entry(lut);
|
||
+ cfr->flush_dbr_cnt++;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+/* Correlate and relay: This function correlate the data coming from
|
||
+ * WMI_PDEV_DMA_RING_BUF_RELEASE_EVENT(DBR event) and
|
||
+ * WMI_PEER_CFR_CAPTURE_EVENT(Tx capture event). if both the events
|
||
+ * are received and PPDU id matches from the both events,
|
||
+ * return CORRELATE_STATUS_RELEASE which means relay the correlated data
|
||
+ * to user space. Otherwise return CORRELATE_STATUS_HOLD which means wait
|
||
+ * for the second event to come. It will return CORRELATE_STATUS_ERR in
|
||
+ * case of any error.
|
||
+ *
|
||
+ * It also check for the pending DBR events and clear those events
|
||
+ * in case of corresponding TX capture event is not received for
|
||
+ * the PPDU.
|
||
+ */
|
||
+
|
||
+static int ath11k_cfr_correlate_and_relay(struct ath11k *ar,
|
||
+ struct ath11k_cfr_look_up_table *lut,
|
||
+ u8 event_type)
|
||
+{
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+ u64 diff;
|
||
+
|
||
+ if (event_type == ATH11K_CORRELATE_TX_EVENT) {
|
||
+ if (lut->tx_recv)
|
||
+ cfr->cfr_dma_aborts++;
|
||
+ cfr->tx_evt_cnt++;
|
||
+ lut->tx_recv = true;
|
||
+ } else if (event_type == ATH11K_CORRELATE_DBR_EVENT) {
|
||
+ cfr->dbr_evt_cnt++;
|
||
+ lut->dbr_recv = true;
|
||
+ }
|
||
+
|
||
+ if (lut->dbr_recv && lut->tx_recv) {
|
||
+ if (lut->dbr_ppdu_id == lut->tx_ppdu_id) {
|
||
+ cfr->last_success_tstamp = lut->dbr_tstamp;
|
||
+ if (lut->dbr_tstamp > lut->txrx_tstamp) {
|
||
+ diff = lut->dbr_tstamp - lut->txrx_tstamp;
|
||
+ ath11k_dbg(ar->ab, ATH11K_DBG_CFR,
|
||
+ "txrx event -> dbr event delay = %u ms",
|
||
+ jiffies_to_msecs(diff));
|
||
+ } else if (lut->txrx_tstamp > lut->dbr_tstamp) {
|
||
+ diff = lut->txrx_tstamp - lut->dbr_tstamp;
|
||
+ ath11k_dbg(ar->ab, ATH11K_DBG_CFR,
|
||
+ "dbr event -> txrx event delay = %u ms",
|
||
+ jiffies_to_msecs(diff));
|
||
+ }
|
||
+
|
||
+ ath11k_cfr_free_pending_dbr_events(ar);
|
||
+
|
||
+ cfr->release_cnt++;
|
||
+ return ATH11K_CORRELATE_STATUS_RELEASE;
|
||
+ } else {
|
||
+ /*
|
||
+ * When there is a ppdu id mismatch, discard the TXRX
|
||
+ * event since multiple PPDUs are likely to have same
|
||
+ * dma addr, due to ucode aborts.
|
||
+ */
|
||
+
|
||
+ ath11k_dbg(ar->ab, ATH11K_DBG_CFR,
|
||
+ "Received dbr event twice for the same lut entry");
|
||
+ lut->tx_recv = false;
|
||
+ lut->tx_ppdu_id = 0;
|
||
+ cfr->clear_txrx_event++;
|
||
+ cfr->cfr_dma_aborts++;
|
||
+ return ATH11K_CORRELATE_STATUS_HOLD;
|
||
+ }
|
||
+ } else {
|
||
+ return ATH11K_CORRELATE_STATUS_HOLD;
|
||
+ }
|
||
+}
|
||
+
|
||
+static int ath11k_cfr_process_data(struct ath11k *ar,
|
||
+ struct ath11k_dbring_data *param)
|
||
+{
|
||
+ struct ath11k_base *ab = ar->ab;
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+ struct ath11k_cfr_look_up_table *lut;
|
||
+ struct ath11k_csi_cfr_header *header;
|
||
+ struct ath11k_cfir_dma_hdr dma_hdr;
|
||
+ u8 *data;
|
||
+ u32 end_magic = ATH11K_CFR_END_MAGIC;
|
||
+ u32 buf_id;
|
||
+ u32 tones;
|
||
+ u32 length;
|
||
+ int status;
|
||
+ u8 num_chains;
|
||
+ int ret = 0;
|
||
+
|
||
+ data = param->data;
|
||
+ buf_id = param->buf_id;
|
||
+
|
||
+ memcpy(&dma_hdr, data, sizeof(struct ath11k_cfir_dma_hdr));
|
||
+
|
||
+ tones = cfr_calculate_tones_form_dma_hdr(&dma_hdr);
|
||
+ if (tones == TONES_INVALID) {
|
||
+ ath11k_err(ar->ab, "Number of tones received is invalid");
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ num_chains = FIELD_GET(CFIR_DMA_HDR_INFO1_NUM_CHAINS,
|
||
+ dma_hdr.info1);
|
||
+
|
||
+ length = sizeof(struct ath11k_cfir_dma_hdr);
|
||
+ length += tones * (num_chains + 1);
|
||
+
|
||
+ spin_lock_bh(&cfr->lut_lock);
|
||
+
|
||
+ if (!cfr->lut) {
|
||
+ spin_unlock_bh(&cfr->lut_lock);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ lut = &cfr->lut[buf_id];
|
||
+
|
||
+ if (!lut) {
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "lut failure to process cfr data id:%d\n", buf_id);
|
||
+ spin_unlock_bh(&cfr->lut_lock);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ ath11k_dbg_dump(ab, ATH11K_DBG_CFR_DUMP,"data_from_buf_rel:", "",
|
||
+ data, length);
|
||
+
|
||
+ lut->buff = param->buff;
|
||
+ lut->data = data;
|
||
+ lut->data_len = length;
|
||
+ lut->dbr_ppdu_id = dma_hdr.phy_ppdu_id;
|
||
+ lut->dbr_tstamp = jiffies;
|
||
+
|
||
+ memcpy(&lut->hdr, &dma_hdr, sizeof(struct ath11k_cfir_dma_hdr));
|
||
+
|
||
+ header = &lut->header;
|
||
+ header->u.meta_v2.channel_bw = FIELD_GET(CFIR_DMA_HDR_INFO1_UPLOAD_PKT_BW,
|
||
+ dma_hdr.info1);
|
||
+ header->u.meta_v2.length = length;
|
||
+
|
||
+ status = ath11k_cfr_correlate_and_relay(ar, lut,
|
||
+ ATH11K_CORRELATE_DBR_EVENT);
|
||
+ if (status == ATH11K_CORRELATE_STATUS_RELEASE) {
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "releasing CFR data to user space");
|
||
+ ath11k_cfr_rfs_write(ar, &lut->header,
|
||
+ sizeof(struct ath11k_csi_cfr_header),
|
||
+ lut->data, lut->data_len,
|
||
+ &end_magic, sizeof(u32));
|
||
+ ath11k_cfr_release_lut_entry(lut);
|
||
+ ret = ATH11K_CORRELATE_STATUS_RELEASE;
|
||
+ } else if (status == ATH11K_CORRELATE_STATUS_HOLD) {
|
||
+ ret = ATH11K_CORRELATE_STATUS_HOLD;
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "tx event is not yet received holding the buf");
|
||
+ } else {
|
||
+ ret = ATH11K_CORRELATE_STATUS_ERR;
|
||
+ ath11k_cfr_release_lut_entry(lut);
|
||
+ ath11k_err(ab, "error in correlating events");
|
||
+ }
|
||
+
|
||
+ spin_unlock_bh(&cfr->lut_lock);
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static void ath11k_cfr_fill_hdr_info(struct ath11k *ar,
|
||
+ struct ath11k_csi_cfr_header *header,
|
||
+ struct ath11k_cfr_peer_tx_param *params)
|
||
+{
|
||
+ header->cfr_metadata_version = ATH11K_CFR_META_VERSION_2;
|
||
+ header->cfr_data_version = ATH11K_CFR_DATA_VERSION_1;
|
||
+ /* TODO: can we add this chip_type to hw param table */
|
||
+ header->chip_type = ATH11K_CFR_RADIO_IPQ8074;
|
||
+ header->u.meta_v2.status = FIELD_GET(WMI_CFR_PEER_CAPTURE_STATUS,
|
||
+ params->status);
|
||
+ header->u.meta_v2.capture_bw = params->bandwidth;
|
||
+ header->u.meta_v2.phy_mode = params->phy_mode;
|
||
+ header->u.meta_v2.prim20_chan = params->primary_20mhz_chan;
|
||
+ header->u.meta_v2.center_freq1 = params->band_center_freq1;
|
||
+ header->u.meta_v2.center_freq2 = params->band_center_freq2;
|
||
+
|
||
+ /* Currently CFR data is captured on ACK of a Qos NULL frame.
|
||
+ * For 20 MHz, ACK is Legacy and for 40/80/160, ACK is DUP Legacy.
|
||
+ */
|
||
+ header->u.meta_v2.capture_mode = params->bandwidth ?
|
||
+ ATH11K_CFR_CAPTURE_DUP_LEGACY_ACK : ATH11K_CFR_CAPTURE_LEGACY_ACK;
|
||
+ header->u.meta_v2.capture_type = params->capture_method;
|
||
+ header->u.meta_v2.num_rx_chain = ar->num_rx_chains;
|
||
+ header->u.meta_v2.sts_count = params->spatial_streams;
|
||
+ header->u.meta_v2.timestamp = params->timestamp_us;
|
||
+ memcpy(header->u.meta_v2.peer_addr, params->peer_mac_addr, ETH_ALEN);
|
||
+ memcpy(header->u.meta_v2.chain_rssi, params->chain_rssi,
|
||
+ sizeof(params->chain_rssi));
|
||
+ memcpy(header->u.meta_v2.chain_phase, params->chain_phase,
|
||
+ sizeof(params->chain_phase));
|
||
+}
|
||
+
|
||
+int ath11k_process_cfr_capture_event(struct ath11k_base *ab,
|
||
+ struct ath11k_cfr_peer_tx_param *params)
|
||
+{
|
||
+ struct ath11k *ar;
|
||
+ struct ath11k_cfr *cfr;
|
||
+ struct ath11k_vif *arvif;
|
||
+ struct ath11k_cfr_look_up_table *lut = NULL, *temp = NULL;
|
||
+ struct ath11k_dbring_element *buff;
|
||
+ struct ath11k_csi_cfr_header *header;
|
||
+ dma_addr_t buf_addr;
|
||
+ u32 end_magic = ATH11K_CFR_END_MAGIC;
|
||
+ u8 tx_status;
|
||
+ int ret = 0;
|
||
+ int status;
|
||
+ int i;
|
||
+
|
||
+ rcu_read_lock();
|
||
+ arvif = ath11k_mac_get_arvif_by_vdev_id(ab, params->vdev_id);
|
||
+ if (!arvif) {
|
||
+ ath11k_warn(ab, "Failed to get arvif for vdev id %d\n",
|
||
+ params->vdev_id);
|
||
+ rcu_read_unlock();
|
||
+ return -ENOENT;
|
||
+ }
|
||
+
|
||
+ ar = arvif->ar;
|
||
+ cfr = &ar->cfr;
|
||
+ rcu_read_unlock();
|
||
+
|
||
+ if (WMI_CFR_CAPTURE_STATUS_PEER_PS & params->status) {
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "CFR capture failed as peer %pM is in powersave",
|
||
+ params->peer_mac_addr);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ if (!(WMI_CFR_PEER_CAPTURE_STATUS & params->status)) {
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "CFR capture failed for the peer : %pM",
|
||
+ params->peer_mac_addr);
|
||
+ cfr->tx_peer_status_cfr_fail++;
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ tx_status = FIELD_GET(WMI_CFR_FRAME_TX_STATUS, params->status);
|
||
+
|
||
+ if (tx_status != WMI_FRAME_TX_STATUS_OK) {
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "WMI tx status %d for the peer %pM",
|
||
+ tx_status, params->peer_mac_addr);
|
||
+ cfr->tx_evt_status_cfr_fail++;
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ buf_addr = (((u64)FIELD_GET(WMI_CFR_CORRELATION_INFO2_BUF_ADDR_HIGH,
|
||
+ params->correlation_info_2)) << 32) |
|
||
+ params->correlation_info_1;
|
||
+
|
||
+ spin_lock_bh(&cfr->lut_lock);
|
||
+
|
||
+ if (!cfr->lut) {
|
||
+ spin_unlock_bh(&cfr->lut_lock);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ for (i = 0; i < cfr->lut_num; i++) {
|
||
+ temp = &cfr->lut[i];
|
||
+ if (temp->dbr_address == buf_addr) {
|
||
+ lut = &cfr->lut[i];
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (!lut) {
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "lut failure to process tx event\n");
|
||
+ cfr->tx_dbr_lookup_fail++;
|
||
+ spin_unlock_bh(&cfr->lut_lock);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ lut->tx_ppdu_id = FIELD_GET(WMI_CFR_CORRELATION_INFO2_PPDU_ID,
|
||
+ params->correlation_info_2);
|
||
+ lut->tx_address1 = params->correlation_info_1;
|
||
+ lut->tx_address2 = params->correlation_info_2;
|
||
+ lut->txrx_tstamp = jiffies;
|
||
+
|
||
+ header = &lut->header;
|
||
+ header->start_magic_num = ATH11K_CFR_START_MAGIC;
|
||
+ header->vendorid = VENDOR_QCA;
|
||
+ header->pltform_type = PLATFORM_TYPE_ARM;
|
||
+
|
||
+ ath11k_cfr_fill_hdr_info(ar, header, params);
|
||
+
|
||
+ status = ath11k_cfr_correlate_and_relay(ar, lut,
|
||
+ ATH11K_CORRELATE_TX_EVENT);
|
||
+ if (status == ATH11K_CORRELATE_STATUS_RELEASE) {
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "Releasing CFR data to user space");
|
||
+ ath11k_cfr_rfs_write(ar, &lut->header,
|
||
+ sizeof(struct ath11k_csi_cfr_header),
|
||
+ lut->data, lut->data_len,
|
||
+ &end_magic, sizeof(u32));
|
||
+ buff = lut->buff;
|
||
+ ath11k_cfr_release_lut_entry(lut);
|
||
+
|
||
+ ath11k_dbring_bufs_replenish(ar, &cfr->rx_ring, buff,
|
||
+ WMI_DIRECT_BUF_CFR, GFP_ATOMIC);
|
||
+ } else if (status == ATH11K_CORRELATE_STATUS_HOLD) {
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "dbr event is not yet received holding buf\n");
|
||
+ } else {
|
||
+ ath11k_cfr_release_lut_entry(lut);
|
||
+ ret = -EINVAL;
|
||
+ }
|
||
+
|
||
+ spin_unlock_bh(&cfr->lut_lock);
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static struct dentry *create_buf_file_handler(const char *filename,
|
||
+ struct dentry *parent,
|
||
+ umode_t mode,
|
||
+ struct rchan_buf *buf,
|
||
+ int *is_global)
|
||
+{
|
||
+ struct dentry *buf_file;
|
||
+
|
||
+ buf_file = debugfs_create_file(filename, mode, parent, buf,
|
||
+ &relay_file_operations);
|
||
+ *is_global = 1;
|
||
+ return buf_file;
|
||
+}
|
||
+
|
||
+static int remove_buf_file_handler(struct dentry *dentry)
|
||
+{
|
||
+ debugfs_remove(dentry);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static struct rchan_callbacks rfs_cfr_capture_cb = {
|
||
+ .create_buf_file = create_buf_file_handler,
|
||
+ .remove_buf_file = remove_buf_file_handler,
|
||
+};
|
||
+
|
||
+static ssize_t ath11k_read_file_enable_cfr(struct file *file,
|
||
+ char __user *user_buf,
|
||
+ size_t count, loff_t *ppos)
|
||
+{
|
||
+ struct ath11k *ar = file->private_data;
|
||
+ char buf[32] = {0};
|
||
+ size_t len;
|
||
+
|
||
+ mutex_lock(&ar->conf_mutex);
|
||
+ len = scnprintf(buf, sizeof(buf), "%d\n", ar->cfr_enabled);
|
||
+ mutex_unlock(&ar->conf_mutex);
|
||
+
|
||
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||
+}
|
||
+
|
||
+static ssize_t ath11k_write_file_enable_cfr(struct file *file,
|
||
+ const char __user *ubuf,
|
||
+ size_t count, loff_t *ppos)
|
||
+{
|
||
+ struct ath11k *ar = file->private_data;
|
||
+ u8 enable_cfr;
|
||
+ int ret;
|
||
+
|
||
+ if (kstrtouint_from_user(ubuf, count, 0, &enable_cfr))
|
||
+ return -EINVAL;
|
||
+
|
||
+ mutex_lock(&ar->conf_mutex);
|
||
+
|
||
+ if (ar->state != ATH11K_STATE_ON) {
|
||
+ ret = -ENETDOWN;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (enable_cfr > 1) {
|
||
+ ret = -EINVAL;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (ar->cfr_enabled == enable_cfr) {
|
||
+ ret = count;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PER_PEER_CFR_ENABLE,
|
||
+ enable_cfr, ar->pdev->pdev_id);
|
||
+ if (ret) {
|
||
+ ath11k_warn(ar->ab,
|
||
+ "Failed to enable/disable per peer cfr (%d)\n",
|
||
+ ret);
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ ar->cfr_enabled = enable_cfr;
|
||
+ ret = count;
|
||
+
|
||
+out:
|
||
+ mutex_unlock(&ar->conf_mutex);
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static const struct file_operations fops_enable_cfr = {
|
||
+ .read = ath11k_read_file_enable_cfr,
|
||
+ .write = ath11k_write_file_enable_cfr,
|
||
+ .open = simple_open,
|
||
+ .owner = THIS_MODULE,
|
||
+ .llseek = default_llseek,
|
||
+};
|
||
+
|
||
+static ssize_t ath11k_write_file_cfr_unassoc(struct file *file,
|
||
+ const char __user *ubuf,
|
||
+ size_t count, loff_t *ppos)
|
||
+{
|
||
+ struct ath11k *ar = file->private_data;
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+ struct cfr_unassoc_pool_entry *entry;
|
||
+ char buf[64] = {0};
|
||
+ u8 peer_mac[6];
|
||
+ u32 cfr_capture_enable;
|
||
+ u32 cfr_capture_period;
|
||
+ int available_idx = -1;
|
||
+ int ret, i;
|
||
+
|
||
+ simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);
|
||
+
|
||
+ mutex_lock(&ar->conf_mutex);
|
||
+ spin_lock_bh(&cfr->lock);
|
||
+
|
||
+ if (ar->state != ATH11K_STATE_ON) {
|
||
+ ret = -ENETDOWN;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (!ar->cfr_enabled) {
|
||
+ ret = -EINVAL;
|
||
+ ath11k_err(ar->ab, "CFR is not enabled on this pdev %d\n",
|
||
+ ar->pdev_idx);
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ ret = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %u %u",
|
||
+ &peer_mac[0], &peer_mac[1], &peer_mac[2], &peer_mac[3],
|
||
+ &peer_mac[4], &peer_mac[5], &cfr_capture_enable,
|
||
+ &cfr_capture_period);
|
||
+
|
||
+ if (ret < 1) {
|
||
+ ret = -EINVAL;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (cfr_capture_enable && ret != 8) {
|
||
+ ret = -EINVAL;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (!cfr_capture_enable) {
|
||
+ for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
|
||
+ entry = &cfr->unassoc_pool[i];
|
||
+ if (ether_addr_equal(peer_mac, entry->peer_mac)) {
|
||
+ memset(entry->peer_mac, 0, ETH_ALEN);
|
||
+ entry->is_valid = false;
|
||
+ cfr->cfr_enabled_peer_cnt--;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ ret = count;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+
|
||
+ if (cfr->cfr_enabled_peer_cnt >= ATH11K_MAX_CFR_ENABLED_CLIENTS) {
|
||
+ ath11k_info(ar->ab, "Max cfr peer threshold reached\n");
|
||
+ ret = count;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
|
||
+ entry = &cfr->unassoc_pool[i];
|
||
+
|
||
+ if ((available_idx < 0) && !entry->is_valid)
|
||
+ available_idx = i;
|
||
+
|
||
+ if (ether_addr_equal(peer_mac, entry->peer_mac)) {
|
||
+ ath11k_info(ar->ab,
|
||
+ "peer entry already present updating params\n");
|
||
+ entry->period = cfr_capture_period;
|
||
+ ret = count;
|
||
+ goto out;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (available_idx >= 0) {
|
||
+ entry = &cfr->unassoc_pool[available_idx];
|
||
+ ether_addr_copy(entry->peer_mac, peer_mac);
|
||
+ entry->period = cfr_capture_period;
|
||
+ entry->is_valid = true;
|
||
+ cfr->cfr_enabled_peer_cnt++;
|
||
+ }
|
||
+
|
||
+ ret = count;
|
||
+out:
|
||
+ spin_unlock_bh(&cfr->lock);
|
||
+ mutex_unlock(&ar->conf_mutex);
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static ssize_t ath11k_read_file_cfr_unassoc(struct file *file,
|
||
+ char __user *ubuf,
|
||
+ size_t count, loff_t *ppos)
|
||
+{
|
||
+ char buf[512] = {0};
|
||
+ struct ath11k *ar = file->private_data;
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+ struct cfr_unassoc_pool_entry *entry;
|
||
+ int len = 0, i;
|
||
+
|
||
+ mutex_lock(&ar->conf_mutex);
|
||
+ spin_lock_bh(&cfr->lock);
|
||
+
|
||
+ for (i = 0; i < ATH11K_MAX_CFR_ENABLED_CLIENTS; i++) {
|
||
+ entry = &cfr->unassoc_pool[i];
|
||
+ if (entry->is_valid)
|
||
+ len += scnprintf(buf + len, sizeof(buf) - len,
|
||
+ "peer: %pM period: %u\n",
|
||
+ entry->peer_mac, entry->period);
|
||
+ }
|
||
+
|
||
+ spin_unlock_bh(&cfr->lock);
|
||
+ mutex_unlock(&ar->conf_mutex);
|
||
+
|
||
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
|
||
+}
|
||
+
|
||
+static const struct file_operations fops_configure_cfr_unassoc = {
|
||
+ .write = ath11k_write_file_cfr_unassoc,
|
||
+ .read = ath11k_read_file_cfr_unassoc,
|
||
+ .open = simple_open,
|
||
+ .owner = THIS_MODULE,
|
||
+ .llseek = default_llseek,
|
||
+};
|
||
+
|
||
+static inline void ath11k_cfr_debug_unregister(struct ath11k *ar)
|
||
+{
|
||
+ debugfs_remove(ar->cfr.enable_cfr);
|
||
+ ar->cfr.enable_cfr = NULL;
|
||
+ debugfs_remove(ar->cfr.cfr_unassoc);
|
||
+ ar->cfr.cfr_unassoc = NULL;
|
||
+
|
||
+ if (ar->cfr.rfs_cfr_capture) {
|
||
+ relay_close(ar->cfr.rfs_cfr_capture);
|
||
+ ar->cfr.rfs_cfr_capture = NULL;
|
||
+ }
|
||
+}
|
||
+
|
||
+static inline int ath11k_cfr_debug_register(struct ath11k *ar)
|
||
+{
|
||
+ int ret;
|
||
+
|
||
+ ar->cfr.rfs_cfr_capture = relay_open("cfr_capture",
|
||
+ ar->debug.debugfs_pdev,
|
||
+ ar->ab->hw_params.cfr_stream_buf_size,
|
||
+ ar->ab->hw_params.cfr_num_stream_bufs,
|
||
+ &rfs_cfr_capture_cb, NULL);
|
||
+ if (!ar->cfr.rfs_cfr_capture) {
|
||
+ ath11k_warn(ar->ab, "failed to open relay for cfr in pdev %d\n",
|
||
+ ar->pdev_idx);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ ar->cfr.enable_cfr = debugfs_create_file("enable_cfr", 0600,
|
||
+ ar->debug.debugfs_pdev, ar,
|
||
+ &fops_enable_cfr);
|
||
+ if (!ar->cfr.enable_cfr) {
|
||
+ ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
|
||
+ ar->pdev_idx);
|
||
+ ret = -EINVAL;
|
||
+ goto debug_unregister;
|
||
+ }
|
||
+
|
||
+ ar->cfr.cfr_unassoc = debugfs_create_file("cfr_unassoc", 0600,
|
||
+ ar->debug.debugfs_pdev, ar,
|
||
+ &fops_configure_cfr_unassoc);
|
||
+
|
||
+ if (!ar->cfr.cfr_unassoc) {
|
||
+ ath11k_warn(ar->ab,
|
||
+ "failed to open debugfs for unassoc pool in pdev %d\n",
|
||
+ ar->pdev_idx);
|
||
+ ret = -EINVAL;
|
||
+ goto debug_unregister;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+
|
||
+debug_unregister :
|
||
+ ath11k_cfr_debug_unregister(ar);
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static int ath11k_cfr_ring_alloc(struct ath11k *ar,
|
||
+ struct ath11k_dbring_cap *db_cap)
|
||
+{
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+ int ret;
|
||
+
|
||
+ ret = ath11k_dbring_srng_setup(ar, &cfr->rx_ring,
|
||
+ 1, db_cap->min_elem);
|
||
+ if (ret) {
|
||
+ ath11k_warn(ar->ab, "failed to setup db ring\n");
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ ath11k_dbring_set_cfg(ar, &cfr->rx_ring,
|
||
+ ATH11K_CFR_NUM_RESP_PER_EVENT,
|
||
+ ATH11K_CFR_EVENT_TIMEOUT_MS,
|
||
+ ath11k_cfr_process_data);
|
||
+
|
||
+ ret = ath11k_dbring_buf_setup(ar, &cfr->rx_ring, db_cap);
|
||
+ if (ret) {
|
||
+ ath11k_warn(ar->ab, "failed to setup db ring buffer\n");
|
||
+ goto srng_cleanup;
|
||
+ }
|
||
+
|
||
+ ret = ath11k_dbring_wmi_cfg_setup(ar, &cfr->rx_ring, WMI_DIRECT_BUF_CFR);
|
||
+ if (ret) {
|
||
+ ath11k_warn(ar->ab, "failed to setup db ring cfg\n");
|
||
+ goto buffer_cleanup;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+
|
||
+buffer_cleanup:
|
||
+ ath11k_dbring_buf_cleanup(ar, &cfr->rx_ring);
|
||
+srng_cleanup:
|
||
+ ath11k_dbring_srng_cleanup(ar, &cfr->rx_ring);
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+void ath11k_cfr_ring_free(struct ath11k *ar)
|
||
+{
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+
|
||
+ ath11k_dbring_srng_cleanup(ar, &cfr->rx_ring);
|
||
+ ath11k_dbring_buf_cleanup(ar, &cfr->rx_ring);
|
||
+}
|
||
+
|
||
+void ath11k_cfr_deinit(struct ath11k_base *ab)
|
||
+{
|
||
+ struct ath11k *ar;
|
||
+ struct ath11k_cfr *cfr;
|
||
+ int i;
|
||
+
|
||
+ if (!test_bit(WMI_TLV_SERVICE_CFR_CAPTURE_SUPPORT,
|
||
+ ab->wmi_ab.svc_map) || !ab->hw_params.cfr_support)
|
||
+ return;
|
||
+
|
||
+ for (i = 0; i < ab->num_radios; i++) {
|
||
+ ar = ab->pdevs[i].ar;
|
||
+ cfr = &ar->cfr;
|
||
+
|
||
+ ath11k_cfr_debug_unregister(ar);
|
||
+ ath11k_cfr_ring_free(ar);
|
||
+
|
||
+ spin_lock_bh(&cfr->lut_lock);
|
||
+ if (cfr->lut) {
|
||
+ kfree(cfr->lut);
|
||
+ cfr->lut = NULL;
|
||
+ }
|
||
+
|
||
+ spin_unlock_bh(&cfr->lut_lock);
|
||
+ ar->cfr_enabled = 0;
|
||
+ }
|
||
+}
|
||
+
|
||
+int ath11k_cfr_init(struct ath11k_base *ab)
|
||
+{
|
||
+ struct ath11k *ar;
|
||
+ struct ath11k_cfr *cfr;
|
||
+ struct ath11k_dbring_cap db_cap;
|
||
+ struct ath11k_cfr_look_up_table *lut;
|
||
+ u32 num_lut_entries;
|
||
+ int ret = 0;
|
||
+ int i;
|
||
+
|
||
+ if (!test_bit(WMI_TLV_SERVICE_CFR_CAPTURE_SUPPORT,
|
||
+ ab->wmi_ab.svc_map) || !ab->hw_params.cfr_support)
|
||
+ return ret;
|
||
+
|
||
+ for (i = 0; i < ab->num_radios; i++) {
|
||
+ ar = ab->pdevs[i].ar;
|
||
+ cfr = &ar->cfr;
|
||
+
|
||
+ ret = ath11k_dbring_get_cap(ar->ab, ar->pdev_idx,
|
||
+ WMI_DIRECT_BUF_CFR, &db_cap);
|
||
+ if (ret)
|
||
+ continue;
|
||
+
|
||
+ idr_init(&cfr->rx_ring.bufs_idr);
|
||
+ spin_lock_init(&cfr->rx_ring.idr_lock);
|
||
+ spin_lock_init(&cfr->lock);
|
||
+ spin_lock_init(&cfr->lut_lock);
|
||
+
|
||
+ num_lut_entries = min((u32)CFR_MAX_LUT_ENTRIES, db_cap.min_elem);
|
||
+
|
||
+ cfr->lut = kzalloc(num_lut_entries * sizeof(*lut), GFP_KERNEL);
|
||
+ if (!cfr->lut) {
|
||
+ ath11k_warn(ab, "failed to allocate lut for pdev %d\n", i);
|
||
+ return -ENOMEM;
|
||
+ }
|
||
+
|
||
+ ret = ath11k_cfr_ring_alloc(ar, &db_cap);
|
||
+ if (ret) {
|
||
+ ath11k_warn(ab, "failed to init cfr ring for pdev %d\n", i);
|
||
+ goto deinit;
|
||
+ }
|
||
+
|
||
+ spin_lock_bh(&cfr->lock);
|
||
+ cfr->lut_num = num_lut_entries;
|
||
+ spin_unlock_bh(&cfr->lock);
|
||
+
|
||
+ ret = ath11k_cfr_debug_register(ar);
|
||
+ if (ret) {
|
||
+ ath11k_warn(ab, "failed to register cfr for pdev %d\n", i);
|
||
+ goto deinit;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+
|
||
+deinit:
|
||
+ ath11k_cfr_deinit(ab);
|
||
+ return ret;
|
||
+}
|
||
--- /dev/null
|
||
+++ b/drivers/net/wireless/ath/ath11k/cfr.h
|
||
@@ -0,0 +1,282 @@
|
||
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
|
||
+/*
|
||
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
||
+ */
|
||
+
|
||
+#ifndef ATH11K_CFR_H
|
||
+#define ATH11K_CFR_H
|
||
+
|
||
+#include "dbring.h"
|
||
+#include "wmi.h"
|
||
+
|
||
+#define ATH11K_CFR_NUM_RESP_PER_EVENT 1
|
||
+#define ATH11K_CFR_EVENT_TIMEOUT_MS 1
|
||
+
|
||
+#define ATH11K_CORRELATE_TX_EVENT 1
|
||
+#define ATH11K_CORRELATE_DBR_EVENT 0
|
||
+
|
||
+#define ATH11K_MAX_CFR_ENABLED_CLIENTS 10
|
||
+
|
||
+#define ATH11K_CFR_START_MAGIC 0xDEADBEAF
|
||
+#define ATH11K_CFR_END_MAGIC 0xBEAFDEAD
|
||
+
|
||
+#define ATH11K_CFR_RADIO_IPQ8074 23
|
||
+
|
||
+#define VENDOR_QCA 0x8cfdf0
|
||
+#define PLATFORM_TYPE_ARM 2
|
||
+
|
||
+enum ath11k_cfr_meta_version {
|
||
+ ATH11K_CFR_META_VERSION_NONE,
|
||
+ ATH11K_CFR_META_VERSION_1,
|
||
+ ATH11K_CFR_META_VERSION_2,
|
||
+ ATH11K_CFR_META_VERSION_3,
|
||
+ ATH11K_CFR_META_VERSION_MAX = 0xFF,
|
||
+};
|
||
+
|
||
+enum ath11k_cfr_data_version {
|
||
+ ATH11K_CFR_DATA_VERSION_NONE,
|
||
+ ATH11K_CFR_DATA_VERSION_1,
|
||
+ ATH11K_CFR_DATA_VERSION_MAX = 0xFF,
|
||
+};
|
||
+
|
||
+enum ath11k_cfr_capture_ack_mode {
|
||
+ ATH11K_CFR_CAPTURE_LEGACY_ACK,
|
||
+ ATH11K_CFR_CAPTURE_DUP_LEGACY_ACK,
|
||
+ ATH11K_CFR_CAPTURE_HT_ACK,
|
||
+ ATH11K_CFR_CPATURE_VHT_ACK,
|
||
+
|
||
+ /*Always keep this at last*/
|
||
+ ATH11K_CFR_CPATURE_INVALID_ACK
|
||
+};
|
||
+
|
||
+enum ath11k_cfr_correlate_status {
|
||
+ ATH11K_CORRELATE_STATUS_RELEASE,
|
||
+ ATH11K_CORRELATE_STATUS_HOLD,
|
||
+ ATH11K_CORRELATE_STATUS_ERR,
|
||
+};
|
||
+
|
||
+struct ath11k_cfr_peer_tx_param {
|
||
+ u32 capture_method;
|
||
+ u32 vdev_id;
|
||
+ u8 peer_mac_addr[ETH_ALEN];
|
||
+ u32 primary_20mhz_chan;
|
||
+ u32 bandwidth;
|
||
+ u32 phy_mode;
|
||
+ u32 band_center_freq1;
|
||
+ u32 band_center_freq2;
|
||
+ u32 spatial_streams;
|
||
+ u32 correlation_info_1;
|
||
+ u32 correlation_info_2;
|
||
+ u32 status;
|
||
+ u32 timestamp_us;
|
||
+ u32 counter;
|
||
+ u32 chain_rssi[WMI_MAX_CHAINS];
|
||
+ u16 chain_phase[WMI_MAX_CHAINS];
|
||
+};
|
||
+
|
||
+struct cfr_metadata_version_1 {
|
||
+ u8 peer_addr[ETH_ALEN];
|
||
+ u8 status;
|
||
+ u8 capture_bw;
|
||
+ u8 channel_bw;
|
||
+ u8 phy_mode;
|
||
+ u16 prim20_chan;
|
||
+ u16 center_freq1;
|
||
+ u16 center_freq2;
|
||
+ u8 capture_mode;
|
||
+ u8 capture_type;
|
||
+ u8 sts_count;
|
||
+ u8 num_rx_chain;
|
||
+ u32 timestamp;
|
||
+ u32 length;
|
||
+} __packed;
|
||
+
|
||
+#define HOST_MAX_CHAINS 8
|
||
+
|
||
+struct cfr_metadata_version_2 {
|
||
+ u8 peer_addr[ETH_ALEN];
|
||
+ u8 status;
|
||
+ u8 capture_bw;
|
||
+ u8 channel_bw;
|
||
+ u8 phy_mode;
|
||
+ u16 prim20_chan;
|
||
+ u16 center_freq1;
|
||
+ u16 center_freq2;
|
||
+ u8 capture_mode;
|
||
+ u8 capture_type;
|
||
+ u8 sts_count;
|
||
+ u8 num_rx_chain;
|
||
+ u32 timestamp;
|
||
+ u32 length;
|
||
+ u32 chain_rssi[HOST_MAX_CHAINS];
|
||
+ u16 chain_phase[HOST_MAX_CHAINS];
|
||
+} __packed;
|
||
+
|
||
+struct ath11k_csi_cfr_header {
|
||
+ u32 start_magic_num;
|
||
+ u32 vendorid;
|
||
+ u8 cfr_metadata_version;
|
||
+ u8 cfr_data_version;
|
||
+ u8 chip_type;
|
||
+ u8 pltform_type;
|
||
+ u32 Reserved;
|
||
+ union {
|
||
+ struct cfr_metadata_version_1 meta_v1;
|
||
+ struct cfr_metadata_version_2 meta_v2;
|
||
+ } u;
|
||
+} __packed;
|
||
+
|
||
+enum ath11k_cfr_preamble_type {
|
||
+ ATH11K_CFR_PREAMBLE_TYPE_LEGACY,
|
||
+ ATH11K_CFR_PREAMBLE_TYPE_HT,
|
||
+ ATH11K_CFR_PREAMBLE_TYPE_VHT,
|
||
+};
|
||
+
|
||
+#define TONES_IN_20MHZ 256
|
||
+#define TONES_IN_40MHZ 512
|
||
+#define TONES_IN_80MHZ 1024
|
||
+#define TONES_IN_160MHZ 2048 /* 160 MHz isn't supported yet */
|
||
+#define TONES_INVALID 0
|
||
+
|
||
+#define CFIR_DMA_HDR_INFO0_TAG GENMASK(7, 0)
|
||
+#define CFIR_DMA_HDR_INFO0_LEN GENMASK(13, 8)
|
||
+
|
||
+#define CFIR_DMA_HDR_INFO1_UPLOAD_DONE GENMASK(0, 0)
|
||
+#define CFIR_DMA_HDR_INFO1_CAPTURE_TYPE GENMASK(3, 1)
|
||
+#define CFIR_DMA_HDR_INFO1_PREABLE_TYPE GENMASK(5, 4)
|
||
+#define CFIR_DMA_HDR_INFO1_NSS GENMASK(8, 6)
|
||
+#define CFIR_DMA_HDR_INFO1_NUM_CHAINS GENMASK(11, 9)
|
||
+#define CFIR_DMA_HDR_INFO1_UPLOAD_PKT_BW GENMASK(14, 12)
|
||
+#define CFIR_DMA_HDR_INFO1_SW_PEER_ID_VALID GENMASK(15, 15)
|
||
+
|
||
+struct ath11k_cfir_dma_hdr {
|
||
+ u16 info0;
|
||
+ u16 info1;
|
||
+ u16 sw_peer_id;
|
||
+ u16 phy_ppdu_id;
|
||
+};
|
||
+
|
||
+#define CFR_MAX_LUT_ENTRIES 136
|
||
+
|
||
+struct ath11k_cfr_look_up_table {
|
||
+ bool dbr_recv;
|
||
+ bool tx_recv;
|
||
+ u8 *data;
|
||
+ u32 data_len;
|
||
+ u16 dbr_ppdu_id;
|
||
+ u16 tx_ppdu_id;
|
||
+ dma_addr_t dbr_address;
|
||
+ u32 tx_address1;
|
||
+ u32 tx_address2;
|
||
+ struct ath11k_csi_cfr_header header;
|
||
+ struct ath11k_cfir_dma_hdr hdr;
|
||
+ u64 txrx_tstamp;
|
||
+ u64 dbr_tstamp;
|
||
+ u32 header_length;
|
||
+ u32 payload_length;
|
||
+ struct ath11k_dbring_element *buff;
|
||
+};
|
||
+
|
||
+enum cfr_capture_type {
|
||
+ CFR_CAPTURE_METHOD_NULL_FRAME = 0,
|
||
+ CFR_CAPURE_METHOD_NULL_FRAME_WITH_PHASE = 1,
|
||
+ CFR_CAPTURE_METHOD_PROBE_RESP = 2,
|
||
+ CFR_CAPTURE_METHOD_TM = 3,
|
||
+ CFR_CAPTURE_METHOD_FTM = 4,
|
||
+ CFR_CAPTURE_METHOD_ACK_RESP_TO_TM_FTM = 5,
|
||
+ CFR_CAPTURE_METHOD_TA_RA_TYPE_FILTER = 6,
|
||
+ CFR_CAPTURE_METHOD_NDPA_NDP = 7,
|
||
+ CFR_CAPTURE_METHOD_ALL_PACKET = 8,
|
||
+ /* Add new capture methods before this line */
|
||
+ CFR_CAPTURE_METHOD_LAST_VALID,
|
||
+ CFR_CAPTURE_METHOD_AUTO = 0xff,
|
||
+ CFR_CAPTURE_METHOD_MAX,
|
||
+};
|
||
+
|
||
+struct cfr_unassoc_pool_entry {
|
||
+ u8 peer_mac[ETH_ALEN];
|
||
+ u32 period;
|
||
+ bool is_valid;
|
||
+};
|
||
+
|
||
+struct ath11k_cfr {
|
||
+ struct ath11k_dbring rx_ring;
|
||
+ /* Protects enabled for ath11k_cfr */
|
||
+ spinlock_t lock;
|
||
+ struct rchan *rfs_cfr_capture;
|
||
+ struct dentry *enable_cfr;
|
||
+ struct dentry *cfr_unassoc;
|
||
+ u8 cfr_enabled_peer_cnt;
|
||
+ struct ath11k_cfr_look_up_table *lut;
|
||
+ u32 lut_num;
|
||
+ u32 dbr_buf_size;
|
||
+ u32 dbr_num_bufs;
|
||
+ u32 max_mu_users;
|
||
+ /* protect look up table data */
|
||
+ spinlock_t lut_lock;
|
||
+ u64 tx_evt_cnt;
|
||
+ u64 dbr_evt_cnt;
|
||
+ u64 total_tx_evt_cnt;
|
||
+ u64 release_cnt;
|
||
+ u64 tx_peer_status_cfr_fail;
|
||
+ u64 tx_evt_status_cfr_fail;
|
||
+ u64 tx_dbr_lookup_fail;
|
||
+ u64 last_success_tstamp;
|
||
+ u64 flush_dbr_cnt;
|
||
+ u64 invalid_dma_length_cnt;
|
||
+ u64 clear_txrx_event;
|
||
+ u64 cfr_dma_aborts;
|
||
+ u64 flush_timeout_dbr_cnt;
|
||
+ struct cfr_unassoc_pool_entry unassoc_pool[ATH11K_MAX_CFR_ENABLED_CLIENTS];
|
||
+};
|
||
+
|
||
+#ifdef CPTCFG_ATH11K_CFR
|
||
+
|
||
+int ath11k_cfr_init(struct ath11k_base *ab);
|
||
+void ath11k_cfr_deinit(struct ath11k_base *ab);
|
||
+struct ath11k_dbring *ath11k_cfr_get_dbring(struct ath11k *ar);
|
||
+int ath11k_process_cfr_capture_event(struct ath11k_base *ab,
|
||
+ struct ath11k_cfr_peer_tx_param *params);
|
||
+bool peer_is_in_cfr_unassoc_pool(struct ath11k *ar, u8 *peer_mac);
|
||
+void ath11k_cfr_relase_lut_entry(struct ath11k_cfr_look_up_table *lut);
|
||
+void ath11k_cfr_lut_update_paddr(struct ath11k *ar, dma_addr_t paddr,
|
||
+ u32 buf_id);
|
||
+void ath11k_cfr_decrement_peer_count(struct ath11k *ar,
|
||
+ struct ath11k_sta *arsta);
|
||
+#else
|
||
+static inline int ath11k_cfr_init(struct ath11k_base *ab)
|
||
+{
|
||
+ return 0;
|
||
+}
|
||
+static inline void ath11k_cfr_deinit(struct ath11k_base *ab)
|
||
+{
|
||
+}
|
||
+static inline
|
||
+struct ath11k_dbring *ath11k_cfr_get_dbring(struct ath11k *ar)
|
||
+{
|
||
+ return NULL;
|
||
+}
|
||
+static inline bool peer_is_in_cfr_unassoc_pool(struct ath11k *ar, u8 *peer_mac)
|
||
+{
|
||
+ return false;
|
||
+}
|
||
+static inline
|
||
+void ath11k_cfr_relase_lut_entry(struct ath11k_cfr_look_up_table *lut)
|
||
+{
|
||
+}
|
||
+static inline
|
||
+int ath11k_process_cfr_capture_event(struct ath11k_base *ab,
|
||
+ struct ath11k_cfr_peer_tx_param *params)
|
||
+{
|
||
+ return 0;
|
||
+}
|
||
+static inline void ath11k_cfr_lut_update_paddr(struct ath11k *ar,
|
||
+ dma_addr_t paddr, u32 buf_id)
|
||
+{
|
||
+}
|
||
+static inline void ath11k_cfr_decrement_peer_count(struct ath11k *ar,
|
||
+ struct ath11k_sta *arsta)
|
||
+{
|
||
+}
|
||
+#endif /* CPTCFG_ATH11K_CFR */
|
||
+#endif /* ATH11K_CFR_H */
|
||
--- a/drivers/net/wireless/ath/ath11k/core.c
|
||
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
||
@@ -103,6 +103,11 @@ static const struct ath11k_hw_params ath
|
||
.ce_fwlog_enable = false,
|
||
.fwmem_mode_change = false,
|
||
.is_qdss_support = false,
|
||
+ .cfr_support = true,
|
||
+ .cfr_dma_hdr_size = sizeof(struct ath11k_cfir_dma_hdr),
|
||
+ .cfr_num_stream_bufs = 255,
|
||
+ /* csi_cfr_header + cfr header + max cfr payload */
|
||
+ .cfr_stream_buf_size = 8500,
|
||
},
|
||
{
|
||
.hw_rev = ATH11K_HW_IPQ6018_HW10,
|
||
@@ -843,8 +848,16 @@ static int ath11k_core_pdev_create(struc
|
||
goto err_thermal_unregister;
|
||
}
|
||
|
||
+ ret = ath11k_cfr_init(ab);
|
||
+ if (ret) {
|
||
+ ath11k_err(ab, "failed to init cfr %d\n", ret);
|
||
+ goto err_spectral_unregister;
|
||
+ }
|
||
+
|
||
return 0;
|
||
|
||
+err_spectral_unregister:
|
||
+ ath11k_spectral_deinit(ab);
|
||
err_thermal_unregister:
|
||
ath11k_thermal_unregister(ab);
|
||
err_mac_unregister:
|
||
@@ -861,6 +874,7 @@ err_pdev_debug:
|
||
|
||
static void ath11k_core_pdev_destroy(struct ath11k_base *ab)
|
||
{
|
||
+ ath11k_cfr_deinit(ab);
|
||
ath11k_spectral_deinit(ab);
|
||
ath11k_thermal_unregister(ab);
|
||
ath11k_mac_unregister(ab);
|
||
@@ -1153,6 +1167,7 @@ static int ath11k_core_reconfigure_on_cr
|
||
ath11k_hif_irq_disable(ab);
|
||
ath11k_dp_pdev_free(ab);
|
||
ath11k_spectral_deinit(ab);
|
||
+ ath11k_cfr_deinit(ab);
|
||
ath11k_hif_stop(ab);
|
||
ath11k_wmi_detach(ab);
|
||
ath11k_dp_pdev_reo_cleanup(ab);
|
||
--- a/drivers/net/wireless/ath/ath11k/core.h
|
||
+++ b/drivers/net/wireless/ath/ath11k/core.h
|
||
@@ -27,6 +27,7 @@
|
||
#include "vendor.h"
|
||
#include "rx_desc.h"
|
||
#include "nss.h"
|
||
+#include "cfr.h"
|
||
|
||
extern unsigned int ath11k_skip_radio;
|
||
|
||
@@ -401,6 +402,13 @@ struct ath11k_htt_tx_stats {
|
||
u32 mu_group[MAX_MU_GROUP_ID];
|
||
};
|
||
|
||
+struct ath11k_per_peer_cfr_capture {
|
||
+ u32 cfr_enable;
|
||
+ u32 cfr_period;
|
||
+ u32 cfr_bandwidth;
|
||
+ u32 cfr_method;
|
||
+};
|
||
+
|
||
struct ath11k_per_ppdu_tx_stats {
|
||
u16 succ_pkts;
|
||
u16 failed_pkts;
|
||
@@ -458,6 +466,9 @@ struct ath11k_sta {
|
||
u32 ps_start_jiffies;
|
||
u8 peer_current_ps_valid;
|
||
u32 ps_total_duration;
|
||
+#ifdef CPTCFG_ATH11K_CFR
|
||
+ struct ath11k_per_peer_cfr_capture cfr_capture;
|
||
+#endif
|
||
};
|
||
|
||
#define ATH11K_MIN_5G_FREQ 4150
|
||
@@ -724,6 +735,10 @@ struct ath11k {
|
||
u8 ps_state_enable;
|
||
u8 ps_timekeeper_enable;
|
||
u8 reset_ps_duration;
|
||
+#ifdef CPTCFG_ATH11K_CFR
|
||
+ struct ath11k_cfr cfr;
|
||
+#endif
|
||
+ u8 cfr_enabled;
|
||
};
|
||
|
||
struct ath11k_band_cap {
|
||
--- a/drivers/net/wireless/ath/ath11k/dbring.c
|
||
+++ b/drivers/net/wireless/ath/ath11k/dbring.c
|
||
@@ -41,11 +41,11 @@ static void ath11k_dbring_fill_magic_val
|
||
}
|
||
}
|
||
|
||
-static int ath11k_dbring_bufs_replenish(struct ath11k *ar,
|
||
- struct ath11k_dbring *ring,
|
||
- struct ath11k_dbring_element *buff,
|
||
- enum wmi_direct_buffer_module id,
|
||
- gfp_t gfp)
|
||
+int ath11k_dbring_bufs_replenish(struct ath11k *ar,
|
||
+ struct ath11k_dbring *ring,
|
||
+ struct ath11k_dbring_element *buff,
|
||
+ enum wmi_direct_buffer_module id,
|
||
+ gfp_t gfp)
|
||
{
|
||
struct ath11k_base *ab = ar->ab;
|
||
struct hal_srng *srng;
|
||
@@ -85,6 +85,9 @@ static int ath11k_dbring_bufs_replenish(
|
||
goto err_idr_remove;
|
||
}
|
||
|
||
+ if (id == WMI_DIRECT_BUF_CFR)
|
||
+ ath11k_cfr_lut_update_paddr(ar, paddr, buf_id);
|
||
+
|
||
buff->paddr = paddr;
|
||
|
||
cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, ar->pdev_idx) |
|
||
@@ -200,8 +203,7 @@ int ath11k_dbring_set_cfg(struct ath11k
|
||
|
||
int ath11k_dbring_buf_setup(struct ath11k *ar,
|
||
struct ath11k_dbring *ring,
|
||
- struct ath11k_dbring_cap *db_cap,
|
||
- enum wmi_direct_buffer_module id)
|
||
+ struct ath11k_dbring_cap *db_cap)
|
||
{
|
||
struct ath11k_base *ab = ar->ab;
|
||
struct hal_srng *srng;
|
||
@@ -217,7 +219,7 @@ int ath11k_dbring_buf_setup(struct ath11
|
||
ring->hp_addr = ath11k_hal_srng_get_hp_addr(ar->ab, srng);
|
||
ring->tp_addr = ath11k_hal_srng_get_tp_addr(ar->ab, srng);
|
||
|
||
- ret = ath11k_dbring_fill_bufs(ar, ring, id, GFP_KERNEL);
|
||
+ ret = ath11k_dbring_fill_bufs(ar, ring, db_cap->id, GFP_KERNEL);
|
||
|
||
return ret;
|
||
}
|
||
@@ -283,6 +285,7 @@ int ath11k_dbring_buffer_release_event(s
|
||
int size;
|
||
dma_addr_t paddr;
|
||
int ret = 0;
|
||
+ int status;
|
||
|
||
pdev_idx = ev->fixed.pdev_id;
|
||
module_id = ev->fixed.module_id;
|
||
@@ -312,6 +315,9 @@ int ath11k_dbring_buffer_release_event(s
|
||
case WMI_DIRECT_BUF_SPECTRAL:
|
||
ring = ath11k_spectral_get_dbring(ar);
|
||
break;
|
||
+ case WMI_DIRECT_BUF_CFR:
|
||
+ ring = ath11k_cfr_get_dbring(ar);
|
||
+ break;
|
||
default:
|
||
ring = NULL;
|
||
ath11k_warn(ab, "Recv dma buffer release ev on unsupp module %d\n",
|
||
@@ -362,8 +368,12 @@ int ath11k_dbring_buffer_release_event(s
|
||
handler_data.data = PTR_ALIGN(vaddr_unalign,
|
||
ring->buf_align);
|
||
handler_data.data_sz = ring->buf_sz;
|
||
+ handler_data.buff = buff;
|
||
+ handler_data.buf_id = buf_id;
|
||
|
||
- ring->handler(ar, &handler_data);
|
||
+ status = ring->handler(ar, &handler_data);
|
||
+ if (status == ATH11K_CORRELATE_STATUS_HOLD)
|
||
+ continue;
|
||
}
|
||
|
||
memset(buff, 0, size);
|
||
--- a/drivers/net/wireless/ath/ath11k/dbring.h
|
||
+++ b/drivers/net/wireless/ath/ath11k/dbring.h
|
||
@@ -20,6 +20,8 @@ struct ath11k_dbring_data {
|
||
void *data;
|
||
u32 data_sz;
|
||
struct wmi_dma_buf_release_meta_data meta;
|
||
+ struct ath11k_dbring_element *buff;
|
||
+ u32 buf_id;
|
||
};
|
||
|
||
struct ath11k_dbring_buf_release_event {
|
||
@@ -60,13 +62,18 @@ int ath11k_dbring_set_cfg(struct ath11k
|
||
u32 event_timeout_ms,
|
||
int (*handler)(struct ath11k *,
|
||
struct ath11k_dbring_data *));
|
||
+
|
||
+int ath11k_dbring_bufs_replenish(struct ath11k *ar,
|
||
+ struct ath11k_dbring *ring,
|
||
+ struct ath11k_dbring_element *buff,
|
||
+ enum wmi_direct_buffer_module id,
|
||
+ gfp_t gfp);
|
||
int ath11k_dbring_wmi_cfg_setup(struct ath11k *ar,
|
||
struct ath11k_dbring *ring,
|
||
enum wmi_direct_buffer_module id);
|
||
int ath11k_dbring_buf_setup(struct ath11k *ar,
|
||
struct ath11k_dbring *ring,
|
||
- struct ath11k_dbring_cap *db_cap,
|
||
- enum wmi_direct_buffer_module id);
|
||
+ struct ath11k_dbring_cap *db_cap);
|
||
int ath11k_dbring_srng_setup(struct ath11k *ar, struct ath11k_dbring *ring,
|
||
int ring_num, int num_entries);
|
||
int ath11k_dbring_buffer_release_event(struct ath11k_base *ab,
|
||
--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c
|
||
+++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c
|
||
@@ -1331,6 +1331,157 @@ static const struct file_operations fops
|
||
.llseek = default_llseek,
|
||
};
|
||
|
||
+#ifdef CPTCFG_ATH11K_CFR
|
||
+static ssize_t ath11k_dbg_sta_write_cfr_capture(struct file *file,
|
||
+ const char __user *user_buf,
|
||
+ size_t count, loff_t *ppos)
|
||
+{
|
||
+ struct ieee80211_sta *sta = file->private_data;
|
||
+ struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
|
||
+ struct ath11k *ar = arsta->arvif->ar;
|
||
+ struct ath11k_cfr *cfr = &ar->cfr;
|
||
+ struct wmi_peer_cfr_capture_conf_arg arg;
|
||
+ u32 cfr_capture_enable = 0, cfr_capture_bw = 0;
|
||
+ u32 cfr_capture_method = 0, cfr_capture_period = 0;
|
||
+ int ret;
|
||
+ char buf[64] = {0};
|
||
+
|
||
+ simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
|
||
+
|
||
+ mutex_lock(&ar->conf_mutex);
|
||
+
|
||
+ if (ar->state != ATH11K_STATE_ON) {
|
||
+ ret = -ENETDOWN;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (!ar->cfr_enabled) {
|
||
+ ret = -EINVAL;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ ret = sscanf(buf, "%u %u %u %u", &cfr_capture_enable, &cfr_capture_bw,
|
||
+ &cfr_capture_period, &cfr_capture_method);
|
||
+
|
||
+ if ((ret < 1) || (cfr_capture_enable && ret != 4)) {
|
||
+ ret = -EINVAL;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (cfr_capture_enable == arsta->cfr_capture.cfr_enable &&
|
||
+ (cfr_capture_period &&
|
||
+ cfr_capture_period == arsta->cfr_capture.cfr_period) &&
|
||
+ cfr_capture_bw == arsta->cfr_capture.cfr_bandwidth &&
|
||
+ cfr_capture_method == arsta->cfr_capture.cfr_method) {
|
||
+ ret = count;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (!cfr_capture_enable &&
|
||
+ cfr_capture_enable == arsta->cfr_capture.cfr_enable) {
|
||
+ ret = count;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (cfr_capture_enable > WMI_PEER_CFR_CAPTURE_ENABLE ||
|
||
+ cfr_capture_bw > WMI_PEER_CFR_CAPTURE_BW_80MHZ ||
|
||
+ cfr_capture_bw > sta->bandwidth ||
|
||
+ cfr_capture_method > CFR_CAPURE_METHOD_NULL_FRAME_WITH_PHASE ||
|
||
+ cfr_capture_period > WMI_PEER_CFR_PERIODICITY_MAX) {
|
||
+ ret = -EINVAL;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ /* Target expects cfr period in multiple of 10 */
|
||
+ if (cfr_capture_period % 10) {
|
||
+ ath11k_err(ar->ab, "periodicity should be 10x\n");
|
||
+ ret = -EINVAL;
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (ar->cfr.cfr_enabled_peer_cnt >= ATH11K_MAX_CFR_ENABLED_CLIENTS &&
|
||
+ !arsta->cfr_capture.cfr_enable) {
|
||
+ ret = -EINVAL;
|
||
+ ath11k_err(ar->ab, "CFR enable peer threshold reached %u\n",
|
||
+ ar->cfr.cfr_enabled_peer_cnt);
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ if (!cfr_capture_enable) {
|
||
+ cfr_capture_bw = arsta->cfr_capture.cfr_bandwidth;
|
||
+ cfr_capture_period = arsta->cfr_capture.cfr_period;
|
||
+ cfr_capture_method = arsta->cfr_capture.cfr_method;
|
||
+ }
|
||
+
|
||
+ arg.request = cfr_capture_enable;
|
||
+ arg.periodicity = cfr_capture_period;
|
||
+ arg.bandwidth = cfr_capture_bw;
|
||
+ arg.capture_method = cfr_capture_method;
|
||
+
|
||
+ ret = ath11k_wmi_peer_set_cfr_capture_conf(ar, arsta->arvif->vdev_id,
|
||
+ sta->addr, &arg);
|
||
+ if (ret) {
|
||
+ ath11k_warn(ar, "failed to send cfr capture info: vdev_id %u peer %pM\n",
|
||
+ arsta->arvif->vdev_id, sta->addr);
|
||
+ goto out;
|
||
+ }
|
||
+
|
||
+ ret = count;
|
||
+
|
||
+ spin_lock_bh(&ar->cfr.lock);
|
||
+
|
||
+ if (cfr_capture_enable &&
|
||
+ cfr_capture_enable != arsta->cfr_capture.cfr_enable)
|
||
+ cfr->cfr_enabled_peer_cnt++;
|
||
+ else if (!cfr_capture_enable)
|
||
+ cfr->cfr_enabled_peer_cnt--;
|
||
+
|
||
+ spin_unlock_bh(&ar->cfr.lock);
|
||
+
|
||
+ arsta->cfr_capture.cfr_enable = cfr_capture_enable;
|
||
+ arsta->cfr_capture.cfr_period = cfr_capture_period;
|
||
+ arsta->cfr_capture.cfr_bandwidth = cfr_capture_bw;
|
||
+ arsta->cfr_capture.cfr_method = cfr_capture_method;
|
||
+out:
|
||
+ mutex_unlock(&ar->conf_mutex);
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static ssize_t ath11k_dbg_sta_read_cfr_capture(struct file *file,
|
||
+ char __user *user_buf,
|
||
+ size_t count, loff_t *ppos)
|
||
+{
|
||
+ struct ieee80211_sta *sta = file->private_data;
|
||
+ struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
|
||
+ struct ath11k *ar = arsta->arvif->ar;
|
||
+ char buf[512] = {0};
|
||
+ int len = 0;
|
||
+
|
||
+ mutex_lock(&ar->conf_mutex);
|
||
+
|
||
+ len += scnprintf(buf + len, sizeof(buf) - len, "cfr_enabled = %d\n",
|
||
+ arsta->cfr_capture.cfr_enable);
|
||
+ len += scnprintf(buf + len, sizeof(buf) - len, "bandwidth = %d\n",
|
||
+ arsta->cfr_capture.cfr_bandwidth);
|
||
+ len += scnprintf(buf + len, sizeof(buf) - len, "period = %d\n",
|
||
+ arsta->cfr_capture.cfr_period);
|
||
+ len += scnprintf(buf + len, sizeof(buf) - len, "cfr_method = %d\n",
|
||
+ arsta->cfr_capture.cfr_method);
|
||
+
|
||
+ mutex_unlock(&ar->conf_mutex);
|
||
+
|
||
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||
+}
|
||
+
|
||
+static const struct file_operations fops_peer_cfr_capture = {
|
||
+ .write = ath11k_dbg_sta_write_cfr_capture,
|
||
+ .read = ath11k_dbg_sta_read_cfr_capture,
|
||
+ .open = simple_open,
|
||
+ .owner = THIS_MODULE,
|
||
+ .llseek = default_llseek,
|
||
+};
|
||
+#endif /* CPTCFG_ATH11K_CFR */
|
||
+
|
||
void ath11k_debugfs_sta_op_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||
struct ieee80211_sta *sta, struct dentry *dir)
|
||
{
|
||
@@ -1378,4 +1529,10 @@ void ath11k_debugfs_sta_op_add(struct ie
|
||
ar->ab->wmi_ab.svc_map))
|
||
debugfs_create_file("htt_peer_stats_reset", 0600, dir, sta,
|
||
&fops_htt_peer_stats_reset);
|
||
+#ifdef CPTCFG_ATH11K_CFR
|
||
+ if (test_bit(WMI_TLV_SERVICE_CFR_CAPTURE_SUPPORT,
|
||
+ ar->ab->wmi_ab.svc_map))
|
||
+ debugfs_create_file("cfr_capture", 0400, dir, sta,
|
||
+ &fops_peer_cfr_capture);
|
||
+#endif/* CPTCFG_ATH11K_CFR */
|
||
}
|
||
--- a/drivers/net/wireless/ath/ath11k/hal.c
|
||
+++ b/drivers/net/wireless/ath/ath11k/hal.c
|
||
@@ -181,7 +181,7 @@ static const struct hal_srng_config hw_s
|
||
},
|
||
{ /* RXDMA DIR BUF */
|
||
.start_ring_id = HAL_SRNG_RING_ID_RXDMA_DIR_BUF,
|
||
- .max_rings = 1,
|
||
+ .max_rings = 2,
|
||
.entry_size = 8 >> 2, /* TODO: Define the struct */
|
||
.lmac_ring = true,
|
||
.ring_dir = HAL_SRNG_DIR_SRC,
|
||
--- a/drivers/net/wireless/ath/ath11k/hw.h
|
||
+++ b/drivers/net/wireless/ath/ath11k/hw.h
|
||
@@ -204,6 +204,10 @@ struct ath11k_hw_params {
|
||
bool ce_fwlog_enable;
|
||
bool fwmem_mode_change;
|
||
bool is_qdss_support;
|
||
+ bool cfr_support;
|
||
+ u32 cfr_dma_hdr_size;
|
||
+ u32 cfr_num_stream_bufs;
|
||
+ u32 cfr_stream_buf_size;
|
||
};
|
||
|
||
struct ath11k_hw_ops {
|
||
--- a/drivers/net/wireless/ath/ath11k/mac.c
|
||
+++ b/drivers/net/wireless/ath/ath11k/mac.c
|
||
@@ -4796,6 +4796,8 @@ static int ath11k_mac_op_sta_state(struc
|
||
|
||
kfree(arsta->rx_stats);
|
||
arsta->rx_stats = NULL;
|
||
+
|
||
+ ath11k_cfr_decrement_peer_count(ar, arsta);
|
||
} else if (old_state == IEEE80211_STA_AUTH &&
|
||
new_state == IEEE80211_STA_ASSOC &&
|
||
(vif->type == NL80211_IFTYPE_AP ||
|
||
@@ -5727,6 +5729,7 @@ static int ath11k_mac_mgmt_tx_wmi(struct
|
||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||
struct ieee80211_tx_info *info;
|
||
dma_addr_t paddr;
|
||
+ bool tx_params_valid = false;
|
||
int buf_id;
|
||
int ret;
|
||
|
||
@@ -5766,7 +5769,13 @@ static int ath11k_mac_mgmt_tx_wmi(struct
|
||
return 0;
|
||
}
|
||
|
||
- ret = ath11k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb);
|
||
+ if (ar->cfr_enabled &&
|
||
+ ieee80211_is_probe_resp(hdr->frame_control) &&
|
||
+ peer_is_in_cfr_unassoc_pool(ar, hdr->addr1))
|
||
+ tx_params_valid = true;
|
||
+
|
||
+ ret = ath11k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb,
|
||
+ tx_params_valid);
|
||
if (ret) {
|
||
ath11k_warn(ar->ab, "failed to send mgmt frame: %d\n", ret);
|
||
goto err_unmap_buf;
|
||
--- a/drivers/net/wireless/ath/ath11k/spectral.c
|
||
+++ b/drivers/net/wireless/ath/ath11k/spectral.c
|
||
@@ -817,7 +817,7 @@ static int ath11k_spectral_ring_alloc(st
|
||
ATH11K_SPECTRAL_EVENT_TIMEOUT_MS,
|
||
ath11k_spectral_process_data);
|
||
|
||
- ret = ath11k_dbring_buf_setup(ar, &sp->rx_ring, db_cap, WMI_DIRECT_BUF_SPECTRAL);
|
||
+ ret = ath11k_dbring_buf_setup(ar, &sp->rx_ring, db_cap);
|
||
if (ret) {
|
||
ath11k_warn(ar->ab, "failed to setup db ring buffer\n");
|
||
goto srng_cleanup;
|
||
--- a/drivers/net/wireless/ath/ath11k/wmi.c
|
||
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
|
||
@@ -612,10 +612,11 @@ struct sk_buff *ath11k_wmi_alloc_skb(str
|
||
}
|
||
|
||
int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id,
|
||
- struct sk_buff *frame)
|
||
+ struct sk_buff *frame, bool tx_params_valid)
|
||
{
|
||
struct ath11k_pdev_wmi *wmi = ar->wmi;
|
||
struct wmi_mgmt_send_cmd *cmd;
|
||
+ struct wmi_tx_send_params *params;
|
||
struct wmi_tlv *frame_tlv;
|
||
struct sk_buff *skb;
|
||
u32 buf_len;
|
||
@@ -626,6 +627,9 @@ int ath11k_wmi_mgmt_send(struct ath11k *
|
||
|
||
len = sizeof(*cmd) + sizeof(*frame_tlv) + roundup(buf_len, 4);
|
||
|
||
+ if (tx_params_valid)
|
||
+ len += sizeof(*params);
|
||
+
|
||
skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
|
||
if (!skb)
|
||
return -ENOMEM;
|
||
@@ -640,7 +644,7 @@ int ath11k_wmi_mgmt_send(struct ath11k *
|
||
cmd->paddr_hi = upper_32_bits(ATH11K_SKB_CB(frame)->paddr);
|
||
cmd->frame_len = frame->len;
|
||
cmd->buf_len = buf_len;
|
||
- cmd->tx_params_valid = 0;
|
||
+ cmd->tx_params_valid = tx_params_valid;
|
||
|
||
frame_tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd));
|
||
frame_tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
|
||
@@ -650,6 +654,13 @@ int ath11k_wmi_mgmt_send(struct ath11k *
|
||
|
||
ath11k_ce_byte_swap(frame_tlv->value, buf_len);
|
||
|
||
+ if (tx_params_valid) {
|
||
+ params = (struct wmi_tx_send_params *)(skb->data + (len - sizeof(*params)));
|
||
+ params->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TX_SEND_PARAMS) |
|
||
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*params) - TLV_HDR_SIZE);
|
||
+ params->tx_params_dword1 |= WMI_TX_PARAMS_DWORD1_CFR_CAPTURE;
|
||
+ }
|
||
+
|
||
ret = ath11k_wmi_cmd_send(wmi, skb, WMI_MGMT_TX_SEND_CMDID);
|
||
if (ret) {
|
||
ath11k_warn(ar->ab,
|
||
@@ -4053,6 +4064,42 @@ int ath11k_wmi_fils_discovery_tmpl(struc
|
||
return ret;
|
||
}
|
||
|
||
+int ath11k_wmi_peer_set_cfr_capture_conf(struct ath11k *ar,
|
||
+ u32 vdev_id, const u8 *mac_addr,
|
||
+ struct wmi_peer_cfr_capture_conf_arg *arg)
|
||
+{
|
||
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
|
||
+ struct wmi_peer_cfr_capture_cmd_fixed_param *cmd;
|
||
+ struct sk_buff *skb;
|
||
+ int ret;
|
||
+
|
||
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd));
|
||
+ if (!skb)
|
||
+ return -ENOMEM;
|
||
+
|
||
+ cmd = (struct wmi_peer_cfr_capture_cmd_fixed_param *)skb->data;
|
||
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
|
||
+ WMI_TAG_PEER_CFR_CAPTURE_CMD) |
|
||
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
|
||
+
|
||
+ ether_addr_copy(cmd->mac_addr.addr, mac_addr);
|
||
+ cmd->request = arg->request;
|
||
+ cmd->vdev_id = vdev_id;
|
||
+ cmd->periodicity = arg->periodicity;
|
||
+ cmd->bandwidth = arg->bandwidth;
|
||
+ cmd->capture_method = arg->capture_method;
|
||
+
|
||
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_PEER_CFR_CAPTURE_CMDID);
|
||
+ if (ret) {
|
||
+ ath11k_warn(ar->ab,
|
||
+ "WMI vdev %d failed to send peer cfr capture cmd\n",
|
||
+ vdev_id);
|
||
+ dev_kfree_skb(skb);
|
||
+ }
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
int ath11k_wmi_probe_resp_tmpl(struct ath11k *ar, u32 vdev_id,
|
||
struct sk_buff *tmpl)
|
||
{
|
||
@@ -8928,6 +8975,89 @@ static void ath11k_wmi_diag_event(struct
|
||
ath11k_fwlog_write(ab,data, tlv_len);
|
||
}
|
||
|
||
+static void ath11k_wmi_tlv_cfr_cpature_event_fixed_param(const void *ptr,
|
||
+ void *data)
|
||
+{
|
||
+ struct ath11k_cfr_peer_tx_param *tx_params =
|
||
+ (struct ath11k_cfr_peer_tx_param *)data;
|
||
+ struct ath11k_wmi_cfr_peer_tx_event_param *params =
|
||
+ (struct ath11k_wmi_cfr_peer_tx_event_param *)ptr;
|
||
+
|
||
+ tx_params->capture_method = params->capture_method;
|
||
+ tx_params->vdev_id = params->vdev_id;
|
||
+ ether_addr_copy(tx_params->peer_mac_addr, params->mac_addr.addr);
|
||
+ tx_params->primary_20mhz_chan = params->chan_mhz;
|
||
+ tx_params->bandwidth = params->bandwidth;
|
||
+ tx_params->phy_mode = params->phy_mode;
|
||
+ tx_params->band_center_freq1 = params->band_center_freq1;
|
||
+ tx_params->band_center_freq2 = params->band_center_freq2;
|
||
+ tx_params->spatial_streams = params->sts_count;
|
||
+ tx_params->correlation_info_1 = params->correlation_info_1;
|
||
+ tx_params->correlation_info_2 = params->correlation_info_2;
|
||
+ tx_params->status = params->status;
|
||
+ tx_params->timestamp_us = params->timestamp_us;
|
||
+ tx_params->counter = params->counter;
|
||
+ memcpy(tx_params->chain_rssi, params->chain_rssi,
|
||
+ sizeof(tx_params->chain_rssi));
|
||
+}
|
||
+
|
||
+static void ath11k_wmi_tlv_cfr_cpature_phase_fixed_param(const void *ptr,
|
||
+ void *data)
|
||
+{
|
||
+ struct ath11k_cfr_peer_tx_param *tx_params =
|
||
+ (struct ath11k_cfr_peer_tx_param *)data;
|
||
+ struct ath11k_wmi_cfr_peer_tx_event_phase_param *params =
|
||
+ (struct ath11k_wmi_cfr_peer_tx_event_phase_param *)ptr;
|
||
+ int i;
|
||
+
|
||
+ for (i = 0; i < WMI_MAX_CHAINS; i++)
|
||
+ tx_params->chain_phase[i] = params->chain_phase[i];
|
||
+}
|
||
+
|
||
+static int ath11k_wmi_tlv_cfr_capture_evt_parse(struct ath11k_base *ab,
|
||
+ u16 tag, u16 len,
|
||
+ const void *ptr, void *data)
|
||
+{
|
||
+ switch (tag) {
|
||
+ case WMI_TAG_PEER_CFR_CAPTURE_EVENT:
|
||
+ ath11k_wmi_tlv_cfr_cpature_event_fixed_param(ptr, data);
|
||
+ break;
|
||
+ case WMI_TAG_CFR_CAPTURE_PHASE_PARAM:
|
||
+ ath11k_wmi_tlv_cfr_cpature_phase_fixed_param(ptr, data);
|
||
+ break;
|
||
+ default:
|
||
+ ath11k_warn(ab, "Invalid tag received tag %d len %d\n",
|
||
+ tag, len);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static void ath11k_wmi_parse_cfr_capture_event(struct ath11k_base *ab,
|
||
+ struct sk_buff *skb)
|
||
+{
|
||
+ struct ath11k_cfr_peer_tx_param params = {};
|
||
+ int ret;
|
||
+
|
||
+ ath11k_dbg_dump(ab, ATH11K_DBG_CFR_DUMP, "cfr_dump:", "",
|
||
+ skb->data, skb->len);
|
||
+
|
||
+ ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len,
|
||
+ ath11k_wmi_tlv_cfr_capture_evt_parse,
|
||
+ ¶ms);
|
||
+ if (ret) {
|
||
+ ath11k_warn(ab, "failed to parse cfr capture event tlv %d\n",
|
||
+ ret);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ ret = ath11k_process_cfr_capture_event(ab, ¶ms);
|
||
+ if (ret)
|
||
+ ath11k_dbg(ab, ATH11K_DBG_CFR,
|
||
+ "failed to process cfr cpature ret = %d\n", ret);
|
||
+}
|
||
+
|
||
static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
|
||
{
|
||
struct wmi_cmd_hdr *cmd_hdr;
|
||
@@ -9077,6 +9207,9 @@ static void ath11k_wmi_tlv_op_rx(struct
|
||
case WMI_CTRL_PATH_STATS_EVENTID:
|
||
ath11k_wmi_ctrl_path_stats_event(ab, skb);
|
||
break;
|
||
+ case WMI_PEER_CFR_CAPTURE_EVENTID:
|
||
+ ath11k_wmi_parse_cfr_capture_event(ab, skb);
|
||
+ break;
|
||
/* TODO: Add remaining events */
|
||
default:
|
||
ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
|
||
--- a/drivers/net/wireless/ath/ath11k/wmi.h
|
||
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
|
||
@@ -339,6 +339,10 @@ enum wmi_tlv_cmd_id {
|
||
WMI_PEER_REORDER_QUEUE_REMOVE_CMDID,
|
||
WMI_PEER_SET_RX_BLOCKSIZE_CMDID,
|
||
WMI_PEER_ANTDIV_INFO_REQ_CMDID,
|
||
+ WMI_PEER_RESERVED0_CMDID,
|
||
+ WMI_PEER_TID_MSDUQ_QDEPTH_THRESH_UPDATE_CMDID,
|
||
+ WMI_PEER_TID_CONFIGURATIONS_CMDID,
|
||
+ WMI_PEER_CFR_CAPTURE_CMDID,
|
||
WMI_BCN_TX_CMDID = WMI_TLV_CMD(WMI_GRP_MGMT),
|
||
WMI_PDEV_SEND_BCN_CMDID,
|
||
WMI_BCN_TMPL_CMDID,
|
||
@@ -966,6 +970,7 @@ enum wmi_tlv_pdev_param {
|
||
WMI_PDEV_PARAM_RADIO_CHAN_STATS_ENABLE,
|
||
WMI_PDEV_PARAM_RADIO_DIAGNOSIS_ENABLE,
|
||
WMI_PDEV_PARAM_MESH_MCAST_ENABLE,
|
||
+ WMI_PDEV_PARAM_PER_PEER_CFR_ENABLE = 0xa8,
|
||
WMI_PDEV_PARAM_SET_CMD_OBSS_PD_THRESHOLD = 0xbc,
|
||
WMI_PDEV_PARAM_SET_CMD_OBSS_PD_PER_AC = 0xbe,
|
||
WMI_PDEV_PARAM_ENABLE_SR_PROHIBIT = 0xc6,
|
||
@@ -1869,7 +1874,9 @@ enum wmi_tlv_tag {
|
||
WMI_TAG_NDP_EVENT,
|
||
WMI_TAG_PDEV_PEER_PKTLOG_FILTER_CMD = 0x301,
|
||
WMI_TAG_PDEV_PEER_PKTLOG_FILTER_INFO,
|
||
+ WMI_TAG_PEER_CFR_CAPTURE_EVENT = 0x317,
|
||
WMI_TAG_MUEDCA_PARAMS_CONFIG_EVENT = 0x32a,
|
||
+ WMI_TAG_CFR_CAPTURE_PHASE_PARAM = 0x33b,
|
||
WMI_TAG_FILS_DISCOVERY_TMPL_CMD = 0x344,
|
||
WMI_TAG_PDEV_SRG_BSS_COLOR_BITMAP_CMD = 0x37b,
|
||
WMI_TAG_PDEV_SRG_PARTIAL_BSSID_BITMAP_CMD,
|
||
@@ -3494,6 +3501,85 @@ struct wmi_bssid_arg {
|
||
const u8 *bssid;
|
||
};
|
||
|
||
+enum ath11k_wmi_frame_tx_status {
|
||
+ WMI_FRAME_TX_STATUS_OK,
|
||
+ WMI_FRAME_TX_STATUS_XRETRY,
|
||
+ WMI_FRAME_TX_STATUS_DROP,
|
||
+ WMI_FRAME_TX_STATUS_FILTERED,
|
||
+};
|
||
+
|
||
+struct wmi_peer_cfr_capture_conf_arg {
|
||
+ u32 request;
|
||
+ u32 periodicity;
|
||
+ u32 bandwidth;
|
||
+ u32 capture_method;
|
||
+};
|
||
+
|
||
+struct wmi_peer_cfr_capture_cmd_fixed_param {
|
||
+ u32 tlv_header;
|
||
+ u32 request;
|
||
+ struct wmi_mac_addr mac_addr;
|
||
+ u32 vdev_id;
|
||
+ u32 periodicity;
|
||
+ u32 bandwidth;
|
||
+ u32 capture_method;
|
||
+} __packed;
|
||
+
|
||
+#define WMI_PEER_CFR_CAPTURE_ENABLE 1
|
||
+#define WMI_PEER_CFR_CAPTURE_DISABLE 0
|
||
+
|
||
+/* periodicity in ms */
|
||
+#define WMI_PEER_CFR_PERIODICITY_MAX (10*60*1000)
|
||
+
|
||
+#define WMI_CFR_FRAME_TX_STATUS GENMASK(1, 0)
|
||
+#define WMI_CFR_CAPTURE_STATUS_PEER_PS BIT(30)
|
||
+#define WMI_CFR_PEER_CAPTURE_STATUS BIT(31)
|
||
+
|
||
+#define WMI_CFR_CORRELATION_INFO2_BUF_ADDR_HIGH GENMASK(3, 0)
|
||
+#define WMI_CFR_CORRELATION_INFO2_PPDU_ID GENMASK(31, 16)
|
||
+
|
||
+#define WMI_CFR_CFO_MEASUREMENT_VALID GENMASK(0, 0)
|
||
+#define WMI_CFR_CFO_MEASUREMENT_RAW_DATA GENMASK(14, 1)
|
||
+
|
||
+struct ath11k_wmi_cfr_peer_tx_event_param {
|
||
+ u32 capture_method;
|
||
+ u32 vdev_id;
|
||
+ struct wmi_mac_addr mac_addr;
|
||
+ u32 chan_mhz;
|
||
+ u32 bandwidth;
|
||
+ u32 phy_mode;
|
||
+ u32 band_center_freq1;
|
||
+ u32 band_center_freq2;
|
||
+ u32 sts_count;
|
||
+ u32 correlation_info_1;
|
||
+ u32 correlation_info_2;
|
||
+ u32 status;
|
||
+ u32 timestamp_us;
|
||
+ u32 counter;
|
||
+ u32 chain_rssi[WMI_MAX_CHAINS];
|
||
+ u32 cfo_measurement;
|
||
+} __packed;
|
||
+
|
||
+struct ath11k_wmi_cfr_peer_tx_event_phase_param {
|
||
+ u32 chain_phase[WMI_MAX_CHAINS];
|
||
+} __packed;
|
||
+
|
||
+enum ath11k_wmi_cfr_capture_bw {
|
||
+ WMI_PEER_CFR_CAPTURE_BW_20MHZ,
|
||
+ WMI_PEER_CFR_CAPTURE_BW_40MHZ,
|
||
+ WMI_PEER_CFR_CAPTURE_BW_80MHZ,
|
||
+ WMI_PEER_CFR_CAPTURE_BW_160MHZ,
|
||
+ WMI_PEER_CFR_CAPTURE_BW_80_80MHZ,
|
||
+ WMI_PEER_CFR_CAPTURE_BW_MAX,
|
||
+};
|
||
+
|
||
+struct ath11k_wmi_peer_cfr_capture_conf {
|
||
+ u32 request;
|
||
+ u32 periodicity;
|
||
+ u32 bandwidth;
|
||
+ u32 capture_method;
|
||
+};
|
||
+
|
||
struct wmi_start_scan_arg {
|
||
u32 scan_id;
|
||
u32 scan_req_id;
|
||
@@ -6334,7 +6420,7 @@ int ath11k_wmi_cmd_send(struct ath11k_pd
|
||
u32 cmd_id);
|
||
struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len);
|
||
int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id,
|
||
- struct sk_buff *frame);
|
||
+ struct sk_buff *frame, bool tx_params_valid);
|
||
int ath11k_wmi_qos_null_send(struct ath11k *ar, u32 vdev_id, u32 buf_id,
|
||
struct sk_buff *frame);
|
||
int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id,
|
||
@@ -6496,4 +6582,7 @@ int ath11k_wmi_send_aggr_size_cmd(struct
|
||
int ath11k_wmi_send_wmi_ctrl_stats_cmd(struct ath11k *ar,
|
||
struct wmi_ctrl_path_stats_cmd_param *param);
|
||
int ath11k_wmi_dbglog_cfg(struct ath11k *ar, u32 param, u64 value);
|
||
+int ath11k_wmi_peer_set_cfr_capture_conf(struct ath11k *ar,
|
||
+ u32 vdev_id, const u8 *mac,
|
||
+ struct wmi_peer_cfr_capture_conf_arg *arg);
|
||
#endif
|
||
--- a/local-symbols
|
||
+++ b/local-symbols
|
||
@@ -140,3 +140,4 @@ ATH11K_DEBUGFS=
|
||
ATH11K_TRACING=
|
||
ATH11K_SPECTRAL=
|
||
ATH11K_PKTLOG=
|
||
+ATH11K_CFR=
|
||
--- a/drivers/net/wireless/ath/ath11k/debug.h
|
||
+++ b/drivers/net/wireless/ath/ath11k/debug.h
|
||
@@ -25,6 +25,8 @@ enum ath11k_debug_mask {
|
||
ATH11K_DBG_PCI = 0x00001000,
|
||
ATH11K_DBG_DP_TX = 0x00001000,
|
||
ATH11K_DBG_DP_RX = 0x00002000,
|
||
+ ATH11K_DBG_CFR = 0x00008000,
|
||
+ ATH11K_DBG_CFR_DUMP = 0x00010000,
|
||
ATH11K_DBG_ANY = 0xffffffff,
|
||
};
|
||
|