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