2488 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.4 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.5 */6 7#include <linux/module.h>8#include <linux/debugfs.h>9#include <linux/seq_file.h>10#include <linux/pci.h>11#include <linux/rtnetlink.h>12#include <linux/power_supply.h>13#include "wil6210.h"14#include "wmi.h"15#include "txrx.h"16#include "pmc.h"17 18/* Nasty hack. Better have per device instances */19static u32 mem_addr;20static u32 dbg_txdesc_index;21static u32 dbg_ring_index; /* 24+ for Rx, 0..23 for Tx */22static u32 dbg_status_msg_index;23/* 0..wil->num_rx_status_rings-1 for Rx, wil->tx_sring_idx for Tx */24static u32 dbg_sring_index;25 26enum dbg_off_type {27 doff_u32 = 0,28 doff_x32 = 1,29 doff_ulong = 2,30 doff_io32 = 3,31 doff_u8 = 432};33 34/* offset to "wil" */35struct dbg_off {36 const char *name;37 umode_t mode;38 ulong off;39 enum dbg_off_type type;40};41 42static void wil_print_desc_edma(struct seq_file *s, struct wil6210_priv *wil,43 struct wil_ring *ring,44 char _s, char _h, int idx)45{46 u8 num_of_descs;47 bool has_skb = false;48 49 if (ring->is_rx) {50 struct wil_rx_enhanced_desc *rx_d =51 (struct wil_rx_enhanced_desc *)52 &ring->va[idx].rx.enhanced;53 u16 buff_id = le16_to_cpu(rx_d->mac.buff_id);54 55 if (wil->rx_buff_mgmt.buff_arr &&56 wil_val_in_range(buff_id, 0, wil->rx_buff_mgmt.size))57 has_skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;58 seq_printf(s, "%c", (has_skb) ? _h : _s);59 } else {60 struct wil_tx_enhanced_desc *d =61 (struct wil_tx_enhanced_desc *)62 &ring->va[idx].tx.enhanced;63 64 num_of_descs = (u8)d->mac.d[2];65 has_skb = ring->ctx && ring->ctx[idx].skb;66 if (num_of_descs >= 1)67 seq_printf(s, "%c", has_skb ? _h : _s);68 else69 /* num_of_descs == 0, it's a frag in a list of descs */70 seq_printf(s, "%c", has_skb ? 'h' : _s);71 }72}73 74static void wil_print_ring(struct seq_file *s, struct wil6210_priv *wil,75 const char *name, struct wil_ring *ring,76 char _s, char _h)77{78 void __iomem *x;79 u32 v;80 81 seq_printf(s, "RING %s = {\n", name);82 seq_printf(s, " pa = %pad\n", &ring->pa);83 seq_printf(s, " va = 0x%p\n", ring->va);84 seq_printf(s, " size = %d\n", ring->size);85 if (wil->use_enhanced_dma_hw && ring->is_rx)86 seq_printf(s, " swtail = %u\n", *ring->edma_rx_swtail.va);87 else88 seq_printf(s, " swtail = %d\n", ring->swtail);89 seq_printf(s, " swhead = %d\n", ring->swhead);90 if (wil->use_enhanced_dma_hw) {91 int ring_id = ring->is_rx ?92 WIL_RX_DESC_RING_ID : ring - wil->ring_tx;93 /* SUBQ_CONS is a table of 32 entries, one for each Q pair.94 * lower 16bits are for even ring_id and upper 16bits are for95 * odd ring_id96 */97 x = wmi_addr(wil, RGF_DMA_SCM_SUBQ_CONS + 4 * (ring_id / 2));98 v = readl_relaxed(x);99 100 v = (ring_id % 2 ? (v >> 16) : (v & 0xffff));101 seq_printf(s, " hwhead = %u\n", v);102 }103 seq_printf(s, " hwtail = [0x%08x] -> ", ring->hwtail);104 x = wmi_addr(wil, ring->hwtail);105 if (x) {106 v = readl(x);107 seq_printf(s, "0x%08x = %d\n", v, v);108 } else {109 seq_puts(s, "???\n");110 }111 112 if (ring->va && (ring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) {113 uint i;114 115 for (i = 0; i < ring->size; i++) {116 if ((i % 128) == 0 && i != 0)117 seq_puts(s, "\n");118 if (wil->use_enhanced_dma_hw) {119 wil_print_desc_edma(s, wil, ring, _s, _h, i);120 } else {121 volatile struct vring_tx_desc *d =122 &ring->va[i].tx.legacy;123 seq_printf(s, "%c", (d->dma.status & BIT(0)) ?124 _s : (ring->ctx[i].skb ? _h : 'h'));125 }126 }127 seq_puts(s, "\n");128 }129 seq_puts(s, "}\n");130}131 132static int ring_show(struct seq_file *s, void *data)133{134 uint i;135 struct wil6210_priv *wil = s->private;136 137 wil_print_ring(s, wil, "rx", &wil->ring_rx, 'S', '_');138 139 for (i = 0; i < ARRAY_SIZE(wil->ring_tx); i++) {140 struct wil_ring *ring = &wil->ring_tx[i];141 struct wil_ring_tx_data *txdata = &wil->ring_tx_data[i];142 143 if (ring->va) {144 int cid = wil->ring2cid_tid[i][0];145 int tid = wil->ring2cid_tid[i][1];146 u32 swhead = ring->swhead;147 u32 swtail = ring->swtail;148 int used = (ring->size + swhead - swtail)149 % ring->size;150 int avail = ring->size - used - 1;151 char name[10];152 char sidle[10];153 /* performance monitoring */154 cycles_t now = get_cycles();155 uint64_t idle = txdata->idle * 100;156 uint64_t total = now - txdata->begin;157 158 if (total != 0) {159 do_div(idle, total);160 snprintf(sidle, sizeof(sidle), "%3d%%",161 (int)idle);162 } else {163 snprintf(sidle, sizeof(sidle), "N/A");164 }165 txdata->begin = now;166 txdata->idle = 0ULL;167 168 snprintf(name, sizeof(name), "tx_%2d", i);169 170 if (cid < wil->max_assoc_sta)171 seq_printf(s,172 "\n%pM CID %d TID %d 1x%s BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n",173 wil->sta[cid].addr, cid, tid,174 txdata->dot1x_open ? "+" : "-",175 txdata->agg_wsize,176 txdata->agg_timeout,177 txdata->agg_amsdu ? "+" : "-",178 used, avail, sidle);179 else180 seq_printf(s,181 "\nBroadcast 1x%s [%3d|%3d] idle %s\n",182 txdata->dot1x_open ? "+" : "-",183 used, avail, sidle);184 185 wil_print_ring(s, wil, name, ring, '_', 'H');186 }187 }188 189 return 0;190}191DEFINE_SHOW_ATTRIBUTE(ring);192 193static void wil_print_sring(struct seq_file *s, struct wil6210_priv *wil,194 struct wil_status_ring *sring)195{196 void __iomem *x;197 int sring_idx = sring - wil->srings;198 u32 v;199 200 seq_printf(s, "Status Ring %s [ %d ] = {\n",201 sring->is_rx ? "RX" : "TX", sring_idx);202 seq_printf(s, " pa = %pad\n", &sring->pa);203 seq_printf(s, " va = 0x%pK\n", sring->va);204 seq_printf(s, " size = %d\n", sring->size);205 seq_printf(s, " elem_size = %zu\n", sring->elem_size);206 seq_printf(s, " swhead = %d\n", sring->swhead);207 if (wil->use_enhanced_dma_hw) {208 /* COMPQ_PROD is a table of 32 entries, one for each Q pair.209 * lower 16bits are for even ring_id and upper 16bits are for210 * odd ring_id211 */212 x = wmi_addr(wil, RGF_DMA_SCM_COMPQ_PROD + 4 * (sring_idx / 2));213 v = readl_relaxed(x);214 215 v = (sring_idx % 2 ? (v >> 16) : (v & 0xffff));216 seq_printf(s, " hwhead = %u\n", v);217 }218 seq_printf(s, " hwtail = [0x%08x] -> ", sring->hwtail);219 x = wmi_addr(wil, sring->hwtail);220 if (x) {221 v = readl_relaxed(x);222 seq_printf(s, "0x%08x = %d\n", v, v);223 } else {224 seq_puts(s, "???\n");225 }226 seq_printf(s, " desc_rdy_pol = %d\n", sring->desc_rdy_pol);227 seq_printf(s, " invalid_buff_id_cnt = %d\n",228 sring->invalid_buff_id_cnt);229 230 if (sring->va && (sring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) {231 uint i;232 233 for (i = 0; i < sring->size; i++) {234 u32 *sdword_0 =235 (u32 *)(sring->va + (sring->elem_size * i));236 237 if ((i % 128) == 0 && i != 0)238 seq_puts(s, "\n");239 if (i == sring->swhead)240 seq_printf(s, "%c", (*sdword_0 & BIT(31)) ?241 'X' : 'x');242 else243 seq_printf(s, "%c", (*sdword_0 & BIT(31)) ?244 '1' : '0');245 }246 seq_puts(s, "\n");247 }248 seq_puts(s, "}\n");249}250 251static int srings_show(struct seq_file *s, void *data)252{253 struct wil6210_priv *wil = s->private;254 int i = 0;255 256 for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++)257 if (wil->srings[i].va)258 wil_print_sring(s, wil, &wil->srings[i]);259 260 return 0;261}262DEFINE_SHOW_ATTRIBUTE(srings);263 264static void wil_seq_hexdump(struct seq_file *s, void *p, int len,265 const char *prefix)266{267 seq_hex_dump(s, prefix, DUMP_PREFIX_NONE, 16, 1, p, len, false);268}269 270static void wil_print_mbox_ring(struct seq_file *s, const char *prefix,271 void __iomem *off)272{273 struct wil6210_priv *wil = s->private;274 struct wil6210_mbox_ring r;275 int rsize;276 uint i;277 278 wil_halp_vote(wil);279 280 if (wil_mem_access_lock(wil)) {281 wil_halp_unvote(wil);282 return;283 }284 285 wil_memcpy_fromio_32(&r, off, sizeof(r));286 wil_mbox_ring_le2cpus(&r);287 /*288 * we just read memory block from NIC. This memory may be289 * garbage. Check validity before using it.290 */291 rsize = r.size / sizeof(struct wil6210_mbox_ring_desc);292 293 seq_printf(s, "ring %s = {\n", prefix);294 seq_printf(s, " base = 0x%08x\n", r.base);295 seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize);296 seq_printf(s, " tail = 0x%08x\n", r.tail);297 seq_printf(s, " head = 0x%08x\n", r.head);298 seq_printf(s, " entry size = %d\n", r.entry_size);299 300 if (r.size % sizeof(struct wil6210_mbox_ring_desc)) {301 seq_printf(s, " ??? size is not multiple of %zd, garbage?\n",302 sizeof(struct wil6210_mbox_ring_desc));303 goto out;304 }305 306 if (!wmi_addr(wil, r.base) ||307 !wmi_addr(wil, r.tail) ||308 !wmi_addr(wil, r.head)) {309 seq_puts(s, " ??? pointers are garbage?\n");310 goto out;311 }312 313 for (i = 0; i < rsize; i++) {314 struct wil6210_mbox_ring_desc d;315 struct wil6210_mbox_hdr hdr;316 size_t delta = i * sizeof(d);317 void __iomem *x = wil->csr + HOSTADDR(r.base) + delta;318 319 wil_memcpy_fromio_32(&d, x, sizeof(d));320 321 seq_printf(s, " [%2x] %s %s%s 0x%08x", i,322 d.sync ? "F" : "E",323 (r.tail - r.base == delta) ? "t" : " ",324 (r.head - r.base == delta) ? "h" : " ",325 le32_to_cpu(d.addr));326 if (0 == wmi_read_hdr(wil, d.addr, &hdr)) {327 u16 len = le16_to_cpu(hdr.len);328 329 seq_printf(s, " -> %04x %04x %04x %02x\n",330 le16_to_cpu(hdr.seq), len,331 le16_to_cpu(hdr.type), hdr.flags);332 if (len <= MAX_MBOXITEM_SIZE) {333 unsigned char databuf[MAX_MBOXITEM_SIZE];334 void __iomem *src = wmi_buffer(wil, d.addr) +335 sizeof(struct wil6210_mbox_hdr);336 /*337 * No need to check @src for validity -338 * we already validated @d.addr while339 * reading header340 */341 wil_memcpy_fromio_32(databuf, src, len);342 wil_seq_hexdump(s, databuf, len, " : ");343 }344 } else {345 seq_puts(s, "\n");346 }347 }348 out:349 seq_puts(s, "}\n");350 wil_mem_access_unlock(wil);351 wil_halp_unvote(wil);352}353 354static int mbox_show(struct seq_file *s, void *data)355{356 struct wil6210_priv *wil = s->private;357 int ret;358 359 ret = wil_pm_runtime_get(wil);360 if (ret < 0)361 return ret;362 363 wil_print_mbox_ring(s, "tx", wil->csr + HOST_MBOX +364 offsetof(struct wil6210_mbox_ctl, tx));365 wil_print_mbox_ring(s, "rx", wil->csr + HOST_MBOX +366 offsetof(struct wil6210_mbox_ctl, rx));367 368 wil_pm_runtime_put(wil);369 370 return 0;371}372DEFINE_SHOW_ATTRIBUTE(mbox);373 374static int wil_debugfs_iomem_x32_set(void *data, u64 val)375{376 struct wil_debugfs_iomem_data *d = (struct377 wil_debugfs_iomem_data *)data;378 struct wil6210_priv *wil = d->wil;379 int ret;380 381 ret = wil_pm_runtime_get(wil);382 if (ret < 0)383 return ret;384 385 writel_relaxed(val, (void __iomem *)d->offset);386 387 wmb(); /* make sure write propagated to HW */388 389 wil_pm_runtime_put(wil);390 391 return 0;392}393 394static int wil_debugfs_iomem_x32_get(void *data, u64 *val)395{396 struct wil_debugfs_iomem_data *d = (struct397 wil_debugfs_iomem_data *)data;398 struct wil6210_priv *wil = d->wil;399 int ret;400 401 ret = wil_pm_runtime_get(wil);402 if (ret < 0)403 return ret;404 405 *val = readl((void __iomem *)d->offset);406 407 wil_pm_runtime_put(wil);408 409 return 0;410}411 412DEFINE_DEBUGFS_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get,413 wil_debugfs_iomem_x32_set, "0x%08llx\n");414 415static void wil_debugfs_create_iomem_x32(const char *name, umode_t mode,416 struct dentry *parent, void *value,417 struct wil6210_priv *wil)418{419 struct wil_debugfs_iomem_data *data = &wil->dbg_data.data_arr[420 wil->dbg_data.iomem_data_count];421 422 data->wil = wil;423 data->offset = value;424 425 debugfs_create_file_unsafe(name, mode, parent, data, &fops_iomem_x32);426 wil->dbg_data.iomem_data_count++;427}428 429static int wil_debugfs_ulong_set(void *data, u64 val)430{431 *(ulong *)data = val;432 return 0;433}434 435static int wil_debugfs_ulong_get(void *data, u64 *val)436{437 *val = *(ulong *)data;438 return 0;439}440 441DEFINE_DEBUGFS_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get,442 wil_debugfs_ulong_set, "0x%llx\n");443 444/**445 * wil6210_debugfs_init_offset - create set of debugfs files446 * @wil: driver's context, used for printing447 * @dbg: directory on the debugfs, where files will be created448 * @base: base address used in address calculation449 * @tbl: table with file descriptions. Should be terminated with empty element.450 *451 * Creates files accordingly to the @tbl.452 */453static void wil6210_debugfs_init_offset(struct wil6210_priv *wil,454 struct dentry *dbg, void *base,455 const struct dbg_off * const tbl)456{457 int i;458 459 for (i = 0; tbl[i].name; i++) {460 switch (tbl[i].type) {461 case doff_u32:462 debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg,463 base + tbl[i].off);464 break;465 case doff_x32:466 debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg,467 base + tbl[i].off);468 break;469 case doff_ulong:470 debugfs_create_file_unsafe(tbl[i].name, tbl[i].mode,471 dbg, base + tbl[i].off,472 &wil_fops_ulong);473 break;474 case doff_io32:475 wil_debugfs_create_iomem_x32(tbl[i].name, tbl[i].mode,476 dbg, base + tbl[i].off,477 wil);478 break;479 case doff_u8:480 debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg,481 base + tbl[i].off);482 break;483 }484 }485}486 487static const struct dbg_off isr_off[] = {488 {"ICC", 0644, offsetof(struct RGF_ICR, ICC), doff_io32},489 {"ICR", 0644, offsetof(struct RGF_ICR, ICR), doff_io32},490 {"ICM", 0644, offsetof(struct RGF_ICR, ICM), doff_io32},491 {"ICS", 0244, offsetof(struct RGF_ICR, ICS), doff_io32},492 {"IMV", 0644, offsetof(struct RGF_ICR, IMV), doff_io32},493 {"IMS", 0244, offsetof(struct RGF_ICR, IMS), doff_io32},494 {"IMC", 0244, offsetof(struct RGF_ICR, IMC), doff_io32},495 {},496};497 498static void wil6210_debugfs_create_ISR(struct wil6210_priv *wil,499 const char *name, struct dentry *parent,500 u32 off)501{502 struct dentry *d = debugfs_create_dir(name, parent);503 504 wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr + off,505 isr_off);506}507 508static const struct dbg_off pseudo_isr_off[] = {509 {"CAUSE", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE), doff_io32},510 {"MASK_SW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW), doff_io32},511 {"MASK_FW", 0444, HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW), doff_io32},512 {},513};514 515static void wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil,516 struct dentry *parent)517{518 struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent);519 520 wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,521 pseudo_isr_off);522}523 524static const struct dbg_off lgc_itr_cnt_off[] = {525 {"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_CNT_TRSH), doff_io32},526 {"DATA", 0644, HOSTADDR(RGF_DMA_ITR_CNT_DATA), doff_io32},527 {"CTL", 0644, HOSTADDR(RGF_DMA_ITR_CNT_CRL), doff_io32},528 {},529};530 531static const struct dbg_off tx_itr_cnt_off[] = {532 {"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_TRSH),533 doff_io32},534 {"DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_DATA),535 doff_io32},536 {"CTL", 0644, HOSTADDR(RGF_DMA_ITR_TX_CNT_CTL),537 doff_io32},538 {"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_TRSH),539 doff_io32},540 {"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_DATA),541 doff_io32},542 {"IDL_CTL", 0644, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_CTL),543 doff_io32},544 {},545};546 547static const struct dbg_off rx_itr_cnt_off[] = {548 {"TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_TRSH),549 doff_io32},550 {"DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_DATA),551 doff_io32},552 {"CTL", 0644, HOSTADDR(RGF_DMA_ITR_RX_CNT_CTL),553 doff_io32},554 {"IDL_TRSH", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_TRSH),555 doff_io32},556 {"IDL_DATA", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_DATA),557 doff_io32},558 {"IDL_CTL", 0644, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_CTL),559 doff_io32},560 {},561};562 563static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,564 struct dentry *parent)565{566 struct dentry *d, *dtx, *drx;567 568 d = debugfs_create_dir("ITR_CNT", parent);569 570 dtx = debugfs_create_dir("TX", d);571 drx = debugfs_create_dir("RX", d);572 573 wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr,574 lgc_itr_cnt_off);575 576 wil6210_debugfs_init_offset(wil, dtx, (void * __force)wil->csr,577 tx_itr_cnt_off);578 579 wil6210_debugfs_init_offset(wil, drx, (void * __force)wil->csr,580 rx_itr_cnt_off);581 return 0;582}583 584static int memread_show(struct seq_file *s, void *data)585{586 struct wil6210_priv *wil = s->private;587 void __iomem *a;588 int ret;589 590 ret = wil_pm_runtime_get(wil);591 if (ret < 0)592 return ret;593 594 ret = wil_mem_access_lock(wil);595 if (ret) {596 wil_pm_runtime_put(wil);597 return ret;598 }599 600 a = wmi_buffer(wil, cpu_to_le32(mem_addr));601 602 if (a)603 seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, readl(a));604 else605 seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);606 607 wil_mem_access_unlock(wil);608 wil_pm_runtime_put(wil);609 610 return 0;611}612DEFINE_SHOW_ATTRIBUTE(memread);613 614static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,615 size_t count, loff_t *ppos)616{617 enum { max_count = 4096 };618 struct wil_blob_wrapper *wil_blob = file->private_data;619 struct wil6210_priv *wil = wil_blob->wil;620 loff_t aligned_pos, pos = *ppos;621 size_t available = wil_blob->blob.size;622 void *buf;623 size_t unaligned_bytes, aligned_count, ret;624 int rc;625 626 if (pos < 0)627 return -EINVAL;628 629 if (pos >= available || !count)630 return 0;631 632 if (count > available - pos)633 count = available - pos;634 if (count > max_count)635 count = max_count;636 637 /* set pos to 4 bytes aligned */638 unaligned_bytes = pos % 4;639 aligned_pos = pos - unaligned_bytes;640 aligned_count = count + unaligned_bytes;641 642 buf = kmalloc(aligned_count, GFP_KERNEL);643 if (!buf)644 return -ENOMEM;645 646 rc = wil_pm_runtime_get(wil);647 if (rc < 0) {648 kfree(buf);649 return rc;650 }651 652 rc = wil_mem_access_lock(wil);653 if (rc) {654 kfree(buf);655 wil_pm_runtime_put(wil);656 return rc;657 }658 659 wil_memcpy_fromio_32(buf, (const void __iomem *)660 wil_blob->blob.data + aligned_pos, aligned_count);661 662 ret = copy_to_user(user_buf, buf + unaligned_bytes, count);663 664 wil_mem_access_unlock(wil);665 wil_pm_runtime_put(wil);666 667 kfree(buf);668 if (ret == count)669 return -EFAULT;670 671 count -= ret;672 *ppos = pos + count;673 674 return count;675}676 677static const struct file_operations fops_ioblob = {678 .read = wil_read_file_ioblob,679 .open = simple_open,680 .llseek = default_llseek,681};682 683static684struct dentry *wil_debugfs_create_ioblob(const char *name,685 umode_t mode,686 struct dentry *parent,687 struct wil_blob_wrapper *wil_blob)688{689 return debugfs_create_file(name, mode, parent, wil_blob, &fops_ioblob);690}691 692/*---write channel 1..4 to rxon for it, 0 to rxoff---*/693static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,694 size_t len, loff_t *ppos)695{696 struct wil6210_priv *wil = file->private_data;697 int rc;698 long channel;699 bool on;700 701 char *kbuf = memdup_user_nul(buf, len);702 703 if (IS_ERR(kbuf))704 return PTR_ERR(kbuf);705 rc = kstrtol(kbuf, 0, &channel);706 kfree(kbuf);707 if (rc)708 return rc;709 710 if ((channel < 0) || (channel > 4)) {711 wil_err(wil, "Invalid channel %ld\n", channel);712 return -EINVAL;713 }714 on = !!channel;715 716 if (on) {717 rc = wmi_set_channel(wil, (int)channel);718 if (rc)719 return rc;720 }721 722 rc = wmi_rxon(wil, on);723 if (rc)724 return rc;725 726 return len;727}728 729static const struct file_operations fops_rxon = {730 .write = wil_write_file_rxon,731 .open = simple_open,732};733 734static ssize_t wil_write_file_rbufcap(struct file *file,735 const char __user *buf,736 size_t count, loff_t *ppos)737{738 struct wil6210_priv *wil = file->private_data;739 int val;740 int rc;741 742 rc = kstrtoint_from_user(buf, count, 0, &val);743 if (rc) {744 wil_err(wil, "Invalid argument\n");745 return rc;746 }747 /* input value: negative to disable, 0 to use system default,748 * 1..ring size to set descriptor threshold749 */750 wil_info(wil, "%s RBUFCAP, descriptors threshold - %d\n",751 val < 0 ? "Disabling" : "Enabling", val);752 753 if (!wil->ring_rx.va || val > wil->ring_rx.size) {754 wil_err(wil, "Invalid descriptors threshold, %d\n", val);755 return -EINVAL;756 }757 758 rc = wmi_rbufcap_cfg(wil, val < 0 ? 0 : 1, val < 0 ? 0 : val);759 if (rc) {760 wil_err(wil, "RBUFCAP config failed: %d\n", rc);761 return rc;762 }763 764 return count;765}766 767static const struct file_operations fops_rbufcap = {768 .write = wil_write_file_rbufcap,769 .open = simple_open,770};771 772/* block ack control, write:773 * - "add <ringid> <agg_size> <timeout>" to trigger ADDBA774 * - "del_tx <ringid> <reason>" to trigger DELBA for Tx side775 * - "del_rx <CID> <TID> <reason>" to trigger DELBA for Rx side776 */777static ssize_t wil_write_back(struct file *file, const char __user *buf,778 size_t len, loff_t *ppos)779{780 struct wil6210_priv *wil = file->private_data;781 int rc;782 char *kbuf = kmalloc(len + 1, GFP_KERNEL);783 char cmd[9];784 int p1, p2, p3;785 786 if (!kbuf)787 return -ENOMEM;788 789 rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);790 if (rc != len) {791 kfree(kbuf);792 return rc >= 0 ? -EIO : rc;793 }794 795 kbuf[len] = '\0';796 rc = sscanf(kbuf, "%8s %d %d %d", cmd, &p1, &p2, &p3);797 kfree(kbuf);798 799 if (rc < 0)800 return rc;801 if (rc < 2)802 return -EINVAL;803 804 if ((strcmp(cmd, "add") == 0) ||805 (strcmp(cmd, "del_tx") == 0)) {806 struct wil_ring_tx_data *txdata;807 808 if (p1 < 0 || p1 >= WIL6210_MAX_TX_RINGS) {809 wil_err(wil, "BACK: invalid ring id %d\n", p1);810 return -EINVAL;811 }812 txdata = &wil->ring_tx_data[p1];813 if (strcmp(cmd, "add") == 0) {814 if (rc < 3) {815 wil_err(wil, "BACK: add require at least 2 params\n");816 return -EINVAL;817 }818 if (rc < 4)819 p3 = 0;820 wmi_addba(wil, txdata->mid, p1, p2, p3);821 } else {822 if (rc < 3)823 p2 = WLAN_REASON_QSTA_LEAVE_QBSS;824 wmi_delba_tx(wil, txdata->mid, p1, p2);825 }826 } else if (strcmp(cmd, "del_rx") == 0) {827 struct wil_sta_info *sta;828 829 if (rc < 3) {830 wil_err(wil,831 "BACK: del_rx require at least 2 params\n");832 return -EINVAL;833 }834 if (p1 < 0 || p1 >= wil->max_assoc_sta) {835 wil_err(wil, "BACK: invalid CID %d\n", p1);836 return -EINVAL;837 }838 if (rc < 4)839 p3 = WLAN_REASON_QSTA_LEAVE_QBSS;840 sta = &wil->sta[p1];841 wmi_delba_rx(wil, sta->mid, p1, p2, p3);842 } else {843 wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd);844 return -EINVAL;845 }846 847 return len;848}849 850static ssize_t wil_read_back(struct file *file, char __user *user_buf,851 size_t count, loff_t *ppos)852{853 static const char text[] = "block ack control, write:\n"854 " - \"add <ringid> <agg_size> <timeout>\" to trigger ADDBA\n"855 "If missing, <timeout> defaults to 0\n"856 " - \"del_tx <ringid> <reason>\" to trigger DELBA for Tx side\n"857 " - \"del_rx <CID> <TID> <reason>\" to trigger DELBA for Rx side\n"858 "If missing, <reason> set to \"STA_LEAVING\" (36)\n";859 860 return simple_read_from_buffer(user_buf, count, ppos, text,861 sizeof(text));862}863 864static const struct file_operations fops_back = {865 .read = wil_read_back,866 .write = wil_write_back,867 .open = simple_open,868};869 870/* pmc control, write:871 * - "alloc <num descriptors> <descriptor_size>" to allocate PMC872 * - "free" to release memory allocated for PMC873 */874static ssize_t wil_write_pmccfg(struct file *file, const char __user *buf,875 size_t len, loff_t *ppos)876{877 struct wil6210_priv *wil = file->private_data;878 int rc;879 char *kbuf = kmalloc(len + 1, GFP_KERNEL);880 char cmd[9];881 int num_descs, desc_size;882 883 if (!kbuf)884 return -ENOMEM;885 886 rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);887 if (rc != len) {888 kfree(kbuf);889 return rc >= 0 ? -EIO : rc;890 }891 892 kbuf[len] = '\0';893 rc = sscanf(kbuf, "%8s %d %d", cmd, &num_descs, &desc_size);894 kfree(kbuf);895 896 if (rc < 0)897 return rc;898 899 if (rc < 1) {900 wil_err(wil, "pmccfg: no params given\n");901 return -EINVAL;902 }903 904 if (0 == strcmp(cmd, "alloc")) {905 if (rc != 3) {906 wil_err(wil, "pmccfg: alloc requires 2 params\n");907 return -EINVAL;908 }909 wil_pmc_alloc(wil, num_descs, desc_size);910 } else if (0 == strcmp(cmd, "free")) {911 if (rc != 1) {912 wil_err(wil, "pmccfg: free does not have any params\n");913 return -EINVAL;914 }915 wil_pmc_free(wil, true);916 } else {917 wil_err(wil, "pmccfg: Unrecognized command \"%s\"\n", cmd);918 return -EINVAL;919 }920 921 return len;922}923 924static ssize_t wil_read_pmccfg(struct file *file, char __user *user_buf,925 size_t count, loff_t *ppos)926{927 struct wil6210_priv *wil = file->private_data;928 char text[256];929 char help[] = "pmc control, write:\n"930 " - \"alloc <num descriptors> <descriptor_size>\" to allocate pmc\n"931 " - \"free\" to free memory allocated for pmc\n";932 933 snprintf(text, sizeof(text), "Last command status: %d\n\n%s",934 wil_pmc_last_cmd_status(wil), help);935 936 return simple_read_from_buffer(user_buf, count, ppos, text,937 strlen(text) + 1);938}939 940static const struct file_operations fops_pmccfg = {941 .read = wil_read_pmccfg,942 .write = wil_write_pmccfg,943 .open = simple_open,944};945 946static const struct file_operations fops_pmcdata = {947 .open = simple_open,948 .read = wil_pmc_read,949 .llseek = wil_pmc_llseek,950};951 952static int wil_pmcring_seq_open(struct inode *inode, struct file *file)953{954 return single_open(file, wil_pmcring_read, inode->i_private);955}956 957static const struct file_operations fops_pmcring = {958 .open = wil_pmcring_seq_open,959 .release = single_release,960 .read = seq_read,961 .llseek = seq_lseek,962};963 964/*---tx_mgmt---*/965/* Write mgmt frame to this file to send it */966static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,967 size_t len, loff_t *ppos)968{969 struct wil6210_priv *wil = file->private_data;970 struct wiphy *wiphy = wil_to_wiphy(wil);971 struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr;972 struct cfg80211_mgmt_tx_params params;973 int rc;974 void *frame;975 976 memset(¶ms, 0, sizeof(params));977 978 if (!len)979 return -EINVAL;980 981 frame = memdup_user(buf, len);982 if (IS_ERR(frame))983 return PTR_ERR(frame);984 985 params.buf = frame;986 params.len = len;987 988 rc = wil_cfg80211_mgmt_tx(wiphy, wdev, ¶ms, NULL);989 990 kfree(frame);991 wil_info(wil, "-> %d\n", rc);992 993 return len;994}995 996static const struct file_operations fops_txmgmt = {997 .write = wil_write_file_txmgmt,998 .open = simple_open,999};1000 1001/* Write WMI command (w/o mbox header) to this file to send it1002 * WMI starts from wil6210_mbox_hdr_wmi header1003 */1004static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,1005 size_t len, loff_t *ppos)1006{1007 struct wil6210_priv *wil = file->private_data;1008 struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);1009 struct wmi_cmd_hdr *wmi;1010 void *cmd;1011 int cmdlen = len - sizeof(struct wmi_cmd_hdr);1012 u16 cmdid;1013 int rc1;1014 1015 if (cmdlen < 0 || *ppos != 0)1016 return -EINVAL;1017 1018 wmi = memdup_user(buf, len);1019 if (IS_ERR(wmi))1020 return PTR_ERR(wmi);1021 1022 cmd = (cmdlen > 0) ? &wmi[1] : NULL;1023 cmdid = le16_to_cpu(wmi->command_id);1024 1025 rc1 = wmi_send(wil, cmdid, vif->mid, cmd, cmdlen);1026 kfree(wmi);1027 1028 wil_info(wil, "0x%04x[%d] -> %d\n", cmdid, cmdlen, rc1);1029 1030 return len;1031}1032 1033static const struct file_operations fops_wmi = {1034 .write = wil_write_file_wmi,1035 .open = simple_open,1036};1037 1038static void wil_seq_print_skb(struct seq_file *s, struct sk_buff *skb)1039{1040 int i = 0;1041 int len = skb_headlen(skb);1042 void *p = skb->data;1043 int nr_frags = skb_shinfo(skb)->nr_frags;1044 1045 seq_printf(s, " len = %d\n", len);1046 wil_seq_hexdump(s, p, len, " : ");1047 1048 if (nr_frags) {1049 seq_printf(s, " nr_frags = %d\n", nr_frags);1050 for (i = 0; i < nr_frags; i++) {1051 const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];1052 1053 len = skb_frag_size(frag);1054 p = skb_frag_address_safe(frag);1055 seq_printf(s, " [%2d] : len = %d\n", i, len);1056 wil_seq_hexdump(s, p, len, " : ");1057 }1058 }1059}1060 1061/*---------Tx/Rx descriptor------------*/1062static int txdesc_show(struct seq_file *s, void *data)1063{1064 struct wil6210_priv *wil = s->private;1065 struct wil_ring *ring;1066 bool tx;1067 int ring_idx = dbg_ring_index;1068 int txdesc_idx = dbg_txdesc_index;1069 volatile struct vring_tx_desc *d;1070 volatile u32 *u;1071 struct sk_buff *skb;1072 1073 if (wil->use_enhanced_dma_hw) {1074 /* RX ring index == 0 */1075 if (ring_idx >= WIL6210_MAX_TX_RINGS) {1076 seq_printf(s, "invalid ring index %d\n", ring_idx);1077 return 0;1078 }1079 tx = ring_idx > 0; /* desc ring 0 is reserved for RX */1080 } else {1081 /* RX ring index == WIL6210_MAX_TX_RINGS */1082 if (ring_idx > WIL6210_MAX_TX_RINGS) {1083 seq_printf(s, "invalid ring index %d\n", ring_idx);1084 return 0;1085 }1086 tx = (ring_idx < WIL6210_MAX_TX_RINGS);1087 }1088 1089 ring = tx ? &wil->ring_tx[ring_idx] : &wil->ring_rx;1090 1091 if (!ring->va) {1092 if (tx)1093 seq_printf(s, "No Tx[%2d] RING\n", ring_idx);1094 else1095 seq_puts(s, "No Rx RING\n");1096 return 0;1097 }1098 1099 if (txdesc_idx >= ring->size) {1100 if (tx)1101 seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n",1102 ring_idx, txdesc_idx, ring->size);1103 else1104 seq_printf(s, "RxDesc index (%d) >= size (%d)\n",1105 txdesc_idx, ring->size);1106 return 0;1107 }1108 1109 /* use struct vring_tx_desc for Rx as well,1110 * only field used, .dma.length, is the same1111 */1112 d = &ring->va[txdesc_idx].tx.legacy;1113 u = (volatile u32 *)d;1114 skb = NULL;1115 1116 if (wil->use_enhanced_dma_hw) {1117 if (tx) {1118 skb = ring->ctx ? ring->ctx[txdesc_idx].skb : NULL;1119 } else if (wil->rx_buff_mgmt.buff_arr) {1120 struct wil_rx_enhanced_desc *rx_d =1121 (struct wil_rx_enhanced_desc *)1122 &ring->va[txdesc_idx].rx.enhanced;1123 u16 buff_id = le16_to_cpu(rx_d->mac.buff_id);1124 1125 if (!wil_val_in_range(buff_id, 0,1126 wil->rx_buff_mgmt.size))1127 seq_printf(s, "invalid buff_id %d\n", buff_id);1128 else1129 skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb;1130 }1131 } else {1132 skb = ring->ctx[txdesc_idx].skb;1133 }1134 if (tx)1135 seq_printf(s, "Tx[%2d][%3d] = {\n", ring_idx,1136 txdesc_idx);1137 else1138 seq_printf(s, "Rx[%3d] = {\n", txdesc_idx);1139 seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",1140 u[0], u[1], u[2], u[3]);1141 seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",1142 u[4], u[5], u[6], u[7]);1143 seq_printf(s, " SKB = 0x%p\n", skb);1144 1145 if (skb) {1146 skb_get(skb);1147 wil_seq_print_skb(s, skb);1148 kfree_skb(skb);1149 }1150 seq_puts(s, "}\n");1151 1152 return 0;1153}1154DEFINE_SHOW_ATTRIBUTE(txdesc);1155 1156/*---------Tx/Rx status message------------*/1157static int status_msg_show(struct seq_file *s, void *data)1158{1159 struct wil6210_priv *wil = s->private;1160 int sring_idx = dbg_sring_index;1161 struct wil_status_ring *sring;1162 bool tx;1163 u32 status_msg_idx = dbg_status_msg_index;1164 u32 *u;1165 1166 if (sring_idx >= WIL6210_MAX_STATUS_RINGS) {1167 seq_printf(s, "invalid status ring index %d\n", sring_idx);1168 return 0;1169 }1170 1171 sring = &wil->srings[sring_idx];1172 tx = !sring->is_rx;1173 1174 if (!sring->va) {1175 seq_printf(s, "No %cX status ring\n", tx ? 'T' : 'R');1176 return 0;1177 }1178 1179 if (status_msg_idx >= sring->size) {1180 seq_printf(s, "%cxDesc index (%d) >= size (%d)\n",1181 tx ? 'T' : 'R', status_msg_idx, sring->size);1182 return 0;1183 }1184 1185 u = sring->va + (sring->elem_size * status_msg_idx);1186 1187 seq_printf(s, "%cx[%d][%3d] = {\n",1188 tx ? 'T' : 'R', sring_idx, status_msg_idx);1189 1190 seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n",1191 u[0], u[1], u[2], u[3]);1192 if (!tx && !wil->use_compressed_rx_status)1193 seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x\n",1194 u[4], u[5], u[6], u[7]);1195 1196 seq_puts(s, "}\n");1197 1198 return 0;1199}1200DEFINE_SHOW_ATTRIBUTE(status_msg);1201 1202static int wil_print_rx_buff(struct seq_file *s, struct list_head *lh)1203{1204 struct wil_rx_buff *it;1205 int i = 0;1206 1207 list_for_each_entry(it, lh, list) {1208 if ((i % 16) == 0 && i != 0)1209 seq_puts(s, "\n ");1210 seq_printf(s, "[%4d] ", it->id);1211 i++;1212 }1213 seq_printf(s, "\nNumber of buffers: %u\n", i);1214 1215 return i;1216}1217 1218static int rx_buff_mgmt_show(struct seq_file *s, void *data)1219{1220 struct wil6210_priv *wil = s->private;1221 struct wil_rx_buff_mgmt *rbm = &wil->rx_buff_mgmt;1222 int num_active;1223 int num_free;1224 1225 if (!rbm->buff_arr)1226 return -EINVAL;1227 1228 seq_printf(s, " size = %zu\n", rbm->size);1229 seq_printf(s, " free_list_empty_cnt = %lu\n",1230 rbm->free_list_empty_cnt);1231 1232 /* Print active list */1233 seq_puts(s, " Active list:\n");1234 num_active = wil_print_rx_buff(s, &rbm->active);1235 seq_puts(s, "\n Free list:\n");1236 num_free = wil_print_rx_buff(s, &rbm->free);1237 1238 seq_printf(s, " Total number of buffers: %u\n",1239 num_active + num_free);1240 1241 return 0;1242}1243DEFINE_SHOW_ATTRIBUTE(rx_buff_mgmt);1244 1245/*---------beamforming------------*/1246static char *wil_bfstatus_str(u32 status)1247{1248 switch (status) {1249 case 0:1250 return "Failed";1251 case 1:1252 return "OK";1253 case 2:1254 return "Retrying";1255 default:1256 return "??";1257 }1258}1259 1260static bool is_all_zeros(void * const x_, size_t sz)1261{1262 /* if reply is all-0, ignore this CID */1263 u32 *x = x_;1264 int n;1265 1266 for (n = 0; n < sz / sizeof(*x); n++)1267 if (x[n])1268 return false;1269 1270 return true;1271}1272 1273static int bf_show(struct seq_file *s, void *data)1274{1275 int rc;1276 int i;1277 struct wil6210_priv *wil = s->private;1278 struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);1279 struct wmi_notify_req_cmd cmd = {1280 .interval_usec = 0,1281 };1282 struct {1283 struct wmi_cmd_hdr wmi;1284 struct wmi_notify_req_done_event evt;1285 } __packed reply;1286 1287 memset(&reply, 0, sizeof(reply));1288 1289 for (i = 0; i < wil->max_assoc_sta; i++) {1290 u32 status;1291 u8 bf_mcs;1292 1293 cmd.cid = i;1294 rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid,1295 &cmd, sizeof(cmd),1296 WMI_NOTIFY_REQ_DONE_EVENTID, &reply,1297 sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS);1298 /* if reply is all-0, ignore this CID */1299 if (rc || is_all_zeros(&reply.evt, sizeof(reply.evt)))1300 continue;1301 1302 status = le32_to_cpu(reply.evt.status);1303 bf_mcs = le16_to_cpu(reply.evt.bf_mcs);1304 seq_printf(s, "CID %d {\n"1305 " TSF = 0x%016llx\n"1306 " TxMCS = %s TxTpt = %4d\n"1307 " SQI = %4d\n"1308 " RSSI = %4d\n"1309 " Status = 0x%08x %s\n"1310 " Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n"1311 " Goodput(rx:tx) %4d:%4d\n"1312 "}\n",1313 i,1314 le64_to_cpu(reply.evt.tsf),1315 WIL_EXTENDED_MCS_CHECK(bf_mcs),1316 le32_to_cpu(reply.evt.tx_tpt),1317 reply.evt.sqi,1318 reply.evt.rssi,1319 status, wil_bfstatus_str(status),1320 le16_to_cpu(reply.evt.my_rx_sector),1321 le16_to_cpu(reply.evt.my_tx_sector),1322 le16_to_cpu(reply.evt.other_rx_sector),1323 le16_to_cpu(reply.evt.other_tx_sector),1324 le32_to_cpu(reply.evt.rx_goodput),1325 le32_to_cpu(reply.evt.tx_goodput));1326 }1327 return 0;1328}1329DEFINE_SHOW_ATTRIBUTE(bf);1330 1331/*---------temp------------*/1332static void print_temp(struct seq_file *s, const char *prefix, s32 t)1333{1334 switch (t) {1335 case 0:1336 case WMI_INVALID_TEMPERATURE:1337 seq_printf(s, "%s N/A\n", prefix);1338 break;1339 default:1340 seq_printf(s, "%s %s%d.%03d\n", prefix, (t < 0 ? "-" : ""),1341 abs(t / 1000), abs(t % 1000));1342 break;1343 }1344}1345 1346static int temp_show(struct seq_file *s, void *data)1347{1348 struct wil6210_priv *wil = s->private;1349 int rc, i;1350 1351 if (test_bit(WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF,1352 wil->fw_capabilities)) {1353 struct wmi_temp_sense_all_done_event sense_all_evt;1354 1355 wil_dbg_misc(wil,1356 "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is supported");1357 rc = wmi_get_all_temperatures(wil, &sense_all_evt);1358 if (rc) {1359 seq_puts(s, "Failed\n");1360 return 0;1361 }1362 print_temp(s, "T_mac =",1363 le32_to_cpu(sense_all_evt.baseband_t1000));1364 seq_printf(s, "Connected RFs [0x%08x]\n",1365 sense_all_evt.rf_bitmap);1366 for (i = 0; i < WMI_MAX_XIF_PORTS_NUM; i++) {1367 seq_printf(s, "RF[%d] = ", i);1368 print_temp(s, "",1369 le32_to_cpu(sense_all_evt.rf_t1000[i]));1370 }1371 } else {1372 s32 t_m, t_r;1373 1374 wil_dbg_misc(wil,1375 "WMI_FW_CAPABILITY_TEMPERATURE_ALL_RF is not supported");1376 rc = wmi_get_temperature(wil, &t_m, &t_r);1377 if (rc) {1378 seq_puts(s, "Failed\n");1379 return 0;1380 }1381 print_temp(s, "T_mac =", t_m);1382 print_temp(s, "T_radio =", t_r);1383 }1384 return 0;1385}1386DEFINE_SHOW_ATTRIBUTE(temp);1387 1388/*---------link------------*/1389static int link_show(struct seq_file *s, void *data)1390{1391 struct wil6210_priv *wil = s->private;1392 struct station_info *sinfo;1393 int i, rc = 0;1394 1395 sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);1396 if (!sinfo)1397 return -ENOMEM;1398 1399 for (i = 0; i < wil->max_assoc_sta; i++) {1400 struct wil_sta_info *p = &wil->sta[i];1401 char *status = "unknown";1402 struct wil6210_vif *vif;1403 u8 mid;1404 1405 switch (p->status) {1406 case wil_sta_unused:1407 status = "unused ";1408 break;1409 case wil_sta_conn_pending:1410 status = "pending ";1411 break;1412 case wil_sta_connected:1413 status = "connected";1414 break;1415 }1416 mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;1417 seq_printf(s, "[%d][MID %d] %pM %s\n",1418 i, mid, p->addr, status);1419 1420 if (p->status != wil_sta_connected)1421 continue;1422 1423 vif = (mid < GET_MAX_VIFS(wil)) ? wil->vifs[mid] : NULL;1424 if (vif) {1425 rc = wil_cid_fill_sinfo(vif, i, sinfo);1426 if (rc)1427 goto out;1428 1429 seq_printf(s, " Tx_mcs = %s\n",1430 WIL_EXTENDED_MCS_CHECK(sinfo->txrate.mcs));1431 seq_printf(s, " Rx_mcs = %s\n",1432 WIL_EXTENDED_MCS_CHECK(sinfo->rxrate.mcs));1433 seq_printf(s, " SQ = %d\n", sinfo->signal);1434 } else {1435 seq_puts(s, " INVALID MID\n");1436 }1437 }1438 1439out:1440 kfree(sinfo);1441 return rc;1442}1443DEFINE_SHOW_ATTRIBUTE(link);1444 1445/*---------info------------*/1446static int info_show(struct seq_file *s, void *data)1447{1448 struct wil6210_priv *wil = s->private;1449 struct net_device *ndev = wil->main_ndev;1450 int is_ac = power_supply_is_system_supplied();1451 int rx = atomic_xchg(&wil->isr_count_rx, 0);1452 int tx = atomic_xchg(&wil->isr_count_tx, 0);1453 static ulong rxf_old, txf_old;1454 ulong rxf = ndev->stats.rx_packets;1455 ulong txf = ndev->stats.tx_packets;1456 unsigned int i;1457 1458 /* >0 : AC; 0 : battery; <0 : error */1459 seq_printf(s, "AC powered : %d\n", is_ac);1460 seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old);1461 seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old);1462 rxf_old = rxf;1463 txf_old = txf;1464 1465#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \1466 " " __stringify(x) : ""1467 1468 for (i = 0; i < ndev->num_tx_queues; i++) {1469 struct netdev_queue *txq = netdev_get_tx_queue(ndev, i);1470 unsigned long state = txq->state;1471 1472 seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state,1473 CHECK_QSTATE(DRV_XOFF),1474 CHECK_QSTATE(STACK_XOFF),1475 CHECK_QSTATE(FROZEN)1476 );1477 }1478#undef CHECK_QSTATE1479 return 0;1480}1481DEFINE_SHOW_ATTRIBUTE(info);1482 1483/*---------recovery------------*/1484/* mode = [manual|auto]1485 * state = [idle|pending|running]1486 */1487static ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf,1488 size_t count, loff_t *ppos)1489{1490 struct wil6210_priv *wil = file->private_data;1491 char buf[80];1492 int n;1493 static const char * const sstate[] = {"idle", "pending", "running"};1494 1495 n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n",1496 no_fw_recovery ? "manual" : "auto",1497 sstate[wil->recovery_state]);1498 1499 n = min_t(int, n, sizeof(buf));1500 1501 return simple_read_from_buffer(user_buf, count, ppos,1502 buf, n);1503}1504 1505static ssize_t wil_write_file_recovery(struct file *file,1506 const char __user *buf_,1507 size_t count, loff_t *ppos)1508{1509 struct wil6210_priv *wil = file->private_data;1510 static const char run_command[] = "run";1511 char buf[sizeof(run_command) + 1]; /* to detect "runx" */1512 ssize_t rc;1513 1514 if (wil->recovery_state != fw_recovery_pending) {1515 wil_err(wil, "No recovery pending\n");1516 return -EINVAL;1517 }1518 1519 if (*ppos != 0) {1520 wil_err(wil, "Offset [%d]\n", (int)*ppos);1521 return -EINVAL;1522 }1523 1524 if (count > sizeof(buf)) {1525 wil_err(wil, "Input too long, len = %d\n", (int)count);1526 return -EINVAL;1527 }1528 1529 rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count);1530 if (rc < 0)1531 return rc;1532 1533 buf[rc] = '\0';1534 if (0 == strcmp(buf, run_command))1535 wil_set_recovery_state(wil, fw_recovery_running);1536 else1537 wil_err(wil, "Bad recovery command \"%s\"\n", buf);1538 1539 return rc;1540}1541 1542static const struct file_operations fops_recovery = {1543 .read = wil_read_file_recovery,1544 .write = wil_write_file_recovery,1545 .open = simple_open,1546};1547 1548/*---------Station matrix------------*/1549static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)1550{1551 int i;1552 u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size;1553 unsigned long long drop_dup = r->drop_dup, drop_old = r->drop_old;1554 unsigned long long drop_dup_mcast = r->drop_dup_mcast;1555 1556 seq_printf(s, "([%2d]) 0x%03x [", r->buf_size, r->head_seq_num);1557 for (i = 0; i < r->buf_size; i++) {1558 if (i == index)1559 seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|');1560 else1561 seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_');1562 }1563 seq_printf(s,1564 "] total %llu drop %llu (dup %llu + old %llu + dup mcast %llu) last 0x%03x\n",1565 r->total, drop_dup + drop_old + drop_dup_mcast, drop_dup,1566 drop_old, drop_dup_mcast, r->ssn_last_drop);1567}1568 1569static void wil_print_rxtid_crypto(struct seq_file *s, int tid,1570 struct wil_tid_crypto_rx *c)1571{1572 int i;1573 1574 for (i = 0; i < 4; i++) {1575 struct wil_tid_crypto_rx_single *cc = &c->key_id[i];1576 1577 if (cc->key_set)1578 goto has_keys;1579 }1580 return;1581 1582has_keys:1583 if (tid < WIL_STA_TID_NUM)1584 seq_printf(s, " [%2d] PN", tid);1585 else1586 seq_puts(s, " [GR] PN");1587 1588 for (i = 0; i < 4; i++) {1589 struct wil_tid_crypto_rx_single *cc = &c->key_id[i];1590 1591 seq_printf(s, " [%i%s]%6phN", i, cc->key_set ? "+" : "-",1592 cc->pn);1593 }1594 seq_puts(s, "\n");1595}1596 1597static int sta_show(struct seq_file *s, void *data)1598__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)1599{1600 struct wil6210_priv *wil = s->private;1601 int i, tid, mcs;1602 1603 for (i = 0; i < wil->max_assoc_sta; i++) {1604 struct wil_sta_info *p = &wil->sta[i];1605 char *status = "unknown";1606 u8 aid = 0;1607 u8 mid;1608 bool sta_connected = false;1609 1610 switch (p->status) {1611 case wil_sta_unused:1612 status = "unused ";1613 break;1614 case wil_sta_conn_pending:1615 status = "pending ";1616 break;1617 case wil_sta_connected:1618 status = "connected";1619 aid = p->aid;1620 break;1621 }1622 mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;1623 if (mid < GET_MAX_VIFS(wil)) {1624 struct wil6210_vif *vif = wil->vifs[mid];1625 1626 if (vif->wdev.iftype == NL80211_IFTYPE_STATION &&1627 p->status == wil_sta_connected)1628 sta_connected = true;1629 }1630 /* print roam counter only for connected stations */1631 if (sta_connected)1632 seq_printf(s, "[%d] %pM connected (roam counter %d) MID %d AID %d\n",1633 i, p->addr, p->stats.ft_roams, mid, aid);1634 else1635 seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i,1636 p->addr, status, mid, aid);1637 1638 if (p->status == wil_sta_connected) {1639 spin_lock_bh(&p->tid_rx_lock);1640 for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {1641 struct wil_tid_ampdu_rx *r = p->tid_rx[tid];1642 struct wil_tid_crypto_rx *c =1643 &p->tid_crypto_rx[tid];1644 1645 if (r) {1646 seq_printf(s, " [%2d] ", tid);1647 wil_print_rxtid(s, r);1648 }1649 1650 wil_print_rxtid_crypto(s, tid, c);1651 }1652 wil_print_rxtid_crypto(s, WIL_STA_TID_NUM,1653 &p->group_crypto_rx);1654 spin_unlock_bh(&p->tid_rx_lock);1655 seq_printf(s,1656 "Rx invalid frame: non-data %lu, short %lu, large %lu, replay %lu\n",1657 p->stats.rx_non_data_frame,1658 p->stats.rx_short_frame,1659 p->stats.rx_large_frame,1660 p->stats.rx_replay);1661 seq_printf(s,1662 "mic error %lu, key error %lu, amsdu error %lu, csum error %lu\n",1663 p->stats.rx_mic_error,1664 p->stats.rx_key_error,1665 p->stats.rx_amsdu_error,1666 p->stats.rx_csum_err);1667 1668 seq_puts(s, "Rx/MCS:");1669 for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs);1670 mcs++)1671 seq_printf(s, " %lld",1672 p->stats.rx_per_mcs[mcs]);1673 seq_puts(s, "\n");1674 }1675 }1676 1677 return 0;1678}1679DEFINE_SHOW_ATTRIBUTE(sta);1680 1681static int mids_show(struct seq_file *s, void *data)1682{1683 struct wil6210_priv *wil = s->private;1684 struct wil6210_vif *vif;1685 struct net_device *ndev;1686 int i;1687 1688 mutex_lock(&wil->vif_mutex);1689 for (i = 0; i < GET_MAX_VIFS(wil); i++) {1690 vif = wil->vifs[i];1691 1692 if (vif) {1693 ndev = vif_to_ndev(vif);1694 seq_printf(s, "[%d] %pM %s\n", i, ndev->dev_addr,1695 ndev->name);1696 } else {1697 seq_printf(s, "[%d] unused\n", i);1698 }1699 }1700 mutex_unlock(&wil->vif_mutex);1701 1702 return 0;1703}1704DEFINE_SHOW_ATTRIBUTE(mids);1705 1706static int wil_tx_latency_debugfs_show(struct seq_file *s, void *data)1707__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)1708{1709 struct wil6210_priv *wil = s->private;1710 int i, bin;1711 1712 for (i = 0; i < wil->max_assoc_sta; i++) {1713 struct wil_sta_info *p = &wil->sta[i];1714 char *status = "unknown";1715 u8 aid = 0;1716 u8 mid;1717 1718 if (!p->tx_latency_bins)1719 continue;1720 1721 switch (p->status) {1722 case wil_sta_unused:1723 status = "unused ";1724 break;1725 case wil_sta_conn_pending:1726 status = "pending ";1727 break;1728 case wil_sta_connected:1729 status = "connected";1730 aid = p->aid;1731 break;1732 }1733 mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;1734 seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status,1735 mid, aid);1736 1737 if (p->status == wil_sta_connected) {1738 u64 num_packets = 0;1739 u64 tx_latency_avg = p->stats.tx_latency_total_us;1740 1741 seq_puts(s, "Tx/Latency bin:");1742 for (bin = 0; bin < WIL_NUM_LATENCY_BINS; bin++) {1743 seq_printf(s, " %lld",1744 p->tx_latency_bins[bin]);1745 num_packets += p->tx_latency_bins[bin];1746 }1747 seq_puts(s, "\n");1748 if (!num_packets)1749 continue;1750 do_div(tx_latency_avg, num_packets);1751 seq_printf(s, "Tx/Latency min/avg/max (us): %d/%lld/%d",1752 p->stats.tx_latency_min_us,1753 tx_latency_avg,1754 p->stats.tx_latency_max_us);1755 1756 seq_puts(s, "\n");1757 }1758 }1759 1760 return 0;1761}1762 1763static int wil_tx_latency_seq_open(struct inode *inode, struct file *file)1764{1765 return single_open(file, wil_tx_latency_debugfs_show,1766 inode->i_private);1767}1768 1769static ssize_t wil_tx_latency_write(struct file *file, const char __user *buf,1770 size_t len, loff_t *ppos)1771{1772 struct seq_file *s = file->private_data;1773 struct wil6210_priv *wil = s->private;1774 int val, rc, i;1775 bool enable;1776 1777 rc = kstrtoint_from_user(buf, len, 0, &val);1778 if (rc) {1779 wil_err(wil, "Invalid argument\n");1780 return rc;1781 }1782 if (val == 1)1783 /* default resolution */1784 val = 500;1785 if (val && (val < 50 || val > 1000)) {1786 wil_err(wil, "Invalid resolution %d\n", val);1787 return -EINVAL;1788 }1789 1790 enable = !!val;1791 if (wil->tx_latency == enable)1792 return len;1793 1794 wil_info(wil, "%s TX latency measurements (resolution %dusec)\n",1795 enable ? "Enabling" : "Disabling", val);1796 1797 if (enable) {1798 size_t sz = sizeof(u64) * WIL_NUM_LATENCY_BINS;1799 1800 wil->tx_latency_res = val;1801 for (i = 0; i < wil->max_assoc_sta; i++) {1802 struct wil_sta_info *sta = &wil->sta[i];1803 1804 kfree(sta->tx_latency_bins);1805 sta->tx_latency_bins = kzalloc(sz, GFP_KERNEL);1806 if (!sta->tx_latency_bins)1807 return -ENOMEM;1808 sta->stats.tx_latency_min_us = U32_MAX;1809 sta->stats.tx_latency_max_us = 0;1810 sta->stats.tx_latency_total_us = 0;1811 }1812 }1813 wil->tx_latency = enable;1814 1815 return len;1816}1817 1818static const struct file_operations fops_tx_latency = {1819 .open = wil_tx_latency_seq_open,1820 .release = single_release,1821 .read = seq_read,1822 .write = wil_tx_latency_write,1823 .llseek = seq_lseek,1824};1825 1826static void wil_link_stats_print_basic(struct wil6210_vif *vif,1827 struct seq_file *s,1828 struct wmi_link_stats_basic *basic)1829{1830 char per[5] = "?";1831 1832 if (basic->per_average != 0xff)1833 snprintf(per, sizeof(per), "%d%%", basic->per_average);1834 1835 seq_printf(s, "CID %d {\n"1836 "\tTxMCS %s TxTpt %d\n"1837 "\tGoodput(rx:tx) %d:%d\n"1838 "\tRxBcastFrames %d\n"1839 "\tRSSI %d SQI %d SNR %d PER %s\n"1840 "\tRx RFC %d Ant num %d\n"1841 "\tSectors(rx:tx) my %d:%d peer %d:%d\n"1842 "}\n",1843 basic->cid,1844 WIL_EXTENDED_MCS_CHECK(basic->bf_mcs),1845 le32_to_cpu(basic->tx_tpt),1846 le32_to_cpu(basic->rx_goodput),1847 le32_to_cpu(basic->tx_goodput),1848 le32_to_cpu(basic->rx_bcast_frames),1849 basic->rssi, basic->sqi, basic->snr, per,1850 basic->selected_rfc, basic->rx_effective_ant_num,1851 basic->my_rx_sector, basic->my_tx_sector,1852 basic->other_rx_sector, basic->other_tx_sector);1853}1854 1855static void wil_link_stats_print_global(struct wil6210_priv *wil,1856 struct seq_file *s,1857 struct wmi_link_stats_global *global)1858{1859 seq_printf(s, "Frames(rx:tx) %d:%d\n"1860 "BA Frames(rx:tx) %d:%d\n"1861 "Beacons %d\n"1862 "Rx Errors (MIC:CRC) %d:%d\n"1863 "Tx Errors (no ack) %d\n",1864 le32_to_cpu(global->rx_frames),1865 le32_to_cpu(global->tx_frames),1866 le32_to_cpu(global->rx_ba_frames),1867 le32_to_cpu(global->tx_ba_frames),1868 le32_to_cpu(global->tx_beacons),1869 le32_to_cpu(global->rx_mic_errors),1870 le32_to_cpu(global->rx_crc_errors),1871 le32_to_cpu(global->tx_fail_no_ack));1872}1873 1874static void wil_link_stats_debugfs_show_vif(struct wil6210_vif *vif,1875 struct seq_file *s)1876{1877 struct wil6210_priv *wil = vif_to_wil(vif);1878 struct wmi_link_stats_basic *stats;1879 int i;1880 1881 if (!vif->fw_stats_ready) {1882 seq_puts(s, "no statistics\n");1883 return;1884 }1885 1886 seq_printf(s, "TSF %lld\n", vif->fw_stats_tsf);1887 for (i = 0; i < wil->max_assoc_sta; i++) {1888 if (wil->sta[i].status == wil_sta_unused)1889 continue;1890 if (wil->sta[i].mid != vif->mid)1891 continue;1892 1893 stats = &wil->sta[i].fw_stats_basic;1894 wil_link_stats_print_basic(vif, s, stats);1895 }1896}1897 1898static int wil_link_stats_debugfs_show(struct seq_file *s, void *data)1899{1900 struct wil6210_priv *wil = s->private;1901 struct wil6210_vif *vif;1902 int i, rc;1903 1904 rc = mutex_lock_interruptible(&wil->vif_mutex);1905 if (rc)1906 return rc;1907 1908 /* iterate over all MIDs and show per-cid statistics. Then show the1909 * global statistics1910 */1911 for (i = 0; i < GET_MAX_VIFS(wil); i++) {1912 vif = wil->vifs[i];1913 1914 seq_printf(s, "MID %d ", i);1915 if (!vif) {1916 seq_puts(s, "unused\n");1917 continue;1918 }1919 1920 wil_link_stats_debugfs_show_vif(vif, s);1921 }1922 1923 mutex_unlock(&wil->vif_mutex);1924 1925 return 0;1926}1927 1928static int wil_link_stats_seq_open(struct inode *inode, struct file *file)1929{1930 return single_open(file, wil_link_stats_debugfs_show, inode->i_private);1931}1932 1933static ssize_t wil_link_stats_write(struct file *file, const char __user *buf,1934 size_t len, loff_t *ppos)1935{1936 struct seq_file *s = file->private_data;1937 struct wil6210_priv *wil = s->private;1938 int cid, interval, rc, i;1939 struct wil6210_vif *vif;1940 char *kbuf = kmalloc(len + 1, GFP_KERNEL);1941 1942 if (!kbuf)1943 return -ENOMEM;1944 1945 rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);1946 if (rc != len) {1947 kfree(kbuf);1948 return rc >= 0 ? -EIO : rc;1949 }1950 1951 kbuf[len] = '\0';1952 /* specify cid (use -1 for all cids) and snapshot interval in ms */1953 rc = sscanf(kbuf, "%d %d", &cid, &interval);1954 kfree(kbuf);1955 if (rc < 0)1956 return rc;1957 if (rc < 2 || interval < 0)1958 return -EINVAL;1959 1960 wil_info(wil, "request link statistics, cid %d interval %d\n",1961 cid, interval);1962 1963 rc = mutex_lock_interruptible(&wil->vif_mutex);1964 if (rc)1965 return rc;1966 1967 for (i = 0; i < GET_MAX_VIFS(wil); i++) {1968 vif = wil->vifs[i];1969 if (!vif)1970 continue;1971 1972 rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_BASIC,1973 (cid == -1 ? 0xff : cid), interval);1974 if (rc)1975 wil_err(wil, "link statistics failed for mid %d\n", i);1976 }1977 mutex_unlock(&wil->vif_mutex);1978 1979 return len;1980}1981 1982static const struct file_operations fops_link_stats = {1983 .open = wil_link_stats_seq_open,1984 .release = single_release,1985 .read = seq_read,1986 .write = wil_link_stats_write,1987 .llseek = seq_lseek,1988};1989 1990static int1991wil_link_stats_global_debugfs_show(struct seq_file *s, void *data)1992{1993 struct wil6210_priv *wil = s->private;1994 1995 if (!wil->fw_stats_global.ready)1996 return 0;1997 1998 seq_printf(s, "TSF %lld\n", wil->fw_stats_global.tsf);1999 wil_link_stats_print_global(wil, s, &wil->fw_stats_global.stats);2000 2001 return 0;2002}2003 2004static int2005wil_link_stats_global_seq_open(struct inode *inode, struct file *file)2006{2007 return single_open(file, wil_link_stats_global_debugfs_show,2008 inode->i_private);2009}2010 2011static ssize_t2012wil_link_stats_global_write(struct file *file, const char __user *buf,2013 size_t len, loff_t *ppos)2014{2015 struct seq_file *s = file->private_data;2016 struct wil6210_priv *wil = s->private;2017 int interval, rc;2018 struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);2019 2020 /* specify snapshot interval in ms */2021 rc = kstrtoint_from_user(buf, len, 0, &interval);2022 if (rc || interval < 0) {2023 wil_err(wil, "Invalid argument\n");2024 return -EINVAL;2025 }2026 2027 wil_info(wil, "request global link stats, interval %d\n", interval);2028 2029 rc = wmi_link_stats_cfg(vif, WMI_LINK_STATS_TYPE_GLOBAL, 0, interval);2030 if (rc)2031 wil_err(wil, "global link stats failed %d\n", rc);2032 2033 return rc ? rc : len;2034}2035 2036static const struct file_operations fops_link_stats_global = {2037 .open = wil_link_stats_global_seq_open,2038 .release = single_release,2039 .read = seq_read,2040 .write = wil_link_stats_global_write,2041 .llseek = seq_lseek,2042};2043 2044static ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf,2045 size_t count, loff_t *ppos)2046{2047 char buf[80];2048 int n;2049 2050 n = snprintf(buf, sizeof(buf),2051 "led_id is set to %d, echo 1 to enable, 0 to disable\n",2052 led_id);2053 2054 n = min_t(int, n, sizeof(buf));2055 2056 return simple_read_from_buffer(user_buf, count, ppos,2057 buf, n);2058}2059 2060static ssize_t wil_write_file_led_cfg(struct file *file,2061 const char __user *buf_,2062 size_t count, loff_t *ppos)2063{2064 struct wil6210_priv *wil = file->private_data;2065 int val;2066 int rc;2067 2068 rc = kstrtoint_from_user(buf_, count, 0, &val);2069 if (rc) {2070 wil_err(wil, "Invalid argument\n");2071 return rc;2072 }2073 2074 wil_info(wil, "%s led %d\n", val ? "Enabling" : "Disabling", led_id);2075 rc = wmi_led_cfg(wil, val);2076 if (rc) {2077 wil_info(wil, "%s led %d failed\n",2078 val ? "Enabling" : "Disabling", led_id);2079 return rc;2080 }2081 2082 return count;2083}2084 2085static const struct file_operations fops_led_cfg = {2086 .read = wil_read_file_led_cfg,2087 .write = wil_write_file_led_cfg,2088 .open = simple_open,2089};2090 2091/* led_blink_time, write:2092 * "<blink_on_slow> <blink_off_slow> <blink_on_med> <blink_off_med> <blink_on_fast> <blink_off_fast>2093 */2094static ssize_t wil_write_led_blink_time(struct file *file,2095 const char __user *buf,2096 size_t len, loff_t *ppos)2097{2098 int rc;2099 char *kbuf = kmalloc(len + 1, GFP_KERNEL);2100 2101 if (!kbuf)2102 return -ENOMEM;2103 2104 rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);2105 if (rc != len) {2106 kfree(kbuf);2107 return rc >= 0 ? -EIO : rc;2108 }2109 2110 kbuf[len] = '\0';2111 rc = sscanf(kbuf, "%d %d %d %d %d %d",2112 &led_blink_time[WIL_LED_TIME_SLOW].on_ms,2113 &led_blink_time[WIL_LED_TIME_SLOW].off_ms,2114 &led_blink_time[WIL_LED_TIME_MED].on_ms,2115 &led_blink_time[WIL_LED_TIME_MED].off_ms,2116 &led_blink_time[WIL_LED_TIME_FAST].on_ms,2117 &led_blink_time[WIL_LED_TIME_FAST].off_ms);2118 kfree(kbuf);2119 2120 if (rc < 0)2121 return rc;2122 if (rc < 6)2123 return -EINVAL;2124 2125 return len;2126}2127 2128static ssize_t wil_read_led_blink_time(struct file *file, char __user *user_buf,2129 size_t count, loff_t *ppos)2130{2131 static char text[400];2132 2133 snprintf(text, sizeof(text),2134 "To set led blink on/off time variables write:\n"2135 "<blink_on_slow> <blink_off_slow> <blink_on_med> "2136 "<blink_off_med> <blink_on_fast> <blink_off_fast>\n"2137 "The current values are:\n"2138 "%d %d %d %d %d %d\n",2139 led_blink_time[WIL_LED_TIME_SLOW].on_ms,2140 led_blink_time[WIL_LED_TIME_SLOW].off_ms,2141 led_blink_time[WIL_LED_TIME_MED].on_ms,2142 led_blink_time[WIL_LED_TIME_MED].off_ms,2143 led_blink_time[WIL_LED_TIME_FAST].on_ms,2144 led_blink_time[WIL_LED_TIME_FAST].off_ms);2145 2146 return simple_read_from_buffer(user_buf, count, ppos, text,2147 sizeof(text));2148}2149 2150static const struct file_operations fops_led_blink_time = {2151 .read = wil_read_led_blink_time,2152 .write = wil_write_led_blink_time,2153 .open = simple_open,2154};2155 2156/*---------FW capabilities------------*/2157static int fw_capabilities_show(struct seq_file *s, void *data)2158{2159 struct wil6210_priv *wil = s->private;2160 2161 seq_printf(s, "fw_capabilities : %*pb\n", WMI_FW_CAPABILITY_MAX,2162 wil->fw_capabilities);2163 2164 return 0;2165}2166DEFINE_SHOW_ATTRIBUTE(fw_capabilities);2167 2168/*---------FW version------------*/2169static int fw_version_show(struct seq_file *s, void *data)2170{2171 struct wil6210_priv *wil = s->private;2172 2173 if (wil->fw_version[0])2174 seq_printf(s, "%s\n", wil->fw_version);2175 else2176 seq_puts(s, "N/A\n");2177 2178 return 0;2179}2180DEFINE_SHOW_ATTRIBUTE(fw_version);2181 2182/*---------suspend_stats---------*/2183static ssize_t wil_write_suspend_stats(struct file *file,2184 const char __user *buf,2185 size_t len, loff_t *ppos)2186{2187 struct wil6210_priv *wil = file->private_data;2188 2189 memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));2190 2191 return len;2192}2193 2194static ssize_t wil_read_suspend_stats(struct file *file,2195 char __user *user_buf,2196 size_t count, loff_t *ppos)2197{2198 struct wil6210_priv *wil = file->private_data;2199 char *text;2200 int n, ret, text_size = 500;2201 2202 text = kmalloc(text_size, GFP_KERNEL);2203 if (!text)2204 return -ENOMEM;2205 2206 n = snprintf(text, text_size,2207 "Radio on suspend statistics:\n"2208 "successful suspends:%ld failed suspends:%ld\n"2209 "successful resumes:%ld failed resumes:%ld\n"2210 "rejected by device:%ld\n"2211 "Radio off suspend statistics:\n"2212 "successful suspends:%ld failed suspends:%ld\n"2213 "successful resumes:%ld failed resumes:%ld\n"2214 "General statistics:\n"2215 "rejected by host:%ld\n",2216 wil->suspend_stats.r_on.successful_suspends,2217 wil->suspend_stats.r_on.failed_suspends,2218 wil->suspend_stats.r_on.successful_resumes,2219 wil->suspend_stats.r_on.failed_resumes,2220 wil->suspend_stats.rejected_by_device,2221 wil->suspend_stats.r_off.successful_suspends,2222 wil->suspend_stats.r_off.failed_suspends,2223 wil->suspend_stats.r_off.successful_resumes,2224 wil->suspend_stats.r_off.failed_resumes,2225 wil->suspend_stats.rejected_by_host);2226 2227 n = min_t(int, n, text_size);2228 2229 ret = simple_read_from_buffer(user_buf, count, ppos, text, n);2230 2231 kfree(text);2232 2233 return ret;2234}2235 2236static const struct file_operations fops_suspend_stats = {2237 .read = wil_read_suspend_stats,2238 .write = wil_write_suspend_stats,2239 .open = simple_open,2240};2241 2242/*---------compressed_rx_status---------*/2243static ssize_t wil_compressed_rx_status_write(struct file *file,2244 const char __user *buf,2245 size_t len, loff_t *ppos)2246{2247 struct seq_file *s = file->private_data;2248 struct wil6210_priv *wil = s->private;2249 int compressed_rx_status;2250 int rc;2251 2252 rc = kstrtoint_from_user(buf, len, 0, &compressed_rx_status);2253 if (rc) {2254 wil_err(wil, "Invalid argument\n");2255 return rc;2256 }2257 2258 if (wil_has_active_ifaces(wil, true, false)) {2259 wil_err(wil, "cannot change edma config after iface is up\n");2260 return -EPERM;2261 }2262 2263 wil_info(wil, "%sable compressed_rx_status\n",2264 compressed_rx_status ? "En" : "Dis");2265 2266 wil->use_compressed_rx_status = compressed_rx_status;2267 2268 return len;2269}2270 2271static int2272wil_compressed_rx_status_show(struct seq_file *s, void *data)2273{2274 struct wil6210_priv *wil = s->private;2275 2276 seq_printf(s, "%d\n", wil->use_compressed_rx_status);2277 2278 return 0;2279}2280 2281static int2282wil_compressed_rx_status_seq_open(struct inode *inode, struct file *file)2283{2284 return single_open(file, wil_compressed_rx_status_show,2285 inode->i_private);2286}2287 2288static const struct file_operations fops_compressed_rx_status = {2289 .open = wil_compressed_rx_status_seq_open,2290 .release = single_release,2291 .read = seq_read,2292 .write = wil_compressed_rx_status_write,2293 .llseek = seq_lseek,2294};2295 2296/*----------------*/2297static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,2298 struct dentry *dbg)2299{2300 int i;2301 char name[32];2302 2303 for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {2304 struct wil_blob_wrapper *wil_blob = &wil->blobs[i];2305 struct debugfs_blob_wrapper *blob = &wil_blob->blob;2306 const struct fw_map *map = &fw_mapping[i];2307 2308 if (!map->name)2309 continue;2310 2311 wil_blob->wil = wil;2312 blob->data = (void * __force)wil->csr + HOSTADDR(map->host);2313 blob->size = map->to - map->from;2314 snprintf(name, sizeof(name), "blob_%s", map->name);2315 wil_debugfs_create_ioblob(name, 0444, dbg, wil_blob);2316 }2317}2318 2319/* misc files */2320static const struct {2321 const char *name;2322 umode_t mode;2323 const struct file_operations *fops;2324} dbg_files[] = {2325 {"mbox", 0444, &mbox_fops},2326 {"rings", 0444, &ring_fops},2327 {"stations", 0444, &sta_fops},2328 {"mids", 0444, &mids_fops},2329 {"desc", 0444, &txdesc_fops},2330 {"bf", 0444, &bf_fops},2331 {"mem_val", 0644, &memread_fops},2332 {"rxon", 0244, &fops_rxon},2333 {"tx_mgmt", 0244, &fops_txmgmt},2334 {"wmi_send", 0244, &fops_wmi},2335 {"back", 0644, &fops_back},2336 {"pmccfg", 0644, &fops_pmccfg},2337 {"pmcdata", 0444, &fops_pmcdata},2338 {"pmcring", 0444, &fops_pmcring},2339 {"temp", 0444, &temp_fops},2340 {"link", 0444, &link_fops},2341 {"info", 0444, &info_fops},2342 {"recovery", 0644, &fops_recovery},2343 {"led_cfg", 0644, &fops_led_cfg},2344 {"led_blink_time", 0644, &fops_led_blink_time},2345 {"fw_capabilities", 0444, &fw_capabilities_fops},2346 {"fw_version", 0444, &fw_version_fops},2347 {"suspend_stats", 0644, &fops_suspend_stats},2348 {"compressed_rx_status", 0644, &fops_compressed_rx_status},2349 {"srings", 0444, &srings_fops},2350 {"status_msg", 0444, &status_msg_fops},2351 {"rx_buff_mgmt", 0444, &rx_buff_mgmt_fops},2352 {"tx_latency", 0644, &fops_tx_latency},2353 {"link_stats", 0644, &fops_link_stats},2354 {"link_stats_global", 0644, &fops_link_stats_global},2355 {"rbufcap", 0244, &fops_rbufcap},2356};2357 2358static void wil6210_debugfs_init_files(struct wil6210_priv *wil,2359 struct dentry *dbg)2360{2361 int i;2362 2363 for (i = 0; i < ARRAY_SIZE(dbg_files); i++)2364 debugfs_create_file(dbg_files[i].name, dbg_files[i].mode, dbg,2365 wil, dbg_files[i].fops);2366}2367 2368/* interrupt control blocks */2369static const struct {2370 const char *name;2371 u32 icr_off;2372} dbg_icr[] = {2373 {"USER_ICR", HOSTADDR(RGF_USER_USER_ICR)},2374 {"DMA_EP_TX_ICR", HOSTADDR(RGF_DMA_EP_TX_ICR)},2375 {"DMA_EP_RX_ICR", HOSTADDR(RGF_DMA_EP_RX_ICR)},2376 {"DMA_EP_MISC_ICR", HOSTADDR(RGF_DMA_EP_MISC_ICR)},2377};2378 2379static void wil6210_debugfs_init_isr(struct wil6210_priv *wil,2380 struct dentry *dbg)2381{2382 int i;2383 2384 for (i = 0; i < ARRAY_SIZE(dbg_icr); i++)2385 wil6210_debugfs_create_ISR(wil, dbg_icr[i].name, dbg,2386 dbg_icr[i].icr_off);2387}2388 2389#define WIL_FIELD(name, mode, type) { __stringify(name), mode, \2390 offsetof(struct wil6210_priv, name), type}2391 2392/* fields in struct wil6210_priv */2393static const struct dbg_off dbg_wil_off[] = {2394 WIL_FIELD(status[0], 0644, doff_ulong),2395 WIL_FIELD(hw_version, 0444, doff_x32),2396 WIL_FIELD(recovery_count, 0444, doff_u32),2397 WIL_FIELD(discovery_mode, 0644, doff_u8),2398 WIL_FIELD(chip_revision, 0444, doff_u8),2399 WIL_FIELD(abft_len, 0644, doff_u8),2400 WIL_FIELD(wakeup_trigger, 0644, doff_u8),2401 WIL_FIELD(ring_idle_trsh, 0644, doff_u32),2402 WIL_FIELD(num_rx_status_rings, 0644, doff_u8),2403 WIL_FIELD(rx_status_ring_order, 0644, doff_u32),2404 WIL_FIELD(tx_status_ring_order, 0644, doff_u32),2405 WIL_FIELD(rx_buff_id_count, 0644, doff_u32),2406 WIL_FIELD(amsdu_en, 0644, doff_u8),2407 {},2408};2409 2410static const struct dbg_off dbg_wil_regs[] = {2411 {"RGF_MAC_MTRL_COUNTER_0", 0444, HOSTADDR(RGF_MAC_MTRL_COUNTER_0),2412 doff_io32},2413 {"RGF_USER_USAGE_1", 0444, HOSTADDR(RGF_USER_USAGE_1), doff_io32},2414 {"RGF_USER_USAGE_2", 0444, HOSTADDR(RGF_USER_USAGE_2), doff_io32},2415 {},2416};2417 2418/* static parameters */2419static const struct dbg_off dbg_statics[] = {2420 {"desc_index", 0644, (ulong)&dbg_txdesc_index, doff_u32},2421 {"ring_index", 0644, (ulong)&dbg_ring_index, doff_u32},2422 {"mem_addr", 0644, (ulong)&mem_addr, doff_u32},2423 {"led_polarity", 0644, (ulong)&led_polarity, doff_u8},2424 {"status_index", 0644, (ulong)&dbg_status_msg_index, doff_u32},2425 {"sring_index", 0644, (ulong)&dbg_sring_index, doff_u32},2426 {"drop_if_ring_full", 0644, (ulong)&drop_if_ring_full, doff_u8},2427 {},2428};2429 2430static const int dbg_off_count = 4 * (ARRAY_SIZE(isr_off) - 1) +2431 ARRAY_SIZE(dbg_wil_regs) - 1 +2432 ARRAY_SIZE(pseudo_isr_off) - 1 +2433 ARRAY_SIZE(lgc_itr_cnt_off) - 1 +2434 ARRAY_SIZE(tx_itr_cnt_off) - 1 +2435 ARRAY_SIZE(rx_itr_cnt_off) - 1;2436 2437int wil6210_debugfs_init(struct wil6210_priv *wil)2438{2439 struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,2440 wil_to_wiphy(wil)->debugfsdir);2441 if (IS_ERR_OR_NULL(dbg))2442 return -ENODEV;2443 2444 wil->dbg_data.data_arr = kcalloc(dbg_off_count,2445 sizeof(struct wil_debugfs_iomem_data),2446 GFP_KERNEL);2447 if (!wil->dbg_data.data_arr) {2448 debugfs_remove_recursive(dbg);2449 wil->debug = NULL;2450 return -ENOMEM;2451 }2452 2453 wil->dbg_data.iomem_data_count = 0;2454 2455 wil_pmc_init(wil);2456 2457 wil6210_debugfs_init_files(wil, dbg);2458 wil6210_debugfs_init_isr(wil, dbg);2459 wil6210_debugfs_init_blobs(wil, dbg);2460 wil6210_debugfs_init_offset(wil, dbg, wil, dbg_wil_off);2461 wil6210_debugfs_init_offset(wil, dbg, (void * __force)wil->csr,2462 dbg_wil_regs);2463 wil6210_debugfs_init_offset(wil, dbg, NULL, dbg_statics);2464 2465 wil6210_debugfs_create_pseudo_ISR(wil, dbg);2466 2467 wil6210_debugfs_create_ITR_CNT(wil, dbg);2468 2469 return 0;2470}2471 2472void wil6210_debugfs_remove(struct wil6210_priv *wil)2473{2474 int i;2475 2476 debugfs_remove_recursive(wil->debug);2477 wil->debug = NULL;2478 2479 kfree(wil->dbg_data.data_arr);2480 for (i = 0; i < wil->max_assoc_sta; i++)2481 kfree(wil->sta[i].tx_latency_bins);2482 2483 /* free pmc memory without sending command to fw, as it will2484 * be reset on the way down anyway2485 */2486 wil_pmc_free(wil, false);2487}2488