2019-10-28 13:24:37 -04:00
|
|
|
// SPDX-License-Identifier: ISC
|
2012-12-20 16:13:19 -05:00
|
|
|
/*
|
2017-01-20 06:49:45 -05:00
|
|
|
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
|
2019-02-22 09:21:00 -05:00
|
|
|
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
2012-12-20 16:13:19 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/debugfs.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
2014-06-16 12:37:21 -04:00
|
|
|
#include <linux/power_supply.h>
|
2012-12-20 16:13:19 -05:00
|
|
|
#include "wil6210.h"
|
2014-08-06 03:31:56 -04:00
|
|
|
#include "wmi.h"
|
2012-12-20 16:13:19 -05:00
|
|
|
#include "txrx.h"
|
2015-04-30 09:25:09 -04:00
|
|
|
#include "pmc.h"
|
2012-12-20 16:13:19 -05:00
|
|
|
|
|
|
|
/* Nasty hack. Better have per device instances */
|
|
|
|
static u32 mem_addr;
|
|
|
|
static u32 dbg_txdesc_index;
|
2018-06-29 09:28:18 -04:00
|
|
|
static u32 dbg_ring_index; /* 24+ for Rx, 0..23 for Tx */
|
2018-06-29 09:28:37 -04:00
|
|
|
static u32 dbg_status_msg_index;
|
|
|
|
/* 0..wil->num_rx_status_rings-1 for Rx, wil->tx_sring_idx for Tx */
|
|
|
|
static u32 dbg_sring_index;
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2014-08-06 03:31:55 -04:00
|
|
|
enum dbg_off_type {
|
|
|
|
doff_u32 = 0,
|
|
|
|
doff_x32 = 1,
|
|
|
|
doff_ulong = 2,
|
|
|
|
doff_io32 = 3,
|
2016-03-01 12:18:08 -05:00
|
|
|
doff_u8 = 4
|
2014-08-06 03:31:55 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/* offset to "wil" */
|
|
|
|
struct dbg_off {
|
|
|
|
const char *name;
|
|
|
|
umode_t mode;
|
|
|
|
ulong off;
|
|
|
|
enum dbg_off_type type;
|
|
|
|
};
|
|
|
|
|
2018-06-29 09:28:37 -04:00
|
|
|
static void wil_print_desc_edma(struct seq_file *s, struct wil6210_priv *wil,
|
|
|
|
struct wil_ring *ring,
|
|
|
|
char _s, char _h, int idx)
|
|
|
|
{
|
|
|
|
u8 num_of_descs;
|
|
|
|
bool has_skb = false;
|
|
|
|
|
|
|
|
if (ring->is_rx) {
|
|
|
|
struct wil_rx_enhanced_desc *rx_d =
|
|
|
|
(struct wil_rx_enhanced_desc *)
|
|
|
|
&ring->va[idx].rx.enhanced;
|
|
|
|
u16 buff_id = le16_to_cpu(rx_d->mac.buff_id);
|
|
|
|
|
2019-04-26 11:43:33 -04:00
|
|
|
if (wil->rx_buff_mgmt.buff_arr &&
|
|
|
|
wil_val_in_range(buff_id, 0, wil->rx_buff_mgmt.size))
|
|
|
|
has_skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;
|
2018-06-29 09:28:37 -04:00
|
|
|
seq_printf(s, "%c", (has_skb) ? _h : _s);
|
|
|
|
} else {
|
|
|
|
struct wil_tx_enhanced_desc *d =
|
|
|
|
(struct wil_tx_enhanced_desc *)
|
|
|
|
&ring->va[idx].tx.enhanced;
|
|
|
|
|
|
|
|
num_of_descs = (u8)d->mac.d[2];
|
2019-04-26 11:43:33 -04:00
|
|
|
has_skb = ring->ctx && ring->ctx[idx].skb;
|
2018-06-29 09:28:37 -04:00
|
|
|
if (num_of_descs >= 1)
|
2019-04-26 11:43:33 -04:00
|
|
|
seq_printf(s, "%c", has_skb ? _h : _s);
|
2018-06-29 09:28:37 -04:00
|
|
|
else
|
|
|
|
/* num_of_descs == 0, it's a frag in a list of descs */
|
|
|
|
seq_printf(s, "%c", has_skb ? 'h' : _s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil,
|
|
|
|
const char *name, struct wil_ring *ring,
|
|
|
|
char _s, char _h)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
2019-04-26 11:43:33 -04:00
|
|
|
void __iomem *x;
|
2014-12-23 02:47:08 -05:00
|
|
|
u32 v;
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
seq_printf(s, "RING %s = {\n", name);
|
|
|
|
seq_printf(s, " pa = %pad\n", &ring->pa);
|
|
|
|
seq_printf(s, " va = 0x%p\n", ring->va);
|
|
|
|
seq_printf(s, " size = %d\n", ring->size);
|
2018-06-29 09:28:37 -04:00
|
|
|
if (wil->use_enhanced_dma_hw && ring->is_rx)
|
|
|
|
seq_printf(s, " swtail = %u\n", *ring->edma_rx_swtail.va);
|
|
|
|
else
|
|
|
|
seq_printf(s, " swtail = %d\n", ring->swtail);
|
2018-06-29 09:28:18 -04:00
|
|
|
seq_printf(s, " swhead = %d\n", ring->swhead);
|
2019-04-26 11:43:33 -04:00
|
|
|
if (wil->use_enhanced_dma_hw) {
|
|
|
|
int ring_id = ring->is_rx ?
|
|
|
|
WIL_RX_DESC_RING_ID : ring - wil->ring_tx;
|
|
|
|
/* SUBQ_CONS is a table of 32 entries, one for each Q pair.
|
|
|
|
* lower 16bits are for even ring_id and upper 16bits are for
|
|
|
|
* odd ring_id
|
|
|
|
*/
|
|
|
|
x = wmi_addr(wil, RGF_DMA_SCM_SUBQ_CONS + 4 * (ring_id / 2));
|
|
|
|
v = readl_relaxed(x);
|
|
|
|
|
|
|
|
v = (ring_id % 2 ? (v >> 16) : (v & 0xffff));
|
|
|
|
seq_printf(s, " hwhead = %u\n", v);
|
|
|
|
}
|
2018-06-29 09:28:18 -04:00
|
|
|
seq_printf(s, " hwtail = [0x%08x] -> ", ring->hwtail);
|
2019-04-26 11:43:33 -04:00
|
|
|
x = wmi_addr(wil, ring->hwtail);
|
2014-12-23 02:47:08 -05:00
|
|
|
if (x) {
|
2015-07-30 06:52:03 -04:00
|
|
|
v = readl(x);
|
2014-12-23 02:47:08 -05:00
|
|
|
seq_printf(s, "0x%08x = %d\n", v, v);
|
|
|
|
} else {
|
2014-09-10 09:34:34 -04:00
|
|
|
seq_puts(s, "???\n");
|
2014-12-23 02:47:08 -05:00
|
|
|
}
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
if (ring->va && (ring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) {
|
2012-12-20 16:13:19 -05:00
|
|
|
uint i;
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
for (i = 0; i < ring->size; i++) {
|
2018-06-29 09:28:37 -04:00
|
|
|
if ((i % 128) == 0 && i != 0)
|
2014-09-10 09:34:34 -04:00
|
|
|
seq_puts(s, "\n");
|
2018-06-29 09:28:37 -04:00
|
|
|
if (wil->use_enhanced_dma_hw) {
|
|
|
|
wil_print_desc_edma(s, wil, ring, _s, _h, i);
|
|
|
|
} else {
|
|
|
|
volatile struct vring_tx_desc *d =
|
|
|
|
&ring->va[i].tx.legacy;
|
|
|
|
seq_printf(s, "%c", (d->dma.status & BIT(0)) ?
|
|
|
|
_s : (ring->ctx[i].skb ? _h : 'h'));
|
|
|
|
}
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
2014-09-10 09:34:34 -04:00
|
|
|
seq_puts(s, "\n");
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
2014-09-10 09:34:34 -04:00
|
|
|
seq_puts(s, "}\n");
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
|
|
|
|
2018-12-03 08:25:57 -05:00
|
|
|
static int ring_show(struct seq_file *s, void *data)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
|
|
|
uint i;
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
wil_print_ring(s, wil, "rx", &wil->ring_rx, 'S', '_');
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
for (i = 0; i < ARRAY_SIZE(wil->ring_tx); i++) {
|
|
|
|
struct wil_ring *ring = &wil->ring_tx[i];
|
|
|
|
struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i];
|
2014-06-16 12:37:05 -04:00
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
if (ring->va) {
|
|
|
|
int cid = wil->ring2cid_tid[i][0];
|
|
|
|
int tid = wil->ring2cid_tid[i][1];
|
|
|
|
u32 swhead = ring->swhead;
|
|
|
|
u32 swtail = ring->swtail;
|
|
|
|
int used = (ring->size + swhead - swtail)
|
|
|
|
% ring->size;
|
|
|
|
int avail = ring->size - used - 1;
|
2012-12-20 16:13:19 -05:00
|
|
|
char name[10];
|
2015-02-15 07:02:35 -05:00
|
|
|
char sidle[10];
|
2014-06-16 12:37:05 -04:00
|
|
|
/* performance monitoring */
|
|
|
|
cycles_t now = get_cycles();
|
wil6210: use 'uint64_t' instead of 'cycles_t' to avoid warnings
do_div() checks the type strictly. 'cycles_t' may be 32-bit under quite
a few architectures (parisc, arm, avr32 ...). So use 'uint64_t' instead
of, the related warning (with allmodconfig under parisc):
CC [M] drivers/net/wireless/ath/wil6210/debugfs.o
In file included from arch/parisc/include/generated/asm/div64.h:1:0,
from include/linux/kernel.h:124,
from include/linux/list.h:8,
from include/linux/module.h:9,
from drivers/net/wireless/ath/wil6210/debugfs.c:17:
drivers/net/wireless/ath/wil6210/debugfs.c: In function ‘wil_vring_debugfs_show’:
include/asm-generic/div64.h:43:28: warning: comparison of distinct pointer types lacks a cast
(void)(((typeof((n)) *)0) == ((uint64_t *)0)); \
^
drivers/net/wireless/ath/wil6210/debugfs.c:107:4: note: in expansion of macro ‘do_div’
do_div(idle, total);
^
In file included from include/uapi/linux/stddef.h:1:0,
from include/linux/stddef.h:4,
from ./include/uapi/linux/posix_types.h:4,
from include/uapi/linux/types.h:13,
from include/linux/types.h:5,
from include/linux/list.h:4,
from include/linux/module.h:9,
from drivers/net/wireless/ath/wil6210/debugfs.c:17:
include/asm-generic/div64.h:44:18: warning: right shift count >= width of type [-Wshift-count-overflow]
if (likely(((n) >> 32) == 0)) { \
^
include/linux/compiler.h:159:40: note: in definition of macro ‘likely’
# define likely(x) __builtin_expect(!!(x), 1)
^
drivers/net/wireless/ath/wil6210/debugfs.c:107:4: note: in expansion of macro ‘do_div’
do_div(idle, total);
^
In file included from arch/parisc/include/generated/asm/div64.h:1:0,
from include/linux/kernel.h:124,
from include/linux/list.h:8,
from include/linux/module.h:9,
from drivers/net/wireless/ath/wil6210/debugfs.c:17:
include/asm-generic/div64.h:48:22: warning: passing argument 1 of ‘__div64_32’ from incompatible pointer type [-Wincompatible-pointer-types]
__rem = __div64_32(&(n), __base); \
^
drivers/net/wireless/ath/wil6210/debugfs.c:107:4: note: in expansion of macro ‘do_div’
do_div(idle, total);
^
include/asm-generic/div64.h:35:17: note: expected ‘uint64_t * {aka long long unsigned int *}’ but argument is of type ‘cycles_t * {aka long unsigned int *}’
extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
^
Signed-off-by: Chen Gang <gang.chen.5i5j@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2014-12-24 10:08:44 -05:00
|
|
|
uint64_t idle = txdata->idle * 100;
|
|
|
|
uint64_t total = now - txdata->begin;
|
2014-06-16 12:37:05 -04:00
|
|
|
|
2015-02-15 07:02:35 -05:00
|
|
|
if (total != 0) {
|
|
|
|
do_div(idle, total);
|
|
|
|
snprintf(sidle, sizeof(sidle), "%3d%%",
|
|
|
|
(int)idle);
|
|
|
|
} else {
|
|
|
|
snprintf(sidle, sizeof(sidle), "N/A");
|
|
|
|
}
|
2014-06-16 12:37:05 -04:00
|
|
|
txdata->begin = now;
|
|
|
|
txdata->idle = 0ULL;
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
snprintf(name, sizeof(name), "tx_%2d", i);
|
2014-02-27 09:20:43 -05:00
|
|
|
|
2019-04-26 11:43:34 -04:00
|
|
|
if (cid < wil->max_assoc_sta)
|
2015-03-15 10:00:23 -04:00
|
|
|
seq_printf(s,
|
2015-04-30 09:25:10 -04:00
|
|
|
"\n%pM CID %d TID %d 1x%s BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n",
|
2015-03-15 10:00:23 -04:00
|
|
|
wil->sta[cid].addr, cid, tid,
|
2015-04-30 09:25:10 -04:00
|
|
|
txdata->dot1x_open ? "+" : "-",
|
2015-03-15 10:00:23 -04:00
|
|
|
txdata->agg_wsize,
|
|
|
|
txdata->agg_timeout,
|
|
|
|
txdata->agg_amsdu ? "+" : "-",
|
|
|
|
used, avail, sidle);
|
|
|
|
else
|
|
|
|
seq_printf(s,
|
2015-04-30 09:25:10 -04:00
|
|
|
"\nBroadcast 1x%s [%3d|%3d] idle %s\n",
|
|
|
|
txdata->dot1x_open ? "+" : "-",
|
2015-03-15 10:00:23 -04:00
|
|
|
used, avail, sidle);
|
2014-06-16 12:37:05 -04:00
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
wil_print_ring(s, wil, name, ring, '_', 'H');
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(ring);
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2018-06-29 09:28:37 -04:00
|
|
|
static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil,
|
|
|
|
struct wil_status_ring *sring)
|
|
|
|
{
|
2019-04-26 11:43:33 -04:00
|
|
|
void __iomem *x;
|
2018-06-29 09:28:37 -04:00
|
|
|
int sring_idx = sring - wil->srings;
|
|
|
|
u32 v;
|
|
|
|
|
|
|
|
seq_printf(s, "Status Ring %s [ %d ] = {\n",
|
|
|
|
sring->is_rx ? "RX" : "TX", sring_idx);
|
|
|
|
seq_printf(s, " pa = %pad\n", &sring->pa);
|
|
|
|
seq_printf(s, " va = 0x%pK\n", sring->va);
|
|
|
|
seq_printf(s, " size = %d\n", sring->size);
|
|
|
|
seq_printf(s, " elem_size = %zu\n", sring->elem_size);
|
|
|
|
seq_printf(s, " swhead = %d\n", sring->swhead);
|
2019-04-26 11:43:33 -04:00
|
|
|
if (wil->use_enhanced_dma_hw) {
|
|
|
|
/* COMPQ_PROD is a table of 32 entries, one for each Q pair.
|
|
|
|
* lower 16bits are for even ring_id and upper 16bits are for
|
|
|
|
* odd ring_id
|
|
|
|
*/
|
|
|
|
x = wmi_addr(wil, RGF_DMA_SCM_COMPQ_PROD + 4 * (sring_idx / 2));
|
|
|
|
v = readl_relaxed(x);
|
|
|
|
|
|
|
|
v = (sring_idx % 2 ? (v >> 16) : (v & 0xffff));
|
|
|
|
seq_printf(s, " hwhead = %u\n", v);
|
|
|
|
}
|
2018-06-29 09:28:37 -04:00
|
|
|
seq_printf(s, " hwtail = [0x%08x] -> ", sring->hwtail);
|
2019-04-26 11:43:33 -04:00
|
|
|
x = wmi_addr(wil, sring->hwtail);
|
2018-06-29 09:28:37 -04:00
|
|
|
if (x) {
|
|
|
|
v = readl_relaxed(x);
|
|
|
|
seq_printf(s, "0x%08x = %d\n", v, v);
|
|
|
|
} else {
|
|
|
|
seq_puts(s, "???\n");
|
|
|
|
}
|
|
|
|
seq_printf(s, " desc_rdy_pol = %d\n", sring->desc_rdy_pol);
|
2019-02-28 04:35:08 -05:00
|
|
|
seq_printf(s, " invalid_buff_id_cnt = %d\n",
|
|
|
|
sring->invalid_buff_id_cnt);
|
2018-06-29 09:28:37 -04:00
|
|
|
|
|
|
|
if (sring->va && (sring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) {
|
|
|
|
uint i;
|
|
|
|
|
|
|
|
for (i = 0; i < sring->size; i++) {
|
|
|
|
u32 *sdword_0 =
|
|
|
|
(u32 *)(sring->va + (sring->elem_size * i));
|
|
|
|
|
|
|
|
if ((i % 128) == 0 && i != 0)
|
|
|
|
seq_puts(s, "\n");
|
|
|
|
if (i == sring->swhead)
|
|
|
|
seq_printf(s, "%c", (*sdword_0 & BIT(31)) ?
|
|
|
|
'X' : 'x');
|
|
|
|
else
|
|
|
|
seq_printf(s, "%c", (*sdword_0 & BIT(31)) ?
|
|
|
|
'1' : '0');
|
|
|
|
}
|
|
|
|
seq_puts(s, "\n");
|
|
|
|
}
|
|
|
|
seq_puts(s, "}\n");
|
|
|
|
}
|
|
|
|
|
2018-12-03 08:25:57 -05:00
|
|
|
static int srings_show(struct seq_file *s, void *data)
|
2018-06-29 09:28:37 -04:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++)
|
|
|
|
if (wil->srings[i].va)
|
|
|
|
wil_print_sring(s, wil, &wil->srings[i]);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(srings);
|
2018-06-29 09:28:37 -04:00
|
|
|
|
2015-09-09 18:38:48 -04:00
|
|
|
static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
|
|
|
|
const char *prefix)
|
|
|
|
{
|
|
|
|
seq_hex_dump(s, prefix, DUMP_PREFIX_NONE, 16, 1, p, len, false);
|
|
|
|
}
|
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
static void wil_print_mbox_ring(struct seq_file *s, const char *prefix,
|
|
|
|
void __iomem *off)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
struct wil6210_mbox_ring r;
|
|
|
|
int rsize;
|
|
|
|
uint i;
|
|
|
|
|
2016-04-26 07:41:41 -04:00
|
|
|
wil_halp_vote(wil);
|
|
|
|
|
2019-02-28 04:34:44 -05:00
|
|
|
if (wil_mem_access_lock(wil)) {
|
|
|
|
wil_halp_unvote(wil);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
wil_memcpy_fromio_32(&r, off, sizeof(r));
|
|
|
|
wil_mbox_ring_le2cpus(&r);
|
|
|
|
/*
|
|
|
|
* we just read memory block from NIC. This memory may be
|
|
|
|
* garbage. Check validity before using it.
|
|
|
|
*/
|
|
|
|
rsize = r.size / sizeof(struct wil6210_mbox_ring_desc);
|
|
|
|
|
|
|
|
seq_printf(s, "ring %s = {\n", prefix);
|
|
|
|
seq_printf(s, " base = 0x%08x\n", r.base);
|
|
|
|
seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize);
|
|
|
|
seq_printf(s, " tail = 0x%08x\n", r.tail);
|
|
|
|
seq_printf(s, " head = 0x%08x\n", r.head);
|
|
|
|
seq_printf(s, " entry size = %d\n", r.entry_size);
|
|
|
|
|
|
|
|
if (r.size % sizeof(struct wil6210_mbox_ring_desc)) {
|
|
|
|
seq_printf(s, " ??? size is not multiple of %zd, garbage?\n",
|
|
|
|
sizeof(struct wil6210_mbox_ring_desc));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wmi_addr(wil, r.base) ||
|
|
|
|
!wmi_addr(wil, r.tail) ||
|
|
|
|
!wmi_addr(wil, r.head)) {
|
2014-09-10 09:34:34 -04:00
|
|
|
seq_puts(s, " ??? pointers are garbage?\n");
|
2012-12-20 16:13:19 -05:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < rsize; i++) {
|
|
|
|
struct wil6210_mbox_ring_desc d;
|
|
|
|
struct wil6210_mbox_hdr hdr;
|
|
|
|
size_t delta = i * sizeof(d);
|
|
|
|
void __iomem *x = wil->csr + HOSTADDR(r.base) + delta;
|
|
|
|
|
|
|
|
wil_memcpy_fromio_32(&d, x, sizeof(d));
|
|
|
|
|
|
|
|
seq_printf(s, " [%2x] %s %s%s 0x%08x", i,
|
|
|
|
d.sync ? "F" : "E",
|
|
|
|
(r.tail - r.base == delta) ? "t" : " ",
|
|
|
|
(r.head - r.base == delta) ? "h" : " ",
|
|
|
|
le32_to_cpu(d.addr));
|
|
|
|
if (0 == wmi_read_hdr(wil, d.addr, &hdr)) {
|
|
|
|
u16 len = le16_to_cpu(hdr.len);
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
seq_printf(s, " -> %04x %04x %04x %02x\n",
|
|
|
|
le16_to_cpu(hdr.seq), len,
|
|
|
|
le16_to_cpu(hdr.type), hdr.flags);
|
|
|
|
if (len <= MAX_MBOXITEM_SIZE) {
|
|
|
|
unsigned char databuf[MAX_MBOXITEM_SIZE];
|
|
|
|
void __iomem *src = wmi_buffer(wil, d.addr) +
|
|
|
|
sizeof(struct wil6210_mbox_hdr);
|
|
|
|
/*
|
|
|
|
* No need to check @src for validity -
|
|
|
|
* we already validated @d.addr while
|
|
|
|
* reading header
|
|
|
|
*/
|
|
|
|
wil_memcpy_fromio_32(databuf, src, len);
|
2015-09-09 18:38:48 -04:00
|
|
|
wil_seq_hexdump(s, databuf, len, " : ");
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
|
|
|
} else {
|
2014-09-10 09:34:34 -04:00
|
|
|
seq_puts(s, "\n");
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
2014-09-10 09:34:34 -04:00
|
|
|
seq_puts(s, "}\n");
|
2019-02-28 04:34:44 -05:00
|
|
|
wil_mem_access_unlock(wil);
|
2016-04-26 07:41:41 -04:00
|
|
|
wil_halp_unvote(wil);
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
|
|
|
|
2018-12-03 08:25:57 -05:00
|
|
|
static int mbox_show(struct seq_file *s, void *data)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
2017-11-14 08:25:33 -05:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = wil_pm_runtime_get(wil);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
wil_print_mbox_ring(s, "tx", wil->csr + HOST_MBOX +
|
2012-12-20 16:13:19 -05:00
|
|
|
offsetof(struct wil6210_mbox_ctl, tx));
|
2018-06-29 09:28:18 -04:00
|
|
|
wil_print_mbox_ring(s, "rx", wil->csr + HOST_MBOX +
|
2012-12-20 16:13:19 -05:00
|
|
|
offsetof(struct wil6210_mbox_ctl, rx));
|
|
|
|
|
2017-11-14 08:25:33 -05:00
|
|
|
wil_pm_runtime_put(wil);
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(mbox);
|
2012-12-20 16:13:19 -05:00
|
|
|
|
|
|
|
static int wil_debugfs_iomem_x32_set(void *data, u64 val)
|
|
|
|
{
|
2017-11-14 08:25:33 -05:00
|
|
|
struct wil_debugfs_iomem_data *d = (struct
|
|
|
|
wil_debugfs_iomem_data *)data;
|
|
|
|
struct wil6210_priv *wil = d->wil;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = wil_pm_runtime_get(wil);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2019-09-10 09:46:39 -04:00
|
|
|
writel_relaxed(val, (void __iomem *)d->offset);
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
wmb(); /* make sure write propagated to HW */
|
|
|
|
|
2017-11-14 08:25:33 -05:00
|
|
|
wil_pm_runtime_put(wil);
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wil_debugfs_iomem_x32_get(void *data, u64 *val)
|
|
|
|
{
|
2017-11-14 08:25:33 -05:00
|
|
|
struct wil_debugfs_iomem_data *d = (struct
|
|
|
|
wil_debugfs_iomem_data *)data;
|
|
|
|
struct wil6210_priv *wil = d->wil;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = wil_pm_runtime_get(wil);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*val = readl((void __iomem *)d->offset);
|
|
|
|
|
|
|
|
wil_pm_runtime_put(wil);
|
2012-12-20 16:13:19 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-05 07:07:26 -04:00
|
|
|
DEFINE_DEBUGFS_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get,
|
|
|
|
wil_debugfs_iomem_x32_set, "0x%08llx\n");
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2019-06-11 15:10:24 -04:00
|
|
|
static void wil_debugfs_create_iomem_x32(const char *name, umode_t mode,
|
|
|
|
struct dentry *parent, void *value,
|
|
|
|
struct wil6210_priv *wil)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
2017-11-14 08:25:33 -05:00
|
|
|
struct wil_debugfs_iomem_data *data = &wil->dbg_data.data_arr[
|
|
|
|
wil->dbg_data.iomem_data_count];
|
|
|
|
|
|
|
|
data->wil = wil;
|
|
|
|
data->offset = value;
|
|
|
|
|
2019-06-11 15:10:24 -04:00
|
|
|
debugfs_create_file_unsafe(name, mode, parent, data, &fops_iomem_x32);
|
|
|
|
wil->dbg_data.iomem_data_count++;
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
|
|
|
|
2014-06-16 12:37:02 -04:00
|
|
|
static int wil_debugfs_ulong_set(void *data, u64 val)
|
|
|
|
{
|
|
|
|
*(ulong *)data = val;
|
|
|
|
return 0;
|
|
|
|
}
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2014-06-16 12:37:02 -04:00
|
|
|
static int wil_debugfs_ulong_get(void *data, u64 *val)
|
|
|
|
{
|
|
|
|
*val = *(ulong *)data;
|
|
|
|
return 0;
|
|
|
|
}
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2018-10-05 07:07:26 -04:00
|
|
|
DEFINE_DEBUGFS_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get,
|
|
|
|
wil_debugfs_ulong_set, "0x%llx\n");
|
2014-06-16 12:37:02 -04:00
|
|
|
|
2014-08-06 03:31:55 -04:00
|
|
|
/**
|
|
|
|
* wil6210_debugfs_init_offset - create set of debugfs files
|
2020-08-19 03:23:55 -04:00
|
|
|
* @wil: driver's context, used for printing
|
|
|
|
* @dbg: directory on the debugfs, where files will be created
|
|
|
|
* @base: base address used in address calculation
|
|
|
|
* @tbl: table with file descriptions. Should be terminated with empty element.
|
2014-08-06 03:31:55 -04:00
|
|
|
*
|
|
|
|
* Creates files accordingly to the @tbl.
|
|
|
|
*/
|
|
|
|
static void wil6210_debugfs_init_offset(struct wil6210_priv *wil,
|
|
|
|
struct dentry *dbg, void *base,
|
|
|
|
const struct dbg_off * const tbl)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; tbl[i].name; i++) {
|
|
|
|
switch (tbl[i].type) {
|
|
|
|
case doff_u32:
|
2019-06-11 15:10:24 -04:00
|
|
|
debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg,
|
|
|
|
base + tbl[i].off);
|
2014-08-06 03:31:55 -04:00
|
|
|
break;
|
|
|
|
case doff_x32:
|
2019-06-11 15:10:24 -04:00
|
|
|
debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg,
|
|
|
|
base + tbl[i].off);
|
2014-08-06 03:31:55 -04:00
|
|
|
break;
|
|
|
|
case doff_ulong:
|
2019-06-11 15:10:24 -04:00
|
|
|
debugfs_create_file_unsafe(tbl[i].name, tbl[i].mode,
|
|
|
|
dbg, base + tbl[i].off,
|
|
|
|
&wil_fops_ulong);
|
2014-08-06 03:31:55 -04:00
|
|
|
break;
|
|
|
|
case doff_io32:
|
2019-06-11 15:10:24 -04:00
|
|
|
wil_debugfs_create_iomem_x32(tbl[i].name, tbl[i].mode,
|
|
|
|
dbg, base + tbl[i].off,
|
|
|
|
wil);
|
2014-08-06 03:31:55 -04:00
|
|
|
break;
|
2016-03-01 12:18:08 -05:00
|
|
|
case doff_u8:
|
2019-06-11 15:10:24 -04:00
|
|
|
debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg,
|
|
|
|
base + tbl[i].off);
|
2016-03-01 12:18:08 -05:00
|
|
|
break;
|
2014-08-06 03:31:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dbg_off isr_off[] = {
|
2017-01-20 06:49:53 -05:00
|
|
|
{"ICC", 0644, offsetof(struct RGF_ICR, ICC), doff_io32},
|
|
|
|
{"ICR", 0644, offsetof(struct RGF_ICR, ICR), doff_io32},
|
|
|
|
{"ICM", 0644, offsetof(struct RGF_ICR, ICM), doff_io32},
|
|
|
|
{"ICS", 0244, offsetof(struct RGF_ICR, ICS), doff_io32},
|
|
|
|
{"IMV", 0644, offsetof(struct RGF_ICR, IMV), doff_io32},
|
|
|
|
{"IMS", 0244, offsetof(struct RGF_ICR, IMS), doff_io32},
|
|
|
|
{"IMC", 0244, offsetof(struct RGF_ICR, IMC), doff_io32},
|
2014-08-06 03:31:55 -04:00
|
|
|
{},
|
|
|
|
};
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2019-06-11 15:10:24 -04:00
|
|
|
static void wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
|
|
|
|
const char *name, struct dentry *parent,
|
|
|
|
u32 off)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
|
|
|
struct dentry *d = debugfs_create_dir(name, parent);
|
|
|
|
|
2014-08-06 03:31:55 -04:00
|
|
|
wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr + off,
|
|
|
|
isr_off);
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
|
|
|
|
2014-08-06 03:31:55 -04:00
|
|
|
static const struct dbg_off pseudo_isr_off[] = {
|
2017-01-20 06:49:53 -05:00
|
|
|
{"CAUSE", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE), doff_io32},
|
|
|
|
{"MASK_SW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW), doff_io32},
|
|
|
|
{"MASK_FW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW), doff_io32},
|
2014-08-06 03:31:55 -04:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
2019-06-11 15:10:24 -04:00
|
|
|
static void wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil,
|
|
|
|
struct dentry *parent)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
|
|
|
struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent);
|
|
|
|
|
2014-08-06 03:31:55 -04:00
|
|
|
wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,
|
|
|
|
pseudo_isr_off);
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
|
|
|
|
2014-12-23 02:47:19 -05:00
|
|
|
static const struct dbg_off lgc_itr_cnt_off[] = {
|
2017-01-20 06:49:53 -05:00
|
|
|
{"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_CNT_TRSH), doff_io32},
|
|
|
|
{"DATA", 0644, HOSTADDR(RGF_DMA_ITR_CNT_DATA), doff_io32},
|
|
|
|
{"CTL", 0644, HOSTADDR(RGF_DMA_ITR_CNT_CRL), doff_io32},
|
2014-08-06 03:31:55 -04:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
2014-12-23 02:47:19 -05:00
|
|
|
static const struct dbg_off tx_itr_cnt_off[] = {
|
2017-01-20 06:49:53 -05:00
|
|
|
{"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_TRSH),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_DATA),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"CTL", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_CTL),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_TRSH),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_DATA),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"IDL_CTL", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_CTL),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dbg_off rx_itr_cnt_off[] = {
|
2017-01-20 06:49:53 -05:00
|
|
|
{"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_TRSH),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_DATA),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"CTL", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_CTL),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_TRSH),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_DATA),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"IDL_CTL", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_CTL),
|
2014-12-23 02:47:19 -05:00
|
|
|
doff_io32},
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
|
|
|
|
struct dentry *parent)
|
|
|
|
{
|
2014-12-23 02:47:19 -05:00
|
|
|
struct dentry *d, *dtx, *drx;
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2014-12-23 02:47:19 -05:00
|
|
|
d = debugfs_create_dir("ITR_CNT", parent);
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2014-12-23 02:47:19 -05:00
|
|
|
dtx = debugfs_create_dir("TX", d);
|
|
|
|
drx = debugfs_create_dir("RX", d);
|
|
|
|
|
2014-08-06 03:31:55 -04:00
|
|
|
wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,
|
2014-12-23 02:47:19 -05:00
|
|
|
lgc_itr_cnt_off);
|
|
|
|
|
|
|
|
wil6210_debugfs_init_offset(wil, dtx, (void * __force)wil->csr,
|
|
|
|
tx_itr_cnt_off);
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2014-12-23 02:47:19 -05:00
|
|
|
wil6210_debugfs_init_offset(wil, drx, (void * __force)wil->csr,
|
|
|
|
rx_itr_cnt_off);
|
2012-12-20 16:13:19 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-12-03 08:25:57 -05:00
|
|
|
static int memread_show(struct seq_file *s, void *data)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
2017-11-14 08:25:33 -05:00
|
|
|
void __iomem *a;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = wil_pm_runtime_get(wil);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2019-02-28 04:34:44 -05:00
|
|
|
ret = wil_mem_access_lock(wil);
|
|
|
|
if (ret) {
|
|
|
|
wil_pm_runtime_put(wil);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-11-14 08:25:33 -05:00
|
|
|
a = wmi_buffer(wil, cpu_to_le32(mem_addr));
|
2012-12-20 16:13:19 -05:00
|
|
|
|
|
|
|
if (a)
|
2015-07-30 06:52:03 -04:00
|
|
|
seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, readl(a));
|
2012-12-20 16:13:19 -05:00
|
|
|
else
|
|
|
|
seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);
|
|
|
|
|
2019-02-28 04:34:44 -05:00
|
|
|
wil_mem_access_unlock(wil);
|
2017-11-14 08:25:33 -05:00
|
|
|
wil_pm_runtime_put(wil);
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(memread);
|
2012-12-20 16:13:19 -05:00
|
|
|
|
|
|
|
static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
|
2014-09-10 09:34:34 -04:00
|
|
|
size_t count, loff_t *ppos)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
|
|
|
enum { max_count = 4096 };
|
2016-04-26 07:41:41 -04:00
|
|
|
struct wil_blob_wrapper *wil_blob = file->private_data;
|
2017-11-14 08:25:33 -05:00
|
|
|
struct wil6210_priv *wil = wil_blob->wil;
|
2018-10-31 04:52:18 -04:00
|
|
|
loff_t aligned_pos, pos = *ppos;
|
2016-04-26 07:41:41 -04:00
|
|
|
size_t available = wil_blob->blob.size;
|
2012-12-20 16:13:19 -05:00
|
|
|
void *buf;
|
2018-10-31 04:52:18 -04:00
|
|
|
size_t unaligned_bytes, aligned_count, ret;
|
2017-11-14 08:25:33 -05:00
|
|
|
int rc;
|
2012-12-20 16:13:19 -05:00
|
|
|
|
|
|
|
if (pos < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (pos >= available || !count)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (count > available - pos)
|
|
|
|
count = available - pos;
|
|
|
|
if (count > max_count)
|
|
|
|
count = max_count;
|
|
|
|
|
2018-10-31 04:52:18 -04:00
|
|
|
/* set pos to 4 bytes aligned */
|
|
|
|
unaligned_bytes = pos % 4;
|
|
|
|
aligned_pos = pos - unaligned_bytes;
|
|
|
|
aligned_count = count + unaligned_bytes;
|
|
|
|
|
|
|
|
buf = kmalloc(aligned_count, GFP_KERNEL);
|
2012-12-20 16:13:19 -05:00
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2017-11-14 08:25:33 -05:00
|
|
|
rc = wil_pm_runtime_get(wil);
|
|
|
|
if (rc < 0) {
|
|
|
|
kfree(buf);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-02-28 04:34:44 -05:00
|
|
|
rc = wil_mem_access_lock(wil);
|
|
|
|
if (rc) {
|
|
|
|
kfree(buf);
|
|
|
|
wil_pm_runtime_put(wil);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-04-05 07:58:12 -04:00
|
|
|
wil_memcpy_fromio_32(buf, (const void __iomem *)
|
2018-10-31 04:52:18 -04:00
|
|
|
wil_blob->blob.data + aligned_pos, aligned_count);
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2018-10-31 04:52:18 -04:00
|
|
|
ret = copy_to_user(user_buf, buf + unaligned_bytes, count);
|
2017-11-14 08:25:33 -05:00
|
|
|
|
2019-02-28 04:34:44 -05:00
|
|
|
wil_mem_access_unlock(wil);
|
2017-11-14 08:25:33 -05:00
|
|
|
wil_pm_runtime_put(wil);
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
kfree(buf);
|
|
|
|
if (ret == count)
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
count -= ret;
|
|
|
|
*ppos = pos + count;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_ioblob = {
|
|
|
|
.read = wil_read_file_ioblob,
|
2013-02-25 21:29:34 -05:00
|
|
|
.open = simple_open,
|
2012-12-20 16:13:19 -05:00
|
|
|
.llseek = default_llseek,
|
|
|
|
};
|
|
|
|
|
|
|
|
static
|
|
|
|
struct dentry *wil_debugfs_create_ioblob(const char *name,
|
2013-03-29 12:23:28 -04:00
|
|
|
umode_t mode,
|
2012-12-20 16:13:19 -05:00
|
|
|
struct dentry *parent,
|
2016-04-26 07:41:41 -04:00
|
|
|
struct wil_blob_wrapper *wil_blob)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
2016-04-26 07:41:41 -04:00
|
|
|
return debugfs_create_file(name, mode, parent, wil_blob, &fops_ioblob);
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2014-06-16 12:36:59 -04:00
|
|
|
/*---write channel 1..4 to rxon for it, 0 to rxoff---*/
|
|
|
|
static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
int rc;
|
|
|
|
long channel;
|
|
|
|
bool on;
|
|
|
|
|
2015-12-24 00:06:05 -05:00
|
|
|
char *kbuf = memdup_user_nul(buf, len);
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2015-12-24 00:06:05 -05:00
|
|
|
if (IS_ERR(kbuf))
|
|
|
|
return PTR_ERR(kbuf);
|
2014-06-16 12:36:59 -04:00
|
|
|
rc = kstrtol(kbuf, 0, &channel);
|
|
|
|
kfree(kbuf);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if ((channel < 0) || (channel > 4)) {
|
|
|
|
wil_err(wil, "Invalid channel %ld\n", channel);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
on = !!channel;
|
|
|
|
|
|
|
|
if (on) {
|
|
|
|
rc = wmi_set_channel(wil, (int)channel);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = wmi_rxon(wil, on);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_rxon = {
|
|
|
|
.write = wil_write_file_rxon,
|
|
|
|
.open = simple_open,
|
|
|
|
};
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2019-06-16 03:26:00 -04:00
|
|
|
static ssize_t wil_write_file_rbufcap(struct file *file,
|
|
|
|
const char __user *buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
int val;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = kstrtoint_from_user(buf, count, 0, &val);
|
|
|
|
if (rc) {
|
|
|
|
wil_err(wil, "Invalid argument\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
/* input value: negative to disable, 0 to use system default,
|
|
|
|
* 1..ring size to set descriptor threshold
|
|
|
|
*/
|
|
|
|
wil_info(wil, "%s RBUFCAP, descriptors threshold - %d\n",
|
|
|
|
val < 0 ? "Disabling" : "Enabling", val);
|
|
|
|
|
|
|
|
if (!wil->ring_rx.va || val > wil->ring_rx.size) {
|
|
|
|
wil_err(wil, "Invalid descriptors threshold, %d\n", val);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = wmi_rbufcap_cfg(wil, val < 0 ? 0 : 1, val < 0 ? 0 : val);
|
|
|
|
if (rc) {
|
|
|
|
wil_err(wil, "RBUFCAP config failed: %d\n", rc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_rbufcap = {
|
|
|
|
.write = wil_write_file_rbufcap,
|
|
|
|
.open = simple_open,
|
|
|
|
};
|
|
|
|
|
2014-12-23 02:47:16 -05:00
|
|
|
/* block ack control, write:
|
|
|
|
* - "add <ringid> <agg_size> <timeout>" to trigger ADDBA
|
|
|
|
* - "del_tx <ringid> <reason>" to trigger DELBA for Tx side
|
|
|
|
* - "del_rx <CID> <TID> <reason>" to trigger DELBA for Rx side
|
wil6210: ADDBA/DELBA flows
Introduce BACK establishment procedures; decision logic is not implemented
yet; debugfs entry 'addba' used to manually trigger addba/delba for ringid 0.
debugfs usage:
to establish BACK with agg_wsize 16:
echo 16 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to delete BACK:
echo 0 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to change agg_wsize, one need to delete BACK and establish it anew
ADDBA flow for:
- originator
Tx side (initiator) sends WMI_VRING_BA_EN_CMDID providing
agg_wsize and timeout parameters.
Eventually, it gets event confirming BACK agreement - WMI_BA_STATUS_EVENTID
with negotiated parameters. On this event, update Tx vring data
(struct vring_tx_data) and display BACK parameters on debugfs
- recipient
Rx side (recipient) firmware informs driver about ADDBA with
WMI_RCP_ADDBA_REQ_EVENTID, driver process it in service work
queue wq_service. It adjusts parameters and sends response
with WMI_RCP_ADDBA_RESP_CMDID, and final confirmation provided
by firmware with WMI_ADDBA_RESP_SENT_EVENTID. In case of success,
driver updates Rx BACK reorder buffer.
policy for BACK parameters:
- aggregation size (agg_wsize * MPDUsize)) to not exceed 64Kbytes
DELBA flow for:
- originator
driver decides to terminate BACK, it sends WMI_VRING_BA_DIS_CMDID
and updates struct vring_tx_data associated with vring; ignore
WMI_DELBA_EVENTID.
- recipient
firmware informs driver with WMI_DELBA_EVENTID,
driver deletes correspondent reorder buffer
ADDBA request processing requires sending WMI command, therefore
it is processed in work queue context. Same work queue used as for
connect, it get renamed to wq_service
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2014-12-23 02:47:03 -05:00
|
|
|
*/
|
2014-12-23 02:47:16 -05:00
|
|
|
static ssize_t wil_write_back(struct file *file, const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
wil6210: ADDBA/DELBA flows
Introduce BACK establishment procedures; decision logic is not implemented
yet; debugfs entry 'addba' used to manually trigger addba/delba for ringid 0.
debugfs usage:
to establish BACK with agg_wsize 16:
echo 16 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to delete BACK:
echo 0 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to change agg_wsize, one need to delete BACK and establish it anew
ADDBA flow for:
- originator
Tx side (initiator) sends WMI_VRING_BA_EN_CMDID providing
agg_wsize and timeout parameters.
Eventually, it gets event confirming BACK agreement - WMI_BA_STATUS_EVENTID
with negotiated parameters. On this event, update Tx vring data
(struct vring_tx_data) and display BACK parameters on debugfs
- recipient
Rx side (recipient) firmware informs driver about ADDBA with
WMI_RCP_ADDBA_REQ_EVENTID, driver process it in service work
queue wq_service. It adjusts parameters and sends response
with WMI_RCP_ADDBA_RESP_CMDID, and final confirmation provided
by firmware with WMI_ADDBA_RESP_SENT_EVENTID. In case of success,
driver updates Rx BACK reorder buffer.
policy for BACK parameters:
- aggregation size (agg_wsize * MPDUsize)) to not exceed 64Kbytes
DELBA flow for:
- originator
driver decides to terminate BACK, it sends WMI_VRING_BA_DIS_CMDID
and updates struct vring_tx_data associated with vring; ignore
WMI_DELBA_EVENTID.
- recipient
firmware informs driver with WMI_DELBA_EVENTID,
driver deletes correspondent reorder buffer
ADDBA request processing requires sending WMI command, therefore
it is processed in work queue context. Same work queue used as for
connect, it get renamed to wq_service
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2014-12-23 02:47:03 -05:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
int rc;
|
|
|
|
char *kbuf = kmalloc(len + 1, GFP_KERNEL);
|
2015-03-01 12:48:33 -05:00
|
|
|
char cmd[9];
|
2014-12-23 02:47:16 -05:00
|
|
|
int p1, p2, p3;
|
wil6210: ADDBA/DELBA flows
Introduce BACK establishment procedures; decision logic is not implemented
yet; debugfs entry 'addba' used to manually trigger addba/delba for ringid 0.
debugfs usage:
to establish BACK with agg_wsize 16:
echo 16 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to delete BACK:
echo 0 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to change agg_wsize, one need to delete BACK and establish it anew
ADDBA flow for:
- originator
Tx side (initiator) sends WMI_VRING_BA_EN_CMDID providing
agg_wsize and timeout parameters.
Eventually, it gets event confirming BACK agreement - WMI_BA_STATUS_EVENTID
with negotiated parameters. On this event, update Tx vring data
(struct vring_tx_data) and display BACK parameters on debugfs
- recipient
Rx side (recipient) firmware informs driver about ADDBA with
WMI_RCP_ADDBA_REQ_EVENTID, driver process it in service work
queue wq_service. It adjusts parameters and sends response
with WMI_RCP_ADDBA_RESP_CMDID, and final confirmation provided
by firmware with WMI_ADDBA_RESP_SENT_EVENTID. In case of success,
driver updates Rx BACK reorder buffer.
policy for BACK parameters:
- aggregation size (agg_wsize * MPDUsize)) to not exceed 64Kbytes
DELBA flow for:
- originator
driver decides to terminate BACK, it sends WMI_VRING_BA_DIS_CMDID
and updates struct vring_tx_data associated with vring; ignore
WMI_DELBA_EVENTID.
- recipient
firmware informs driver with WMI_DELBA_EVENTID,
driver deletes correspondent reorder buffer
ADDBA request processing requires sending WMI command, therefore
it is processed in work queue context. Same work queue used as for
connect, it get renamed to wq_service
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2014-12-23 02:47:03 -05:00
|
|
|
|
|
|
|
if (!kbuf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
|
|
|
|
if (rc != len) {
|
|
|
|
kfree(kbuf);
|
|
|
|
return rc >= 0 ? -EIO : rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
kbuf[len] = '\0';
|
2014-12-23 02:47:16 -05:00
|
|
|
rc = sscanf(kbuf, "%8s %d %d %d", cmd, &p1, &p2, &p3);
|
wil6210: ADDBA/DELBA flows
Introduce BACK establishment procedures; decision logic is not implemented
yet; debugfs entry 'addba' used to manually trigger addba/delba for ringid 0.
debugfs usage:
to establish BACK with agg_wsize 16:
echo 16 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to delete BACK:
echo 0 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to change agg_wsize, one need to delete BACK and establish it anew
ADDBA flow for:
- originator
Tx side (initiator) sends WMI_VRING_BA_EN_CMDID providing
agg_wsize and timeout parameters.
Eventually, it gets event confirming BACK agreement - WMI_BA_STATUS_EVENTID
with negotiated parameters. On this event, update Tx vring data
(struct vring_tx_data) and display BACK parameters on debugfs
- recipient
Rx side (recipient) firmware informs driver about ADDBA with
WMI_RCP_ADDBA_REQ_EVENTID, driver process it in service work
queue wq_service. It adjusts parameters and sends response
with WMI_RCP_ADDBA_RESP_CMDID, and final confirmation provided
by firmware with WMI_ADDBA_RESP_SENT_EVENTID. In case of success,
driver updates Rx BACK reorder buffer.
policy for BACK parameters:
- aggregation size (agg_wsize * MPDUsize)) to not exceed 64Kbytes
DELBA flow for:
- originator
driver decides to terminate BACK, it sends WMI_VRING_BA_DIS_CMDID
and updates struct vring_tx_data associated with vring; ignore
WMI_DELBA_EVENTID.
- recipient
firmware informs driver with WMI_DELBA_EVENTID,
driver deletes correspondent reorder buffer
ADDBA request processing requires sending WMI command, therefore
it is processed in work queue context. Same work queue used as for
connect, it get renamed to wq_service
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2014-12-23 02:47:03 -05:00
|
|
|
kfree(kbuf);
|
|
|
|
|
2014-12-23 02:47:16 -05:00
|
|
|
if (rc < 0)
|
wil6210: ADDBA/DELBA flows
Introduce BACK establishment procedures; decision logic is not implemented
yet; debugfs entry 'addba' used to manually trigger addba/delba for ringid 0.
debugfs usage:
to establish BACK with agg_wsize 16:
echo 16 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to delete BACK:
echo 0 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to change agg_wsize, one need to delete BACK and establish it anew
ADDBA flow for:
- originator
Tx side (initiator) sends WMI_VRING_BA_EN_CMDID providing
agg_wsize and timeout parameters.
Eventually, it gets event confirming BACK agreement - WMI_BA_STATUS_EVENTID
with negotiated parameters. On this event, update Tx vring data
(struct vring_tx_data) and display BACK parameters on debugfs
- recipient
Rx side (recipient) firmware informs driver about ADDBA with
WMI_RCP_ADDBA_REQ_EVENTID, driver process it in service work
queue wq_service. It adjusts parameters and sends response
with WMI_RCP_ADDBA_RESP_CMDID, and final confirmation provided
by firmware with WMI_ADDBA_RESP_SENT_EVENTID. In case of success,
driver updates Rx BACK reorder buffer.
policy for BACK parameters:
- aggregation size (agg_wsize * MPDUsize)) to not exceed 64Kbytes
DELBA flow for:
- originator
driver decides to terminate BACK, it sends WMI_VRING_BA_DIS_CMDID
and updates struct vring_tx_data associated with vring; ignore
WMI_DELBA_EVENTID.
- recipient
firmware informs driver with WMI_DELBA_EVENTID,
driver deletes correspondent reorder buffer
ADDBA request processing requires sending WMI command, therefore
it is processed in work queue context. Same work queue used as for
connect, it get renamed to wq_service
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2014-12-23 02:47:03 -05:00
|
|
|
return rc;
|
2014-12-23 02:47:16 -05:00
|
|
|
if (rc < 2)
|
wil6210: ADDBA/DELBA flows
Introduce BACK establishment procedures; decision logic is not implemented
yet; debugfs entry 'addba' used to manually trigger addba/delba for ringid 0.
debugfs usage:
to establish BACK with agg_wsize 16:
echo 16 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to delete BACK:
echo 0 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to change agg_wsize, one need to delete BACK and establish it anew
ADDBA flow for:
- originator
Tx side (initiator) sends WMI_VRING_BA_EN_CMDID providing
agg_wsize and timeout parameters.
Eventually, it gets event confirming BACK agreement - WMI_BA_STATUS_EVENTID
with negotiated parameters. On this event, update Tx vring data
(struct vring_tx_data) and display BACK parameters on debugfs
- recipient
Rx side (recipient) firmware informs driver about ADDBA with
WMI_RCP_ADDBA_REQ_EVENTID, driver process it in service work
queue wq_service. It adjusts parameters and sends response
with WMI_RCP_ADDBA_RESP_CMDID, and final confirmation provided
by firmware with WMI_ADDBA_RESP_SENT_EVENTID. In case of success,
driver updates Rx BACK reorder buffer.
policy for BACK parameters:
- aggregation size (agg_wsize * MPDUsize)) to not exceed 64Kbytes
DELBA flow for:
- originator
driver decides to terminate BACK, it sends WMI_VRING_BA_DIS_CMDID
and updates struct vring_tx_data associated with vring; ignore
WMI_DELBA_EVENTID.
- recipient
firmware informs driver with WMI_DELBA_EVENTID,
driver deletes correspondent reorder buffer
ADDBA request processing requires sending WMI command, therefore
it is processed in work queue context. Same work queue used as for
connect, it get renamed to wq_service
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2014-12-23 02:47:03 -05:00
|
|
|
return -EINVAL;
|
|
|
|
|
2018-02-26 13:12:13 -05:00
|
|
|
if ((strcmp(cmd, "add") == 0) ||
|
|
|
|
(strcmp(cmd, "del_tx") == 0)) {
|
2018-06-29 09:28:18 -04:00
|
|
|
struct wil_ring_tx_data *txdata;
|
2018-02-26 13:12:13 -05:00
|
|
|
|
|
|
|
if (p1 < 0 || p1 >= WIL6210_MAX_TX_RINGS) {
|
|
|
|
wil_err(wil, "BACK: invalid ring id %d\n", p1);
|
2014-12-23 02:47:16 -05:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-06-29 09:28:18 -04:00
|
|
|
txdata = &wil->ring_tx_data[p1];
|
2018-02-26 13:12:13 -05:00
|
|
|
if (strcmp(cmd, "add") == 0) {
|
|
|
|
if (rc < 3) {
|
|
|
|
wil_err(wil, "BACK: add require at least 2 params\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (rc < 4)
|
|
|
|
p3 = 0;
|
|
|
|
wmi_addba(wil, txdata->mid, p1, p2, p3);
|
|
|
|
} else {
|
|
|
|
if (rc < 3)
|
|
|
|
p2 = WLAN_REASON_QSTA_LEAVE_QBSS;
|
|
|
|
wmi_delba_tx(wil, txdata->mid, p1, p2);
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "del_rx") == 0) {
|
|
|
|
struct wil_sta_info *sta;
|
|
|
|
|
2014-12-23 02:47:16 -05:00
|
|
|
if (rc < 3) {
|
|
|
|
wil_err(wil,
|
|
|
|
"BACK: del_rx require at least 2 params\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2019-04-26 11:43:34 -04:00
|
|
|
if (p1 < 0 || p1 >= wil->max_assoc_sta) {
|
2018-02-26 13:12:13 -05:00
|
|
|
wil_err(wil, "BACK: invalid CID %d\n", p1);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2014-12-23 02:47:16 -05:00
|
|
|
if (rc < 4)
|
|
|
|
p3 = WLAN_REASON_QSTA_LEAVE_QBSS;
|
2018-02-26 13:12:13 -05:00
|
|
|
sta = &wil->sta[p1];
|
2019-02-22 09:21:01 -05:00
|
|
|
wmi_delba_rx(wil, sta->mid, p1, p2, p3);
|
2014-12-23 02:47:16 -05:00
|
|
|
} else {
|
|
|
|
wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
wil6210: ADDBA/DELBA flows
Introduce BACK establishment procedures; decision logic is not implemented
yet; debugfs entry 'addba' used to manually trigger addba/delba for ringid 0.
debugfs usage:
to establish BACK with agg_wsize 16:
echo 16 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to delete BACK:
echo 0 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to change agg_wsize, one need to delete BACK and establish it anew
ADDBA flow for:
- originator
Tx side (initiator) sends WMI_VRING_BA_EN_CMDID providing
agg_wsize and timeout parameters.
Eventually, it gets event confirming BACK agreement - WMI_BA_STATUS_EVENTID
with negotiated parameters. On this event, update Tx vring data
(struct vring_tx_data) and display BACK parameters on debugfs
- recipient
Rx side (recipient) firmware informs driver about ADDBA with
WMI_RCP_ADDBA_REQ_EVENTID, driver process it in service work
queue wq_service. It adjusts parameters and sends response
with WMI_RCP_ADDBA_RESP_CMDID, and final confirmation provided
by firmware with WMI_ADDBA_RESP_SENT_EVENTID. In case of success,
driver updates Rx BACK reorder buffer.
policy for BACK parameters:
- aggregation size (agg_wsize * MPDUsize)) to not exceed 64Kbytes
DELBA flow for:
- originator
driver decides to terminate BACK, it sends WMI_VRING_BA_DIS_CMDID
and updates struct vring_tx_data associated with vring; ignore
WMI_DELBA_EVENTID.
- recipient
firmware informs driver with WMI_DELBA_EVENTID,
driver deletes correspondent reorder buffer
ADDBA request processing requires sending WMI command, therefore
it is processed in work queue context. Same work queue used as for
connect, it get renamed to wq_service
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2014-12-23 02:47:03 -05:00
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2014-12-23 02:47:16 -05:00
|
|
|
static ssize_t wil_read_back(struct file *file, char __user *user_buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
static const char text[] = "block ack control, write:\n"
|
|
|
|
" - \"add <ringid> <agg_size> <timeout>\" to trigger ADDBA\n"
|
|
|
|
"If missing, <timeout> defaults to 0\n"
|
|
|
|
" - \"del_tx <ringid> <reason>\" to trigger DELBA for Tx side\n"
|
|
|
|
" - \"del_rx <CID> <TID> <reason>\" to trigger DELBA for Rx side\n"
|
|
|
|
"If missing, <reason> set to \"STA_LEAVING\" (36)\n";
|
|
|
|
|
|
|
|
return simple_read_from_buffer(user_buf, count, ppos, text,
|
|
|
|
sizeof(text));
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_back = {
|
|
|
|
.read = wil_read_back,
|
|
|
|
.write = wil_write_back,
|
wil6210: ADDBA/DELBA flows
Introduce BACK establishment procedures; decision logic is not implemented
yet; debugfs entry 'addba' used to manually trigger addba/delba for ringid 0.
debugfs usage:
to establish BACK with agg_wsize 16:
echo 16 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to delete BACK:
echo 0 > /sys/kernel/debug/ieee80211/phy0/wil6210/addba
to change agg_wsize, one need to delete BACK and establish it anew
ADDBA flow for:
- originator
Tx side (initiator) sends WMI_VRING_BA_EN_CMDID providing
agg_wsize and timeout parameters.
Eventually, it gets event confirming BACK agreement - WMI_BA_STATUS_EVENTID
with negotiated parameters. On this event, update Tx vring data
(struct vring_tx_data) and display BACK parameters on debugfs
- recipient
Rx side (recipient) firmware informs driver about ADDBA with
WMI_RCP_ADDBA_REQ_EVENTID, driver process it in service work
queue wq_service. It adjusts parameters and sends response
with WMI_RCP_ADDBA_RESP_CMDID, and final confirmation provided
by firmware with WMI_ADDBA_RESP_SENT_EVENTID. In case of success,
driver updates Rx BACK reorder buffer.
policy for BACK parameters:
- aggregation size (agg_wsize * MPDUsize)) to not exceed 64Kbytes
DELBA flow for:
- originator
driver decides to terminate BACK, it sends WMI_VRING_BA_DIS_CMDID
and updates struct vring_tx_data associated with vring; ignore
WMI_DELBA_EVENTID.
- recipient
firmware informs driver with WMI_DELBA_EVENTID,
driver deletes correspondent reorder buffer
ADDBA request processing requires sending WMI command, therefore
it is processed in work queue context. Same work queue used as for
connect, it get renamed to wq_service
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
2014-12-23 02:47:03 -05:00
|
|
|
.open = simple_open,
|
|
|
|
};
|
|
|
|
|
2015-04-30 09:25:09 -04:00
|
|
|
/* pmc control, write:
|
|
|
|
* - "alloc <num descriptors> <descriptor_size>" to allocate PMC
|
|
|
|
* - "free" to release memory allocated for PMC
|
|
|
|
*/
|
|
|
|
static ssize_t wil_write_pmccfg(struct file *file, const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
int rc;
|
|
|
|
char *kbuf = kmalloc(len + 1, GFP_KERNEL);
|
|
|
|
char cmd[9];
|
|
|
|
int num_descs, desc_size;
|
|
|
|
|
|
|
|
if (!kbuf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
|
|
|
|
if (rc != len) {
|
|
|
|
kfree(kbuf);
|
|
|
|
return rc >= 0 ? -EIO : rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
kbuf[len] = '\0';
|
|
|
|
rc = sscanf(kbuf, "%8s %d %d", cmd, &num_descs, &desc_size);
|
|
|
|
kfree(kbuf);
|
|
|
|
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if (rc < 1) {
|
|
|
|
wil_err(wil, "pmccfg: no params given\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 == strcmp(cmd, "alloc")) {
|
|
|
|
if (rc != 3) {
|
|
|
|
wil_err(wil, "pmccfg: alloc requires 2 params\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
wil_pmc_alloc(wil, num_descs, desc_size);
|
|
|
|
} else if (0 == strcmp(cmd, "free")) {
|
|
|
|
if (rc != 1) {
|
|
|
|
wil_err(wil, "pmccfg: free does not have any params\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
wil_pmc_free(wil, true);
|
|
|
|
} else {
|
|
|
|
wil_err(wil, "pmccfg: Unrecognized command \"%s\"\n", cmd);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wil_read_pmccfg(struct file *file, char __user *user_buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
char text[256];
|
|
|
|
char help[] = "pmc control, write:\n"
|
|
|
|
" - \"alloc <num descriptors> <descriptor_size>\" to allocate pmc\n"
|
|
|
|
" - \"free\" to free memory allocated for pmc\n";
|
|
|
|
|
2019-06-16 03:26:01 -04:00
|
|
|
snprintf(text, sizeof(text), "Last command status: %d\n\n%s",
|
|
|
|
wil_pmc_last_cmd_status(wil), help);
|
2015-04-30 09:25:09 -04:00
|
|
|
|
|
|
|
return simple_read_from_buffer(user_buf, count, ppos, text,
|
|
|
|
strlen(text) + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_pmccfg = {
|
|
|
|
.read = wil_read_pmccfg,
|
|
|
|
.write = wil_write_pmccfg,
|
|
|
|
.open = simple_open,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct file_operations fops_pmcdata = {
|
|
|
|
.open = simple_open,
|
|
|
|
.read = wil_pmc_read,
|
|
|
|
.llseek = wil_pmc_llseek,
|
|
|
|
};
|
|
|
|
|
2019-09-10 09:46:24 -04:00
|
|
|
static int wil_pmcring_seq_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, wil_pmcring_read, inode->i_private);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_pmcring = {
|
|
|
|
.open = wil_pmcring_seq_open,
|
|
|
|
.release = single_release,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
};
|
|
|
|
|
2014-06-16 12:36:59 -04:00
|
|
|
/*---tx_mgmt---*/
|
|
|
|
/* Write mgmt frame to this file to send it */
|
|
|
|
static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
struct wiphy *wiphy = wil_to_wiphy(wil);
|
2018-02-26 13:12:13 -05:00
|
|
|
struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;
|
2014-06-16 12:36:59 -04:00
|
|
|
struct cfg80211_mgmt_tx_params params;
|
|
|
|
int rc;
|
2017-05-06 11:42:19 -04:00
|
|
|
void *frame;
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2018-10-31 04:52:17 -04:00
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
2017-08-08 07:16:40 -04:00
|
|
|
if (!len)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-05-06 11:42:19 -04:00
|
|
|
frame = memdup_user(buf, len);
|
|
|
|
if (IS_ERR(frame))
|
|
|
|
return PTR_ERR(frame);
|
2014-06-16 12:36:59 -04:00
|
|
|
|
|
|
|
params.buf = frame;
|
|
|
|
params.len = len;
|
|
|
|
|
|
|
|
rc = wil_cfg80211_mgmt_tx(wiphy, wdev, ¶ms, NULL);
|
|
|
|
|
|
|
|
kfree(frame);
|
2017-01-20 06:49:46 -05:00
|
|
|
wil_info(wil, "-> %d\n", rc);
|
2014-06-16 12:36:59 -04:00
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_txmgmt = {
|
|
|
|
.write = wil_write_file_txmgmt,
|
|
|
|
.open = simple_open,
|
|
|
|
};
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2014-06-16 12:37:08 -04:00
|
|
|
/* Write WMI command (w/o mbox header) to this file to send it
|
|
|
|
* WMI starts from wil6210_mbox_hdr_wmi header
|
|
|
|
*/
|
|
|
|
static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
2018-02-26 13:12:13 -05:00
|
|
|
struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
|
2016-03-01 12:18:09 -05:00
|
|
|
struct wmi_cmd_hdr *wmi;
|
2014-06-16 12:37:08 -04:00
|
|
|
void *cmd;
|
2016-03-01 12:18:09 -05:00
|
|
|
int cmdlen = len - sizeof(struct wmi_cmd_hdr);
|
2014-06-16 12:37:08 -04:00
|
|
|
u16 cmdid;
|
2022-07-25 13:49:11 -04:00
|
|
|
int rc1;
|
2014-06-16 12:37:08 -04:00
|
|
|
|
2022-07-15 06:35:18 -04:00
|
|
|
if (cmdlen < 0 || *ppos != 0)
|
2014-06-16 12:37:08 -04:00
|
|
|
return -EINVAL;
|
|
|
|
|
2022-07-15 06:35:18 -04:00
|
|
|
wmi = memdup_user(buf, len);
|
|
|
|
if (IS_ERR(wmi))
|
|
|
|
return PTR_ERR(wmi);
|
2014-06-16 12:37:08 -04:00
|
|
|
|
2016-03-21 16:01:11 -04:00
|
|
|
cmd = (cmdlen > 0) ? &wmi[1] : NULL;
|
2016-03-01 12:18:09 -05:00
|
|
|
cmdid = le16_to_cpu(wmi->command_id);
|
2014-06-16 12:37:08 -04:00
|
|
|
|
2018-02-26 13:12:13 -05:00
|
|
|
rc1 = wmi_send(wil, cmdid, vif->mid, cmd, cmdlen);
|
2014-06-16 12:37:08 -04:00
|
|
|
kfree(wmi);
|
|
|
|
|
2017-01-20 06:49:46 -05:00
|
|
|
wil_info(wil, "0x%04x[%d] -> %d\n", cmdid, cmdlen, rc1);
|
2014-06-16 12:37:08 -04:00
|
|
|
|
2022-07-25 13:49:11 -04:00
|
|
|
return len;
|
2014-06-16 12:37:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_wmi = {
|
|
|
|
.write = wil_write_file_wmi,
|
|
|
|
.open = simple_open,
|
|
|
|
};
|
|
|
|
|
2014-03-17 09:34:08 -04:00
|
|
|
static void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int len = skb_headlen(skb);
|
|
|
|
void *p = skb->data;
|
|
|
|
int nr_frags = skb_shinfo(skb)->nr_frags;
|
|
|
|
|
|
|
|
seq_printf(s, " len = %d\n", len);
|
|
|
|
wil_seq_hexdump(s, p, len, " : ");
|
|
|
|
|
|
|
|
if (nr_frags) {
|
|
|
|
seq_printf(s, " nr_frags = %d\n", nr_frags);
|
|
|
|
for (i = 0; i < nr_frags; i++) {
|
2019-07-22 23:08:25 -04:00
|
|
|
const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
|
2014-03-17 09:34:08 -04:00
|
|
|
|
|
|
|
len = skb_frag_size(frag);
|
|
|
|
p = skb_frag_address_safe(frag);
|
|
|
|
seq_printf(s, " [%2d] : len = %d\n", i, len);
|
|
|
|
wil_seq_hexdump(s, p, len, " : ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-27 09:20:41 -05:00
|
|
|
/*---------Tx/Rx descriptor------------*/
|
2018-12-03 08:25:57 -05:00
|
|
|
static int txdesc_show(struct seq_file *s, void *data)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
2018-06-29 09:28:18 -04:00
|
|
|
struct wil_ring *ring;
|
2018-06-29 09:28:37 -04:00
|
|
|
bool tx;
|
|
|
|
int ring_idx = dbg_ring_index;
|
|
|
|
int txdesc_idx = dbg_txdesc_index;
|
|
|
|
volatile struct vring_tx_desc *d;
|
|
|
|
volatile u32 *u;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
if (wil->use_enhanced_dma_hw) {
|
|
|
|
/* RX ring index == 0 */
|
|
|
|
if (ring_idx >= WIL6210_MAX_TX_RINGS) {
|
|
|
|
seq_printf(s, "invalid ring index %d\n", ring_idx);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
tx = ring_idx > 0; /* desc ring 0 is reserved for RX */
|
|
|
|
} else {
|
|
|
|
/* RX ring index == WIL6210_MAX_TX_RINGS */
|
|
|
|
if (ring_idx > WIL6210_MAX_TX_RINGS) {
|
|
|
|
seq_printf(s, "invalid ring index %d\n", ring_idx);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
tx = (ring_idx < WIL6210_MAX_TX_RINGS);
|
|
|
|
}
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2018-06-29 09:28:37 -04:00
|
|
|
ring = tx ? &wil->ring_tx[ring_idx] : &wil->ring_rx;
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2018-06-29 09:28:18 -04:00
|
|
|
if (!ring->va) {
|
2014-03-02 04:20:50 -05:00
|
|
|
if (tx)
|
2018-06-29 09:28:37 -04:00
|
|
|
seq_printf(s, "No Tx[%2d] RING\n", ring_idx);
|
2014-02-27 09:20:41 -05:00
|
|
|
else
|
2018-06-29 09:28:37 -04:00
|
|
|
seq_puts(s, "No Rx RING\n");
|
2012-12-20 16:13:19 -05:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-06-29 09:28:37 -04:00
|
|
|
if (txdesc_idx >= ring->size) {
|
2014-03-02 04:20:50 -05:00
|
|
|
if (tx)
|
2018-06-29 09:28:37 -04:00
|
|
|
seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n",
|
|
|
|
ring_idx, txdesc_idx, ring->size);
|
2014-02-27 09:20:41 -05:00
|
|
|
else
|
2018-06-29 09:28:37 -04:00
|
|
|
seq_printf(s, "RxDesc index (%d) >= size (%d)\n",
|
|
|
|
txdesc_idx, ring->size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* use struct vring_tx_desc for Rx as well,
|
|
|
|
* only field used, .dma.length, is the same
|
|
|
|
*/
|
|
|
|
d = &ring->va[txdesc_idx].tx.legacy;
|
|
|
|
u = (volatile u32 *)d;
|
|
|
|
skb = NULL;
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2018-06-29 09:28:37 -04:00
|
|
|
if (wil->use_enhanced_dma_hw) {
|
|
|
|
if (tx) {
|
2019-04-26 11:43:33 -04:00
|
|
|
skb = ring->ctx ? ring->ctx[txdesc_idx].skb : NULL;
|
|
|
|
} else if (wil->rx_buff_mgmt.buff_arr) {
|
2018-06-29 09:28:37 -04:00
|
|
|
struct wil_rx_enhanced_desc *rx_d =
|
|
|
|
(struct wil_rx_enhanced_desc *)
|
|
|
|
&ring->va[txdesc_idx].rx.enhanced;
|
|
|
|
u16 buff_id = le16_to_cpu(rx_d->mac.buff_id);
|
|
|
|
|
|
|
|
if (!wil_val_in_range(buff_id, 0,
|
2019-04-26 11:43:33 -04:00
|
|
|
wil->rx_buff_mgmt.size))
|
2018-06-29 09:28:37 -04:00
|
|
|
seq_printf(s, "invalid buff_id %d\n", buff_id);
|
2019-04-26 11:43:33 -04:00
|
|
|
else
|
|
|
|
skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
|
|
|
} else {
|
2018-06-29 09:28:37 -04:00
|
|
|
skb = ring->ctx[txdesc_idx].skb;
|
|
|
|
}
|
|
|
|
if (tx)
|
|
|
|
seq_printf(s, "Tx[%2d][%3d] = {\n", ring_idx,
|
|
|
|
txdesc_idx);
|
|
|
|
else
|
|
|
|
seq_printf(s, "Rx[%3d] = {\n", txdesc_idx);
|
|
|
|
seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
|
|
|
u[0], u[1], u[2], u[3]);
|
|
|
|
seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
|
|
|
u[4], u[5], u[6], u[7]);
|
|
|
|
seq_printf(s, " SKB = 0x%p\n", skb);
|
|
|
|
|
|
|
|
if (skb) {
|
|
|
|
skb_get(skb);
|
|
|
|
wil_seq_print_skb(s, skb);
|
|
|
|
kfree_skb(skb);
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|
2018-06-29 09:28:37 -04:00
|
|
|
seq_puts(s, "}\n");
|
2012-12-20 16:13:19 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(txdesc);
|
2012-12-20 16:13:19 -05:00
|
|
|
|
2018-06-29 09:28:37 -04:00
|
|
|
/*---------Tx/Rx status message------------*/
|
2018-12-03 08:25:57 -05:00
|
|
|
static int status_msg_show(struct seq_file *s, void *data)
|
2018-06-29 09:28:37 -04:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
int sring_idx = dbg_sring_index;
|
|
|
|
struct wil_status_ring *sring;
|
2019-04-26 11:43:33 -04:00
|
|
|
bool tx;
|
2018-06-29 09:28:37 -04:00
|
|
|
u32 status_msg_idx = dbg_status_msg_index;
|
|
|
|
u32 *u;
|
|
|
|
|
|
|
|
if (sring_idx >= WIL6210_MAX_STATUS_RINGS) {
|
|
|
|
seq_printf(s, "invalid status ring index %d\n", sring_idx);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
sring = &wil->srings[sring_idx];
|
2019-04-26 11:43:33 -04:00
|
|
|
tx = !sring->is_rx;
|
2018-06-29 09:28:37 -04:00
|
|
|
|
|
|
|
if (!sring->va) {
|
|
|
|
seq_printf(s, "No %cX status ring\n", tx ? 'T' : 'R');
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status_msg_idx >= sring->size) {
|
|
|
|
seq_printf(s, "%cxDesc index (%d) >= size (%d)\n",
|
|
|
|
tx ? 'T' : 'R', status_msg_idx, sring->size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
u = sring->va + (sring->elem_size * status_msg_idx);
|
|
|
|
|
|
|
|
seq_printf(s, "%cx[%d][%3d] = {\n",
|
|
|
|
tx ? 'T' : 'R', sring_idx, status_msg_idx);
|
|
|
|
|
|
|
|
seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
|
|
|
u[0], u[1], u[2], u[3]);
|
|
|
|
if (!tx && !wil->use_compressed_rx_status)
|
|
|
|
seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
|
|
|
u[4], u[5], u[6], u[7]);
|
|
|
|
|
|
|
|
seq_puts(s, "}\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(status_msg);
|
2018-06-29 09:28:37 -04:00
|
|
|
|
|
|
|
static int wil_print_rx_buff(struct seq_file *s, struct list_head *lh)
|
|
|
|
{
|
|
|
|
struct wil_rx_buff *it;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
list_for_each_entry(it, lh, list) {
|
|
|
|
if ((i % 16) == 0 && i != 0)
|
|
|
|
seq_puts(s, "\n ");
|
|
|
|
seq_printf(s, "[%4d] ", it->id);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
seq_printf(s, "\nNumber of buffers: %u\n", i);
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2018-12-03 08:25:57 -05:00
|
|
|
static int rx_buff_mgmt_show(struct seq_file *s, void *data)
|
2018-06-29 09:28:37 -04:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
struct wil_rx_buff_mgmt *rbm = &wil->rx_buff_mgmt;
|
|
|
|
int num_active;
|
|
|
|
int num_free;
|
|
|
|
|
2018-08-23 07:47:13 -04:00
|
|
|
if (!rbm->buff_arr)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-06-29 09:28:37 -04:00
|
|
|
seq_printf(s, " size = %zu\n", rbm->size);
|
|
|
|
seq_printf(s, " free_list_empty_cnt = %lu\n",
|
|
|
|
rbm->free_list_empty_cnt);
|
|
|
|
|
|
|
|
/* Print active list */
|
|
|
|
seq_puts(s, " Active list:\n");
|
|
|
|
num_active = wil_print_rx_buff(s, &rbm->active);
|
|
|
|
seq_puts(s, "\n Free list:\n");
|
|
|
|
num_free = wil_print_rx_buff(s, &rbm->free);
|
|
|
|
|
|
|
|
seq_printf(s, " Total number of buffers: %u\n",
|
|
|
|
num_active + num_free);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(rx_buff_mgmt);
|
2018-06-29 09:28:37 -04:00
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
/*---------beamforming------------*/
|
2014-08-06 03:31:56 -04:00
|
|
|
static char *wil_bfstatus_str(u32 status)
|
|
|
|
{
|
|
|
|
switch (status) {
|
|
|
|
case 0:
|
|
|
|
return "Failed";
|
|
|
|
case 1:
|
|
|
|
return "OK";
|
|
|
|
case 2:
|
|
|
|
return "Retrying";
|
|
|
|
default:
|
|
|
|
return "??";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_all_zeros(void * const x_, size_t sz)
|
|
|
|
{
|
|
|
|
/* if reply is all-0, ignore this CID */
|
|
|
|
u32 *x = x_;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
for (n = 0; n < sz / sizeof(*x); n++)
|
|
|
|
if (x[n])
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-12-03 08:25:57 -05:00
|
|
|
static int bf_show(struct seq_file *s, void *data)
|
2012-12-20 16:13:19 -05:00
|
|
|
{
|
2014-08-06 03:31:56 -04:00
|
|
|
int rc;
|
|
|
|
int i;
|
2012-12-20 16:13:19 -05:00
|
|
|
struct wil6210_priv *wil = s->private;
|
2018-02-26 13:12:13 -05:00
|
|
|
struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
|
2014-08-06 03:31:56 -04:00
|
|
|
struct wmi_notify_req_cmd cmd = {
|
|
|
|
.interval_usec = 0,
|
|
|
|
};
|
|
|
|
struct {
|
2016-03-01 12:18:09 -05:00
|
|
|
struct wmi_cmd_hdr wmi;
|
2014-08-06 03:31:56 -04:00
|
|
|
struct wmi_notify_req_done_event evt;
|
|
|
|
} __packed reply;
|
|
|
|
|
2018-05-09 06:06:57 -04:00
|
|
|
memset(&reply, 0, sizeof(reply));
|
|
|
|
|
2019-04-26 11:43:34 -04:00
|
|
|
for (i = 0; i < wil->max_assoc_sta; i++) {
|
2014-08-06 03:31:56 -04:00
|
|
|
u32 status;
|
2021-01-06 18:50:50 -05:00
|
|
|
u8 bf_mcs;
|
2014-08-06 03:31:56 -04:00
|
|
|
|
|
|
|
cmd.cid = i;
|
2018-02-26 13:12:13 -05:00
|
|
|
rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid,
|
|
|
|
&cmd, sizeof(cmd),
|
2014-08-06 03:31:56 -04:00
|
|
|
WMI_NOTIFY_REQ_DONE_EVENTID, &reply,
|
2019-06-16 03:26:06 -04:00
|
|
|
sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS);
|
2014-08-06 03:31:56 -04:00
|
|
|
/* if reply is all-0, ignore this CID */
|
|
|
|
if (rc || is_all_zeros(&reply.evt, sizeof(reply.evt)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
status = le32_to_cpu(reply.evt.status);
|
2021-01-06 18:50:50 -05:00
|
|
|
bf_mcs = le16_to_cpu(reply.evt.bf_mcs);
|
2014-08-06 03:31:56 -04:00
|
|
|
seq_printf(s, "CID %d {\n"
|
|
|
|
" TSF = 0x%016llx\n"
|
2021-01-06 18:50:50 -05:00
|
|
|
" TxMCS = %s TxTpt = %4d\n"
|
2014-08-06 03:31:56 -04:00
|
|
|
" SQI = %4d\n"
|
2017-08-08 07:16:43 -04:00
|
|
|
" RSSI = %4d\n"
|
2014-08-06 03:31:56 -04:00
|
|
|
" Status = 0x%08x %s\n"
|
|
|
|
" Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n"
|
|
|
|
" Goodput(rx:tx) %4d:%4d\n"
|
|
|
|
"}\n",
|
|
|
|
i,
|
|
|
|
le64_to_cpu(reply.evt.tsf),
|
2021-01-06 18:50:50 -05:00
|
|
|
WIL_EXTENDED_MCS_CHECK(bf_mcs),
|
2014-08-06 03:31:56 -04:00
|
|
|
le32_to_cpu(reply.evt.tx_tpt),
|
|
|
|
reply.evt.sqi,
|
2017-08-08 07:16:43 -04:00
|
|
|
reply.evt.rssi,
|
2014-08-06 03:31:56 -04:00
|
|
|
status, wil_bfstatus_str(status),
|
|
|
|
le16_to_cpu(reply.evt.my_rx_sector),
|
|
|
|
le16_to_cpu(reply.evt.my_tx_sector),
|
|
|
|
le16_to_cpu(reply.evt.other_rx_sector),
|
|
|
|
le16_to_cpu(reply.evt.other_tx_sector),
|
|
|
|
le32_to_cpu(reply.evt.rx_goodput),
|
|
|
|
le32_to_cpu(reply.evt.tx_goodput));
|
|
|
|
}
|
2012-12-20 16:13:19 -05:00
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(bf);
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2013-03-13 08:12:51 -04:00
|
|
|
/*---------temp------------*/
|
2018-07-24 03:44:25 -04:00
|
|
|
static void print_temp(struct seq_file *s, const char *prefix, s32 t)
|
2013-03-13 08:12:51 -04:00
|
|
|
{
|
|
|
|
switch (t) {
|
|
|
|
case 0:
|
2019-06-16 03:26:05 -04:00
|
|
|
case WMI_INVALID_TEMPERATURE:
|
2013-03-13 08:12:51 -04:00
|
|
|
seq_printf(s, "%s N/A\n", prefix);
|
|
|
|
break;
|
|
|
|
default:
|
2018-07-24 03:44:25 -04:00
|
|
|
seq_printf(s, "%s %s%d.%03d\n", prefix, (t < 0 ? "-" : ""),
|
|
|
|
abs(t / 1000), abs(t % 1000));
|
2013-03-13 08:12:51 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-03 08:25:57 -05:00
|
|
|
static int temp_show(struct seq_file *s, void *data)
|
2013-03-13 08:12:51 -04:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
2019-06-16 03:26:05 -04:00
|
|
|
int rc, i;
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2019-06-16 03:26:05 -04:00
|
|
|
if (test_bit(WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF,
|
|
|
|
wil->fw_capabilities)) {
|
|
|
|
struct wmi_temp_sense_all_done_event sense_all_evt;
|
2013-03-13 08:12:51 -04:00
|
|
|
|
2019-06-16 03:26:05 -04:00
|
|
|
wil_dbg_misc(wil,
|
|
|
|
"WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is supported");
|
|
|
|
rc = wmi_get_all_temperatures(wil, &sense_all_evt);
|
|
|
|
if (rc) {
|
|
|
|
seq_puts(s, "Failed\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
print_temp(s, "T_mac =",
|
|
|
|
le32_to_cpu(sense_all_evt.baseband_t1000));
|
|
|
|
seq_printf(s, "Connected RFs [0x%08x]\n",
|
|
|
|
sense_all_evt.rf_bitmap);
|
|
|
|
for (i = 0; i < WMI_MAX_XIF_PORTS_NUM; i++) {
|
|
|
|
seq_printf(s, "RF[%d] = ", i);
|
|
|
|
print_temp(s, "",
|
|
|
|
le32_to_cpu(sense_all_evt.rf_t1000[i]));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s32 t_m, t_r;
|
2013-03-13 08:12:51 -04:00
|
|
|
|
2019-06-16 03:26:05 -04:00
|
|
|
wil_dbg_misc(wil,
|
|
|
|
"WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is not supported");
|
|
|
|
rc = wmi_get_temperature(wil, &t_m, &t_r);
|
|
|
|
if (rc) {
|
|
|
|
seq_puts(s, "Failed\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
print_temp(s, "T_mac =", t_m);
|
|
|
|
print_temp(s, "T_radio =", t_r);
|
|
|
|
}
|
2013-03-13 08:12:51 -04:00
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(temp);
|
2013-03-13 08:12:51 -04:00
|
|
|
|
2014-06-16 12:37:13 -04:00
|
|
|
/*---------link------------*/
|
2018-12-03 08:25:57 -05:00
|
|
|
static int link_show(struct seq_file *s, void *data)
|
2014-06-16 12:37:13 -04:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
2018-05-10 08:57:35 -04:00
|
|
|
struct station_info *sinfo;
|
|
|
|
int i, rc = 0;
|
|
|
|
|
|
|
|
sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
|
|
|
|
if (!sinfo)
|
|
|
|
return -ENOMEM;
|
2014-06-16 12:37:13 -04:00
|
|
|
|
2019-04-26 11:43:34 -04:00
|
|
|
for (i = 0; i < wil->max_assoc_sta; i++) {
|
2014-06-16 12:37:13 -04:00
|
|
|
struct wil_sta_info *p = &wil->sta[i];
|
|
|
|
char *status = "unknown";
|
2018-02-26 13:12:17 -05:00
|
|
|
struct wil6210_vif *vif;
|
|
|
|
u8 mid;
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2014-06-16 12:37:13 -04:00
|
|
|
switch (p->status) {
|
|
|
|
case wil_sta_unused:
|
|
|
|
status = "unused ";
|
|
|
|
break;
|
|
|
|
case wil_sta_conn_pending:
|
|
|
|
status = "pending ";
|
|
|
|
break;
|
|
|
|
case wil_sta_connected:
|
|
|
|
status = "connected";
|
|
|
|
break;
|
|
|
|
}
|
2018-02-26 13:12:17 -05:00
|
|
|
mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
|
|
|
|
seq_printf(s, "[%d][MID %d] %pM %s\n",
|
|
|
|
i, mid, p->addr, status);
|
2014-06-16 12:37:13 -04:00
|
|
|
|
2018-02-26 13:12:17 -05:00
|
|
|
if (p->status != wil_sta_connected)
|
|
|
|
continue;
|
|
|
|
|
2019-02-28 04:34:52 -05:00
|
|
|
vif = (mid < GET_MAX_VIFS(wil)) ? wil->vifs[mid] : NULL;
|
2018-02-26 13:12:17 -05:00
|
|
|
if (vif) {
|
2018-05-10 08:57:35 -04:00
|
|
|
rc = wil_cid_fill_sinfo(vif, i, sinfo);
|
2014-06-16 12:37:13 -04:00
|
|
|
if (rc)
|
2018-05-10 08:57:35 -04:00
|
|
|
goto out;
|
2014-06-16 12:37:13 -04:00
|
|
|
|
2021-01-06 18:50:50 -05:00
|
|
|
seq_printf(s, " Tx_mcs = %s\n",
|
|
|
|
WIL_EXTENDED_MCS_CHECK(sinfo->txrate.mcs));
|
|
|
|
seq_printf(s, " Rx_mcs = %s\n",
|
|
|
|
WIL_EXTENDED_MCS_CHECK(sinfo->rxrate.mcs));
|
2018-05-10 08:57:35 -04:00
|
|
|
seq_printf(s, " SQ = %d\n", sinfo->signal);
|
2018-02-26 13:12:17 -05:00
|
|
|
} else {
|
|
|
|
seq_puts(s, " INVALID MID\n");
|
2014-06-16 12:37:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-10 08:57:35 -04:00
|
|
|
out:
|
|
|
|
kfree(sinfo);
|
|
|
|
return rc;
|
2014-06-16 12:37:13 -04:00
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(link);
|
2014-06-16 12:37:13 -04:00
|
|
|
|
2014-06-16 12:37:21 -04:00
|
|
|
/*---------info------------*/
|
2018-12-03 08:25:57 -05:00
|
|
|
static int info_show(struct seq_file *s, void *data)
|
2014-06-16 12:37:21 -04:00
|
|
|
{
|
2014-06-16 12:37:22 -04:00
|
|
|
struct wil6210_priv *wil = s->private;
|
2018-02-26 13:12:13 -05:00
|
|
|
struct net_device *ndev = wil->main_ndev;
|
2014-06-16 12:37:21 -04:00
|
|
|
int is_ac = power_supply_is_system_supplied();
|
2014-06-16 12:37:22 -04:00
|
|
|
int rx = atomic_xchg(&wil->isr_count_rx, 0);
|
|
|
|
int tx = atomic_xchg(&wil->isr_count_tx, 0);
|
|
|
|
static ulong rxf_old, txf_old;
|
|
|
|
ulong rxf = ndev->stats.rx_packets;
|
|
|
|
ulong txf = ndev->stats.tx_packets;
|
2014-06-16 12:37:23 -04:00
|
|
|
unsigned int i;
|
2014-06-16 12:37:21 -04:00
|
|
|
|
|
|
|
/* >0 : AC; 0 : battery; <0 : error */
|
|
|
|
seq_printf(s, "AC powered : %d\n", is_ac);
|
2014-06-16 12:37:22 -04:00
|
|
|
seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old);
|
|
|
|
seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old);
|
|
|
|
rxf_old = rxf;
|
|
|
|
txf_old = txf;
|
2014-06-16 12:37:21 -04:00
|
|
|
|
2014-06-16 12:37:23 -04:00
|
|
|
#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \
|
|
|
|
" " __stringify(x) : ""
|
|
|
|
|
|
|
|
for (i = 0; i < ndev->num_tx_queues; i++) {
|
|
|
|
struct netdev_queue *txq = netdev_get_tx_queue(ndev, i);
|
|
|
|
unsigned long state = txq->state;
|
|
|
|
|
|
|
|
seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state,
|
|
|
|
CHECK_QSTATE(DRV_XOFF),
|
|
|
|
CHECK_QSTATE(STACK_XOFF),
|
|
|
|
CHECK_QSTATE(FROZEN)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#undef CHECK_QSTATE
|
2014-06-16 12:37:21 -04:00
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(info);
|
2014-06-16 12:37:21 -04:00
|
|
|
|
2014-10-01 08:05:24 -04:00
|
|
|
/*---------recovery------------*/
|
|
|
|
/* mode = [manual|auto]
|
|
|
|
* state = [idle|pending|running]
|
|
|
|
*/
|
|
|
|
static ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
char buf[80];
|
|
|
|
int n;
|
|
|
|
static const char * const sstate[] = {"idle", "pending", "running"};
|
|
|
|
|
|
|
|
n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n",
|
|
|
|
no_fw_recovery ? "manual" : "auto",
|
|
|
|
sstate[wil->recovery_state]);
|
|
|
|
|
|
|
|
n = min_t(int, n, sizeof(buf));
|
|
|
|
|
|
|
|
return simple_read_from_buffer(user_buf, count, ppos,
|
|
|
|
buf, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wil_write_file_recovery(struct file *file,
|
|
|
|
const char __user *buf_,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
static const char run_command[] = "run";
|
|
|
|
char buf[sizeof(run_command) + 1]; /* to detect "runx" */
|
|
|
|
ssize_t rc;
|
|
|
|
|
|
|
|
if (wil->recovery_state != fw_recovery_pending) {
|
|
|
|
wil_err(wil, "No recovery pending\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*ppos != 0) {
|
|
|
|
wil_err(wil, "Offset [%d]\n", (int)*ppos);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count > sizeof(buf)) {
|
|
|
|
wil_err(wil, "Input too long, len = %d\n", (int)count);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
buf[rc] = '\0';
|
|
|
|
if (0 == strcmp(buf, run_command))
|
|
|
|
wil_set_recovery_state(wil, fw_recovery_running);
|
|
|
|
else
|
|
|
|
wil_err(wil, "Bad recovery command \"%s\"\n", buf);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_recovery = {
|
|
|
|
.read = wil_read_file_recovery,
|
|
|
|
.write = wil_write_file_recovery,
|
|
|
|
.open = simple_open,
|
|
|
|
};
|
|
|
|
|
2014-02-27 09:20:43 -05:00
|
|
|
/*---------Station matrix------------*/
|
2014-02-27 09:20:44 -05:00
|
|
|
static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size;
|
2015-07-30 06:52:01 -04:00
|
|
|
unsigned long long drop_dup = r->drop_dup, drop_old = r->drop_old;
|
2018-07-24 03:44:22 -04:00
|
|
|
unsigned long long drop_dup_mcast = r->drop_dup_mcast;
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2018-05-09 06:06:58 -04:00
|
|
|
seq_printf(s, "([%2d]) 0x%03x [", r->buf_size, r->head_seq_num);
|
2014-02-27 09:20:44 -05:00
|
|
|
for (i = 0; i < r->buf_size; i++) {
|
|
|
|
if (i == index)
|
|
|
|
seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|');
|
|
|
|
else
|
|
|
|
seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_');
|
|
|
|
}
|
2015-07-30 06:52:01 -04:00
|
|
|
seq_printf(s,
|
2018-07-24 03:44:22 -04:00
|
|
|
"] total %llu drop %llu (dup %llu + old %llu + dup mcast %llu) last 0x%03x\n",
|
|
|
|
r->total, drop_dup + drop_old + drop_dup_mcast, drop_dup,
|
|
|
|
drop_old, drop_dup_mcast, r->ssn_last_drop);
|
2014-02-27 09:20:44 -05:00
|
|
|
}
|
2014-02-27 09:20:43 -05:00
|
|
|
|
2016-03-01 12:18:07 -05:00
|
|
|
static void wil_print_rxtid_crypto(struct seq_file *s, int tid,
|
|
|
|
struct wil_tid_crypto_rx *c)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
struct wil_tid_crypto_rx_single *cc = &c->key_id[i];
|
|
|
|
|
|
|
|
if (cc->key_set)
|
|
|
|
goto has_keys;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
has_keys:
|
|
|
|
if (tid < WIL_STA_TID_NUM)
|
|
|
|
seq_printf(s, " [%2d] PN", tid);
|
|
|
|
else
|
|
|
|
seq_puts(s, " [GR] PN");
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
struct wil_tid_crypto_rx_single *cc = &c->key_id[i];
|
|
|
|
|
|
|
|
seq_printf(s, " [%i%s]%6phN", i, cc->key_set ? "+" : "-",
|
|
|
|
cc->pn);
|
|
|
|
}
|
|
|
|
seq_puts(s, "\n");
|
|
|
|
}
|
|
|
|
|
2018-12-03 08:25:57 -05:00
|
|
|
static int sta_show(struct seq_file *s, void *data)
|
2014-12-23 02:47:24 -05:00
|
|
|
__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
|
2014-02-27 09:20:43 -05:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
2015-06-09 07:11:18 -04:00
|
|
|
int i, tid, mcs;
|
2014-02-27 09:20:43 -05:00
|
|
|
|
2019-04-26 11:43:34 -04:00
|
|
|
for (i = 0; i < wil->max_assoc_sta; i++) {
|
2014-02-27 09:20:43 -05:00
|
|
|
struct wil_sta_info *p = &wil->sta[i];
|
|
|
|
char *status = "unknown";
|
2017-01-20 06:49:51 -05:00
|
|
|
u8 aid = 0;
|
2018-02-26 13:12:17 -05:00
|
|
|
u8 mid;
|
2018-08-23 07:47:12 -04:00
|
|
|
bool sta_connected = false;
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2014-02-27 09:20:43 -05:00
|
|
|
switch (p->status) {
|
|
|
|
case wil_sta_unused:
|
|
|
|
status = "unused ";
|
|
|
|
break;
|
|
|
|
case wil_sta_conn_pending:
|
|
|
|
status = "pending ";
|
|
|
|
break;
|
|
|
|
case wil_sta_connected:
|
|
|
|
status = "connected";
|
2017-01-20 06:49:51 -05:00
|
|
|
aid = p->aid;
|
2014-02-27 09:20:43 -05:00
|
|
|
break;
|
|
|
|
}
|
2018-02-26 13:12:17 -05:00
|
|
|
mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
|
2019-02-28 04:34:52 -05:00
|
|
|
if (mid < GET_MAX_VIFS(wil)) {
|
2018-08-23 07:47:12 -04:00
|
|
|
struct wil6210_vif *vif = wil->vifs[mid];
|
|
|
|
|
|
|
|
if (vif->wdev.iftype == NL80211_IFTYPE_STATION &&
|
|
|
|
p->status == wil_sta_connected)
|
|
|
|
sta_connected = true;
|
|
|
|
}
|
|
|
|
/* print roam counter only for connected stations */
|
|
|
|
if (sta_connected)
|
|
|
|
seq_printf(s, "[%d] %pM connected (roam counter %d) MID %d AID %d\n",
|
|
|
|
i, p->addr, p->stats.ft_roams, mid, aid);
|
|
|
|
else
|
|
|
|
seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i,
|
|
|
|
p->addr, status, mid, aid);
|
2014-02-27 09:20:44 -05:00
|
|
|
|
|
|
|
if (p->status == wil_sta_connected) {
|
2014-12-23 02:47:24 -05:00
|
|
|
spin_lock_bh(&p->tid_rx_lock);
|
2014-02-27 09:20:44 -05:00
|
|
|
for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
|
|
|
|
struct wil_tid_ampdu_rx *r = p->tid_rx[tid];
|
2016-03-01 12:18:07 -05:00
|
|
|
struct wil_tid_crypto_rx *c =
|
|
|
|
&p->tid_crypto_rx[tid];
|
2014-09-10 09:34:34 -04:00
|
|
|
|
2014-02-27 09:20:44 -05:00
|
|
|
if (r) {
|
2016-03-01 12:18:07 -05:00
|
|
|
seq_printf(s, " [%2d] ", tid);
|
2014-02-27 09:20:44 -05:00
|
|
|
wil_print_rxtid(s, r);
|
|
|
|
}
|
2016-03-01 12:18:07 -05:00
|
|
|
|
|
|
|
wil_print_rxtid_crypto(s, tid, c);
|
2014-02-27 09:20:44 -05:00
|
|
|
}
|
2016-03-01 12:18:07 -05:00
|
|
|
wil_print_rxtid_crypto(s, WIL_STA_TID_NUM,
|
|
|
|
&p->group_crypto_rx);
|
2014-12-23 02:47:24 -05:00
|
|
|
spin_unlock_bh(&p->tid_rx_lock);
|
2015-10-04 03:23:19 -04:00
|
|
|
seq_printf(s,
|
2016-03-01 12:18:07 -05:00
|
|
|
"Rx invalid frame: non-data %lu, short %lu, large %lu, replay %lu\n",
|
2015-10-04 03:23:19 -04:00
|
|
|
p->stats.rx_non_data_frame,
|
|
|
|
p->stats.rx_short_frame,
|
2016-03-01 12:18:07 -05:00
|
|
|
p->stats.rx_large_frame,
|
|
|
|
p->stats.rx_replay);
|
2018-07-24 03:44:23 -04:00
|
|
|
seq_printf(s,
|
2018-07-24 03:44:26 -04:00
|
|
|
"mic error %lu, key error %lu, amsdu error %lu, csum error %lu\n",
|
2018-07-24 03:44:23 -04:00
|
|
|
p->stats.rx_mic_error,
|
|
|
|
p->stats.rx_key_error,
|
2018-07-24 03:44:26 -04:00
|
|
|
p->stats.rx_amsdu_error,
|
|
|
|
p->stats.rx_csum_err);
|
2018-06-29 09:28:37 -04:00
|
|
|
|
2015-06-09 07:11:18 -04:00
|
|
|
seq_puts(s, "Rx/MCS:");
|
|
|
|
for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs);
|
|
|
|
mcs++)
|
|
|
|
seq_printf(s, " %lld",
|
|
|
|
p->stats.rx_per_mcs[mcs]);
|
|
|
|
seq_puts(s, "\n");
|
2014-02-27 09:20:44 -05:00
|
|
|
}
|
2014-02-27 09:20:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(sta);
|
2014-02-27 09:20:43 -05:00
|
|
|
|
2018-12-03 08:25:57 -05:00
|
|
|
static int mids_show(struct seq_file *s, void *data)
|
2018-02-26 13:12:18 -05:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
struct wil6210_vif *vif;
|
|
|
|
struct net_device *ndev;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
mutex_lock(&wil->vif_mutex);
|
2019-02-28 04:34:52 -05:00
|
|
|
for (i = 0; i < GET_MAX_VIFS(wil); i++) {
|
2018-02-26 13:12:18 -05:00
|
|
|
vif = wil->vifs[i];
|
|
|
|
|
|
|
|
if (vif) {
|
|
|
|
ndev = vif_to_ndev(vif);
|
|
|
|
seq_printf(s, "[%d] %pM %s\n", i, ndev->dev_addr,
|
|
|
|
ndev->name);
|
|
|
|
} else {
|
|
|
|
seq_printf(s, "[%d] unused\n", i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mutex_unlock(&wil->vif_mutex);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-12-03 08:25:57 -05:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(mids);
|
2018-02-26 13:12:18 -05:00
|
|
|
|
2018-07-24 03:44:24 -04:00
|
|
|
static int wil_tx_latency_debugfs_show(struct seq_file *s, void *data)
|
|
|
|
__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
int i, bin;
|
|
|
|
|
2019-04-26 11:43:34 -04:00
|
|
|
for (i = 0; i < wil->max_assoc_sta; i++) {
|
2018-07-24 03:44:24 -04:00
|
|
|
struct wil_sta_info *p = &wil->sta[i];
|
|
|
|
char *status = "unknown";
|
|
|
|
u8 aid = 0;
|
|
|
|
u8 mid;
|
|
|
|
|
|
|
|
if (!p->tx_latency_bins)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (p->status) {
|
|
|
|
case wil_sta_unused:
|
|
|
|
status = "unused ";
|
|
|
|
break;
|
|
|
|
case wil_sta_conn_pending:
|
|
|
|
status = "pending ";
|
|
|
|
break;
|
|
|
|
case wil_sta_connected:
|
|
|
|
status = "connected";
|
|
|
|
aid = p->aid;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
|
|
|
|
seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status,
|
|
|
|
mid, aid);
|
|
|
|
|
|
|
|
if (p->status == wil_sta_connected) {
|
|
|
|
u64 num_packets = 0;
|
|
|
|
u64 tx_latency_avg = p->stats.tx_latency_total_us;
|
|
|
|
|
|
|
|
seq_puts(s, "Tx/Latency bin:");
|
|
|
|
for (bin = 0; bin < WIL_NUM_LATENCY_BINS; bin++) {
|
|
|
|
seq_printf(s, " %lld",
|
|
|
|
p->tx_latency_bins[bin]);
|
|
|
|
num_packets += p->tx_latency_bins[bin];
|
|
|
|
}
|
|
|
|
seq_puts(s, "\n");
|
|
|
|
if (!num_packets)
|
|
|
|
continue;
|
|
|
|
do_div(tx_latency_avg, num_packets);
|
|
|
|
seq_printf(s, "Tx/Latency min/avg/max (us): %d/%lld/%d",
|
|
|
|
p->stats.tx_latency_min_us,
|
|
|
|
tx_latency_avg,
|
|
|
|
p->stats.tx_latency_max_us);
|
|
|
|
|
|
|
|
seq_puts(s, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wil_tx_latency_seq_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, wil_tx_latency_debugfs_show,
|
|
|
|
inode->i_private);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wil_tx_latency_write(struct file *file, const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct seq_file *s = file->private_data;
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
int val, rc, i;
|
|
|
|
bool enable;
|
|
|
|
|
|
|
|
rc = kstrtoint_from_user(buf, len, 0, &val);
|
|
|
|
if (rc) {
|
|
|
|
wil_err(wil, "Invalid argument\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
if (val == 1)
|
|
|
|
/* default resolution */
|
|
|
|
val = 500;
|
|
|
|
if (val && (val < 50 || val > 1000)) {
|
|
|
|
wil_err(wil, "Invalid resolution %d\n", val);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
enable = !!val;
|
|
|
|
if (wil->tx_latency == enable)
|
|
|
|
return len;
|
|
|
|
|
|
|
|
wil_info(wil, "%s TX latency measurements (resolution %dusec)\n",
|
|
|
|
enable ? "Enabling" : "Disabling", val);
|
|
|
|
|
|
|
|
if (enable) {
|
|
|
|
size_t sz = sizeof(u64) * WIL_NUM_LATENCY_BINS;
|
|
|
|
|
|
|
|
wil->tx_latency_res = val;
|
2019-04-26 11:43:34 -04:00
|
|
|
for (i = 0; i < wil->max_assoc_sta; i++) {
|
2018-07-24 03:44:24 -04:00
|
|
|
struct wil_sta_info *sta = &wil->sta[i];
|
|
|
|
|
|
|
|
kfree(sta->tx_latency_bins);
|
|
|
|
sta->tx_latency_bins = kzalloc(sz, GFP_KERNEL);
|
|
|
|
if (!sta->tx_latency_bins)
|
|
|
|
return -ENOMEM;
|
|
|
|
sta->stats.tx_latency_min_us = U32_MAX;
|
|
|
|
sta->stats.tx_latency_max_us = 0;
|
|
|
|
sta->stats.tx_latency_total_us = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wil->tx_latency = enable;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_tx_latency = {
|
|
|
|
.open = wil_tx_latency_seq_open,
|
|
|
|
.release = single_release,
|
|
|
|
.read = seq_read,
|
|
|
|
.write = wil_tx_latency_write,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
};
|
|
|
|
|
2018-07-24 03:44:31 -04:00
|
|
|
static void wil_link_stats_print_basic(struct wil6210_vif *vif,
|
|
|
|
struct seq_file *s,
|
|
|
|
struct wmi_link_stats_basic *basic)
|
|
|
|
{
|
|
|
|
char per[5] = "?";
|
|
|
|
|
|
|
|
if (basic->per_average != 0xff)
|
|
|
|
snprintf(per, sizeof(per), "%d%%", basic->per_average);
|
|
|
|
|
|
|
|
seq_printf(s, "CID %d {\n"
|
2021-01-06 18:50:50 -05:00
|
|
|
"\tTxMCS %s TxTpt %d\n"
|
2018-07-24 03:44:31 -04:00
|
|
|
"\tGoodput(rx:tx) %d:%d\n"
|
|
|
|
"\tRxBcastFrames %d\n"
|
|
|
|
"\tRSSI %d SQI %d SNR %d PER %s\n"
|
|
|
|
"\tRx RFC %d Ant num %d\n"
|
|
|
|
"\tSectors(rx:tx) my %d:%d peer %d:%d\n"
|
|
|
|
"}\n",
|
|
|
|
basic->cid,
|
2021-01-06 18:50:50 -05:00
|
|
|
WIL_EXTENDED_MCS_CHECK(basic->bf_mcs),
|
|
|
|
le32_to_cpu(basic->tx_tpt),
|
2018-07-24 03:44:31 -04:00
|
|
|
le32_to_cpu(basic->rx_goodput),
|
|
|
|
le32_to_cpu(basic->tx_goodput),
|
|
|
|
le32_to_cpu(basic->rx_bcast_frames),
|
|
|
|
basic->rssi, basic->sqi, basic->snr, per,
|
|
|
|
basic->selected_rfc, basic->rx_effective_ant_num,
|
|
|
|
basic->my_rx_sector, basic->my_tx_sector,
|
|
|
|
basic->other_rx_sector, basic->other_tx_sector);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wil_link_stats_print_global(struct wil6210_priv *wil,
|
|
|
|
struct seq_file *s,
|
|
|
|
struct wmi_link_stats_global *global)
|
|
|
|
{
|
|
|
|
seq_printf(s, "Frames(rx:tx) %d:%d\n"
|
|
|
|
"BA Frames(rx:tx) %d:%d\n"
|
|
|
|
"Beacons %d\n"
|
|
|
|
"Rx Errors (MIC:CRC) %d:%d\n"
|
|
|
|
"Tx Errors (no ack) %d\n",
|
|
|
|
le32_to_cpu(global->rx_frames),
|
|
|
|
le32_to_cpu(global->tx_frames),
|
|
|
|
le32_to_cpu(global->rx_ba_frames),
|
|
|
|
le32_to_cpu(global->tx_ba_frames),
|
|
|
|
le32_to_cpu(global->tx_beacons),
|
|
|
|
le32_to_cpu(global->rx_mic_errors),
|
|
|
|
le32_to_cpu(global->rx_crc_errors),
|
|
|
|
le32_to_cpu(global->tx_fail_no_ack));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wil_link_stats_debugfs_show_vif(struct wil6210_vif *vif,
|
|
|
|
struct seq_file *s)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = vif_to_wil(vif);
|
|
|
|
struct wmi_link_stats_basic *stats;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!vif->fw_stats_ready) {
|
|
|
|
seq_puts(s, "no statistics\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
seq_printf(s, "TSF %lld\n", vif->fw_stats_tsf);
|
2019-04-26 11:43:34 -04:00
|
|
|
for (i = 0; i < wil->max_assoc_sta; i++) {
|
2018-07-24 03:44:31 -04:00
|
|
|
if (wil->sta[i].status == wil_sta_unused)
|
|
|
|
continue;
|
|
|
|
if (wil->sta[i].mid != vif->mid)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
stats = &wil->sta[i].fw_stats_basic;
|
|
|
|
wil_link_stats_print_basic(vif, s, stats);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wil_link_stats_debugfs_show(struct seq_file *s, void *data)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
struct wil6210_vif *vif;
|
|
|
|
int i, rc;
|
|
|
|
|
|
|
|
rc = mutex_lock_interruptible(&wil->vif_mutex);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/* iterate over all MIDs and show per-cid statistics. Then show the
|
|
|
|
* global statistics
|
|
|
|
*/
|
2019-02-28 04:34:52 -05:00
|
|
|
for (i = 0; i < GET_MAX_VIFS(wil); i++) {
|
2018-07-24 03:44:31 -04:00
|
|
|
vif = wil->vifs[i];
|
|
|
|
|
|
|
|
seq_printf(s, "MID %d ", i);
|
|
|
|
if (!vif) {
|
|
|
|
seq_puts(s, "unused\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
wil_link_stats_debugfs_show_vif(vif, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&wil->vif_mutex);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wil_link_stats_seq_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, wil_link_stats_debugfs_show, inode->i_private);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wil_link_stats_write(struct file *file, const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct seq_file *s = file->private_data;
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
int cid, interval, rc, i;
|
|
|
|
struct wil6210_vif *vif;
|
|
|
|
char *kbuf = kmalloc(len + 1, GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!kbuf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
|
|
|
|
if (rc != len) {
|
|
|
|
kfree(kbuf);
|
|
|
|
return rc >= 0 ? -EIO : rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
kbuf[len] = '\0';
|
|
|
|
/* specify cid (use -1 for all cids) and snapshot interval in ms */
|
|
|
|
rc = sscanf(kbuf, "%d %d", &cid, &interval);
|
|
|
|
kfree(kbuf);
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
if (rc < 2 || interval < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
wil_info(wil, "request link statistics, cid %d interval %d\n",
|
|
|
|
cid, interval);
|
|
|
|
|
|
|
|
rc = mutex_lock_interruptible(&wil->vif_mutex);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
2019-02-28 04:34:52 -05:00
|
|
|
for (i = 0; i < GET_MAX_VIFS(wil); i++) {
|
2018-07-24 03:44:31 -04:00
|
|
|
vif = wil->vifs[i];
|
|
|
|
if (!vif)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_BASIC,
|
|
|
|
(cid == -1 ? 0xff : cid), interval);
|
|
|
|
if (rc)
|
|
|
|
wil_err(wil, "link statistics failed for mid %d\n", i);
|
|
|
|
}
|
|
|
|
mutex_unlock(&wil->vif_mutex);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_link_stats = {
|
|
|
|
.open = wil_link_stats_seq_open,
|
|
|
|
.release = single_release,
|
|
|
|
.read = seq_read,
|
|
|
|
.write = wil_link_stats_write,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
wil_link_stats_global_debugfs_show(struct seq_file *s, void *data)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
|
|
|
|
if (!wil->fw_stats_global.ready)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
seq_printf(s, "TSF %lld\n", wil->fw_stats_global.tsf);
|
|
|
|
wil_link_stats_print_global(wil, s, &wil->fw_stats_global.stats);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
wil_link_stats_global_seq_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, wil_link_stats_global_debugfs_show,
|
|
|
|
inode->i_private);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
wil_link_stats_global_write(struct file *file, const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct seq_file *s = file->private_data;
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
int interval, rc;
|
|
|
|
struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
|
|
|
|
|
|
|
|
/* specify snapshot interval in ms */
|
|
|
|
rc = kstrtoint_from_user(buf, len, 0, &interval);
|
|
|
|
if (rc || interval < 0) {
|
|
|
|
wil_err(wil, "Invalid argument\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
wil_info(wil, "request global link stats, interval %d\n", interval);
|
|
|
|
|
|
|
|
rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_GLOBAL, 0, interval);
|
|
|
|
if (rc)
|
|
|
|
wil_err(wil, "global link stats failed %d\n", rc);
|
|
|
|
|
|
|
|
return rc ? rc : len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_link_stats_global = {
|
|
|
|
.open = wil_link_stats_global_seq_open,
|
|
|
|
.release = single_release,
|
|
|
|
.read = seq_read,
|
|
|
|
.write = wil_link_stats_global_write,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
};
|
|
|
|
|
2016-05-09 14:57:11 -04:00
|
|
|
static ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
char buf[80];
|
|
|
|
int n;
|
|
|
|
|
|
|
|
n = snprintf(buf, sizeof(buf),
|
|
|
|
"led_id is set to %d, echo 1 to enable, 0 to disable\n",
|
|
|
|
led_id);
|
|
|
|
|
|
|
|
n = min_t(int, n, sizeof(buf));
|
|
|
|
|
|
|
|
return simple_read_from_buffer(user_buf, count, ppos,
|
|
|
|
buf, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wil_write_file_led_cfg(struct file *file,
|
|
|
|
const char __user *buf_,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
int val;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = kstrtoint_from_user(buf_, count, 0, &val);
|
|
|
|
if (rc) {
|
|
|
|
wil_err(wil, "Invalid argument\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
wil_info(wil, "%s led %d\n", val ? "Enabling" : "Disabling", led_id);
|
|
|
|
rc = wmi_led_cfg(wil, val);
|
|
|
|
if (rc) {
|
|
|
|
wil_info(wil, "%s led %d failed\n",
|
|
|
|
val ? "Enabling" : "Disabling", led_id);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_led_cfg = {
|
|
|
|
.read = wil_read_file_led_cfg,
|
|
|
|
.write = wil_write_file_led_cfg,
|
|
|
|
.open = simple_open,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* led_blink_time, write:
|
|
|
|
* "<blink_on_slow> <blink_off_slow> <blink_on_med> <blink_off_med> <blink_on_fast> <blink_off_fast>
|
|
|
|
*/
|
|
|
|
static ssize_t wil_write_led_blink_time(struct file *file,
|
|
|
|
const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char *kbuf = kmalloc(len + 1, GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!kbuf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
|
|
|
|
if (rc != len) {
|
|
|
|
kfree(kbuf);
|
|
|
|
return rc >= 0 ? -EIO : rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
kbuf[len] = '\0';
|
|
|
|
rc = sscanf(kbuf, "%d %d %d %d %d %d",
|
|
|
|
&led_blink_time[WIL_LED_TIME_SLOW].on_ms,
|
|
|
|
&led_blink_time[WIL_LED_TIME_SLOW].off_ms,
|
|
|
|
&led_blink_time[WIL_LED_TIME_MED].on_ms,
|
|
|
|
&led_blink_time[WIL_LED_TIME_MED].off_ms,
|
|
|
|
&led_blink_time[WIL_LED_TIME_FAST].on_ms,
|
|
|
|
&led_blink_time[WIL_LED_TIME_FAST].off_ms);
|
|
|
|
kfree(kbuf);
|
|
|
|
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
if (rc < 6)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wil_read_led_blink_time(struct file *file, char __user *user_buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
static char text[400];
|
|
|
|
|
|
|
|
snprintf(text, sizeof(text),
|
|
|
|
"To set led blink on/off time variables write:\n"
|
|
|
|
"<blink_on_slow> <blink_off_slow> <blink_on_med> "
|
|
|
|
"<blink_off_med> <blink_on_fast> <blink_off_fast>\n"
|
|
|
|
"The current values are:\n"
|
|
|
|
"%d %d %d %d %d %d\n",
|
|
|
|
led_blink_time[WIL_LED_TIME_SLOW].on_ms,
|
|
|
|
led_blink_time[WIL_LED_TIME_SLOW].off_ms,
|
|
|
|
led_blink_time[WIL_LED_TIME_MED].on_ms,
|
|
|
|
led_blink_time[WIL_LED_TIME_MED].off_ms,
|
|
|
|
led_blink_time[WIL_LED_TIME_FAST].on_ms,
|
|
|
|
led_blink_time[WIL_LED_TIME_FAST].off_ms);
|
|
|
|
|
|
|
|
return simple_read_from_buffer(user_buf, count, ppos, text,
|
|
|
|
sizeof(text));
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_led_blink_time = {
|
|
|
|
.read = wil_read_led_blink_time,
|
|
|
|
.write = wil_write_led_blink_time,
|
|
|
|
.open = simple_open,
|
|
|
|
};
|
|
|
|
|
2016-08-22 05:42:21 -04:00
|
|
|
/*---------FW capabilities------------*/
|
2022-09-27 02:18:54 -04:00
|
|
|
static int fw_capabilities_show(struct seq_file *s, void *data)
|
2016-08-22 05:42:21 -04:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
|
|
|
|
seq_printf(s, "fw_capabilities : %*pb\n", WMI_FW_CAPABILITY_MAX,
|
|
|
|
wil->fw_capabilities);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2022-09-27 02:18:54 -04:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(fw_capabilities);
|
2016-08-22 05:42:21 -04:00
|
|
|
|
2016-08-22 05:42:22 -04:00
|
|
|
/*---------FW version------------*/
|
2022-09-27 02:18:54 -04:00
|
|
|
static int fw_version_show(struct seq_file *s, void *data)
|
2016-08-22 05:42:22 -04:00
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
|
|
|
|
if (wil->fw_version[0])
|
|
|
|
seq_printf(s, "%s\n", wil->fw_version);
|
|
|
|
else
|
|
|
|
seq_puts(s, "N/A\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2022-09-27 02:18:54 -04:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(fw_version);
|
2016-08-22 05:42:22 -04:00
|
|
|
|
2017-06-16 03:38:04 -04:00
|
|
|
/*---------suspend_stats---------*/
|
|
|
|
static ssize_t wil_write_suspend_stats(struct file *file,
|
|
|
|
const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
|
|
|
|
|
|
|
memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wil_read_suspend_stats(struct file *file,
|
|
|
|
char __user *user_buf,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = file->private_data;
|
2017-11-14 08:25:44 -05:00
|
|
|
char *text;
|
|
|
|
int n, ret, text_size = 500;
|
|
|
|
|
|
|
|
text = kmalloc(text_size, GFP_KERNEL);
|
|
|
|
if (!text)
|
|
|
|
return -ENOMEM;
|
2017-06-16 03:38:04 -04:00
|
|
|
|
2017-11-14 08:25:44 -05:00
|
|
|
n = snprintf(text, text_size,
|
|
|
|
"Radio on suspend statistics:\n"
|
2017-06-16 03:38:04 -04:00
|
|
|
"successful suspends:%ld failed suspends:%ld\n"
|
|
|
|
"successful resumes:%ld failed resumes:%ld\n"
|
2017-11-14 08:25:44 -05:00
|
|
|
"rejected by device:%ld\n"
|
|
|
|
"Radio off suspend statistics:\n"
|
|
|
|
"successful suspends:%ld failed suspends:%ld\n"
|
|
|
|
"successful resumes:%ld failed resumes:%ld\n"
|
|
|
|
"General statistics:\n"
|
|
|
|
"rejected by host:%ld\n",
|
|
|
|
wil->suspend_stats.r_on.successful_suspends,
|
|
|
|
wil->suspend_stats.r_on.failed_suspends,
|
|
|
|
wil->suspend_stats.r_on.successful_resumes,
|
|
|
|
wil->suspend_stats.r_on.failed_resumes,
|
|
|
|
wil->suspend_stats.rejected_by_device,
|
|
|
|
wil->suspend_stats.r_off.successful_suspends,
|
|
|
|
wil->suspend_stats.r_off.failed_suspends,
|
|
|
|
wil->suspend_stats.r_off.successful_resumes,
|
|
|
|
wil->suspend_stats.r_off.failed_resumes,
|
|
|
|
wil->suspend_stats.rejected_by_host);
|
|
|
|
|
|
|
|
n = min_t(int, n, text_size);
|
|
|
|
|
|
|
|
ret = simple_read_from_buffer(user_buf, count, ppos, text, n);
|
|
|
|
|
|
|
|
kfree(text);
|
|
|
|
|
|
|
|
return ret;
|
2017-06-16 03:38:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_suspend_stats = {
|
|
|
|
.read = wil_read_suspend_stats,
|
|
|
|
.write = wil_write_suspend_stats,
|
|
|
|
.open = simple_open,
|
|
|
|
};
|
|
|
|
|
2018-06-29 09:28:23 -04:00
|
|
|
/*---------compressed_rx_status---------*/
|
|
|
|
static ssize_t wil_compressed_rx_status_write(struct file *file,
|
|
|
|
const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct seq_file *s = file->private_data;
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
int compressed_rx_status;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = kstrtoint_from_user(buf, len, 0, &compressed_rx_status);
|
|
|
|
if (rc) {
|
|
|
|
wil_err(wil, "Invalid argument\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wil_has_active_ifaces(wil, true, false)) {
|
|
|
|
wil_err(wil, "cannot change edma config after iface is up\n");
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
wil_info(wil, "%sable compressed_rx_status\n",
|
|
|
|
compressed_rx_status ? "En" : "Dis");
|
|
|
|
|
|
|
|
wil->use_compressed_rx_status = compressed_rx_status;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
wil_compressed_rx_status_show(struct seq_file *s, void *data)
|
|
|
|
{
|
|
|
|
struct wil6210_priv *wil = s->private;
|
|
|
|
|
|
|
|
seq_printf(s, "%d\n", wil->use_compressed_rx_status);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
wil_compressed_rx_status_seq_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, wil_compressed_rx_status_show,
|
|
|
|
inode->i_private);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations fops_compressed_rx_status = {
|
|
|
|
.open = wil_compressed_rx_status_seq_open,
|
|
|
|
.release = single_release,
|
|
|
|
.read = seq_read,
|
|
|
|
.write = wil_compressed_rx_status_write,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
};
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
/*----------------*/
|
2014-07-14 02:49:41 -04:00
|
|
|
static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
|
|
|
|
struct dentry *dbg)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char name[32];
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
|
2016-04-26 07:41:41 -04:00
|
|
|
struct wil_blob_wrapper *wil_blob = &wil->blobs[i];
|
|
|
|
struct debugfs_blob_wrapper *blob = &wil_blob->blob;
|
2014-07-14 02:49:41 -04:00
|
|
|
const struct fw_map *map = &fw_mapping[i];
|
|
|
|
|
|
|
|
if (!map->name)
|
|
|
|
continue;
|
|
|
|
|
2016-04-26 07:41:41 -04:00
|
|
|
wil_blob->wil = wil;
|
2014-07-14 02:49:41 -04:00
|
|
|
blob->data = (void * __force)wil->csr + HOSTADDR(map->host);
|
|
|
|
blob->size = map->to - map->from;
|
|
|
|
snprintf(name, sizeof(name), "blob_%s", map->name);
|
2017-01-20 06:49:53 -05:00
|
|
|
wil_debugfs_create_ioblob(name, 0444, dbg, wil_blob);
|
2014-07-14 02:49:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-06 03:31:55 -04:00
|
|
|
/* misc files */
|
|
|
|
static const struct {
|
|
|
|
const char *name;
|
|
|
|
umode_t mode;
|
|
|
|
const struct file_operations *fops;
|
|
|
|
} dbg_files[] = {
|
2018-12-03 08:25:57 -05:00
|
|
|
{"mbox", 0444, &mbox_fops},
|
|
|
|
{"rings", 0444, &ring_fops},
|
|
|
|
{"stations", 0444, &sta_fops},
|
|
|
|
{"mids", 0444, &mids_fops},
|
|
|
|
{"desc", 0444, &txdesc_fops},
|
|
|
|
{"bf", 0444, &bf_fops},
|
|
|
|
{"mem_val", 0644, &memread_fops},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"rxon", 0244, &fops_rxon},
|
|
|
|
{"tx_mgmt", 0244, &fops_txmgmt},
|
|
|
|
{"wmi_send", 0244, &fops_wmi},
|
|
|
|
{"back", 0644, &fops_back},
|
|
|
|
{"pmccfg", 0644, &fops_pmccfg},
|
|
|
|
{"pmcdata", 0444, &fops_pmcdata},
|
2019-09-10 09:46:24 -04:00
|
|
|
{"pmcring", 0444, &fops_pmcring},
|
2018-12-03 08:25:57 -05:00
|
|
|
{"temp", 0444, &temp_fops},
|
|
|
|
{"link", 0444, &link_fops},
|
|
|
|
{"info", 0444, &info_fops},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"recovery", 0644, &fops_recovery},
|
|
|
|
{"led_cfg", 0644, &fops_led_cfg},
|
|
|
|
{"led_blink_time", 0644, &fops_led_blink_time},
|
2022-09-27 02:18:54 -04:00
|
|
|
{"fw_capabilities", 0444, &fw_capabilities_fops},
|
|
|
|
{"fw_version", 0444, &fw_version_fops},
|
2017-06-16 03:38:04 -04:00
|
|
|
{"suspend_stats", 0644, &fops_suspend_stats},
|
2018-06-29 09:28:23 -04:00
|
|
|
{"compressed_rx_status", 0644, &fops_compressed_rx_status},
|
2018-12-03 08:25:57 -05:00
|
|
|
{"srings", 0444, &srings_fops},
|
|
|
|
{"status_msg", 0444, &status_msg_fops},
|
|
|
|
{"rx_buff_mgmt", 0444, &rx_buff_mgmt_fops},
|
2018-07-24 03:44:24 -04:00
|
|
|
{"tx_latency", 0644, &fops_tx_latency},
|
2018-07-24 03:44:31 -04:00
|
|
|
{"link_stats", 0644, &fops_link_stats},
|
|
|
|
{"link_stats_global", 0644, &fops_link_stats_global},
|
2019-06-16 03:26:00 -04:00
|
|
|
{"rbufcap", 0244, &fops_rbufcap},
|
2014-08-06 03:31:55 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
|
|
|
|
struct dentry *dbg)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dbg_files); i++)
|
|
|
|
debugfs_create_file(dbg_files[i].name, dbg_files[i].mode, dbg,
|
|
|
|
wil, dbg_files[i].fops);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* interrupt control blocks */
|
|
|
|
static const struct {
|
|
|
|
const char *name;
|
|
|
|
u32 icr_off;
|
|
|
|
} dbg_icr[] = {
|
|
|
|
{"USER_ICR", HOSTADDR(RGF_USER_USER_ICR)},
|
|
|
|
{"DMA_EP_TX_ICR", HOSTADDR(RGF_DMA_EP_TX_ICR)},
|
|
|
|
{"DMA_EP_RX_ICR", HOSTADDR(RGF_DMA_EP_RX_ICR)},
|
|
|
|
{"DMA_EP_MISC_ICR", HOSTADDR(RGF_DMA_EP_MISC_ICR)},
|
|
|
|
};
|
|
|
|
|
|
|
|
static void wil6210_debugfs_init_isr(struct wil6210_priv *wil,
|
|
|
|
struct dentry *dbg)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dbg_icr); i++)
|
|
|
|
wil6210_debugfs_create_ISR(wil, dbg_icr[i].name, dbg,
|
|
|
|
dbg_icr[i].icr_off);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define WIL_FIELD(name, mode, type) { __stringify(name), mode, \
|
|
|
|
offsetof(struct wil6210_priv, name), type}
|
|
|
|
|
|
|
|
/* fields in struct wil6210_priv */
|
|
|
|
static const struct dbg_off dbg_wil_off[] = {
|
2017-01-20 06:49:53 -05:00
|
|
|
WIL_FIELD(status[0], 0644, doff_ulong),
|
|
|
|
WIL_FIELD(hw_version, 0444, doff_x32),
|
|
|
|
WIL_FIELD(recovery_count, 0444, doff_u32),
|
|
|
|
WIL_FIELD(discovery_mode, 0644, doff_u8),
|
|
|
|
WIL_FIELD(chip_revision, 0444, doff_u8),
|
|
|
|
WIL_FIELD(abft_len, 0644, doff_u8),
|
2017-06-16 03:38:04 -04:00
|
|
|
WIL_FIELD(wakeup_trigger, 0644, doff_u8),
|
2018-06-29 09:28:18 -04:00
|
|
|
WIL_FIELD(ring_idle_trsh, 0644, doff_u32),
|
2018-06-29 09:28:23 -04:00
|
|
|
WIL_FIELD(num_rx_status_rings, 0644, doff_u8),
|
|
|
|
WIL_FIELD(rx_status_ring_order, 0644, doff_u32),
|
|
|
|
WIL_FIELD(tx_status_ring_order, 0644, doff_u32),
|
|
|
|
WIL_FIELD(rx_buff_id_count, 0644, doff_u32),
|
2018-06-29 09:28:33 -04:00
|
|
|
WIL_FIELD(amsdu_en, 0644, doff_u8),
|
2014-08-06 03:31:55 -04:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dbg_off dbg_wil_regs[] = {
|
2017-01-20 06:49:53 -05:00
|
|
|
{"RGF_MAC_MTRL_COUNTER_0", 0444, HOSTADDR(RGF_MAC_MTRL_COUNTER_0),
|
2014-08-06 03:31:55 -04:00
|
|
|
doff_io32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"RGF_USER_USAGE_1", 0444, HOSTADDR(RGF_USER_USAGE_1), doff_io32},
|
2019-02-28 04:35:05 -05:00
|
|
|
{"RGF_USER_USAGE_2", 0444, HOSTADDR(RGF_USER_USAGE_2), doff_io32},
|
2014-08-06 03:31:55 -04:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
|
|
|
/* static parameters */
|
|
|
|
static const struct dbg_off dbg_statics[] = {
|
2017-01-20 06:49:53 -05:00
|
|
|
{"desc_index", 0644, (ulong)&dbg_txdesc_index, doff_u32},
|
2018-06-29 09:28:18 -04:00
|
|
|
{"ring_index", 0644, (ulong)&dbg_ring_index, doff_u32},
|
2017-01-20 06:49:53 -05:00
|
|
|
{"mem_addr", 0644, (ulong)&mem_addr, doff_u32},
|
|
|
|
{"led_polarity", 0644, (ulong)&led_polarity, doff_u8},
|
2018-06-29 09:28:37 -04:00
|
|
|
{"status_index", 0644, (ulong)&dbg_status_msg_index, doff_u32},
|
|
|
|
{"sring_index", 0644, (ulong)&dbg_sring_index, doff_u32},
|
2019-02-22 09:21:00 -05:00
|
|
|
{"drop_if_ring_full", 0644, (ulong)&drop_if_ring_full, doff_u8},
|
2014-08-06 03:31:55 -04:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
2017-11-14 08:25:33 -05:00
|
|
|
static const int dbg_off_count = 4 * (ARRAY_SIZE(isr_off) - 1) +
|
|
|
|
ARRAY_SIZE(dbg_wil_regs) - 1 +
|
|
|
|
ARRAY_SIZE(pseudo_isr_off) - 1 +
|
|
|
|
ARRAY_SIZE(lgc_itr_cnt_off) - 1 +
|
|
|
|
ARRAY_SIZE(tx_itr_cnt_off) - 1 +
|
|
|
|
ARRAY_SIZE(rx_itr_cnt_off) - 1;
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
int wil6210_debugfs_init(struct wil6210_priv *wil)
|
|
|
|
{
|
|
|
|
struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
|
|
|
|
wil_to_wiphy(wil)->debugfsdir);
|
|
|
|
if (IS_ERR_OR_NULL(dbg))
|
|
|
|
return -ENODEV;
|
|
|
|
|
2017-11-14 08:25:33 -05:00
|
|
|
wil->dbg_data.data_arr = kcalloc(dbg_off_count,
|
|
|
|
sizeof(struct wil_debugfs_iomem_data),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!wil->dbg_data.data_arr) {
|
|
|
|
debugfs_remove_recursive(dbg);
|
|
|
|
wil->debug = NULL;
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
wil->dbg_data.iomem_data_count = 0;
|
|
|
|
|
2015-04-30 09:25:09 -04:00
|
|
|
wil_pmc_init(wil);
|
|
|
|
|
2014-08-06 03:31:55 -04:00
|
|
|
wil6210_debugfs_init_files(wil, dbg);
|
|
|
|
wil6210_debugfs_init_isr(wil, dbg);
|
|
|
|
wil6210_debugfs_init_blobs(wil, dbg);
|
|
|
|
wil6210_debugfs_init_offset(wil, dbg, wil, dbg_wil_off);
|
|
|
|
wil6210_debugfs_init_offset(wil, dbg, (void * __force)wil->csr,
|
|
|
|
dbg_wil_regs);
|
|
|
|
wil6210_debugfs_init_offset(wil, dbg, NULL, dbg_statics);
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
wil6210_debugfs_create_pseudo_ISR(wil, dbg);
|
|
|
|
|
2014-08-06 03:31:55 -04:00
|
|
|
wil6210_debugfs_create_ITR_CNT(wil, dbg);
|
2012-12-20 16:13:19 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void wil6210_debugfs_remove(struct wil6210_priv *wil)
|
|
|
|
{
|
2018-07-24 03:44:24 -04:00
|
|
|
int i;
|
|
|
|
|
2012-12-20 16:13:19 -05:00
|
|
|
debugfs_remove_recursive(wil->debug);
|
|
|
|
wil->debug = NULL;
|
2015-04-30 09:25:09 -04:00
|
|
|
|
2017-11-14 08:25:33 -05:00
|
|
|
kfree(wil->dbg_data.data_arr);
|
2019-04-26 11:43:34 -04:00
|
|
|
for (i = 0; i < wil->max_assoc_sta; i++)
|
2018-07-24 03:44:24 -04:00
|
|
|
kfree(wil->sta[i].tx_latency_bins);
|
2017-11-14 08:25:33 -05:00
|
|
|
|
2015-04-30 09:25:09 -04:00
|
|
|
/* free pmc memory without sending command to fw, as it will
|
|
|
|
* be reset on the way down anyway
|
|
|
|
*/
|
|
|
|
wil_pmc_free(wil, false);
|
2012-12-20 16:13:19 -05:00
|
|
|
}
|