mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-18 18:01:23 +00:00
769 lines
20 KiB
Diff
769 lines
20 KiB
Diff
--- a/drivers/net/wireless/ath/ath11k/Kconfig
|
|
+++ b/drivers/net/wireless/ath/ath11k/Kconfig
|
|
@@ -59,3 +59,12 @@ config ATH11K_SPECTRAL
|
|
Enable ath11k spectral scan support
|
|
|
|
Say Y to enable access to the FFT/spectral data via debugfs.
|
|
+
|
|
+config ATH11K_PKTLOG
|
|
+ bool "ath11k packet logging support"
|
|
+ depends on ATH11K_DEBUGFS
|
|
+ ---help---
|
|
+ Say Y to dump frame information during frame transmission
|
|
+ and reception, rate information and ani state. The packet
|
|
+ log uses ring buffer to dump the data. The buffer size,
|
|
+ frame filters can be alterted by debugfs entries.
|
|
--- a/drivers/net/wireless/ath/ath11k/Makefile
|
|
+++ b/drivers/net/wireless/ath/ath11k/Makefile
|
|
@@ -23,6 +23,7 @@ ath11k-$(CPTCFG_NL80211_TESTMODE) += tes
|
|
ath11k-$(CPTCFG_ATH11K_TRACING) += trace.o
|
|
ath11k-$(CONFIG_THERMAL) += thermal.o
|
|
ath11k-$(CPTCFG_ATH11K_SPECTRAL) += spectral.o
|
|
+ath11k-$(CPTCFG_ATH11K_PKTLOG) += pktlog.o
|
|
|
|
obj-$(CPTCFG_ATH11K_AHB) += ath11k_ahb.o
|
|
ath11k_ahb-y += ahb.o
|
|
--- a/drivers/net/wireless/ath/ath11k/core.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/core.h
|
|
@@ -12,6 +12,7 @@
|
|
#include <linux/bitfield.h>
|
|
#include "qmi.h"
|
|
#include "htc.h"
|
|
+#include "pktlog.h"
|
|
#include "wmi.h"
|
|
#include "hal.h"
|
|
#include "dp.h"
|
|
@@ -397,6 +398,11 @@ struct ath11k_debug {
|
|
u32 pktlog_mode;
|
|
u32 pktlog_peer_valid;
|
|
u8 pktlog_peer_addr[ETH_ALEN];
|
|
+#ifdef CPTCFG_ATH11K_PKTLOG
|
|
+ struct dentry *debugfs_pktlog;
|
|
+ struct ath_pktlog pktlog;
|
|
+ bool is_pkt_logging;
|
|
+#endif
|
|
u32 rx_filter;
|
|
};
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/debug.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/debug.c
|
|
@@ -11,6 +11,7 @@
|
|
#include "dp_tx.h"
|
|
#include "debug_htt_stats.h"
|
|
#include "peer.h"
|
|
+#include "pktlog.h"
|
|
|
|
static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {
|
|
"REO2SW1_RING",
|
|
@@ -1181,6 +1182,7 @@ int ath11k_debug_register(struct ath11k
|
|
ath11k_debug_htt_stats_init(ar);
|
|
|
|
ath11k_debug_fw_stats_init(ar);
|
|
+ ath11k_init_pktlog(ar);
|
|
|
|
debugfs_create_file("ext_tx_stats", 0644,
|
|
ar->debug.debugfs_pdev, ar,
|
|
@@ -1206,5 +1208,6 @@ int ath11k_debug_register(struct ath11k
|
|
|
|
void ath11k_debug_unregister(struct ath11k *ar)
|
|
{
|
|
+ ath11k_deinit_pktlog(ar);
|
|
}
|
|
#endif /* CPTCFG_ATH11K_DEBUGFS */
|
|
--- a/drivers/net/wireless/ath/ath11k/debug.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/debug.h
|
|
@@ -304,4 +304,39 @@ do { \
|
|
__ath11k_dbg(ar, dbg_mask, fmt, ##__VA_ARGS__); \
|
|
} while (0)
|
|
|
|
+
|
|
+#ifdef CPTCFG_ATH11K_PKTLOG
|
|
+void ath11k_init_pktlog(struct ath11k *ar);
|
|
+void ath11k_deinit_pktlog(struct ath11k *ar);
|
|
+void ath11k_htt_pktlog_process(struct ath11k *ar, u8 *data);
|
|
+void ath11k_htt_ppdu_pktlog_process(struct ath11k *ar, u8 *data, u32 len);
|
|
+void ath11k_rx_stats_buf_pktlog_process(struct ath11k *ar, u8 *data, u16 log_type, u32 len);
|
|
+
|
|
+#else /* CPTCFG_ATH11K_PKTLOG */
|
|
+static inline void ath11k_init_pktlog(struct ath11k *ar)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath11k_deinit_pktlog(struct ath11k *ar)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath11k_htt_pktlog_process(struct ath11k *ar,
|
|
+ u8 *data)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath11k_htt_ppdu_pktlog_process(struct ath11k *ar,
|
|
+ u8 *data, u32 len)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath11k_pktlog_rx(struct ath11k *ar, struct sk_buff_head *amsdu)
|
|
+{
|
|
+}
|
|
+static inline void ath11k_rx_stats_buf_pktlog_process(struct ath11k *ar,
|
|
+ u8 *data, u16 log_type, u32 len)
|
|
+{
|
|
+}
|
|
+#endif /* CONFIG_ATH11K_PKTLOG */
|
|
#endif /* _ATH11K_DEBUG_H_ */
|
|
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
|
|
@@ -1511,8 +1511,10 @@ static int ath11k_htt_pull_ppdu_stats(st
|
|
goto exit;
|
|
}
|
|
|
|
- if (ath11k_debug_is_pktlog_lite_mode_enabled(ar))
|
|
+ if (ath11k_debug_is_pktlog_lite_mode_enabled(ar)) {
|
|
trace_ath11k_htt_ppdu_stats(ar, skb->data, len);
|
|
+ ath11k_htt_ppdu_pktlog_process(ar, (u8 *)skb->data, DP_RX_BUFFER_SIZE);
|
|
+ }
|
|
|
|
ppdu_info = ath11k_dp_htt_get_ppdu_desc(ar, ppdu_id);
|
|
if (!ppdu_info) {
|
|
@@ -1551,6 +1553,7 @@ static void ath11k_htt_pktlog(struct ath
|
|
|
|
trace_ath11k_htt_pktlog(ar, data->payload, hdr->size,
|
|
ar->ab->pktlog_defs_checksum);
|
|
+ ath11k_htt_pktlog_process(ar, (u8 *)data->payload);
|
|
}
|
|
|
|
static void ath11k_htt_backpressure_event_handler(struct ath11k_base *ab,
|
|
@@ -2906,8 +2909,11 @@ int ath11k_dp_rx_process_mon_status(stru
|
|
memset(&ppdu_info, 0, sizeof(ppdu_info));
|
|
ppdu_info.peer_id = HAL_INVALID_PEERID;
|
|
|
|
- if (ath11k_debug_is_pktlog_rx_stats_enabled(ar))
|
|
+ if (ath11k_debug_is_pktlog_rx_stats_enabled(ar)) {
|
|
trace_ath11k_htt_rxdesc(ar, skb->data, DP_RX_BUFFER_SIZE);
|
|
+ ath11k_rx_stats_buf_pktlog_process(ar, skb->data,
|
|
+ DP_RX_BUFFER_SIZE);
|
|
+ }
|
|
|
|
hal_status = ath11k_hal_rx_parse_mon_status(ab, &ppdu_info, skb);
|
|
|
|
@@ -2934,8 +2940,11 @@ int ath11k_dp_rx_process_mon_status(stru
|
|
arsta = (struct ath11k_sta *)peer->sta->drv_priv;
|
|
ath11k_dp_rx_update_peer_stats(arsta, &ppdu_info);
|
|
|
|
- if (ath11k_debug_is_pktlog_peer_valid(ar, peer->addr))
|
|
+ if (ath11k_debug_is_pktlog_peer_valid(ar, peer->addr)) {
|
|
trace_ath11k_htt_rxdesc(ar, skb->data, DP_RX_BUFFER_SIZE);
|
|
+ ath11k_rx_stats_buf_pktlog_process(ar, skb->data,
|
|
+ DP_RX_BUFFER_SIZE);
|
|
+ }
|
|
|
|
spin_unlock_bh(&ab->base_lock);
|
|
rcu_read_unlock();
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/ath/ath11k/pktlog.c
|
|
@@ -0,0 +1,537 @@
|
|
+
|
|
+#include <linux/device.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/vmalloc.h>
|
|
+#include "core.h"
|
|
+#include "wmi.h"
|
|
+#include "debug.h"
|
|
+
|
|
+static struct page *pktlog_virt_to_logical(void *addr)
|
|
+{
|
|
+ struct page *page;
|
|
+ unsigned long vpage = 0UL;
|
|
+
|
|
+ page = vmalloc_to_page(addr);
|
|
+ if (page) {
|
|
+ vpage = (unsigned long)page_address(page);
|
|
+ vpage |= ((unsigned long)addr & (PAGE_SIZE - 1));
|
|
+ }
|
|
+ return virt_to_page((void *)vpage);
|
|
+}
|
|
+
|
|
+static void ath_pktlog_release(struct ath_pktlog *pktlog)
|
|
+{
|
|
+ unsigned long page_cnt, vaddr;
|
|
+ struct page *page;
|
|
+
|
|
+ page_cnt =
|
|
+ ((sizeof(*pktlog->buf) +
|
|
+ pktlog->buf_size) / PAGE_SIZE) + 1;
|
|
+
|
|
+ for (vaddr = (unsigned long)(pktlog->buf); vaddr <
|
|
+ (unsigned long)(pktlog->buf) +
|
|
+ (page_cnt * PAGE_SIZE);
|
|
+ vaddr += PAGE_SIZE) {
|
|
+ page = pktlog_virt_to_logical((void *)vaddr);
|
|
+ clear_bit(PG_reserved, &page->flags);
|
|
+ }
|
|
+
|
|
+ vfree(pktlog->buf);
|
|
+ pktlog->buf = NULL;
|
|
+}
|
|
+
|
|
+static int ath_alloc_pktlog_buf(struct ath11k *ar)
|
|
+{
|
|
+ u32 page_cnt;
|
|
+ unsigned long vaddr;
|
|
+ struct page *page;
|
|
+ struct ath_pktlog *pktlog = &ar->debug.pktlog;
|
|
+
|
|
+ if (pktlog->buf_size == 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ page_cnt = (sizeof(*pktlog->buf) +
|
|
+ pktlog->buf_size) / PAGE_SIZE;
|
|
+
|
|
+ pktlog->buf = vmalloc((page_cnt + 2) * PAGE_SIZE);
|
|
+ if (!pktlog->buf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ pktlog->buf = (struct ath_pktlog_buf *)
|
|
+ (((unsigned long)
|
|
+ (pktlog->buf)
|
|
+ + PAGE_SIZE - 1) & PAGE_MASK);
|
|
+
|
|
+ for (vaddr = (unsigned long)(pktlog->buf);
|
|
+ vaddr < ((unsigned long)(pktlog->buf)
|
|
+ + (page_cnt * PAGE_SIZE)); vaddr += PAGE_SIZE) {
|
|
+ page = pktlog_virt_to_logical((void *)vaddr);
|
|
+ set_bit(PG_reserved, &page->flags);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void ath_init_pktlog_buf(struct ath11k *ar, struct ath_pktlog *pktlog)
|
|
+{
|
|
+ if (!ar->ab->pktlog_defs_checksum) {
|
|
+ pktlog->buf->bufhdr.magic_num = PKTLOG_MAGIC_NUM;
|
|
+ pktlog->buf->bufhdr.version = CUR_PKTLOG_VER;
|
|
+ } else {
|
|
+ pktlog->buf->bufhdr.magic_num = PKTLOG_NEW_MAGIC_NUM;
|
|
+ pktlog->buf->bufhdr.version = ar->ab->pktlog_defs_checksum;
|
|
+ }
|
|
+ pktlog->buf->rd_offset = -1;
|
|
+ pktlog->buf->wr_offset = 0;
|
|
+}
|
|
+
|
|
+static inline void ath_pktlog_mov_rd_idx(struct ath_pktlog *pl_info,
|
|
+ int32_t *rd_offset)
|
|
+{
|
|
+ int32_t boundary;
|
|
+ struct ath_pktlog_buf *log_buf = pl_info->buf;
|
|
+ struct ath_pktlog_hdr *log_hdr;
|
|
+
|
|
+ log_hdr = (struct ath_pktlog_hdr *)(log_buf->log_data + *rd_offset);
|
|
+ boundary = *rd_offset;
|
|
+ boundary += pl_info->hdr_size;
|
|
+ boundary += log_hdr->size;
|
|
+
|
|
+ if (boundary <= pl_info->buf_size)
|
|
+ *rd_offset = boundary;
|
|
+ else
|
|
+ *rd_offset = log_hdr->size;
|
|
+
|
|
+ if ((pl_info->buf_size - *rd_offset) < pl_info->hdr_size)
|
|
+ *rd_offset = 0;
|
|
+}
|
|
+
|
|
+static char *ath_pktlog_getbuf(struct ath_pktlog *pl_info,
|
|
+ struct ath_pktlog_hdr_arg *hdr_arg)
|
|
+{
|
|
+ struct ath_pktlog_buf *log_buf;
|
|
+ int32_t cur_wr_offset, buf_size;
|
|
+ char *log_ptr;
|
|
+
|
|
+ log_buf = pl_info->buf;
|
|
+ buf_size = pl_info->buf_size;
|
|
+
|
|
+ spin_lock_bh(&pl_info->lock);
|
|
+ cur_wr_offset = log_buf->wr_offset;
|
|
+ /* Move read offset to the next entry if there is a buffer overlap */
|
|
+ if (log_buf->rd_offset >= 0) {
|
|
+ if ((cur_wr_offset <= log_buf->rd_offset) &&
|
|
+ (cur_wr_offset + pl_info->hdr_size) >
|
|
+ log_buf->rd_offset)
|
|
+ ath_pktlog_mov_rd_idx(pl_info, &log_buf->rd_offset);
|
|
+ } else {
|
|
+ log_buf->rd_offset = cur_wr_offset;
|
|
+ }
|
|
+
|
|
+ memcpy(&log_buf->log_data[cur_wr_offset],
|
|
+ hdr_arg->pktlog_hdr, pl_info->hdr_size);
|
|
+
|
|
+ cur_wr_offset += pl_info->hdr_size;
|
|
+
|
|
+ if ((buf_size - cur_wr_offset) < hdr_arg->payload_size) {
|
|
+ while ((cur_wr_offset <= log_buf->rd_offset) &&
|
|
+ (log_buf->rd_offset < buf_size))
|
|
+ ath_pktlog_mov_rd_idx(pl_info, &log_buf->rd_offset);
|
|
+ cur_wr_offset = 0;
|
|
+ }
|
|
+
|
|
+ while ((cur_wr_offset <= log_buf->rd_offset) &&
|
|
+ ((cur_wr_offset + hdr_arg->payload_size) > log_buf->rd_offset))
|
|
+ ath_pktlog_mov_rd_idx(pl_info, &log_buf->rd_offset);
|
|
+
|
|
+ log_ptr = &log_buf->log_data[cur_wr_offset];
|
|
+ cur_wr_offset += hdr_arg->payload_size;
|
|
+
|
|
+ log_buf->wr_offset =
|
|
+ ((buf_size - cur_wr_offset) >=
|
|
+ pl_info->hdr_size) ? cur_wr_offset : 0;
|
|
+ spin_unlock_bh(&pl_info->lock);
|
|
+
|
|
+ return log_ptr;
|
|
+}
|
|
+
|
|
+static int pktlog_pgfault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
|
+{
|
|
+ unsigned long address = (unsigned long)vmf->virtual_address;
|
|
+
|
|
+ if (address == 0UL)
|
|
+ return VM_FAULT_NOPAGE;
|
|
+
|
|
+ if (vmf->pgoff > vma->vm_end)
|
|
+ return VM_FAULT_SIGBUS;
|
|
+
|
|
+ get_page(virt_to_page(address));
|
|
+ vmf->page = virt_to_page(address);
|
|
+ return VM_FAULT_MINOR;
|
|
+}
|
|
+
|
|
+static struct vm_operations_struct pktlog_vmops = {
|
|
+ .fault = pktlog_pgfault
|
|
+};
|
|
+
|
|
+static int ath_pktlog_mmap(struct file *file, struct vm_area_struct *vma)
|
|
+{
|
|
+ struct ath11k *ar = file->private_data;
|
|
+
|
|
+ /* entire buffer should be mapped */
|
|
+ if (vma->vm_pgoff != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!ar->debug.pktlog.buf) {
|
|
+ pr_err("Can't allocate pktlog buf\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ vma->vm_flags |= VM_LOCKED;
|
|
+ vma->vm_ops = &pktlog_vmops;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t ath_pktlog_read(struct file *file, char __user *userbuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ size_t bufhdr_size;
|
|
+ size_t nbytes = 0, ret_val = 0;
|
|
+ int rem_len;
|
|
+ int start_offset, end_offset;
|
|
+ int fold_offset, ppos_data, cur_rd_offset;
|
|
+ struct ath11k *ar = file->private_data;
|
|
+ struct ath_pktlog *info = &ar->debug.pktlog;
|
|
+ struct ath_pktlog_buf *log_buf = info->buf;
|
|
+
|
|
+ if (log_buf == NULL)
|
|
+ return 0;
|
|
+
|
|
+ bufhdr_size = sizeof(log_buf->bufhdr);
|
|
+
|
|
+ /* copy valid log entries from circular buffer into user space */
|
|
+ rem_len = count;
|
|
+
|
|
+ nbytes = 0;
|
|
+
|
|
+ if (*ppos < bufhdr_size) {
|
|
+ nbytes = min((int)(bufhdr_size - *ppos), rem_len);
|
|
+ if (copy_to_user(userbuf,
|
|
+ ((char *)&log_buf->bufhdr) + *ppos, nbytes))
|
|
+ return -EFAULT;
|
|
+ rem_len -= nbytes;
|
|
+ ret_val += nbytes;
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&info->lock);
|
|
+
|
|
+ start_offset = log_buf->rd_offset;
|
|
+
|
|
+ if ((rem_len == 0) || (start_offset < 0))
|
|
+ goto read_done;
|
|
+
|
|
+ fold_offset = -1;
|
|
+ cur_rd_offset = start_offset;
|
|
+
|
|
+ /* Find the last offset and fold-offset if the buffer is folded */
|
|
+ do {
|
|
+ int log_data_offset;
|
|
+ struct ath_pktlog_hdr *log_hdr;
|
|
+
|
|
+ log_hdr = (struct ath_pktlog_hdr *)(log_buf->log_data + cur_rd_offset);
|
|
+ log_data_offset = cur_rd_offset + info->hdr_size;
|
|
+
|
|
+ if ((fold_offset == -1) &&
|
|
+ ((info->buf_size - log_data_offset) <= log_hdr->size))
|
|
+ fold_offset = log_data_offset - 1;
|
|
+
|
|
+ ath_pktlog_mov_rd_idx(info, &cur_rd_offset);
|
|
+
|
|
+ if ((fold_offset == -1) && (cur_rd_offset == 0) &&
|
|
+ (cur_rd_offset != log_buf->wr_offset))
|
|
+ fold_offset = log_data_offset + log_hdr->size - 1;
|
|
+
|
|
+ end_offset = log_data_offset + log_hdr->size - 1;
|
|
+
|
|
+ } while (cur_rd_offset != log_buf->wr_offset);
|
|
+
|
|
+ ppos_data = *ppos + ret_val - bufhdr_size + start_offset;
|
|
+
|
|
+ if (fold_offset == -1) {
|
|
+ if (ppos_data > end_offset)
|
|
+ goto read_done;
|
|
+
|
|
+ nbytes = min(rem_len, end_offset - ppos_data + 1);
|
|
+ if (copy_to_user(userbuf + ret_val,
|
|
+ log_buf->log_data + ppos_data, nbytes)) {
|
|
+ ret_val = -EFAULT;
|
|
+ goto out;
|
|
+ }
|
|
+ ret_val += nbytes;
|
|
+ rem_len -= nbytes;
|
|
+ } else {
|
|
+ if (ppos_data <= fold_offset) {
|
|
+ nbytes = min(rem_len, fold_offset - ppos_data + 1);
|
|
+ if (copy_to_user(userbuf + ret_val,
|
|
+ log_buf->log_data + ppos_data, nbytes)) {
|
|
+ ret_val = -EFAULT;
|
|
+ goto out;
|
|
+ }
|
|
+ ret_val += nbytes;
|
|
+ rem_len -= nbytes;
|
|
+ }
|
|
+
|
|
+ if (rem_len == 0)
|
|
+ goto read_done;
|
|
+
|
|
+ ppos_data =
|
|
+ *ppos + ret_val - (bufhdr_size +
|
|
+ (fold_offset - start_offset + 1));
|
|
+
|
|
+ if (ppos_data <= end_offset) {
|
|
+ nbytes = min(rem_len, end_offset - ppos_data + 1);
|
|
+ if (copy_to_user(userbuf + ret_val, log_buf->log_data
|
|
+ + ppos_data,
|
|
+ nbytes)) {
|
|
+ ret_val = -EFAULT;
|
|
+ goto out;
|
|
+ }
|
|
+ ret_val += nbytes;
|
|
+ rem_len -= nbytes;
|
|
+ }
|
|
+ }
|
|
+
|
|
+read_done:
|
|
+ *ppos += ret_val;
|
|
+out:
|
|
+ spin_unlock_bh(&info->lock);
|
|
+ return ret_val;
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_pktlog_dump = {
|
|
+ .read = ath_pktlog_read,
|
|
+ .mmap = ath_pktlog_mmap,
|
|
+ .open = simple_open
|
|
+};
|
|
+
|
|
+static ssize_t write_pktlog_start(struct file *file, const char __user *ubuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k *ar = file->private_data;
|
|
+ struct ath_pktlog *pktlog = &ar->debug.pktlog;
|
|
+ u32 start_pktlog;
|
|
+ int err;
|
|
+
|
|
+ err = kstrtou32_from_user(ubuf, count, 0, &start_pktlog);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (ar->debug.is_pkt_logging && start_pktlog) {
|
|
+ pr_err("packet logging is inprogress\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (start_pktlog) {
|
|
+ if (pktlog->buf != NULL)
|
|
+ ath_pktlog_release(pktlog);
|
|
+
|
|
+ err = ath_alloc_pktlog_buf(ar);
|
|
+ if (err != 0)
|
|
+ return err;
|
|
+
|
|
+ ath_init_pktlog_buf(ar, pktlog);
|
|
+ ar->debug.is_pkt_logging = 1;
|
|
+ } else {
|
|
+ ar->debug.is_pkt_logging = 0;
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t read_pktlog_start(struct file *file, char __user *ubuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ char buf[32];
|
|
+ struct ath11k *ar = file->private_data;
|
|
+ int len = 0;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf) - len, "%d\n",
|
|
+ ar->debug.is_pkt_logging);
|
|
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_pktlog_start = {
|
|
+ .read = read_pktlog_start,
|
|
+ .write = write_pktlog_start,
|
|
+ .open = simple_open
|
|
+};
|
|
+
|
|
+static ssize_t pktlog_size_write(struct file *file, const char __user *ubuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath11k *ar = file->private_data;
|
|
+ u32 pktlog_size;
|
|
+
|
|
+ if (kstrtou32_from_user(ubuf, count, 0, &pktlog_size))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (pktlog_size == ar->debug.pktlog.buf_size)
|
|
+ return count;
|
|
+
|
|
+ if (ar->debug.is_pkt_logging) {
|
|
+ pr_debug("Stop packet logging before changing the size\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ar->debug.pktlog.buf_size = pktlog_size;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t pktlog_size_read(struct file *file, char __user *ubuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ char buf[32];
|
|
+ struct ath11k *ar = file->private_data;
|
|
+ int len = 0;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf) - len, "%uL\n",
|
|
+ ar->debug.pktlog.buf_size);
|
|
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_pktlog_size = {
|
|
+ .read = pktlog_size_read,
|
|
+ .write = pktlog_size_write,
|
|
+ .open = simple_open
|
|
+};
|
|
+
|
|
+static void ath_pktlog_init(struct ath11k *ar)
|
|
+{
|
|
+ struct ath_pktlog *pktlog = &ar->debug.pktlog;
|
|
+
|
|
+ spin_lock_init(&pktlog->lock);
|
|
+ pktlog->buf_size = ATH_DEBUGFS_PKTLOG_SIZE_DEFAULT;
|
|
+ pktlog->buf = NULL;
|
|
+
|
|
+ pktlog->hdr_size = sizeof(struct ath_pktlog_hdr);
|
|
+ pktlog->hdr_size_field_offset =
|
|
+ offsetof(struct ath_pktlog_hdr, size);
|
|
+}
|
|
+
|
|
+void ath11k_init_pktlog(struct ath11k *ar)
|
|
+{
|
|
+ ar->debug.debugfs_pktlog = debugfs_create_dir("pktlog",
|
|
+ ar->debug.debugfs_pdev);
|
|
+ debugfs_create_file("start", S_IRUGO | S_IWUSR,
|
|
+ ar->debug.debugfs_pktlog, ar, &fops_pktlog_start);
|
|
+ debugfs_create_file("size", S_IRUGO | S_IWUSR,
|
|
+ ar->debug.debugfs_pktlog, ar, &fops_pktlog_size);
|
|
+ debugfs_create_file("dump", S_IRUGO | S_IWUSR,
|
|
+ ar->debug.debugfs_pktlog, ar, &fops_pktlog_dump);
|
|
+ ath_pktlog_init(ar);
|
|
+}
|
|
+
|
|
+void ath11k_deinit_pktlog(struct ath11k *ar)
|
|
+{
|
|
+ struct ath_pktlog *pktlog = &ar->debug.pktlog;
|
|
+
|
|
+ if (pktlog->buf != NULL)
|
|
+ ath_pktlog_release(pktlog);
|
|
+}
|
|
+
|
|
+static int ath_pktlog_pull_hdr(struct ath_pktlog_hdr_arg *arg,
|
|
+ u8 *data)
|
|
+{
|
|
+ struct ath_pktlog_hdr *hdr = (struct ath_pktlog_hdr *)data;
|
|
+
|
|
+ hdr->flags = __le16_to_cpu(hdr->flags);
|
|
+ hdr->missed_cnt = __le16_to_cpu(hdr->missed_cnt);
|
|
+ hdr->log_type = __le16_to_cpu(hdr->log_type);
|
|
+ hdr->size = __le16_to_cpu(hdr->size);
|
|
+ hdr->timestamp = __le32_to_cpu(hdr->timestamp);
|
|
+ hdr->type_specific_data = __le32_to_cpu(hdr->type_specific_data);
|
|
+
|
|
+ arg->log_type = hdr->log_type;
|
|
+ arg->payload = hdr->payload;
|
|
+ arg->payload_size = hdr->size;
|
|
+ arg->pktlog_hdr = data;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void ath_pktlog_write_buf(struct ath_pktlog *pl_info,
|
|
+ struct ath_pktlog_hdr_arg *hdr_arg)
|
|
+{
|
|
+ char *log_data;
|
|
+
|
|
+ log_data = ath_pktlog_getbuf(pl_info, hdr_arg);
|
|
+ if (!log_data)
|
|
+ return;
|
|
+
|
|
+ memcpy(log_data, hdr_arg->payload, hdr_arg->payload_size);
|
|
+}
|
|
+
|
|
+void ath11k_htt_pktlog_process(struct ath11k *ar, u8 *data)
|
|
+{
|
|
+ struct ath_pktlog *pl_info = &ar->debug.pktlog;
|
|
+ struct ath_pktlog_hdr_arg hdr_arg;
|
|
+ int ret;
|
|
+
|
|
+ if (!ar->debug.is_pkt_logging)
|
|
+ return;
|
|
+
|
|
+ ret = ath_pktlog_pull_hdr(&hdr_arg, data);
|
|
+ if (ret)
|
|
+ return;
|
|
+
|
|
+ ath_pktlog_write_buf(pl_info, &hdr_arg);
|
|
+}
|
|
+
|
|
+void ath11k_htt_ppdu_pktlog_process(struct ath11k *ar, u8 *data, u32 len)
|
|
+{
|
|
+ struct ath_pktlog *pl_info = &ar->debug.pktlog;
|
|
+ struct ath_pktlog_hdr hdr;
|
|
+ struct ath_pktlog_hdr_arg hdr_arg;
|
|
+
|
|
+ if (!ar->debug.is_pkt_logging)
|
|
+ return;
|
|
+
|
|
+ hdr.flags = (1 << PKTLOG_FLG_FRM_TYPE_REMOTE_S);
|
|
+ hdr.missed_cnt = 0;
|
|
+ hdr.log_type = ATH11K_PKTLOG_TYPE_PPDU_STATS;
|
|
+ hdr.timestamp = 0;
|
|
+ hdr.size = len;
|
|
+ hdr.type_specific_data = 0;
|
|
+
|
|
+ hdr_arg.log_type = hdr.log_type;
|
|
+ hdr_arg.payload_size = hdr.size;
|
|
+ hdr_arg.payload = (u8 *)data;
|
|
+ hdr_arg.pktlog_hdr = (u8 *)&hdr;
|
|
+
|
|
+ ath_pktlog_write_buf(pl_info, &hdr_arg);
|
|
+}
|
|
+
|
|
+void ath11k_rx_stats_buf_pktlog_process(struct ath11k *ar, u8 *data, u16 log_type, u32 len)
|
|
+{
|
|
+ struct ath_pktlog *pl_info = &ar->debug.pktlog;
|
|
+ struct ath_pktlog_hdr hdr;
|
|
+ struct ath_pktlog_hdr_arg hdr_arg;
|
|
+
|
|
+ if (!ar->debug.is_pkt_logging)
|
|
+ return;
|
|
+
|
|
+ hdr.flags = (1 << PKTLOG_FLG_FRM_TYPE_REMOTE_S);
|
|
+ hdr.missed_cnt = 0;
|
|
+ hdr.log_type = log_type;
|
|
+ hdr.timestamp = 0;
|
|
+ hdr.size = len;
|
|
+ hdr.type_specific_data = 0;
|
|
+
|
|
+ hdr_arg.log_type = log_type;
|
|
+ hdr_arg.payload_size = len;
|
|
+ hdr_arg.payload = data;
|
|
+ hdr_arg.pktlog_hdr = (u8 *)&hdr;
|
|
+
|
|
+ ath_pktlog_write_buf(pl_info, &hdr_arg);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/ath/ath11k/pktlog.h
|
|
@@ -0,0 +1,56 @@
|
|
+#ifndef _PKTLOG_H_
|
|
+#define _PKTLOG_H_
|
|
+
|
|
+#ifdef CPTCFG_ATH11K_PKTLOG
|
|
+#define CUR_PKTLOG_VER 10010 /* Packet log version */
|
|
+#define PKTLOG_MAGIC_NUM 7735225
|
|
+#define PKTLOG_NEW_MAGIC_NUM 2453506
|
|
+
|
|
+/* Masks for setting pktlog events filters */
|
|
+#define ATH_PKTLOG_RX 0x000000001
|
|
+#define ATH_PKTLOG_TX 0x000000002
|
|
+#define ATH_PKTLOG_RCFIND 0x000000004
|
|
+#define ATH_PKTLOG_RCUPDATE 0x000000008
|
|
+
|
|
+#define ATH_DEBUGFS_PKTLOG_SIZE_DEFAULT (8 * 1024 * 1024)
|
|
+#define ATH_PKTLOG_FILTER_DEFAULT (ATH_PKTLOG_TX | ATH_PKTLOG_RX | \
|
|
+ ATH_PKTLOG_RCFIND | ATH_PKTLOG_RCUPDATE)
|
|
+
|
|
+enum {
|
|
+ PKTLOG_FLG_FRM_TYPE_LOCAL_S = 0,
|
|
+ PKTLOG_FLG_FRM_TYPE_REMOTE_S,
|
|
+ PKTLOG_FLG_FRM_TYPE_CLONE_S,
|
|
+ PKTLOG_FLG_FRM_TYPE_UNKNOWN_S
|
|
+};
|
|
+
|
|
+struct ath_pktlog_hdr_arg {
|
|
+ u16 log_type;
|
|
+ u8 *payload;
|
|
+ u16 payload_size;
|
|
+ u8 *pktlog_hdr;
|
|
+};
|
|
+
|
|
+struct ath_pktlog_bufhdr {
|
|
+ u32 magic_num; /* Used by post processing scripts */
|
|
+ u32 version; /* Set to CUR_PKTLOG_VER */
|
|
+};
|
|
+
|
|
+struct ath_pktlog_buf {
|
|
+ struct ath_pktlog_bufhdr bufhdr;
|
|
+ int rd_offset;
|
|
+ int wr_offset;
|
|
+ char log_data[0];
|
|
+};
|
|
+
|
|
+struct ath_pktlog {
|
|
+ struct ath_pktlog_buf *buf;
|
|
+ u32 filter;
|
|
+ u32 buf_size; /* Size of buffer in bytes */
|
|
+ spinlock_t lock;
|
|
+ u8 hdr_size;
|
|
+ u8 hdr_size_field_offset;
|
|
+};
|
|
+
|
|
+#endif /* CONFIG_ATH10K_PKTLOG */
|
|
+
|
|
+#endif /* _PKTLOG_H_ */
|
|
--- a/local-symbols
|
|
+++ b/local-symbols
|
|
@@ -138,3 +138,4 @@ ATH11K_DEBUG=
|
|
ATH11K_DEBUGFS=
|
|
ATH11K_TRACING=
|
|
ATH11K_SPECTRAL=
|
|
+ATH11K_PKTLOG=
|