18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci  Broadcom B43 wireless driver
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci  debugfs driver debugging code
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci  Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci*/
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
178c2ecf20Sopenharmony_ci#include <linux/pci.h>
188c2ecf20Sopenharmony_ci#include <linux/mutex.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "b43.h"
218c2ecf20Sopenharmony_ci#include "main.h"
228c2ecf20Sopenharmony_ci#include "debugfs.h"
238c2ecf20Sopenharmony_ci#include "dma.h"
248c2ecf20Sopenharmony_ci#include "xmit.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* The root directory. */
288c2ecf20Sopenharmony_cistatic struct dentry *rootdir;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistruct b43_debugfs_fops {
318c2ecf20Sopenharmony_ci	ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize);
328c2ecf20Sopenharmony_ci	int (*write)(struct b43_wldev *dev, const char *buf, size_t count);
338c2ecf20Sopenharmony_ci	struct file_operations fops;
348c2ecf20Sopenharmony_ci	/* Offset of struct b43_dfs_file in struct b43_dfsentry */
358c2ecf20Sopenharmony_ci	size_t file_struct_offset;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic inline
398c2ecf20Sopenharmony_cistruct b43_dfs_file *fops_to_dfs_file(struct b43_wldev *dev,
408c2ecf20Sopenharmony_ci				      const struct b43_debugfs_fops *dfops)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	void *p;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	p = dev->dfsentry;
458c2ecf20Sopenharmony_ci	p += dfops->file_struct_offset;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	return p;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define fappend(fmt, x...)	\
528c2ecf20Sopenharmony_ci	do {							\
538c2ecf20Sopenharmony_ci		if (bufsize - count)				\
548c2ecf20Sopenharmony_ci			count += scnprintf(buf + count,		\
558c2ecf20Sopenharmony_ci					  bufsize - count,	\
568c2ecf20Sopenharmony_ci					  fmt , ##x);		\
578c2ecf20Sopenharmony_ci		else						\
588c2ecf20Sopenharmony_ci			printk(KERN_ERR "b43: fappend overflow\n"); \
598c2ecf20Sopenharmony_ci	} while (0)
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* The biggest address values for SHM access from the debugfs files. */
638c2ecf20Sopenharmony_ci#define B43_MAX_SHM_ROUTING	4
648c2ecf20Sopenharmony_ci#define B43_MAX_SHM_ADDR	0xFFFF
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic ssize_t shm16read__read_file(struct b43_wldev *dev,
678c2ecf20Sopenharmony_ci				    char *buf, size_t bufsize)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	ssize_t count = 0;
708c2ecf20Sopenharmony_ci	unsigned int routing, addr;
718c2ecf20Sopenharmony_ci	u16 val;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	routing = dev->dfsentry->shm16read_routing_next;
748c2ecf20Sopenharmony_ci	addr = dev->dfsentry->shm16read_addr_next;
758c2ecf20Sopenharmony_ci	if ((routing > B43_MAX_SHM_ROUTING) ||
768c2ecf20Sopenharmony_ci	    (addr > B43_MAX_SHM_ADDR))
778c2ecf20Sopenharmony_ci		return -EDESTADDRREQ;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	val = b43_shm_read16(dev, routing, addr);
808c2ecf20Sopenharmony_ci	fappend("0x%04X\n", val);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return count;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int shm16read__write_file(struct b43_wldev *dev,
868c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	unsigned int routing, addr;
898c2ecf20Sopenharmony_ci	int res;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X", &routing, &addr);
928c2ecf20Sopenharmony_ci	if (res != 2)
938c2ecf20Sopenharmony_ci		return -EINVAL;
948c2ecf20Sopenharmony_ci	if (routing > B43_MAX_SHM_ROUTING)
958c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
968c2ecf20Sopenharmony_ci	if (addr > B43_MAX_SHM_ADDR)
978c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
988c2ecf20Sopenharmony_ci	if (routing == B43_SHM_SHARED) {
998c2ecf20Sopenharmony_ci		if ((addr % 2) != 0)
1008c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	dev->dfsentry->shm16read_routing_next = routing;
1048c2ecf20Sopenharmony_ci	dev->dfsentry->shm16read_addr_next = addr;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int shm16write__write_file(struct b43_wldev *dev,
1108c2ecf20Sopenharmony_ci				  const char *buf, size_t count)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	unsigned int routing, addr, mask, set;
1138c2ecf20Sopenharmony_ci	u16 val;
1148c2ecf20Sopenharmony_ci	int res;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
1178c2ecf20Sopenharmony_ci		     &routing, &addr, &mask, &set);
1188c2ecf20Sopenharmony_ci	if (res != 4)
1198c2ecf20Sopenharmony_ci		return -EINVAL;
1208c2ecf20Sopenharmony_ci	if (routing > B43_MAX_SHM_ROUTING)
1218c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
1228c2ecf20Sopenharmony_ci	if (addr > B43_MAX_SHM_ADDR)
1238c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
1248c2ecf20Sopenharmony_ci	if (routing == B43_SHM_SHARED) {
1258c2ecf20Sopenharmony_ci		if ((addr % 2) != 0)
1268c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci	if ((mask > 0xFFFF) || (set > 0xFFFF))
1298c2ecf20Sopenharmony_ci		return -E2BIG;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (mask == 0)
1328c2ecf20Sopenharmony_ci		val = 0;
1338c2ecf20Sopenharmony_ci	else
1348c2ecf20Sopenharmony_ci		val = b43_shm_read16(dev, routing, addr);
1358c2ecf20Sopenharmony_ci	val &= mask;
1368c2ecf20Sopenharmony_ci	val |= set;
1378c2ecf20Sopenharmony_ci	b43_shm_write16(dev, routing, addr, val);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return 0;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic ssize_t shm32read__read_file(struct b43_wldev *dev,
1438c2ecf20Sopenharmony_ci				    char *buf, size_t bufsize)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	ssize_t count = 0;
1468c2ecf20Sopenharmony_ci	unsigned int routing, addr;
1478c2ecf20Sopenharmony_ci	u32 val;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	routing = dev->dfsentry->shm32read_routing_next;
1508c2ecf20Sopenharmony_ci	addr = dev->dfsentry->shm32read_addr_next;
1518c2ecf20Sopenharmony_ci	if ((routing > B43_MAX_SHM_ROUTING) ||
1528c2ecf20Sopenharmony_ci	    (addr > B43_MAX_SHM_ADDR))
1538c2ecf20Sopenharmony_ci		return -EDESTADDRREQ;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	val = b43_shm_read32(dev, routing, addr);
1568c2ecf20Sopenharmony_ci	fappend("0x%08X\n", val);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return count;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic int shm32read__write_file(struct b43_wldev *dev,
1628c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	unsigned int routing, addr;
1658c2ecf20Sopenharmony_ci	int res;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X", &routing, &addr);
1688c2ecf20Sopenharmony_ci	if (res != 2)
1698c2ecf20Sopenharmony_ci		return -EINVAL;
1708c2ecf20Sopenharmony_ci	if (routing > B43_MAX_SHM_ROUTING)
1718c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
1728c2ecf20Sopenharmony_ci	if (addr > B43_MAX_SHM_ADDR)
1738c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
1748c2ecf20Sopenharmony_ci	if (routing == B43_SHM_SHARED) {
1758c2ecf20Sopenharmony_ci		if ((addr % 2) != 0)
1768c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	dev->dfsentry->shm32read_routing_next = routing;
1808c2ecf20Sopenharmony_ci	dev->dfsentry->shm32read_addr_next = addr;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int shm32write__write_file(struct b43_wldev *dev,
1868c2ecf20Sopenharmony_ci				  const char *buf, size_t count)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	unsigned int routing, addr, mask, set;
1898c2ecf20Sopenharmony_ci	u32 val;
1908c2ecf20Sopenharmony_ci	int res;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
1938c2ecf20Sopenharmony_ci		     &routing, &addr, &mask, &set);
1948c2ecf20Sopenharmony_ci	if (res != 4)
1958c2ecf20Sopenharmony_ci		return -EINVAL;
1968c2ecf20Sopenharmony_ci	if (routing > B43_MAX_SHM_ROUTING)
1978c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
1988c2ecf20Sopenharmony_ci	if (addr > B43_MAX_SHM_ADDR)
1998c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
2008c2ecf20Sopenharmony_ci	if (routing == B43_SHM_SHARED) {
2018c2ecf20Sopenharmony_ci		if ((addr % 2) != 0)
2028c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci	if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))
2058c2ecf20Sopenharmony_ci		return -E2BIG;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (mask == 0)
2088c2ecf20Sopenharmony_ci		val = 0;
2098c2ecf20Sopenharmony_ci	else
2108c2ecf20Sopenharmony_ci		val = b43_shm_read32(dev, routing, addr);
2118c2ecf20Sopenharmony_ci	val &= mask;
2128c2ecf20Sopenharmony_ci	val |= set;
2138c2ecf20Sopenharmony_ci	b43_shm_write32(dev, routing, addr, val);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/* The biggest MMIO address that we allow access to from the debugfs files. */
2198c2ecf20Sopenharmony_ci#define B43_MAX_MMIO_ACCESS	(0xF00 - 1)
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic ssize_t mmio16read__read_file(struct b43_wldev *dev,
2228c2ecf20Sopenharmony_ci				     char *buf, size_t bufsize)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	ssize_t count = 0;
2258c2ecf20Sopenharmony_ci	unsigned int addr;
2268c2ecf20Sopenharmony_ci	u16 val;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	addr = dev->dfsentry->mmio16read_next;
2298c2ecf20Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
2308c2ecf20Sopenharmony_ci		return -EDESTADDRREQ;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	val = b43_read16(dev, addr);
2338c2ecf20Sopenharmony_ci	fappend("0x%04X\n", val);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return count;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int mmio16read__write_file(struct b43_wldev *dev,
2398c2ecf20Sopenharmony_ci				  const char *buf, size_t count)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	unsigned int addr;
2428c2ecf20Sopenharmony_ci	int res;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	res = sscanf(buf, "0x%X", &addr);
2458c2ecf20Sopenharmony_ci	if (res != 1)
2468c2ecf20Sopenharmony_ci		return -EINVAL;
2478c2ecf20Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
2488c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
2498c2ecf20Sopenharmony_ci	if ((addr % 2) != 0)
2508c2ecf20Sopenharmony_ci		return -EINVAL;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	dev->dfsentry->mmio16read_next = addr;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return 0;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int mmio16write__write_file(struct b43_wldev *dev,
2588c2ecf20Sopenharmony_ci				   const char *buf, size_t count)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	unsigned int addr, mask, set;
2618c2ecf20Sopenharmony_ci	int res;
2628c2ecf20Sopenharmony_ci	u16 val;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);
2658c2ecf20Sopenharmony_ci	if (res != 3)
2668c2ecf20Sopenharmony_ci		return -EINVAL;
2678c2ecf20Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
2688c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
2698c2ecf20Sopenharmony_ci	if ((mask > 0xFFFF) || (set > 0xFFFF))
2708c2ecf20Sopenharmony_ci		return -E2BIG;
2718c2ecf20Sopenharmony_ci	if ((addr % 2) != 0)
2728c2ecf20Sopenharmony_ci		return -EINVAL;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (mask == 0)
2758c2ecf20Sopenharmony_ci		val = 0;
2768c2ecf20Sopenharmony_ci	else
2778c2ecf20Sopenharmony_ci		val = b43_read16(dev, addr);
2788c2ecf20Sopenharmony_ci	val &= mask;
2798c2ecf20Sopenharmony_ci	val |= set;
2808c2ecf20Sopenharmony_ci	b43_write16(dev, addr, val);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic ssize_t mmio32read__read_file(struct b43_wldev *dev,
2868c2ecf20Sopenharmony_ci				     char *buf, size_t bufsize)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	ssize_t count = 0;
2898c2ecf20Sopenharmony_ci	unsigned int addr;
2908c2ecf20Sopenharmony_ci	u32 val;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	addr = dev->dfsentry->mmio32read_next;
2938c2ecf20Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
2948c2ecf20Sopenharmony_ci		return -EDESTADDRREQ;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	val = b43_read32(dev, addr);
2978c2ecf20Sopenharmony_ci	fappend("0x%08X\n", val);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return count;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int mmio32read__write_file(struct b43_wldev *dev,
3038c2ecf20Sopenharmony_ci				  const char *buf, size_t count)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	unsigned int addr;
3068c2ecf20Sopenharmony_ci	int res;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	res = sscanf(buf, "0x%X", &addr);
3098c2ecf20Sopenharmony_ci	if (res != 1)
3108c2ecf20Sopenharmony_ci		return -EINVAL;
3118c2ecf20Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
3128c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
3138c2ecf20Sopenharmony_ci	if ((addr % 4) != 0)
3148c2ecf20Sopenharmony_ci		return -EINVAL;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	dev->dfsentry->mmio32read_next = addr;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int mmio32write__write_file(struct b43_wldev *dev,
3228c2ecf20Sopenharmony_ci				   const char *buf, size_t count)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	unsigned int addr, mask, set;
3258c2ecf20Sopenharmony_ci	int res;
3268c2ecf20Sopenharmony_ci	u32 val;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);
3298c2ecf20Sopenharmony_ci	if (res != 3)
3308c2ecf20Sopenharmony_ci		return -EINVAL;
3318c2ecf20Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
3328c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
3338c2ecf20Sopenharmony_ci	if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))
3348c2ecf20Sopenharmony_ci		return -E2BIG;
3358c2ecf20Sopenharmony_ci	if ((addr % 4) != 0)
3368c2ecf20Sopenharmony_ci		return -EINVAL;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (mask == 0)
3398c2ecf20Sopenharmony_ci		val = 0;
3408c2ecf20Sopenharmony_ci	else
3418c2ecf20Sopenharmony_ci		val = b43_read32(dev, addr);
3428c2ecf20Sopenharmony_ci	val &= mask;
3438c2ecf20Sopenharmony_ci	val |= set;
3448c2ecf20Sopenharmony_ci	b43_write32(dev, addr, val);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic ssize_t txstat_read_file(struct b43_wldev *dev,
3508c2ecf20Sopenharmony_ci				char *buf, size_t bufsize)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct b43_txstatus_log *log = &dev->dfsentry->txstatlog;
3538c2ecf20Sopenharmony_ci	ssize_t count = 0;
3548c2ecf20Sopenharmony_ci	int i, idx;
3558c2ecf20Sopenharmony_ci	struct b43_txstatus *stat;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (log->end < 0) {
3588c2ecf20Sopenharmony_ci		fappend("Nothing transmitted, yet\n");
3598c2ecf20Sopenharmony_ci		goto out;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci	fappend("b43 TX status reports:\n\n"
3628c2ecf20Sopenharmony_ci		"index | cookie | seq | phy_stat | frame_count | "
3638c2ecf20Sopenharmony_ci		"rts_count | supp_reason | pm_indicated | "
3648c2ecf20Sopenharmony_ci		"intermediate | for_ampdu | acked\n" "---\n");
3658c2ecf20Sopenharmony_ci	i = log->end + 1;
3668c2ecf20Sopenharmony_ci	idx = 0;
3678c2ecf20Sopenharmony_ci	while (1) {
3688c2ecf20Sopenharmony_ci		if (i == B43_NR_LOGGED_TXSTATUS)
3698c2ecf20Sopenharmony_ci			i = 0;
3708c2ecf20Sopenharmony_ci		stat = &(log->log[i]);
3718c2ecf20Sopenharmony_ci		if (stat->cookie) {
3728c2ecf20Sopenharmony_ci			fappend("%03d | "
3738c2ecf20Sopenharmony_ci				"0x%04X | 0x%04X | 0x%02X | "
3748c2ecf20Sopenharmony_ci				"0x%X | 0x%X | "
3758c2ecf20Sopenharmony_ci				"%u | %u | "
3768c2ecf20Sopenharmony_ci				"%u | %u | %u\n",
3778c2ecf20Sopenharmony_ci				idx,
3788c2ecf20Sopenharmony_ci				stat->cookie, stat->seq, stat->phy_stat,
3798c2ecf20Sopenharmony_ci				stat->frame_count, stat->rts_count,
3808c2ecf20Sopenharmony_ci				stat->supp_reason, stat->pm_indicated,
3818c2ecf20Sopenharmony_ci				stat->intermediate, stat->for_ampdu,
3828c2ecf20Sopenharmony_ci				stat->acked);
3838c2ecf20Sopenharmony_ci			idx++;
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci		if (i == log->end)
3868c2ecf20Sopenharmony_ci			break;
3878c2ecf20Sopenharmony_ci		i++;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ciout:
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return count;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int restart_write_file(struct b43_wldev *dev,
3958c2ecf20Sopenharmony_ci			      const char *buf, size_t count)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	int err = 0;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	if (count > 0 && buf[0] == '1') {
4008c2ecf20Sopenharmony_ci		b43_controller_restart(dev, "manually restarted");
4018c2ecf20Sopenharmony_ci	} else
4028c2ecf20Sopenharmony_ci		err = -EINVAL;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return err;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic unsigned long calc_expire_secs(unsigned long now,
4088c2ecf20Sopenharmony_ci				      unsigned long time,
4098c2ecf20Sopenharmony_ci				      unsigned long expire)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	expire = time + expire;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (time_after(now, expire))
4148c2ecf20Sopenharmony_ci		return 0; /* expired */
4158c2ecf20Sopenharmony_ci	if (expire < now) {
4168c2ecf20Sopenharmony_ci		/* jiffies wrapped */
4178c2ecf20Sopenharmony_ci		expire -= MAX_JIFFY_OFFSET;
4188c2ecf20Sopenharmony_ci		now -= MAX_JIFFY_OFFSET;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci	B43_WARN_ON(expire < now);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return (expire - now) / HZ;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic ssize_t loctls_read_file(struct b43_wldev *dev,
4268c2ecf20Sopenharmony_ci				char *buf, size_t bufsize)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	ssize_t count = 0;
4298c2ecf20Sopenharmony_ci	struct b43_txpower_lo_control *lo;
4308c2ecf20Sopenharmony_ci	int i, err = 0;
4318c2ecf20Sopenharmony_ci	struct b43_lo_calib *cal;
4328c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
4338c2ecf20Sopenharmony_ci	struct b43_phy *phy = &dev->phy;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (phy->type != B43_PHYTYPE_G) {
4368c2ecf20Sopenharmony_ci		fappend("Device is not a G-PHY\n");
4378c2ecf20Sopenharmony_ci		err = -ENODEV;
4388c2ecf20Sopenharmony_ci		goto out;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci	lo = phy->g->lo_control;
4418c2ecf20Sopenharmony_ci	fappend("-- Local Oscillator calibration data --\n\n");
4428c2ecf20Sopenharmony_ci	fappend("HW-power-control enabled: %d\n",
4438c2ecf20Sopenharmony_ci		dev->phy.hardware_power_control);
4448c2ecf20Sopenharmony_ci	fappend("TX Bias: 0x%02X,  TX Magn: 0x%02X  (expire in %lu sec)\n",
4458c2ecf20Sopenharmony_ci		lo->tx_bias, lo->tx_magn,
4468c2ecf20Sopenharmony_ci		calc_expire_secs(now, lo->txctl_measured_time,
4478c2ecf20Sopenharmony_ci				 B43_LO_TXCTL_EXPIRE));
4488c2ecf20Sopenharmony_ci	fappend("Power Vector: 0x%08X%08X  (expires in %lu sec)\n",
4498c2ecf20Sopenharmony_ci		(unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32),
4508c2ecf20Sopenharmony_ci		(unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL),
4518c2ecf20Sopenharmony_ci		calc_expire_secs(now, lo->pwr_vec_read_time,
4528c2ecf20Sopenharmony_ci				 B43_LO_PWRVEC_EXPIRE));
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	fappend("\nCalibrated settings:\n");
4558c2ecf20Sopenharmony_ci	list_for_each_entry(cal, &lo->calib_list, list) {
4568c2ecf20Sopenharmony_ci		bool active;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		active = (b43_compare_bbatt(&cal->bbatt, &phy->g->bbatt) &&
4598c2ecf20Sopenharmony_ci			  b43_compare_rfatt(&cal->rfatt, &phy->g->rfatt));
4608c2ecf20Sopenharmony_ci		fappend("BB(%d), RF(%d,%d)  ->  I=%d, Q=%d  "
4618c2ecf20Sopenharmony_ci			"(expires in %lu sec)%s\n",
4628c2ecf20Sopenharmony_ci			cal->bbatt.att,
4638c2ecf20Sopenharmony_ci			cal->rfatt.att, cal->rfatt.with_padmix,
4648c2ecf20Sopenharmony_ci			cal->ctl.i, cal->ctl.q,
4658c2ecf20Sopenharmony_ci			calc_expire_secs(now, cal->calib_time,
4668c2ecf20Sopenharmony_ci					 B43_LO_CALIB_EXPIRE),
4678c2ecf20Sopenharmony_ci			active ? "  ACTIVE" : "");
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	fappend("\nUsed RF attenuation values:  Value(WithPadmix flag)\n");
4718c2ecf20Sopenharmony_ci	for (i = 0; i < lo->rfatt_list.len; i++) {
4728c2ecf20Sopenharmony_ci		fappend("%u(%d), ",
4738c2ecf20Sopenharmony_ci			lo->rfatt_list.list[i].att,
4748c2ecf20Sopenharmony_ci			lo->rfatt_list.list[i].with_padmix);
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci	fappend("\n");
4778c2ecf20Sopenharmony_ci	fappend("\nUsed Baseband attenuation values:\n");
4788c2ecf20Sopenharmony_ci	for (i = 0; i < lo->bbatt_list.len; i++) {
4798c2ecf20Sopenharmony_ci		fappend("%u, ",
4808c2ecf20Sopenharmony_ci			lo->bbatt_list.list[i].att);
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci	fappend("\n");
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ciout:
4858c2ecf20Sopenharmony_ci	return err ? err : count;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci#undef fappend
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cistatic ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
4918c2ecf20Sopenharmony_ci				size_t count, loff_t *ppos)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct b43_wldev *dev;
4948c2ecf20Sopenharmony_ci	struct b43_debugfs_fops *dfops;
4958c2ecf20Sopenharmony_ci	struct b43_dfs_file *dfile;
4968c2ecf20Sopenharmony_ci	ssize_t ret;
4978c2ecf20Sopenharmony_ci	char *buf;
4988c2ecf20Sopenharmony_ci	const size_t bufsize = 1024 * 16; /* 16 kiB buffer */
4998c2ecf20Sopenharmony_ci	const size_t buforder = get_order(bufsize);
5008c2ecf20Sopenharmony_ci	int err = 0;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (!count)
5038c2ecf20Sopenharmony_ci		return 0;
5048c2ecf20Sopenharmony_ci	dev = file->private_data;
5058c2ecf20Sopenharmony_ci	if (!dev)
5068c2ecf20Sopenharmony_ci		return -ENODEV;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	mutex_lock(&dev->wl->mutex);
5098c2ecf20Sopenharmony_ci	if (b43_status(dev) < B43_STAT_INITIALIZED) {
5108c2ecf20Sopenharmony_ci		err = -ENODEV;
5118c2ecf20Sopenharmony_ci		goto out_unlock;
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	dfops = container_of(debugfs_real_fops(file),
5158c2ecf20Sopenharmony_ci			     struct b43_debugfs_fops, fops);
5168c2ecf20Sopenharmony_ci	if (!dfops->read) {
5178c2ecf20Sopenharmony_ci		err = -ENOSYS;
5188c2ecf20Sopenharmony_ci		goto out_unlock;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci	dfile = fops_to_dfs_file(dev, dfops);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (!dfile->buffer) {
5238c2ecf20Sopenharmony_ci		buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
5248c2ecf20Sopenharmony_ci		if (!buf) {
5258c2ecf20Sopenharmony_ci			err = -ENOMEM;
5268c2ecf20Sopenharmony_ci			goto out_unlock;
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci		memset(buf, 0, bufsize);
5298c2ecf20Sopenharmony_ci		ret = dfops->read(dev, buf, bufsize);
5308c2ecf20Sopenharmony_ci		if (ret <= 0) {
5318c2ecf20Sopenharmony_ci			free_pages((unsigned long)buf, buforder);
5328c2ecf20Sopenharmony_ci			err = ret;
5338c2ecf20Sopenharmony_ci			goto out_unlock;
5348c2ecf20Sopenharmony_ci		}
5358c2ecf20Sopenharmony_ci		dfile->data_len = ret;
5368c2ecf20Sopenharmony_ci		dfile->buffer = buf;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	ret = simple_read_from_buffer(userbuf, count, ppos,
5408c2ecf20Sopenharmony_ci				      dfile->buffer,
5418c2ecf20Sopenharmony_ci				      dfile->data_len);
5428c2ecf20Sopenharmony_ci	if (*ppos >= dfile->data_len) {
5438c2ecf20Sopenharmony_ci		free_pages((unsigned long)dfile->buffer, buforder);
5448c2ecf20Sopenharmony_ci		dfile->buffer = NULL;
5458c2ecf20Sopenharmony_ci		dfile->data_len = 0;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ciout_unlock:
5488c2ecf20Sopenharmony_ci	mutex_unlock(&dev->wl->mutex);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return err ? err : ret;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic ssize_t b43_debugfs_write(struct file *file,
5548c2ecf20Sopenharmony_ci				 const char __user *userbuf,
5558c2ecf20Sopenharmony_ci				 size_t count, loff_t *ppos)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct b43_wldev *dev;
5588c2ecf20Sopenharmony_ci	struct b43_debugfs_fops *dfops;
5598c2ecf20Sopenharmony_ci	char *buf;
5608c2ecf20Sopenharmony_ci	int err = 0;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (!count)
5638c2ecf20Sopenharmony_ci		return 0;
5648c2ecf20Sopenharmony_ci	if (count > PAGE_SIZE)
5658c2ecf20Sopenharmony_ci		return -E2BIG;
5668c2ecf20Sopenharmony_ci	dev = file->private_data;
5678c2ecf20Sopenharmony_ci	if (!dev)
5688c2ecf20Sopenharmony_ci		return -ENODEV;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	mutex_lock(&dev->wl->mutex);
5718c2ecf20Sopenharmony_ci	if (b43_status(dev) < B43_STAT_INITIALIZED) {
5728c2ecf20Sopenharmony_ci		err = -ENODEV;
5738c2ecf20Sopenharmony_ci		goto out_unlock;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	dfops = container_of(debugfs_real_fops(file),
5778c2ecf20Sopenharmony_ci			     struct b43_debugfs_fops, fops);
5788c2ecf20Sopenharmony_ci	if (!dfops->write) {
5798c2ecf20Sopenharmony_ci		err = -ENOSYS;
5808c2ecf20Sopenharmony_ci		goto out_unlock;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	buf = (char *)get_zeroed_page(GFP_KERNEL);
5848c2ecf20Sopenharmony_ci	if (!buf) {
5858c2ecf20Sopenharmony_ci		err = -ENOMEM;
5868c2ecf20Sopenharmony_ci		goto out_unlock;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci	if (copy_from_user(buf, userbuf, count)) {
5898c2ecf20Sopenharmony_ci		err = -EFAULT;
5908c2ecf20Sopenharmony_ci		goto out_freepage;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci	err = dfops->write(dev, buf, count);
5938c2ecf20Sopenharmony_ci	if (err)
5948c2ecf20Sopenharmony_ci		goto out_freepage;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ciout_freepage:
5978c2ecf20Sopenharmony_ci	free_page((unsigned long)buf);
5988c2ecf20Sopenharmony_ciout_unlock:
5998c2ecf20Sopenharmony_ci	mutex_unlock(&dev->wl->mutex);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	return err ? err : count;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci#define B43_DEBUGFS_FOPS(name, _read, _write)			\
6068c2ecf20Sopenharmony_ci	static struct b43_debugfs_fops fops_##name = {		\
6078c2ecf20Sopenharmony_ci		.read	= _read,				\
6088c2ecf20Sopenharmony_ci		.write	= _write,				\
6098c2ecf20Sopenharmony_ci		.fops	= {					\
6108c2ecf20Sopenharmony_ci			.open	= simple_open,			\
6118c2ecf20Sopenharmony_ci			.read	= b43_debugfs_read,		\
6128c2ecf20Sopenharmony_ci			.write	= b43_debugfs_write,		\
6138c2ecf20Sopenharmony_ci			.llseek = generic_file_llseek,		\
6148c2ecf20Sopenharmony_ci		},						\
6158c2ecf20Sopenharmony_ci		.file_struct_offset = offsetof(struct b43_dfsentry, \
6168c2ecf20Sopenharmony_ci					       file_##name),	\
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file);
6208c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file);
6218c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file);
6228c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file);
6238c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file);
6248c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file);
6258c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file);
6268c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file);
6278c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL);
6288c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(restart, NULL, restart_write_file);
6298c2ecf20Sopenharmony_ciB43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cibool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	bool enabled;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
6378c2ecf20Sopenharmony_ci	if (unlikely(enabled)) {
6388c2ecf20Sopenharmony_ci		/* Force full debugging messages, if the user enabled
6398c2ecf20Sopenharmony_ci		 * some dynamic debugging feature. */
6408c2ecf20Sopenharmony_ci		b43_modparam_verbose = B43_VERBOSITY_MAX;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	return enabled;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic void b43_remove_dynamic_debug(struct b43_wldev *dev)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	struct b43_dfsentry *e = dev->dfsentry;
6498c2ecf20Sopenharmony_ci	int i;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	for (i = 0; i < __B43_NR_DYNDBG; i++)
6528c2ecf20Sopenharmony_ci		debugfs_remove(e->dyn_debug_dentries[i]);
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic void b43_add_dynamic_debug(struct b43_wldev *dev)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct b43_dfsentry *e = dev->dfsentry;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci#define add_dyn_dbg(name, id, initstate) do {			\
6608c2ecf20Sopenharmony_ci	e->dyn_debug[id] = (initstate);				\
6618c2ecf20Sopenharmony_ci	e->dyn_debug_dentries[id] =				\
6628c2ecf20Sopenharmony_ci		debugfs_create_bool(name, 0600, e->subdir,	\
6638c2ecf20Sopenharmony_ci				&(e->dyn_debug[id]));		\
6648c2ecf20Sopenharmony_ci	} while (0)
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, false);
6678c2ecf20Sopenharmony_ci	add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, false);
6688c2ecf20Sopenharmony_ci	add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, false);
6698c2ecf20Sopenharmony_ci	add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, false);
6708c2ecf20Sopenharmony_ci	add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, false);
6718c2ecf20Sopenharmony_ci	add_dyn_dbg("debug_lo", B43_DBG_LO, false);
6728c2ecf20Sopenharmony_ci	add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, false);
6738c2ecf20Sopenharmony_ci	add_dyn_dbg("debug_keys", B43_DBG_KEYS, false);
6748c2ecf20Sopenharmony_ci	add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, false);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci#undef add_dyn_dbg
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_civoid b43_debugfs_add_device(struct b43_wldev *dev)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	struct b43_dfsentry *e;
6828c2ecf20Sopenharmony_ci	struct b43_txstatus_log *log;
6838c2ecf20Sopenharmony_ci	char devdir[16];
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	B43_WARN_ON(!dev);
6868c2ecf20Sopenharmony_ci	e = kzalloc(sizeof(*e), GFP_KERNEL);
6878c2ecf20Sopenharmony_ci	if (!e) {
6888c2ecf20Sopenharmony_ci		b43err(dev->wl, "debugfs: add device OOM\n");
6898c2ecf20Sopenharmony_ci		return;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci	e->dev = dev;
6928c2ecf20Sopenharmony_ci	log = &e->txstatlog;
6938c2ecf20Sopenharmony_ci	log->log = kcalloc(B43_NR_LOGGED_TXSTATUS,
6948c2ecf20Sopenharmony_ci			   sizeof(struct b43_txstatus), GFP_KERNEL);
6958c2ecf20Sopenharmony_ci	if (!log->log) {
6968c2ecf20Sopenharmony_ci		b43err(dev->wl, "debugfs: add device txstatus OOM\n");
6978c2ecf20Sopenharmony_ci		kfree(e);
6988c2ecf20Sopenharmony_ci		return;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci	log->end = -1;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	dev->dfsentry = e;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
7058c2ecf20Sopenharmony_ci	e->subdir = debugfs_create_dir(devdir, rootdir);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	e->mmio16read_next = 0xFFFF; /* invalid address */
7088c2ecf20Sopenharmony_ci	e->mmio32read_next = 0xFFFF; /* invalid address */
7098c2ecf20Sopenharmony_ci	e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */
7108c2ecf20Sopenharmony_ci	e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */
7118c2ecf20Sopenharmony_ci	e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */
7128c2ecf20Sopenharmony_ci	e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci#define ADD_FILE(name, mode)	\
7158c2ecf20Sopenharmony_ci	do {							\
7168c2ecf20Sopenharmony_ci		e->file_##name.dentry =				\
7178c2ecf20Sopenharmony_ci			debugfs_create_file(__stringify(name),	\
7188c2ecf20Sopenharmony_ci					mode, e->subdir, dev,	\
7198c2ecf20Sopenharmony_ci					&fops_##name.fops);	\
7208c2ecf20Sopenharmony_ci	} while (0)
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	ADD_FILE(shm16read, 0600);
7248c2ecf20Sopenharmony_ci	ADD_FILE(shm16write, 0200);
7258c2ecf20Sopenharmony_ci	ADD_FILE(shm32read, 0600);
7268c2ecf20Sopenharmony_ci	ADD_FILE(shm32write, 0200);
7278c2ecf20Sopenharmony_ci	ADD_FILE(mmio16read, 0600);
7288c2ecf20Sopenharmony_ci	ADD_FILE(mmio16write, 0200);
7298c2ecf20Sopenharmony_ci	ADD_FILE(mmio32read, 0600);
7308c2ecf20Sopenharmony_ci	ADD_FILE(mmio32write, 0200);
7318c2ecf20Sopenharmony_ci	ADD_FILE(txstat, 0400);
7328c2ecf20Sopenharmony_ci	ADD_FILE(restart, 0200);
7338c2ecf20Sopenharmony_ci	ADD_FILE(loctls, 0400);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci#undef ADD_FILE
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	b43_add_dynamic_debug(dev);
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_civoid b43_debugfs_remove_device(struct b43_wldev *dev)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	struct b43_dfsentry *e;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (!dev)
7458c2ecf20Sopenharmony_ci		return;
7468c2ecf20Sopenharmony_ci	e = dev->dfsentry;
7478c2ecf20Sopenharmony_ci	if (!e)
7488c2ecf20Sopenharmony_ci		return;
7498c2ecf20Sopenharmony_ci	b43_remove_dynamic_debug(dev);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	debugfs_remove(e->file_shm16read.dentry);
7528c2ecf20Sopenharmony_ci	debugfs_remove(e->file_shm16write.dentry);
7538c2ecf20Sopenharmony_ci	debugfs_remove(e->file_shm32read.dentry);
7548c2ecf20Sopenharmony_ci	debugfs_remove(e->file_shm32write.dentry);
7558c2ecf20Sopenharmony_ci	debugfs_remove(e->file_mmio16read.dentry);
7568c2ecf20Sopenharmony_ci	debugfs_remove(e->file_mmio16write.dentry);
7578c2ecf20Sopenharmony_ci	debugfs_remove(e->file_mmio32read.dentry);
7588c2ecf20Sopenharmony_ci	debugfs_remove(e->file_mmio32write.dentry);
7598c2ecf20Sopenharmony_ci	debugfs_remove(e->file_txstat.dentry);
7608c2ecf20Sopenharmony_ci	debugfs_remove(e->file_restart.dentry);
7618c2ecf20Sopenharmony_ci	debugfs_remove(e->file_loctls.dentry);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	debugfs_remove(e->subdir);
7648c2ecf20Sopenharmony_ci	kfree(e->txstatlog.log);
7658c2ecf20Sopenharmony_ci	kfree(e);
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_civoid b43_debugfs_log_txstat(struct b43_wldev *dev,
7698c2ecf20Sopenharmony_ci			    const struct b43_txstatus *status)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	struct b43_dfsentry *e = dev->dfsentry;
7728c2ecf20Sopenharmony_ci	struct b43_txstatus_log *log;
7738c2ecf20Sopenharmony_ci	struct b43_txstatus *cur;
7748c2ecf20Sopenharmony_ci	int i;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (!e)
7778c2ecf20Sopenharmony_ci		return;
7788c2ecf20Sopenharmony_ci	log = &e->txstatlog;
7798c2ecf20Sopenharmony_ci	i = log->end + 1;
7808c2ecf20Sopenharmony_ci	if (i == B43_NR_LOGGED_TXSTATUS)
7818c2ecf20Sopenharmony_ci		i = 0;
7828c2ecf20Sopenharmony_ci	log->end = i;
7838c2ecf20Sopenharmony_ci	cur = &(log->log[i]);
7848c2ecf20Sopenharmony_ci	memcpy(cur, status, sizeof(*cur));
7858c2ecf20Sopenharmony_ci}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_civoid b43_debugfs_init(void)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_civoid b43_debugfs_exit(void)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	debugfs_remove(rootdir);
7958c2ecf20Sopenharmony_ci}
796