summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath/Kconfig4
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c4
-rw-r--r--drivers/net/wireless/ath/ath11k/Kconfig9
-rw-r--r--drivers/net/wireless/ath/ath11k/Makefile4
-rw-r--r--drivers/net/wireless/ath/ath11k/core.c10
-rw-r--r--drivers/net/wireless/ath/ath11k/core.h52
-rw-r--r--drivers/net/wireless/ath/ath11k/dbring.c356
-rw-r--r--drivers/net/wireless/ath/ath11k/dbring.h79
-rw-r--r--drivers/net/wireless/ath/ath11k/debug.c128
-rw-r--r--drivers/net/wireless/ath/ath11k/dp.c3
-rw-r--r--drivers/net/wireless/ath/ath11k/dp.h42
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_rx.c36
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_tx.c7
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.c220
-rw-r--r--drivers/net/wireless/ath/ath11k/reg.c4
-rw-r--r--drivers/net/wireless/ath/ath11k/spectral.c1023
-rw-r--r--drivers/net/wireless/ath/ath11k/spectral.h82
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.c690
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.h184
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig5
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/Kconfig2
-rw-r--r--drivers/net/wireless/ath/carl9170/usb.c2
-rw-r--r--drivers/net/wireless/ath/spectral_common.h17
-rw-r--r--drivers/net/wireless/ath/wil6210/Kconfig2
25 files changed, 2843 insertions, 124 deletions
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
index 6e9d46b96555..d88edbf1bea3 100644
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -15,11 +15,11 @@ config WLAN_VENDOR_ATH
For more information and documentation on this module you can visit:
- http://wireless.kernel.org/en/users/Drivers/ath
+ https://wireless.wiki.kernel.org/en/users/Drivers/ath
For information on all Atheros wireless drivers visit:
- http://wireless.kernel.org/en/users/Drivers/Atheros
+ https://wireless.wiki.kernel.org/en/users/Drivers/Atheros
if WLAN_VENDOR_ATH
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 4fd10ac3a941..bbe869575855 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -1591,7 +1591,9 @@ static int ath10k_htt_tx_32(struct ath10k_htt *htt,
err_unmap_msdu:
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
err_free_msdu_id:
+ spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+ spin_unlock_bh(&htt->tx_lock);
err:
return res;
}
@@ -1798,7 +1800,9 @@ static int ath10k_htt_tx_64(struct ath10k_htt *htt,
err_unmap_msdu:
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
err_free_msdu_id:
+ spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
+ spin_unlock_bh(&htt->tx_lock);
err:
return res;
}
diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig
index 7acb42e2e6e2..88a97356f0a1 100644
--- a/drivers/net/wireless/ath/ath11k/Kconfig
+++ b/drivers/net/wireless/ath/ath11k/Kconfig
@@ -34,3 +34,12 @@ config ATH11K_TRACING
depends on ATH11K && EVENT_TRACING
help
Select this to use ath11k tracing infrastructure.
+
+config ATH11K_SPECTRAL
+ bool "QCA ath11k spectral scan support"
+ depends on ATH11K_DEBUGFS
+ depends on RELAY
+ help
+ Enable ath11k spectral scan support
+
+ Say Y to enable access to the FFT/spectral data via debugfs.
diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile
index fe7736e53583..104186373c9e 100644
--- a/drivers/net/wireless/ath/ath11k/Makefile
+++ b/drivers/net/wireless/ath/ath11k/Makefile
@@ -15,12 +15,14 @@ ath11k-y += core.o \
dp_rx.o \
debug.o \
ce.o \
- peer.o
+ peer.o \
+ dbring.o
ath11k-$(CONFIG_ATH11K_DEBUGFS) += debug_htt_stats.o debugfs_sta.o
ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o
ath11k-$(CONFIG_ATH11K_TRACING) += trace.o
ath11k-$(CONFIG_THERMAL) += thermal.o
+ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 02501cc154fe..905cd8beaf28 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -400,8 +400,16 @@ static int ath11k_core_pdev_create(struct ath11k_base *ab)
goto err_dp_pdev_free;
}
+ ret = ath11k_spectral_init(ab);
+ if (ret) {
+ ath11k_err(ab, "failed to init spectral %d\n", ret);
+ goto err_thermal_unregister;
+ }
+
return 0;
+err_thermal_unregister:
+ ath11k_thermal_unregister(ab);
err_dp_pdev_free:
ath11k_dp_pdev_free(ab);
err_mac_unregister:
@@ -414,6 +422,7 @@ err_pdev_debug:
static void ath11k_core_pdev_destroy(struct ath11k_base *ab)
{
+ ath11k_spectral_deinit(ab);
ath11k_thermal_unregister(ab);
ath11k_mac_unregister(ab);
ath11k_hif_irq_disable(ab);
@@ -582,6 +591,7 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab)
ath11k_thermal_unregister(ab);
ath11k_hif_irq_disable(ab);
ath11k_dp_pdev_free(ab);
+ ath11k_spectral_deinit(ab);
ath11k_hif_stop(ab);
ath11k_wmi_detach(ab);
ath11k_dp_pdev_reo_cleanup(ab);
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index e04f0e711779..e5c4e19020ee 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -21,6 +21,8 @@
#include "hal_rx.h"
#include "reg.h"
#include "thermal.h"
+#include "dbring.h"
+#include "spectral.h"
#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
@@ -215,12 +217,15 @@ struct ath11k_vif {
bool is_started;
bool is_up;
+ bool spectral_enabled;
u32 aid;
u8 bssid[ETH_ALEN];
struct cfg80211_bitrate_mask bitrate_mask;
int num_legacy_stations;
int rtscts_prot_mode;
int txpower;
+ bool rsnie_present;
+ bool wpaie_present;
};
struct ath11k_vif_iter {
@@ -353,7 +358,10 @@ struct ath11k_sta {
#endif
};
-#define ATH11K_NUM_CHANS 41
+#define ATH11K_MIN_5G_FREQ 4150
+#define ATH11K_MIN_6G_FREQ 5945
+#define ATH11K_MAX_6G_FREQ 7115
+#define ATH11K_NUM_CHANS 100
#define ATH11K_MAX_5G_CHAN 173
enum ath11k_state {
@@ -431,6 +439,7 @@ struct ath11k {
u32 vht_cap_info;
struct ath11k_he ar_he;
enum ath11k_state state;
+ bool supports_6ghz;
struct {
struct completion started;
struct completion completed;
@@ -537,6 +546,9 @@ struct ath11k {
#ifdef CONFIG_ATH11K_DEBUGFS
struct ath11k_debug debug;
#endif
+#ifdef CONFIG_ATH11K_SPECTRAL
+ struct ath11k_spectral spectral;
+#endif
bool dfs_block_radar_events;
struct ath11k_thermal thermal;
};
@@ -548,6 +560,7 @@ struct ath11k_band_cap {
u32 he_mcs;
u32 he_cap_phy_info[PSOC_HOST_MAX_PHY_SIZE];
struct ath11k_ppe_threshold he_ppet;
+ u16 he_6ghz_capa;
};
struct ath11k_pdev_cap {
@@ -579,12 +592,42 @@ struct ath11k_board_data {
/* IPQ8074 HW channel counters frequency value in hertz */
#define IPQ8074_CC_FREQ_HERTZ 320000
-struct ath11k_soc_dp_rx_stats {
+struct ath11k_bp_stats {
+ /* Head Pointer reported by the last HTT Backpressure event for the ring */
+ u16 hp;
+
+ /* Tail Pointer reported by the last HTT Backpressure event for the ring */
+ u16 tp;
+
+ /* Number of Backpressure events received for the ring */
+ u32 count;
+
+ /* Last recorded event timestamp */
+ unsigned long jiffies;
+};
+
+struct ath11k_dp_ring_bp_stats {
+ struct ath11k_bp_stats umac_ring_bp_stats[HTT_SW_UMAC_RING_IDX_MAX];
+ struct ath11k_bp_stats lmac_ring_bp_stats[HTT_SW_LMAC_RING_IDX_MAX][MAX_RADIOS];
+};
+
+struct ath11k_soc_dp_tx_err_stats {
+ /* TCL Ring Descriptor unavailable */
+ u32 desc_na[DP_TCL_NUM_RING_MAX];
+ /* Other failures during dp_tx due to mem allocation failure
+ * idr unavailable etc.
+ */
+ atomic_t misc_fail;
+};
+
+struct ath11k_soc_dp_stats {
u32 err_ring_pkts;
u32 invalid_rbm;
u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX];
u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX];
u32 hal_reo_error[DP_REO_DST_RING_MAX];
+ struct ath11k_soc_dp_tx_err_stats tx_err;
+ struct ath11k_dp_ring_bp_stats bp_stats;
};
/* Master structure to hold the hw data which may be used in core module */
@@ -653,7 +696,7 @@ struct ath11k_base {
struct dentry *debugfs_soc;
struct dentry *debugfs_ath11k;
#endif
- struct ath11k_soc_dp_rx_stats soc_stats;
+ struct ath11k_soc_dp_stats soc_stats;
unsigned long dev_flags;
struct completion driver_recovery;
@@ -668,6 +711,9 @@ struct ath11k_base {
/* Round robbin based TCL ring selector */
atomic_t tcl_ring_selector;
+ struct ath11k_dbring_cap *db_caps;
+ u32 num_db_cap;
+
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
diff --git a/drivers/net/wireless/ath/ath11k/dbring.c b/drivers/net/wireless/ath/ath11k/dbring.c
new file mode 100644
index 000000000000..cf20db370123
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/dbring.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ */
+
+#include "core.h"
+#include "debug.h"
+
+static int ath11k_dbring_bufs_replenish(struct ath11k *ar,
+ struct ath11k_dbring *ring,
+ struct ath11k_dbring_element *buff,
+ gfp_t gfp)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct hal_srng *srng;
+ dma_addr_t paddr;
+ void *ptr_aligned, *ptr_unaligned, *desc;
+ int ret;
+ int buf_id;
+ u32 cookie;
+
+ srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
+
+ lockdep_assert_held(&srng->lock);
+
+ ath11k_hal_srng_access_begin(ab, srng);
+
+ ptr_unaligned = buff->payload;
+ ptr_aligned = PTR_ALIGN(ptr_unaligned, ring->buf_align);
+ paddr = dma_map_single(ab->dev, ptr_aligned, ring->buf_sz,
+ DMA_FROM_DEVICE);
+
+ ret = dma_mapping_error(ab->dev, paddr);
+ if (ret)
+ goto err;
+
+ spin_lock_bh(&ring->idr_lock);
+ buf_id = idr_alloc(&ring->bufs_idr, buff, 0, ring->bufs_max, gfp);
+ spin_unlock_bh(&ring->idr_lock);
+ if (buf_id < 0) {
+ ret = -ENOBUFS;
+ goto err_dma_unmap;
+ }
+
+ desc = ath11k_hal_srng_src_get_next_entry(ab, srng);
+ if (!desc) {
+ ret = -ENOENT;
+ goto err_idr_remove;
+ }
+
+ buff->paddr = paddr;
+
+ cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, ar->pdev_idx) |
+ FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id);
+
+ ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, 0);
+
+ ath11k_hal_srng_access_end(ab, srng);
+
+ return 0;
+
+err_idr_remove:
+ spin_lock_bh(&ring->idr_lock);
+ idr_remove(&ring->bufs_idr, buf_id);
+ spin_unlock_bh(&ring->idr_lock);
+err_dma_unmap:
+ dma_unmap_single(ab->dev, paddr, ring->buf_sz,
+ DMA_FROM_DEVICE);
+err:
+ ath11k_hal_srng_access_end(ab, srng);
+ return ret;
+}
+
+static int ath11k_dbring_fill_bufs(struct ath11k *ar,
+ struct ath11k_dbring *ring,
+ gfp_t gfp)
+{
+ struct ath11k_dbring_element *buff;
+ struct hal_srng *srng;
+ int num_remain, req_entries, num_free;
+ u32 align;
+ int size, ret;
+
+ srng = &ar->ab->hal.srng_list[ring->refill_srng.ring_id];
+
+ spin_lock_bh(&srng->lock);
+
+ num_free = ath11k_hal_srng_src_num_free(ar->ab, srng, true);
+ req_entries = min(num_free, ring->bufs_max);
+ num_remain = req_entries;
+ align = ring->buf_align;
+ size = sizeof(*buff) + ring->buf_sz + align - 1;
+
+ while (num_remain > 0) {
+ buff = kzalloc(size, gfp);
+ if (!buff)
+ break;
+
+ ret = ath11k_dbring_bufs_replenish(ar, ring, buff, gfp);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to replenish db ring num_remain %d req_ent %d\n",
+ num_remain, req_entries);
+ kfree(buff);
+ break;
+ }
+ num_remain--;
+ }
+
+ spin_unlock_bh(&srng->lock);
+
+ return num_remain;
+}
+
+int ath11k_dbring_wmi_cfg_setup(struct ath11k *ar,
+ struct ath11k_dbring *ring,
+ enum wmi_direct_buffer_module id)
+{
+ struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd param = {0};
+ int ret;
+
+ if (id >= WMI_DIRECT_BUF_MAX)
+ return -EINVAL;
+
+ param.pdev_id = DP_SW2HW_MACID(ring->pdev_id);
+ param.module_id = id;
+ param.base_paddr_lo = lower_32_bits(ring->refill_srng.paddr);
+ param.base_paddr_hi = upper_32_bits(ring->refill_srng.paddr);
+ param.head_idx_paddr_lo = lower_32_bits(ring->hp_addr);
+ param.head_idx_paddr_hi = upper_32_bits(ring->hp_addr);
+ param.tail_idx_paddr_lo = lower_32_bits(ring->tp_addr);
+ param.tail_idx_paddr_hi = upper_32_bits(ring->tp_addr);
+ param.num_elems = ring->bufs_max;
+ param.buf_size = ring->buf_sz;
+ param.num_resp_per_event = ring->num_resp_per_event;
+ param.event_timeout_ms = ring->event_timeout_ms;
+
+ ret = ath11k_wmi_pdev_dma_ring_cfg(ar, &param);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to setup db ring cfg\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int ath11k_dbring_set_cfg(struct ath11k *ar, struct ath11k_dbring *ring,
+ u32 num_resp_per_event, u32 event_timeout_ms,
+ int (*handler)(struct ath11k *,
+ struct ath11k_dbring_data *))
+{
+ if (WARN_ON(!ring))
+ return -EINVAL;
+
+ ring->num_resp_per_event = num_resp_per_event;
+ ring->event_timeout_ms = event_timeout_ms;
+ ring->handler = handler;
+
+ return 0;
+}
+
+int ath11k_dbring_buf_setup(struct ath11k *ar,
+ struct ath11k_dbring *ring,
+ struct ath11k_dbring_cap *db_cap)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct hal_srng *srng;
+ int ret;
+
+ srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
+ ring->bufs_max = ring->refill_srng.size /
+ ath11k_hal_srng_get_entrysize(HAL_RXDMA_DIR_BUF);
+
+ ring->buf_sz = db_cap->min_buf_sz;
+ ring->buf_align = db_cap->min_buf_align;
+ ring->pdev_id = db_cap->pdev_id;
+ 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, GFP_KERNEL);
+
+ return ret;
+}
+
+int ath11k_dbring_srng_setup(struct ath11k *ar, struct ath11k_dbring *ring,
+ int ring_num, int num_entries)
+{
+ int ret;
+
+ ret = ath11k_dp_srng_setup(ar->ab, &ring->refill_srng, HAL_RXDMA_DIR_BUF,
+ ring_num, ar->pdev_idx, num_entries);
+ if (ret < 0) {
+ ath11k_warn(ar->ab, "failed to setup srng: %d ring_id %d\n",
+ ret, ring_num);
+ goto err;
+ }
+
+ return 0;
+err:
+ ath11k_dp_srng_cleanup(ar->ab, &ring->refill_srng);
+ return ret;
+}
+
+int ath11k_dbring_get_cap(struct ath11k_base *ab,
+ u8 pdev_idx,
+ enum wmi_direct_buffer_module id,
+ struct ath11k_dbring_cap *db_cap)
+{
+ int i;
+
+ if (!ab->num_db_cap || !ab->db_caps)
+ return -ENOENT;
+
+ if (id >= WMI_DIRECT_BUF_MAX)
+ return -EINVAL;
+
+ for (i = 0; i < ab->num_db_cap; i++) {
+ if (pdev_idx == ab->db_caps[i].pdev_id &&
+ id == ab->db_caps[i].id) {
+ *db_cap = ab->db_caps[i];
+
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+int ath11k_dbring_buffer_release_event(struct ath11k_base *ab,
+ struct ath11k_dbring_buf_release_event *ev)
+{
+ struct ath11k_dbring *ring;
+ struct hal_srng *srng;
+ struct ath11k *ar;
+ struct ath11k_dbring_element *buff;
+ struct ath11k_dbring_data handler_data;
+ struct ath11k_buffer_addr desc;
+ u8 *vaddr_unalign;
+ u32 num_entry, num_buff_reaped;
+ u8 pdev_idx, rbm;
+ u32 cookie;
+ int buf_id;
+ int size;
+ dma_addr_t paddr;
+ int ret = 0;
+
+ pdev_idx = ev->fixed.pdev_id;
+
+ if (pdev_idx >= ab->num_radios) {
+ ath11k_warn(ab, "Invalid pdev id %d\n", pdev_idx);
+ return -EINVAL;
+ }
+
+ if (ev->fixed.num_buf_release_entry !=
+ ev->fixed.num_meta_data_entry) {
+ ath11k_warn(ab, "Buffer entry %d mismatch meta entry %d\n",
+ ev->fixed.num_buf_release_entry,
+ ev->fixed.num_meta_data_entry);
+ return -EINVAL;
+ }
+
+ ar = ab->pdevs[pdev_idx].ar;
+
+ rcu_read_lock();
+ if (!rcu_dereference(ab->pdevs_active[pdev_idx])) {
+ ret = -EINVAL;
+ goto rcu_unlock;
+ }
+
+ switch (ev->fixed.module_id) {
+ case WMI_DIRECT_BUF_SPECTRAL:
+ ring = ath11k_spectral_get_dbring(ar);
+ break;
+ default:
+ ring = NULL;
+ ath11k_warn(ab, "Recv dma buffer release ev on unsupp module %d\n",
+ ev->fixed.module_id);
+ break;
+ }
+
+ if (!ring) {
+ ret = -EINVAL;
+ goto rcu_unlock;
+ }
+
+ srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
+ num_entry = ev->fixed.num_buf_release_entry;
+ size = sizeof(*buff) + ring->buf_sz + ring->buf_align - 1;
+ num_buff_reaped = 0;
+
+ spin_lock_bh(&srng->lock);
+
+ while (num_buff_reaped < num_entry) {
+ desc.info0 = ev->buf_entry[num_buff_reaped].paddr_lo;
+ desc.info1 = ev->buf_entry[num_buff_reaped].paddr_hi;
+ handler_data.meta = ev->meta_data[num_buff_reaped];
+
+ num_buff_reaped++;
+
+ ath11k_hal_rx_buf_addr_info_get(&desc, &paddr, &cookie, &rbm);
+
+ buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie);
+
+ spin_lock_bh(&ring->idr_lock);
+ buff = idr_find(&ring->bufs_idr, buf_id);
+ if (!buff) {
+ spin_unlock_bh(&ring->idr_lock);
+ continue;
+ }
+ idr_remove(&ring->bufs_idr, buf_id);
+ spin_unlock_bh(&ring->idr_lock);
+
+ dma_unmap_single(ab->dev, buff->paddr, ring->buf_sz,
+ DMA_FROM_DEVICE);
+
+ if (ring->handler) {
+ vaddr_unalign = buff->payload;
+ handler_data.data = PTR_ALIGN(vaddr_unalign,
+ ring->buf_align);
+ handler_data.data_sz = ring->buf_sz;
+
+ ring->handler(ar, &handler_data);
+ }
+
+ memset(buff, 0, size);
+ ath11k_dbring_bufs_replenish(ar, ring, buff, GFP_ATOMIC);
+ }
+
+ spin_unlock_bh(&srng->lock);
+
+rcu_unlock:
+ rcu_read_unlock();
+
+ return ret;
+}
+
+void ath11k_dbring_srng_cleanup(struct ath11k *ar, struct ath11k_dbring *ring)
+{
+ ath11k_dp_srng_cleanup(ar->ab, &ring->refill_srng);
+}
+
+void ath11k_dbring_buf_cleanup(struct ath11k *ar, struct ath11k_dbring *ring)
+{
+ struct ath11k_dbring_element *buff;
+ int buf_id;
+
+ spin_lock_bh(&ring->idr_lock);
+ idr_for_each_entry(&ring->bufs_idr, buff, buf_id) {
+ idr_remove(&ring->bufs_idr, buf_id);
+ dma_unmap_single(ar->ab->dev, buff->paddr,
+ ring->buf_sz, DMA_FROM_DEVICE);
+ kfree(buff);
+ }
+
+ idr_destroy(&ring->bufs_idr);
+ spin_unlock_bh(&ring->idr_lock);
+}
diff --git a/drivers/net/wireless/ath/ath11k/dbring.h b/drivers/net/wireless/ath/ath11k/dbring.h
new file mode 100644
index 000000000000..f7fce9ef9c36
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/dbring.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef ATH11K_DBRING_H
+#define ATH11K_DBRING_H
+
+#include <linux/types.h>
+#include <linux/idr.h>
+#include <linux/spinlock.h>
+#include "dp.h"
+
+struct ath11k_dbring_element {
+ dma_addr_t paddr;
+ u8 payload[0];
+};
+
+struct ath11k_dbring_data {
+ void *data;
+ u32 data_sz;
+ struct wmi_dma_buf_release_meta_data meta;
+};
+
+struct ath11k_dbring_buf_release_event {
+ struct ath11k_wmi_dma_buf_release_fixed_param fixed;
+ struct wmi_dma_buf_release_entry *buf_entry;
+ struct wmi_dma_buf_release_meta_data *meta_data;
+ u32 num_buf_entry;
+ u32 num_meta;
+};
+
+struct ath11k_dbring_cap {
+ u32 pdev_id;
+ enum wmi_direct_buffer_module id;
+ u32 min_elem;
+ u32 min_buf_sz;
+ u32 min_buf_align;
+};
+
+struct ath11k_dbring {
+ struct dp_srng refill_srng;
+ struct idr bufs_idr;
+ /* Protects bufs_idr */
+ spinlock_t idr_lock;
+ dma_addr_t tp_addr;
+ dma_addr_t hp_addr;
+ int bufs_max;
+ u32 pdev_id;
+ u32 buf_sz;
+ u32 buf_align;
+ u32 num_resp_per_event;
+ u32 event_timeout_ms;
+ int (*handler)(struct ath11k *, struct ath11k_dbring_data *);
+};
+
+int ath11k_dbring_set_cfg(struct ath11k *ar,
+ struct ath11k_dbring *ring,
+ u32 num_resp_per_event,
+ u32 event_timeout_ms,
+ int (*handler)(struct ath11k *,
+ struct ath11k_dbring_data *));
+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);
+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,
+ struct ath11k_dbring_buf_release_event *ev);
+int ath11k_dbring_get_cap(struct ath11k_base *ab,
+ u8 pdev_idx,
+ enum wmi_direct_buffer_module id,
+ struct ath11k_dbring_cap *db_cap);
+void ath11k_dbring_srng_cleanup(struct ath11k *ar, struct ath11k_dbring *ring);
+void ath11k_dbring_buf_cleanup(struct ath11k *ar, struct ath11k_dbring *ring);
+#endif /* ATH11K_DBRING_H */
diff --git a/drivers/net/wireless/ath/ath11k/debug.c b/drivers/net/wireless/ath/ath11k/debug.c
index 3fd6b5af073b..62a1aa0565a9 100644
--- a/drivers/net/wireless/ath/ath11k/debug.c
+++ b/drivers/net/wireless/ath/ath11k/debug.c
@@ -12,6 +12,43 @@
#include "debug_htt_stats.h"
#include "peer.h"
+static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {
+ "REO2SW1_RING",
+ "REO2SW2_RING",
+ "REO2SW3_RING",
+ "REO2SW4_RING",
+ "WBM2REO_LINK_RING",
+ "REO2TCL_RING",
+ "REO2FW_RING",
+ "RELEASE_RING",
+ "PPE_RELEASE_RING",
+ "TCL2TQM_RING",
+ "TQM_RELEASE_RING",
+ "REO_RELEASE_RING",
+ "WBM2SW0_RELEASE_RING",
+ "WBM2SW1_RELEASE_RING",
+ "WBM2SW2_RELEASE_RING",
+ "WBM2SW3_RELEASE_RING",
+ "REO_CMD_RING",
+ "REO_STATUS_RING",
+};
+
+static const char *htt_bp_lmac_ring[HTT_SW_LMAC_RING_IDX_MAX] = {
+ "FW2RXDMA_BUF_RING",
+ "FW2RXDMA_STATUS_RING",
+ "FW2RXDMA_LINK_RING",
+ "SW2RXDMA_BUF_RING",
+ "WBM2RXDMA_LINK_RING",
+ "RXDMA2FW_RING",
+ "RXDMA2SW_RING",
+ "RXDMA2RELEASE_RING",
+ "RXDMA2REO_RING",
+ "MONITOR_STATUS_RING",
+ "MONITOR_BUF_RING",
+ "MONITOR_DESC_RING",
+ "MONITOR_DEST_RING",
+};
+
void ath11k_info(struct ath11k_base *ab, const char *fmt, ...)
{
struct va_format vaf = {
@@ -739,12 +776,78 @@ static const struct file_operations fops_extd_rx_stats = {
.open = simple_open,
};
-static ssize_t ath11k_debug_dump_soc_rx_stats(struct file *file,
+static int ath11k_fill_bp_stats(struct ath11k_base *ab,
+ struct ath11k_bp_stats *bp_stats,
+ char *buf, int len, int size)
+{
+ lockdep_assert_held(&ab->base_lock);
+
+ len += scnprintf(buf + len, size - len, "count: %u\n",
+ bp_stats->count);
+ len += scnprintf(buf + len, size - len, "hp: %u\n",
+ bp_stats->hp);
+ len += scnprintf(buf + len, size - len, "tp: %u\n",
+ bp_stats->tp);
+ len += scnprintf(buf + len, size - len, "seen before: %ums\n\n",
+ jiffies_to_msecs(jiffies - bp_stats->jiffies));
+ return len;
+}
+
+static ssize_t ath11k_debug_dump_soc_ring_bp_stats(struct ath11k_base *ab,
+ char *buf, int size)
+{
+ struct ath11k_bp_stats *bp_stats;
+ bool stats_rxd = false;
+ u8 i, pdev_idx;
+ int len = 0;
+
+ len += scnprintf(buf + len, size - len, "\nBackpressure Stats\n");
+ len += scnprintf(buf + len, size - len, "==================\n");
+
+ spin_lock_bh(&ab->base_lock);
+ for (i = 0; i < HTT_SW_UMAC_RING_IDX_MAX; i++) {
+ bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[i];
+
+ if (!bp_stats->count)
+ continue;
+
+ len += scnprintf(buf + len, size - len, "Ring: %s\n",
+ htt_bp_umac_ring[i]);
+ len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
+ stats_rxd = true;
+ }
+
+ for (i = 0; i < HTT_SW_LMAC_RING_IDX_MAX; i++) {
+ for (pdev_idx = 0; pdev_idx < MAX_RADIOS; pdev_idx++) {
+ bp_stats =
+ &ab->soc_stats.bp_stats.lmac_ring_bp_stats[i][pdev_idx];
+
+ if (!bp_stats->count)
+ continue;
+
+ len += scnprintf(buf + len, size - len, "Ring: %s\n",
+ htt_bp_lmac_ring[i]);
+ len += scnprintf(buf + len, size - len, "pdev: %d\n",
+ pdev_idx);
+ len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);
+ stats_rxd = true;
+ }
+ }
+ spin_unlock_bh(&ab->base_lock);
+
+ if (!stats_rxd)
+ len += scnprintf(buf + len, size - len,
+ "No Ring Backpressure stats received\n\n");
+
+ return len;
+}
+
+static ssize_t ath11k_debug_dump_soc_dp_stats(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath11k_base *ab = file->private_data;
- struct ath11k_soc_dp_rx_stats *soc_stats = &ab->soc_stats;
+ struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats;
int len = 0, i, retval;
const int size = 4096;
static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = {
@@ -788,6 +891,19 @@ static ssize_t ath11k_debug_dump_soc_rx_stats(struct file *file,
soc_stats->hal_reo_error[2],
soc_stats->hal_reo_error[3]);
+ len += scnprintf(buf + len, size - len, "\nSOC TX STATS:\n");
+ len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n");
+
+ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++)
+ len += scnprintf(buf + len, size - len, "ring%d: %u\n",
+ i, soc_stats->tx_err.desc_na[i]);
+
+ len += scnprintf(buf + len, size - len,
+ "\nMisc Transmit Failures: %d\n",
+ atomic_read(&soc_stats->tx_err.misc_fail));
+
+ len += ath11k_debug_dump_soc_ring_bp_stats(ab, buf + len, size - len);
+
if (len > size)
len = size;
retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
@@ -796,8 +912,8 @@ static ssize_t ath11k_debug_dump_soc_rx_stats(struct file *file,
return retval;
}
-static const struct file_operations fops_soc_rx_stats = {
- .read = ath11k_debug_dump_soc_rx_stats,
+static const struct file_operations fops_soc_dp_stats = {
+ .read = ath11k_debug_dump_soc_dp_stats,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
@@ -819,8 +935,8 @@ int ath11k_debug_pdev_create(struct ath11k_base *ab)
debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab,
&fops_simulate_fw_crash);
- debugfs_create_file("soc_rx_stats", 0600, ab->debugfs_soc, ab,
- &fops_soc_rx_stats);
+ debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab,
+ &fops_soc_dp_stats);
return 0;
}
diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
index 9ae743e528af..1d64c3c51ac9 100644
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -172,11 +172,12 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
case HAL_RXDMA_DST:
case HAL_RXDMA_MONITOR_DST:
case HAL_RXDMA_MONITOR_DESC:
- case HAL_RXDMA_DIR_BUF:
params.intr_batch_cntr_thres_entries =
HAL_SRNG_INT_BATCH_THRESHOLD_OTHER;
params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_OTHER;
break;
+ case HAL_RXDMA_DIR_BUF:
+ break;
default:
ath11k_warn(ab, "Not a valid ring type in dp :%d\n", type);
return -EINVAL;
diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h
index 058a5c1d86ff..7587862d2e32 100644
--- a/drivers/net/wireless/ath/ath11k/dp.h
+++ b/drivers/net/wireless/ath/ath11k/dp.h
@@ -999,6 +999,48 @@ struct htt_resp_msg {
#define HTT_BACKPRESSURE_EVENT_HP_M GENMASK(15, 0)
#define HTT_BACKPRESSURE_EVENT_TP_M GENMASK(31, 16)
+#define HTT_BACKPRESSURE_UMAC_RING_TYPE 0
+#define HTT_BACKPRESSURE_LMAC_RING_TYPE 1
+
+enum htt_backpressure_umac_ringid {
+ HTT_SW_RING_IDX_REO_REO2SW1_RING,
+ HTT_SW_RING_IDX_REO_REO2SW2_RING,
+ HTT_SW_RING_IDX_REO_REO2SW3_RING,
+ HTT_SW_RING_IDX_REO_REO2SW4_RING,
+ HTT_SW_RING_IDX_REO_WBM2REO_LINK_RING,
+ HTT_SW_RING_IDX_REO_REO2TCL_RING,
+ HTT_SW_RING_IDX_REO_REO2FW_RING,
+ HTT_SW_RING_IDX_REO_REO_RELEASE_RING,
+ HTT_SW_RING_IDX_WBM_PPE_RELEASE_RING,
+ HTT_SW_RING_IDX_TCL_TCL2TQM_RING,
+ HTT_SW_RING_IDX_WBM_TQM_RELEASE_RING,
+ HTT_SW_RING_IDX_WBM_REO_RELEASE_RING,
+ HTT_SW_RING_IDX_WBM_WBM2SW0_RELEASE_RING,
+ HTT_SW_RING_IDX_WBM_WBM2SW1_RELEASE_RING,
+ HTT_SW_RING_IDX_WBM_WBM2SW2_RELEASE_RING,
+ HTT_SW_RING_IDX_WBM_WBM2SW3_RELEASE_RING,
+ HTT_SW_RING_IDX_REO_REO_CMD_RING,
+ HTT_SW_RING_IDX_REO_REO_STATUS_RING,
+ HTT_SW_UMAC_RING_IDX_MAX,
+};
+
+enum htt_backpressure_lmac_ringid {
+ HTT_SW_RING_IDX_FW2RXDMA_BUF_RING,
+ HTT_SW_RING_IDX_FW2RXDMA_STATUS_RING,
+ HTT_SW_RING_IDX_FW2RXDMA_LINK_RING,
+ HTT_SW_RING_IDX_SW2RXDMA_BUF_RING,
+ HTT_SW_RING_IDX_WBM2RXDMA_LINK_RING,
+ HTT_SW_RING_IDX_RXDMA2FW_RING,
+ HTT_SW_RING_IDX_RXDMA2SW_RING,
+ HTT_SW_RING_IDX_RXDMA2RELEASE_RING,
+ HTT_SW_RING_IDX_RXDMA2REO_RING,
+ HTT_SW_RING_IDX_MONITOR_STATUS_RING,
+ HTT_SW_RING_IDX_MONITOR_BUF_RING,
+ HTT_SW_RING_IDX_MONITOR_DESC_RING,
+ HTT_SW_RING_IDX_MONITOR_DEST_RING,
+ HTT_SW_LMAC_RING_IDX_MAX,
+};
+
/* ppdu stats
*
* @details
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index a54610d75c40..791d971784ce 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -653,10 +653,8 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx,
spin_lock_bh(&dp->reo_cmd_lock);
list_add_tail(&elem->list, &dp->reo_cmd_cache_flush_list);
dp->reo_cmd_cache_flush_count++;
- spin_unlock_bh(&dp->reo_cmd_lock);
/* Flush and invalidate aged REO desc from HW cache */
- spin_lock_bh(&dp->reo_cmd_lock);
list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_cache_flush_list,
list) {
if (dp->reo_cmd_cache_flush_count > DP_REO_DESC_FREE_THRESHOLD ||
@@ -1503,9 +1501,10 @@ static void ath11k_htt_backpressure_event_handler(struct ath11k_base *ab,
struct sk_buff *skb)
{
u32 *data = (u32 *)skb->data;
- u8 pdev_id, ring_type, ring_id;
+ u8 pdev_id, ring_type, ring_id, pdev_idx;
u16 hp, tp;
u32 backpressure_time;
+ struct ath11k_bp_stats *bp_stats;
pdev_id = FIELD_GET(HTT_BACKPRESSURE_EVENT_PDEV_ID_M, *data);
ring_type = FIELD_GET(HTT_BACKPRESSURE_EVENT_RING_TYPE_M, *data);
@@ -1520,6 +1519,31 @@ static void ath11k_htt_backpressure_event_handler(struct ath11k_base *ab,
ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt backpressure event, pdev %d, ring type %d,ring id %d, hp %d tp %d, backpressure time %d\n",
pdev_id, ring_type, ring_id, hp, tp, backpressure_time);
+
+ if (ring_type == HTT_BACKPRESSURE_UMAC_RING_TYPE) {
+ if (ring_id >= HTT_SW_UMAC_RING_IDX_MAX)
+ return;
+
+ bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[ring_id];
+ } else if (ring_type == HTT_BACKPRESSURE_LMAC_RING_TYPE) {
+ pdev_idx = DP_HW2SW_MACID(pdev_id);
+
+ if (ring_id >= HTT_SW_LMAC_RING_IDX_MAX || pdev_idx >= MAX_RADIOS)
+ return;
+
+ bp_stats = &ab->soc_stats.bp_stats.lmac_ring_bp_stats[ring_id][pdev_idx];
+ } else {
+ ath11k_warn(ab, "unknown ring type received in htt bp event %d\n",
+ ring_type);
+ return;
+ }
+
+ spin_lock_bh(&ab->base_lock);
+ bp_stats->hp = hp;
+ bp_stats->tp = tp;
+ bp_stats->count++;
+ bp_stats->jiffies = jiffies;
+ spin_unlock_bh(&ab->base_lock);
}
void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab,
@@ -2162,6 +2186,7 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc,
struct ieee80211_rx_status *rx_status)
{
u8 channel_num;
+ u32 center_freq;
rx_status->freq = 0;
rx_status->rate_idx = 0;
@@ -2172,8 +2197,11 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc,
rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
channel_num = ath11k_dp_rx_h_msdu_start_freq(rx_desc);
+ center_freq = ath11k_dp_rx_h_msdu_start_freq(rx_desc) >> 16;
- if (channel_num >= 1 && channel_num <= 14) {
+ if (center_freq >= 5935 && center_freq <= 7105) {
+ rx_status->band = NL80211_BAND_6GHZ;
+ } 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;
diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c
index 41c990aec6b7..1af76775b1a8 100644
--- a/drivers/net/wireless/ath/ath11k/dp_tx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
@@ -121,8 +121,10 @@ tcl_ring_sel:
spin_unlock_bh(&tx_ring->tx_idr_lock);
if (ret < 0) {
- if (ring_map == (BIT(DP_TCL_NUM_RING_MAX) - 1))
+ if (ring_map == (BIT(DP_TCL_NUM_RING_MAX) - 1)) {
+ atomic_inc(&ab->soc_stats.tx_err.misc_fail);
return -ENOSPC;
+ }
/* Check if the next ring is available */
ring_selector++;
@@ -180,11 +182,13 @@ tcl_ring_sel:
default:
/* TODO: Take care of other encap modes as well */
ret = -EINVAL;
+ atomic_inc(&ab->soc_stats.tx_err.misc_fail);
goto fail_remove_idr;
}
ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE);
if (dma_mapping_error(ab->dev, ti.paddr)) {
+ atomic_inc(&ab->soc_stats.tx_err.misc_fail);
ath11k_warn(ab, "failed to DMA map data Tx buffer\n");
ret = -ENOMEM;
goto fail_remove_idr;
@@ -208,6 +212,7 @@ tcl_ring_sel:
* desc because the desc is directly enqueued onto hw queue.
*/
ath11k_hal_srng_access_end(ab, tcl_ring);
+ ab->soc_stats.tx_err.desc_na[ti.ring_id]++;
spin_unlock_bh(&tcl_ring->lock);
ret = -ENOMEM;
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 2836a0f197ab..07d3e031c75a 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -33,6 +33,15 @@
.max_power = 30, \
}
+#define CHAN6G(_channel, _freq, _flags) { \
+ .band = NL80211_BAND_6GHZ, \
+ .hw_value = (_channel), \
+ .center_freq = (_freq), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
/* frame mode values are mapped as per enum ath11k_hw_txrx_mode */
static unsigned int ath11k_frame_mode = ATH11K_HW_TXRX_NATIVE_WIFI;
module_param_named(frame_mode, ath11k_frame_mode, uint, 0644);
@@ -86,6 +95,68 @@ static const struct ieee80211_channel ath11k_5ghz_channels[] = {
CHAN5G(173, 5865, 0),
};
+static const struct ieee80211_channel ath11k_6ghz_channels[] = {
+ CHAN6G(1, 5955, 0),
+ CHAN6G(5, 5975, 0),
+ CHAN6G(9, 5995, 0),
+ CHAN6G(13, 6015, 0),
+ CHAN6G(17, 6035, 0),
+ CHAN6G(21, 6055, 0),
+ CHAN6G(25, 6075, 0),
+ CHAN6G(29, 6095, 0),
+ CHAN6G(33, 6115, 0),
+ CHAN6G(37, 6135, 0),
+ CHAN6G(41, 6155, 0),
+ CHAN6G(45, 6175, 0),
+ CHAN6G(49, 6195, 0),
+ CHAN6G(53, 6215, 0),
+ CHAN6G(57, 6235, 0),
+ CHAN6G(61, 6255, 0),
+ CHAN6G(65, 6275, 0),
+ CHAN6G(69, 6295, 0),
+ CHAN6G(73, 6315, 0),
+ CHAN6G(77, 6335, 0),
+ CHAN6G(81, 6355, 0),
+ CHAN6G(85, 6375, 0),
+ CHAN6G(89, 6395, 0),
+ CHAN6G(93, 6415, 0),
+ CHAN6G(97, 6435, 0),
+ CHAN6G(101, 6455, 0),
+ CHAN6G(105, 6475, 0),
+ CHAN6G(109, 6495, 0),
+ CHAN6G(113, 6515, 0),
+ CHAN6G(117, 6535, 0),
+ CHAN6G(121, 6555, 0),
+ CHAN6G(125, 6575, 0),
+ CHAN6G(129, 6595, 0),
+ CHAN6G(133, 6615, 0),
+ CHAN6G(137, 6635, 0),
+ CHAN6G(141, 6655, 0),
+ CHAN6G(145, 6675, 0),
+ CHAN6G(149, 6695, 0),
+ CHAN6G(153, 6715, 0),
+ CHAN6G(157, 6735, 0),
+ CHAN6G(161, 6755, 0),
+ CHAN6G(165, 6775, 0),
+ CHAN6G(169, 6795, 0),
+ CHAN6G(173, 6815, 0),
+ CHAN6G(177, 6835, 0),
+ CHAN6G(181, 6855, 0),
+ CHAN6G(185, 6875, 0),
+ CHAN6G(189, 6895, 0),
+ CHAN6G(193, 6915, 0),
+ CHAN6G(197, 6935, 0),
+ CHAN6G(201, 6955, 0),
+ CHAN6G(205, 6975, 0),
+ CHAN6G(209, 6995, 0),
+ CHAN6G(213, 7015, 0),
+ CHAN6G(217, 7035, 0),
+ CHAN6G(221, 7055, 0),
+ CHAN6G(225, 7075, 0),
+ CHAN6G(229, 7095, 0),
+ CHAN6G(233, 7115, 0),
+};
+
static struct ieee80211_rate ath11k_legacy_rates[] = {
{ .bitrate = 10,
.hw_value = ATH11K_HW_RATE_CCK_LP_1M },
@@ -134,6 +205,17 @@ ath11k_phymodes[NUM_NL80211_BANDS][ATH11K_CHAN_WIDTH_NUM] = {
[NL80211_CHAN_WIDTH_160] = MODE_11AX_HE160,
[NL80211_CHAN_WIDTH_80P80] = MODE_11AX_HE80_80,
},
+ [NL80211_BAND_6GHZ] = {
+ [NL80211_CHAN_WIDTH_5] = MODE_UNKNOWN,
+ [NL80211_CHAN_WIDTH_10] = MODE_UNKNOWN,
+ [NL80211_CHAN_WIDTH_20_NOHT] = MODE_11AX_HE20,
+ [NL80211_CHAN_WIDTH_20] = MODE_11AX_HE20,
+ [NL80211_CHAN_WIDTH_40] = MODE_11AX_HE40,
+ [NL80211_CHAN_WIDTH_80] = MODE_11AX_HE80,
+ [NL80211_CHAN_WIDTH_160] = MODE_11AX_HE160,
+ [NL80211_CHAN_WIDTH_80P80] = MODE_11AX_HE80_80,
+ },
+
};
const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default = {
@@ -698,6 +780,8 @@ static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
struct ieee80211_vif *vif = arvif->vif;
struct ieee80211_mutable_offsets offs = {};
struct sk_buff *bcn;
+ struct ieee80211_mgmt *mgmt;
+ u8 *ies;
int ret;
if (arvif->vdev_type != WMI_VDEV_TYPE_AP)
@@ -709,6 +793,17 @@ static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
return -EPERM;
}
+ ies = bcn->data + ieee80211_get_hdrlen_from_skb(bcn);
+ ies += sizeof(mgmt->u.beacon);
+
+ if (cfg80211_find_ie(WLAN_EID_RSN, ies, (skb_tail_pointer(bcn) - ies)))
+ arvif->rsnie_present = true;
+
+ if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ ies, (skb_tail_pointer(bcn) - ies)))
+ arvif->wpaie_present = true;
+
ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn);
kfree_skb(bcn);
@@ -798,6 +893,7 @@ static void ath11k_peer_assoc_h_crypto(struct ath11k *ar,
struct ieee80211_bss_conf *info = &vif->bss_conf;
struct cfg80211_chan_def def;
struct cfg80211_bss *bss;
+ struct ath11k_vif *arvif = (struct ath11k_vif *)vif->drv_priv;
const u8 *rsnie = NULL;
const u8 *wpaie = NULL;
@@ -808,7 +904,12 @@ static void ath11k_peer_assoc_h_crypto(struct ath11k *ar,
bss = cfg80211_get_bss(ar->hw->wiphy, def.chan, info->bssid, NULL, 0,
IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
- if (bss) {
+
+ if (arvif->rsnie_present || arvif->wpaie_present) {
+ arg->need_ptk_4_way = true;
+ if (arvif->wpaie_present)
+ arg->need_gtk_2_way = true;
+ } else if (bss) {
const struct cfg80211_bss_ies *ies;
rcu_read_lock();
@@ -1489,6 +1590,7 @@ static void ath11k_peer_assoc_h_phymode(struct ath11k *ar,
}
break;
case NL80211_BAND_5GHZ:
+ case NL80211_BAND_6GHZ:
/* Check HE first */
if (sta->he_cap.has_he) {
phymode = ath11k_mac_get_phymode_he(ar, sta);
@@ -2125,6 +2227,9 @@ static int ath11k_start_scan(struct ath11k *ar,
lockdep_assert_held(&ar->conf_mutex);
+ if (ath11k_spectral_get_mode(ar) == ATH11K_SPECTRAL_BACKGROUND)
+ ath11k_spectral_reset_buffer(ar);
+
ret = ath11k_wmi_send_scan_start_cmd(ar, arg);
if (ret)
return ret;
@@ -3411,7 +3516,7 @@ static void ath11k_mac_setup_ht_vht_cap(struct ath11k *ar,
rate_cap_rx_chainmask);
}
- if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) {
+ if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP && !ar->supports_6ghz) {
band = &ar->mac.sbands[NL80211_BAND_5GHZ];
ht_cap = cap->band[NL80211_BAND_5GHZ].ht_cap_info;
if (ht_cap_info)
@@ -3532,6 +3637,35 @@ ath11k_mac_filter_he_cap_mesh(struct ieee80211_he_cap_elem *he_cap_elem)
he_cap_elem->phy_cap_info[9] &= ~m;
}
+static __le16 ath11k_mac_setup_he_6ghz_cap(struct ath11k_pdev_cap *pcap,
+ struct ath11k_band_cap *bcap)
+{
+ u8 val;
+
+ bcap->he_6ghz_capa = IEEE80211_HT_MPDU_DENSITY_NONE;
+ if (bcap->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
+ bcap->he_6ghz_capa |=
+ FIELD_PREP(IEEE80211_HE_6GHZ_CAP_SM_PS,
+ WLAN_HT_CAP_SM_PS_DYNAMIC);
+ else
+ bcap->he_6ghz_capa |=
+ FIELD_PREP(IEEE80211_HE_6GHZ_CAP_SM_PS,
+ WLAN_HT_CAP_SM_PS_DISABLED);
+ val = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+ pcap->vht_cap);
+ bcap->he_6ghz_capa |=
+ FIELD_PREP(IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP, val);
+ val = FIELD_GET(IEEE80211_VHT_CAP_MAX_MPDU_MASK, pcap->vht_cap);
+ bcap->he_6ghz_capa |=
+ FIELD_PREP(IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN, val);
+ if (pcap->vht_cap & IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN)
+ bcap->he_6ghz_capa |= IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS;
+ if (pcap->vht_cap & IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN)
+ bcap->he_6ghz_capa |= IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS;
+
+ return cpu_to_le16(bcap->he_6ghz_capa);
+}
+
static int ath11k_mac_copy_he_cap(struct ath11k *ar,
struct ath11k_pdev_cap *cap,
struct ieee80211_sband_iftype_data *data,
@@ -3614,6 +3748,11 @@ static int ath11k_mac_copy_he_cap(struct ath11k *ar,
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT)
ath11k_gen_ppe_thresh(&band_cap->he_ppet,
he_cap->ppe_thres);
+
+ if (band == NL80211_BAND_6GHZ) {
+ data[idx].he_6ghz_capa.capa =
+ ath11k_mac_setup_he_6ghz_cap(cap, band_cap);
+ }
idx++;
}
@@ -3643,6 +3782,16 @@ static void ath11k_mac_setup_he_cap(struct ath11k *ar,
band->iftype_data = ar->mac.iftype[NL80211_BAND_5GHZ];
band->n_iftype_data = count;
}
+
+ if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP &&
+ ar->supports_6ghz) {
+ count = ath11k_mac_copy_he_cap(ar, cap,
+ ar->mac.iftype[NL80211_BAND_6GHZ],
+ NL80211_BAND_6GHZ);
+ band = &ar->mac.sbands[NL80211_BAND_6GHZ];
+ band->iftype_data = ar->mac.iftype[NL80211_BAND_6GHZ];
+ band->n_iftype_data = count;
+ }
}
static int __ath11k_set_antenna(struct ath11k *ar, u32 tx_ant, u32 rx_ant)
@@ -4085,6 +4234,11 @@ ath11k_mac_setup_vdev_create_params(struct ath11k_vif *arvif,
params->chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains;
params->chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains;
}
+ if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP &&
+ ar->supports_6ghz) {
+ params->chains[NL80211_BAND_6GHZ].tx = ar->num_tx_chains;
+ params->chains[NL80211_BAND_6GHZ].rx = ar->num_rx_chains;
+ }
}
static u32
@@ -5217,7 +5371,7 @@ ath11k_mac_get_single_legacy_rate(struct ath11k *ar,
rate_idx = ffs(mask->control[band].legacy) - 1;
- if (band == NL80211_BAND_5GHZ)
+ if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ)
rate_idx += ATH11K_MAC_FIRST_OFDM_RATE_IDX;
hw_rate = ath11k_legacy_rates[rate_idx].hw_value;
@@ -5683,7 +5837,8 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar,
void *channels;
BUILD_BUG_ON((ARRAY_SIZE(ath11k_2ghz_channels) +
- ARRAY_SIZE(ath11k_5ghz_channels)) !=
+ ARRAY_SIZE(ath11k_5ghz_channels) +
+ ARRAY_SIZE(ath11k_6ghz_channels)) !=
ATH11K_NUM_CHANS);
reg_cap = &ar->ab->hal_reg_cap[ar->pdev_idx];
@@ -5696,6 +5851,7 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar,
return -ENOMEM;
band = &ar->mac.sbands[NL80211_BAND_2GHZ];
+ band->band = NL80211_BAND_2GHZ;
band->n_channels = ARRAY_SIZE(ath11k_2ghz_channels);
band->channels = channels;
band->n_bitrates = ath11k_g_rates_size;
@@ -5707,23 +5863,48 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar,
}
if (supported_bands & WMI_HOST_WLAN_5G_CAP) {
- channels = kmemdup(ath11k_5ghz_channels,
- sizeof(ath11k_5ghz_channels),
- GFP_KERNEL);
- if (!channels) {
- kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
- return -ENOMEM;
+ if (reg_cap->high_5ghz_chan >= ATH11K_MAX_6G_FREQ) {
+ channels = kmemdup(ath11k_6ghz_channels,
+ sizeof(ath11k_6ghz_channels), GFP_KERNEL);
+ if (!channels) {
+ kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
+ return -ENOMEM;
+ }
+
+ ar->supports_6ghz = true;
+ band = &ar->mac.sbands[NL80211_BAND_6GHZ];
+ band->band = NL80211_BAND_6GHZ;
+ band->n_channels = ARRAY_SIZE(ath11k_6ghz_channels);
+ band->channels = channels;
+ band->n_bitrates = ath11k_a_rates_size;
+ band->bitrates = ath11k_a_rates;
+ ar->hw->wiphy->bands[NL80211_BAND_6GHZ] = band;
+ ath11k_mac_update_ch_list(ar, band,
+ reg_cap->low_5ghz_chan,
+ reg_cap->high_5ghz_chan);
}
- band = &ar->mac.sbands[NL80211_BAND_5GHZ];
- band->n_channels = ARRAY_SIZE(ath11k_5ghz_channels);
- band->channels = channels;
- band->n_bitrates = ath11k_a_rates_size;
- band->bitrates = ath11k_a_rates;
- ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band;
- ath11k_mac_update_ch_list(ar, band,
- reg_cap->low_5ghz_chan,
- reg_cap->high_5ghz_chan);
+ if (reg_cap->low_5ghz_chan < ATH11K_MIN_6G_FREQ) {
+ channels = kmemdup(ath11k_5ghz_channels,
+ sizeof(ath11k_5ghz_channels),
+ GFP_KERNEL);
+ if (!channels) {
+ kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
+ kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels);
+ return -ENOMEM;
+ }
+
+ band = &ar->mac.sbands[NL80211_BAND_5GHZ];
+ band->band = NL80211_BAND_5GHZ;
+ band->n_channels = ARRAY_SIZE(ath11k_5ghz_channels);
+ band->channels = channels;
+ band->n_bitrates = ath11k_a_rates_size;
+ band->bitrates = ath11k_a_rates;
+ ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band;
+ ath11k_mac_update_ch_list(ar, band,
+ reg_cap->low_5ghz_chan,
+ reg_cap->high_5ghz_chan);
+ }
}
return 0;
@@ -5777,6 +5958,7 @@ static void __ath11k_mac_unregister(struct ath11k *ar)
kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels);
kfree(ar->mac.sbands[NL80211_BAND_5GHZ].channels);
+ kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels);
SET_IEEE80211_DEV(ar->hw, NULL);
}
diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index 453aa9c06969..7c9dc91cc48a 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -161,6 +161,10 @@ int ath11k_reg_update_chan_list(struct ath11k *ar)
else
ch->phy_mode = MODE_11A;
+ if (channel->band == NL80211_BAND_6GHZ &&
+ cfg80211_channel_is_psc(channel))
+ ch->psc_channel = true;
+
ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
"mac channel [%d/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
i, params->nallchans,
diff --git a/drivers/net/wireless/ath/ath11k/spectral.c b/drivers/net/wireless/ath/ath11k/spectral.c
new file mode 100644
index 000000000000..1c5d65bb411f
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/spectral.c
@@ -0,0 +1,1023 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/relay.h>
+#include "core.h"
+#include "debug.h"
+
+#define ATH11K_SPECTRAL_NUM_RESP_PER_EVENT 2
+#define ATH11K_SPECTRAL_EVENT_TIMEOUT_MS 1
+
+#define ATH11K_SPECTRAL_DWORD_SIZE 4
+/* HW bug, expected BIN size is 2 bytes but HW report as 4 bytes */
+#define ATH11K_SPECTRAL_BIN_SIZE 4
+#define ATH11K_SPECTRAL_ATH11K_MIN_BINS 64
+#define ATH11K_SPECTRAL_ATH11K_MIN_IB_BINS 32
+#define ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS 256
+
+#define ATH11K_SPECTRAL_SAMPLE_FFT_BIN_MASK 0xFF
+
+#define ATH11K_SPECTRAL_SCAN_COUNT_MAX 4095
+
+/* Max channel computed by sum of 2g and 5g band channels */
+#define ATH11K_SPECTRAL_TOTAL_CHANNEL 41
+#define ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL 70
+#define ATH11K_SPECTRAL_PER_SAMPLE_SIZE (sizeof(struct fft_sample_ath11k) + \
+ ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS)
+#define ATH11K_SPECTRAL_TOTAL_SAMPLE (ATH11K_SPECTRAL_TOTAL_CHANNEL * \
+ ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL)
+#define ATH11K_SPECTRAL_SUB_BUFF_SIZE ATH11K_SPECTRAL_PER_SAMPLE_SIZE
+#define ATH11K_SPECTRAL_NUM_SUB_BUF ATH11K_SPECTRAL_TOTAL_SAMPLE
+
+#define ATH11K_SPECTRAL_20MHZ 20
+#define ATH11K_SPECTRAL_40MHZ 40
+#define ATH11K_SPECTRAL_80MHZ 80
+
+#define ATH11K_SPECTRAL_SIGNATURE 0xFA
+
+#define ATH11K_SPECTRAL_TAG_RADAR_SUMMARY 0x0
+#define ATH11K_SPECTRAL_TAG_RADAR_FFT 0x1
+#define ATH11K_SPECTRAL_TAG_SCAN_SUMMARY 0x2
+#define ATH11K_SPECTRAL_TAG_SCAN_SEARCH 0x3
+
+#define SPECTRAL_TLV_HDR_LEN GENMASK(15, 0)
+#define SPECTRAL_TLV_HDR_TAG GENMASK(23, 16)
+#define SPECTRAL_TLV_HDR_SIGN GENMASK(31, 24)
+
+#define SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN GENMASK(7, 0)
+#define SPECTRAL_SUMMARY_INFO0_OB_FLAG BIT(8)
+#define SPECTRAL_SUMMARY_INFO0_GRP_IDX GENMASK(16, 9)
+#define SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT BIT(17)
+#define SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB GENMASK(27, 18)
+#define SPECTRAL_SUMMARY_INFO0_FALSE_SCAN BIT(28)
+#define SPECTRAL_SUMMARY_INFO0_DETECTOR_ID GENMASK(30, 29)
+#define SPECTRAL_SUMMARY_INFO0_PRI80 BIT(31)
+
+#define SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX GENMASK(11, 0)
+#define SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE GENMASK(21, 12)
+#define SPECTRAL_SUMMARY_INFO2_NARROWBAND_MASK GENMASK(29, 22)
+#define SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE BIT(30)
+
+struct spectral_tlv {
+ __le32 timestamp;
+ __le32 header;
+} __packed;
+
+struct spectral_summary_fft_report {
+ __le32 timestamp;
+ __le32 tlv_header;
+ __le32 info0;
+ __le32 reserve0;
+ __le32 info2;
+ __le32 reserve1;
+} __packed;
+
+struct ath11k_spectral_summary_report {
+ struct wmi_dma_buf_release_meta_data meta;
+ u32 timestamp;
+ u8 agc_total_gain;
+ u8 grp_idx;
+ u16 inb_pwr_db;
+ s16 peak_idx;
+ u16 peak_mag;
+ u8 detector_id;
+ bool out_of_band_flag;
+ bool rf_saturation;
+ bool primary80;
+ bool gain_change;
+ bool false_scan;
+};
+
+#define SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID GENMASK(1, 0)
+#define SPECTRAL_FFT_REPORT_INFO0_FFT_NUM GENMASK(4, 2)
+#define SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK GENMASK(16, 5)
+#define SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX GENMASK(27, 17)
+#define SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX GENMASK(30, 28)
+
+#define SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB GENMASK(8, 0)
+#define SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB GENMASK(16, 9)
+
+#define SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS GENMASK(7, 0)
+#define SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE GENMASK(17, 8)
+#define SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB GENMASK(24, 18)
+#define SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB GENMASK(31, 25)
+
+struct spectral_search_fft_report {
+ __le32 timestamp;
+ __le32 tlv_header;
+ __le32 info0;
+ __le32 info1;
+ __le32 info2;
+ __le32 reserve0;
+ u8 bins[0];
+} __packed;
+
+struct ath11k_spectral_search_report {
+ u32 timestamp;
+ u8 detector_id;
+ u8 fft_count;
+ u16 radar_check;
+ s16 peak_idx;
+ u8 chain_idx;
+ u16 base_pwr_db;
+ u8 total_gain_db;
+ u8 strong_bin_count;
+ u16 peak_mag;
+ u8 avg_pwr_db;
+ u8 rel_pwr_db;
+};
+
+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_scan_cb = {
+ .create_buf_file = create_buf_file_handler,
+ .remove_buf_file = remove_buf_file_handler,
+};
+
+static struct ath11k_vif *ath11k_spectral_get_vdev(struct ath11k *ar)
+{
+ struct ath11k_vif *arvif;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (list_empty(&ar->arvifs))
+ return NULL;
+
+ /* if there already is a vif doing spectral, return that. */
+ list_for_each_entry(arvif, &ar->arvifs, list)
+ if (arvif->spectral_enabled)
+ return arvif;
+
+ /* otherwise, return the first vif. */
+ return list_first_entry(&ar->arvifs, typeof(*arvif), list);
+}
+
+static int ath11k_spectral_scan_trigger(struct ath11k *ar)
+{
+ struct ath11k_vif *arvif;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ arvif = ath11k_spectral_get_vdev(ar);
+ if (!arvif)
+ return -ENODEV;
+
+ if (ar->spectral.mode == ATH11K_SPECTRAL_DISABLED)
+ return 0;
+
+ ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
+ ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
+ ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
+ ATH11K_WMI_SPECTRAL_TRIGGER_CMD_TRIGGER,
+ ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ath11k_spectral_scan_config(struct ath11k *ar,
+ enum ath11k_spectral_mode mode)
+{
+ struct ath11k_wmi_vdev_spectral_conf_param param = { 0 };
+ struct ath11k_vif *arvif;
+ int ret, count;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ arvif = ath11k_spectral_get_vdev(ar);
+ if (!arvif)
+ return -ENODEV;
+
+ arvif->spectral_enabled = (mode != ATH11K_SPECTRAL_DISABLED);
+ ar->spectral.mode = mode;
+
+ ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
+ ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
+ ATH11K_WMI_SPECTRAL_ENABLE_CMD_DISABLE);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to enable spectral scan: %d\n", ret);
+ return ret;
+ }
+
+ if (mode == ATH11K_SPECTRAL_DISABLED)
+ return 0;
+
+ if (mode == ATH11K_SPECTRAL_BACKGROUND)
+ count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;
+ else
+ count = max_t(u16, 1, ar->spectral.count);
+
+ param.vdev_id = arvif->vdev_id;
+ param.scan_count = count;
+ param.scan_fft_size = ar->spectral.fft_size;
+ param.scan_period = ATH11K_WMI_SPECTRAL_PERIOD_DEFAULT;
+ param.scan_priority = ATH11K_WMI_SPECTRAL_PRIORITY_DEFAULT;
+ param.scan_gc_ena = ATH11K_WMI_SPECTRAL_GC_ENA_DEFAULT;
+ param.scan_restart_ena = ATH11K_WMI_SPECTRAL_RESTART_ENA_DEFAULT;
+ param.scan_noise_floor_ref = ATH11K_WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT;
+ param.scan_init_delay = ATH11K_WMI_SPECTRAL_INIT_DELAY_DEFAULT;
+ param.scan_nb_tone_thr = ATH11K_WMI_SPECTRAL_NB_TONE_THR_DEFAULT;
+ param.scan_str_bin_thr = ATH11K_WMI_SPECTRAL_STR_BIN_THR_DEFAULT;
+ param.scan_wb_rpt_mode = ATH11K_WMI_SPECTRAL_WB_RPT_MODE_DEFAULT;
+ param.scan_rssi_rpt_mode = ATH11K_WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT;
+ param.scan_rssi_thr = ATH11K_WMI_SPECTRAL_RSSI_THR_DEFAULT;
+ param.scan_pwr_format = ATH11K_WMI_SPECTRAL_PWR_FORMAT_DEFAULT;
+ param.scan_rpt_mode = ATH11K_WMI_SPECTRAL_RPT_MODE_DEFAULT;
+ param.scan_bin_scale = ATH11K_WMI_SPECTRAL_BIN_SCALE_DEFAULT;
+ param.scan_dbm_adj = ATH11K_WMI_SPECTRAL_DBM_ADJ_DEFAULT;
+ param.scan_chn_mask = ATH11K_WMI_SPECTRAL_CHN_MASK_DEFAULT;
+
+ ret = ath11k_wmi_vdev_spectral_conf(ar, &param);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to configure spectral scan: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static ssize_t ath11k_read_file_spec_scan_ctl(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ char *mode = "";
+ size_t len;
+ enum ath11k_spectral_mode spectral_mode;
+
+ mutex_lock(&ar->conf_mutex);
+ spectral_mode = ar->spectral.mode;
+ mutex_unlock(&ar->conf_mutex);
+
+ switch (spectral_mode) {
+ case ATH11K_SPECTRAL_DISABLED:
+ mode = "disable";
+ break;
+ case ATH11K_SPECTRAL_BACKGROUND:
+ mode = "background";
+ break;
+ case ATH11K_SPECTRAL_MANUAL:
+ mode = "manual";
+ break;
+ }
+
+ len = strlen(mode);
+ return simple_read_from_buffer(user_buf, count, ppos, mode, len);
+}
+
+static ssize_t ath11k_write_file_spec_scan_ctl(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ char buf[32];
+ ssize_t len;
+ int ret;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (strncmp("trigger", buf, 7) == 0) {
+ if (ar->spectral.mode == ATH11K_SPECTRAL_MANUAL ||
+ ar->spectral.mode == ATH11K_SPECTRAL_BACKGROUND) {
+ /* reset the configuration to adopt possibly changed
+ * debugfs parameters
+ */
+ ret = ath11k_spectral_scan_config(ar, ar->spectral.mode);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to reconfigure spectral scan: %d\n",
+ ret);
+ goto unlock;
+ }
+
+ ret = ath11k_spectral_scan_trigger(ar);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to trigger spectral scan: %d\n",
+ ret);
+ }
+ } else {
+ ret = -EINVAL;
+ }
+ } else if (strncmp("background", buf, 10) == 0) {
+ ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_BACKGROUND);
+ } else if (strncmp("manual", buf, 6) == 0) {
+ ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_MANUAL);
+ } else if (strncmp("disable", buf, 7) == 0) {
+ ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_DISABLED);
+ } else {
+ ret = -EINVAL;
+ }
+
+unlock:
+ mutex_unlock(&ar->conf_mutex);
+
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static const struct file_operations fops_scan_ctl = {
+ .read = ath11k_read_file_spec_scan_ctl,
+ .write = ath11k_write_file_spec_scan_ctl,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath11k_read_file_spectral_count(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ char buf[32];
+ size_t len;
+ u16 spectral_count;
+
+ mutex_lock(&ar->conf_mutex);
+ spectral_count = ar->spectral.count;
+ mutex_unlock(&ar->conf_mutex);
+
+ len = sprintf(buf, "%d\n", spectral_count);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath11k_write_file_spectral_count(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ unsigned long val;
+ char buf[32];
+ ssize_t len;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val > ATH11K_SPECTRAL_SCAN_COUNT_MAX)
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+ ar->spectral.count = val;
+ mutex_unlock(&ar->conf_mutex);
+
+ return count;
+}
+
+static const struct file_operations fops_scan_count = {
+ .read = ath11k_read_file_spectral_count,
+ .write = ath11k_write_file_spectral_count,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath11k_read_file_spectral_bins(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ char buf[32];
+ unsigned int bins, fft_size;
+ size_t len;
+
+ mutex_lock(&ar->conf_mutex);
+
+ fft_size = ar->spectral.fft_size;
+ bins = 1 << fft_size;
+
+ mutex_unlock(&ar->conf_mutex);
+
+ len = sprintf(buf, "%d\n", bins);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath11k_write_file_spectral_bins(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath11k *ar = file->private_data;
+ unsigned long val;
+ char buf[32];
+ ssize_t len;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val < ATH11K_SPECTRAL_ATH11K_MIN_BINS ||
+ val > SPECTRAL_ATH11K_MAX_NUM_BINS)
+ return -EINVAL;
+
+ if (!is_power_of_2(val))
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+ ar->spectral.fft_size = ilog2(val);
+ mutex_unlock(&ar->conf_mutex);
+
+ return count;
+}
+
+static const struct file_operations fops_scan_bins = {
+ .read = ath11k_read_file_spectral_bins,
+ .write = ath11k_write_file_spectral_bins,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static int ath11k_spectral_pull_summary(struct ath11k *ar,
+ struct wmi_dma_buf_release_meta_data *meta,
+ struct spectral_summary_fft_report *summary,
+ struct ath11k_spectral_summary_report *report)
+{
+ report->timestamp = __le32_to_cpu(summary->timestamp);
+ report->agc_total_gain = FIELD_GET(SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN,
+ __le32_to_cpu(summary->info0));
+ report->out_of_band_flag = FIELD_GET(SPECTRAL_SUMMARY_INFO0_OB_FLAG,
+ __le32_to_cpu(summary->info0));
+ report->grp_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO0_GRP_IDX,
+ __le32_to_cpu(summary->info0));
+ report->rf_saturation = FIELD_GET(SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT,
+ __le32_to_cpu(summary->info0));
+ report->inb_pwr_db = FIELD_GET(SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB,
+ __le32_to_cpu(summary->info0));
+ report->false_scan = FIELD_GET(SPECTRAL_SUMMARY_INFO0_FALSE_SCAN,
+ __le32_to_cpu(summary->info0));
+ report->detector_id = FIELD_GET(SPECTRAL_SUMMARY_INFO0_DETECTOR_ID,
+ __le32_to_cpu(summary->info0));
+ report->primary80 = FIELD_GET(SPECTRAL_SUMMARY_INFO0_PRI80,
+ __le32_to_cpu(summary->info0));
+ report->peak_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX,
+ __le32_to_cpu(summary->info2));
+ report->peak_mag = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE,
+ __le32_to_cpu(summary->info2));
+ report->gain_change = FIELD_GET(SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE,
+ __le32_to_cpu(summary->info2));
+
+ memcpy(&report->meta, meta, sizeof(*meta));
+
+ return 0;
+}
+
+static int ath11k_spectral_pull_search(struct ath11k *ar,
+ struct spectral_search_fft_report *search,
+ struct ath11k_spectral_search_report *report)
+{
+ report->timestamp = __le32_to_cpu(search->timestamp);
+ report->detector_id = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID,
+ __le32_to_cpu(search->info0));
+ report->fft_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_FFT_NUM,
+ __le32_to_cpu(search->info0));
+ report->radar_check = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK,
+ __le32_to_cpu(search->info0));
+ report->peak_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,
+ __le32_to_cpu(search->info0));
+ report->chain_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX,
+ __le32_to_cpu(search->info0));
+ report->base_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB,
+ __le32_to_cpu(search->info1));
+ report->total_gain_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB,
+ __le32_to_cpu(search->info1));
+ report->strong_bin_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS,
+ __le32_to_cpu(search->info2));
+ report->peak_mag = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE,
+ __le32_to_cpu(search->info2));
+ report->avg_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB,
+ __le32_to_cpu(search->info2));
+ report->rel_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB,
+ __le32_to_cpu(search->info2));
+
+ return 0;
+}
+
+static u8 ath11k_spectral_get_max_exp(s8 max_index, u8 max_magnitude,
+ int bin_len, u8 *bins)
+{
+ int dc_pos;
+ u8 max_exp;
+
+ dc_pos = bin_len / 2;
+
+ /* peak index outside of bins */
+ if (dc_pos <= max_index || -dc_pos >= max_index)
+ return 0;
+
+ for (max_exp = 0; max_exp < 8; max_exp++) {
+ if (bins[dc_pos + max_index] == (max_magnitude >> max_exp))
+ break;
+ }
+
+ /* max_exp not found */
+ if (bins[dc_pos + max_index] != (max_magnitude >> max_exp))
+ return 0;
+
+ return max_exp;
+}
+
+static void ath11k_spectral_parse_16bit_fft(u8 *outbins, u8 *inbins, int num_bins)
+{
+ int i;
+ __le16 *data = (__le16 *)inbins;
+
+ i = 0;
+ while (i < num_bins) {
+ outbins[i] = (__le16_to_cpu(data[i])) &
+ ATH11K_SPECTRAL_SAMPLE_FFT_BIN_MASK;
+ i++;
+ }
+}
+
+static
+int ath11k_spectral_process_fft(struct ath11k *ar,
+ struct ath11k_spectral_summary_report *summary,
+ void *data,
+ struct fft_sample_ath11k *fft_sample,
+ u32 data_len)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct spectral_search_fft_report *fft_report = data;
+ struct ath11k_spectral_search_report search;
+ struct spectral_tlv *tlv;
+ int tlv_len, bin_len, num_bins;
+ u16 length, freq;
+ u8 chan_width_mhz;
+ int ret;
+
+ lockdep_assert_held(&ar->spectral.lock);
+
+ tlv = (struct spectral_tlv *)data;
+ tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN, __le32_to_cpu(tlv->header));
+ /* convert Dword into bytes */
+ tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;
+ bin_len = tlv_len - (sizeof(*fft_report) - sizeof(*tlv));
+
+ if (data_len < (bin_len + sizeof(*fft_report))) {
+ ath11k_warn(ab, "mismatch in expected bin len %d and data len %d\n",
+ bin_len, data_len);
+ return -EINVAL;
+ }
+
+ num_bins = bin_len / ATH11K_SPECTRAL_BIN_SIZE;
+ /* Only In-band bins are useful to user for visualize */
+ num_bins >>= 1;
+
+ if (num_bins < ATH11K_SPECTRAL_ATH11K_MIN_IB_BINS ||
+ num_bins > ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS ||
+ !is_power_of_2(num_bins)) {
+ ath11k_warn(ab, "Invalid num of bins %d\n", num_bins);
+ return -EINVAL;
+ }
+
+ ret = ath11k_spectral_pull_search(ar, data, &search);
+ if (ret) {
+ ath11k_warn(ab, "failed to pull search report %d\n", ret);
+ return ret;
+ }
+
+ chan_width_mhz = summary->meta.ch_width;
+
+ switch (chan_width_mhz) {
+ case ATH11K_SPECTRAL_20MHZ:
+ case ATH11K_SPECTRAL_40MHZ:
+ case ATH11K_SPECTRAL_80MHZ:
+ fft_sample->chan_width_mhz = chan_width_mhz;
+ break;
+ default:
+ ath11k_warn(ab, "invalid channel width %d\n", chan_width_mhz);
+ return -EINVAL;
+ }
+
+ length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + num_bins;
+ fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH11K;
+ fft_sample->tlv.length = __cpu_to_be16(length);
+
+ fft_sample->tsf = __cpu_to_be32(search.timestamp);
+ fft_sample->max_magnitude = __cpu_to_be16(search.peak_mag);
+ fft_sample->max_index = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,
+ __le32_to_cpu(fft_report->info0));
+
+ summary->inb_pwr_db >>= 1;
+ fft_sample->rssi = __cpu_to_be16(summary->inb_pwr_db);
+ fft_sample->noise = __cpu_to_be32(summary->meta.noise_floor[search.chain_idx]);
+
+ freq = summary->meta.freq1;
+ fft_sample->freq1 = __cpu_to_be16(freq);
+
+ freq = summary->meta.freq2;
+ fft_sample->freq2 = __cpu_to_be16(freq);
+
+ ath11k_spectral_parse_16bit_fft(fft_sample->data,
+ fft_report->bins,
+ num_bins);
+
+ fft_sample->max_exp = ath11k_spectral_get_max_exp(fft_sample->max_index,
+ search.peak_mag,
+ num_bins,
+ fft_sample->data);
+
+ if (ar->spectral.rfs_scan)
+ relay_write(ar->spectral.rfs_scan, fft_sample,
+ length + sizeof(struct fft_sample_tlv));
+
+ return 0;
+}
+
+static int ath11k_spectral_process_data(struct ath11k *ar,
+ struct ath11k_dbring_data *param)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct spectral_tlv *tlv;
+ struct spectral_summary_fft_report *summary = NULL;
+ struct ath11k_spectral_summary_report summ_rpt;
+ struct fft_sample_ath11k *fft_sample = NULL;
+ u8 *data;
+ u32 data_len, i;
+ u8 sign, tag;
+ int tlv_len, sample_sz;
+ int ret;
+ bool quit = false;
+
+ spin_lock_bh(&ar->spectral.lock);
+
+ if (!ar->spectral.enabled) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ sample_sz = sizeof(*fft_sample) + ATH11K_SPECTRAL_ATH11K_MAX_IB_BINS;
+ fft_sample = kmalloc(sample_sz, GFP_ATOMIC);
+ if (!fft_sample) {
+ ret = -ENOBUFS;
+ goto unlock;
+ }
+
+ data = param->data;
+ data_len = param->data_sz;
+ i = 0;
+ while (!quit && (i < data_len)) {
+ if ((i + sizeof(*tlv)) > data_len) {
+ ath11k_warn(ab, "failed to parse spectral tlv hdr at bytes %d\n",
+ i);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tlv = (struct spectral_tlv *)&data[i];
+ sign = FIELD_GET(SPECTRAL_TLV_HDR_SIGN,
+ __le32_to_cpu(tlv->header));
+ if (sign != ATH11K_SPECTRAL_SIGNATURE) {
+ ath11k_warn(ab, "Invalid sign 0x%x at bytes %d\n",
+ sign, i);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN,
+ __le32_to_cpu(tlv->header));
+ /* convert Dword into bytes */
+ tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;
+ if ((i + sizeof(*tlv) + tlv_len) > data_len) {
+ ath11k_warn(ab, "failed to parse spectral tlv payload at bytes %d tlv_len:%d data_len:%d\n",
+ i, tlv_len, data_len);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tag = FIELD_GET(SPECTRAL_TLV_HDR_TAG,
+ __le32_to_cpu(tlv->header));
+ switch (tag) {
+ case ATH11K_SPECTRAL_TAG_SCAN_SUMMARY:
+ /* HW bug in tlv length of summary report,
+ * HW report 3 DWORD size but the data payload
+ * is 4 DWORD size (16 bytes).
+ * Need to remove this workaround once HW bug fixed
+ */
+ tlv_len = sizeof(*summary) - sizeof(*tlv);
+
+ if (tlv_len < (sizeof(*summary) - sizeof(*tlv))) {
+ ath11k_warn(ab, "failed to parse spectral summary at bytes %d tlv_len:%d\n",
+ i, tlv_len);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ summary = (struct spectral_summary_fft_report *)tlv;
+ ath11k_spectral_pull_summary(ar, &param->meta,
+ summary, &summ_rpt);
+ break;
+ case ATH11K_SPECTRAL_TAG_SCAN_SEARCH:
+ if (tlv_len < (sizeof(struct spectral_search_fft_report) -
+ sizeof(*tlv))) {
+ ath11k_warn(ab, "failed to parse spectral search fft at bytes %d\n",
+ i);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ memset(fft_sample, 0, sample_sz);
+ ret = ath11k_spectral_process_fft(ar, &summ_rpt, tlv,
+ fft_sample,
+ data_len - i);
+ if (ret) {
+ ath11k_warn(ab, "failed to process spectral fft at bytes %d\n",
+ i);
+ goto err;
+ }
+ quit = true;
+ break;
+ }
+
+ i += sizeof(*tlv) + tlv_len;
+ }
+
+err:
+ kfree(fft_sample);
+unlock:
+ spin_unlock_bh(&ar->spectral.lock);
+ return ret;
+}
+
+static int ath11k_spectral_ring_alloc(struct ath11k *ar,
+ struct ath11k_dbring_cap *db_cap)
+{
+ struct ath11k_spectral *sp = &ar->spectral;
+ int ret;
+
+ ret = ath11k_dbring_srng_setup(ar, &sp->rx_ring,
+ 0, db_cap->min_elem);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to setup db ring\n");
+ return ret;
+ }
+
+ ath11k_dbring_set_cfg(ar, &sp->rx_ring,
+ ATH11K_SPECTRAL_NUM_RESP_PER_EVENT,
+ ATH11K_SPECTRAL_EVENT_TIMEOUT_MS,
+ ath11k_spectral_process_data);
+
+ 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;
+ }
+
+ ret = ath11k_dbring_wmi_cfg_setup(ar, &sp->rx_ring,
+ WMI_DIRECT_BUF_SPECTRAL);
+ 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, &sp->rx_ring);
+srng_cleanup:
+ ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);
+ return ret;
+}
+
+static inline void ath11k_spectral_ring_free(struct ath11k *ar)
+{
+ struct ath11k_spectral *sp = &ar->spectral;
+
+ if (!sp->enabled)
+ return;
+
+ ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);
+ ath11k_dbring_buf_cleanup(ar, &sp->rx_ring);
+}
+
+static inline void ath11k_spectral_debug_unregister(struct ath11k *ar)
+{
+ debugfs_remove(ar->spectral.scan_bins);
+ ar->spectral.scan_bins = NULL;
+
+ debugfs_remove(ar->spectral.scan_count);
+ ar->spectral.scan_count = NULL;
+
+ debugfs_remove(ar->spectral.scan_ctl);
+ ar->spectral.scan_ctl = NULL;
+
+ if (ar->spectral.rfs_scan) {
+ relay_close(ar->spectral.rfs_scan);
+ ar->spectral.rfs_scan = NULL;
+ }
+}
+
+int ath11k_spectral_vif_stop(struct ath11k_vif *arvif)
+{
+ if (!arvif->spectral_enabled)
+ return 0;
+
+ return ath11k_spectral_scan_config(arvif->ar, ATH11K_SPECTRAL_DISABLED);
+}
+
+void ath11k_spectral_reset_buffer(struct ath11k *ar)
+{
+ if (!ar->spectral.enabled)
+ return;
+
+ if (ar->spectral.rfs_scan)
+ relay_reset(ar->spectral.rfs_scan);
+}
+
+void ath11k_spectral_deinit(struct ath11k_base *ab)
+{
+ struct ath11k *ar;
+ struct ath11k_spectral *sp;
+ int i;
+
+ for (i = 0; i < ab->num_radios; i++) {
+ ar = ab->pdevs[i].ar;
+ sp = &ar->spectral;
+
+ if (!sp->enabled)
+ continue;
+
+ ath11k_spectral_debug_unregister(ar);
+ ath11k_spectral_ring_free(ar);
+
+ spin_lock_bh(&sp->lock);
+
+ sp->mode = ATH11K_SPECTRAL_DISABLED;
+ sp->enabled = false;
+
+ spin_unlock_bh(&sp->lock);
+ }
+}
+
+static inline int ath11k_spectral_debug_register(struct ath11k *ar)
+{
+ int ret;
+
+ ar->spectral.rfs_scan = relay_open("spectral_scan",
+ ar->debug.debugfs_pdev,
+ ATH11K_SPECTRAL_SUB_BUFF_SIZE,
+ ATH11K_SPECTRAL_NUM_SUB_BUF,
+ &rfs_scan_cb, NULL);
+ if (!ar->spectral.rfs_scan) {
+ ath11k_warn(ar->ab, "failed to open relay in pdev %d\n",
+ ar->pdev_idx);
+ return -EINVAL;
+ }
+
+ ar->spectral.scan_ctl = debugfs_create_file("spectral_scan_ctl",
+ 0600,
+ ar->debug.debugfs_pdev, ar,
+ &fops_scan_ctl);
+ if (!ar->spectral.scan_ctl) {
+ ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
+ ar->pdev_idx);
+ ret = -EINVAL;
+ goto debug_unregister;
+ }
+
+ ar->spectral.scan_count = debugfs_create_file("spectral_count",
+ 0600,
+ ar->debug.debugfs_pdev, ar,
+ &fops_scan_count);
+ if (!ar->spectral.scan_count) {
+ ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
+ ar->pdev_idx);
+ ret = -EINVAL;
+ goto debug_unregister;
+ }
+
+ ar->spectral.scan_bins = debugfs_create_file("spectral_bins",
+ 0600,
+ ar->debug.debugfs_pdev, ar,
+ &fops_scan_bins);
+ if (!ar->spectral.scan_bins) {
+ ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
+ ar->pdev_idx);
+ ret = -EINVAL;
+ goto debug_unregister;
+ }
+
+ return 0;
+
+debug_unregister:
+ ath11k_spectral_debug_unregister(ar);
+ return ret;
+}
+
+int ath11k_spectral_init(struct ath11k_base *ab)
+{
+ struct ath11k *ar;
+ struct ath11k_spectral *sp;
+ struct ath11k_dbring_cap db_cap;
+ int ret;
+ int i;
+
+ if (!test_bit(WMI_TLV_SERVICE_FREQINFO_IN_METADATA,
+ ab->wmi_ab.svc_map)) {
+ ath11k_info(ab, "spectral not supported\n");
+ return 0;
+ }
+
+ for (i = 0; i < ab->num_radios; i++) {
+ ar = ab->pdevs[i].ar;
+ sp = &ar->spectral;
+
+ ret = ath11k_dbring_get_cap(ar->ab, ar->pdev_idx,
+ WMI_DIRECT_BUF_SPECTRAL,
+ &db_cap);
+ if (ret) {
+ ath11k_info(ab, "spectral not enabled for pdev %d\n", i);
+ continue;
+ }
+
+ idr_init(&sp->rx_ring.bufs_idr);
+ spin_lock_init(&sp->rx_ring.idr_lock);
+ spin_lock_init(&sp->lock);
+
+ ret = ath11k_spectral_ring_alloc(ar, &db_cap);
+ if (ret) {
+ ath11k_warn(ab, "failed to init spectral ring for pdev %d\n",
+ i);
+ goto deinit;
+ }
+
+ spin_lock_bh(&sp->lock);
+
+ sp->mode = ATH11K_SPECTRAL_DISABLED;
+ sp->count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;
+ sp->fft_size = ATH11K_WMI_SPECTRAL_FFT_SIZE_DEFAULT;
+ sp->enabled = true;
+
+ spin_unlock_bh(&sp->lock);
+
+ ret = ath11k_spectral_debug_register(ar);
+ if (ret) {
+ ath11k_warn(ab, "failed to register spectral for pdev %d\n",
+ i);
+ goto deinit;
+ }
+ }
+
+ return 0;
+
+deinit:
+ ath11k_spectral_deinit(ab);
+ return ret;
+}
+
+enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar)
+{
+ if (ar->spectral.enabled)
+ return ar->spectral.mode;
+ else
+ return ATH11K_SPECTRAL_DISABLED;
+}
+
+struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar)
+{
+ if (ar->spectral.enabled)
+ return &ar->spectral.rx_ring;
+ else
+ return NULL;
+}
diff --git a/drivers/net/wireless/ath/ath11k/spectral.h b/drivers/net/wireless/ath/ath11k/spectral.h
new file mode 100644
index 000000000000..081744265f2a
--- /dev/null
+++ b/drivers/net/wireless/ath/ath11k/spectral.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
+ */
+
+#ifndef ATH11K_SPECTRAL_H
+#define ATH11K_SPECTRAL_H
+
+#include "../spectral_common.h"
+#include "dbring.h"
+
+/* enum ath11k_spectral_mode:
+ *
+ * @SPECTRAL_DISABLED: spectral mode is disabled
+ * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
+ * something else.
+ * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
+ * is performed manually.
+ */
+enum ath11k_spectral_mode {
+ ATH11K_SPECTRAL_DISABLED = 0,
+ ATH11K_SPECTRAL_BACKGROUND,
+ ATH11K_SPECTRAL_MANUAL,
+};
+
+struct ath11k_spectral {
+ struct ath11k_dbring rx_ring;
+ /* Protects enabled */
+ spinlock_t lock;
+ struct rchan *rfs_scan; /* relay(fs) channel for spectral scan */
+ struct dentry *scan_ctl;
+ struct dentry *scan_count;
+ struct dentry *scan_bins;
+ enum ath11k_spectral_mode mode;
+ u16 count;
+ u8 fft_size;
+ bool enabled;
+};
+
+#ifdef CONFIG_ATH11K_SPECTRAL
+
+int ath11k_spectral_init(struct ath11k_base *ab);
+void ath11k_spectral_deinit(struct ath11k_base *ab);
+int ath11k_spectral_vif_stop(struct ath11k_vif *arvif);
+void ath11k_spectral_reset_buffer(struct ath11k *ar);
+enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar);
+struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar);
+
+#else
+
+static inline int ath11k_spectral_init(struct ath11k_base *ab)
+{
+ return 0;
+}
+
+static inline void ath11k_spectral_deinit(struct ath11k_base *ab)
+{
+}
+
+static inline int ath11k_spectral_vif_stop(struct ath11k_vif *arvif)
+{
+ return 0;
+}
+
+static inline void ath11k_spectral_reset_buffer(struct ath11k *ar)
+{
+}
+
+static inline
+enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar)
+{
+ return ATH11K_SPECTRAL_DISABLED;
+}
+
+static inline
+struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_ATH11K_SPECTRAL */
+#endif /* ATH11K_SPECTRAL_H */
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index c2a972377687..8e3437a65673 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -27,6 +27,11 @@ struct wmi_tlv_svc_ready_parse {
bool wmi_svc_bitmap_done;
};
+struct wmi_tlv_dma_ring_caps_parse {
+ struct wmi_dma_ring_capabilities *dma_ring_caps;
+ u32 n_dma_ring_caps;
+};
+
struct wmi_tlv_svc_rdy_ext_parse {
struct ath11k_service_ext_param param;
struct wmi_soc_mac_phy_hw_mode_caps *hw_caps;
@@ -39,15 +44,35 @@ struct wmi_tlv_svc_rdy_ext_parse {
struct wmi_soc_hal_reg_capabilities *soc_hal_reg_caps;
struct wmi_hal_reg_capabilities_ext *ext_hal_reg_caps;
u32 n_ext_hal_reg_caps;
+ struct wmi_tlv_dma_ring_caps_parse dma_caps_parse;
bool hw_mode_done;
bool mac_phy_done;
bool ext_hal_reg_done;
+ bool mac_phy_chainmask_combo_done;
+ bool mac_phy_chainmask_cap_done;
+ bool oem_dma_ring_cap_done;
+ bool dma_ring_cap_done;
+};
+
+struct wmi_tlv_svc_rdy_ext2_parse {
+ struct wmi_tlv_dma_ring_caps_parse dma_caps_parse;
+ bool dma_ring_cap_done;
};
struct wmi_tlv_rdy_parse {
u32 num_extra_mac_addr;
};
+struct wmi_tlv_dma_buf_release_parse {
+ struct ath11k_wmi_dma_buf_release_fixed_param fixed;
+ struct wmi_dma_buf_release_entry *buf_entry;
+ struct wmi_dma_buf_release_meta_data *meta_data;
+ u32 num_buf_entry;
+ u32 num_meta;
+ bool buf_entry_done;
+ bool meta_data_done;
+};
+
static const struct wmi_tlv_policy wmi_tlv_policies[] = {
[WMI_TAG_ARRAY_BYTE]
= { .min_len = 0 },
@@ -368,6 +393,17 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle,
memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g,
sizeof(struct ath11k_ppe_threshold));
+ cap_band = &pdev_cap->band[NL80211_BAND_6GHZ];
+ cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g;
+ cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g;
+ cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g;
+ cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext;
+ cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g;
+ memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g,
+ sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE);
+ memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g,
+ sizeof(struct ath11k_ppe_threshold));
+
return 0;
}
@@ -1692,10 +1728,10 @@ ath11k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd,
*/
if (param->auth_flag)
cmd->peer_flags |= WMI_PEER_AUTH;
- if (param->need_ptk_4_way)
+ if (param->need_ptk_4_way) {
cmd->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
- else
- cmd->peer_flags &= ~WMI_PEER_NEED_PTK_4_WAY;
+ cmd->peer_flags &= ~WMI_PEER_AUTH;
+ }
if (param->need_gtk_2_way)
cmd->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
/* safe mode bypass the 4-way handshake */
@@ -1778,6 +1814,7 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar,
cmd->peer_he_cap_info = param->peer_he_cap_macinfo[0];
cmd->peer_he_cap_info_ext = param->peer_he_cap_macinfo[1];
cmd->peer_he_cap_info_internal = param->peer_he_cap_macinfo_internal;
+ cmd->peer_he_caps_6ghz = param->peer_he_caps_6ghz;
cmd->peer_he_ops = param->peer_he_ops;
memcpy(&cmd->peer_he_cap_phy, &param->peer_he_cap_phyinfo,
sizeof(param->peer_he_cap_phyinfo));
@@ -1831,6 +1868,7 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar,
/* HE Rates */
cmd->peer_he_mcs = param->peer_he_mcs_count;
+ cmd->min_data_rate = param->min_data_rate;
ptr += sizeof(*mcs);
@@ -1886,6 +1924,8 @@ void ath11k_wmi_start_scan_init(struct ath11k *ar,
arg->dwell_time_active = 50;
arg->dwell_time_active_2g = 0;
arg->dwell_time_passive = 150;
+ arg->dwell_time_active_6g = 40;
+ arg->dwell_time_passive_6g = 30;
arg->min_rest_time = 50;
arg->max_rest_time = 500;
arg->repeat_probe_time = 0;
@@ -1990,6 +2030,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
int i, ret, len;
u32 *tmp_ptr;
u8 extraie_len_with_pad = 0;
+ struct hint_short_ssid *s_ssid = NULL;
+ struct hint_bssid *hint_bssid = NULL;
len = sizeof(*cmd);
@@ -2011,6 +2053,14 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
roundup(params->extraie.len, sizeof(u32));
len += extraie_len_with_pad;
+ if (params->num_hint_bssid)
+ len += TLV_HDR_SIZE +
+ params->num_hint_bssid * sizeof(struct hint_bssid);
+
+ if (params->num_hint_s_ssid)
+ len += TLV_HDR_SIZE +
+ params->num_hint_s_ssid * sizeof(struct hint_short_ssid);
+
skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
if (!skb)
return -ENOMEM;
@@ -2032,6 +2082,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
cmd->dwell_time_active = params->dwell_time_active;
cmd->dwell_time_active_2g = params->dwell_time_active_2g;
cmd->dwell_time_passive = params->dwell_time_passive;
+ cmd->dwell_time_active_6g = params->dwell_time_active_6g;
+ cmd->dwell_time_passive_6g = params->dwell_time_passive_6g;
cmd->min_rest_time = params->min_rest_time;
cmd->max_rest_time = params->max_rest_time;
cmd->repeat_probe_time = params->repeat_probe_time;
@@ -2109,6 +2161,68 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,
ptr += extraie_len_with_pad;
+ if (params->num_hint_s_ssid) {
+ len = params->num_hint_s_ssid * sizeof(struct hint_short_ssid);
+ tlv = ptr;
+ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) |
+ FIELD_PREP(WMI_TLV_LEN, len);
+ ptr += TLV_HDR_SIZE;
+ s_ssid = ptr;
+ for (i = 0; i < params->num_hint_s_ssid; ++i) {
+ s_ssid->freq_flags = params->hint_s_ssid[i].freq_flags;
+ s_ssid->short_ssid = params->hint_s_ssid[i].short_ssid;
+ s_ssid++;
+ }
+ ptr += len;
+ }
+
+ if (params->num_hint_bssid) {
+ len = params->num_hint_bssid * sizeof(struct hint_bssid);
+ tlv = ptr;
+ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) |
+ FIELD_PREP(WMI_TLV_LEN, len);
+ ptr += TLV_HDR_SIZE;
+ hint_bssid = ptr;
+ for (i = 0; i < params->num_hint_bssid; ++i) {
+ hint_bssid->freq_flags =
+ params->hint_bssid[i].freq_flags;
+ ether_addr_copy(&params->hint_bssid[i].bssid.addr[0],
+ &hint_bssid->bssid.addr[0]);
+ hint_bssid++;
+ }
+ }
+
+ len = params->num_hint_s_ssid * sizeof(struct hint_short_ssid);
+ tlv = ptr;
+ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) |
+ FIELD_PREP(WMI_TLV_LEN, len);
+ ptr += TLV_HDR_SIZE;
+ if (params->num_hint_s_ssid) {
+ s_ssid = ptr;
+ for (i = 0; i < params->num_hint_s_ssid; ++i) {
+ s_ssid->freq_flags = params->hint_s_ssid[i].freq_flags;
+ s_ssid->short_ssid = params->hint_s_ssid[i].short_ssid;
+ s_ssid++;
+ }
+ }
+ ptr += len;
+
+ len = params->num_hint_bssid * sizeof(struct hint_bssid);
+ tlv = ptr;
+ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_FIXED_STRUCT) |
+ FIELD_PREP(WMI_TLV_LEN, len);
+ ptr += TLV_HDR_SIZE;
+ if (params->num_hint_bssid) {
+ hint_bssid = ptr;
+ for (i = 0; i < params->num_hint_bssid; ++i) {
+ hint_bssid->freq_flags =
+ params->hint_bssid[i].freq_flags;
+ ether_addr_copy(&params->hint_bssid[i].bssid.addr[0],
+ &hint_bssid->bssid.addr[0]);
+ hint_bssid++;
+ }
+ }
+
ret = ath11k_wmi_cmd_send(wmi, skb,
WMI_START_SCAN_CMDID);
if (ret) {
@@ -2178,91 +2292,110 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar,
struct wmi_tlv *tlv;
void *ptr;
int i, ret, len;
+ u16 num_send_chans, num_sends = 0, max_chan_limit = 0;
u32 *reg1, *reg2;
- len = sizeof(*cmd) + TLV_HDR_SIZE +
- sizeof(*chan_info) * chan_list->nallchans;
+ tchan_info = &chan_list->ch_param[0];
+ while (chan_list->nallchans) {
+ len = sizeof(*cmd) + TLV_HDR_SIZE;
+ max_chan_limit = (wmi->wmi_ab->max_msg_len[ar->pdev_idx] - len) /
+ sizeof(*chan_info);
- skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
- if (!skb)
- return -ENOMEM;
+ if (chan_list->nallchans > max_chan_limit)
+ num_send_chans = max_chan_limit;
+ else
+ num_send_chans = chan_list->nallchans;
- cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
- cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_SCAN_CHAN_LIST_CMD) |
- FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+ chan_list->nallchans -= num_send_chans;
+ len += sizeof(*chan_info) * num_send_chans;
- ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
- "WMI no.of chan = %d len = %d\n", chan_list->nallchans, len);
- cmd->pdev_id = chan_list->pdev_id;
- cmd->num_scan_chans = chan_list->nallchans;
-
- ptr = skb->data + sizeof(*cmd);
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
+ if (!skb)
+ return -ENOMEM;
- len = sizeof(*chan_info) * chan_list->nallchans;
- tlv = ptr;
- tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) |
- FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE);
- ptr += TLV_HDR_SIZE;
+ cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_SCAN_CHAN_LIST_CMD) |
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+ cmd->pdev_id = chan_list->pdev_id;
+ cmd->num_scan_chans = num_send_chans;
+ if (num_sends)
+ cmd->flags |= WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG;
- tchan_info = &chan_list->ch_param[0];
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "WMI no.of chan = %d len = %d pdev_id = %d num_sends = %d\n",
+ num_send_chans, len, cmd->pdev_id, num_sends);
- for (i = 0; i < chan_list->nallchans; ++i) {
- chan_info = ptr;
- memset(chan_info, 0, sizeof(*chan_info));
- len = sizeof(*chan_info);
- chan_info->tlv_header = FIELD_PREP(WMI_TLV_TAG,
- WMI_TAG_CHANNEL) |
- FIELD_PREP(WMI_TLV_LEN,
- len - TLV_HDR_SIZE);
-
- reg1 = &chan_info->reg_info_1;
- reg2 = &chan_info->reg_info_2;
- chan_info->mhz = tchan_info->mhz;
- chan_info->band_center_freq1 = tchan_info->cfreq1;
- chan_info->band_center_freq2 = tchan_info->cfreq2;
-
- if (tchan_info->is_chan_passive)
- chan_info->info |= WMI_CHAN_INFO_PASSIVE;
- if (tchan_info->allow_he)
- chan_info->info |= WMI_CHAN_INFO_ALLOW_HE;
- else if (tchan_info->allow_vht)
- chan_info->info |= WMI_CHAN_INFO_ALLOW_VHT;
- else if (tchan_info->allow_ht)
- chan_info->info |= WMI_CHAN_INFO_ALLOW_HT;
- if (tchan_info->half_rate)
- chan_info->info |= WMI_CHAN_INFO_HALF_RATE;
- if (tchan_info->quarter_rate)
- chan_info->info |= WMI_CHAN_INFO_QUARTER_RATE;
-
- chan_info->info |= FIELD_PREP(WMI_CHAN_INFO_MODE,
- tchan_info->phy_mode);
- *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MIN_PWR,
- tchan_info->minpower);
- *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR,
- tchan_info->maxpower);
- *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR,
- tchan_info->maxregpower);
- *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_REG_CLS,
- tchan_info->reg_class_id);
- *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX,
- tchan_info->antennamax);
+ ptr = skb->data + sizeof(*cmd);
- ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
- "WMI chan scan list chan[%d] = %u\n",
- i, chan_info->mhz);
+ len = sizeof(*chan_info) * num_send_chans;
+ tlv = ptr;
+ tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) |
+ FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE);
+ ptr += TLV_HDR_SIZE;
- ptr += sizeof(*chan_info);
+ for (i = 0; i < num_send_chans; ++i) {
+ chan_info = ptr;
+ memset(chan_info, 0, sizeof(*chan_info));
+ len = sizeof(*chan_info);
+ chan_info->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+ WMI_TAG_CHANNEL) |
+ FIELD_PREP(WMI_TLV_LEN,
+ len - TLV_HDR_SIZE);
+
+ reg1 = &chan_info->reg_info_1;
+ reg2 = &chan_info->reg_info_2;
+ chan_info->mhz = tchan_info->mhz;
+ chan_info->band_center_freq1 = tchan_info->cfreq1;
+ chan_info->band_center_freq2 = tchan_info->cfreq2;
+
+ if (tchan_info->is_chan_passive)
+ chan_info->info |= WMI_CHAN_INFO_PASSIVE;
+ if (tchan_info->allow_he)
+ chan_info->info |= WMI_CHAN_INFO_ALLOW_HE;
+ else if (tchan_info->allow_vht)
+ chan_info->info |= WMI_CHAN_INFO_ALLOW_VHT;
+ else if (tchan_info->allow_ht)
+ chan_info->info |= WMI_CHAN_INFO_ALLOW_HT;
+ if (tchan_info->half_rate)
+ chan_info->info |= WMI_CHAN_INFO_HALF_RATE;
+ if (tchan_info->quarter_rate)
+ chan_info->info |= WMI_CHAN_INFO_QUARTER_RATE;
+ if (tchan_info->psc_channel)
+ chan_info->info |= WMI_CHAN_INFO_PSC;
+
+ chan_info->info |= FIELD_PREP(WMI_CHAN_INFO_MODE,
+ tchan_info->phy_mode);
+ *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MIN_PWR,
+ tchan_info->minpower);
+ *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_PWR,
+ tchan_info->maxpower);
+ *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_MAX_REG_PWR,
+ tchan_info->maxregpower);
+ *reg1 |= FIELD_PREP(WMI_CHAN_REG_INFO1_REG_CLS,
+ tchan_info->reg_class_id);
+ *reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX,
+ tchan_info->antennamax);
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "WMI chan scan list chan[%d] = %u, chan_info->info %8x\n",
+ i, chan_info->mhz, chan_info->info);
+
+ ptr += sizeof(*chan_info);
+
+ tchan_info++;
+ }
- tchan_info++;
- }
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_SCAN_CHAN_LIST_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_SCAN_CHAN_LIST cmd\n");
+ dev_kfree_skb(skb);
+ return ret;
+ }
- ret = ath11k_wmi_cmd_send(wmi, skb, WMI_SCAN_CHAN_LIST_CMDID);
- if (ret) {
- ath11k_warn(ar->ab, "failed to send WMI_SCAN_CHAN_LIST cmd\n");
- dev_kfree_skb(skb);
+ num_sends++;
}
- return ret;
+ return 0;
}
int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id,
@@ -3265,6 +3398,236 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab)
return ath11k_init_cmd_send(&wmi_sc->wmi[0], &init_param);
}
+int ath11k_wmi_vdev_spectral_conf(struct ath11k *ar,
+ struct ath11k_wmi_vdev_spectral_conf_param *param)
+{
+ struct ath11k_wmi_vdev_spectral_conf_cmd *cmd;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct ath11k_wmi_vdev_spectral_conf_cmd *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+ WMI_TAG_VDEV_SPECTRAL_CONFIGURE_CMD) |
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+
+ memcpy(&cmd->param, param, sizeof(*param));
+
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb,
+ WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab,
+ "failed to send spectral scan config wmi cmd\n");
+ goto err;
+ }
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "WMI spectral scan config cmd vdev_id 0x%x\n",
+ param->vdev_id);
+
+ return 0;
+err:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+int ath11k_wmi_vdev_spectral_enable(struct ath11k *ar, u32 vdev_id,
+ u32 trigger, u32 enable)
+{
+ struct ath11k_wmi_vdev_spectral_enable_cmd *cmd;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct ath11k_wmi_vdev_spectral_enable_cmd *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+ WMI_TAG_VDEV_SPECTRAL_ENABLE_CMD) |
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+
+ cmd->vdev_id = vdev_id;
+ cmd->trigger_cmd = trigger;
+ cmd->enable_cmd = enable;
+
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb,
+ WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab,
+ "failed to send spectral enable wmi cmd\n");
+ goto err;
+ }
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "WMI spectral enable cmd vdev id 0x%x\n",
+ vdev_id);
+
+ return 0;
+err:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+int ath11k_wmi_pdev_dma_ring_cfg(struct ath11k *ar,
+ struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd *param)
+{
+ struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd *cmd;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_DMA_RING_CFG_REQ) |
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+
+ cmd->pdev_id = param->pdev_id;
+ cmd->module_id = param->module_id;
+ cmd->base_paddr_lo = param->base_paddr_lo;
+ cmd->base_paddr_hi = param->base_paddr_hi;
+ cmd->head_idx_paddr_lo = param->head_idx_paddr_lo;
+ cmd->head_idx_paddr_hi = param->head_idx_paddr_hi;
+ cmd->tail_idx_paddr_lo = param->tail_idx_paddr_lo;
+ cmd->tail_idx_paddr_hi = param->tail_idx_paddr_hi;
+ cmd->num_elems = param->num_elems;
+ cmd->buf_size = param->buf_size;
+ cmd->num_resp_per_event = param->num_resp_per_event;
+ cmd->event_timeout_ms = param->event_timeout_ms;
+
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb,
+ WMI_PDEV_DMA_RING_CFG_REQ_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab,
+ "failed to send dma ring cfg req wmi cmd\n");
+ goto err;
+ }
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "WMI DMA ring cfg req cmd pdev_id 0x%x\n",
+ param->pdev_id);
+
+ return 0;
+err:
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static int ath11k_wmi_tlv_dma_buf_entry_parse(struct ath11k_base *soc,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ struct wmi_tlv_dma_buf_release_parse *parse = data;
+
+ if (tag != WMI_TAG_DMA_BUF_RELEASE_ENTRY)
+ return -EPROTO;
+
+ if (parse->num_buf_entry >= parse->fixed.num_buf_release_entry)
+ return -ENOBUFS;
+
+ parse->num_buf_entry++;
+ return 0;
+}
+
+static int ath11k_wmi_tlv_dma_buf_meta_parse(struct ath11k_base *soc,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ struct wmi_tlv_dma_buf_release_parse *parse = data;
+
+ if (tag != WMI_TAG_DMA_BUF_RELEASE_SPECTRAL_META_DATA)
+ return -EPROTO;
+
+ if (parse->num_meta >= parse->fixed.num_meta_data_entry)
+ return -ENOBUFS;
+
+ parse->num_meta++;
+ return 0;
+}
+
+static int ath11k_wmi_tlv_dma_buf_parse(struct ath11k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ struct wmi_tlv_dma_buf_release_parse *parse = data;
+ int ret;
+
+ switch (tag) {
+ case WMI_TAG_DMA_BUF_RELEASE:
+ memcpy(&parse->fixed, ptr,
+ sizeof(struct ath11k_wmi_dma_buf_release_fixed_param));
+ parse->fixed.pdev_id = DP_HW2SW_MACID(parse->fixed.pdev_id);
+ break;
+ case WMI_TAG_ARRAY_STRUCT:
+ if (!parse->buf_entry_done) {
+ parse->num_buf_entry = 0;
+ parse->buf_entry = (struct wmi_dma_buf_release_entry *)ptr;
+
+ ret = ath11k_wmi_tlv_iter(ab, ptr, len,
+ ath11k_wmi_tlv_dma_buf_entry_parse,
+ parse);
+ if (ret) {
+ ath11k_warn(ab, "failed to parse dma buf entry tlv %d\n",
+ ret);
+ return ret;
+ }
+
+ parse->buf_entry_done = true;
+ } else if (!parse->meta_data_done) {
+ parse->num_meta = 0;
+ parse->meta_data = (struct wmi_dma_buf_release_meta_data *)ptr;
+
+ ret = ath11k_wmi_tlv_iter(ab, ptr, len,
+ ath11k_wmi_tlv_dma_buf_meta_parse,
+ parse);
+ if (ret) {
+ ath11k_warn(ab, "failed to parse dma buf meta tlv %d\n",
+ ret);
+ return ret;
+ }
+
+ parse->meta_data_done = true;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void ath11k_wmi_pdev_dma_ring_buf_release_event(struct ath11k_base *ab,
+ struct sk_buff *skb)
+{
+ struct wmi_tlv_dma_buf_release_parse parse = { };
+ struct ath11k_dbring_buf_release_event param;
+ int ret;
+
+ ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len,
+ ath11k_wmi_tlv_dma_buf_parse,
+ &parse);
+ if (ret) {
+ ath11k_warn(ab, "failed to parse dma buf release tlv %d\n", ret);
+ return;
+ }
+
+ param.fixed = parse.fixed;
+ param.buf_entry = parse.buf_entry;
+ param.num_buf_entry = parse.num_buf_entry;
+ param.meta_data = parse.meta_data;
+ param.num_meta = parse.num_meta;
+
+ ret = ath11k_dbring_buffer_release_event(ab, &param);
+ if (ret) {
+ ath11k_warn(ab, "failed to handle dma buf release event %d\n", ret);
+ return;
+ }
+}
+
static int ath11k_wmi_tlv_hw_mode_caps_parse(struct ath11k_base *soc,
u16 tag, u16 len,
const void *ptr, void *data)
@@ -3445,6 +3808,95 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc,
return 0;
}
+static int ath11k_wmi_tlv_dma_ring_caps_parse(struct ath11k_base *soc,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ struct wmi_tlv_dma_ring_caps_parse *parse = data;
+
+ if (tag != WMI_TAG_DMA_RING_CAPABILITIES)
+ return -EPROTO;
+
+ parse->n_dma_ring_caps++;
+ return 0;
+}
+
+static int ath11k_wmi_alloc_dbring_caps(struct ath11k_base *ab,
+ u32 num_cap)
+{
+ size_t sz;
+ void *ptr;
+
+ sz = num_cap * sizeof(struct ath11k_dbring_cap);
+ ptr = kzalloc(sz, GFP_ATOMIC);
+ if (!ptr)
+ return -ENOMEM;
+
+ ab->db_caps = ptr;
+ ab->num_db_cap = num_cap;
+
+ return 0;
+}
+
+static void ath11k_wmi_free_dbring_caps(struct ath11k_base *ab)
+{
+ kfree(ab->db_caps);
+ ab->db_caps = NULL;
+}
+
+static int ath11k_wmi_tlv_dma_ring_caps(struct ath11k_base *ab,
+ u16 len, const void *ptr, void *data)
+{
+ struct wmi_tlv_dma_ring_caps_parse *dma_caps_parse = data;
+ struct wmi_dma_ring_capabilities *dma_caps;
+ struct ath11k_dbring_cap *dir_buff_caps;
+ int ret;
+ u32 i;
+
+ dma_caps_parse->n_dma_ring_caps = 0;
+ dma_caps = (struct wmi_dma_ring_capabilities *)ptr;
+ ret = ath11k_wmi_tlv_iter(ab, ptr, len,
+ ath11k_wmi_tlv_dma_ring_caps_parse,
+ dma_caps_parse);
+ if (ret) {
+ ath11k_warn(ab, "failed to parse dma ring caps tlv %d\n", ret);
+ return ret;
+ }
+
+ if (!dma_caps_parse->n_dma_ring_caps)
+ return 0;
+
+ if (ab->num_db_cap) {
+ ath11k_warn(ab, "Already processed, so ignoring dma ring caps\n");
+ return 0;
+ }
+
+ ret = ath11k_wmi_alloc_dbring_caps(ab, dma_caps_parse->n_dma_ring_caps);
+ if (ret)
+ return ret;
+
+ dir_buff_caps = ab->db_caps;
+ for (i = 0; i < dma_caps_parse->n_dma_ring_caps; i++) {
+ if (dma_caps[i].module_id >= WMI_DIRECT_BUF_MAX) {
+ ath11k_warn(ab, "Invalid module id %d\n", dma_caps[i].module_id);
+ ret = -EINVAL;
+ goto free_dir_buff;
+ }
+
+ dir_buff_caps[i].id = dma_caps[i].module_id;
+ dir_buff_caps[i].pdev_id = DP_HW2SW_MACID(dma_caps[i].pdev_id);
+ dir_buff_caps[i].min_elem = dma_caps[i].min_elem;
+ dir_buff_caps[i].min_buf_sz = dma_caps[i].min_buf_sz;
+ dir_buff_caps[i].min_buf_align = dma_caps[i].min_buf_align;
+ }
+
+ return 0;
+
+free_dir_buff:
+ ath11k_wmi_free_dbring_caps(ab);
+ return ret;
+}
+
static int ath11k_wmi_tlv_svc_rdy_ext_parse(struct ath11k_base *ab,
u16 tag, u16 len,
const void *ptr, void *data)
@@ -3501,7 +3953,19 @@ static int ath11k_wmi_tlv_svc_rdy_ext_parse(struct ath11k_base *ab,
return ret;
svc_rdy_ext->ext_hal_reg_done = true;
- complete(&ab->wmi_ab.service_ready);
+ } else if (!svc_rdy_ext->mac_phy_chainmask_combo_done) {
+ svc_rdy_ext->mac_phy_chainmask_combo_done = true;
+ } else if (!svc_rdy_ext->mac_phy_chainmask_cap_done) {
+ svc_rdy_ext->mac_phy_chainmask_cap_done = true;
+ } else if (!svc_rdy_ext->oem_dma_ring_cap_done) {
+ svc_rdy_ext->oem_dma_ring_cap_done = true;
+ } else if (!svc_rdy_ext->dma_ring_cap_done) {
+ ret = ath11k_wmi_tlv_dma_ring_caps(ab, len, ptr,
+ &svc_rdy_ext->dma_caps_parse);
+ if (ret)
+ return ret;
+
+ svc_rdy_ext->dma_ring_cap_done = true;
}
break;
@@ -3522,11 +3986,66 @@ static int ath11k_service_ready_ext_event(struct ath11k_base *ab,
&svc_rdy_ext);
if (ret) {
ath11k_warn(ab, "failed to parse tlv %d\n", ret);
- return ret;
+ goto err;
}
+ if (!test_bit(WMI_TLV_SERVICE_EXT2_MSG, ab->wmi_ab.svc_map))
+ complete(&ab->wmi_ab.service_ready);
+
kfree(svc_rdy_ext.mac_phy_caps);
return 0;
+
+err:
+ ath11k_wmi_free_dbring_caps(ab);
+ return ret;
+}
+
+static int ath11k_wmi_tlv_svc_rdy_ext2_parse(struct ath11k_base *ab,
+ u16 tag, u16 len,
+ const void *ptr, void *data)
+{
+ struct wmi_tlv_svc_rdy_ext2_parse *parse = data;
+ int ret;
+
+ switch (tag) {
+ case WMI_TAG_ARRAY_STRUCT:
+ if (!parse->dma_ring_cap_done) {
+ ret = ath11k_wmi_tlv_dma_ring_caps(ab, len, ptr,
+ &parse->dma_caps_parse);
+ if (ret)
+ return ret;
+
+ parse->dma_ring_cap_done = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ath11k_service_ready_ext2_event(struct ath11k_base *ab,
+ struct sk_buff *skb)
+{
+ struct wmi_tlv_svc_rdy_ext2_parse svc_rdy_ext2 = { };
+ int ret;
+
+ ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len,
+ ath11k_wmi_tlv_svc_rdy_ext2_parse,
+ &svc_rdy_ext2);
+ if (ret) {
+ ath11k_warn(ab, "failed to parse ext2 event tlv %d\n", ret);
+ goto err;
+ }
+
+ complete(&ab->wmi_ab.service_ready);
+
+ return 0;
+
+err:
+ ath11k_wmi_free_dbring_caps(ab);
+ return ret;
}
static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buff *skb,
@@ -3822,6 +4341,7 @@ static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab,
}
hdr->pdev_id = ev->pdev_id;
+ hdr->chan_freq = ev->chan_freq;
hdr->channel = ev->channel;
hdr->snr = ev->snr;
hdr->rate = ev->rate;
@@ -5193,7 +5713,9 @@ static void ath11k_mgmt_rx_event(struct ath11k_base *ab, struct sk_buff *skb)
if (rx_ev.status & WMI_RX_STATUS_ERR_MIC)
status->flag |= RX_FLAG_MMIC_ERROR;
- if (rx_ev.channel >= 1 && rx_ev.channel <= 14) {
+ if (rx_ev.chan_freq >= ATH11K_MIN_6G_FREQ) {
+ status->band = NL80211_BAND_6GHZ;
+ } else if (rx_ev.channel >= 1 && rx_ev.channel <= 14) {
status->band = NL80211_BAND_2GHZ;
} else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH11K_MAX_5G_CHAN) {
status->band = NL80211_BAND_5GHZ;
@@ -5206,9 +5728,10 @@ static void ath11k_mgmt_rx_event(struct ath11k_base *ab, struct sk_buff *skb)
goto exit;
}
- if (rx_ev.phy_mode == MODE_11B && status->band == NL80211_BAND_5GHZ)
+ if (rx_ev.phy_mode == MODE_11B &&
+ (status->band == NL80211_BAND_5GHZ || status->band == NL80211_BAND_6GHZ))
ath11k_dbg(ab, ATH11K_DBG_WMI,
- "wmi mgmt rx 11b (CCK) on 5GHz\n");
+ "wmi mgmt rx 11b (CCK) on 5/6GHz, band = %d\n", status->band);
sband = &ar->mac.sbands[status->band];
@@ -5933,6 +6456,9 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
case WMI_SERVICE_READY_EXT_EVENTID:
ath11k_service_ready_ext_event(ab, skb);
break;
+ case WMI_SERVICE_READY_EXT2_EVENTID:
+ ath11k_service_ready_ext2_event(ab, skb);
+ break;
case WMI_REG_CHAN_LIST_CC_EVENTID:
ath11k_reg_chan_list_event(ab, skb);
break;
@@ -5994,12 +6520,16 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
case WMI_PDEV_TEMPERATURE_EVENTID:
ath11k_wmi_pdev_temperature_event(ab, skb);
break;
+ case WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID:
+ ath11k_wmi_pdev_dma_ring_buf_release_event(ab, skb);
+ break;
/* add Unsupported events here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
case WMI_VDEV_DELETE_RESP_EVENTID:
case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
case WMI_TWT_ENABLE_EVENTID:
case WMI_TWT_DISABLE_EVENTID:
+ case WMI_PDEV_DMA_RING_CFG_RSP_EVENTID:
ath11k_dbg(ab, ATH11K_DBG_WMI,
"ignoring unsupported event 0x%x\n", id);
break;
@@ -6213,4 +6743,6 @@ void ath11k_wmi_detach(struct ath11k_base *ab)
for (i = 0; i < ab->htc.wmi_ep_count; i++)
ath11k_wmi_pdev_detach(ab, i);
+
+ ath11k_wmi_free_dbring_caps(ab);
}
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index b9f3e559ced7..5a32ba0eb4f5 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -24,6 +24,8 @@ struct ath11k_fw_stats;
#define HE_PET_8_USEC 1
#define HE_PET_16_USEC 2
+#define WMI_MAX_CHAINS 8
+
#define WMI_MAX_NUM_SS MAX_HE_NSS
#define WMI_MAX_NUM_RU MAX_HE_RU
@@ -50,10 +52,20 @@ struct wmi_tlv {
#define WMI_MAX_MEM_REQS 32
#define ATH11K_MAX_HW_LISTEN_INTERVAL 5
+#define WLAN_SCAN_MAX_HINT_S_SSID 10
+#define WLAN_SCAN_MAX_HINT_BSSID 10
+#define MAX_RNR_BSS 5
+
+#define WLAN_SCAN_MAX_HINT_S_SSID 10
+#define WLAN_SCAN_MAX_HINT_BSSID 10
+#define MAX_RNR_BSS 5
+
#define WLAN_SCAN_PARAMS_MAX_SSID 16
#define WLAN_SCAN_PARAMS_MAX_BSSID 4
#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256
+#define WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG 1
+
#define WMI_BA_MODE_BUFFER_SIZE_256 3
/*
* HW mode config type replicated from FW header
@@ -586,6 +598,11 @@ enum wmi_tlv_event_id {
WMI_PDEV_DMA_RING_CFG_RSP_EVENTID,
WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID,
WMI_PDEV_CTL_FAILSAFE_CHECK_EVENTID,
+ WMI_PDEV_CSC_SWITCH_COUNT_STATUS_EVENTID,
+ WMI_PDEV_COLD_BOOT_CAL_DATA_EVENTID,
+ WMI_PDEV_RAP_INFO_EVENTID,
+ WMI_CHAN_RF_CHARACTERIZATION_INFO_EVENTID,
+ WMI_SERVICE_READY_EXT2_EVENTID,
WMI_VDEV_START_RESP_EVENTID = WMI_TLV_CMD(WMI_GRP_VDEV),
WMI_VDEV_STOPPED_EVENTID,
WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID,
@@ -1011,6 +1028,7 @@ enum wmi_tlv_vdev_param {
WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME,
WMI_VDEV_PARAM_BA_MODE = 0x7e,
WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE = 0x87,
+ WMI_VDEV_PARAM_6GHZ_PARAMS = 0x99,
WMI_VDEV_PARAM_PROTOTYPE = 0x8000,
WMI_VDEV_PARAM_BSS_COLOR,
WMI_VDEV_PARAM_SET_HEMU_MODE,
@@ -2013,9 +2031,10 @@ enum wmi_tlv_service {
WMI_TLV_SERVICE_DSM_ROAM_FILTER = 211,
WMI_TLV_SERVICE_PACKET_CAPTURE_SUPPORT = 212,
WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET = 213,
+ WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219,
+ WMI_TLV_SERVICE_EXT2_MSG = 220,
WMI_MAX_EXT_SERVICE
-
};
enum {
@@ -2076,6 +2095,14 @@ enum wmi_beacon_gen_mode {
WMI_BEACON_BURST_MODE = 1
};
+enum wmi_direct_buffer_module {
+ WMI_DIRECT_BUF_SPECTRAL = 0,
+ WMI_DIRECT_BUF_CFR = 1,
+
+ /* keep it last */
+ WMI_DIRECT_BUF_MAX
+};
+
struct wmi_host_pdev_band_to_mac {
u32 pdev_id;
u32 start_freq;
@@ -2382,6 +2409,15 @@ struct wmi_mac_addr {
} __packed;
} __packed;
+struct wmi_dma_ring_capabilities {
+ u32 tlv_header;
+ u32 pdev_id;
+ u32 module_id;
+ u32 min_elem;
+ u32 min_buf_sz;
+ u32 min_buf_align;
+} __packed;
+
struct wmi_ready_event_min {
struct wmi_abi_version fw_abi_vers;
struct wmi_mac_addr mac_addr;
@@ -2519,7 +2555,8 @@ struct channel_param {
allow_ht:1,
allow_vht:1,
allow_he:1,
- set_agile:1;
+ set_agile:1,
+ psc_channel:1;
u32 phy_mode;
u32 cfreq1;
u32 cfreq2;
@@ -3059,6 +3096,9 @@ struct wmi_start_scan_cmd {
u32 num_vendor_oui;
u32 scan_ctrl_flags_ext;
u32 dwell_time_active_2g;
+ u32 dwell_time_active_6g;
+ u32 dwell_time_passive_6g;
+ u32 scan_start_offset;
} __packed;
#define WMI_SCAN_FLAG_PASSIVE 0x1
@@ -3098,6 +3138,16 @@ enum {
((flag) |= (((mode) << WMI_SCAN_DWELL_MODE_SHIFT) & \
WMI_SCAN_DWELL_MODE_MASK))
+struct hint_short_ssid {
+ u32 freq_flags;
+ u32 short_ssid;
+};
+
+struct hint_bssid {
+ u32 freq_flags;
+ struct wmi_mac_addr bssid;
+};
+
struct scan_req_params {
u32 scan_id;
u32 scan_req_id;
@@ -3125,6 +3175,8 @@ struct scan_req_params {
u32 dwell_time_active;
u32 dwell_time_active_2g;
u32 dwell_time_passive;
+ u32 dwell_time_active_6g;
+ u32 dwell_time_passive_6g;
u32 min_rest_time;
u32 max_rest_time;
u32 repeat_probe_time;
@@ -3175,6 +3227,10 @@ struct scan_req_params {
struct element_info extraie;
struct element_info htcap;
struct element_info vhtcap;
+ u32 num_hint_s_ssid;
+ u32 num_hint_bssid;
+ struct hint_short_ssid hint_s_ssid[WLAN_SCAN_MAX_HINT_S_SSID];
+ struct hint_bssid hint_bssid[WLAN_SCAN_MAX_HINT_BSSID];
};
struct wmi_ssid_arg {
@@ -3264,6 +3320,7 @@ struct wmi_bcn_send_from_host_cmd {
#define WMI_CHAN_INFO_QUARTER_RATE BIT(15)
#define WMI_CHAN_INFO_DFS_FREQ2 BIT(16)
#define WMI_CHAN_INFO_ALLOW_HE BIT(17)
+#define WMI_CHAN_INFO_PSC BIT(18)
#define WMI_CHAN_REG_INFO1_MIN_PWR GENMASK(7, 0)
#define WMI_CHAN_REG_INFO1_MAX_PWR GENMASK(15, 8)
@@ -3444,6 +3501,7 @@ struct peer_assoc_params {
u32 tx_max_rate;
u32 tx_mcs_set;
u8 vht_capable;
+ u8 min_data_rate;
u32 tx_max_mcs_nss;
u32 peer_bw_rxnss_override;
bool is_pmf_enabled;
@@ -3472,6 +3530,7 @@ struct peer_assoc_params {
bool he_flag;
u32 peer_he_cap_macinfo[2];
u32 peer_he_cap_macinfo_internal;
+ u32 peer_he_caps_6ghz;
u32 peer_he_ops;
u32 peer_he_cap_phyinfo[WMI_HOST_MAX_HECAP_PHY_SIZE];
u32 peer_he_mcs_count;
@@ -3509,6 +3568,8 @@ struct wmi_peer_assoc_complete_cmd {
u32 peer_he_mcs;
u32 peer_he_cap_info_ext;
u32 peer_he_cap_info_internal;
+ u32 min_data_rate;
+ u32 peer_he_caps_6ghz;
} __packed;
struct wmi_stop_scan_cmd {
@@ -4228,6 +4289,7 @@ struct wmi_pdev_temperature_event {
#define WLAN_MGMT_TXRX_HOST_MAX_ANTENNA 4
struct mgmt_rx_event_params {
+ u32 chan_freq;
u32 channel;
u32 snr;
u8 rssi_ctl[WLAN_MGMT_TXRX_HOST_MAX_ANTENNA];
@@ -4257,6 +4319,7 @@ struct wmi_mgmt_rx_hdr {
u32 rx_tsf_l32;
u32 rx_tsf_u32;
u32 pdev_id;
+ u32 chan_freq;
} __packed;
#define MAX_ANTENNA_EIGHT 8
@@ -4734,6 +4797,117 @@ struct ath11k_wmi_pdev_lro_config_cmd {
u32 pdev_id;
} __packed;
+#define ATH11K_WMI_SPECTRAL_COUNT_DEFAULT 0
+#define ATH11K_WMI_SPECTRAL_PERIOD_DEFAULT 224
+#define ATH11K_WMI_SPECTRAL_PRIORITY_DEFAULT 1
+#define ATH11K_WMI_SPECTRAL_FFT_SIZE_DEFAULT 7
+#define ATH11K_WMI_SPECTRAL_GC_ENA_DEFAULT 1
+#define ATH11K_WMI_SPECTRAL_RESTART_ENA_DEFAULT 0
+#define ATH11K_WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT -96
+#define ATH11K_WMI_SPECTRAL_INIT_DELAY_DEFAULT 80
+#define ATH11K_WMI_SPECTRAL_NB_TONE_THR_DEFAULT 12
+#define ATH11K_WMI_SPECTRAL_STR_BIN_THR_DEFAULT 8
+#define ATH11K_WMI_SPECTRAL_WB_RPT_MODE_DEFAULT 0
+#define ATH11K_WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT 0
+#define ATH11K_WMI_SPECTRAL_RSSI_THR_DEFAULT 0xf0
+#define ATH11K_WMI_SPECTRAL_PWR_FORMAT_DEFAULT 0
+#define ATH11K_WMI_SPECTRAL_RPT_MODE_DEFAULT 2
+#define ATH11K_WMI_SPECTRAL_BIN_SCALE_DEFAULT 1
+#define ATH11K_WMI_SPECTRAL_DBM_ADJ_DEFAULT 1
+#define ATH11K_WMI_SPECTRAL_CHN_MASK_DEFAULT 1
+
+struct ath11k_wmi_vdev_spectral_conf_param {
+ u32 vdev_id;
+ u32 scan_count;
+ u32 scan_period;
+ u32 scan_priority;
+ u32 scan_fft_size;
+ u32 scan_gc_ena;
+ u32 scan_restart_ena;
+ u32 scan_noise_floor_ref;
+ u32 scan_init_delay;
+ u32 scan_nb_tone_thr;
+ u32 scan_str_bin_thr;
+ u32 scan_wb_rpt_mode;
+ u32 scan_rssi_rpt_mode;
+ u32 scan_rssi_thr;
+ u32 scan_pwr_format;
+ u32 scan_rpt_mode;
+ u32 scan_bin_scale;
+ u32 scan_dbm_adj;
+ u32 scan_chn_mask;
+} __packed;
+
+struct ath11k_wmi_vdev_spectral_conf_cmd {
+ u32 tlv_header;
+ struct ath11k_wmi_vdev_spectral_conf_param param;
+} __packed;
+
+#define ATH11K_WMI_SPECTRAL_TRIGGER_CMD_TRIGGER 1
+#define ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR 2
+#define ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE 1
+#define ATH11K_WMI_SPECTRAL_ENABLE_CMD_DISABLE 2
+
+struct ath11k_wmi_vdev_spectral_enable_cmd {
+ u32 tlv_header;
+ u32 vdev_id;
+ u32 trigger_cmd;
+ u32 enable_cmd;
+} __packed;
+
+struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd {
+ u32 tlv_header;
+ u32 pdev_id;
+ u32 module_id; /* see enum wmi_direct_buffer_module */
+ u32 base_paddr_lo;
+ u32 base_paddr_hi;
+ u32 head_idx_paddr_lo;
+ u32 head_idx_paddr_hi;
+ u32 tail_idx_paddr_lo;
+ u32 tail_idx_paddr_hi;
+ u32 num_elems; /* Number of elems in the ring */
+ u32 buf_size; /* size of allocated buffer in bytes */
+
+ /* Number of wmi_dma_buf_release_entry packed together */
+ u32 num_resp_per_event;
+
+ /* Target should timeout and send whatever resp
+ * it has if this time expires, units in milliseconds
+ */
+ u32 event_timeout_ms;
+} __packed;
+
+struct ath11k_wmi_dma_buf_release_fixed_param {
+ u32 pdev_id;
+ u32 module_id;
+ u32 num_buf_release_entry;
+ u32 num_meta_data_entry;
+} __packed;
+
+struct wmi_dma_buf_release_entry {
+ u32 tlv_header;
+ u32 paddr_lo;
+
+ /* Bits 11:0: address of data
+ * Bits 31:12: host context data
+ */
+ u32 paddr_hi;
+} __packed;
+
+#define WMI_SPECTRAL_META_INFO1_FREQ1 GENMASK(15, 0)
+#define WMI_SPECTRAL_META_INFO1_FREQ2 GENMASK(31, 16)
+
+#define WMI_SPECTRAL_META_INFO2_CHN_WIDTH GENMASK(7, 0)
+
+struct wmi_dma_buf_release_meta_data {
+ u32 tlv_header;
+ s32 noise_floor[WMI_MAX_CHAINS];
+ u32 reset_delay;
+ u32 freq1;
+ u32 freq2;
+ u32 ch_width;
+} __packed;
+
struct target_resource_config {
u32 num_vdevs;
u32 num_peers;
@@ -4941,4 +5115,10 @@ int ath11k_wmi_send_obss_color_collision_cfg_cmd(struct ath11k *ar, u32 vdev_id,
int ath11k_wmi_send_bss_color_change_enable_cmd(struct ath11k *ar, u32 vdev_id,
bool enable);
int ath11k_wmi_pdev_lro_cfg(struct ath11k *ar, int pdev_id);
+int ath11k_wmi_pdev_dma_ring_cfg(struct ath11k *ar,
+ struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd *param);
+int ath11k_wmi_vdev_spectral_enable(struct ath11k *ar, u32 vdev_id,
+ u32 trigger, u32 enable);
+int ath11k_wmi_vdev_spectral_conf(struct ath11k *ar,
+ struct ath11k_wmi_vdev_spectral_conf_param *param);
#endif
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index 42bfdb4a6214..d5e9af2dddd8 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -34,7 +34,7 @@ config ATH9K
APs that come with these cards refer to ath9k wiki
products page:
- http://wireless.kernel.org/en/users/Drivers/ath9k/products
+ https://wireless.wiki.kernel.org/en/users/Drivers/ath9k/products
If you choose to build a module, it'll be called ath9k.
@@ -185,7 +185,8 @@ config ATH9K_HTC
Support for Atheros HTC based cards.
Chipsets supported: AR9271
- For more information: http://wireless.kernel.org/en/users/Drivers/ath9k_htc
+ For more information:
+ https://wireless.wiki.kernel.org/en/users/Drivers/ath9k_htc
The built module will be ath9k_htc.
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 052deffb4c9d..8c97db73e34c 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2410,7 +2410,7 @@ static u8 fixup_chainmask(u8 chip_chainmask, u8 eeprom_chainmask)
* of tests. The testing requirements are going to be documented. Desired
* test requirements are documented at:
*
- * http://wireless.kernel.org/en/users/Drivers/ath9k/dfs
+ * https://wireless.wiki.kernel.org/en/users/Drivers/ath9k/dfs
*
* Once a new chipset gets properly tested an individual commit can be used
* to document the testing for DFS for that chipset.
diff --git a/drivers/net/wireless/ath/carl9170/Kconfig b/drivers/net/wireless/ath/carl9170/Kconfig
index b1bce7aad399..b2d760873992 100644
--- a/drivers/net/wireless/ath/carl9170/Kconfig
+++ b/drivers/net/wireless/ath/carl9170/Kconfig
@@ -10,7 +10,7 @@ config CARL9170
It needs a special firmware (carl9170-1.fw), which can be downloaded
from our wiki here:
- <http://wireless.kernel.org/en/users/Drivers/carl9170>
+ <https://wireless.wiki.kernel.org/en/users/Drivers/carl9170>
If you choose to build a module, it'll be called carl9170.
diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c
index 486957a04bd1..ead79335823a 100644
--- a/drivers/net/wireless/ath/carl9170/usb.c
+++ b/drivers/net/wireless/ath/carl9170/usb.c
@@ -61,7 +61,7 @@ MODULE_ALIAS("arusb_lnx");
* Note:
*
* Always update our wiki's device list (located at:
- * http://wireless.kernel.org/en/users/Drivers/ar9170/devices ),
+ * https://wireless.wiki.kernel.org/en/users/Drivers/ar9170/devices ),
* whenever you add a new device.
*/
static const struct usb_device_id carl9170_usb_ids[] = {
diff --git a/drivers/net/wireless/ath/spectral_common.h b/drivers/net/wireless/ath/spectral_common.h
index 0d742acb1599..9c2e5458e425 100644
--- a/drivers/net/wireless/ath/spectral_common.h
+++ b/drivers/net/wireless/ath/spectral_common.h
@@ -24,6 +24,7 @@
* could be acquired so far.
*/
#define SPECTRAL_ATH10K_MAX_NUM_BINS 256
+#define SPECTRAL_ATH11K_MAX_NUM_BINS 512
/* FFT sample format given to userspace via debugfs.
*
@@ -37,6 +38,7 @@ enum ath_fft_sample_type {
ATH_FFT_SAMPLE_HT20 = 1,
ATH_FFT_SAMPLE_HT20_40,
ATH_FFT_SAMPLE_ATH10K,
+ ATH_FFT_SAMPLE_ATH11K
};
struct fft_sample_tlv {
@@ -110,4 +112,19 @@ struct fft_sample_ath10k {
u8 data[0];
} __packed;
+struct fft_sample_ath11k {
+ struct fft_sample_tlv tlv;
+ u8 chan_width_mhz;
+ s8 max_index;
+ u8 max_exp;
+ __be16 freq1;
+ __be16 freq2;
+ __be16 max_magnitude;
+ __be16 rssi;
+ __be32 tsf;
+ __be32 noise;
+
+ u8 data[0];
+} __packed;
+
#endif /* SPECTRAL_COMMON_H */
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
index dadba2d41bbb..6a95b199bf62 100644
--- a/drivers/net/wireless/ath/wil6210/Kconfig
+++ b/drivers/net/wireless/ath/wil6210/Kconfig
@@ -10,7 +10,7 @@ config WIL6210
wil6210 chip by Wilocity. It supports operation on the
60 GHz band, covered by the IEEE802.11ad standard.
- http://wireless.kernel.org/en/users/Drivers/wil6210
+ https://wireless.wiki.kernel.org/en/users/Drivers/wil6210
If you choose to build it as a module, it will be called
wil6210