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 int err = 0; 499 500 if (!count) 501 return 0; 502 dev = file->private_data; 503 if (!dev) 504 return -ENODEV; 505 506 mutex_lock(&dev->wl->mutex); 507 if (b43_status(dev) < B43_STAT_INITIALIZED) { 508 err = -ENODEV; 509 goto out_unlock; 510 } 511 512 dfops = debugfs_get_aux(file); 513 if (!dfops->read) { 514 err = -ENOSYS; 515 goto out_unlock; 516 } 517 dfile = fops_to_dfs_file(dev, dfops); 518 519 if (!dfile->buffer) { 520 buf = kzalloc(bufsize, GFP_KERNEL); 521 if (!buf) { 522 err = -ENOMEM; 523 goto out_unlock; 524 } 525 ret = dfops->read(dev, buf, bufsize); 526 if (ret <= 0) { 527 kfree(buf); 528 err = ret; 529 goto out_unlock; 530 } 531 dfile->data_len = ret; 532 dfile->buffer = buf; 533 } 534 535 ret = simple_read_from_buffer(userbuf, count, ppos, 536 dfile->buffer, 537 dfile->data_len); 538 if (*ppos >= dfile->data_len) { 539 kfree(dfile->buffer); 540 dfile->buffer = NULL; 541 dfile->data_len = 0; 542 } 543 out_unlock: 544 mutex_unlock(&dev->wl->mutex); 545 546 return err ? err : ret; 547 } 548 549 static ssize_t b43_debugfs_write(struct file *file, 550 const char __user *userbuf, 551 size_t count, loff_t *ppos) 552 { 553 struct b43_wldev *dev; 554 const struct b43_debugfs_fops *dfops; 555 char *buf; 556 int err = 0; 557 558 if (!count) 559 return 0; 560 if (count > PAGE_SIZE) 561 return -E2BIG; 562 dev = file->private_data; 563 if (!dev) 564 return -ENODEV; 565 566 mutex_lock(&dev->wl->mutex); 567 if (b43_status(dev) < B43_STAT_INITIALIZED) { 568 err = -ENODEV; 569 goto out_unlock; 570 } 571 572 dfops = debugfs_get_aux(file); 573 if (!dfops->write) { 574 err = -ENOSYS; 575 goto out_unlock; 576 } 577 578 buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 579 if (!buf) { 580 err = -ENOMEM; 581 goto out_unlock; 582 } 583 if (copy_from_user(buf, userbuf, count)) { 584 err = -EFAULT; 585 goto out_freepage; 586 } 587 err = dfops->write(dev, buf, count); 588 if (err) 589 goto out_freepage; 590 591 out_freepage: 592 kfree(buf); 593 out_unlock: 594 mutex_unlock(&dev->wl->mutex); 595 596 return err ? err : count; 597 } 598 599 600 static struct debugfs_short_fops debugfs_ops = { 601 .read = b43_debugfs_read, 602 .write = b43_debugfs_write, 603 .llseek = generic_file_llseek, 604 }; 605 606 #define B43_DEBUGFS_FOPS(name, _read, _write) \ 607 static struct b43_debugfs_fops fops_##name = { \ 608 .read = _read, \ 609 .write = _write, \ 610 .file_struct_offset = offsetof(struct b43_dfsentry, \ 611 file_##name), \ 612 } 613 614 B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file); 615 B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file); 616 B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file); 617 B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file); 618 B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file); 619 B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file); 620 B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file); 621 B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file); 622 B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL); 623 B43_DEBUGFS_FOPS(restart, NULL, restart_write_file); 624 B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL); 625 626 627 bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) 628 { 629 bool enabled; 630 631 enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]); 632 if (unlikely(enabled)) { 633 /* Force full debugging messages, if the user enabled 634 * some dynamic debugging feature. */ 635 b43_modparam_verbose = B43_VERBOSITY_MAX; 636 } 637 638 return enabled; 639 } 640 641 static void b43_add_dynamic_debug(struct b43_wldev *dev) 642 { 643 struct b43_dfsentry *e = dev->dfsentry; 644 645 #define add_dyn_dbg(name, id, initstate) do { \ 646 e->dyn_debug[id] = (initstate); \ 647 debugfs_create_bool(name, 0600, e->subdir, \ 648 &(e->dyn_debug[id])); \ 649 } while (0) 650 651 add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, false); 652 add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, false); 653 add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, false); 654 add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, false); 655 add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, false); 656 add_dyn_dbg("debug_lo", B43_DBG_LO, false); 657 add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, false); 658 add_dyn_dbg("debug_keys", B43_DBG_KEYS, false); 659 add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, false); 660 661 #undef add_dyn_dbg 662 } 663 664 void b43_debugfs_add_device(struct b43_wldev *dev) 665 { 666 struct b43_dfsentry *e; 667 struct b43_txstatus_log *log; 668 char devdir[16]; 669 670 B43_WARN_ON(!dev); 671 e = kzalloc_obj(*e); 672 if (!e) { 673 b43err(dev->wl, "debugfs: add device OOM\n"); 674 return; 675 } 676 e->dev = dev; 677 log = &e->txstatlog; 678 log->log = kzalloc_objs(struct b43_txstatus, B43_NR_LOGGED_TXSTATUS); 679 if (!log->log) { 680 b43err(dev->wl, "debugfs: add device txstatus OOM\n"); 681 kfree(e); 682 return; 683 } 684 log->end = -1; 685 686 dev->dfsentry = e; 687 688 snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy)); 689 e->subdir = debugfs_create_dir(devdir, rootdir); 690 691 e->mmio16read_next = 0xFFFF; /* invalid address */ 692 e->mmio32read_next = 0xFFFF; /* invalid address */ 693 e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */ 694 e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */ 695 e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */ 696 e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */ 697 698 #define ADD_FILE(name, mode) \ 699 do { \ 700 debugfs_create_file_aux(__stringify(name), \ 701 mode, e->subdir, dev, \ 702 &fops_##name, &debugfs_ops); \ 703 } while (0) 704 705 706 ADD_FILE(shm16read, 0600); 707 ADD_FILE(shm16write, 0200); 708 ADD_FILE(shm32read, 0600); 709 ADD_FILE(shm32write, 0200); 710 ADD_FILE(mmio16read, 0600); 711 ADD_FILE(mmio16write, 0200); 712 ADD_FILE(mmio32read, 0600); 713 ADD_FILE(mmio32write, 0200); 714 ADD_FILE(txstat, 0400); 715 ADD_FILE(restart, 0200); 716 ADD_FILE(loctls, 0400); 717 718 #undef ADD_FILE 719 720 b43_add_dynamic_debug(dev); 721 } 722 723 void b43_debugfs_remove_device(struct b43_wldev *dev) 724 { 725 struct b43_dfsentry *e; 726 727 if (!dev) 728 return; 729 e = dev->dfsentry; 730 if (!e) 731 return; 732 733 debugfs_remove(e->subdir); 734 kfree(e->txstatlog.log); 735 kfree(e); 736 } 737 738 void b43_debugfs_log_txstat(struct b43_wldev *dev, 739 const struct b43_txstatus *status) 740 { 741 struct b43_dfsentry *e = dev->dfsentry; 742 struct b43_txstatus_log *log; 743 struct b43_txstatus *cur; 744 int i; 745 746 if (!e) 747 return; 748 log = &e->txstatlog; 749 i = log->end + 1; 750 if (i == B43_NR_LOGGED_TXSTATUS) 751 i = 0; 752 log->end = i; 753 cur = &(log->log[i]); 754 memcpy(cur, status, sizeof(*cur)); 755 } 756 757 void b43_debugfs_init(void) 758 { 759 rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); 760 } 761 762 void b43_debugfs_exit(void) 763 { 764 debugfs_remove(rootdir); 765 } 766