root/drivers/net/wireless/broadcom/b43legacy/debugfs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fops_to_dfs_file
  2. tsf_read_file
  3. tsf_write_file
  4. ucode_regs_read_file
  5. shm_read_file
  6. txstat_read_file
  7. restart_write_file
  8. b43legacy_debugfs_read
  9. b43legacy_debugfs_write
  10. b43legacy_debug
  11. b43legacy_remove_dynamic_debug
  12. b43legacy_add_dynamic_debug
  13. b43legacy_debugfs_add_device
  14. b43legacy_debugfs_remove_device
  15. b43legacy_debugfs_log_txstat
  16. b43legacy_debugfs_init
  17. b43legacy_debugfs_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3 
   4   Broadcom B43legacy 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 "b43legacy.h"
  21 #include "main.h"
  22 #include "debugfs.h"
  23 #include "dma.h"
  24 #include "pio.h"
  25 #include "xmit.h"
  26 
  27 
  28 /* The root directory. */
  29 static struct dentry *rootdir;
  30 
  31 struct b43legacy_debugfs_fops {
  32         ssize_t (*read)(struct b43legacy_wldev *dev, char *buf, size_t bufsize);
  33         int (*write)(struct b43legacy_wldev *dev, const char *buf, size_t count);
  34         struct file_operations fops;
  35         /* Offset of struct b43legacy_dfs_file in struct b43legacy_dfsentry */
  36         size_t file_struct_offset;
  37         /* Take wl->irq_lock before calling read/write? */
  38         bool take_irqlock;
  39 };
  40 
  41 static inline
  42 struct b43legacy_dfs_file * fops_to_dfs_file(struct b43legacy_wldev *dev,
  43                                        const struct b43legacy_debugfs_fops *dfops)
  44 {
  45         void *p;
  46 
  47         p = dev->dfsentry;
  48         p += dfops->file_struct_offset;
  49 
  50         return p;
  51 }
  52 
  53 
  54 #define fappend(fmt, x...)      \
  55         do {                                                    \
  56                 if (bufsize - count)                            \
  57                         count += snprintf(buf + count,          \
  58                                           bufsize - count,      \
  59                                           fmt , ##x);           \
  60                 else                                            \
  61                         printk(KERN_ERR "b43legacy: fappend overflow\n"); \
  62         } while (0)
  63 
  64 
  65 /* wl->irq_lock is locked */
  66 static ssize_t tsf_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
  67 {
  68         ssize_t count = 0;
  69         u64 tsf;
  70 
  71         b43legacy_tsf_read(dev, &tsf);
  72         fappend("0x%08x%08x\n",
  73                 (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32),
  74                 (unsigned int)(tsf & 0xFFFFFFFFULL));
  75 
  76         return count;
  77 }
  78 
  79 /* wl->irq_lock is locked */
  80 static int tsf_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)
  81 {
  82         u64 tsf;
  83 
  84         if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1)
  85                 return -EINVAL;
  86         b43legacy_tsf_write(dev, tsf);
  87 
  88         return 0;
  89 }
  90 
  91 /* wl->irq_lock is locked */
  92 static ssize_t ucode_regs_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
  93 {
  94         ssize_t count = 0;
  95         int i;
  96 
  97         for (i = 0; i < 64; i++) {
  98                 fappend("r%d = 0x%04x\n", i,
  99                         b43legacy_shm_read16(dev, B43legacy_SHM_WIRELESS, i));
 100         }
 101 
 102         return count;
 103 }
 104 
 105 /* wl->irq_lock is locked */
 106 static ssize_t shm_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
 107 {
 108         ssize_t count = 0;
 109         int i;
 110         u16 tmp;
 111         __le16 *le16buf = (__le16 *)buf;
 112 
 113         for (i = 0; i < 0x1000; i++) {
 114                 if (bufsize < sizeof(tmp))
 115                         break;
 116                 tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 2 * i);
 117                 le16buf[i] = cpu_to_le16(tmp);
 118                 count += sizeof(tmp);
 119                 bufsize -= sizeof(tmp);
 120         }
 121 
 122         return count;
 123 }
 124 
 125 static ssize_t txstat_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
 126 {
 127         struct b43legacy_txstatus_log *log = &dev->dfsentry->txstatlog;
 128         ssize_t count = 0;
 129         unsigned long flags;
 130         int i, idx;
 131         struct b43legacy_txstatus *stat;
 132 
 133         spin_lock_irqsave(&log->lock, flags);
 134         if (log->end < 0) {
 135                 fappend("Nothing transmitted, yet\n");
 136                 goto out_unlock;
 137         }
 138         fappend("b43legacy TX status reports:\n\n"
 139                 "index | cookie | seq | phy_stat | frame_count | "
 140                 "rts_count | supp_reason | pm_indicated | "
 141                 "intermediate | for_ampdu | acked\n" "---\n");
 142         i = log->end + 1;
 143         idx = 0;
 144         while (1) {
 145                 if (i == B43legacy_NR_LOGGED_TXSTATUS)
 146                         i = 0;
 147                 stat = &(log->log[i]);
 148                 if (stat->cookie) {
 149                         fappend("%03d | "
 150                                 "0x%04X | 0x%04X | 0x%02X | "
 151                                 "0x%X | 0x%X | "
 152                                 "%u | %u | "
 153                                 "%u | %u | %u\n",
 154                                 idx,
 155                                 stat->cookie, stat->seq, stat->phy_stat,
 156                                 stat->frame_count, stat->rts_count,
 157                                 stat->supp_reason, stat->pm_indicated,
 158                                 stat->intermediate, stat->for_ampdu,
 159                                 stat->acked);
 160                         idx++;
 161                 }
 162                 if (i == log->end)
 163                         break;
 164                 i++;
 165         }
 166 out_unlock:
 167         spin_unlock_irqrestore(&log->lock, flags);
 168 
 169         return count;
 170 }
 171 
 172 /* wl->irq_lock is locked */
 173 static int restart_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)
 174 {
 175         int err = 0;
 176 
 177         if (count > 0 && buf[0] == '1') {
 178                 b43legacy_controller_restart(dev, "manually restarted");
 179         } else
 180                 err = -EINVAL;
 181 
 182         return err;
 183 }
 184 
 185 #undef fappend
 186 
 187 static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
 188                                 size_t count, loff_t *ppos)
 189 {
 190         struct b43legacy_wldev *dev;
 191         struct b43legacy_debugfs_fops *dfops;
 192         struct b43legacy_dfs_file *dfile;
 193         ssize_t uninitialized_var(ret);
 194         char *buf;
 195         const size_t bufsize = 1024 * 16; /* 16 KiB buffer */
 196         const size_t buforder = get_order(bufsize);
 197         int err = 0;
 198 
 199         if (!count)
 200                 return 0;
 201         dev = file->private_data;
 202         if (!dev)
 203                 return -ENODEV;
 204 
 205         mutex_lock(&dev->wl->mutex);
 206         if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
 207                 err = -ENODEV;
 208                 goto out_unlock;
 209         }
 210 
 211         dfops = container_of(debugfs_real_fops(file),
 212                              struct b43legacy_debugfs_fops, fops);
 213         if (!dfops->read) {
 214                 err = -ENOSYS;
 215                 goto out_unlock;
 216         }
 217         dfile = fops_to_dfs_file(dev, dfops);
 218 
 219         if (!dfile->buffer) {
 220                 buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
 221                 if (!buf) {
 222                         err = -ENOMEM;
 223                         goto out_unlock;
 224                 }
 225                 memset(buf, 0, bufsize);
 226                 if (dfops->take_irqlock) {
 227                         spin_lock_irq(&dev->wl->irq_lock);
 228                         ret = dfops->read(dev, buf, bufsize);
 229                         spin_unlock_irq(&dev->wl->irq_lock);
 230                 } else
 231                         ret = dfops->read(dev, buf, bufsize);
 232                 if (ret <= 0) {
 233                         free_pages((unsigned long)buf, buforder);
 234                         err = ret;
 235                         goto out_unlock;
 236                 }
 237                 dfile->data_len = ret;
 238                 dfile->buffer = buf;
 239         }
 240 
 241         ret = simple_read_from_buffer(userbuf, count, ppos,
 242                                       dfile->buffer,
 243                                       dfile->data_len);
 244         if (*ppos >= dfile->data_len) {
 245                 free_pages((unsigned long)dfile->buffer, buforder);
 246                 dfile->buffer = NULL;
 247                 dfile->data_len = 0;
 248         }
 249 out_unlock:
 250         mutex_unlock(&dev->wl->mutex);
 251 
 252         return err ? err : ret;
 253 }
 254 
 255 static ssize_t b43legacy_debugfs_write(struct file *file,
 256                                  const char __user *userbuf,
 257                                  size_t count, loff_t *ppos)
 258 {
 259         struct b43legacy_wldev *dev;
 260         struct b43legacy_debugfs_fops *dfops;
 261         char *buf;
 262         int err = 0;
 263 
 264         if (!count)
 265                 return 0;
 266         if (count > PAGE_SIZE)
 267                 return -E2BIG;
 268         dev = file->private_data;
 269         if (!dev)
 270                 return -ENODEV;
 271 
 272         mutex_lock(&dev->wl->mutex);
 273         if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
 274                 err = -ENODEV;
 275                 goto out_unlock;
 276         }
 277 
 278         dfops = container_of(debugfs_real_fops(file),
 279                              struct b43legacy_debugfs_fops, fops);
 280         if (!dfops->write) {
 281                 err = -ENOSYS;
 282                 goto out_unlock;
 283         }
 284 
 285         buf = (char *)get_zeroed_page(GFP_KERNEL);
 286         if (!buf) {
 287                 err = -ENOMEM;
 288                 goto out_unlock;
 289         }
 290         if (copy_from_user(buf, userbuf, count)) {
 291                 err = -EFAULT;
 292                 goto out_freepage;
 293         }
 294         if (dfops->take_irqlock) {
 295                 spin_lock_irq(&dev->wl->irq_lock);
 296                 err = dfops->write(dev, buf, count);
 297                 spin_unlock_irq(&dev->wl->irq_lock);
 298         } else
 299                 err = dfops->write(dev, buf, count);
 300         if (err)
 301                 goto out_freepage;
 302 
 303 out_freepage:
 304         free_page((unsigned long)buf);
 305 out_unlock:
 306         mutex_unlock(&dev->wl->mutex);
 307 
 308         return err ? err : count;
 309 }
 310 
 311 
 312 #define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock)      \
 313         static struct b43legacy_debugfs_fops fops_##name = {            \
 314                 .read   = _read,                                \
 315                 .write  = _write,                               \
 316                 .fops   = {                                     \
 317                         .open   = simple_open,                          \
 318                         .read   = b43legacy_debugfs_read,               \
 319                         .write  = b43legacy_debugfs_write,              \
 320                         .llseek = generic_file_llseek,                  \
 321                 },                                              \
 322                 .file_struct_offset = offsetof(struct b43legacy_dfsentry, \
 323                                                file_##name),    \
 324                 .take_irqlock   = _take_irqlock,                \
 325         }
 326 
 327 B43legacy_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1);
 328 B43legacy_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1);
 329 B43legacy_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1);
 330 B43legacy_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0);
 331 B43legacy_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1);
 332 
 333 
 334 int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature)
 335 {
 336         return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
 337 }
 338 
 339 static void b43legacy_remove_dynamic_debug(struct b43legacy_wldev *dev)
 340 {
 341         struct b43legacy_dfsentry *e = dev->dfsentry;
 342         int i;
 343 
 344         for (i = 0; i < __B43legacy_NR_DYNDBG; i++)
 345                 debugfs_remove(e->dyn_debug_dentries[i]);
 346 }
 347 
 348 static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev)
 349 {
 350         struct b43legacy_dfsentry *e = dev->dfsentry;
 351 
 352 #define add_dyn_dbg(name, id, initstate) do {                   \
 353         e->dyn_debug[id] = (initstate);                         \
 354         e->dyn_debug_dentries[id] =                             \
 355                 debugfs_create_bool(name, 0600, e->subdir,      \
 356                                 &(e->dyn_debug[id]));           \
 357         } while (0)
 358 
 359         add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, false);
 360         add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, false);
 361         add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE, false);
 362         add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST, false);
 363         add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP, false);
 364 
 365 #undef add_dyn_dbg
 366 }
 367 
 368 void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev)
 369 {
 370         struct b43legacy_dfsentry *e;
 371         struct b43legacy_txstatus_log *log;
 372         char devdir[16];
 373 
 374         B43legacy_WARN_ON(!dev);
 375         e = kzalloc(sizeof(*e), GFP_KERNEL);
 376         if (!e) {
 377                 b43legacyerr(dev->wl, "debugfs: add device OOM\n");
 378                 return;
 379         }
 380         e->dev = dev;
 381         log = &e->txstatlog;
 382         log->log = kcalloc(B43legacy_NR_LOGGED_TXSTATUS,
 383                            sizeof(struct b43legacy_txstatus), GFP_KERNEL);
 384         if (!log->log) {
 385                 b43legacyerr(dev->wl, "debugfs: add device txstatus OOM\n");
 386                 kfree(e);
 387                 return;
 388         }
 389         log->end = -1;
 390         spin_lock_init(&log->lock);
 391 
 392         dev->dfsentry = e;
 393 
 394         snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
 395         e->subdir = debugfs_create_dir(devdir, rootdir);
 396 
 397 #define ADD_FILE(name, mode)    \
 398         do {                                                    \
 399                 e->file_##name.dentry =                         \
 400                         debugfs_create_file(__stringify(name),  \
 401                                         mode, e->subdir, dev,   \
 402                                         &fops_##name.fops);     \
 403                 e->file_##name.dentry = NULL;                   \
 404         } while (0)
 405 
 406 
 407         ADD_FILE(tsf, 0600);
 408         ADD_FILE(ucode_regs, 0400);
 409         ADD_FILE(shm, 0400);
 410         ADD_FILE(txstat, 0400);
 411         ADD_FILE(restart, 0200);
 412 
 413 #undef ADD_FILE
 414 
 415         b43legacy_add_dynamic_debug(dev);
 416 }
 417 
 418 void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev)
 419 {
 420         struct b43legacy_dfsentry *e;
 421 
 422         if (!dev)
 423                 return;
 424         e = dev->dfsentry;
 425         if (!e)
 426                 return;
 427         b43legacy_remove_dynamic_debug(dev);
 428 
 429         debugfs_remove(e->file_tsf.dentry);
 430         debugfs_remove(e->file_ucode_regs.dentry);
 431         debugfs_remove(e->file_shm.dentry);
 432         debugfs_remove(e->file_txstat.dentry);
 433         debugfs_remove(e->file_restart.dentry);
 434 
 435         debugfs_remove(e->subdir);
 436         kfree(e->txstatlog.log);
 437         kfree(e);
 438 }
 439 
 440 void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev,
 441                             const struct b43legacy_txstatus *status)
 442 {
 443         struct b43legacy_dfsentry *e = dev->dfsentry;
 444         struct b43legacy_txstatus_log *log;
 445         struct b43legacy_txstatus *cur;
 446         int i;
 447 
 448         if (!e)
 449                 return;
 450         log = &e->txstatlog;
 451         B43legacy_WARN_ON(!irqs_disabled());
 452         spin_lock(&log->lock);
 453         i = log->end + 1;
 454         if (i == B43legacy_NR_LOGGED_TXSTATUS)
 455                 i = 0;
 456         log->end = i;
 457         cur = &(log->log[i]);
 458         memcpy(cur, status, sizeof(*cur));
 459         spin_unlock(&log->lock);
 460 }
 461 
 462 void b43legacy_debugfs_init(void)
 463 {
 464         rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
 465 }
 466 
 467 void b43legacy_debugfs_exit(void)
 468 {
 469         debugfs_remove(rootdir);
 470 }

/* [<][>][^][v][top][bottom][index][help] */