1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | |
4 | Broadcom B43 wireless driver |
5 | |
6 | debugfs driver debugging code |
7 | |
8 | Copyright (c) 2005-2007 Michael Buesch <m@bues.ch> |
9 | |
10 | |
11 | */ |
12 | |
13 | #include <linux/fs.h> |
14 | #include <linux/debugfs.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/netdevice.h> |
17 | #include <linux/pci.h> |
18 | #include <linux/mutex.h> |
19 | |
20 | #include "b43.h" |
21 | #include "main.h" |
22 | #include "debugfs.h" |
23 | #include "dma.h" |
24 | #include "xmit.h" |
25 | |
26 | |
27 | /* The root directory. */ |
28 | static struct dentry *rootdir; |
29 | |
30 | struct b43_debugfs_fops { |
31 | ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize); |
32 | int (*write)(struct b43_wldev *dev, const char *buf, size_t count); |
33 | struct file_operations fops; |
34 | /* Offset of struct b43_dfs_file in struct b43_dfsentry */ |
35 | size_t file_struct_offset; |
36 | }; |
37 | |
38 | static inline |
39 | struct b43_dfs_file *fops_to_dfs_file(struct b43_wldev *dev, |
40 | const struct b43_debugfs_fops *dfops) |
41 | { |
42 | void *p; |
43 | |
44 | p = dev->dfsentry; |
45 | p += dfops->file_struct_offset; |
46 | |
47 | return p; |
48 | } |
49 | |
50 | |
51 | #define fappend(fmt, x...) \ |
52 | do { \ |
53 | if (bufsize - count) \ |
54 | count += scnprintf(buf + count, \ |
55 | bufsize - count, \ |
56 | fmt , ##x); \ |
57 | else \ |
58 | printk(KERN_ERR "b43: fappend overflow\n"); \ |
59 | } while (0) |
60 | |
61 | |
62 | /* The biggest address values for SHM access from the debugfs files. */ |
63 | #define B43_MAX_SHM_ROUTING 4 |
64 | #define B43_MAX_SHM_ADDR 0xFFFF |
65 | |
66 | static ssize_t shm16read__read_file(struct b43_wldev *dev, |
67 | char *buf, size_t bufsize) |
68 | { |
69 | ssize_t count = 0; |
70 | unsigned int routing, addr; |
71 | u16 val; |
72 | |
73 | routing = dev->dfsentry->shm16read_routing_next; |
74 | addr = dev->dfsentry->shm16read_addr_next; |
75 | if ((routing > B43_MAX_SHM_ROUTING) || |
76 | (addr > B43_MAX_SHM_ADDR)) |
77 | return -EDESTADDRREQ; |
78 | |
79 | val = b43_shm_read16(dev, routing, offset: addr); |
80 | fappend("0x%04X\n" , val); |
81 | |
82 | return count; |
83 | } |
84 | |
85 | static int shm16read__write_file(struct b43_wldev *dev, |
86 | const char *buf, size_t count) |
87 | { |
88 | unsigned int routing, addr; |
89 | int res; |
90 | |
91 | res = sscanf(buf, "0x%X 0x%X" , &routing, &addr); |
92 | if (res != 2) |
93 | return -EINVAL; |
94 | if (routing > B43_MAX_SHM_ROUTING) |
95 | return -EADDRNOTAVAIL; |
96 | if (addr > B43_MAX_SHM_ADDR) |
97 | return -EADDRNOTAVAIL; |
98 | if (routing == B43_SHM_SHARED) { |
99 | if ((addr % 2) != 0) |
100 | return -EADDRNOTAVAIL; |
101 | } |
102 | |
103 | dev->dfsentry->shm16read_routing_next = routing; |
104 | dev->dfsentry->shm16read_addr_next = addr; |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | static int shm16write__write_file(struct b43_wldev *dev, |
110 | const char *buf, size_t count) |
111 | { |
112 | unsigned int routing, addr, mask, set; |
113 | u16 val; |
114 | int res; |
115 | |
116 | res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X" , |
117 | &routing, &addr, &mask, &set); |
118 | if (res != 4) |
119 | return -EINVAL; |
120 | if (routing > B43_MAX_SHM_ROUTING) |
121 | return -EADDRNOTAVAIL; |
122 | if (addr > B43_MAX_SHM_ADDR) |
123 | return -EADDRNOTAVAIL; |
124 | if (routing == B43_SHM_SHARED) { |
125 | if ((addr % 2) != 0) |
126 | return -EADDRNOTAVAIL; |
127 | } |
128 | if ((mask > 0xFFFF) || (set > 0xFFFF)) |
129 | return -E2BIG; |
130 | |
131 | if (mask == 0) |
132 | val = 0; |
133 | else |
134 | val = b43_shm_read16(dev, routing, offset: addr); |
135 | val &= mask; |
136 | val |= set; |
137 | b43_shm_write16(dev, routing, offset: addr, value: val); |
138 | |
139 | return 0; |
140 | } |
141 | |
142 | static ssize_t shm32read__read_file(struct b43_wldev *dev, |
143 | char *buf, size_t bufsize) |
144 | { |
145 | ssize_t count = 0; |
146 | unsigned int routing, addr; |
147 | u32 val; |
148 | |
149 | routing = dev->dfsentry->shm32read_routing_next; |
150 | addr = dev->dfsentry->shm32read_addr_next; |
151 | if ((routing > B43_MAX_SHM_ROUTING) || |
152 | (addr > B43_MAX_SHM_ADDR)) |
153 | return -EDESTADDRREQ; |
154 | |
155 | val = b43_shm_read32(dev, routing, offset: addr); |
156 | fappend("0x%08X\n" , val); |
157 | |
158 | return count; |
159 | } |
160 | |
161 | static int shm32read__write_file(struct b43_wldev *dev, |
162 | const char *buf, size_t count) |
163 | { |
164 | unsigned int routing, addr; |
165 | int res; |
166 | |
167 | res = sscanf(buf, "0x%X 0x%X" , &routing, &addr); |
168 | if (res != 2) |
169 | return -EINVAL; |
170 | if (routing > B43_MAX_SHM_ROUTING) |
171 | return -EADDRNOTAVAIL; |
172 | if (addr > B43_MAX_SHM_ADDR) |
173 | return -EADDRNOTAVAIL; |
174 | if (routing == B43_SHM_SHARED) { |
175 | if ((addr % 2) != 0) |
176 | return -EADDRNOTAVAIL; |
177 | } |
178 | |
179 | dev->dfsentry->shm32read_routing_next = routing; |
180 | dev->dfsentry->shm32read_addr_next = addr; |
181 | |
182 | return 0; |
183 | } |
184 | |
185 | static int shm32write__write_file(struct b43_wldev *dev, |
186 | const char *buf, size_t count) |
187 | { |
188 | unsigned int routing, addr, mask, set; |
189 | u32 val; |
190 | int res; |
191 | |
192 | res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X" , |
193 | &routing, &addr, &mask, &set); |
194 | if (res != 4) |
195 | return -EINVAL; |
196 | if (routing > B43_MAX_SHM_ROUTING) |
197 | return -EADDRNOTAVAIL; |
198 | if (addr > B43_MAX_SHM_ADDR) |
199 | return -EADDRNOTAVAIL; |
200 | if (routing == B43_SHM_SHARED) { |
201 | if ((addr % 2) != 0) |
202 | return -EADDRNOTAVAIL; |
203 | } |
204 | if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF)) |
205 | return -E2BIG; |
206 | |
207 | if (mask == 0) |
208 | val = 0; |
209 | else |
210 | val = b43_shm_read32(dev, routing, offset: addr); |
211 | val &= mask; |
212 | val |= set; |
213 | b43_shm_write32(dev, routing, offset: addr, value: val); |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | /* The biggest MMIO address that we allow access to from the debugfs files. */ |
219 | #define B43_MAX_MMIO_ACCESS (0xF00 - 1) |
220 | |
221 | static ssize_t mmio16read__read_file(struct b43_wldev *dev, |
222 | char *buf, size_t bufsize) |
223 | { |
224 | ssize_t count = 0; |
225 | unsigned int addr; |
226 | u16 val; |
227 | |
228 | addr = dev->dfsentry->mmio16read_next; |
229 | if (addr > B43_MAX_MMIO_ACCESS) |
230 | return -EDESTADDRREQ; |
231 | |
232 | val = b43_read16(dev, offset: addr); |
233 | fappend("0x%04X\n" , val); |
234 | |
235 | return count; |
236 | } |
237 | |
238 | static int mmio16read__write_file(struct b43_wldev *dev, |
239 | const char *buf, size_t count) |
240 | { |
241 | unsigned int addr; |
242 | int res; |
243 | |
244 | res = sscanf(buf, "0x%X" , &addr); |
245 | if (res != 1) |
246 | return -EINVAL; |
247 | if (addr > B43_MAX_MMIO_ACCESS) |
248 | return -EADDRNOTAVAIL; |
249 | if ((addr % 2) != 0) |
250 | return -EINVAL; |
251 | |
252 | dev->dfsentry->mmio16read_next = addr; |
253 | |
254 | return 0; |
255 | } |
256 | |
257 | static int mmio16write__write_file(struct b43_wldev *dev, |
258 | const char *buf, size_t count) |
259 | { |
260 | unsigned int addr, mask, set; |
261 | int res; |
262 | u16 val; |
263 | |
264 | res = sscanf(buf, "0x%X 0x%X 0x%X" , &addr, &mask, &set); |
265 | if (res != 3) |
266 | return -EINVAL; |
267 | if (addr > B43_MAX_MMIO_ACCESS) |
268 | return -EADDRNOTAVAIL; |
269 | if ((mask > 0xFFFF) || (set > 0xFFFF)) |
270 | return -E2BIG; |
271 | if ((addr % 2) != 0) |
272 | return -EINVAL; |
273 | |
274 | if (mask == 0) |
275 | val = 0; |
276 | else |
277 | val = b43_read16(dev, offset: addr); |
278 | val &= mask; |
279 | val |= set; |
280 | b43_write16(dev, offset: addr, value: val); |
281 | |
282 | return 0; |
283 | } |
284 | |
285 | static ssize_t mmio32read__read_file(struct b43_wldev *dev, |
286 | char *buf, size_t bufsize) |
287 | { |
288 | ssize_t count = 0; |
289 | unsigned int addr; |
290 | u32 val; |
291 | |
292 | addr = dev->dfsentry->mmio32read_next; |
293 | if (addr > B43_MAX_MMIO_ACCESS) |
294 | return -EDESTADDRREQ; |
295 | |
296 | val = b43_read32(dev, offset: addr); |
297 | fappend("0x%08X\n" , val); |
298 | |
299 | return count; |
300 | } |
301 | |
302 | static int mmio32read__write_file(struct b43_wldev *dev, |
303 | const char *buf, size_t count) |
304 | { |
305 | unsigned int addr; |
306 | int res; |
307 | |
308 | res = sscanf(buf, "0x%X" , &addr); |
309 | if (res != 1) |
310 | return -EINVAL; |
311 | if (addr > B43_MAX_MMIO_ACCESS) |
312 | return -EADDRNOTAVAIL; |
313 | if ((addr % 4) != 0) |
314 | return -EINVAL; |
315 | |
316 | dev->dfsentry->mmio32read_next = addr; |
317 | |
318 | return 0; |
319 | } |
320 | |
321 | static int mmio32write__write_file(struct b43_wldev *dev, |
322 | const char *buf, size_t count) |
323 | { |
324 | unsigned int addr, mask, set; |
325 | int res; |
326 | u32 val; |
327 | |
328 | res = sscanf(buf, "0x%X 0x%X 0x%X" , &addr, &mask, &set); |
329 | if (res != 3) |
330 | return -EINVAL; |
331 | if (addr > B43_MAX_MMIO_ACCESS) |
332 | return -EADDRNOTAVAIL; |
333 | if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF)) |
334 | return -E2BIG; |
335 | if ((addr % 4) != 0) |
336 | return -EINVAL; |
337 | |
338 | if (mask == 0) |
339 | val = 0; |
340 | else |
341 | val = b43_read32(dev, offset: addr); |
342 | val &= mask; |
343 | val |= set; |
344 | b43_write32(dev, offset: addr, value: val); |
345 | |
346 | return 0; |
347 | } |
348 | |
349 | static ssize_t txstat_read_file(struct b43_wldev *dev, |
350 | char *buf, size_t bufsize) |
351 | { |
352 | struct b43_txstatus_log *log = &dev->dfsentry->txstatlog; |
353 | ssize_t count = 0; |
354 | int i, idx; |
355 | struct b43_txstatus *stat; |
356 | |
357 | if (log->end < 0) { |
358 | fappend("Nothing transmitted, yet\n" ); |
359 | goto out; |
360 | } |
361 | fappend("b43 TX status reports:\n\n" |
362 | "index | cookie | seq | phy_stat | frame_count | " |
363 | "rts_count | supp_reason | pm_indicated | " |
364 | "intermediate | for_ampdu | acked\n" "---\n" ); |
365 | i = log->end + 1; |
366 | idx = 0; |
367 | while (1) { |
368 | if (i == B43_NR_LOGGED_TXSTATUS) |
369 | i = 0; |
370 | stat = &(log->log[i]); |
371 | if (stat->cookie) { |
372 | fappend("%03d | " |
373 | "0x%04X | 0x%04X | 0x%02X | " |
374 | "0x%X | 0x%X | " |
375 | "%u | %u | " |
376 | "%u | %u | %u\n" , |
377 | idx, |
378 | stat->cookie, stat->seq, stat->phy_stat, |
379 | stat->frame_count, stat->rts_count, |
380 | stat->supp_reason, stat->pm_indicated, |
381 | stat->intermediate, stat->for_ampdu, |
382 | stat->acked); |
383 | idx++; |
384 | } |
385 | if (i == log->end) |
386 | break; |
387 | i++; |
388 | } |
389 | out: |
390 | |
391 | return count; |
392 | } |
393 | |
394 | static int restart_write_file(struct b43_wldev *dev, |
395 | const char *buf, size_t count) |
396 | { |
397 | int err = 0; |
398 | |
399 | if (count > 0 && buf[0] == '1') { |
400 | b43_controller_restart(dev, reason: "manually restarted" ); |
401 | } else |
402 | err = -EINVAL; |
403 | |
404 | return err; |
405 | } |
406 | |
407 | static unsigned long calc_expire_secs(unsigned long now, |
408 | unsigned long time, |
409 | unsigned long expire) |
410 | { |
411 | expire = time + expire; |
412 | |
413 | if (time_after(now, expire)) |
414 | return 0; /* expired */ |
415 | if (expire < now) { |
416 | /* jiffies wrapped */ |
417 | expire -= MAX_JIFFY_OFFSET; |
418 | now -= MAX_JIFFY_OFFSET; |
419 | } |
420 | B43_WARN_ON(expire < now); |
421 | |
422 | return (expire - now) / HZ; |
423 | } |
424 | |
425 | static ssize_t loctls_read_file(struct b43_wldev *dev, |
426 | char *buf, size_t bufsize) |
427 | { |
428 | ssize_t count = 0; |
429 | struct b43_txpower_lo_control *lo; |
430 | int i, err = 0; |
431 | struct b43_lo_calib *cal; |
432 | unsigned long now = jiffies; |
433 | struct b43_phy *phy = &dev->phy; |
434 | |
435 | if (phy->type != B43_PHYTYPE_G) { |
436 | fappend("Device is not a G-PHY\n" ); |
437 | err = -ENODEV; |
438 | goto out; |
439 | } |
440 | lo = phy->g->lo_control; |
441 | fappend("-- Local Oscillator calibration data --\n\n" ); |
442 | fappend("HW-power-control enabled: %d\n" , |
443 | dev->phy.hardware_power_control); |
444 | fappend("TX Bias: 0x%02X, TX Magn: 0x%02X (expire in %lu sec)\n" , |
445 | lo->tx_bias, lo->tx_magn, |
446 | calc_expire_secs(now, lo->txctl_measured_time, |
447 | B43_LO_TXCTL_EXPIRE)); |
448 | fappend("Power Vector: 0x%08X%08X (expires in %lu sec)\n" , |
449 | (unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32), |
450 | (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL), |
451 | calc_expire_secs(now, lo->pwr_vec_read_time, |
452 | B43_LO_PWRVEC_EXPIRE)); |
453 | |
454 | fappend("\nCalibrated settings:\n" ); |
455 | list_for_each_entry(cal, &lo->calib_list, list) { |
456 | bool active; |
457 | |
458 | active = (b43_compare_bbatt(a: &cal->bbatt, b: &phy->g->bbatt) && |
459 | b43_compare_rfatt(a: &cal->rfatt, b: &phy->g->rfatt)); |
460 | fappend("BB(%d), RF(%d,%d) -> I=%d, Q=%d " |
461 | "(expires in %lu sec)%s\n" , |
462 | cal->bbatt.att, |
463 | cal->rfatt.att, cal->rfatt.with_padmix, |
464 | cal->ctl.i, cal->ctl.q, |
465 | calc_expire_secs(now, cal->calib_time, |
466 | B43_LO_CALIB_EXPIRE), |
467 | active ? " ACTIVE" : "" ); |
468 | } |
469 | |
470 | fappend("\nUsed RF attenuation values: Value(WithPadmix flag)\n" ); |
471 | for (i = 0; i < lo->rfatt_list.len; i++) { |
472 | fappend("%u(%d), " , |
473 | lo->rfatt_list.list[i].att, |
474 | lo->rfatt_list.list[i].with_padmix); |
475 | } |
476 | fappend("\n" ); |
477 | fappend("\nUsed Baseband attenuation values:\n" ); |
478 | for (i = 0; i < lo->bbatt_list.len; i++) { |
479 | fappend("%u, " , |
480 | lo->bbatt_list.list[i].att); |
481 | } |
482 | fappend("\n" ); |
483 | |
484 | out: |
485 | return err ? err : count; |
486 | } |
487 | |
488 | #undef fappend |
489 | |
490 | static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf, |
491 | size_t count, loff_t *ppos) |
492 | { |
493 | struct b43_wldev *dev; |
494 | struct b43_debugfs_fops *dfops; |
495 | struct b43_dfs_file *dfile; |
496 | ssize_t ret; |
497 | char *buf; |
498 | const size_t bufsize = 1024 * 16; /* 16 kiB buffer */ |
499 | const size_t buforder = get_order(size: bufsize); |
500 | int err = 0; |
501 | |
502 | if (!count) |
503 | return 0; |
504 | dev = file->private_data; |
505 | if (!dev) |
506 | return -ENODEV; |
507 | |
508 | mutex_lock(&dev->wl->mutex); |
509 | if (b43_status(dev) < B43_STAT_INITIALIZED) { |
510 | err = -ENODEV; |
511 | goto out_unlock; |
512 | } |
513 | |
514 | dfops = container_of(debugfs_real_fops(file), |
515 | struct b43_debugfs_fops, fops); |
516 | if (!dfops->read) { |
517 | err = -ENOSYS; |
518 | goto out_unlock; |
519 | } |
520 | dfile = fops_to_dfs_file(dev, dfops); |
521 | |
522 | if (!dfile->buffer) { |
523 | buf = (char *)__get_free_pages(GFP_KERNEL, order: buforder); |
524 | if (!buf) { |
525 | err = -ENOMEM; |
526 | goto out_unlock; |
527 | } |
528 | memset(buf, 0, bufsize); |
529 | ret = dfops->read(dev, buf, bufsize); |
530 | if (ret <= 0) { |
531 | free_pages(addr: (unsigned long)buf, order: buforder); |
532 | err = ret; |
533 | goto out_unlock; |
534 | } |
535 | dfile->data_len = ret; |
536 | dfile->buffer = buf; |
537 | } |
538 | |
539 | ret = simple_read_from_buffer(to: userbuf, count, ppos, |
540 | from: dfile->buffer, |
541 | available: dfile->data_len); |
542 | if (*ppos >= dfile->data_len) { |
543 | free_pages(addr: (unsigned long)dfile->buffer, order: buforder); |
544 | dfile->buffer = NULL; |
545 | dfile->data_len = 0; |
546 | } |
547 | out_unlock: |
548 | mutex_unlock(lock: &dev->wl->mutex); |
549 | |
550 | return err ? err : ret; |
551 | } |
552 | |
553 | static ssize_t b43_debugfs_write(struct file *file, |
554 | const char __user *userbuf, |
555 | size_t count, loff_t *ppos) |
556 | { |
557 | struct b43_wldev *dev; |
558 | struct b43_debugfs_fops *dfops; |
559 | char *buf; |
560 | int err = 0; |
561 | |
562 | if (!count) |
563 | return 0; |
564 | if (count > PAGE_SIZE) |
565 | return -E2BIG; |
566 | dev = file->private_data; |
567 | if (!dev) |
568 | return -ENODEV; |
569 | |
570 | mutex_lock(&dev->wl->mutex); |
571 | if (b43_status(dev) < B43_STAT_INITIALIZED) { |
572 | err = -ENODEV; |
573 | goto out_unlock; |
574 | } |
575 | |
576 | dfops = container_of(debugfs_real_fops(file), |
577 | struct b43_debugfs_fops, fops); |
578 | if (!dfops->write) { |
579 | err = -ENOSYS; |
580 | goto out_unlock; |
581 | } |
582 | |
583 | buf = (char *)get_zeroed_page(GFP_KERNEL); |
584 | if (!buf) { |
585 | err = -ENOMEM; |
586 | goto out_unlock; |
587 | } |
588 | if (copy_from_user(to: buf, from: userbuf, n: count)) { |
589 | err = -EFAULT; |
590 | goto out_freepage; |
591 | } |
592 | err = dfops->write(dev, buf, count); |
593 | if (err) |
594 | goto out_freepage; |
595 | |
596 | out_freepage: |
597 | free_page((unsigned long)buf); |
598 | out_unlock: |
599 | mutex_unlock(lock: &dev->wl->mutex); |
600 | |
601 | return err ? err : count; |
602 | } |
603 | |
604 | |
605 | #define B43_DEBUGFS_FOPS(name, _read, _write) \ |
606 | static struct b43_debugfs_fops fops_##name = { \ |
607 | .read = _read, \ |
608 | .write = _write, \ |
609 | .fops = { \ |
610 | .open = simple_open, \ |
611 | .read = b43_debugfs_read, \ |
612 | .write = b43_debugfs_write, \ |
613 | .llseek = generic_file_llseek, \ |
614 | }, \ |
615 | .file_struct_offset = offsetof(struct b43_dfsentry, \ |
616 | file_##name), \ |
617 | } |
618 | |
619 | B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file); |
620 | B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file); |
621 | B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file); |
622 | B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file); |
623 | B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file); |
624 | B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file); |
625 | B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file); |
626 | B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file); |
627 | B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL); |
628 | B43_DEBUGFS_FOPS(restart, NULL, restart_write_file); |
629 | B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL); |
630 | |
631 | |
632 | bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) |
633 | { |
634 | bool enabled; |
635 | |
636 | enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]); |
637 | if (unlikely(enabled)) { |
638 | /* Force full debugging messages, if the user enabled |
639 | * some dynamic debugging feature. */ |
640 | b43_modparam_verbose = B43_VERBOSITY_MAX; |
641 | } |
642 | |
643 | return enabled; |
644 | } |
645 | |
646 | static void b43_add_dynamic_debug(struct b43_wldev *dev) |
647 | { |
648 | struct b43_dfsentry *e = dev->dfsentry; |
649 | |
650 | #define add_dyn_dbg(name, id, initstate) do { \ |
651 | e->dyn_debug[id] = (initstate); \ |
652 | debugfs_create_bool(name, 0600, e->subdir, \ |
653 | &(e->dyn_debug[id])); \ |
654 | } while (0) |
655 | |
656 | add_dyn_dbg("debug_xmitpower" , B43_DBG_XMITPOWER, false); |
657 | add_dyn_dbg("debug_dmaoverflow" , B43_DBG_DMAOVERFLOW, false); |
658 | add_dyn_dbg("debug_dmaverbose" , B43_DBG_DMAVERBOSE, false); |
659 | add_dyn_dbg("debug_pwork_fast" , B43_DBG_PWORK_FAST, false); |
660 | add_dyn_dbg("debug_pwork_stop" , B43_DBG_PWORK_STOP, false); |
661 | add_dyn_dbg("debug_lo" , B43_DBG_LO, false); |
662 | add_dyn_dbg("debug_firmware" , B43_DBG_FIRMWARE, false); |
663 | add_dyn_dbg("debug_keys" , B43_DBG_KEYS, false); |
664 | add_dyn_dbg("debug_verbose_stats" , B43_DBG_VERBOSESTATS, false); |
665 | |
666 | #undef add_dyn_dbg |
667 | } |
668 | |
669 | void b43_debugfs_add_device(struct b43_wldev *dev) |
670 | { |
671 | struct b43_dfsentry *e; |
672 | struct b43_txstatus_log *log; |
673 | char devdir[16]; |
674 | |
675 | B43_WARN_ON(!dev); |
676 | e = kzalloc(size: sizeof(*e), GFP_KERNEL); |
677 | if (!e) { |
678 | b43err(wl: dev->wl, fmt: "debugfs: add device OOM\n" ); |
679 | return; |
680 | } |
681 | e->dev = dev; |
682 | log = &e->txstatlog; |
683 | log->log = kcalloc(B43_NR_LOGGED_TXSTATUS, |
684 | size: sizeof(struct b43_txstatus), GFP_KERNEL); |
685 | if (!log->log) { |
686 | b43err(wl: dev->wl, fmt: "debugfs: add device txstatus OOM\n" ); |
687 | kfree(objp: e); |
688 | return; |
689 | } |
690 | log->end = -1; |
691 | |
692 | dev->dfsentry = e; |
693 | |
694 | snprintf(buf: devdir, size: sizeof(devdir), fmt: "%s" , wiphy_name(wiphy: dev->wl->hw->wiphy)); |
695 | e->subdir = debugfs_create_dir(name: devdir, parent: rootdir); |
696 | |
697 | e->mmio16read_next = 0xFFFF; /* invalid address */ |
698 | e->mmio32read_next = 0xFFFF; /* invalid address */ |
699 | e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */ |
700 | e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */ |
701 | e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */ |
702 | e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */ |
703 | |
704 | #define ADD_FILE(name, mode) \ |
705 | do { \ |
706 | debugfs_create_file(__stringify(name), \ |
707 | mode, e->subdir, dev, \ |
708 | &fops_##name.fops); \ |
709 | } while (0) |
710 | |
711 | |
712 | ADD_FILE(shm16read, 0600); |
713 | ADD_FILE(shm16write, 0200); |
714 | ADD_FILE(shm32read, 0600); |
715 | ADD_FILE(shm32write, 0200); |
716 | ADD_FILE(mmio16read, 0600); |
717 | ADD_FILE(mmio16write, 0200); |
718 | ADD_FILE(mmio32read, 0600); |
719 | ADD_FILE(mmio32write, 0200); |
720 | ADD_FILE(txstat, 0400); |
721 | ADD_FILE(restart, 0200); |
722 | ADD_FILE(loctls, 0400); |
723 | |
724 | #undef ADD_FILE |
725 | |
726 | b43_add_dynamic_debug(dev); |
727 | } |
728 | |
729 | void b43_debugfs_remove_device(struct b43_wldev *dev) |
730 | { |
731 | struct b43_dfsentry *e; |
732 | |
733 | if (!dev) |
734 | return; |
735 | e = dev->dfsentry; |
736 | if (!e) |
737 | return; |
738 | |
739 | debugfs_remove(dentry: e->subdir); |
740 | kfree(objp: e->txstatlog.log); |
741 | kfree(objp: e); |
742 | } |
743 | |
744 | void b43_debugfs_log_txstat(struct b43_wldev *dev, |
745 | const struct b43_txstatus *status) |
746 | { |
747 | struct b43_dfsentry *e = dev->dfsentry; |
748 | struct b43_txstatus_log *log; |
749 | struct b43_txstatus *cur; |
750 | int i; |
751 | |
752 | if (!e) |
753 | return; |
754 | log = &e->txstatlog; |
755 | i = log->end + 1; |
756 | if (i == B43_NR_LOGGED_TXSTATUS) |
757 | i = 0; |
758 | log->end = i; |
759 | cur = &(log->log[i]); |
760 | memcpy(cur, status, sizeof(*cur)); |
761 | } |
762 | |
763 | void b43_debugfs_init(void) |
764 | { |
765 | rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); |
766 | } |
767 | |
768 | void b43_debugfs_exit(void) |
769 | { |
770 | debugfs_remove(dentry: rootdir); |
771 | } |
772 | |