blob: 9cdd9eca0822ba75729ecce3396e7cc4b278b32b [file] [log] [blame]
Michael Buesche4d6b792007-09-18 15:39:42 -04001/*
2
3 Broadcom B43 wireless driver
4
5 debugfs driver debugging code
6
7 Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; see the file COPYING. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
22 Boston, MA 02110-1301, USA.
23
24*/
25
26#include <linux/fs.h>
27#include <linux/debugfs.h>
28#include <linux/slab.h>
29#include <linux/netdevice.h>
30#include <linux/pci.h>
31#include <linux/mutex.h>
32
33#include "b43.h"
34#include "main.h"
35#include "debugfs.h"
36#include "dma.h"
Michael Buesche4d6b792007-09-18 15:39:42 -040037#include "xmit.h"
38
39
40/* The root directory. */
Michael Buesch1a094042007-09-20 11:13:40 -070041static struct dentry *rootdir;
Michael Buesche4d6b792007-09-18 15:39:42 -040042
43struct b43_debugfs_fops {
44 ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize);
45 int (*write)(struct b43_wldev *dev, const char *buf, size_t count);
46 struct file_operations fops;
47 /* Offset of struct b43_dfs_file in struct b43_dfsentry */
48 size_t file_struct_offset;
49 /* Take wl->irq_lock before calling read/write? */
50 bool take_irqlock;
51};
52
53static inline
54struct b43_dfs_file * fops_to_dfs_file(struct b43_wldev *dev,
55 const struct b43_debugfs_fops *dfops)
56{
57 void *p;
58
59 p = dev->dfsentry;
60 p += dfops->file_struct_offset;
61
62 return p;
63}
64
65
66#define fappend(fmt, x...) \
67 do { \
68 if (bufsize - count) \
69 count += snprintf(buf + count, \
70 bufsize - count, \
71 fmt , ##x); \
72 else \
73 printk(KERN_ERR "b43: fappend overflow\n"); \
74 } while (0)
75
76
Michael Buesch8bd463f2008-06-19 00:35:49 +020077/* The biggest MMIO address that we allow access to from the debugfs files. */
78#define B43_MAX_MMIO_ACCESS (0xF00 - 1)
79
80static ssize_t mmio16read__read_file(struct b43_wldev *dev,
81 char *buf, size_t bufsize)
82{
83 ssize_t count = 0;
84 unsigned int addr;
85 u16 val;
86
87 addr = dev->dfsentry->mmio16read_next;
88 if (addr > B43_MAX_MMIO_ACCESS)
89 return -EDESTADDRREQ;
90
91 val = b43_read16(dev, addr);
92 fappend("0x%04X\n", val);
93
94 return count;
95}
96
97static int mmio16read__write_file(struct b43_wldev *dev,
98 const char *buf, size_t count)
99{
100 unsigned int addr;
101 int res;
102
103 res = sscanf(buf, "0x%X", &addr);
104 if (res != 1)
105 return -EINVAL;
106 if (addr > B43_MAX_MMIO_ACCESS)
107 return -EADDRNOTAVAIL;
108
109 dev->dfsentry->mmio16read_next = addr;
110
111 return 0;
112}
113
114static int mmio16write__write_file(struct b43_wldev *dev,
115 const char *buf, size_t count)
116{
117 unsigned int addr, val;
118 int res;
119
120 res = sscanf(buf, "0x%X = 0x%X", &addr, &val);
121 if (res != 2)
122 return -EINVAL;
123 if (addr > B43_MAX_MMIO_ACCESS)
124 return -EADDRNOTAVAIL;
125 if (val > 0xFFFF)
126 return -E2BIG;
127
128 b43_write16(dev, addr, val);
129
130 return 0;
131}
132
133static ssize_t mmio32read__read_file(struct b43_wldev *dev,
134 char *buf, size_t bufsize)
135{
136 ssize_t count = 0;
137 unsigned int addr;
138 u32 val;
139
140 addr = dev->dfsentry->mmio32read_next;
141 if (addr > B43_MAX_MMIO_ACCESS)
142 return -EDESTADDRREQ;
143
144 val = b43_read32(dev, addr);
145 fappend("0x%08X\n", val);
146
147 return count;
148}
149
150static int mmio32read__write_file(struct b43_wldev *dev,
151 const char *buf, size_t count)
152{
153 unsigned int addr;
154 int res;
155
156 res = sscanf(buf, "0x%X", &addr);
157 if (res != 1)
158 return -EINVAL;
159 if (addr > B43_MAX_MMIO_ACCESS)
160 return -EADDRNOTAVAIL;
161
162 dev->dfsentry->mmio32read_next = addr;
163
164 return 0;
165}
166
167static int mmio32write__write_file(struct b43_wldev *dev,
168 const char *buf, size_t count)
169{
170 unsigned int addr, val;
171 int res;
172
173 res = sscanf(buf, "0x%X = 0x%X", &addr, &val);
174 if (res != 2)
175 return -EINVAL;
176 if (addr > B43_MAX_MMIO_ACCESS)
177 return -EADDRNOTAVAIL;
178 if (val > 0xFFFFFFFF)
179 return -E2BIG;
180
181 b43_write32(dev, addr, val);
182
183 return 0;
184}
185
Michael Buesche4d6b792007-09-18 15:39:42 -0400186/* wl->irq_lock is locked */
Michael Buesch1a094042007-09-20 11:13:40 -0700187static ssize_t tsf_read_file(struct b43_wldev *dev,
188 char *buf, size_t bufsize)
Michael Buesche4d6b792007-09-18 15:39:42 -0400189{
190 ssize_t count = 0;
191 u64 tsf;
192
193 b43_tsf_read(dev, &tsf);
194 fappend("0x%08x%08x\n",
195 (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32),
196 (unsigned int)(tsf & 0xFFFFFFFFULL));
197
198 return count;
199}
200
201/* wl->irq_lock is locked */
Michael Buesch1a094042007-09-20 11:13:40 -0700202static int tsf_write_file(struct b43_wldev *dev,
203 const char *buf, size_t count)
Michael Buesche4d6b792007-09-18 15:39:42 -0400204{
205 u64 tsf;
206
207 if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1)
208 return -EINVAL;
209 b43_tsf_write(dev, tsf);
210
211 return 0;
212}
213
214/* wl->irq_lock is locked */
Michael Buesch1a094042007-09-20 11:13:40 -0700215static ssize_t ucode_regs_read_file(struct b43_wldev *dev,
216 char *buf, size_t bufsize)
Michael Buesche4d6b792007-09-18 15:39:42 -0400217{
218 ssize_t count = 0;
219 int i;
220
221 for (i = 0; i < 64; i++) {
222 fappend("r%d = 0x%04x\n", i,
223 b43_shm_read16(dev, B43_SHM_SCRATCH, i));
224 }
225
226 return count;
227}
228
229/* wl->irq_lock is locked */
Michael Buesch1a094042007-09-20 11:13:40 -0700230static ssize_t shm_read_file(struct b43_wldev *dev,
231 char *buf, size_t bufsize)
Michael Buesche4d6b792007-09-18 15:39:42 -0400232{
233 ssize_t count = 0;
234 int i;
235 u16 tmp;
236 __le16 *le16buf = (__le16 *)buf;
237
238 for (i = 0; i < 0x1000; i++) {
Michael Buesch30c4ae42007-11-02 18:35:02 +0100239 if (bufsize < sizeof(tmp))
Michael Buesche4d6b792007-09-18 15:39:42 -0400240 break;
241 tmp = b43_shm_read16(dev, B43_SHM_SHARED, 2 * i);
242 le16buf[i] = cpu_to_le16(tmp);
243 count += sizeof(tmp);
244 bufsize -= sizeof(tmp);
245 }
246
247 return count;
248}
249
Michael Buesch1a094042007-09-20 11:13:40 -0700250static ssize_t txstat_read_file(struct b43_wldev *dev,
251 char *buf, size_t bufsize)
Michael Buesche4d6b792007-09-18 15:39:42 -0400252{
253 struct b43_txstatus_log *log = &dev->dfsentry->txstatlog;
254 ssize_t count = 0;
255 unsigned long flags;
256 int i, idx;
257 struct b43_txstatus *stat;
258
259 spin_lock_irqsave(&log->lock, flags);
260 if (log->end < 0) {
261 fappend("Nothing transmitted, yet\n");
262 goto out_unlock;
263 }
264 fappend("b43 TX status reports:\n\n"
265 "index | cookie | seq | phy_stat | frame_count | "
266 "rts_count | supp_reason | pm_indicated | "
267 "intermediate | for_ampdu | acked\n" "---\n");
268 i = log->end + 1;
269 idx = 0;
270 while (1) {
271 if (i == B43_NR_LOGGED_TXSTATUS)
272 i = 0;
273 stat = &(log->log[i]);
274 if (stat->cookie) {
275 fappend("%03d | "
276 "0x%04X | 0x%04X | 0x%02X | "
277 "0x%X | 0x%X | "
278 "%u | %u | "
279 "%u | %u | %u\n",
280 idx,
281 stat->cookie, stat->seq, stat->phy_stat,
282 stat->frame_count, stat->rts_count,
283 stat->supp_reason, stat->pm_indicated,
284 stat->intermediate, stat->for_ampdu,
285 stat->acked);
286 idx++;
287 }
288 if (i == log->end)
289 break;
290 i++;
291 }
292out_unlock:
293 spin_unlock_irqrestore(&log->lock, flags);
294
295 return count;
296}
297
Michael Buesch1a094042007-09-20 11:13:40 -0700298static ssize_t txpower_g_read_file(struct b43_wldev *dev,
299 char *buf, size_t bufsize)
Michael Buesche4d6b792007-09-18 15:39:42 -0400300{
301 ssize_t count = 0;
302
303 if (dev->phy.type != B43_PHYTYPE_G) {
304 fappend("Device is not a G-PHY\n");
305 goto out;
306 }
307 fappend("Control: %s\n", dev->phy.manual_txpower_control ?
308 "MANUAL" : "AUTOMATIC");
309 fappend("Baseband attenuation: %u\n", dev->phy.bbatt.att);
310 fappend("Radio attenuation: %u\n", dev->phy.rfatt.att);
311 fappend("TX Mixer Gain: %s\n",
312 (dev->phy.tx_control & B43_TXCTL_TXMIX) ? "ON" : "OFF");
313 fappend("PA Gain 2dB: %s\n",
314 (dev->phy.tx_control & B43_TXCTL_PA2DB) ? "ON" : "OFF");
315 fappend("PA Gain 3dB: %s\n",
316 (dev->phy.tx_control & B43_TXCTL_PA3DB) ? "ON" : "OFF");
317 fappend("\n\n");
318 fappend("You can write to this file:\n");
319 fappend("Writing \"auto\" enables automatic txpower control.\n");
320 fappend
321 ("Writing the attenuation values as \"bbatt rfatt txmix pa2db pa3db\" "
322 "enables manual txpower control.\n");
323 fappend("Example: 5 4 0 0 1\n");
324 fappend("Enables manual control with Baseband attenuation 5, "
325 "Radio attenuation 4, No TX Mixer Gain, "
326 "No PA Gain 2dB, With PA Gain 3dB.\n");
327out:
328 return count;
329}
330
Michael Buesch1a094042007-09-20 11:13:40 -0700331static int txpower_g_write_file(struct b43_wldev *dev,
332 const char *buf, size_t count)
Michael Buesche4d6b792007-09-18 15:39:42 -0400333{
Michael Bueschb85b3b72007-09-19 18:51:38 +0200334 if (dev->phy.type != B43_PHYTYPE_G)
335 return -ENODEV;
Michael Buesche4d6b792007-09-18 15:39:42 -0400336 if ((count >= 4) && (memcmp(buf, "auto", 4) == 0)) {
337 /* Automatic control */
338 dev->phy.manual_txpower_control = 0;
339 b43_phy_xmitpower(dev);
340 } else {
341 int bbatt = 0, rfatt = 0, txmix = 0, pa2db = 0, pa3db = 0;
342 /* Manual control */
343 if (sscanf(buf, "%d %d %d %d %d", &bbatt, &rfatt,
Michael Bueschb85b3b72007-09-19 18:51:38 +0200344 &txmix, &pa2db, &pa3db) != 5)
345 return -EINVAL;
Michael Buesche4d6b792007-09-18 15:39:42 -0400346 b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt);
347 dev->phy.manual_txpower_control = 1;
348 dev->phy.bbatt.att = bbatt;
349 dev->phy.rfatt.att = rfatt;
350 dev->phy.tx_control = 0;
351 if (txmix)
352 dev->phy.tx_control |= B43_TXCTL_TXMIX;
353 if (pa2db)
354 dev->phy.tx_control |= B43_TXCTL_PA2DB;
355 if (pa3db)
356 dev->phy.tx_control |= B43_TXCTL_PA3DB;
Michael Bueschf31800d2008-01-09 19:08:49 +0100357 b43_phy_lock(dev);
Michael Buesche4d6b792007-09-18 15:39:42 -0400358 b43_radio_lock(dev);
359 b43_set_txpower_g(dev, &dev->phy.bbatt,
360 &dev->phy.rfatt, dev->phy.tx_control);
361 b43_radio_unlock(dev);
Michael Bueschf31800d2008-01-09 19:08:49 +0100362 b43_phy_unlock(dev);
Michael Buesche4d6b792007-09-18 15:39:42 -0400363 }
Michael Buesche4d6b792007-09-18 15:39:42 -0400364
Michael Bueschb85b3b72007-09-19 18:51:38 +0200365 return 0;
Michael Buesche4d6b792007-09-18 15:39:42 -0400366}
367
368/* wl->irq_lock is locked */
Michael Buesch1a094042007-09-20 11:13:40 -0700369static int restart_write_file(struct b43_wldev *dev,
370 const char *buf, size_t count)
Michael Buesche4d6b792007-09-18 15:39:42 -0400371{
372 int err = 0;
373
374 if (count > 0 && buf[0] == '1') {
375 b43_controller_restart(dev, "manually restarted");
376 } else
377 err = -EINVAL;
378
379 return err;
380}
381
Michael Bueschf5eda472008-04-20 16:03:32 +0200382static unsigned long calc_expire_secs(unsigned long now,
383 unsigned long time,
384 unsigned long expire)
Michael Buesche4d6b792007-09-18 15:39:42 -0400385{
Michael Bueschf5eda472008-04-20 16:03:32 +0200386 expire = time + expire;
Michael Buesche4d6b792007-09-18 15:39:42 -0400387
Michael Bueschf5eda472008-04-20 16:03:32 +0200388 if (time_after(now, expire))
389 return 0; /* expired */
390 if (expire < now) {
391 /* jiffies wrapped */
392 expire -= MAX_JIFFY_OFFSET;
393 now -= MAX_JIFFY_OFFSET;
Michael Buesche4d6b792007-09-18 15:39:42 -0400394 }
Michael Bueschf5eda472008-04-20 16:03:32 +0200395 B43_WARN_ON(expire < now);
Michael Buesche4d6b792007-09-18 15:39:42 -0400396
Michael Bueschf5eda472008-04-20 16:03:32 +0200397 return (expire - now) / HZ;
Michael Buesche4d6b792007-09-18 15:39:42 -0400398}
399
Michael Buesch1a094042007-09-20 11:13:40 -0700400static ssize_t loctls_read_file(struct b43_wldev *dev,
401 char *buf, size_t bufsize)
Michael Buesche4d6b792007-09-18 15:39:42 -0400402{
403 ssize_t count = 0;
404 struct b43_txpower_lo_control *lo;
405 int i, err = 0;
Michael Bueschf5eda472008-04-20 16:03:32 +0200406 struct b43_lo_calib *cal;
407 unsigned long now = jiffies;
408 struct b43_phy *phy = &dev->phy;
Michael Buesche4d6b792007-09-18 15:39:42 -0400409
Michael Bueschf5eda472008-04-20 16:03:32 +0200410 if (phy->type != B43_PHYTYPE_G) {
Michael Buesche4d6b792007-09-18 15:39:42 -0400411 fappend("Device is not a G-PHY\n");
412 err = -ENODEV;
413 goto out;
414 }
Michael Bueschf5eda472008-04-20 16:03:32 +0200415 lo = phy->lo_control;
Michael Buesche4d6b792007-09-18 15:39:42 -0400416 fappend("-- Local Oscillator calibration data --\n\n");
Michael Bueschf5eda472008-04-20 16:03:32 +0200417 fappend("HW-power-control enabled: %d\n",
Michael Buesche4d6b792007-09-18 15:39:42 -0400418 dev->phy.hardware_power_control);
Michael Bueschf5eda472008-04-20 16:03:32 +0200419 fappend("TX Bias: 0x%02X, TX Magn: 0x%02X (expire in %lu sec)\n",
420 lo->tx_bias, lo->tx_magn,
421 calc_expire_secs(now, lo->txctl_measured_time,
422 B43_LO_TXCTL_EXPIRE));
423 fappend("Power Vector: 0x%08X%08X (expires in %lu sec)\n",
Michael Buesche4d6b792007-09-18 15:39:42 -0400424 (unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32),
Michael Bueschf5eda472008-04-20 16:03:32 +0200425 (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL),
426 calc_expire_secs(now, lo->pwr_vec_read_time,
427 B43_LO_PWRVEC_EXPIRE));
428
429 fappend("\nCalibrated settings:\n");
430 list_for_each_entry(cal, &lo->calib_list, list) {
431 bool active;
432
433 active = (b43_compare_bbatt(&cal->bbatt, &phy->bbatt) &&
434 b43_compare_rfatt(&cal->rfatt, &phy->rfatt));
435 fappend("BB(%d), RF(%d,%d) -> I=%d, Q=%d "
436 "(expires in %lu sec)%s\n",
437 cal->bbatt.att,
438 cal->rfatt.att, cal->rfatt.with_padmix,
439 cal->ctl.i, cal->ctl.q,
440 calc_expire_secs(now, cal->calib_time,
441 B43_LO_CALIB_EXPIRE),
442 active ? " ACTIVE" : "");
443 }
444
Michael Buesche4d6b792007-09-18 15:39:42 -0400445 fappend("\nUsed RF attenuation values: Value(WithPadmix flag)\n");
446 for (i = 0; i < lo->rfatt_list.len; i++) {
447 fappend("%u(%d), ",
448 lo->rfatt_list.list[i].att,
449 lo->rfatt_list.list[i].with_padmix);
450 }
451 fappend("\n");
452 fappend("\nUsed Baseband attenuation values:\n");
453 for (i = 0; i < lo->bbatt_list.len; i++) {
454 fappend("%u, ",
455 lo->bbatt_list.list[i].att);
456 }
457 fappend("\n");
458
459out:
460 return err ? err : count;
461}
462
463#undef fappend
464
465static int b43_debugfs_open(struct inode *inode, struct file *file)
466{
467 file->private_data = inode->i_private;
468 return 0;
469}
470
471static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
472 size_t count, loff_t *ppos)
473{
474 struct b43_wldev *dev;
475 struct b43_debugfs_fops *dfops;
476 struct b43_dfs_file *dfile;
Frank Lichtenheld7223e8d2007-11-12 11:12:52 +0100477 ssize_t uninitialized_var(ret);
Michael Buesche4d6b792007-09-18 15:39:42 -0400478 char *buf;
Michael Bueschf5eda472008-04-20 16:03:32 +0200479 const size_t bufsize = 1024 * 16; /* 16 kiB buffer */
Michael Buesche4d6b792007-09-18 15:39:42 -0400480 const size_t buforder = get_order(bufsize);
481 int err = 0;
482
483 if (!count)
484 return 0;
485 dev = file->private_data;
486 if (!dev)
487 return -ENODEV;
488
489 mutex_lock(&dev->wl->mutex);
490 if (b43_status(dev) < B43_STAT_INITIALIZED) {
491 err = -ENODEV;
492 goto out_unlock;
493 }
494
495 dfops = container_of(file->f_op, struct b43_debugfs_fops, fops);
496 if (!dfops->read) {
497 err = -ENOSYS;
498 goto out_unlock;
499 }
500 dfile = fops_to_dfs_file(dev, dfops);
501
502 if (!dfile->buffer) {
503 buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
504 if (!buf) {
505 err = -ENOMEM;
506 goto out_unlock;
507 }
Michael Buesche4d6b792007-09-18 15:39:42 -0400508 memset(buf, 0, bufsize);
509 if (dfops->take_irqlock) {
510 spin_lock_irq(&dev->wl->irq_lock);
511 ret = dfops->read(dev, buf, bufsize);
512 spin_unlock_irq(&dev->wl->irq_lock);
513 } else
514 ret = dfops->read(dev, buf, bufsize);
515 if (ret <= 0) {
516 free_pages((unsigned long)buf, buforder);
517 err = ret;
518 goto out_unlock;
519 }
520 dfile->data_len = ret;
521 dfile->buffer = buf;
522 }
523
524 ret = simple_read_from_buffer(userbuf, count, ppos,
525 dfile->buffer,
526 dfile->data_len);
527 if (*ppos >= dfile->data_len) {
528 free_pages((unsigned long)dfile->buffer, buforder);
529 dfile->buffer = NULL;
530 dfile->data_len = 0;
531 }
532out_unlock:
533 mutex_unlock(&dev->wl->mutex);
534
535 return err ? err : ret;
536}
537
538static ssize_t b43_debugfs_write(struct file *file,
539 const char __user *userbuf,
540 size_t count, loff_t *ppos)
541{
542 struct b43_wldev *dev;
543 struct b43_debugfs_fops *dfops;
544 char *buf;
545 int err = 0;
546
547 if (!count)
548 return 0;
549 if (count > PAGE_SIZE)
550 return -E2BIG;
551 dev = file->private_data;
552 if (!dev)
553 return -ENODEV;
554
555 mutex_lock(&dev->wl->mutex);
556 if (b43_status(dev) < B43_STAT_INITIALIZED) {
557 err = -ENODEV;
558 goto out_unlock;
559 }
560
561 dfops = container_of(file->f_op, struct b43_debugfs_fops, fops);
562 if (!dfops->write) {
563 err = -ENOSYS;
564 goto out_unlock;
565 }
566
567 buf = (char *)get_zeroed_page(GFP_KERNEL);
568 if (!buf) {
569 err = -ENOMEM;
570 goto out_unlock;
571 }
572 if (copy_from_user(buf, userbuf, count)) {
573 err = -EFAULT;
574 goto out_freepage;
575 }
576 if (dfops->take_irqlock) {
577 spin_lock_irq(&dev->wl->irq_lock);
578 err = dfops->write(dev, buf, count);
579 spin_unlock_irq(&dev->wl->irq_lock);
580 } else
581 err = dfops->write(dev, buf, count);
582 if (err)
583 goto out_freepage;
584
585out_freepage:
586 free_page((unsigned long)buf);
587out_unlock:
588 mutex_unlock(&dev->wl->mutex);
589
590 return err ? err : count;
591}
592
593
594#define B43_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \
595 static struct b43_debugfs_fops fops_##name = { \
596 .read = _read, \
597 .write = _write, \
598 .fops = { \
599 .open = b43_debugfs_open, \
600 .read = b43_debugfs_read, \
601 .write = b43_debugfs_write, \
602 }, \
603 .file_struct_offset = offsetof(struct b43_dfsentry, \
604 file_##name), \
605 .take_irqlock = _take_irqlock, \
606 }
607
Michael Buesch8bd463f2008-06-19 00:35:49 +0200608B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file, 1);
609B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file, 1);
610B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file, 1);
611B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file, 1);
Michael Buesche4d6b792007-09-18 15:39:42 -0400612B43_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1);
613B43_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1);
614B43_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1);
615B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0);
616B43_DEBUGFS_FOPS(txpower_g, txpower_g_read_file, txpower_g_write_file, 0);
617B43_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1);
618B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL, 0);
619
620
621int b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)
622{
623 return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
624}
625
626static void b43_remove_dynamic_debug(struct b43_wldev *dev)
627{
628 struct b43_dfsentry *e = dev->dfsentry;
629 int i;
630
631 for (i = 0; i < __B43_NR_DYNDBG; i++)
632 debugfs_remove(e->dyn_debug_dentries[i]);
633}
634
635static void b43_add_dynamic_debug(struct b43_wldev *dev)
636{
637 struct b43_dfsentry *e = dev->dfsentry;
638 struct dentry *d;
639
640#define add_dyn_dbg(name, id, initstate) do { \
641 e->dyn_debug[id] = (initstate); \
642 d = debugfs_create_bool(name, 0600, e->subdir, \
643 &(e->dyn_debug[id])); \
644 if (!IS_ERR(d)) \
645 e->dyn_debug_dentries[id] = d; \
646 } while (0)
647
648 add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, 0);
649 add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, 0);
650 add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, 0);
651 add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, 0);
652 add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, 0);
Michael Bueschf5eda472008-04-20 16:03:32 +0200653 add_dyn_dbg("debug_lo", B43_DBG_LO, 0);
Michael Buesche4d6b792007-09-18 15:39:42 -0400654
655#undef add_dyn_dbg
656}
657
658void b43_debugfs_add_device(struct b43_wldev *dev)
659{
660 struct b43_dfsentry *e;
661 struct b43_txstatus_log *log;
662 char devdir[16];
663
664 B43_WARN_ON(!dev);
665 e = kzalloc(sizeof(*e), GFP_KERNEL);
666 if (!e) {
667 b43err(dev->wl, "debugfs: add device OOM\n");
668 return;
669 }
670 e->dev = dev;
671 log = &e->txstatlog;
672 log->log = kcalloc(B43_NR_LOGGED_TXSTATUS,
673 sizeof(struct b43_txstatus), GFP_KERNEL);
674 if (!log->log) {
675 b43err(dev->wl, "debugfs: add device txstatus OOM\n");
676 kfree(e);
677 return;
678 }
679 log->end = -1;
680 spin_lock_init(&log->lock);
681
682 dev->dfsentry = e;
683
684 snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
685 e->subdir = debugfs_create_dir(devdir, rootdir);
686 if (!e->subdir || IS_ERR(e->subdir)) {
687 if (e->subdir == ERR_PTR(-ENODEV)) {
688 b43dbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not "
689 "enabled in kernel config\n");
690 } else {
691 b43err(dev->wl, "debugfs: cannot create %s directory\n",
692 devdir);
693 }
694 dev->dfsentry = NULL;
695 kfree(log->log);
696 kfree(e);
697 return;
698 }
699
Michael Buesch8bd463f2008-06-19 00:35:49 +0200700 e->mmio16read_next = 0xFFFF; /* invalid address */
701 e->mmio32read_next = 0xFFFF; /* invalid address */
702
Michael Buesche4d6b792007-09-18 15:39:42 -0400703#define ADD_FILE(name, mode) \
704 do { \
705 struct dentry *d; \
706 d = debugfs_create_file(__stringify(name), \
707 mode, e->subdir, dev, \
708 &fops_##name.fops); \
709 e->file_##name.dentry = NULL; \
710 if (!IS_ERR(d)) \
711 e->file_##name.dentry = d; \
712 } while (0)
713
714
Michael Buesch8bd463f2008-06-19 00:35:49 +0200715 ADD_FILE(mmio16read, 0600);
716 ADD_FILE(mmio16write, 0200);
717 ADD_FILE(mmio32read, 0600);
718 ADD_FILE(mmio32write, 0200);
Michael Buesche4d6b792007-09-18 15:39:42 -0400719 ADD_FILE(tsf, 0600);
720 ADD_FILE(ucode_regs, 0400);
721 ADD_FILE(shm, 0400);
722 ADD_FILE(txstat, 0400);
723 ADD_FILE(txpower_g, 0600);
724 ADD_FILE(restart, 0200);
725 ADD_FILE(loctls, 0400);
726
727#undef ADD_FILE
728
729 b43_add_dynamic_debug(dev);
730}
731
732void b43_debugfs_remove_device(struct b43_wldev *dev)
733{
734 struct b43_dfsentry *e;
735
736 if (!dev)
737 return;
738 e = dev->dfsentry;
739 if (!e)
740 return;
741 b43_remove_dynamic_debug(dev);
742
Michael Buesch8bd463f2008-06-19 00:35:49 +0200743 debugfs_remove(e->file_mmio16read.dentry);
744 debugfs_remove(e->file_mmio16write.dentry);
745 debugfs_remove(e->file_mmio32read.dentry);
746 debugfs_remove(e->file_mmio32write.dentry);
Michael Buesche4d6b792007-09-18 15:39:42 -0400747 debugfs_remove(e->file_tsf.dentry);
748 debugfs_remove(e->file_ucode_regs.dentry);
749 debugfs_remove(e->file_shm.dentry);
750 debugfs_remove(e->file_txstat.dentry);
751 debugfs_remove(e->file_txpower_g.dentry);
752 debugfs_remove(e->file_restart.dentry);
753 debugfs_remove(e->file_loctls.dentry);
754
755 debugfs_remove(e->subdir);
756 kfree(e->txstatlog.log);
757 kfree(e);
758}
759
Michael Buesch7a193a52008-03-23 01:08:22 +0100760/* Called with IRQs disabled. */
Michael Buesche4d6b792007-09-18 15:39:42 -0400761void b43_debugfs_log_txstat(struct b43_wldev *dev,
762 const struct b43_txstatus *status)
763{
764 struct b43_dfsentry *e = dev->dfsentry;
765 struct b43_txstatus_log *log;
766 struct b43_txstatus *cur;
767 int i;
768
769 if (!e)
770 return;
771 log = &e->txstatlog;
Michael Buesch7a193a52008-03-23 01:08:22 +0100772 spin_lock(&log->lock); /* IRQs are already disabled. */
Michael Buesche4d6b792007-09-18 15:39:42 -0400773 i = log->end + 1;
774 if (i == B43_NR_LOGGED_TXSTATUS)
775 i = 0;
776 log->end = i;
777 cur = &(log->log[i]);
778 memcpy(cur, status, sizeof(*cur));
779 spin_unlock(&log->lock);
780}
781
782void b43_debugfs_init(void)
783{
784 rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
785 if (IS_ERR(rootdir))
786 rootdir = NULL;
787}
788
789void b43_debugfs_exit(void)
790{
791 debugfs_remove(rootdir);
792}