Commit f5f58a0b authored by Kalle Valo's avatar Kalle Valo

Merge ath-next from git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git

ath.git patches for v5.9. Major changes:

ath11k

* add 6G band support

* add spectral scan support
parents b3a9e3b9 01e34233
......@@ -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
......
......@@ -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;
}
......
......@@ -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.
......@@ -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)
......@@ -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 @@ static int ath11k_core_pdev_create(struct ath11k_base *ab)
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);
......
......@@ -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;
......@@ -536,6 +545,9 @@ struct ath11k {
u32 cached_ppdu_id;
#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 *));
};
......
// 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);
}
/* 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 */
......@@ -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;
}
......
......@@ -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;
......
......@@ -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
......
......@@ -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;
......
......@@ -121,8 +121,10 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif,
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 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif,
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 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif,
* 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;
......
......@@ -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);
}
......
......@@ -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,
......
// 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;
}
/* 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 */
......@@ -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);
}
......@@ -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
......@@ -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.
......
......@@ -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.
......
......@@ -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.
......
......@@ -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[] = {
......
......@@ -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 */
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment