xref: /openbmc/linux/drivers/net/wireless/broadcom/b43legacy/debugfs.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1ca47d344SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2423e3ce3SKalle Valo /*
3423e3ce3SKalle Valo 
4423e3ce3SKalle Valo   Broadcom B43legacy wireless driver
5423e3ce3SKalle Valo 
6423e3ce3SKalle Valo   debugfs driver debugging code
7423e3ce3SKalle Valo 
8423e3ce3SKalle Valo   Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
9423e3ce3SKalle Valo 
10423e3ce3SKalle Valo 
11423e3ce3SKalle Valo */
12423e3ce3SKalle Valo 
13423e3ce3SKalle Valo #include <linux/fs.h>
14423e3ce3SKalle Valo #include <linux/debugfs.h>
15423e3ce3SKalle Valo #include <linux/slab.h>
16423e3ce3SKalle Valo #include <linux/netdevice.h>
17423e3ce3SKalle Valo #include <linux/pci.h>
18423e3ce3SKalle Valo #include <linux/mutex.h>
19423e3ce3SKalle Valo 
20423e3ce3SKalle Valo #include "b43legacy.h"
21423e3ce3SKalle Valo #include "main.h"
22423e3ce3SKalle Valo #include "debugfs.h"
23423e3ce3SKalle Valo #include "dma.h"
24423e3ce3SKalle Valo #include "pio.h"
25423e3ce3SKalle Valo #include "xmit.h"
26423e3ce3SKalle Valo 
27423e3ce3SKalle Valo 
28423e3ce3SKalle Valo /* The root directory. */
29423e3ce3SKalle Valo static struct dentry *rootdir;
30423e3ce3SKalle Valo 
31423e3ce3SKalle Valo struct b43legacy_debugfs_fops {
32423e3ce3SKalle Valo 	ssize_t (*read)(struct b43legacy_wldev *dev, char *buf, size_t bufsize);
33423e3ce3SKalle Valo 	int (*write)(struct b43legacy_wldev *dev, const char *buf, size_t count);
34423e3ce3SKalle Valo 	struct file_operations fops;
35423e3ce3SKalle Valo 	/* Offset of struct b43legacy_dfs_file in struct b43legacy_dfsentry */
36423e3ce3SKalle Valo 	size_t file_struct_offset;
37423e3ce3SKalle Valo 	/* Take wl->irq_lock before calling read/write? */
38423e3ce3SKalle Valo 	bool take_irqlock;
39423e3ce3SKalle Valo };
40423e3ce3SKalle Valo 
41423e3ce3SKalle Valo static inline
fops_to_dfs_file(struct b43legacy_wldev * dev,const struct b43legacy_debugfs_fops * dfops)42423e3ce3SKalle Valo struct b43legacy_dfs_file * fops_to_dfs_file(struct b43legacy_wldev *dev,
43423e3ce3SKalle Valo 				       const struct b43legacy_debugfs_fops *dfops)
44423e3ce3SKalle Valo {
45423e3ce3SKalle Valo 	void *p;
46423e3ce3SKalle Valo 
47423e3ce3SKalle Valo 	p = dev->dfsentry;
48423e3ce3SKalle Valo 	p += dfops->file_struct_offset;
49423e3ce3SKalle Valo 
50423e3ce3SKalle Valo 	return p;
51423e3ce3SKalle Valo }
52423e3ce3SKalle Valo 
53423e3ce3SKalle Valo 
54423e3ce3SKalle Valo #define fappend(fmt, x...)	\
55423e3ce3SKalle Valo 	do {							\
56423e3ce3SKalle Valo 		if (bufsize - count)				\
57d3f8c708STakashi Iwai 			count += scnprintf(buf + count,		\
58423e3ce3SKalle Valo 					  bufsize - count,	\
59423e3ce3SKalle Valo 					  fmt , ##x);		\
60423e3ce3SKalle Valo 		else						\
61423e3ce3SKalle Valo 			printk(KERN_ERR "b43legacy: fappend overflow\n"); \
62423e3ce3SKalle Valo 	} while (0)
63423e3ce3SKalle Valo 
64423e3ce3SKalle Valo 
65423e3ce3SKalle Valo /* wl->irq_lock is locked */
tsf_read_file(struct b43legacy_wldev * dev,char * buf,size_t bufsize)66423e3ce3SKalle Valo static ssize_t tsf_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
67423e3ce3SKalle Valo {
68423e3ce3SKalle Valo 	ssize_t count = 0;
69423e3ce3SKalle Valo 	u64 tsf;
70423e3ce3SKalle Valo 
71423e3ce3SKalle Valo 	b43legacy_tsf_read(dev, &tsf);
72423e3ce3SKalle Valo 	fappend("0x%08x%08x\n",
73423e3ce3SKalle Valo 		(unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32),
74423e3ce3SKalle Valo 		(unsigned int)(tsf & 0xFFFFFFFFULL));
75423e3ce3SKalle Valo 
76423e3ce3SKalle Valo 	return count;
77423e3ce3SKalle Valo }
78423e3ce3SKalle Valo 
79423e3ce3SKalle Valo /* wl->irq_lock is locked */
tsf_write_file(struct b43legacy_wldev * dev,const char * buf,size_t count)80423e3ce3SKalle Valo static int tsf_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)
81423e3ce3SKalle Valo {
82423e3ce3SKalle Valo 	u64 tsf;
83423e3ce3SKalle Valo 
84423e3ce3SKalle Valo 	if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1)
85423e3ce3SKalle Valo 		return -EINVAL;
86423e3ce3SKalle Valo 	b43legacy_tsf_write(dev, tsf);
87423e3ce3SKalle Valo 
88423e3ce3SKalle Valo 	return 0;
89423e3ce3SKalle Valo }
90423e3ce3SKalle Valo 
91423e3ce3SKalle Valo /* wl->irq_lock is locked */
ucode_regs_read_file(struct b43legacy_wldev * dev,char * buf,size_t bufsize)92423e3ce3SKalle Valo static ssize_t ucode_regs_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
93423e3ce3SKalle Valo {
94423e3ce3SKalle Valo 	ssize_t count = 0;
95423e3ce3SKalle Valo 	int i;
96423e3ce3SKalle Valo 
97423e3ce3SKalle Valo 	for (i = 0; i < 64; i++) {
98423e3ce3SKalle Valo 		fappend("r%d = 0x%04x\n", i,
99423e3ce3SKalle Valo 			b43legacy_shm_read16(dev, B43legacy_SHM_WIRELESS, i));
100423e3ce3SKalle Valo 	}
101423e3ce3SKalle Valo 
102423e3ce3SKalle Valo 	return count;
103423e3ce3SKalle Valo }
104423e3ce3SKalle Valo 
105423e3ce3SKalle Valo /* wl->irq_lock is locked */
shm_read_file(struct b43legacy_wldev * dev,char * buf,size_t bufsize)106423e3ce3SKalle Valo static ssize_t shm_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
107423e3ce3SKalle Valo {
108423e3ce3SKalle Valo 	ssize_t count = 0;
109423e3ce3SKalle Valo 	int i;
110423e3ce3SKalle Valo 	u16 tmp;
111423e3ce3SKalle Valo 	__le16 *le16buf = (__le16 *)buf;
112423e3ce3SKalle Valo 
113423e3ce3SKalle Valo 	for (i = 0; i < 0x1000; i++) {
114423e3ce3SKalle Valo 		if (bufsize < sizeof(tmp))
115423e3ce3SKalle Valo 			break;
116423e3ce3SKalle Valo 		tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 2 * i);
117423e3ce3SKalle Valo 		le16buf[i] = cpu_to_le16(tmp);
118423e3ce3SKalle Valo 		count += sizeof(tmp);
119423e3ce3SKalle Valo 		bufsize -= sizeof(tmp);
120423e3ce3SKalle Valo 	}
121423e3ce3SKalle Valo 
122423e3ce3SKalle Valo 	return count;
123423e3ce3SKalle Valo }
124423e3ce3SKalle Valo 
txstat_read_file(struct b43legacy_wldev * dev,char * buf,size_t bufsize)125423e3ce3SKalle Valo static ssize_t txstat_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
126423e3ce3SKalle Valo {
127423e3ce3SKalle Valo 	struct b43legacy_txstatus_log *log = &dev->dfsentry->txstatlog;
128423e3ce3SKalle Valo 	ssize_t count = 0;
129423e3ce3SKalle Valo 	unsigned long flags;
130423e3ce3SKalle Valo 	int i, idx;
131423e3ce3SKalle Valo 	struct b43legacy_txstatus *stat;
132423e3ce3SKalle Valo 
133423e3ce3SKalle Valo 	spin_lock_irqsave(&log->lock, flags);
134423e3ce3SKalle Valo 	if (log->end < 0) {
135423e3ce3SKalle Valo 		fappend("Nothing transmitted, yet\n");
136423e3ce3SKalle Valo 		goto out_unlock;
137423e3ce3SKalle Valo 	}
138423e3ce3SKalle Valo 	fappend("b43legacy TX status reports:\n\n"
139423e3ce3SKalle Valo 		"index | cookie | seq | phy_stat | frame_count | "
140423e3ce3SKalle Valo 		"rts_count | supp_reason | pm_indicated | "
141423e3ce3SKalle Valo 		"intermediate | for_ampdu | acked\n" "---\n");
142423e3ce3SKalle Valo 	i = log->end + 1;
143423e3ce3SKalle Valo 	idx = 0;
144423e3ce3SKalle Valo 	while (1) {
145423e3ce3SKalle Valo 		if (i == B43legacy_NR_LOGGED_TXSTATUS)
146423e3ce3SKalle Valo 			i = 0;
147423e3ce3SKalle Valo 		stat = &(log->log[i]);
148423e3ce3SKalle Valo 		if (stat->cookie) {
149423e3ce3SKalle Valo 			fappend("%03d | "
150423e3ce3SKalle Valo 				"0x%04X | 0x%04X | 0x%02X | "
151423e3ce3SKalle Valo 				"0x%X | 0x%X | "
152423e3ce3SKalle Valo 				"%u | %u | "
153423e3ce3SKalle Valo 				"%u | %u | %u\n",
154423e3ce3SKalle Valo 				idx,
155423e3ce3SKalle Valo 				stat->cookie, stat->seq, stat->phy_stat,
156423e3ce3SKalle Valo 				stat->frame_count, stat->rts_count,
157423e3ce3SKalle Valo 				stat->supp_reason, stat->pm_indicated,
158423e3ce3SKalle Valo 				stat->intermediate, stat->for_ampdu,
159423e3ce3SKalle Valo 				stat->acked);
160423e3ce3SKalle Valo 			idx++;
161423e3ce3SKalle Valo 		}
162423e3ce3SKalle Valo 		if (i == log->end)
163423e3ce3SKalle Valo 			break;
164423e3ce3SKalle Valo 		i++;
165423e3ce3SKalle Valo 	}
166423e3ce3SKalle Valo out_unlock:
167423e3ce3SKalle Valo 	spin_unlock_irqrestore(&log->lock, flags);
168423e3ce3SKalle Valo 
169423e3ce3SKalle Valo 	return count;
170423e3ce3SKalle Valo }
171423e3ce3SKalle Valo 
172423e3ce3SKalle Valo /* wl->irq_lock is locked */
restart_write_file(struct b43legacy_wldev * dev,const char * buf,size_t count)173423e3ce3SKalle Valo static int restart_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)
174423e3ce3SKalle Valo {
175423e3ce3SKalle Valo 	int err = 0;
176423e3ce3SKalle Valo 
177423e3ce3SKalle Valo 	if (count > 0 && buf[0] == '1') {
178423e3ce3SKalle Valo 		b43legacy_controller_restart(dev, "manually restarted");
179423e3ce3SKalle Valo 	} else
180423e3ce3SKalle Valo 		err = -EINVAL;
181423e3ce3SKalle Valo 
182423e3ce3SKalle Valo 	return err;
183423e3ce3SKalle Valo }
184423e3ce3SKalle Valo 
185423e3ce3SKalle Valo #undef fappend
186423e3ce3SKalle Valo 
b43legacy_debugfs_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)187423e3ce3SKalle Valo static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
188423e3ce3SKalle Valo 				size_t count, loff_t *ppos)
189423e3ce3SKalle Valo {
190423e3ce3SKalle Valo 	struct b43legacy_wldev *dev;
191423e3ce3SKalle Valo 	struct b43legacy_debugfs_fops *dfops;
192423e3ce3SKalle Valo 	struct b43legacy_dfs_file *dfile;
1933f649ab7SKees Cook 	ssize_t ret;
194423e3ce3SKalle Valo 	char *buf;
195423e3ce3SKalle Valo 	const size_t bufsize = 1024 * 16; /* 16 KiB buffer */
196423e3ce3SKalle Valo 	const size_t buforder = get_order(bufsize);
197423e3ce3SKalle Valo 	int err = 0;
198423e3ce3SKalle Valo 
199423e3ce3SKalle Valo 	if (!count)
200423e3ce3SKalle Valo 		return 0;
201423e3ce3SKalle Valo 	dev = file->private_data;
202423e3ce3SKalle Valo 	if (!dev)
203423e3ce3SKalle Valo 		return -ENODEV;
204423e3ce3SKalle Valo 
205423e3ce3SKalle Valo 	mutex_lock(&dev->wl->mutex);
206423e3ce3SKalle Valo 	if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
207423e3ce3SKalle Valo 		err = -ENODEV;
208423e3ce3SKalle Valo 		goto out_unlock;
209423e3ce3SKalle Valo 	}
210423e3ce3SKalle Valo 
2119c4a45b1SChristian Lamparter 	dfops = container_of(debugfs_real_fops(file),
2129c4a45b1SChristian Lamparter 			     struct b43legacy_debugfs_fops, fops);
213423e3ce3SKalle Valo 	if (!dfops->read) {
214423e3ce3SKalle Valo 		err = -ENOSYS;
215423e3ce3SKalle Valo 		goto out_unlock;
216423e3ce3SKalle Valo 	}
217423e3ce3SKalle Valo 	dfile = fops_to_dfs_file(dev, dfops);
218423e3ce3SKalle Valo 
219423e3ce3SKalle Valo 	if (!dfile->buffer) {
220423e3ce3SKalle Valo 		buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
221423e3ce3SKalle Valo 		if (!buf) {
222423e3ce3SKalle Valo 			err = -ENOMEM;
223423e3ce3SKalle Valo 			goto out_unlock;
224423e3ce3SKalle Valo 		}
225423e3ce3SKalle Valo 		memset(buf, 0, bufsize);
226423e3ce3SKalle Valo 		if (dfops->take_irqlock) {
227423e3ce3SKalle Valo 			spin_lock_irq(&dev->wl->irq_lock);
228423e3ce3SKalle Valo 			ret = dfops->read(dev, buf, bufsize);
229423e3ce3SKalle Valo 			spin_unlock_irq(&dev->wl->irq_lock);
230423e3ce3SKalle Valo 		} else
231423e3ce3SKalle Valo 			ret = dfops->read(dev, buf, bufsize);
232423e3ce3SKalle Valo 		if (ret <= 0) {
233423e3ce3SKalle Valo 			free_pages((unsigned long)buf, buforder);
234423e3ce3SKalle Valo 			err = ret;
235423e3ce3SKalle Valo 			goto out_unlock;
236423e3ce3SKalle Valo 		}
237423e3ce3SKalle Valo 		dfile->data_len = ret;
238423e3ce3SKalle Valo 		dfile->buffer = buf;
239423e3ce3SKalle Valo 	}
240423e3ce3SKalle Valo 
241423e3ce3SKalle Valo 	ret = simple_read_from_buffer(userbuf, count, ppos,
242423e3ce3SKalle Valo 				      dfile->buffer,
243423e3ce3SKalle Valo 				      dfile->data_len);
244423e3ce3SKalle Valo 	if (*ppos >= dfile->data_len) {
245423e3ce3SKalle Valo 		free_pages((unsigned long)dfile->buffer, buforder);
246423e3ce3SKalle Valo 		dfile->buffer = NULL;
247423e3ce3SKalle Valo 		dfile->data_len = 0;
248423e3ce3SKalle Valo 	}
249423e3ce3SKalle Valo out_unlock:
250423e3ce3SKalle Valo 	mutex_unlock(&dev->wl->mutex);
251423e3ce3SKalle Valo 
252423e3ce3SKalle Valo 	return err ? err : ret;
253423e3ce3SKalle Valo }
254423e3ce3SKalle Valo 
b43legacy_debugfs_write(struct file * file,const char __user * userbuf,size_t count,loff_t * ppos)255423e3ce3SKalle Valo static ssize_t b43legacy_debugfs_write(struct file *file,
256423e3ce3SKalle Valo 				 const char __user *userbuf,
257423e3ce3SKalle Valo 				 size_t count, loff_t *ppos)
258423e3ce3SKalle Valo {
259423e3ce3SKalle Valo 	struct b43legacy_wldev *dev;
260423e3ce3SKalle Valo 	struct b43legacy_debugfs_fops *dfops;
261423e3ce3SKalle Valo 	char *buf;
262423e3ce3SKalle Valo 	int err = 0;
263423e3ce3SKalle Valo 
264423e3ce3SKalle Valo 	if (!count)
265423e3ce3SKalle Valo 		return 0;
266423e3ce3SKalle Valo 	if (count > PAGE_SIZE)
267423e3ce3SKalle Valo 		return -E2BIG;
268423e3ce3SKalle Valo 	dev = file->private_data;
269423e3ce3SKalle Valo 	if (!dev)
270423e3ce3SKalle Valo 		return -ENODEV;
271423e3ce3SKalle Valo 
272423e3ce3SKalle Valo 	mutex_lock(&dev->wl->mutex);
273423e3ce3SKalle Valo 	if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
274423e3ce3SKalle Valo 		err = -ENODEV;
275423e3ce3SKalle Valo 		goto out_unlock;
276423e3ce3SKalle Valo 	}
277423e3ce3SKalle Valo 
2789c4a45b1SChristian Lamparter 	dfops = container_of(debugfs_real_fops(file),
2799c4a45b1SChristian Lamparter 			     struct b43legacy_debugfs_fops, fops);
280423e3ce3SKalle Valo 	if (!dfops->write) {
281423e3ce3SKalle Valo 		err = -ENOSYS;
282423e3ce3SKalle Valo 		goto out_unlock;
283423e3ce3SKalle Valo 	}
284423e3ce3SKalle Valo 
285423e3ce3SKalle Valo 	buf = (char *)get_zeroed_page(GFP_KERNEL);
286423e3ce3SKalle Valo 	if (!buf) {
287423e3ce3SKalle Valo 		err = -ENOMEM;
288423e3ce3SKalle Valo 		goto out_unlock;
289423e3ce3SKalle Valo 	}
290423e3ce3SKalle Valo 	if (copy_from_user(buf, userbuf, count)) {
291423e3ce3SKalle Valo 		err = -EFAULT;
292423e3ce3SKalle Valo 		goto out_freepage;
293423e3ce3SKalle Valo 	}
294423e3ce3SKalle Valo 	if (dfops->take_irqlock) {
295423e3ce3SKalle Valo 		spin_lock_irq(&dev->wl->irq_lock);
296423e3ce3SKalle Valo 		err = dfops->write(dev, buf, count);
297423e3ce3SKalle Valo 		spin_unlock_irq(&dev->wl->irq_lock);
298423e3ce3SKalle Valo 	} else
299423e3ce3SKalle Valo 		err = dfops->write(dev, buf, count);
300423e3ce3SKalle Valo 	if (err)
301423e3ce3SKalle Valo 		goto out_freepage;
302423e3ce3SKalle Valo 
303423e3ce3SKalle Valo out_freepage:
304423e3ce3SKalle Valo 	free_page((unsigned long)buf);
305423e3ce3SKalle Valo out_unlock:
306423e3ce3SKalle Valo 	mutex_unlock(&dev->wl->mutex);
307423e3ce3SKalle Valo 
308423e3ce3SKalle Valo 	return err ? err : count;
309423e3ce3SKalle Valo }
310423e3ce3SKalle Valo 
311423e3ce3SKalle Valo 
312423e3ce3SKalle Valo #define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock)	\
313423e3ce3SKalle Valo 	static struct b43legacy_debugfs_fops fops_##name = {		\
314423e3ce3SKalle Valo 		.read	= _read,				\
315423e3ce3SKalle Valo 		.write	= _write,				\
316423e3ce3SKalle Valo 		.fops	= {					\
317423e3ce3SKalle Valo 			.open	= simple_open,				\
318423e3ce3SKalle Valo 			.read	= b43legacy_debugfs_read,		\
319423e3ce3SKalle Valo 			.write	= b43legacy_debugfs_write,		\
320423e3ce3SKalle Valo 			.llseek = generic_file_llseek,			\
321423e3ce3SKalle Valo 		},						\
322423e3ce3SKalle Valo 		.file_struct_offset = offsetof(struct b43legacy_dfsentry, \
323423e3ce3SKalle Valo 					       file_##name),	\
324423e3ce3SKalle Valo 		.take_irqlock	= _take_irqlock,		\
325423e3ce3SKalle Valo 	}
326423e3ce3SKalle Valo 
327423e3ce3SKalle Valo B43legacy_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1);
328423e3ce3SKalle Valo B43legacy_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1);
329423e3ce3SKalle Valo B43legacy_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1);
330423e3ce3SKalle Valo B43legacy_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0);
331423e3ce3SKalle Valo B43legacy_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1);
332423e3ce3SKalle Valo 
333423e3ce3SKalle Valo 
b43legacy_debug(struct b43legacy_wldev * dev,enum b43legacy_dyndbg feature)334423e3ce3SKalle Valo int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature)
335423e3ce3SKalle Valo {
336423e3ce3SKalle Valo 	return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
337423e3ce3SKalle Valo }
338423e3ce3SKalle Valo 
b43legacy_add_dynamic_debug(struct b43legacy_wldev * dev)339423e3ce3SKalle Valo static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev)
340423e3ce3SKalle Valo {
341423e3ce3SKalle Valo 	struct b43legacy_dfsentry *e = dev->dfsentry;
342423e3ce3SKalle Valo 
343423e3ce3SKalle Valo #define add_dyn_dbg(name, id, initstate) do {			\
344423e3ce3SKalle Valo 	e->dyn_debug[id] = (initstate);				\
34532b4ebfeSGreg Kroah-Hartman 	debugfs_create_bool(name, 0600, e->subdir,		\
346423e3ce3SKalle Valo 			    &(e->dyn_debug[id]));		\
347423e3ce3SKalle Valo 	} while (0)
348423e3ce3SKalle Valo 
349423e3ce3SKalle Valo 	add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, false);
350423e3ce3SKalle Valo 	add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, false);
351423e3ce3SKalle Valo 	add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE, false);
352423e3ce3SKalle Valo 	add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST, false);
353423e3ce3SKalle Valo 	add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP, false);
354423e3ce3SKalle Valo 
355423e3ce3SKalle Valo #undef add_dyn_dbg
356423e3ce3SKalle Valo }
357423e3ce3SKalle Valo 
b43legacy_debugfs_add_device(struct b43legacy_wldev * dev)358423e3ce3SKalle Valo void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev)
359423e3ce3SKalle Valo {
360423e3ce3SKalle Valo 	struct b43legacy_dfsentry *e;
361423e3ce3SKalle Valo 	struct b43legacy_txstatus_log *log;
362423e3ce3SKalle Valo 	char devdir[16];
363423e3ce3SKalle Valo 
364423e3ce3SKalle Valo 	B43legacy_WARN_ON(!dev);
365423e3ce3SKalle Valo 	e = kzalloc(sizeof(*e), GFP_KERNEL);
366423e3ce3SKalle Valo 	if (!e) {
367423e3ce3SKalle Valo 		b43legacyerr(dev->wl, "debugfs: add device OOM\n");
368423e3ce3SKalle Valo 		return;
369423e3ce3SKalle Valo 	}
370423e3ce3SKalle Valo 	e->dev = dev;
371423e3ce3SKalle Valo 	log = &e->txstatlog;
372423e3ce3SKalle Valo 	log->log = kcalloc(B43legacy_NR_LOGGED_TXSTATUS,
373423e3ce3SKalle Valo 			   sizeof(struct b43legacy_txstatus), GFP_KERNEL);
374423e3ce3SKalle Valo 	if (!log->log) {
375423e3ce3SKalle Valo 		b43legacyerr(dev->wl, "debugfs: add device txstatus OOM\n");
376423e3ce3SKalle Valo 		kfree(e);
377423e3ce3SKalle Valo 		return;
378423e3ce3SKalle Valo 	}
379423e3ce3SKalle Valo 	log->end = -1;
380423e3ce3SKalle Valo 	spin_lock_init(&log->lock);
381423e3ce3SKalle Valo 
382423e3ce3SKalle Valo 	dev->dfsentry = e;
383423e3ce3SKalle Valo 
384423e3ce3SKalle Valo 	snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
385423e3ce3SKalle Valo 	e->subdir = debugfs_create_dir(devdir, rootdir);
386423e3ce3SKalle Valo 
387423e3ce3SKalle Valo #define ADD_FILE(name, mode)	\
388423e3ce3SKalle Valo 	do {							\
389*2435628eSGreg Kroah-Hartman 		debugfs_create_file(__stringify(name), mode,	\
390*2435628eSGreg Kroah-Hartman 				    e->subdir, dev,		\
391423e3ce3SKalle Valo 				    &fops_##name.fops);		\
392423e3ce3SKalle Valo 	} while (0)
393423e3ce3SKalle Valo 
394423e3ce3SKalle Valo 
395423e3ce3SKalle Valo 	ADD_FILE(tsf, 0600);
396423e3ce3SKalle Valo 	ADD_FILE(ucode_regs, 0400);
397423e3ce3SKalle Valo 	ADD_FILE(shm, 0400);
398423e3ce3SKalle Valo 	ADD_FILE(txstat, 0400);
399423e3ce3SKalle Valo 	ADD_FILE(restart, 0200);
400423e3ce3SKalle Valo 
401423e3ce3SKalle Valo #undef ADD_FILE
402423e3ce3SKalle Valo 
403423e3ce3SKalle Valo 	b43legacy_add_dynamic_debug(dev);
404423e3ce3SKalle Valo }
405423e3ce3SKalle Valo 
b43legacy_debugfs_remove_device(struct b43legacy_wldev * dev)406423e3ce3SKalle Valo void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev)
407423e3ce3SKalle Valo {
408423e3ce3SKalle Valo 	struct b43legacy_dfsentry *e;
409423e3ce3SKalle Valo 
410423e3ce3SKalle Valo 	if (!dev)
411423e3ce3SKalle Valo 		return;
412423e3ce3SKalle Valo 	e = dev->dfsentry;
413423e3ce3SKalle Valo 	if (!e)
414423e3ce3SKalle Valo 		return;
415423e3ce3SKalle Valo 
416423e3ce3SKalle Valo 	debugfs_remove(e->subdir);
417423e3ce3SKalle Valo 	kfree(e->txstatlog.log);
418423e3ce3SKalle Valo 	kfree(e);
419423e3ce3SKalle Valo }
420423e3ce3SKalle Valo 
b43legacy_debugfs_log_txstat(struct b43legacy_wldev * dev,const struct b43legacy_txstatus * status)421423e3ce3SKalle Valo void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev,
422423e3ce3SKalle Valo 			    const struct b43legacy_txstatus *status)
423423e3ce3SKalle Valo {
424423e3ce3SKalle Valo 	struct b43legacy_dfsentry *e = dev->dfsentry;
425423e3ce3SKalle Valo 	struct b43legacy_txstatus_log *log;
426423e3ce3SKalle Valo 	struct b43legacy_txstatus *cur;
427423e3ce3SKalle Valo 	int i;
428423e3ce3SKalle Valo 
429423e3ce3SKalle Valo 	if (!e)
430423e3ce3SKalle Valo 		return;
431423e3ce3SKalle Valo 	log = &e->txstatlog;
432423e3ce3SKalle Valo 	B43legacy_WARN_ON(!irqs_disabled());
433423e3ce3SKalle Valo 	spin_lock(&log->lock);
434423e3ce3SKalle Valo 	i = log->end + 1;
435423e3ce3SKalle Valo 	if (i == B43legacy_NR_LOGGED_TXSTATUS)
436423e3ce3SKalle Valo 		i = 0;
437423e3ce3SKalle Valo 	log->end = i;
438423e3ce3SKalle Valo 	cur = &(log->log[i]);
439423e3ce3SKalle Valo 	memcpy(cur, status, sizeof(*cur));
440423e3ce3SKalle Valo 	spin_unlock(&log->lock);
441423e3ce3SKalle Valo }
442423e3ce3SKalle Valo 
b43legacy_debugfs_init(void)443423e3ce3SKalle Valo void b43legacy_debugfs_init(void)
444423e3ce3SKalle Valo {
445423e3ce3SKalle Valo 	rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
446423e3ce3SKalle Valo }
447423e3ce3SKalle Valo 
b43legacy_debugfs_exit(void)448423e3ce3SKalle Valo void b43legacy_debugfs_exit(void)
449423e3ce3SKalle Valo {
450423e3ce3SKalle Valo 	debugfs_remove(rootdir);
451423e3ce3SKalle Valo }
452