1412 lines
41 KiB
Diff
1412 lines
41 KiB
Diff
Reason for not yet publishing: This code needs more testing and
|
|
enhancements.
|
|
|
|
From 067eeff8bf0ddb90ea77bf088f924c2a165a98d1 Mon Sep 17 00:00:00 2001
|
|
From: Vasanthakumar Thiagarajan <vasanth@atheros.com>
|
|
Date: Wed, 14 Apr 2010 11:36:44 -0700
|
|
Subject: [PATCH 2/3] ath9k: Add pktlog support
|
|
|
|
This adds packet log support for all of the supported
|
|
Atheros hardware families under ath9k, AR5008, AR9001, AR9002
|
|
and AR9003. Packet log is used to extract specific descriptor
|
|
and rate control data into a binary file parsed for analysis
|
|
by our systems team.
|
|
|
|
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
|
|
---
|
|
drivers/net/wireless/ath/ath9k/Kconfig | 8 +
|
|
drivers/net/wireless/ath/ath9k/Makefile | 1 +
|
|
drivers/net/wireless/ath/ath9k/ar9002_mac.c | 3 +-
|
|
drivers/net/wireless/ath/ath9k/ar9003_mac.c | 4 +-
|
|
drivers/net/wireless/ath/ath9k/ath9k.h | 6 +
|
|
drivers/net/wireless/ath/ath9k/debug.c | 4 +
|
|
drivers/net/wireless/ath/ath9k/hw-ops.h | 5 +-
|
|
drivers/net/wireless/ath/ath9k/hw.c | 2 +-
|
|
drivers/net/wireless/ath/ath9k/hw.h | 4 +-
|
|
drivers/net/wireless/ath/ath9k/pktlog.c | 783 +++++++++++++++++++++++++++
|
|
drivers/net/wireless/ath/ath9k/pktlog.h | 242 +++++++++
|
|
drivers/net/wireless/ath/ath9k/rc.c | 22 +-
|
|
drivers/net/wireless/ath/ath9k/recv.c | 15 +-
|
|
drivers/net/wireless/ath/ath9k/xmit.c | 24 +-
|
|
14 files changed, 1103 insertions(+), 20 deletions(-)
|
|
create mode 100644 drivers/net/wireless/ath/ath9k/pktlog.c
|
|
create mode 100644 drivers/net/wireless/ath/ath9k/pktlog.h
|
|
|
|
--- a/drivers/net/wireless/ath/ath9k/Kconfig
|
|
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
|
|
@@ -40,6 +40,13 @@ config ATH9K_RATE_CONTROL
|
|
Say Y, if you want to use the ath9k specific rate control
|
|
module instead of minstrel_ht.
|
|
|
|
+config ATH9K_PKTLOG
|
|
+ bool "ath9k packet logging support"
|
|
+ depends on ATH9K_DEBUGFS
|
|
+ ---help---
|
|
+ Say Y to dump frame information during tx/rx, rate information
|
|
+ and ani state.
|
|
+
|
|
config ATH9K_HTC
|
|
tristate "Atheros HTC based wireless cards support"
|
|
depends on USB && MAC80211
|
|
@@ -61,3 +68,4 @@ config ATH9K_HTC_DEBUGFS
|
|
depends on ATH9K_HTC && DEBUG_FS
|
|
---help---
|
|
Say Y, if you need access to ath9k_htc's statistics.
|
|
+
|
|
--- a/drivers/net/wireless/ath/ath9k/Makefile
|
|
+++ b/drivers/net/wireless/ath/ath9k/Makefile
|
|
@@ -9,6 +9,7 @@ ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc
|
|
ath9k-$(CONFIG_PCI) += pci.o
|
|
ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
|
|
ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
|
|
+ath9k-$(CONFIG_ATH9K_PKTLOG) += pktlog.o
|
|
|
|
obj-$(CONFIG_ATH9K) += ath9k.o
|
|
|
|
--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
|
|
@@ -205,7 +205,8 @@ static void ar9002_hw_fill_txdesc(struct
|
|
}
|
|
|
|
static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
|
|
- struct ath_tx_status *ts)
|
|
+ struct ath_tx_status *ts,
|
|
+ void *txs_desc)
|
|
{
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
u32 status;
|
|
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
|
|
@@ -234,7 +234,8 @@ static void ar9003_hw_fill_txdesc(struct
|
|
}
|
|
|
|
static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
|
|
- struct ath_tx_status *ts)
|
|
+ struct ath_tx_status *ts,
|
|
+ void *txs_desc)
|
|
{
|
|
struct ar9003_txs *ads;
|
|
u32 status;
|
|
@@ -308,6 +309,7 @@ static int ar9003_hw_proc_txdesc(struct
|
|
ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11);
|
|
ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12);
|
|
|
|
+ memcpy(txs_desc, ads, sizeof(*ads));
|
|
memset(ads, 0, sizeof(*ads));
|
|
|
|
return 0;
|
|
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
|
@@ -24,6 +24,7 @@
|
|
|
|
#include "debug.h"
|
|
#include "common.h"
|
|
+#include "pktlog.h"
|
|
|
|
/*
|
|
* Header for the ath9k.ko driver core *only* -- hw code nor any other driver
|
|
@@ -550,6 +551,7 @@ struct ath_ant_comb {
|
|
#define SC_OP_BT_SCAN BIT(13)
|
|
#define SC_OP_ANI_RUN BIT(14)
|
|
#define SC_OP_ENABLE_APM BIT(15)
|
|
+#define SC_OP_PKTLOGGING BIT(16)
|
|
|
|
/* Powersave flags */
|
|
#define PS_WAIT_FOR_BEACON BIT(0)
|
|
@@ -629,6 +631,10 @@ struct ath_softc {
|
|
struct list_head nodes; /* basically, stations */
|
|
unsigned int tx_complete_poll_work_seen;
|
|
#endif
|
|
+#ifdef CONFIG_ATH9K_PKTLOG
|
|
+ struct ath_pktlog_debugfs pktlog;
|
|
+#endif
|
|
+ bool is_pkt_logging;
|
|
struct ath_beacon_config cur_beacon_conf;
|
|
struct delayed_work tx_complete_work;
|
|
struct delayed_work hw_pll_work;
|
|
--- a/drivers/net/wireless/ath/ath9k/debug.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/debug.c
|
|
@@ -1066,6 +1066,9 @@ static int open_file_regdump(struct inod
|
|
|
|
file->private_data = buf;
|
|
|
|
+ if (ath9k_init_pktlog(sc) != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1148,6 +1151,7 @@ int ath9k_init_debug(struct ath_hw *ah)
|
|
sc->debug.regidx = 0;
|
|
return 0;
|
|
err:
|
|
+ ath9k_deinit_pktlog(sc);
|
|
debugfs_remove_recursive(sc->debug.debugfs_phy);
|
|
sc->debug.debugfs_phy = NULL;
|
|
return -ENOMEM;
|
|
--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
|
|
+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
|
|
@@ -67,9 +67,10 @@ static inline void ath9k_hw_filltxdesc(s
|
|
}
|
|
|
|
static inline int ath9k_hw_txprocdesc(struct ath_hw *ah, void *ds,
|
|
- struct ath_tx_status *ts)
|
|
+ struct ath_tx_status *ts,
|
|
+ void *txs_desc)
|
|
{
|
|
- return ath9k_hw_ops(ah)->proc_txdesc(ah, ds, ts);
|
|
+ return ath9k_hw_ops(ah)->proc_txdesc(ah, ds, ts, txs_desc);
|
|
}
|
|
|
|
static inline void ath9k_hw_set11n_txdesc(struct ath_hw *ah, void *ds,
|
|
--- a/drivers/net/wireless/ath/ath9k/hw.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/hw.c
|
|
@@ -2192,7 +2192,7 @@ void ath9k_hw_setrxfilter(struct ath_hw
|
|
phybits |= AR_PHY_ERR_RADAR;
|
|
if (bits & ATH9K_RX_FILTER_PHYERR)
|
|
phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING;
|
|
- REG_WRITE(ah, AR_PHY_ERR, phybits);
|
|
+ REG_WRITE(ah, AR_PHY_ERR, 0xffffffff);
|
|
|
|
if (phybits)
|
|
REG_WRITE(ah, AR_RXCFG,
|
|
--- a/drivers/net/wireless/ath/ath9k/hw.h
|
|
+++ b/drivers/net/wireless/ath/ath9k/hw.h
|
|
@@ -620,7 +620,7 @@ struct ath_hw_ops {
|
|
const void *ds0, dma_addr_t buf_addr,
|
|
unsigned int qcu);
|
|
int (*proc_txdesc)(struct ath_hw *ah, void *ds,
|
|
- struct ath_tx_status *ts);
|
|
+ struct ath_tx_status *ts, void* txs_desc);
|
|
void (*set11n_txdesc)(struct ath_hw *ah, void *ds,
|
|
u32 pktLen, enum ath9k_pkt_type type,
|
|
u32 txPower, u32 keyIx,
|
|
@@ -856,6 +856,8 @@ struct ath_hw {
|
|
|
|
/* Enterprise mode cap */
|
|
u32 ent_mode;
|
|
+
|
|
+ bool is_pkt_logging;
|
|
};
|
|
|
|
static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/ath/ath9k/pktlog.c
|
|
@@ -0,0 +1,783 @@
|
|
+
|
|
+#include <linux/vmalloc.h>
|
|
+#include <linux/highmem.h>
|
|
+#include "ath9k.h"
|
|
+
|
|
+static int ath9k_debugfs_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ file->private_data = inode->i_private;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+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->pktlog_buf)) +
|
|
+ pktlog->pktlog_buf_size) / PAGE_SIZE) + 1;
|
|
+
|
|
+ for (vaddr = (unsigned long) (pktlog->pktlog_buf); vaddr <
|
|
+ (unsigned long) (pktlog->pktlog_buf) +
|
|
+ (page_cnt * PAGE_SIZE);
|
|
+ vaddr += PAGE_SIZE) {
|
|
+ page = pktlog_virt_to_logical((void *) vaddr);
|
|
+ clear_bit(PG_reserved, &page->flags);
|
|
+ }
|
|
+
|
|
+ vfree(pktlog->pktlog_buf);
|
|
+ pktlog->pktlog_buf = NULL;
|
|
+}
|
|
+
|
|
+static int ath_alloc_pktlog_buf(struct ath_softc *sc)
|
|
+{
|
|
+ u32 page_cnt;
|
|
+ unsigned long vaddr;
|
|
+ struct page *page;
|
|
+ struct ath_pktlog *pktlog = &sc->pktlog.pktlog;
|
|
+
|
|
+ if (pktlog->pktlog_buf_size == 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ page_cnt = (sizeof(*(pktlog->pktlog_buf)) +
|
|
+ pktlog->pktlog_buf_size) / PAGE_SIZE;
|
|
+
|
|
+ pktlog->pktlog_buf = vmalloc((page_cnt + 2) * PAGE_SIZE);
|
|
+ if (pktlog->pktlog_buf == NULL) {
|
|
+ printk(KERN_ERR "Failed to allocate memory for pktlog");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ pktlog->pktlog_buf = (struct ath_pktlog_buf *)
|
|
+ (((unsigned long)
|
|
+ (pktlog->pktlog_buf)
|
|
+ + PAGE_SIZE - 1) & PAGE_MASK);
|
|
+
|
|
+ for (vaddr = (unsigned long) (pktlog->pktlog_buf);
|
|
+ vaddr < ((unsigned long) (pktlog->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 ath_pktlog *pktlog)
|
|
+{
|
|
+ pktlog->pktlog_buf->bufhdr.magic_num = PKTLOG_MAGIC_NUM;
|
|
+ pktlog->pktlog_buf->bufhdr.version = CUR_PKTLOG_VER;
|
|
+ pktlog->pktlog_buf->rd_offset = -1;
|
|
+ pktlog->pktlog_buf->wr_offset = 0;
|
|
+ if (pktlog->pktlog_filter == 0)
|
|
+ pktlog->pktlog_filter = ATH_PKTLOG_FILTER_DEFAULT;
|
|
+}
|
|
+
|
|
+static char *ath_pktlog_getbuf(struct ath_pktlog *pl_info,
|
|
+ u16 log_type, size_t log_size,
|
|
+ u32 flags)
|
|
+{
|
|
+ struct ath_pktlog_buf *log_buf;
|
|
+ struct ath_pktlog_hdr *log_hdr;
|
|
+ int32_t cur_wr_offset, buf_size;
|
|
+ char *log_ptr;
|
|
+
|
|
+ log_buf = pl_info->pktlog_buf;
|
|
+ buf_size = pl_info->pktlog_buf_size;
|
|
+
|
|
+ spin_lock_bh(&pl_info->pktlog_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 +
|
|
+ sizeof(struct ath_pktlog_hdr)) >
|
|
+ log_buf->rd_offset)
|
|
+ PKTLOG_MOV_RD_IDX(log_buf->rd_offset, log_buf,
|
|
+ buf_size);
|
|
+ } else {
|
|
+ log_buf->rd_offset = cur_wr_offset;
|
|
+ }
|
|
+
|
|
+ log_hdr =
|
|
+ (struct ath_pktlog_hdr *) (log_buf->log_data + cur_wr_offset);
|
|
+ log_hdr->log_type = log_type;
|
|
+ log_hdr->flags = flags;
|
|
+ log_hdr->timestamp = jiffies;
|
|
+ log_hdr->size = (u16) log_size;
|
|
+
|
|
+ cur_wr_offset += sizeof(*log_hdr);
|
|
+
|
|
+ if ((buf_size - cur_wr_offset) < log_size) {
|
|
+ while ((cur_wr_offset <= log_buf->rd_offset)
|
|
+ && (log_buf->rd_offset < buf_size))
|
|
+ PKTLOG_MOV_RD_IDX(log_buf->rd_offset, log_buf,
|
|
+ buf_size);
|
|
+ cur_wr_offset = 0;
|
|
+ }
|
|
+
|
|
+ while ((cur_wr_offset <= log_buf->rd_offset)
|
|
+ && (cur_wr_offset + log_size) > log_buf->rd_offset)
|
|
+ PKTLOG_MOV_RD_IDX(log_buf->rd_offset, log_buf, buf_size);
|
|
+
|
|
+ log_ptr = &(log_buf->log_data[cur_wr_offset]);
|
|
+
|
|
+ cur_wr_offset += log_hdr->size;
|
|
+
|
|
+ log_buf->wr_offset =
|
|
+ ((buf_size - cur_wr_offset) >=
|
|
+ sizeof(struct ath_pktlog_hdr)) ? cur_wr_offset : 0;
|
|
+ spin_unlock_bh(&pl_info->pktlog_lock);
|
|
+
|
|
+ return log_ptr;
|
|
+}
|
|
+
|
|
+static void ath9k_hw_get_descinfo(struct ath_hw *ah, struct ath_desc_info *desc_info)
|
|
+{
|
|
+ desc_info->txctl_numwords = TXCTL_NUMWORDS(ah);
|
|
+ desc_info->txctl_offset = TXCTL_OFFSET(ah);
|
|
+ desc_info->txstatus_numwords = TXSTATUS_NUMWORDS(ah);
|
|
+ desc_info->txstatus_offset = TXSTATUS_OFFSET(ah);
|
|
+
|
|
+ desc_info->rxctl_numwords = RXCTL_NUMWORDS(ah);
|
|
+ desc_info->rxctl_offset = RXCTL_OFFSET(ah);
|
|
+ desc_info->rxstatus_numwords = RXSTATUS_NUMWORDS(ah);
|
|
+ desc_info->rxstatus_offset = RXSTATUS_OFFSET(ah);
|
|
+}
|
|
+
|
|
+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 ath_softc *sc = file->private_data;
|
|
+
|
|
+ /* entire buffer should be mapped */
|
|
+ if (vma->vm_pgoff != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!sc->pktlog.pktlog.pktlog_buf) {
|
|
+ printk(KERN_ERR "Can't allocate pktlog buf");
|
|
+ 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 ath_softc *sc = file->private_data;
|
|
+ struct ath_pktlog *pktlog_info = &sc->pktlog.pktlog;
|
|
+ struct ath_pktlog_buf *log_buf = pktlog_info->pktlog_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;
|
|
+ }
|
|
+
|
|
+ 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 {
|
|
+ struct ath_pktlog_hdr *log_hdr;
|
|
+ int log_data_offset;
|
|
+
|
|
+ log_hdr =
|
|
+ (struct ath_pktlog_hdr *) (log_buf->log_data +
|
|
+ cur_rd_offset);
|
|
+
|
|
+ log_data_offset = cur_rd_offset + sizeof(struct ath_pktlog_hdr);
|
|
+
|
|
+ if ((fold_offset == -1)
|
|
+ && ((pktlog_info->pktlog_buf_size -
|
|
+ log_data_offset) <= log_hdr->size))
|
|
+ fold_offset = log_data_offset - 1;
|
|
+
|
|
+ PKTLOG_MOV_RD_IDX(cur_rd_offset, log_buf,
|
|
+ pktlog_info->pktlog_buf_size);
|
|
+
|
|
+ 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))
|
|
+ return -EFAULT;
|
|
+ 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))
|
|
+ return -EFAULT;
|
|
+ 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))
|
|
+ return -EFAULT;
|
|
+ ret_val += nbytes;
|
|
+ rem_len -= nbytes;
|
|
+ }
|
|
+ }
|
|
+
|
|
+read_done:
|
|
+ *ppos += ret_val;
|
|
+
|
|
+ return ret_val;
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_pktlog_dump = {
|
|
+ .read = ath_pktlog_read,
|
|
+ .mmap = ath_pktlog_mmap,
|
|
+ .open = ath9k_debugfs_open
|
|
+};
|
|
+
|
|
+static ssize_t write_pktlog_start(struct file *file, const char __user *ubuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath_softc *sc = file->private_data;
|
|
+ struct ath_pktlog *pktlog = &sc->pktlog.pktlog;
|
|
+ char buf[32];
|
|
+ int buf_size;
|
|
+ int start_pktlog, err;
|
|
+
|
|
+ buf_size = min(count, sizeof(buf) - 1);
|
|
+ if (copy_from_user(buf, ubuf, buf_size))
|
|
+ return -EFAULT;
|
|
+
|
|
+ sscanf(buf, "%d", &start_pktlog);
|
|
+ if (start_pktlog) {
|
|
+ if (pktlog->pktlog_buf != NULL)
|
|
+ ath_pktlog_release(pktlog);
|
|
+
|
|
+ err = ath_alloc_pktlog_buf(sc);
|
|
+ if (err != 0)
|
|
+ return err;
|
|
+
|
|
+ ath_init_pktlog_buf(pktlog);
|
|
+ pktlog->pktlog_buf->rd_offset = -1;
|
|
+ pktlog->pktlog_buf->wr_offset = 0;
|
|
+ sc->is_pkt_logging = 1;
|
|
+ } else {
|
|
+ sc->is_pkt_logging = 0;
|
|
+ }
|
|
+
|
|
+ sc->sc_ah->is_pkt_logging = sc->is_pkt_logging;
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t read_pktlog_start(struct file *file, char __user *ubuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ char buf[32];
|
|
+ struct ath_softc *sc = file->private_data;
|
|
+ int len = 0;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf) - len, "%d", sc->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 = ath9k_debugfs_open
|
|
+};
|
|
+
|
|
+static ssize_t pktlog_size_write(struct file *file, const char __user *ubuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ struct ath_softc *sc = file->private_data;
|
|
+ char buf[32];
|
|
+ u32 pktlog_size;
|
|
+ int buf_size;
|
|
+
|
|
+ buf_size = min(count, sizeof(buf) - 1);
|
|
+ if (copy_from_user(buf, ubuf, buf_size))
|
|
+ return -EFAULT;
|
|
+
|
|
+ sscanf(buf, "%d", &pktlog_size);
|
|
+
|
|
+ if (pktlog_size == sc->pktlog.pktlog.pktlog_buf_size)
|
|
+ return count;
|
|
+
|
|
+ if (sc->is_pkt_logging) {
|
|
+ printk(KERN_DEBUG "Stop packet logging before"
|
|
+ " changing the pktlog size \n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ sc->pktlog.pktlog.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 ath_softc *sc = file->private_data;
|
|
+ int len = 0;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf) - len, "%ul",
|
|
+ sc->pktlog.pktlog.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 = ath9k_debugfs_open
|
|
+};
|
|
+
|
|
+static ssize_t pktlog_filter_write(struct file *file, const char __user *ubuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ char buf[32];
|
|
+ struct ath_softc *sc = file->private_data;
|
|
+ u32 filter;
|
|
+ int buf_count;
|
|
+
|
|
+ buf_count = min(count, sizeof(buf) - 1);
|
|
+ if (copy_from_user(buf, ubuf, buf_count))
|
|
+ return -EFAULT;
|
|
+
|
|
+ if (sscanf(buf, "%x", &filter))
|
|
+ sc->pktlog.pktlog.pktlog_filter = filter;
|
|
+ else
|
|
+ sc->pktlog.pktlog.pktlog_filter = 0;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t pktlog_filter_read(struct file *file, char __user *ubuf,
|
|
+ size_t count, loff_t *ppos)
|
|
+{
|
|
+ char buf[32];
|
|
+ struct ath_softc *sc = file->private_data;
|
|
+ int len = 0;
|
|
+
|
|
+ len = scnprintf(buf, sizeof(buf) - len, "%ul",
|
|
+ sc->pktlog.pktlog.pktlog_filter);
|
|
+
|
|
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
|
|
+}
|
|
+
|
|
+static const struct file_operations fops_pktlog_filter = {
|
|
+ .read = pktlog_filter_read,
|
|
+ .write = pktlog_filter_write,
|
|
+ .open = ath9k_debugfs_open
|
|
+};
|
|
+
|
|
+void ath_pktlog_txctl(struct ath_softc *sc, struct ath_buf *bf)
|
|
+{
|
|
+ struct ath_pktlog_txctl *tx_log;
|
|
+ struct ath_pktlog *pl_info;
|
|
+ struct ieee80211_hdr *hdr;
|
|
+ struct ath_desc_info desc_info;
|
|
+ int i;
|
|
+ u32 *ds_words, flags = 0;
|
|
+
|
|
+ pl_info = &sc->pktlog.pktlog;
|
|
+
|
|
+ if ((pl_info->pktlog_filter & ATH_PKTLOG_TX) == 0 ||
|
|
+ bf->bf_mpdu == NULL || !sc->is_pkt_logging)
|
|
+ return;
|
|
+
|
|
+ flags |= (((sc->sc_ah->hw_version.macRev <<
|
|
+ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
|
|
+ ((sc->sc_ah->hw_version.macVersion << PHFLAGS_MACVERSION_SFT)
|
|
+ & PHFLAGS_MACVERSION_MASK));
|
|
+
|
|
+ tx_log = (struct ath_pktlog_txctl *)ath_pktlog_getbuf(pl_info,
|
|
+ PKTLOG_TYPE_TXCTL, sizeof(*tx_log), flags);
|
|
+
|
|
+ memset(tx_log, 0, sizeof(*tx_log));
|
|
+
|
|
+ hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
|
|
+ tx_log->framectrl = hdr->frame_control;
|
|
+ tx_log->seqctrl = hdr->seq_ctrl;
|
|
+
|
|
+ if (ieee80211_has_tods(tx_log->framectrl)) {
|
|
+ tx_log->bssid_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr1[ETH_ALEN - 1]);
|
|
+ tx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr2[ETH_ALEN - 1]);
|
|
+ tx_log->da_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr3[ETH_ALEN - 1]);
|
|
+ } else if (ieee80211_has_fromds(tx_log->framectrl)) {
|
|
+ tx_log->bssid_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr2[ETH_ALEN - 1]);
|
|
+ tx_log->sa_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr3[ETH_ALEN - 1]);
|
|
+ tx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr1[ETH_ALEN - 1]);
|
|
+ } else {
|
|
+ tx_log->bssid_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr3[ETH_ALEN - 1]);
|
|
+ tx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr2[ETH_ALEN - 1]);
|
|
+ tx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr1[ETH_ALEN - 1]);
|
|
+ }
|
|
+
|
|
+ ath9k_hw_get_descinfo(sc->sc_ah, &desc_info);
|
|
+
|
|
+ ds_words = (u32 *)(bf->bf_desc) + desc_info.txctl_offset;
|
|
+ for (i = 0; i < desc_info.txctl_numwords; i++)
|
|
+ tx_log->txdesc_ctl[i] = ds_words[i];
|
|
+}
|
|
+
|
|
+void ath_pktlog_txstatus(struct ath_softc *sc, void *ds)
|
|
+{
|
|
+ struct ath_pktlog_txstatus *tx_log;
|
|
+ struct ath_pktlog *pl_info;
|
|
+ struct ath_desc_info desc_info;
|
|
+ int i;
|
|
+ u32 *ds_words, flags = 0;
|
|
+
|
|
+ pl_info = &sc->pktlog.pktlog;
|
|
+
|
|
+ if ((pl_info->pktlog_filter & ATH_PKTLOG_TX) == 0 ||
|
|
+ !sc->is_pkt_logging)
|
|
+ return;
|
|
+
|
|
+ flags |= (((sc->sc_ah->hw_version.macRev <<
|
|
+ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
|
|
+ ((sc->sc_ah->hw_version.macVersion << PHFLAGS_MACVERSION_SFT)
|
|
+ & PHFLAGS_MACVERSION_MASK));
|
|
+ tx_log = (struct ath_pktlog_txstatus *)ath_pktlog_getbuf(pl_info,
|
|
+ PKTLOG_TYPE_TXSTATUS, sizeof(*tx_log), flags);
|
|
+
|
|
+ memset(tx_log, 0, sizeof(*tx_log));
|
|
+
|
|
+ ath9k_hw_get_descinfo(sc->sc_ah, &desc_info);
|
|
+
|
|
+ ds_words = (u32 *)(ds) + desc_info.txstatus_offset;
|
|
+
|
|
+ for (i = 0; i < desc_info.txstatus_numwords; i++)
|
|
+ tx_log->txdesc_status[i] = ds_words[i];
|
|
+}
|
|
+
|
|
+void ath_pktlog_rx(struct ath_softc *sc, void *desc, struct sk_buff *skb)
|
|
+{
|
|
+ struct ath_pktlog_rx *rx_log;
|
|
+ struct ath_pktlog *pl_info;
|
|
+ struct ieee80211_hdr *hdr;
|
|
+ struct ath_desc_info desc_info;
|
|
+ int i;
|
|
+ u32 *ds_words, flags = 0;
|
|
+
|
|
+ pl_info = &sc->pktlog.pktlog;
|
|
+
|
|
+ if ((pl_info->pktlog_filter & ATH_PKTLOG_RX) == 0 ||
|
|
+ !sc->is_pkt_logging)
|
|
+ return;
|
|
+
|
|
+ flags |= (((sc->sc_ah->hw_version.macRev <<
|
|
+ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
|
|
+ ((sc->sc_ah->hw_version.macVersion <<
|
|
+ PHFLAGS_MACVERSION_SFT) & PHFLAGS_MACVERSION_MASK));
|
|
+
|
|
+ rx_log = (struct ath_pktlog_rx *)ath_pktlog_getbuf(pl_info, PKTLOG_TYPE_RX,
|
|
+ sizeof(*rx_log), flags);
|
|
+
|
|
+ memset(rx_log, 0, sizeof(*rx_log));
|
|
+
|
|
+ if (skb->len > sizeof(struct ieee80211_hdr)) {
|
|
+ hdr = (struct ieee80211_hdr *) skb->data;
|
|
+ rx_log->framectrl = hdr->frame_control;
|
|
+ rx_log->seqctrl = hdr->seq_ctrl;
|
|
+
|
|
+ if (ieee80211_has_tods(rx_log->framectrl)) {
|
|
+ rx_log->bssid_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr1[ETH_ALEN - 1]);
|
|
+ rx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr2[ETH_ALEN - 1]);
|
|
+ rx_log->da_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr3[ETH_ALEN - 1]);
|
|
+ } else if (ieee80211_has_fromds(rx_log->framectrl)) {
|
|
+ rx_log->bssid_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr2[ETH_ALEN - 1]);
|
|
+ rx_log->sa_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr3[ETH_ALEN - 1]);
|
|
+ rx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr1[ETH_ALEN - 1]);
|
|
+ } else {
|
|
+ rx_log->bssid_tail = (hdr->addr3[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr3[ETH_ALEN - 1]);
|
|
+ rx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr2[ETH_ALEN - 1]);
|
|
+ rx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr1[ETH_ALEN - 1]);
|
|
+ }
|
|
+ } else {
|
|
+ hdr = (struct ieee80211_hdr *) skb->data;
|
|
+
|
|
+ if (ieee80211_is_ctl(hdr->frame_control)) {
|
|
+ rx_log->framectrl = hdr->frame_control;
|
|
+ rx_log->da_tail = (hdr->addr1[ETH_ALEN - 2] << 8) |
|
|
+ (hdr->addr1[ETH_ALEN - 1]);
|
|
+ if (skb->len < sizeof(struct ieee80211_rts)) {
|
|
+ rx_log->sa_tail = 0;
|
|
+ } else {
|
|
+ rx_log->sa_tail = (hdr->addr2[ETH_ALEN - 2]
|
|
+ << 8) |
|
|
+ (hdr->addr2[ETH_ALEN - 1]);
|
|
+ }
|
|
+ } else {
|
|
+ rx_log->framectrl = 0xFFFF;
|
|
+ rx_log->da_tail = 0;
|
|
+ rx_log->sa_tail = 0;
|
|
+ }
|
|
+
|
|
+ rx_log->seqctrl = 0;
|
|
+ rx_log->bssid_tail = 0;
|
|
+ }
|
|
+
|
|
+ ath9k_hw_get_descinfo(sc->sc_ah, &desc_info);
|
|
+
|
|
+ ds_words = (u32 *)(desc) + desc_info.rxstatus_offset;
|
|
+
|
|
+ for (i = 0; i < desc_info.rxstatus_numwords; i++)
|
|
+ rx_log->rxdesc_status[i] = ds_words[i];
|
|
+}
|
|
+
|
|
+void ath9k_pktlog_rc(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv,
|
|
+ int8_t ratecode, u8 rate, int8_t is_probing, u16 ac)
|
|
+{
|
|
+ struct ath_pktlog_rcfind *rcf_log;
|
|
+ struct ath_pktlog *pl_info;
|
|
+ u32 flags = 0;
|
|
+
|
|
+ pl_info = &sc->pktlog.pktlog;
|
|
+
|
|
+ if ((pl_info->pktlog_filter & ATH_PKTLOG_RCFIND) == 0 ||
|
|
+ !sc->is_pkt_logging)
|
|
+ return;
|
|
+
|
|
+ flags |= (((sc->sc_ah->hw_version.macRev <<
|
|
+ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
|
|
+ ((sc->sc_ah->hw_version.macVersion <<
|
|
+ PHFLAGS_MACVERSION_SFT) & PHFLAGS_MACVERSION_MASK));
|
|
+ rcf_log = (struct ath_pktlog_rcfind *)ath_pktlog_getbuf(pl_info,
|
|
+ PKTLOG_TYPE_RCFIND, sizeof(*rcf_log), flags);
|
|
+
|
|
+ rcf_log->rate = rate;
|
|
+ rcf_log->rateCode = ratecode;
|
|
+ rcf_log->rcProbeRate = is_probing ? ath_rc_priv->probe_rate : 0;
|
|
+ rcf_log->isProbing = is_probing;
|
|
+ rcf_log->ac = ac;
|
|
+ rcf_log->rcRateMax = ath_rc_priv->rate_max_phy;
|
|
+ rcf_log->rcRateTableSize = ath_rc_priv->rate_table_size;
|
|
+}
|
|
+
|
|
+void ath9k_pktlog_rcupdate(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, u8 tx_rate,
|
|
+ u8 rate_code, u8 xretries, u8 retries, int8_t rssi, u16 ac)
|
|
+{
|
|
+ struct ath_pktlog_rcupdate *rcu_log;
|
|
+ struct ath_pktlog *pl_info;
|
|
+ int i;
|
|
+ u32 flags = 0;
|
|
+
|
|
+ pl_info = &sc->pktlog.pktlog;
|
|
+
|
|
+ if ((pl_info->pktlog_filter & ATH_PKTLOG_RCUPDATE) == 0 ||
|
|
+ !sc->is_pkt_logging)
|
|
+ return;
|
|
+
|
|
+ flags |= (((sc->sc_ah->hw_version.macRev <<
|
|
+ PHFLAGS_MACREV_SFT) & PHFLAGS_MACREV_MASK) |
|
|
+ ((sc->sc_ah->hw_version.macVersion <<
|
|
+ PHFLAGS_MACVERSION_SFT) & PHFLAGS_MACVERSION_MASK));
|
|
+ rcu_log = (struct ath_pktlog_rcupdate *)ath_pktlog_getbuf(pl_info,
|
|
+ PKTLOG_TYPE_RCUPDATE,
|
|
+ sizeof(*rcu_log), flags);
|
|
+
|
|
+ memset(rcu_log, 0, sizeof(*rcu_log));
|
|
+
|
|
+ rcu_log->txRate = tx_rate;
|
|
+ rcu_log->rateCode = rate_code;
|
|
+ rcu_log->Xretries = xretries;
|
|
+ rcu_log->retries = retries;
|
|
+ rcu_log->rssiAck = rssi;
|
|
+ rcu_log->ac = ac;
|
|
+ rcu_log->rcProbeRate = ath_rc_priv->probe_rate;
|
|
+ rcu_log->rcRateMax = ath_rc_priv->rate_max_phy;
|
|
+
|
|
+ for (i = 0; i < RATE_TABLE_SIZE; i++) {
|
|
+ rcu_log->rcPer[i] = ath_rc_priv->per[i];
|
|
+ }
|
|
+}
|
|
+
|
|
+void ath9k_pktlog_txcomplete(struct ath_softc *sc, struct list_head *bf_head,
|
|
+ struct ath_buf *bf, struct ath_buf *bf_last)
|
|
+{
|
|
+ struct log_tx ;
|
|
+ struct ath_buf *tbf;
|
|
+
|
|
+ list_for_each_entry(tbf, bf_head, list)
|
|
+ ath_pktlog_txctl(sc, tbf);
|
|
+
|
|
+ if (bf->bf_next == NULL && bf_last->bf_stale)
|
|
+ ath_pktlog_txctl(sc, bf_last);
|
|
+}
|
|
+
|
|
+void ath9k_pktlog_txctrl(struct ath_softc *sc, struct list_head *bf_head, struct ath_buf *lastbf)
|
|
+{
|
|
+ struct log_tx ;
|
|
+ struct ath_buf *tbf;
|
|
+
|
|
+ list_for_each_entry(tbf, bf_head, list)
|
|
+ ath_pktlog_txctl(sc, tbf);
|
|
+
|
|
+ /* log the last descriptor. */
|
|
+ ath_pktlog_txctl(sc, lastbf);
|
|
+}
|
|
+
|
|
+static void pktlog_init(struct ath_softc *sc)
|
|
+{
|
|
+ spin_lock_init(&sc->pktlog.pktlog.pktlog_lock);
|
|
+ sc->pktlog.pktlog.pktlog_buf_size = ATH_DEBUGFS_PKTLOG_SIZE_DEFAULT;
|
|
+ sc->pktlog.pktlog.pktlog_buf = NULL;
|
|
+ sc->pktlog.pktlog.pktlog_filter = 0;
|
|
+}
|
|
+
|
|
+int ath9k_init_pktlog(struct ath_softc *sc)
|
|
+{
|
|
+ sc->pktlog.debugfs_pktlog = debugfs_create_dir("pktlog",
|
|
+ sc->debug.debugfs_phy);
|
|
+ if (!sc->pktlog.debugfs_pktlog)
|
|
+ goto err;
|
|
+
|
|
+ sc->pktlog.pktlog_start = debugfs_create_file("pktlog_start",
|
|
+ S_IRUGO | S_IWUSR,
|
|
+ sc->pktlog.debugfs_pktlog,
|
|
+ sc, &fops_pktlog_start);
|
|
+ if (!sc->pktlog.pktlog_start)
|
|
+ goto err;
|
|
+
|
|
+ sc->pktlog.pktlog_size = debugfs_create_file("pktlog_size",
|
|
+ S_IRUGO | S_IWUSR,
|
|
+ sc->pktlog.debugfs_pktlog,
|
|
+ sc, &fops_pktlog_size);
|
|
+ if (!sc->pktlog.pktlog_size)
|
|
+ goto err;
|
|
+
|
|
+ sc->pktlog.pktlog_filter = debugfs_create_file("pktlog_filter",
|
|
+ S_IRUGO | S_IWUSR,
|
|
+ sc->pktlog.debugfs_pktlog,
|
|
+ sc, &fops_pktlog_filter);
|
|
+ if (!sc->pktlog.pktlog_filter)
|
|
+ goto err;
|
|
+
|
|
+ sc->pktlog.pktlog_dump = debugfs_create_file("pktlog_dump",
|
|
+ S_IRUGO,
|
|
+ sc->pktlog.debugfs_pktlog,
|
|
+ sc, &fops_pktlog_dump);
|
|
+ if (!sc->pktlog.pktlog_dump)
|
|
+ goto err;
|
|
+
|
|
+ pktlog_init(sc);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err:
|
|
+ return -ENOMEM;
|
|
+}
|
|
+
|
|
+void ath9k_deinit_pktlog(struct ath_softc *sc)
|
|
+{
|
|
+ struct ath_pktlog *pktlog = &sc->pktlog.pktlog;
|
|
+
|
|
+ if (pktlog->pktlog_buf != NULL)
|
|
+ ath_pktlog_release(pktlog);
|
|
+
|
|
+ debugfs_remove(sc->pktlog.pktlog_start);
|
|
+ debugfs_remove(sc->pktlog.pktlog_size);
|
|
+ debugfs_remove(sc->pktlog.pktlog_filter);
|
|
+ debugfs_remove(sc->pktlog.pktlog_dump);
|
|
+ debugfs_remove(sc->pktlog.debugfs_pktlog);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/net/wireless/ath/ath9k/pktlog.h
|
|
@@ -0,0 +1,242 @@
|
|
+#ifndef PKTLOG_H
|
|
+#define PKTLOG_H
|
|
+
|
|
+#ifdef CONFIG_ATH9K_PKTLOG
|
|
+#define CUR_PKTLOG_VER 10010 /* Packet log version */
|
|
+#define PKTLOG_MAGIC_NUM 7735225
|
|
+#define ATH_PKTLOG_TX 0x000000001
|
|
+#define ATH_PKTLOG_RX 0x000000002
|
|
+#define ATH_PKTLOG_RCFIND 0x000000004
|
|
+#define ATH_PKTLOG_RCUPDATE 0x000000008
|
|
+
|
|
+#define ATH_DEBUGFS_PKTLOG_SIZE_DEFAULT (1024 * 1024)
|
|
+#define ATH_PKTLOG_FILTER_DEFAULT (ATH_PKTLOG_TX | ATH_PKTLOG_RX | \
|
|
+ ATH_PKTLOG_RCFIND | ATH_PKTLOG_RCUPDATE)
|
|
+
|
|
+#define PHFLAGS_MACVERSION_MASK 0x00ff0000
|
|
+#define PHFLAGS_MACVERSION_SFT 16
|
|
+#define PHFLAGS_MACREV_MASK 0xff0 /* MAC revision */
|
|
+#define PHFLAGS_MACREV_SFT 4
|
|
+
|
|
+struct ath_pktlog_hdr {
|
|
+ u32 flags;
|
|
+ u16 log_type; /* Type of log information foll this header */
|
|
+ int16_t size; /* Size of variable length log information in bytes */
|
|
+ u32 timestamp;
|
|
+} __packed;
|
|
+
|
|
+/* Types of packet log events */
|
|
+#define PKTLOG_TYPE_TXCTL 0
|
|
+#define PKTLOG_TYPE_TXSTATUS 1
|
|
+#define PKTLOG_TYPE_RX 2
|
|
+#define PKTLOG_TYPE_RCFIND 3
|
|
+#define PKTLOG_TYPE_RCUPDATE 4
|
|
+
|
|
+#define PKTLOG_MAX_TXCTL_WORDS 12
|
|
+#define PKTLOG_MAX_TXSTATUS_WORDS 10
|
|
+#define PKTLOG_MAX_PROTO_WORDS 16
|
|
+
|
|
+struct ath_pktlog_txctl {
|
|
+ __le16 framectrl; /* frame control field from header */
|
|
+ __le16 seqctrl; /* frame control field from header */
|
|
+ u16 bssid_tail; /* last two octets of bssid */
|
|
+ u16 sa_tail; /* last two octets of SA */
|
|
+ u16 da_tail; /* last two octets of DA */
|
|
+ u16 resvd;
|
|
+ u32 txdesc_ctl[PKTLOG_MAX_TXCTL_WORDS]; /* Tx descriptor words */
|
|
+ unsigned long proto_hdr; /* protocol header (variable length!) */
|
|
+ int32_t misc[0]; /* Can be used for HT specific or other misc info */
|
|
+} __packed;
|
|
+
|
|
+struct ath_pktlog_txstatus {
|
|
+ /* Tx descriptor status words */
|
|
+ u32 txdesc_status[PKTLOG_MAX_TXSTATUS_WORDS];
|
|
+ int32_t misc[0]; /* Can be used for HT specific or other misc info */
|
|
+} __packed;
|
|
+
|
|
+#define PKTLOG_MAX_RXSTATUS_WORDS 11
|
|
+
|
|
+struct ath_pktlog_rx {
|
|
+ u16 framectrl; /* frame control field from header */
|
|
+ u16 seqctrl; /* sequence control field */
|
|
+ u16 bssid_tail; /* last two octets of bssid */
|
|
+ u16 sa_tail; /* last two octets of SA */
|
|
+ u16 da_tail; /* last two octets of DA */
|
|
+ u16 resvd;
|
|
+ u32 rxdesc_status[PKTLOG_MAX_RXSTATUS_WORDS]; /* Rx descriptor words */
|
|
+ unsigned long proto_hdr; /* protocol header (variable length!) */
|
|
+ int32_t misc[0]; /* Can be used for HT specific or other misc info */
|
|
+} __packed;
|
|
+
|
|
+struct ath_pktlog_rcfind {
|
|
+ u8 rate;
|
|
+ u8 rateCode;
|
|
+ s8 rcRssiLast;
|
|
+ s8 rcRssiLastPrev;
|
|
+ s8 rcRssiLastPrev2;
|
|
+ s8 rssiReduce;
|
|
+ u8 rcProbeRate;
|
|
+ s8 isProbing;
|
|
+ s8 primeInUse;
|
|
+ s8 currentPrimeState;
|
|
+ u8 rcRateTableSize;
|
|
+ u8 rcRateMax;
|
|
+ u8 ac;
|
|
+ int32_t misc[0]; /* Can be used for HT specific or other misc info */
|
|
+} __packed;
|
|
+
|
|
+struct ath_pktlog_rcupdate {
|
|
+ u8 txRate;
|
|
+ u8 rateCode;
|
|
+ s8 rssiAck;
|
|
+ u8 Xretries;
|
|
+ u8 retries;
|
|
+ s8 rcRssiLast;
|
|
+ s8 rcRssiLastLkup;
|
|
+ s8 rcRssiLastPrev;
|
|
+ s8 rcRssiLastPrev2;
|
|
+ u8 rcProbeRate;
|
|
+ u8 rcRateMax;
|
|
+ s8 useTurboPrime;
|
|
+ s8 currentBoostState;
|
|
+ u8 rcHwMaxRetryRate;
|
|
+ u8 ac;
|
|
+ u8 resvd[2];
|
|
+ s8 rcRssiThres[RATE_TABLE_SIZE];
|
|
+ u8 rcPer[RATE_TABLE_SIZE];
|
|
+ u8 resv2[RATE_TABLE_SIZE + 5];
|
|
+ int32_t misc[0]; /* Can be used for HT specific or other misc info */
|
|
+};
|
|
+
|
|
+#define TXCTL_OFFSET(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 11 : 2)
|
|
+#define TXCTL_NUMWORDS(ah) (AR_SREV_5416_20_OR_LATER(ah) ? 12 : 8)
|
|
+#define TXSTATUS_OFFSET(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 2 : 14)
|
|
+#define TXSTATUS_NUMWORDS(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 7 : 10)
|
|
+
|
|
+#define RXCTL_OFFSET(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 0 : 3)
|
|
+#define RXCTL_NUMWORDS(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 0 : 1)
|
|
+#define RXSTATUS_OFFSET(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 1 : 4)
|
|
+#define RXSTATUS_NUMWORDS(ah) (AR_SREV_9300_20_OR_LATER(ah) ? 11 : 9)
|
|
+
|
|
+struct ath_desc_info {
|
|
+ u8 txctl_offset;
|
|
+ u8 txctl_numwords;
|
|
+ u8 txstatus_offset;
|
|
+ u8 txstatus_numwords;
|
|
+ u8 rxctl_offset;
|
|
+ u8 rxctl_numwords;
|
|
+ u8 rxstatus_offset;
|
|
+ u8 rxstatus_numwords;
|
|
+};
|
|
+
|
|
+#define PKTLOG_MOV_RD_IDX(_rd_offset, _log_buf, _log_size) \
|
|
+ do { \
|
|
+ if ((_rd_offset + sizeof(struct ath_pktlog_hdr) + \
|
|
+ ((struct ath_pktlog_hdr *)((_log_buf)->log_data + \
|
|
+ (_rd_offset)))->size) <= _log_size) { \
|
|
+ _rd_offset = ((_rd_offset) + \
|
|
+ sizeof(struct ath_pktlog_hdr) + \
|
|
+ ((struct ath_pktlog_hdr *) \
|
|
+ ((_log_buf)->log_data + \
|
|
+ (_rd_offset)))->size); \
|
|
+ } else { \
|
|
+ _rd_offset = ((struct ath_pktlog_hdr *) \
|
|
+ ((_log_buf)->log_data + \
|
|
+ (_rd_offset)))->size; \
|
|
+ } \
|
|
+ (_rd_offset) = (((_log_size) - (_rd_offset)) >= \
|
|
+ sizeof(struct ath_pktlog_hdr)) ? \
|
|
+ _rd_offset : 0; \
|
|
+ } while (0);
|
|
+
|
|
+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;
|
|
+ int32_t rd_offset;
|
|
+ int32_t wr_offset;
|
|
+ char log_data[0];
|
|
+};
|
|
+
|
|
+struct ath_pktlog {
|
|
+ struct ath_pktlog_buf *pktlog_buf;
|
|
+ u32 pktlog_filter;
|
|
+ u32 pktlog_buf_size; /* Size of buffer in bytes */
|
|
+ spinlock_t pktlog_lock;
|
|
+};
|
|
+
|
|
+struct ath_pktlog_debugfs {
|
|
+ struct dentry *debugfs_pktlog;
|
|
+ struct dentry *pktlog_enable;
|
|
+ struct dentry *pktlog_start;
|
|
+ struct dentry *pktlog_filter;
|
|
+ struct dentry *pktlog_size;
|
|
+ struct dentry *pktlog_dump;
|
|
+ struct ath_pktlog pktlog;
|
|
+};
|
|
+
|
|
+void ath_pktlog_txctl(struct ath_softc *sc, struct ath_buf *bf);
|
|
+void ath_pktlog_txstatus(struct ath_softc *sc, void *ds);
|
|
+void ath_pktlog_rx(struct ath_softc *sc, void *ds, struct sk_buff *skb);
|
|
+void ath9k_pktlog_rc(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv,
|
|
+ int8_t ratecode, u8 rate, int8_t is_probing, u16 ac);
|
|
+void ath9k_pktlog_rcupdate(struct ath_softc *sc,
|
|
+ struct ath_rate_priv *ath_rc_priv, u8 tx_rate,
|
|
+ u8 rate_code, u8 xretries, u8 retries, int8_t rssi,
|
|
+ u16 ac);
|
|
+void ath9k_pktlog_txcomplete(struct ath_softc *sc ,struct list_head *bf_head,
|
|
+ struct ath_buf *bf, struct ath_buf *bf_last);
|
|
+void ath9k_pktlog_txctrl(struct ath_softc *sc, struct list_head *bf_head,
|
|
+ struct ath_buf *lastbf);
|
|
+int ath9k_init_pktlog(struct ath_softc *sc);
|
|
+void ath9k_deinit_pktlog(struct ath_softc *sc);
|
|
+#else /* CONFIG_ATH9K_PKTLOG */
|
|
+static inline void ath_pktlog_txstatus(struct ath_softc *sc, void *ds)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath_pktlog_rx(struct ath_softc *sc, void *ds,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath9k_pktlog_rc(struct ath_softc *sc,
|
|
+ struct ath_rate_priv *ath_rc_priv,
|
|
+ int8_t ratecode, u8 rate,
|
|
+ int8_t is_probing, u16 ac)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath9k_pktlog_rcupdate(struct ath_softc *sc,
|
|
+ struct ath_rate_priv *ath_rc_priv,
|
|
+ u8 tx_rate, u8 rate_code,
|
|
+ u8 xretries, u8 retries,
|
|
+ int8_t rssi, u16 ac)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath9k_pktlog_txcomplete(struct ath_softc *sc,
|
|
+ struct list_head *bf_head,
|
|
+ struct ath_buf *bf,
|
|
+ struct ath_buf *bf_last)
|
|
+{
|
|
+}
|
|
+
|
|
+static inline void ath9k_pktlog_txctrl(struct ath_softc *sc,
|
|
+ struct list_head *bf_head,
|
|
+ struct ath_buf *lastbf)
|
|
+{
|
|
+}
|
|
+static inline int ath9k_init_pktlog(struct ath_softc *sc)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+static inline void ath9k_deinit_pktlog(struct ath_softc *sc)
|
|
+{
|
|
+}
|
|
+#endif /* CONFIG_ATH9K_PKTLOG */
|
|
+
|
|
+#endif
|
|
--- a/drivers/net/wireless/ath/ath9k/rc.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/rc.c
|
|
@@ -580,7 +580,7 @@ static u8 ath_rc_setvalid_htrates(struct
|
|
static u8 ath_rc_get_highest_rix(struct ath_softc *sc,
|
|
struct ath_rate_priv *ath_rc_priv,
|
|
const struct ath_rate_table *rate_table,
|
|
- int *is_probing)
|
|
+ int *is_probing, u16 ac)
|
|
{
|
|
u32 best_thruput, this_thruput, now_msec;
|
|
u8 rate, next_rate, best_rate, maxindex, minindex;
|
|
@@ -671,6 +671,8 @@ static u8 ath_rc_get_highest_rix(struct
|
|
|
|
rate = ath_rc_priv->valid_rate_index[0];
|
|
|
|
+ ath9k_pktlog_rc(sc, ath_rc_priv, rate_table->info[rate].ratecode,
|
|
+ rate, *is_probing, ac);
|
|
return rate;
|
|
}
|
|
|
|
@@ -762,7 +764,7 @@ static void ath_get_rate(void *priv, str
|
|
try_per_rate = 4;
|
|
|
|
rate_table = ath_rc_priv->rate_table;
|
|
- rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe);
|
|
+ rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe, skb_get_queue_mapping(skb));
|
|
|
|
/*
|
|
* If we're in HT mode and both us and our peer supports LDPC.
|
|
@@ -1012,7 +1014,8 @@ static void ath_debug_stat_retries(struc
|
|
static void ath_rc_update_ht(struct ath_softc *sc,
|
|
struct ath_rate_priv *ath_rc_priv,
|
|
struct ieee80211_tx_info *tx_info,
|
|
- int tx_rate, int xretries, int retries)
|
|
+ int tx_rate, int xretries, int retries,
|
|
+ u16 ac)
|
|
{
|
|
u32 now_msec = jiffies_to_msecs(jiffies);
|
|
int rate;
|
|
@@ -1081,6 +1084,9 @@ static void ath_rc_update_ht(struct ath_
|
|
ath_debug_stat_retries(ath_rc_priv, tx_rate, xretries, retries,
|
|
ath_rc_priv->per[tx_rate]);
|
|
|
|
+ ath9k_pktlog_rcupdate(sc, ath_rc_priv, tx_rate,
|
|
+ rate_table->info[tx_rate].ratecode,
|
|
+ xretries, retries, tx_info->status.ack_signal, ac);
|
|
}
|
|
|
|
static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table,
|
|
@@ -1113,7 +1119,8 @@ static int ath_rc_get_rateindex(const st
|
|
static void ath_rc_tx_status(struct ath_softc *sc,
|
|
struct ath_rate_priv *ath_rc_priv,
|
|
struct ieee80211_tx_info *tx_info,
|
|
- int final_ts_idx, int xretries, int long_retry)
|
|
+ int final_ts_idx, int xretries, int long_retry,
|
|
+ u16 ac)
|
|
{
|
|
const struct ath_rate_table *rate_table;
|
|
struct ieee80211_tx_rate *rates = tx_info->status.rates;
|
|
@@ -1142,7 +1149,7 @@ static void ath_rc_tx_status(struct ath_
|
|
rix = ath_rc_get_rateindex(rate_table, &rates[i]);
|
|
ath_rc_update_ht(sc, ath_rc_priv, tx_info,
|
|
rix, xretries ? 1 : 2,
|
|
- rates[i].count);
|
|
+ rates[i].count, ac);
|
|
}
|
|
}
|
|
} else {
|
|
@@ -1164,7 +1171,7 @@ static void ath_rc_tx_status(struct ath_
|
|
return;
|
|
|
|
rix = ath_rc_get_rateindex(rate_table, &rates[i]);
|
|
- ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry);
|
|
+ ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry, ac);
|
|
}
|
|
|
|
static const
|
|
@@ -1358,7 +1365,7 @@ static void ath_tx_status(void *priv, st
|
|
tx_status = 1;
|
|
|
|
ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status,
|
|
- long_retry);
|
|
+ long_retry, skb_get_queue_mapping(skb));
|
|
|
|
/* Check if aggregation has to be enabled for this tid */
|
|
if (conf_is_ht(&sc->hw->conf) &&
|
|
--- a/drivers/net/wireless/ath/ath9k/recv.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/recv.c
|
|
@@ -1577,6 +1577,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
|
|
struct ieee80211_rx_status *rxs;
|
|
struct ath_hw *ah = sc->sc_ah;
|
|
struct ath_common *common = ath9k_hw_common(ah);
|
|
+ u32 *rx_desc = NULL;
|
|
/*
|
|
* The hw can technically differ from common->hw when using ath9k
|
|
* virtual wiphy so to account for that we iterate over the active
|
|
@@ -1676,13 +1677,24 @@ int ath_rx_tasklet(struct ath_softc *sc,
|
|
dma_type);
|
|
|
|
skb_put(skb, rs.rs_datalen + ah->caps.rx_status_len);
|
|
- if (ah->caps.rx_status_len)
|
|
+ if (ah->caps.rx_status_len) {
|
|
+ rx_desc = kzalloc(ah->caps.rx_status_len, GFP_ATOMIC);
|
|
+ if (rx_desc == NULL)
|
|
+ BUG_ON(1);
|
|
+ memcpy(rx_desc, skb->data, ah->caps.rx_status_len);
|
|
skb_pull(skb, ah->caps.rx_status_len);
|
|
+}
|
|
|
|
if (!rs.rs_more)
|
|
ath9k_rx_skb_postprocess(common, hdr_skb, &rs,
|
|
rxs, decrypt_error);
|
|
|
|
+ if (rx_desc) {
|
|
+ ath_pktlog_rx(sc, (void *) rx_desc, skb);
|
|
+ kfree(rx_desc);
|
|
+ } else
|
|
+ ath_pktlog_rx(sc, bf->bf_desc, skb);
|
|
+
|
|
/* We will now give hardware our shiny new allocated skb */
|
|
bf->bf_mpdu = requeue_skb;
|
|
bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
|
|
--- a/drivers/net/wireless/ath/ath9k/xmit.c
|
|
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
|
|
@@ -475,6 +475,8 @@ static void ath_tx_complete_aggr(struct
|
|
list_move_tail(&bf->list, &bf_head);
|
|
}
|
|
|
|
+ ath9k_pktlog_txcomplete(sc, &bf_head, bf, bf_last);
|
|
+
|
|
if (!txpending || (tid->state & AGGR_CLEANUP)) {
|
|
/*
|
|
* complete the acked-ones/xretried ones; update
|
|
@@ -2043,7 +2045,7 @@ static void ath_tx_processq(struct ath_s
|
|
ds = lastbf->bf_desc;
|
|
|
|
memset(&ts, 0, sizeof(ts));
|
|
- status = ath9k_hw_txprocdesc(ah, ds, &ts);
|
|
+ status = ath9k_hw_txprocdesc(ah, ds, &ts, NULL);
|
|
if (status == -EINPROGRESS) {
|
|
spin_unlock_bh(&txq->axq_lock);
|
|
break;
|
|
@@ -2085,11 +2087,15 @@ static void ath_tx_processq(struct ath_s
|
|
ath_tx_rc_status(sc, bf, &ts, 1, txok ? 0 : 1, txok, true);
|
|
}
|
|
|
|
- if (bf_isampdu(bf))
|
|
+ if (bf_isampdu(bf)) {
|
|
ath_tx_complete_aggr(sc, txq, bf, &bf_head, &ts, txok,
|
|
true);
|
|
- else
|
|
+ } else {
|
|
+ ath9k_pktlog_txctrl(sc, &bf_head, lastbf);
|
|
ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, txok, 0);
|
|
+ }
|
|
+
|
|
+ ath_pktlog_txstatus(sc, lastbf->bf_desc);
|
|
|
|
spin_lock_bh(&txq->axq_lock);
|
|
|
|
@@ -2210,9 +2216,11 @@ void ath_tx_edma_tasklet(struct ath_soft
|
|
struct list_head bf_head;
|
|
int status;
|
|
int txok;
|
|
+ u32 txs_desc[9];
|
|
|
|
for (;;) {
|
|
- status = ath9k_hw_txprocdesc(ah, NULL, (void *)&txs);
|
|
+ status = ath9k_hw_txprocdesc(ah, NULL, (void *)&txs,
|
|
+ (void *) txs_desc);
|
|
if (status == -EINPROGRESS)
|
|
break;
|
|
if (status == -EIO) {
|
|
@@ -2258,9 +2266,13 @@ void ath_tx_edma_tasklet(struct ath_soft
|
|
if (bf_isampdu(bf))
|
|
ath_tx_complete_aggr(sc, txq, bf, &bf_head, &txs,
|
|
txok, true);
|
|
- else
|
|
+ else {
|
|
+ ath9k_pktlog_txctrl(sc, &bf_head, lastbf);
|
|
ath_tx_complete_buf(sc, bf, txq, &bf_head,
|
|
&txs, txok, 0);
|
|
+ }
|
|
+
|
|
+ ath_pktlog_txstatus(sc, txs_desc);
|
|
|
|
spin_lock_bh(&txq->axq_lock);
|
|
|