| 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); |
| |