linux/drivers/net/wireless/broadcom/b43/debugfs.c
<<
>>
Prefs
   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. */
  28static struct dentry *rootdir;
  29
  30struct 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
  38static inline
  39struct 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 += snprintf(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
  66static 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, addr);
  80        fappend("0x%04X\n", val);
  81
  82        return count;
  83}
  84
  85static 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
 109static 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, addr);
 135        val &= mask;
 136        val |= set;
 137        b43_shm_write16(dev, routing, addr, val);
 138
 139        return 0;
 140}
 141
 142static 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, addr);
 156        fappend("0x%08X\n", val);
 157
 158        return count;
 159}
 160
 161static 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
 185static 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, addr);
 211        val &= mask;
 212        val |= set;
 213        b43_shm_write32(dev, routing, addr, 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
 221static 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, addr);
 233        fappend("0x%04X\n", val);
 234
 235        return count;
 236}
 237
 238static 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
 257static 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, addr);
 278        val &= mask;
 279        val |= set;
 280        b43_write16(dev, addr, val);
 281
 282        return 0;
 283}
 284
 285static 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, addr);
 297        fappend("0x%08X\n", val);
 298
 299        return count;
 300}
 301
 302static 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
 321static 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, addr);
 342        val &= mask;
 343        val |= set;
 344        b43_write32(dev, addr, val);
 345
 346        return 0;
 347}
 348
 349static 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        }
 389out:
 390
 391        return count;
 392}
 393
 394static 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, "manually restarted");
 401        } else
 402                err = -EINVAL;
 403
 404        return err;
 405}
 406
 407static 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
 425static 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(&cal->bbatt, &phy->g->bbatt) &&
 459                          b43_compare_rfatt(&cal->rfatt, &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
 484out:
 485        return err ? err : count;
 486}
 487
 488#undef fappend
 489
 490static 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 uninitialized_var(ret);
 497        char *buf;
 498        const size_t bufsize = 1024 * 16; /* 16 kiB buffer */
 499        const size_t buforder = get_order(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, 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((unsigned long)buf, 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(userbuf, count, ppos,
 540                                      dfile->buffer,
 541                                      dfile->data_len);
 542        if (*ppos >= dfile->data_len) {
 543                free_pages((unsigned long)dfile->buffer, buforder);
 544                dfile->buffer = NULL;
 545                dfile->data_len = 0;
 546        }
 547out_unlock:
 548        mutex_unlock(&dev->wl->mutex);
 549
 550        return err ? err : ret;
 551}
 552
 553static 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(buf, userbuf, count)) {
 589                err = -EFAULT;
 590                goto out_freepage;
 591        }
 592        err = dfops->write(dev, buf, count);
 593        if (err)
 594                goto out_freepage;
 595
 596out_freepage:
 597        free_page((unsigned long)buf);
 598out_unlock:
 599        mutex_unlock(&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
 619B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file);
 620B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file);
 621B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file);
 622B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file);
 623B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file);
 624B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file);
 625B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file);
 626B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file);
 627B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL);
 628B43_DEBUGFS_FOPS(restart, NULL, restart_write_file);
 629B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL);
 630
 631
 632bool 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
 646static void b43_remove_dynamic_debug(struct b43_wldev *dev)
 647{
 648        struct b43_dfsentry *e = dev->dfsentry;
 649        int i;
 650
 651        for (i = 0; i < __B43_NR_DYNDBG; i++)
 652                debugfs_remove(e->dyn_debug_dentries[i]);
 653}
 654
 655static void b43_add_dynamic_debug(struct b43_wldev *dev)
 656{
 657        struct b43_dfsentry *e = dev->dfsentry;
 658
 659#define add_dyn_dbg(name, id, initstate) do {                   \
 660        e->dyn_debug[id] = (initstate);                         \
 661        e->dyn_debug_dentries[id] =                             \
 662                debugfs_create_bool(name, 0600, e->subdir,      \
 663                                &(e->dyn_debug[id]));           \
 664        } while (0)
 665
 666        add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, false);
 667        add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, false);
 668        add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, false);
 669        add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, false);
 670        add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, false);
 671        add_dyn_dbg("debug_lo", B43_DBG_LO, false);
 672        add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, false);
 673        add_dyn_dbg("debug_keys", B43_DBG_KEYS, false);
 674        add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, false);
 675
 676#undef add_dyn_dbg
 677}
 678
 679void b43_debugfs_add_device(struct b43_wldev *dev)
 680{
 681        struct b43_dfsentry *e;
 682        struct b43_txstatus_log *log;
 683        char devdir[16];
 684
 685        B43_WARN_ON(!dev);
 686        e = kzalloc(sizeof(*e), GFP_KERNEL);
 687        if (!e) {
 688                b43err(dev->wl, "debugfs: add device OOM\n");
 689                return;
 690        }
 691        e->dev = dev;
 692        log = &e->txstatlog;
 693        log->log = kcalloc(B43_NR_LOGGED_TXSTATUS,
 694                           sizeof(struct b43_txstatus), GFP_KERNEL);
 695        if (!log->log) {
 696                b43err(dev->wl, "debugfs: add device txstatus OOM\n");
 697                kfree(e);
 698                return;
 699        }
 700        log->end = -1;
 701
 702        dev->dfsentry = e;
 703
 704        snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
 705        e->subdir = debugfs_create_dir(devdir, rootdir);
 706
 707        e->mmio16read_next = 0xFFFF; /* invalid address */
 708        e->mmio32read_next = 0xFFFF; /* invalid address */
 709        e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */
 710        e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */
 711        e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */
 712        e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */
 713
 714#define ADD_FILE(name, mode)    \
 715        do {                                                    \
 716                e->file_##name.dentry =                         \
 717                        debugfs_create_file(__stringify(name),  \
 718                                        mode, e->subdir, dev,   \
 719                                        &fops_##name.fops);     \
 720        } while (0)
 721
 722
 723        ADD_FILE(shm16read, 0600);
 724        ADD_FILE(shm16write, 0200);
 725        ADD_FILE(shm32read, 0600);
 726        ADD_FILE(shm32write, 0200);
 727        ADD_FILE(mmio16read, 0600);
 728        ADD_FILE(mmio16write, 0200);
 729        ADD_FILE(mmio32read, 0600);
 730        ADD_FILE(mmio32write, 0200);
 731        ADD_FILE(txstat, 0400);
 732        ADD_FILE(restart, 0200);
 733        ADD_FILE(loctls, 0400);
 734
 735#undef ADD_FILE
 736
 737        b43_add_dynamic_debug(dev);
 738}
 739
 740void b43_debugfs_remove_device(struct b43_wldev *dev)
 741{
 742        struct b43_dfsentry *e;
 743
 744        if (!dev)
 745                return;
 746        e = dev->dfsentry;
 747        if (!e)
 748                return;
 749        b43_remove_dynamic_debug(dev);
 750
 751        debugfs_remove(e->file_shm16read.dentry);
 752        debugfs_remove(e->file_shm16write.dentry);
 753        debugfs_remove(e->file_shm32read.dentry);
 754        debugfs_remove(e->file_shm32write.dentry);
 755        debugfs_remove(e->file_mmio16read.dentry);
 756        debugfs_remove(e->file_mmio16write.dentry);
 757        debugfs_remove(e->file_mmio32read.dentry);
 758        debugfs_remove(e->file_mmio32write.dentry);
 759        debugfs_remove(e->file_txstat.dentry);
 760        debugfs_remove(e->file_restart.dentry);
 761        debugfs_remove(e->file_loctls.dentry);
 762
 763        debugfs_remove(e->subdir);
 764        kfree(e->txstatlog.log);
 765        kfree(e);
 766}
 767
 768void b43_debugfs_log_txstat(struct b43_wldev *dev,
 769                            const struct b43_txstatus *status)
 770{
 771        struct b43_dfsentry *e = dev->dfsentry;
 772        struct b43_txstatus_log *log;
 773        struct b43_txstatus *cur;
 774        int i;
 775
 776        if (!e)
 777                return;
 778        log = &e->txstatlog;
 779        i = log->end + 1;
 780        if (i == B43_NR_LOGGED_TXSTATUS)
 781                i = 0;
 782        log->end = i;
 783        cur = &(log->log[i]);
 784        memcpy(cur, status, sizeof(*cur));
 785}
 786
 787void b43_debugfs_init(void)
 788{
 789        rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
 790}
 791
 792void b43_debugfs_exit(void)
 793{
 794        debugfs_remove(rootdir);
 795}
 796