162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci  Broadcom B43 wireless driver
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci  debugfs driver debugging code
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci  Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci*/
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/fs.h>
1462306a36Sopenharmony_ci#include <linux/debugfs.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/netdevice.h>
1762306a36Sopenharmony_ci#include <linux/pci.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "b43.h"
2162306a36Sopenharmony_ci#include "main.h"
2262306a36Sopenharmony_ci#include "debugfs.h"
2362306a36Sopenharmony_ci#include "dma.h"
2462306a36Sopenharmony_ci#include "xmit.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* The root directory. */
2862306a36Sopenharmony_cistatic struct dentry *rootdir;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct b43_debugfs_fops {
3162306a36Sopenharmony_ci	ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize);
3262306a36Sopenharmony_ci	int (*write)(struct b43_wldev *dev, const char *buf, size_t count);
3362306a36Sopenharmony_ci	struct file_operations fops;
3462306a36Sopenharmony_ci	/* Offset of struct b43_dfs_file in struct b43_dfsentry */
3562306a36Sopenharmony_ci	size_t file_struct_offset;
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic inline
3962306a36Sopenharmony_cistruct b43_dfs_file *fops_to_dfs_file(struct b43_wldev *dev,
4062306a36Sopenharmony_ci				      const struct b43_debugfs_fops *dfops)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	void *p;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	p = dev->dfsentry;
4562306a36Sopenharmony_ci	p += dfops->file_struct_offset;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return p;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define fappend(fmt, x...)	\
5262306a36Sopenharmony_ci	do {							\
5362306a36Sopenharmony_ci		if (bufsize - count)				\
5462306a36Sopenharmony_ci			count += scnprintf(buf + count,		\
5562306a36Sopenharmony_ci					  bufsize - count,	\
5662306a36Sopenharmony_ci					  fmt , ##x);		\
5762306a36Sopenharmony_ci		else						\
5862306a36Sopenharmony_ci			printk(KERN_ERR "b43: fappend overflow\n"); \
5962306a36Sopenharmony_ci	} while (0)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* The biggest address values for SHM access from the debugfs files. */
6362306a36Sopenharmony_ci#define B43_MAX_SHM_ROUTING	4
6462306a36Sopenharmony_ci#define B43_MAX_SHM_ADDR	0xFFFF
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic ssize_t shm16read__read_file(struct b43_wldev *dev,
6762306a36Sopenharmony_ci				    char *buf, size_t bufsize)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	ssize_t count = 0;
7062306a36Sopenharmony_ci	unsigned int routing, addr;
7162306a36Sopenharmony_ci	u16 val;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	routing = dev->dfsentry->shm16read_routing_next;
7462306a36Sopenharmony_ci	addr = dev->dfsentry->shm16read_addr_next;
7562306a36Sopenharmony_ci	if ((routing > B43_MAX_SHM_ROUTING) ||
7662306a36Sopenharmony_ci	    (addr > B43_MAX_SHM_ADDR))
7762306a36Sopenharmony_ci		return -EDESTADDRREQ;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	val = b43_shm_read16(dev, routing, addr);
8062306a36Sopenharmony_ci	fappend("0x%04X\n", val);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return count;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int shm16read__write_file(struct b43_wldev *dev,
8662306a36Sopenharmony_ci				 const char *buf, size_t count)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	unsigned int routing, addr;
8962306a36Sopenharmony_ci	int res;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X", &routing, &addr);
9262306a36Sopenharmony_ci	if (res != 2)
9362306a36Sopenharmony_ci		return -EINVAL;
9462306a36Sopenharmony_ci	if (routing > B43_MAX_SHM_ROUTING)
9562306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
9662306a36Sopenharmony_ci	if (addr > B43_MAX_SHM_ADDR)
9762306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
9862306a36Sopenharmony_ci	if (routing == B43_SHM_SHARED) {
9962306a36Sopenharmony_ci		if ((addr % 2) != 0)
10062306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	dev->dfsentry->shm16read_routing_next = routing;
10462306a36Sopenharmony_ci	dev->dfsentry->shm16read_addr_next = addr;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int shm16write__write_file(struct b43_wldev *dev,
11062306a36Sopenharmony_ci				  const char *buf, size_t count)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	unsigned int routing, addr, mask, set;
11362306a36Sopenharmony_ci	u16 val;
11462306a36Sopenharmony_ci	int res;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
11762306a36Sopenharmony_ci		     &routing, &addr, &mask, &set);
11862306a36Sopenharmony_ci	if (res != 4)
11962306a36Sopenharmony_ci		return -EINVAL;
12062306a36Sopenharmony_ci	if (routing > B43_MAX_SHM_ROUTING)
12162306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
12262306a36Sopenharmony_ci	if (addr > B43_MAX_SHM_ADDR)
12362306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
12462306a36Sopenharmony_ci	if (routing == B43_SHM_SHARED) {
12562306a36Sopenharmony_ci		if ((addr % 2) != 0)
12662306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci	if ((mask > 0xFFFF) || (set > 0xFFFF))
12962306a36Sopenharmony_ci		return -E2BIG;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (mask == 0)
13262306a36Sopenharmony_ci		val = 0;
13362306a36Sopenharmony_ci	else
13462306a36Sopenharmony_ci		val = b43_shm_read16(dev, routing, addr);
13562306a36Sopenharmony_ci	val &= mask;
13662306a36Sopenharmony_ci	val |= set;
13762306a36Sopenharmony_ci	b43_shm_write16(dev, routing, addr, val);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic ssize_t shm32read__read_file(struct b43_wldev *dev,
14362306a36Sopenharmony_ci				    char *buf, size_t bufsize)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	ssize_t count = 0;
14662306a36Sopenharmony_ci	unsigned int routing, addr;
14762306a36Sopenharmony_ci	u32 val;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	routing = dev->dfsentry->shm32read_routing_next;
15062306a36Sopenharmony_ci	addr = dev->dfsentry->shm32read_addr_next;
15162306a36Sopenharmony_ci	if ((routing > B43_MAX_SHM_ROUTING) ||
15262306a36Sopenharmony_ci	    (addr > B43_MAX_SHM_ADDR))
15362306a36Sopenharmony_ci		return -EDESTADDRREQ;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	val = b43_shm_read32(dev, routing, addr);
15662306a36Sopenharmony_ci	fappend("0x%08X\n", val);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return count;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int shm32read__write_file(struct b43_wldev *dev,
16262306a36Sopenharmony_ci				 const char *buf, size_t count)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	unsigned int routing, addr;
16562306a36Sopenharmony_ci	int res;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X", &routing, &addr);
16862306a36Sopenharmony_ci	if (res != 2)
16962306a36Sopenharmony_ci		return -EINVAL;
17062306a36Sopenharmony_ci	if (routing > B43_MAX_SHM_ROUTING)
17162306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
17262306a36Sopenharmony_ci	if (addr > B43_MAX_SHM_ADDR)
17362306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
17462306a36Sopenharmony_ci	if (routing == B43_SHM_SHARED) {
17562306a36Sopenharmony_ci		if ((addr % 2) != 0)
17662306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	dev->dfsentry->shm32read_routing_next = routing;
18062306a36Sopenharmony_ci	dev->dfsentry->shm32read_addr_next = addr;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int shm32write__write_file(struct b43_wldev *dev,
18662306a36Sopenharmony_ci				  const char *buf, size_t count)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	unsigned int routing, addr, mask, set;
18962306a36Sopenharmony_ci	u32 val;
19062306a36Sopenharmony_ci	int res;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
19362306a36Sopenharmony_ci		     &routing, &addr, &mask, &set);
19462306a36Sopenharmony_ci	if (res != 4)
19562306a36Sopenharmony_ci		return -EINVAL;
19662306a36Sopenharmony_ci	if (routing > B43_MAX_SHM_ROUTING)
19762306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
19862306a36Sopenharmony_ci	if (addr > B43_MAX_SHM_ADDR)
19962306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
20062306a36Sopenharmony_ci	if (routing == B43_SHM_SHARED) {
20162306a36Sopenharmony_ci		if ((addr % 2) != 0)
20262306a36Sopenharmony_ci			return -EADDRNOTAVAIL;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci	if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))
20562306a36Sopenharmony_ci		return -E2BIG;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (mask == 0)
20862306a36Sopenharmony_ci		val = 0;
20962306a36Sopenharmony_ci	else
21062306a36Sopenharmony_ci		val = b43_shm_read32(dev, routing, addr);
21162306a36Sopenharmony_ci	val &= mask;
21262306a36Sopenharmony_ci	val |= set;
21362306a36Sopenharmony_ci	b43_shm_write32(dev, routing, addr, val);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/* The biggest MMIO address that we allow access to from the debugfs files. */
21962306a36Sopenharmony_ci#define B43_MAX_MMIO_ACCESS	(0xF00 - 1)
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic ssize_t mmio16read__read_file(struct b43_wldev *dev,
22262306a36Sopenharmony_ci				     char *buf, size_t bufsize)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	ssize_t count = 0;
22562306a36Sopenharmony_ci	unsigned int addr;
22662306a36Sopenharmony_ci	u16 val;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	addr = dev->dfsentry->mmio16read_next;
22962306a36Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
23062306a36Sopenharmony_ci		return -EDESTADDRREQ;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	val = b43_read16(dev, addr);
23362306a36Sopenharmony_ci	fappend("0x%04X\n", val);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return count;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int mmio16read__write_file(struct b43_wldev *dev,
23962306a36Sopenharmony_ci				  const char *buf, size_t count)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	unsigned int addr;
24262306a36Sopenharmony_ci	int res;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	res = sscanf(buf, "0x%X", &addr);
24562306a36Sopenharmony_ci	if (res != 1)
24662306a36Sopenharmony_ci		return -EINVAL;
24762306a36Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
24862306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
24962306a36Sopenharmony_ci	if ((addr % 2) != 0)
25062306a36Sopenharmony_ci		return -EINVAL;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	dev->dfsentry->mmio16read_next = addr;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int mmio16write__write_file(struct b43_wldev *dev,
25862306a36Sopenharmony_ci				   const char *buf, size_t count)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	unsigned int addr, mask, set;
26162306a36Sopenharmony_ci	int res;
26262306a36Sopenharmony_ci	u16 val;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);
26562306a36Sopenharmony_ci	if (res != 3)
26662306a36Sopenharmony_ci		return -EINVAL;
26762306a36Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
26862306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
26962306a36Sopenharmony_ci	if ((mask > 0xFFFF) || (set > 0xFFFF))
27062306a36Sopenharmony_ci		return -E2BIG;
27162306a36Sopenharmony_ci	if ((addr % 2) != 0)
27262306a36Sopenharmony_ci		return -EINVAL;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (mask == 0)
27562306a36Sopenharmony_ci		val = 0;
27662306a36Sopenharmony_ci	else
27762306a36Sopenharmony_ci		val = b43_read16(dev, addr);
27862306a36Sopenharmony_ci	val &= mask;
27962306a36Sopenharmony_ci	val |= set;
28062306a36Sopenharmony_ci	b43_write16(dev, addr, val);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return 0;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic ssize_t mmio32read__read_file(struct b43_wldev *dev,
28662306a36Sopenharmony_ci				     char *buf, size_t bufsize)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	ssize_t count = 0;
28962306a36Sopenharmony_ci	unsigned int addr;
29062306a36Sopenharmony_ci	u32 val;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	addr = dev->dfsentry->mmio32read_next;
29362306a36Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
29462306a36Sopenharmony_ci		return -EDESTADDRREQ;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	val = b43_read32(dev, addr);
29762306a36Sopenharmony_ci	fappend("0x%08X\n", val);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return count;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int mmio32read__write_file(struct b43_wldev *dev,
30362306a36Sopenharmony_ci				  const char *buf, size_t count)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	unsigned int addr;
30662306a36Sopenharmony_ci	int res;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	res = sscanf(buf, "0x%X", &addr);
30962306a36Sopenharmony_ci	if (res != 1)
31062306a36Sopenharmony_ci		return -EINVAL;
31162306a36Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
31262306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
31362306a36Sopenharmony_ci	if ((addr % 4) != 0)
31462306a36Sopenharmony_ci		return -EINVAL;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	dev->dfsentry->mmio32read_next = addr;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int mmio32write__write_file(struct b43_wldev *dev,
32262306a36Sopenharmony_ci				   const char *buf, size_t count)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	unsigned int addr, mask, set;
32562306a36Sopenharmony_ci	int res;
32662306a36Sopenharmony_ci	u32 val;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);
32962306a36Sopenharmony_ci	if (res != 3)
33062306a36Sopenharmony_ci		return -EINVAL;
33162306a36Sopenharmony_ci	if (addr > B43_MAX_MMIO_ACCESS)
33262306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
33362306a36Sopenharmony_ci	if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))
33462306a36Sopenharmony_ci		return -E2BIG;
33562306a36Sopenharmony_ci	if ((addr % 4) != 0)
33662306a36Sopenharmony_ci		return -EINVAL;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (mask == 0)
33962306a36Sopenharmony_ci		val = 0;
34062306a36Sopenharmony_ci	else
34162306a36Sopenharmony_ci		val = b43_read32(dev, addr);
34262306a36Sopenharmony_ci	val &= mask;
34362306a36Sopenharmony_ci	val |= set;
34462306a36Sopenharmony_ci	b43_write32(dev, addr, val);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	return 0;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic ssize_t txstat_read_file(struct b43_wldev *dev,
35062306a36Sopenharmony_ci				char *buf, size_t bufsize)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct b43_txstatus_log *log = &dev->dfsentry->txstatlog;
35362306a36Sopenharmony_ci	ssize_t count = 0;
35462306a36Sopenharmony_ci	int i, idx;
35562306a36Sopenharmony_ci	struct b43_txstatus *stat;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (log->end < 0) {
35862306a36Sopenharmony_ci		fappend("Nothing transmitted, yet\n");
35962306a36Sopenharmony_ci		goto out;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci	fappend("b43 TX status reports:\n\n"
36262306a36Sopenharmony_ci		"index | cookie | seq | phy_stat | frame_count | "
36362306a36Sopenharmony_ci		"rts_count | supp_reason | pm_indicated | "
36462306a36Sopenharmony_ci		"intermediate | for_ampdu | acked\n" "---\n");
36562306a36Sopenharmony_ci	i = log->end + 1;
36662306a36Sopenharmony_ci	idx = 0;
36762306a36Sopenharmony_ci	while (1) {
36862306a36Sopenharmony_ci		if (i == B43_NR_LOGGED_TXSTATUS)
36962306a36Sopenharmony_ci			i = 0;
37062306a36Sopenharmony_ci		stat = &(log->log[i]);
37162306a36Sopenharmony_ci		if (stat->cookie) {
37262306a36Sopenharmony_ci			fappend("%03d | "
37362306a36Sopenharmony_ci				"0x%04X | 0x%04X | 0x%02X | "
37462306a36Sopenharmony_ci				"0x%X | 0x%X | "
37562306a36Sopenharmony_ci				"%u | %u | "
37662306a36Sopenharmony_ci				"%u | %u | %u\n",
37762306a36Sopenharmony_ci				idx,
37862306a36Sopenharmony_ci				stat->cookie, stat->seq, stat->phy_stat,
37962306a36Sopenharmony_ci				stat->frame_count, stat->rts_count,
38062306a36Sopenharmony_ci				stat->supp_reason, stat->pm_indicated,
38162306a36Sopenharmony_ci				stat->intermediate, stat->for_ampdu,
38262306a36Sopenharmony_ci				stat->acked);
38362306a36Sopenharmony_ci			idx++;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci		if (i == log->end)
38662306a36Sopenharmony_ci			break;
38762306a36Sopenharmony_ci		i++;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ciout:
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return count;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic int restart_write_file(struct b43_wldev *dev,
39562306a36Sopenharmony_ci			      const char *buf, size_t count)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	int err = 0;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (count > 0 && buf[0] == '1') {
40062306a36Sopenharmony_ci		b43_controller_restart(dev, "manually restarted");
40162306a36Sopenharmony_ci	} else
40262306a36Sopenharmony_ci		err = -EINVAL;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return err;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic unsigned long calc_expire_secs(unsigned long now,
40862306a36Sopenharmony_ci				      unsigned long time,
40962306a36Sopenharmony_ci				      unsigned long expire)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	expire = time + expire;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (time_after(now, expire))
41462306a36Sopenharmony_ci		return 0; /* expired */
41562306a36Sopenharmony_ci	if (expire < now) {
41662306a36Sopenharmony_ci		/* jiffies wrapped */
41762306a36Sopenharmony_ci		expire -= MAX_JIFFY_OFFSET;
41862306a36Sopenharmony_ci		now -= MAX_JIFFY_OFFSET;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci	B43_WARN_ON(expire < now);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	return (expire - now) / HZ;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic ssize_t loctls_read_file(struct b43_wldev *dev,
42662306a36Sopenharmony_ci				char *buf, size_t bufsize)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	ssize_t count = 0;
42962306a36Sopenharmony_ci	struct b43_txpower_lo_control *lo;
43062306a36Sopenharmony_ci	int i, err = 0;
43162306a36Sopenharmony_ci	struct b43_lo_calib *cal;
43262306a36Sopenharmony_ci	unsigned long now = jiffies;
43362306a36Sopenharmony_ci	struct b43_phy *phy = &dev->phy;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (phy->type != B43_PHYTYPE_G) {
43662306a36Sopenharmony_ci		fappend("Device is not a G-PHY\n");
43762306a36Sopenharmony_ci		err = -ENODEV;
43862306a36Sopenharmony_ci		goto out;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci	lo = phy->g->lo_control;
44162306a36Sopenharmony_ci	fappend("-- Local Oscillator calibration data --\n\n");
44262306a36Sopenharmony_ci	fappend("HW-power-control enabled: %d\n",
44362306a36Sopenharmony_ci		dev->phy.hardware_power_control);
44462306a36Sopenharmony_ci	fappend("TX Bias: 0x%02X,  TX Magn: 0x%02X  (expire in %lu sec)\n",
44562306a36Sopenharmony_ci		lo->tx_bias, lo->tx_magn,
44662306a36Sopenharmony_ci		calc_expire_secs(now, lo->txctl_measured_time,
44762306a36Sopenharmony_ci				 B43_LO_TXCTL_EXPIRE));
44862306a36Sopenharmony_ci	fappend("Power Vector: 0x%08X%08X  (expires in %lu sec)\n",
44962306a36Sopenharmony_ci		(unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32),
45062306a36Sopenharmony_ci		(unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL),
45162306a36Sopenharmony_ci		calc_expire_secs(now, lo->pwr_vec_read_time,
45262306a36Sopenharmony_ci				 B43_LO_PWRVEC_EXPIRE));
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	fappend("\nCalibrated settings:\n");
45562306a36Sopenharmony_ci	list_for_each_entry(cal, &lo->calib_list, list) {
45662306a36Sopenharmony_ci		bool active;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		active = (b43_compare_bbatt(&cal->bbatt, &phy->g->bbatt) &&
45962306a36Sopenharmony_ci			  b43_compare_rfatt(&cal->rfatt, &phy->g->rfatt));
46062306a36Sopenharmony_ci		fappend("BB(%d), RF(%d,%d)  ->  I=%d, Q=%d  "
46162306a36Sopenharmony_ci			"(expires in %lu sec)%s\n",
46262306a36Sopenharmony_ci			cal->bbatt.att,
46362306a36Sopenharmony_ci			cal->rfatt.att, cal->rfatt.with_padmix,
46462306a36Sopenharmony_ci			cal->ctl.i, cal->ctl.q,
46562306a36Sopenharmony_ci			calc_expire_secs(now, cal->calib_time,
46662306a36Sopenharmony_ci					 B43_LO_CALIB_EXPIRE),
46762306a36Sopenharmony_ci			active ? "  ACTIVE" : "");
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	fappend("\nUsed RF attenuation values:  Value(WithPadmix flag)\n");
47162306a36Sopenharmony_ci	for (i = 0; i < lo->rfatt_list.len; i++) {
47262306a36Sopenharmony_ci		fappend("%u(%d), ",
47362306a36Sopenharmony_ci			lo->rfatt_list.list[i].att,
47462306a36Sopenharmony_ci			lo->rfatt_list.list[i].with_padmix);
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci	fappend("\n");
47762306a36Sopenharmony_ci	fappend("\nUsed Baseband attenuation values:\n");
47862306a36Sopenharmony_ci	for (i = 0; i < lo->bbatt_list.len; i++) {
47962306a36Sopenharmony_ci		fappend("%u, ",
48062306a36Sopenharmony_ci			lo->bbatt_list.list[i].att);
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	fappend("\n");
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ciout:
48562306a36Sopenharmony_ci	return err ? err : count;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci#undef fappend
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
49162306a36Sopenharmony_ci				size_t count, loff_t *ppos)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct b43_wldev *dev;
49462306a36Sopenharmony_ci	struct b43_debugfs_fops *dfops;
49562306a36Sopenharmony_ci	struct b43_dfs_file *dfile;
49662306a36Sopenharmony_ci	ssize_t ret;
49762306a36Sopenharmony_ci	char *buf;
49862306a36Sopenharmony_ci	const size_t bufsize = 1024 * 16; /* 16 kiB buffer */
49962306a36Sopenharmony_ci	const size_t buforder = get_order(bufsize);
50062306a36Sopenharmony_ci	int err = 0;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (!count)
50362306a36Sopenharmony_ci		return 0;
50462306a36Sopenharmony_ci	dev = file->private_data;
50562306a36Sopenharmony_ci	if (!dev)
50662306a36Sopenharmony_ci		return -ENODEV;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	mutex_lock(&dev->wl->mutex);
50962306a36Sopenharmony_ci	if (b43_status(dev) < B43_STAT_INITIALIZED) {
51062306a36Sopenharmony_ci		err = -ENODEV;
51162306a36Sopenharmony_ci		goto out_unlock;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	dfops = container_of(debugfs_real_fops(file),
51562306a36Sopenharmony_ci			     struct b43_debugfs_fops, fops);
51662306a36Sopenharmony_ci	if (!dfops->read) {
51762306a36Sopenharmony_ci		err = -ENOSYS;
51862306a36Sopenharmony_ci		goto out_unlock;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci	dfile = fops_to_dfs_file(dev, dfops);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (!dfile->buffer) {
52362306a36Sopenharmony_ci		buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
52462306a36Sopenharmony_ci		if (!buf) {
52562306a36Sopenharmony_ci			err = -ENOMEM;
52662306a36Sopenharmony_ci			goto out_unlock;
52762306a36Sopenharmony_ci		}
52862306a36Sopenharmony_ci		memset(buf, 0, bufsize);
52962306a36Sopenharmony_ci		ret = dfops->read(dev, buf, bufsize);
53062306a36Sopenharmony_ci		if (ret <= 0) {
53162306a36Sopenharmony_ci			free_pages((unsigned long)buf, buforder);
53262306a36Sopenharmony_ci			err = ret;
53362306a36Sopenharmony_ci			goto out_unlock;
53462306a36Sopenharmony_ci		}
53562306a36Sopenharmony_ci		dfile->data_len = ret;
53662306a36Sopenharmony_ci		dfile->buffer = buf;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ret = simple_read_from_buffer(userbuf, count, ppos,
54062306a36Sopenharmony_ci				      dfile->buffer,
54162306a36Sopenharmony_ci				      dfile->data_len);
54262306a36Sopenharmony_ci	if (*ppos >= dfile->data_len) {
54362306a36Sopenharmony_ci		free_pages((unsigned long)dfile->buffer, buforder);
54462306a36Sopenharmony_ci		dfile->buffer = NULL;
54562306a36Sopenharmony_ci		dfile->data_len = 0;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ciout_unlock:
54862306a36Sopenharmony_ci	mutex_unlock(&dev->wl->mutex);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return err ? err : ret;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic ssize_t b43_debugfs_write(struct file *file,
55462306a36Sopenharmony_ci				 const char __user *userbuf,
55562306a36Sopenharmony_ci				 size_t count, loff_t *ppos)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct b43_wldev *dev;
55862306a36Sopenharmony_ci	struct b43_debugfs_fops *dfops;
55962306a36Sopenharmony_ci	char *buf;
56062306a36Sopenharmony_ci	int err = 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (!count)
56362306a36Sopenharmony_ci		return 0;
56462306a36Sopenharmony_ci	if (count > PAGE_SIZE)
56562306a36Sopenharmony_ci		return -E2BIG;
56662306a36Sopenharmony_ci	dev = file->private_data;
56762306a36Sopenharmony_ci	if (!dev)
56862306a36Sopenharmony_ci		return -ENODEV;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	mutex_lock(&dev->wl->mutex);
57162306a36Sopenharmony_ci	if (b43_status(dev) < B43_STAT_INITIALIZED) {
57262306a36Sopenharmony_ci		err = -ENODEV;
57362306a36Sopenharmony_ci		goto out_unlock;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	dfops = container_of(debugfs_real_fops(file),
57762306a36Sopenharmony_ci			     struct b43_debugfs_fops, fops);
57862306a36Sopenharmony_ci	if (!dfops->write) {
57962306a36Sopenharmony_ci		err = -ENOSYS;
58062306a36Sopenharmony_ci		goto out_unlock;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	buf = (char *)get_zeroed_page(GFP_KERNEL);
58462306a36Sopenharmony_ci	if (!buf) {
58562306a36Sopenharmony_ci		err = -ENOMEM;
58662306a36Sopenharmony_ci		goto out_unlock;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci	if (copy_from_user(buf, userbuf, count)) {
58962306a36Sopenharmony_ci		err = -EFAULT;
59062306a36Sopenharmony_ci		goto out_freepage;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci	err = dfops->write(dev, buf, count);
59362306a36Sopenharmony_ci	if (err)
59462306a36Sopenharmony_ci		goto out_freepage;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ciout_freepage:
59762306a36Sopenharmony_ci	free_page((unsigned long)buf);
59862306a36Sopenharmony_ciout_unlock:
59962306a36Sopenharmony_ci	mutex_unlock(&dev->wl->mutex);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return err ? err : count;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci#define B43_DEBUGFS_FOPS(name, _read, _write)			\
60662306a36Sopenharmony_ci	static struct b43_debugfs_fops fops_##name = {		\
60762306a36Sopenharmony_ci		.read	= _read,				\
60862306a36Sopenharmony_ci		.write	= _write,				\
60962306a36Sopenharmony_ci		.fops	= {					\
61062306a36Sopenharmony_ci			.open	= simple_open,			\
61162306a36Sopenharmony_ci			.read	= b43_debugfs_read,		\
61262306a36Sopenharmony_ci			.write	= b43_debugfs_write,		\
61362306a36Sopenharmony_ci			.llseek = generic_file_llseek,		\
61462306a36Sopenharmony_ci		},						\
61562306a36Sopenharmony_ci		.file_struct_offset = offsetof(struct b43_dfsentry, \
61662306a36Sopenharmony_ci					       file_##name),	\
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ciB43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file);
62062306a36Sopenharmony_ciB43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file);
62162306a36Sopenharmony_ciB43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file);
62262306a36Sopenharmony_ciB43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file);
62362306a36Sopenharmony_ciB43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file);
62462306a36Sopenharmony_ciB43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file);
62562306a36Sopenharmony_ciB43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file);
62662306a36Sopenharmony_ciB43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file);
62762306a36Sopenharmony_ciB43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL);
62862306a36Sopenharmony_ciB43_DEBUGFS_FOPS(restart, NULL, restart_write_file);
62962306a36Sopenharmony_ciB43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cibool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	bool enabled;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
63762306a36Sopenharmony_ci	if (unlikely(enabled)) {
63862306a36Sopenharmony_ci		/* Force full debugging messages, if the user enabled
63962306a36Sopenharmony_ci		 * some dynamic debugging feature. */
64062306a36Sopenharmony_ci		b43_modparam_verbose = B43_VERBOSITY_MAX;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return enabled;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic void b43_add_dynamic_debug(struct b43_wldev *dev)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct b43_dfsentry *e = dev->dfsentry;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci#define add_dyn_dbg(name, id, initstate) do {			\
65162306a36Sopenharmony_ci	e->dyn_debug[id] = (initstate);				\
65262306a36Sopenharmony_ci	debugfs_create_bool(name, 0600, e->subdir,		\
65362306a36Sopenharmony_ci			    &(e->dyn_debug[id]));		\
65462306a36Sopenharmony_ci	} while (0)
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, false);
65762306a36Sopenharmony_ci	add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, false);
65862306a36Sopenharmony_ci	add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, false);
65962306a36Sopenharmony_ci	add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, false);
66062306a36Sopenharmony_ci	add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, false);
66162306a36Sopenharmony_ci	add_dyn_dbg("debug_lo", B43_DBG_LO, false);
66262306a36Sopenharmony_ci	add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, false);
66362306a36Sopenharmony_ci	add_dyn_dbg("debug_keys", B43_DBG_KEYS, false);
66462306a36Sopenharmony_ci	add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, false);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci#undef add_dyn_dbg
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_civoid b43_debugfs_add_device(struct b43_wldev *dev)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct b43_dfsentry *e;
67262306a36Sopenharmony_ci	struct b43_txstatus_log *log;
67362306a36Sopenharmony_ci	char devdir[16];
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	B43_WARN_ON(!dev);
67662306a36Sopenharmony_ci	e = kzalloc(sizeof(*e), GFP_KERNEL);
67762306a36Sopenharmony_ci	if (!e) {
67862306a36Sopenharmony_ci		b43err(dev->wl, "debugfs: add device OOM\n");
67962306a36Sopenharmony_ci		return;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci	e->dev = dev;
68262306a36Sopenharmony_ci	log = &e->txstatlog;
68362306a36Sopenharmony_ci	log->log = kcalloc(B43_NR_LOGGED_TXSTATUS,
68462306a36Sopenharmony_ci			   sizeof(struct b43_txstatus), GFP_KERNEL);
68562306a36Sopenharmony_ci	if (!log->log) {
68662306a36Sopenharmony_ci		b43err(dev->wl, "debugfs: add device txstatus OOM\n");
68762306a36Sopenharmony_ci		kfree(e);
68862306a36Sopenharmony_ci		return;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci	log->end = -1;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	dev->dfsentry = e;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
69562306a36Sopenharmony_ci	e->subdir = debugfs_create_dir(devdir, rootdir);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	e->mmio16read_next = 0xFFFF; /* invalid address */
69862306a36Sopenharmony_ci	e->mmio32read_next = 0xFFFF; /* invalid address */
69962306a36Sopenharmony_ci	e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */
70062306a36Sopenharmony_ci	e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */
70162306a36Sopenharmony_ci	e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */
70262306a36Sopenharmony_ci	e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci#define ADD_FILE(name, mode)	\
70562306a36Sopenharmony_ci	do {							\
70662306a36Sopenharmony_ci		debugfs_create_file(__stringify(name),		\
70762306a36Sopenharmony_ci				mode, e->subdir, dev,		\
70862306a36Sopenharmony_ci				&fops_##name.fops);		\
70962306a36Sopenharmony_ci	} while (0)
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	ADD_FILE(shm16read, 0600);
71362306a36Sopenharmony_ci	ADD_FILE(shm16write, 0200);
71462306a36Sopenharmony_ci	ADD_FILE(shm32read, 0600);
71562306a36Sopenharmony_ci	ADD_FILE(shm32write, 0200);
71662306a36Sopenharmony_ci	ADD_FILE(mmio16read, 0600);
71762306a36Sopenharmony_ci	ADD_FILE(mmio16write, 0200);
71862306a36Sopenharmony_ci	ADD_FILE(mmio32read, 0600);
71962306a36Sopenharmony_ci	ADD_FILE(mmio32write, 0200);
72062306a36Sopenharmony_ci	ADD_FILE(txstat, 0400);
72162306a36Sopenharmony_ci	ADD_FILE(restart, 0200);
72262306a36Sopenharmony_ci	ADD_FILE(loctls, 0400);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci#undef ADD_FILE
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	b43_add_dynamic_debug(dev);
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_civoid b43_debugfs_remove_device(struct b43_wldev *dev)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	struct b43_dfsentry *e;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (!dev)
73462306a36Sopenharmony_ci		return;
73562306a36Sopenharmony_ci	e = dev->dfsentry;
73662306a36Sopenharmony_ci	if (!e)
73762306a36Sopenharmony_ci		return;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	debugfs_remove(e->subdir);
74062306a36Sopenharmony_ci	kfree(e->txstatlog.log);
74162306a36Sopenharmony_ci	kfree(e);
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_civoid b43_debugfs_log_txstat(struct b43_wldev *dev,
74562306a36Sopenharmony_ci			    const struct b43_txstatus *status)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct b43_dfsentry *e = dev->dfsentry;
74862306a36Sopenharmony_ci	struct b43_txstatus_log *log;
74962306a36Sopenharmony_ci	struct b43_txstatus *cur;
75062306a36Sopenharmony_ci	int i;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (!e)
75362306a36Sopenharmony_ci		return;
75462306a36Sopenharmony_ci	log = &e->txstatlog;
75562306a36Sopenharmony_ci	i = log->end + 1;
75662306a36Sopenharmony_ci	if (i == B43_NR_LOGGED_TXSTATUS)
75762306a36Sopenharmony_ci		i = 0;
75862306a36Sopenharmony_ci	log->end = i;
75962306a36Sopenharmony_ci	cur = &(log->log[i]);
76062306a36Sopenharmony_ci	memcpy(cur, status, sizeof(*cur));
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_civoid b43_debugfs_init(void)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_civoid b43_debugfs_exit(void)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	debugfs_remove(rootdir);
77162306a36Sopenharmony_ci}
772