1 // SPDX-License-Identifier: GPL-2.0-or-later
4 Broadcom B43legacy wireless driver
6 debugfs driver debugging code
8 Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
14 #include <linux/debugfs.h>
15 #include <linux/slab.h>
16 #include <linux/netdevice.h>
17 #include <linux/pci.h>
18 #include <linux/mutex.h>
20 #include "b43legacy.h"
28 /* The root directory. */
29 static struct dentry
*rootdir
;
31 struct b43legacy_debugfs_fops
{
32 ssize_t (*read
)(struct b43legacy_wldev
*dev
, char *buf
, size_t bufsize
);
33 int (*write
)(struct b43legacy_wldev
*dev
, const char *buf
, size_t count
);
34 struct file_operations fops
;
35 /* Offset of struct b43legacy_dfs_file in struct b43legacy_dfsentry */
36 size_t file_struct_offset
;
37 /* Take wl->irq_lock before calling read/write? */
42 struct b43legacy_dfs_file
* fops_to_dfs_file(struct b43legacy_wldev
*dev
,
43 const struct b43legacy_debugfs_fops
*dfops
)
48 p
+= dfops
->file_struct_offset
;
54 #define fappend(fmt, x...) \
56 if (bufsize - count) \
57 count += scnprintf(buf + count, \
61 printk(KERN_ERR "b43legacy: fappend overflow\n"); \
65 /* wl->irq_lock is locked */
66 static ssize_t
tsf_read_file(struct b43legacy_wldev
*dev
, char *buf
, size_t bufsize
)
71 b43legacy_tsf_read(dev
, &tsf
);
72 fappend("0x%08x%08x\n",
73 (unsigned int)((tsf
& 0xFFFFFFFF00000000ULL
) >> 32),
74 (unsigned int)(tsf
& 0xFFFFFFFFULL
));
79 /* wl->irq_lock is locked */
80 static int tsf_write_file(struct b43legacy_wldev
*dev
, const char *buf
, size_t count
)
84 if (sscanf(buf
, "%llu", (unsigned long long *)(&tsf
)) != 1)
86 b43legacy_tsf_write(dev
, tsf
);
91 /* wl->irq_lock is locked */
92 static ssize_t
ucode_regs_read_file(struct b43legacy_wldev
*dev
, char *buf
, size_t bufsize
)
97 for (i
= 0; i
< 64; i
++) {
98 fappend("r%d = 0x%04x\n", i
,
99 b43legacy_shm_read16(dev
, B43legacy_SHM_WIRELESS
, i
));
105 /* wl->irq_lock is locked */
106 static ssize_t
shm_read_file(struct b43legacy_wldev
*dev
, char *buf
, size_t bufsize
)
111 __le16
*le16buf
= (__le16
*)buf
;
113 for (i
= 0; i
< 0x1000; i
++) {
114 if (bufsize
< sizeof(tmp
))
116 tmp
= b43legacy_shm_read16(dev
, B43legacy_SHM_SHARED
, 2 * i
);
117 le16buf
[i
] = cpu_to_le16(tmp
);
118 count
+= sizeof(tmp
);
119 bufsize
-= sizeof(tmp
);
125 static ssize_t
txstat_read_file(struct b43legacy_wldev
*dev
, char *buf
, size_t bufsize
)
127 struct b43legacy_txstatus_log
*log
= &dev
->dfsentry
->txstatlog
;
131 struct b43legacy_txstatus
*stat
;
133 spin_lock_irqsave(&log
->lock
, flags
);
135 fappend("Nothing transmitted, yet\n");
138 fappend("b43legacy TX status reports:\n\n"
139 "index | cookie | seq | phy_stat | frame_count | "
140 "rts_count | supp_reason | pm_indicated | "
141 "intermediate | for_ampdu | acked\n" "---\n");
145 if (i
== B43legacy_NR_LOGGED_TXSTATUS
)
147 stat
= &(log
->log
[i
]);
150 "0x%04X | 0x%04X | 0x%02X | "
155 stat
->cookie
, stat
->seq
, stat
->phy_stat
,
156 stat
->frame_count
, stat
->rts_count
,
157 stat
->supp_reason
, stat
->pm_indicated
,
158 stat
->intermediate
, stat
->for_ampdu
,
167 spin_unlock_irqrestore(&log
->lock
, flags
);
172 /* wl->irq_lock is locked */
173 static int restart_write_file(struct b43legacy_wldev
*dev
, const char *buf
, size_t count
)
177 if (count
> 0 && buf
[0] == '1') {
178 b43legacy_controller_restart(dev
, "manually restarted");
187 static ssize_t
b43legacy_debugfs_read(struct file
*file
, char __user
*userbuf
,
188 size_t count
, loff_t
*ppos
)
190 struct b43legacy_wldev
*dev
;
191 struct b43legacy_debugfs_fops
*dfops
;
192 struct b43legacy_dfs_file
*dfile
;
195 const size_t bufsize
= 1024 * 16; /* 16 KiB buffer */
196 const size_t buforder
= get_order(bufsize
);
201 dev
= file
->private_data
;
205 mutex_lock(&dev
->wl
->mutex
);
206 if (b43legacy_status(dev
) < B43legacy_STAT_INITIALIZED
) {
211 dfops
= container_of(debugfs_real_fops(file
),
212 struct b43legacy_debugfs_fops
, fops
);
217 dfile
= fops_to_dfs_file(dev
, dfops
);
219 if (!dfile
->buffer
) {
220 buf
= (char *)__get_free_pages(GFP_KERNEL
, buforder
);
225 memset(buf
, 0, bufsize
);
226 if (dfops
->take_irqlock
) {
227 spin_lock_irq(&dev
->wl
->irq_lock
);
228 ret
= dfops
->read(dev
, buf
, bufsize
);
229 spin_unlock_irq(&dev
->wl
->irq_lock
);
231 ret
= dfops
->read(dev
, buf
, bufsize
);
233 free_pages((unsigned long)buf
, buforder
);
237 dfile
->data_len
= ret
;
241 ret
= simple_read_from_buffer(userbuf
, count
, ppos
,
244 if (*ppos
>= dfile
->data_len
) {
245 free_pages((unsigned long)dfile
->buffer
, buforder
);
246 dfile
->buffer
= NULL
;
250 mutex_unlock(&dev
->wl
->mutex
);
252 return err
? err
: ret
;
255 static ssize_t
b43legacy_debugfs_write(struct file
*file
,
256 const char __user
*userbuf
,
257 size_t count
, loff_t
*ppos
)
259 struct b43legacy_wldev
*dev
;
260 struct b43legacy_debugfs_fops
*dfops
;
266 if (count
> PAGE_SIZE
)
268 dev
= file
->private_data
;
272 mutex_lock(&dev
->wl
->mutex
);
273 if (b43legacy_status(dev
) < B43legacy_STAT_INITIALIZED
) {
278 dfops
= container_of(debugfs_real_fops(file
),
279 struct b43legacy_debugfs_fops
, fops
);
285 buf
= (char *)get_zeroed_page(GFP_KERNEL
);
290 if (copy_from_user(buf
, userbuf
, count
)) {
294 if (dfops
->take_irqlock
) {
295 spin_lock_irq(&dev
->wl
->irq_lock
);
296 err
= dfops
->write(dev
, buf
, count
);
297 spin_unlock_irq(&dev
->wl
->irq_lock
);
299 err
= dfops
->write(dev
, buf
, count
);
304 free_page((unsigned long)buf
);
306 mutex_unlock(&dev
->wl
->mutex
);
308 return err
? err
: count
;
312 #define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \
313 static struct b43legacy_debugfs_fops fops_##name = { \
317 .open = simple_open, \
318 .read = b43legacy_debugfs_read, \
319 .write = b43legacy_debugfs_write, \
320 .llseek = generic_file_llseek, \
322 .file_struct_offset = offsetof(struct b43legacy_dfsentry, \
324 .take_irqlock = _take_irqlock, \
327 B43legacy_DEBUGFS_FOPS(tsf
, tsf_read_file
, tsf_write_file
, 1);
328 B43legacy_DEBUGFS_FOPS(ucode_regs
, ucode_regs_read_file
, NULL
, 1);
329 B43legacy_DEBUGFS_FOPS(shm
, shm_read_file
, NULL
, 1);
330 B43legacy_DEBUGFS_FOPS(txstat
, txstat_read_file
, NULL
, 0);
331 B43legacy_DEBUGFS_FOPS(restart
, NULL
, restart_write_file
, 1);
334 int b43legacy_debug(struct b43legacy_wldev
*dev
, enum b43legacy_dyndbg feature
)
336 return !!(dev
->dfsentry
&& dev
->dfsentry
->dyn_debug
[feature
]);
339 static void b43legacy_remove_dynamic_debug(struct b43legacy_wldev
*dev
)
341 struct b43legacy_dfsentry
*e
= dev
->dfsentry
;
344 for (i
= 0; i
< __B43legacy_NR_DYNDBG
; i
++)
345 debugfs_remove(e
->dyn_debug_dentries
[i
]);
348 static void b43legacy_add_dynamic_debug(struct b43legacy_wldev
*dev
)
350 struct b43legacy_dfsentry
*e
= dev
->dfsentry
;
352 #define add_dyn_dbg(name, id, initstate) do { \
353 e->dyn_debug[id] = (initstate); \
354 e->dyn_debug_dentries[id] = \
355 debugfs_create_bool(name, 0600, e->subdir, \
356 &(e->dyn_debug[id])); \
359 add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER
, false);
360 add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW
, false);
361 add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE
, false);
362 add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST
, false);
363 add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP
, false);
368 void b43legacy_debugfs_add_device(struct b43legacy_wldev
*dev
)
370 struct b43legacy_dfsentry
*e
;
371 struct b43legacy_txstatus_log
*log
;
374 B43legacy_WARN_ON(!dev
);
375 e
= kzalloc(sizeof(*e
), GFP_KERNEL
);
377 b43legacyerr(dev
->wl
, "debugfs: add device OOM\n");
382 log
->log
= kcalloc(B43legacy_NR_LOGGED_TXSTATUS
,
383 sizeof(struct b43legacy_txstatus
), GFP_KERNEL
);
385 b43legacyerr(dev
->wl
, "debugfs: add device txstatus OOM\n");
390 spin_lock_init(&log
->lock
);
394 snprintf(devdir
, sizeof(devdir
), "%s", wiphy_name(dev
->wl
->hw
->wiphy
));
395 e
->subdir
= debugfs_create_dir(devdir
, rootdir
);
397 #define ADD_FILE(name, mode) \
399 e->file_##name.dentry = \
400 debugfs_create_file(__stringify(name), \
401 mode, e->subdir, dev, \
402 &fops_##name.fops); \
403 e->file_##name.dentry = NULL; \
408 ADD_FILE(ucode_regs
, 0400);
410 ADD_FILE(txstat
, 0400);
411 ADD_FILE(restart
, 0200);
415 b43legacy_add_dynamic_debug(dev
);
418 void b43legacy_debugfs_remove_device(struct b43legacy_wldev
*dev
)
420 struct b43legacy_dfsentry
*e
;
427 b43legacy_remove_dynamic_debug(dev
);
429 debugfs_remove(e
->file_tsf
.dentry
);
430 debugfs_remove(e
->file_ucode_regs
.dentry
);
431 debugfs_remove(e
->file_shm
.dentry
);
432 debugfs_remove(e
->file_txstat
.dentry
);
433 debugfs_remove(e
->file_restart
.dentry
);
435 debugfs_remove(e
->subdir
);
436 kfree(e
->txstatlog
.log
);
440 void b43legacy_debugfs_log_txstat(struct b43legacy_wldev
*dev
,
441 const struct b43legacy_txstatus
*status
)
443 struct b43legacy_dfsentry
*e
= dev
->dfsentry
;
444 struct b43legacy_txstatus_log
*log
;
445 struct b43legacy_txstatus
*cur
;
451 B43legacy_WARN_ON(!irqs_disabled());
452 spin_lock(&log
->lock
);
454 if (i
== B43legacy_NR_LOGGED_TXSTATUS
)
457 cur
= &(log
->log
[i
]);
458 memcpy(cur
, status
, sizeof(*cur
));
459 spin_unlock(&log
->lock
);
462 void b43legacy_debugfs_init(void)
464 rootdir
= debugfs_create_dir(KBUILD_MODNAME
, NULL
);
467 void b43legacy_debugfs_exit(void)
469 debugfs_remove(rootdir
);