772 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 4 Broadcom B43 wireless driver5 6 debugfs driver debugging code7 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 inline39struct 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 464#define B43_MAX_SHM_ADDR 0xFFFF65 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 else134 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 else210 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 else277 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 else341 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 } else402 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 fappend489 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 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 enabled639 * some dynamic debugging feature. */640 b43_modparam_verbose = B43_VERBOSITY_MAX;641 }642 643 return enabled;644}645 646static 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_dbg667}668 669void 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(sizeof(*e), GFP_KERNEL);677 if (!e) {678 b43err(dev->wl, "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 sizeof(struct b43_txstatus), GFP_KERNEL);685 if (!log->log) {686 b43err(dev->wl, "debugfs: add device txstatus OOM\n");687 kfree(e);688 return;689 }690 log->end = -1;691 692 dev->dfsentry = e;693 694 snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));695 e->subdir = debugfs_create_dir(devdir, 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_FILE725 726 b43_add_dynamic_debug(dev);727}728 729void 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(e->subdir);740 kfree(e->txstatlog.log);741 kfree(e);742}743 744void 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 763void b43_debugfs_init(void)764{765 rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);766}767 768void b43_debugfs_exit(void)769{770 debugfs_remove(rootdir);771}772