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