blob: e807bd9306471772509b0f936391c5b609ed4cfa [file] [log] [blame]
Michael Buesche4d6b792007-09-18 15:39:42 -04001/*
2
3 Broadcom B43 wireless driver
4
5 debugfs driver debugging code
6
Michael Büscheb032b92011-07-04 20:50:05 +02007 Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
Michael Buesche4d6b792007-09-18 15:39:42 -04008
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;
Michael Buesche4d6b792007-09-18 15:39:42 -040049};
50
51static inline
John Daiker99da1852009-02-24 02:16:42 -080052struct b43_dfs_file *fops_to_dfs_file(struct b43_wldev *dev,
53 const struct b43_debugfs_fops *dfops)
Michael Buesche4d6b792007-09-18 15:39:42 -040054{
55 void *p;
56
57 p = dev->dfsentry;
58 p += dfops->file_struct_offset;
59
60 return p;
61}
62
63
64#define fappend(fmt, x...) \
65 do { \
66 if (bufsize - count) \
67 count += snprintf(buf + count, \
68 bufsize - count, \
69 fmt , ##x); \
70 else \
71 printk(KERN_ERR "b43: fappend overflow\n"); \
72 } while (0)
73
74
Michael Buesch6bbc3212008-06-19 19:33:51 +020075/* The biggest address values for SHM access from the debugfs files. */
76#define B43_MAX_SHM_ROUTING 4
77#define B43_MAX_SHM_ADDR 0xFFFF
78
79static ssize_t shm16read__read_file(struct b43_wldev *dev,
80 char *buf, size_t bufsize)
81{
82 ssize_t count = 0;
83 unsigned int routing, addr;
84 u16 val;
85
86 routing = dev->dfsentry->shm16read_routing_next;
87 addr = dev->dfsentry->shm16read_addr_next;
88 if ((routing > B43_MAX_SHM_ROUTING) ||
89 (addr > B43_MAX_SHM_ADDR))
90 return -EDESTADDRREQ;
91
92 val = b43_shm_read16(dev, routing, addr);
93 fappend("0x%04X\n", val);
94
95 return count;
96}
97
98static int shm16read__write_file(struct b43_wldev *dev,
99 const char *buf, size_t count)
100{
101 unsigned int routing, addr;
102 int res;
103
104 res = sscanf(buf, "0x%X 0x%X", &routing, &addr);
105 if (res != 2)
106 return -EINVAL;
107 if (routing > B43_MAX_SHM_ROUTING)
108 return -EADDRNOTAVAIL;
109 if (addr > B43_MAX_SHM_ADDR)
110 return -EADDRNOTAVAIL;
111 if (routing == B43_SHM_SHARED) {
112 if ((addr % 2) != 0)
113 return -EADDRNOTAVAIL;
114 }
115
116 dev->dfsentry->shm16read_routing_next = routing;
117 dev->dfsentry->shm16read_addr_next = addr;
118
119 return 0;
120}
121
122static int shm16write__write_file(struct b43_wldev *dev,
123 const char *buf, size_t count)
124{
125 unsigned int routing, addr, mask, set;
126 u16 val;
127 int res;
Michael Buesch6bbc3212008-06-19 19:33:51 +0200128
129 res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
130 &routing, &addr, &mask, &set);
131 if (res != 4)
132 return -EINVAL;
133 if (routing > B43_MAX_SHM_ROUTING)
134 return -EADDRNOTAVAIL;
135 if (addr > B43_MAX_SHM_ADDR)
136 return -EADDRNOTAVAIL;
137 if (routing == B43_SHM_SHARED) {
138 if ((addr % 2) != 0)
139 return -EADDRNOTAVAIL;
140 }
141 if ((mask > 0xFFFF) || (set > 0xFFFF))
142 return -E2BIG;
143
Michael Buesch6bbc3212008-06-19 19:33:51 +0200144 if (mask == 0)
145 val = 0;
146 else
Michael Buesch69eddc82009-09-04 22:57:26 +0200147 val = b43_shm_read16(dev, routing, addr);
Michael Buesch6bbc3212008-06-19 19:33:51 +0200148 val &= mask;
149 val |= set;
Michael Buesch69eddc82009-09-04 22:57:26 +0200150 b43_shm_write16(dev, routing, addr, val);
Michael Buesch6bbc3212008-06-19 19:33:51 +0200151
152 return 0;
153}
154
155static ssize_t shm32read__read_file(struct b43_wldev *dev,
156 char *buf, size_t bufsize)
157{
158 ssize_t count = 0;
159 unsigned int routing, addr;
160 u32 val;
161
162 routing = dev->dfsentry->shm32read_routing_next;
163 addr = dev->dfsentry->shm32read_addr_next;
164 if ((routing > B43_MAX_SHM_ROUTING) ||
165 (addr > B43_MAX_SHM_ADDR))
166 return -EDESTADDRREQ;
167
168 val = b43_shm_read32(dev, routing, addr);
169 fappend("0x%08X\n", val);
170
171 return count;
172}
173
174static int shm32read__write_file(struct b43_wldev *dev,
175 const char *buf, size_t count)
176{
177 unsigned int routing, addr;
178 int res;
179
180 res = sscanf(buf, "0x%X 0x%X", &routing, &addr);
181 if (res != 2)
182 return -EINVAL;
183 if (routing > B43_MAX_SHM_ROUTING)
184 return -EADDRNOTAVAIL;
185 if (addr > B43_MAX_SHM_ADDR)
186 return -EADDRNOTAVAIL;
187 if (routing == B43_SHM_SHARED) {
188 if ((addr % 2) != 0)
189 return -EADDRNOTAVAIL;
190 }
191
192 dev->dfsentry->shm32read_routing_next = routing;
193 dev->dfsentry->shm32read_addr_next = addr;
194
195 return 0;
196}
197
198static int shm32write__write_file(struct b43_wldev *dev,
199 const char *buf, size_t count)
200{
201 unsigned int routing, addr, mask, set;
202 u32 val;
203 int res;
Michael Buesch6bbc3212008-06-19 19:33:51 +0200204
205 res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
206 &routing, &addr, &mask, &set);
207 if (res != 4)
208 return -EINVAL;
209 if (routing > B43_MAX_SHM_ROUTING)
210 return -EADDRNOTAVAIL;
211 if (addr > B43_MAX_SHM_ADDR)
212 return -EADDRNOTAVAIL;
213 if (routing == B43_SHM_SHARED) {
214 if ((addr % 2) != 0)
215 return -EADDRNOTAVAIL;
216 }
217 if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))
218 return -E2BIG;
219
Michael Buesch6bbc3212008-06-19 19:33:51 +0200220 if (mask == 0)
221 val = 0;
222 else
Michael Buesch69eddc82009-09-04 22:57:26 +0200223 val = b43_shm_read32(dev, routing, addr);
Michael Buesch6bbc3212008-06-19 19:33:51 +0200224 val &= mask;
225 val |= set;
Michael Buesch69eddc82009-09-04 22:57:26 +0200226 b43_shm_write32(dev, routing, addr, val);
Michael Buesch6bbc3212008-06-19 19:33:51 +0200227
228 return 0;
229}
230
Michael Buesch8bd463f2008-06-19 00:35:49 +0200231/* The biggest MMIO address that we allow access to from the debugfs files. */
232#define B43_MAX_MMIO_ACCESS (0xF00 - 1)
233
234static ssize_t mmio16read__read_file(struct b43_wldev *dev,
235 char *buf, size_t bufsize)
236{
237 ssize_t count = 0;
238 unsigned int addr;
239 u16 val;
240
241 addr = dev->dfsentry->mmio16read_next;
242 if (addr > B43_MAX_MMIO_ACCESS)
243 return -EDESTADDRREQ;
244
245 val = b43_read16(dev, addr);
246 fappend("0x%04X\n", val);
247
248 return count;
249}
250
251static int mmio16read__write_file(struct b43_wldev *dev,
252 const char *buf, size_t count)
253{
254 unsigned int addr;
255 int res;
256
257 res = sscanf(buf, "0x%X", &addr);
258 if (res != 1)
259 return -EINVAL;
260 if (addr > B43_MAX_MMIO_ACCESS)
261 return -EADDRNOTAVAIL;
Michael Bueschefa27582008-06-19 19:38:32 +0200262 if ((addr % 2) != 0)
263 return -EINVAL;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200264
265 dev->dfsentry->mmio16read_next = addr;
266
267 return 0;
268}
269
270static int mmio16write__write_file(struct b43_wldev *dev,
271 const char *buf, size_t count)
272{
Michael Bueschefa27582008-06-19 19:38:32 +0200273 unsigned int addr, mask, set;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200274 int res;
Michael Bueschefa27582008-06-19 19:38:32 +0200275 u16 val;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200276
Michael Bueschefa27582008-06-19 19:38:32 +0200277 res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);
278 if (res != 3)
Michael Buesch8bd463f2008-06-19 00:35:49 +0200279 return -EINVAL;
280 if (addr > B43_MAX_MMIO_ACCESS)
281 return -EADDRNOTAVAIL;
Michael Bueschefa27582008-06-19 19:38:32 +0200282 if ((mask > 0xFFFF) || (set > 0xFFFF))
Michael Buesch8bd463f2008-06-19 00:35:49 +0200283 return -E2BIG;
Michael Bueschefa27582008-06-19 19:38:32 +0200284 if ((addr % 2) != 0)
285 return -EINVAL;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200286
Michael Bueschefa27582008-06-19 19:38:32 +0200287 if (mask == 0)
288 val = 0;
289 else
290 val = b43_read16(dev, addr);
291 val &= mask;
292 val |= set;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200293 b43_write16(dev, addr, val);
294
295 return 0;
296}
297
298static ssize_t mmio32read__read_file(struct b43_wldev *dev,
299 char *buf, size_t bufsize)
300{
301 ssize_t count = 0;
302 unsigned int addr;
303 u32 val;
304
305 addr = dev->dfsentry->mmio32read_next;
306 if (addr > B43_MAX_MMIO_ACCESS)
307 return -EDESTADDRREQ;
308
309 val = b43_read32(dev, addr);
310 fappend("0x%08X\n", val);
311
312 return count;
313}
314
315static int mmio32read__write_file(struct b43_wldev *dev,
316 const char *buf, size_t count)
317{
318 unsigned int addr;
319 int res;
320
321 res = sscanf(buf, "0x%X", &addr);
322 if (res != 1)
323 return -EINVAL;
324 if (addr > B43_MAX_MMIO_ACCESS)
325 return -EADDRNOTAVAIL;
Michael Bueschefa27582008-06-19 19:38:32 +0200326 if ((addr % 4) != 0)
327 return -EINVAL;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200328
329 dev->dfsentry->mmio32read_next = addr;
330
331 return 0;
332}
333
334static int mmio32write__write_file(struct b43_wldev *dev,
335 const char *buf, size_t count)
336{
Michael Bueschefa27582008-06-19 19:38:32 +0200337 unsigned int addr, mask, set;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200338 int res;
Michael Bueschefa27582008-06-19 19:38:32 +0200339 u32 val;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200340
Michael Bueschefa27582008-06-19 19:38:32 +0200341 res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);
342 if (res != 3)
Michael Buesch8bd463f2008-06-19 00:35:49 +0200343 return -EINVAL;
344 if (addr > B43_MAX_MMIO_ACCESS)
345 return -EADDRNOTAVAIL;
Michael Bueschefa27582008-06-19 19:38:32 +0200346 if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))
Michael Buesch8bd463f2008-06-19 00:35:49 +0200347 return -E2BIG;
Michael Bueschefa27582008-06-19 19:38:32 +0200348 if ((addr % 4) != 0)
349 return -EINVAL;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200350
Michael Bueschefa27582008-06-19 19:38:32 +0200351 if (mask == 0)
352 val = 0;
353 else
354 val = b43_read32(dev, addr);
355 val &= mask;
356 val |= set;
Michael Buesch8bd463f2008-06-19 00:35:49 +0200357 b43_write32(dev, addr, val);
358
359 return 0;
360}
361
Michael Buesch1a094042007-09-20 11:13:40 -0700362static ssize_t txstat_read_file(struct b43_wldev *dev,
363 char *buf, size_t bufsize)
Michael Buesche4d6b792007-09-18 15:39:42 -0400364{
365 struct b43_txstatus_log *log = &dev->dfsentry->txstatlog;
366 ssize_t count = 0;
Michael Buesche4d6b792007-09-18 15:39:42 -0400367 int i, idx;
368 struct b43_txstatus *stat;
369
Michael Buesche4d6b792007-09-18 15:39:42 -0400370 if (log->end < 0) {
371 fappend("Nothing transmitted, yet\n");
Michael Buesch36dbd952009-09-04 22:51:29 +0200372 goto out;
Michael Buesche4d6b792007-09-18 15:39:42 -0400373 }
374 fappend("b43 TX status reports:\n\n"
375 "index | cookie | seq | phy_stat | frame_count | "
376 "rts_count | supp_reason | pm_indicated | "
377 "intermediate | for_ampdu | acked\n" "---\n");
378 i = log->end + 1;
379 idx = 0;
380 while (1) {
381 if (i == B43_NR_LOGGED_TXSTATUS)
382 i = 0;
383 stat = &(log->log[i]);
384 if (stat->cookie) {
385 fappend("%03d | "
386 "0x%04X | 0x%04X | 0x%02X | "
387 "0x%X | 0x%X | "
388 "%u | %u | "
389 "%u | %u | %u\n",
390 idx,
391 stat->cookie, stat->seq, stat->phy_stat,
392 stat->frame_count, stat->rts_count,
393 stat->supp_reason, stat->pm_indicated,
394 stat->intermediate, stat->for_ampdu,
395 stat->acked);
396 idx++;
397 }
398 if (i == log->end)
399 break;
400 i++;
401 }
Michael Buesch36dbd952009-09-04 22:51:29 +0200402out:
Michael Buesche4d6b792007-09-18 15:39:42 -0400403
404 return count;
405}
406
Michael Buesch1a094042007-09-20 11:13:40 -0700407static int restart_write_file(struct b43_wldev *dev,
408 const char *buf, size_t count)
Michael Buesche4d6b792007-09-18 15:39:42 -0400409{
410 int err = 0;
411
412 if (count > 0 && buf[0] == '1') {
413 b43_controller_restart(dev, "manually restarted");
414 } else
415 err = -EINVAL;
416
417 return err;
418}
419
Michael Bueschf5eda472008-04-20 16:03:32 +0200420static unsigned long calc_expire_secs(unsigned long now,
421 unsigned long time,
422 unsigned long expire)
Michael Buesche4d6b792007-09-18 15:39:42 -0400423{
Michael Bueschf5eda472008-04-20 16:03:32 +0200424 expire = time + expire;
Michael Buesche4d6b792007-09-18 15:39:42 -0400425
Michael Bueschf5eda472008-04-20 16:03:32 +0200426 if (time_after(now, expire))
427 return 0; /* expired */
428 if (expire < now) {
429 /* jiffies wrapped */
430 expire -= MAX_JIFFY_OFFSET;
431 now -= MAX_JIFFY_OFFSET;
Michael Buesche4d6b792007-09-18 15:39:42 -0400432 }
Michael Bueschf5eda472008-04-20 16:03:32 +0200433 B43_WARN_ON(expire < now);
Michael Buesche4d6b792007-09-18 15:39:42 -0400434
Michael Bueschf5eda472008-04-20 16:03:32 +0200435 return (expire - now) / HZ;
Michael Buesche4d6b792007-09-18 15:39:42 -0400436}
437
Michael Buesch1a094042007-09-20 11:13:40 -0700438static ssize_t loctls_read_file(struct b43_wldev *dev,
439 char *buf, size_t bufsize)
Michael Buesche4d6b792007-09-18 15:39:42 -0400440{
441 ssize_t count = 0;
442 struct b43_txpower_lo_control *lo;
443 int i, err = 0;
Michael Bueschf5eda472008-04-20 16:03:32 +0200444 struct b43_lo_calib *cal;
445 unsigned long now = jiffies;
446 struct b43_phy *phy = &dev->phy;
Michael Buesche4d6b792007-09-18 15:39:42 -0400447
Michael Bueschf5eda472008-04-20 16:03:32 +0200448 if (phy->type != B43_PHYTYPE_G) {
Michael Buesche4d6b792007-09-18 15:39:42 -0400449 fappend("Device is not a G-PHY\n");
450 err = -ENODEV;
451 goto out;
452 }
Michael Bueschef1a6282008-08-27 18:53:02 +0200453 lo = phy->g->lo_control;
Michael Buesche4d6b792007-09-18 15:39:42 -0400454 fappend("-- Local Oscillator calibration data --\n\n");
Michael Bueschf5eda472008-04-20 16:03:32 +0200455 fappend("HW-power-control enabled: %d\n",
Michael Buesche4d6b792007-09-18 15:39:42 -0400456 dev->phy.hardware_power_control);
Michael Bueschf5eda472008-04-20 16:03:32 +0200457 fappend("TX Bias: 0x%02X, TX Magn: 0x%02X (expire in %lu sec)\n",
458 lo->tx_bias, lo->tx_magn,
459 calc_expire_secs(now, lo->txctl_measured_time,
460 B43_LO_TXCTL_EXPIRE));
461 fappend("Power Vector: 0x%08X%08X (expires in %lu sec)\n",
Michael Buesche4d6b792007-09-18 15:39:42 -0400462 (unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32),
Michael Bueschf5eda472008-04-20 16:03:32 +0200463 (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL),
464 calc_expire_secs(now, lo->pwr_vec_read_time,
465 B43_LO_PWRVEC_EXPIRE));
466
467 fappend("\nCalibrated settings:\n");
468 list_for_each_entry(cal, &lo->calib_list, list) {
469 bool active;
470
Michael Bueschef1a6282008-08-27 18:53:02 +0200471 active = (b43_compare_bbatt(&cal->bbatt, &phy->g->bbatt) &&
472 b43_compare_rfatt(&cal->rfatt, &phy->g->rfatt));
Michael Bueschf5eda472008-04-20 16:03:32 +0200473 fappend("BB(%d), RF(%d,%d) -> I=%d, Q=%d "
474 "(expires in %lu sec)%s\n",
475 cal->bbatt.att,
476 cal->rfatt.att, cal->rfatt.with_padmix,
477 cal->ctl.i, cal->ctl.q,
478 calc_expire_secs(now, cal->calib_time,
479 B43_LO_CALIB_EXPIRE),
480 active ? " ACTIVE" : "");
481 }
482
Michael Buesche4d6b792007-09-18 15:39:42 -0400483 fappend("\nUsed RF attenuation values: Value(WithPadmix flag)\n");
484 for (i = 0; i < lo->rfatt_list.len; i++) {
485 fappend("%u(%d), ",
486 lo->rfatt_list.list[i].att,
487 lo->rfatt_list.list[i].with_padmix);
488 }
489 fappend("\n");
490 fappend("\nUsed Baseband attenuation values:\n");
491 for (i = 0; i < lo->bbatt_list.len; i++) {
492 fappend("%u, ",
493 lo->bbatt_list.list[i].att);
494 }
495 fappend("\n");
496
497out:
498 return err ? err : count;
499}
500
501#undef fappend
502
Michael Buesche4d6b792007-09-18 15:39:42 -0400503static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
504 size_t count, loff_t *ppos)
505{
506 struct b43_wldev *dev;
507 struct b43_debugfs_fops *dfops;
508 struct b43_dfs_file *dfile;
Frank Lichtenheld7223e8d2007-11-12 11:12:52 +0100509 ssize_t uninitialized_var(ret);
Michael Buesche4d6b792007-09-18 15:39:42 -0400510 char *buf;
Michael Bueschf5eda472008-04-20 16:03:32 +0200511 const size_t bufsize = 1024 * 16; /* 16 kiB buffer */
Michael Buesche4d6b792007-09-18 15:39:42 -0400512 const size_t buforder = get_order(bufsize);
513 int err = 0;
514
515 if (!count)
516 return 0;
517 dev = file->private_data;
518 if (!dev)
519 return -ENODEV;
520
521 mutex_lock(&dev->wl->mutex);
522 if (b43_status(dev) < B43_STAT_INITIALIZED) {
523 err = -ENODEV;
524 goto out_unlock;
525 }
526
527 dfops = container_of(file->f_op, struct b43_debugfs_fops, fops);
528 if (!dfops->read) {
529 err = -ENOSYS;
530 goto out_unlock;
531 }
532 dfile = fops_to_dfs_file(dev, dfops);
533
534 if (!dfile->buffer) {
535 buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
536 if (!buf) {
537 err = -ENOMEM;
538 goto out_unlock;
539 }
Michael Buesche4d6b792007-09-18 15:39:42 -0400540 memset(buf, 0, bufsize);
Michael Buesch36dbd952009-09-04 22:51:29 +0200541 ret = dfops->read(dev, buf, bufsize);
Michael Buesche4d6b792007-09-18 15:39:42 -0400542 if (ret <= 0) {
543 free_pages((unsigned long)buf, buforder);
544 err = ret;
545 goto out_unlock;
546 }
547 dfile->data_len = ret;
548 dfile->buffer = buf;
549 }
550
551 ret = simple_read_from_buffer(userbuf, count, ppos,
552 dfile->buffer,
553 dfile->data_len);
554 if (*ppos >= dfile->data_len) {
555 free_pages((unsigned long)dfile->buffer, buforder);
556 dfile->buffer = NULL;
557 dfile->data_len = 0;
558 }
559out_unlock:
560 mutex_unlock(&dev->wl->mutex);
561
562 return err ? err : ret;
563}
564
565static ssize_t b43_debugfs_write(struct file *file,
566 const char __user *userbuf,
567 size_t count, loff_t *ppos)
568{
569 struct b43_wldev *dev;
570 struct b43_debugfs_fops *dfops;
571 char *buf;
572 int err = 0;
573
574 if (!count)
575 return 0;
576 if (count > PAGE_SIZE)
577 return -E2BIG;
578 dev = file->private_data;
579 if (!dev)
580 return -ENODEV;
581
582 mutex_lock(&dev->wl->mutex);
583 if (b43_status(dev) < B43_STAT_INITIALIZED) {
584 err = -ENODEV;
585 goto out_unlock;
586 }
587
588 dfops = container_of(file->f_op, struct b43_debugfs_fops, fops);
589 if (!dfops->write) {
590 err = -ENOSYS;
591 goto out_unlock;
592 }
593
594 buf = (char *)get_zeroed_page(GFP_KERNEL);
595 if (!buf) {
596 err = -ENOMEM;
597 goto out_unlock;
598 }
599 if (copy_from_user(buf, userbuf, count)) {
600 err = -EFAULT;
601 goto out_freepage;
602 }
Michael Buesch36dbd952009-09-04 22:51:29 +0200603 err = dfops->write(dev, buf, count);
Michael Buesche4d6b792007-09-18 15:39:42 -0400604 if (err)
605 goto out_freepage;
606
607out_freepage:
608 free_page((unsigned long)buf);
609out_unlock:
610 mutex_unlock(&dev->wl->mutex);
611
612 return err ? err : count;
613}
614
615
Michael Buesch36dbd952009-09-04 22:51:29 +0200616#define B43_DEBUGFS_FOPS(name, _read, _write) \
Michael Buesche4d6b792007-09-18 15:39:42 -0400617 static struct b43_debugfs_fops fops_##name = { \
618 .read = _read, \
619 .write = _write, \
620 .fops = { \
Stephen Boyd234e3402012-04-05 14:25:11 -0700621 .open = simple_open, \
Michael Buesche4d6b792007-09-18 15:39:42 -0400622 .read = b43_debugfs_read, \
623 .write = b43_debugfs_write, \
Arnd Bergmann2b18ab362010-07-06 19:05:31 +0200624 .llseek = generic_file_llseek, \
Michael Buesche4d6b792007-09-18 15:39:42 -0400625 }, \
626 .file_struct_offset = offsetof(struct b43_dfsentry, \
627 file_##name), \
Michael Buesche4d6b792007-09-18 15:39:42 -0400628 }
629
Michael Buesch36dbd952009-09-04 22:51:29 +0200630B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file);
631B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file);
632B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file);
633B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file);
634B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file);
635B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file);
636B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file);
637B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file);
638B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL);
639B43_DEBUGFS_FOPS(restart, NULL, restart_write_file);
640B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL);
Michael Buesche4d6b792007-09-18 15:39:42 -0400641
642
Michael Buesch060210f2009-01-25 15:49:59 +0100643bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)
Michael Buesche4d6b792007-09-18 15:39:42 -0400644{
Michael Buesch060210f2009-01-25 15:49:59 +0100645 bool enabled;
646
647 enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
648 if (unlikely(enabled)) {
649 /* Force full debugging messages, if the user enabled
650 * some dynamic debugging feature. */
651 b43_modparam_verbose = B43_VERBOSITY_MAX;
652 }
653
654 return enabled;
Michael Buesche4d6b792007-09-18 15:39:42 -0400655}
656
657static void b43_remove_dynamic_debug(struct b43_wldev *dev)
658{
659 struct b43_dfsentry *e = dev->dfsentry;
660 int i;
661
662 for (i = 0; i < __B43_NR_DYNDBG; i++)
663 debugfs_remove(e->dyn_debug_dentries[i]);
664}
665
666static void b43_add_dynamic_debug(struct b43_wldev *dev)
667{
668 struct b43_dfsentry *e = dev->dfsentry;
669 struct dentry *d;
670
671#define add_dyn_dbg(name, id, initstate) do { \
672 e->dyn_debug[id] = (initstate); \
673 d = debugfs_create_bool(name, 0600, e->subdir, \
674 &(e->dyn_debug[id])); \
675 if (!IS_ERR(d)) \
676 e->dyn_debug_dentries[id] = d; \
677 } while (0)
678
679 add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, 0);
680 add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, 0);
681 add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, 0);
682 add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, 0);
683 add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, 0);
Michael Bueschf5eda472008-04-20 16:03:32 +0200684 add_dyn_dbg("debug_lo", B43_DBG_LO, 0);
Michael Buesch923fd702008-06-20 18:02:08 +0200685 add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, 0);
Michael Buesch9cf7f242008-12-19 20:24:30 +0100686 add_dyn_dbg("debug_keys", B43_DBG_KEYS, 0);
Michael Buesch990b86f2009-09-12 00:48:03 +0200687 add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, 0);
Michael Buesche4d6b792007-09-18 15:39:42 -0400688
689#undef add_dyn_dbg
690}
691
692void b43_debugfs_add_device(struct b43_wldev *dev)
693{
694 struct b43_dfsentry *e;
695 struct b43_txstatus_log *log;
696 char devdir[16];
697
698 B43_WARN_ON(!dev);
699 e = kzalloc(sizeof(*e), GFP_KERNEL);
700 if (!e) {
701 b43err(dev->wl, "debugfs: add device OOM\n");
702 return;
703 }
704 e->dev = dev;
705 log = &e->txstatlog;
706 log->log = kcalloc(B43_NR_LOGGED_TXSTATUS,
707 sizeof(struct b43_txstatus), GFP_KERNEL);
708 if (!log->log) {
709 b43err(dev->wl, "debugfs: add device txstatus OOM\n");
710 kfree(e);
711 return;
712 }
713 log->end = -1;
Michael Buesche4d6b792007-09-18 15:39:42 -0400714
715 dev->dfsentry = e;
716
717 snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
718 e->subdir = debugfs_create_dir(devdir, rootdir);
719 if (!e->subdir || IS_ERR(e->subdir)) {
720 if (e->subdir == ERR_PTR(-ENODEV)) {
721 b43dbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not "
722 "enabled in kernel config\n");
723 } else {
724 b43err(dev->wl, "debugfs: cannot create %s directory\n",
725 devdir);
726 }
727 dev->dfsentry = NULL;
728 kfree(log->log);
729 kfree(e);
730 return;
731 }
732
Michael Buesch8bd463f2008-06-19 00:35:49 +0200733 e->mmio16read_next = 0xFFFF; /* invalid address */
734 e->mmio32read_next = 0xFFFF; /* invalid address */
Michael Buesch6bbc3212008-06-19 19:33:51 +0200735 e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */
736 e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */
737 e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */
738 e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */
Michael Buesch8bd463f2008-06-19 00:35:49 +0200739
Michael Buesche4d6b792007-09-18 15:39:42 -0400740#define ADD_FILE(name, mode) \
741 do { \
742 struct dentry *d; \
743 d = debugfs_create_file(__stringify(name), \
744 mode, e->subdir, dev, \
745 &fops_##name.fops); \
746 e->file_##name.dentry = NULL; \
747 if (!IS_ERR(d)) \
748 e->file_##name.dentry = d; \
749 } while (0)
750
751
Michael Buesch6bbc3212008-06-19 19:33:51 +0200752 ADD_FILE(shm16read, 0600);
753 ADD_FILE(shm16write, 0200);
754 ADD_FILE(shm32read, 0600);
755 ADD_FILE(shm32write, 0200);
Michael Buesch8bd463f2008-06-19 00:35:49 +0200756 ADD_FILE(mmio16read, 0600);
757 ADD_FILE(mmio16write, 0200);
758 ADD_FILE(mmio32read, 0600);
759 ADD_FILE(mmio32write, 0200);
Michael Buesche4d6b792007-09-18 15:39:42 -0400760 ADD_FILE(txstat, 0400);
Michael Buesche4d6b792007-09-18 15:39:42 -0400761 ADD_FILE(restart, 0200);
762 ADD_FILE(loctls, 0400);
763
764#undef ADD_FILE
765
766 b43_add_dynamic_debug(dev);
767}
768
769void b43_debugfs_remove_device(struct b43_wldev *dev)
770{
771 struct b43_dfsentry *e;
772
773 if (!dev)
774 return;
775 e = dev->dfsentry;
776 if (!e)
777 return;
778 b43_remove_dynamic_debug(dev);
779
Michael Buesch6bbc3212008-06-19 19:33:51 +0200780 debugfs_remove(e->file_shm16read.dentry);
781 debugfs_remove(e->file_shm16write.dentry);
782 debugfs_remove(e->file_shm32read.dentry);
783 debugfs_remove(e->file_shm32write.dentry);
Michael Buesch8bd463f2008-06-19 00:35:49 +0200784 debugfs_remove(e->file_mmio16read.dentry);
785 debugfs_remove(e->file_mmio16write.dentry);
786 debugfs_remove(e->file_mmio32read.dentry);
787 debugfs_remove(e->file_mmio32write.dentry);
Michael Buesche4d6b792007-09-18 15:39:42 -0400788 debugfs_remove(e->file_txstat.dentry);
Michael Buesche4d6b792007-09-18 15:39:42 -0400789 debugfs_remove(e->file_restart.dentry);
790 debugfs_remove(e->file_loctls.dentry);
791
792 debugfs_remove(e->subdir);
793 kfree(e->txstatlog.log);
794 kfree(e);
795}
796
797void b43_debugfs_log_txstat(struct b43_wldev *dev,
798 const struct b43_txstatus *status)
799{
800 struct b43_dfsentry *e = dev->dfsentry;
801 struct b43_txstatus_log *log;
802 struct b43_txstatus *cur;
803 int i;
804
805 if (!e)
806 return;
807 log = &e->txstatlog;
Michael Buesche4d6b792007-09-18 15:39:42 -0400808 i = log->end + 1;
809 if (i == B43_NR_LOGGED_TXSTATUS)
810 i = 0;
811 log->end = i;
812 cur = &(log->log[i]);
813 memcpy(cur, status, sizeof(*cur));
Michael Buesche4d6b792007-09-18 15:39:42 -0400814}
815
816void b43_debugfs_init(void)
817{
818 rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
819 if (IS_ERR(rootdir))
820 rootdir = NULL;
821}
822
823void b43_debugfs_exit(void)
824{
825 debugfs_remove(rootdir);
826}