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