Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.24 656 lines 16 kB view raw
1/* 2 3 Broadcom B43 wireless driver 4 5 debugfs driver debugging code 6 7 Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de> 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2 of the License, or 12 (at your option) any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; see the file COPYING. If not, write to 21 the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, 22 Boston, MA 02110-1301, USA. 23 24*/ 25 26#include <linux/fs.h> 27#include <linux/debugfs.h> 28#include <linux/slab.h> 29#include <linux/netdevice.h> 30#include <linux/pci.h> 31#include <linux/mutex.h> 32 33#include "b43.h" 34#include "main.h" 35#include "debugfs.h" 36#include "dma.h" 37#include "pio.h" 38#include "xmit.h" 39 40 41/* The root directory. */ 42static struct dentry *rootdir; 43 44struct b43_debugfs_fops { 45 ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize); 46 int (*write)(struct b43_wldev *dev, const char *buf, size_t count); 47 struct file_operations fops; 48 /* Offset of struct b43_dfs_file in struct b43_dfsentry */ 49 size_t file_struct_offset; 50 /* Take wl->irq_lock before calling read/write? */ 51 bool take_irqlock; 52}; 53 54static inline 55struct b43_dfs_file * fops_to_dfs_file(struct b43_wldev *dev, 56 const struct b43_debugfs_fops *dfops) 57{ 58 void *p; 59 60 p = dev->dfsentry; 61 p += dfops->file_struct_offset; 62 63 return p; 64} 65 66 67#define fappend(fmt, x...) \ 68 do { \ 69 if (bufsize - count) \ 70 count += snprintf(buf + count, \ 71 bufsize - count, \ 72 fmt , ##x); \ 73 else \ 74 printk(KERN_ERR "b43: fappend overflow\n"); \ 75 } while (0) 76 77 78/* wl->irq_lock is locked */ 79static ssize_t tsf_read_file(struct b43_wldev *dev, 80 char *buf, size_t bufsize) 81{ 82 ssize_t count = 0; 83 u64 tsf; 84 85 b43_tsf_read(dev, &tsf); 86 fappend("0x%08x%08x\n", 87 (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32), 88 (unsigned int)(tsf & 0xFFFFFFFFULL)); 89 90 return count; 91} 92 93/* wl->irq_lock is locked */ 94static int tsf_write_file(struct b43_wldev *dev, 95 const char *buf, size_t count) 96{ 97 u64 tsf; 98 99 if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1) 100 return -EINVAL; 101 b43_tsf_write(dev, tsf); 102 103 return 0; 104} 105 106/* wl->irq_lock is locked */ 107static ssize_t ucode_regs_read_file(struct b43_wldev *dev, 108 char *buf, size_t bufsize) 109{ 110 ssize_t count = 0; 111 int i; 112 113 for (i = 0; i < 64; i++) { 114 fappend("r%d = 0x%04x\n", i, 115 b43_shm_read16(dev, B43_SHM_SCRATCH, i)); 116 } 117 118 return count; 119} 120 121/* wl->irq_lock is locked */ 122static ssize_t shm_read_file(struct b43_wldev *dev, 123 char *buf, size_t bufsize) 124{ 125 ssize_t count = 0; 126 int i; 127 u16 tmp; 128 __le16 *le16buf = (__le16 *)buf; 129 130 for (i = 0; i < 0x1000; i++) { 131 if (bufsize < sizeof(tmp)) 132 break; 133 tmp = b43_shm_read16(dev, B43_SHM_SHARED, 2 * i); 134 le16buf[i] = cpu_to_le16(tmp); 135 count += sizeof(tmp); 136 bufsize -= sizeof(tmp); 137 } 138 139 return count; 140} 141 142static ssize_t txstat_read_file(struct b43_wldev *dev, 143 char *buf, size_t bufsize) 144{ 145 struct b43_txstatus_log *log = &dev->dfsentry->txstatlog; 146 ssize_t count = 0; 147 unsigned long flags; 148 int i, idx; 149 struct b43_txstatus *stat; 150 151 spin_lock_irqsave(&log->lock, flags); 152 if (log->end < 0) { 153 fappend("Nothing transmitted, yet\n"); 154 goto out_unlock; 155 } 156 fappend("b43 TX status reports:\n\n" 157 "index | cookie | seq | phy_stat | frame_count | " 158 "rts_count | supp_reason | pm_indicated | " 159 "intermediate | for_ampdu | acked\n" "---\n"); 160 i = log->end + 1; 161 idx = 0; 162 while (1) { 163 if (i == B43_NR_LOGGED_TXSTATUS) 164 i = 0; 165 stat = &(log->log[i]); 166 if (stat->cookie) { 167 fappend("%03d | " 168 "0x%04X | 0x%04X | 0x%02X | " 169 "0x%X | 0x%X | " 170 "%u | %u | " 171 "%u | %u | %u\n", 172 idx, 173 stat->cookie, stat->seq, stat->phy_stat, 174 stat->frame_count, stat->rts_count, 175 stat->supp_reason, stat->pm_indicated, 176 stat->intermediate, stat->for_ampdu, 177 stat->acked); 178 idx++; 179 } 180 if (i == log->end) 181 break; 182 i++; 183 } 184out_unlock: 185 spin_unlock_irqrestore(&log->lock, flags); 186 187 return count; 188} 189 190static ssize_t txpower_g_read_file(struct b43_wldev *dev, 191 char *buf, size_t bufsize) 192{ 193 ssize_t count = 0; 194 195 if (dev->phy.type != B43_PHYTYPE_G) { 196 fappend("Device is not a G-PHY\n"); 197 goto out; 198 } 199 fappend("Control: %s\n", dev->phy.manual_txpower_control ? 200 "MANUAL" : "AUTOMATIC"); 201 fappend("Baseband attenuation: %u\n", dev->phy.bbatt.att); 202 fappend("Radio attenuation: %u\n", dev->phy.rfatt.att); 203 fappend("TX Mixer Gain: %s\n", 204 (dev->phy.tx_control & B43_TXCTL_TXMIX) ? "ON" : "OFF"); 205 fappend("PA Gain 2dB: %s\n", 206 (dev->phy.tx_control & B43_TXCTL_PA2DB) ? "ON" : "OFF"); 207 fappend("PA Gain 3dB: %s\n", 208 (dev->phy.tx_control & B43_TXCTL_PA3DB) ? "ON" : "OFF"); 209 fappend("\n\n"); 210 fappend("You can write to this file:\n"); 211 fappend("Writing \"auto\" enables automatic txpower control.\n"); 212 fappend 213 ("Writing the attenuation values as \"bbatt rfatt txmix pa2db pa3db\" " 214 "enables manual txpower control.\n"); 215 fappend("Example: 5 4 0 0 1\n"); 216 fappend("Enables manual control with Baseband attenuation 5, " 217 "Radio attenuation 4, No TX Mixer Gain, " 218 "No PA Gain 2dB, With PA Gain 3dB.\n"); 219out: 220 return count; 221} 222 223static int txpower_g_write_file(struct b43_wldev *dev, 224 const char *buf, size_t count) 225{ 226 unsigned long phy_flags; 227 228 if (dev->phy.type != B43_PHYTYPE_G) 229 return -ENODEV; 230 if ((count >= 4) && (memcmp(buf, "auto", 4) == 0)) { 231 /* Automatic control */ 232 dev->phy.manual_txpower_control = 0; 233 b43_phy_xmitpower(dev); 234 } else { 235 int bbatt = 0, rfatt = 0, txmix = 0, pa2db = 0, pa3db = 0; 236 /* Manual control */ 237 if (sscanf(buf, "%d %d %d %d %d", &bbatt, &rfatt, 238 &txmix, &pa2db, &pa3db) != 5) 239 return -EINVAL; 240 b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt); 241 dev->phy.manual_txpower_control = 1; 242 dev->phy.bbatt.att = bbatt; 243 dev->phy.rfatt.att = rfatt; 244 dev->phy.tx_control = 0; 245 if (txmix) 246 dev->phy.tx_control |= B43_TXCTL_TXMIX; 247 if (pa2db) 248 dev->phy.tx_control |= B43_TXCTL_PA2DB; 249 if (pa3db) 250 dev->phy.tx_control |= B43_TXCTL_PA3DB; 251 b43_phy_lock(dev, phy_flags); 252 b43_radio_lock(dev); 253 b43_set_txpower_g(dev, &dev->phy.bbatt, 254 &dev->phy.rfatt, dev->phy.tx_control); 255 b43_radio_unlock(dev); 256 b43_phy_unlock(dev, phy_flags); 257 } 258 259 return 0; 260} 261 262/* wl->irq_lock is locked */ 263static int restart_write_file(struct b43_wldev *dev, 264 const char *buf, size_t count) 265{ 266 int err = 0; 267 268 if (count > 0 && buf[0] == '1') { 269 b43_controller_restart(dev, "manually restarted"); 270 } else 271 err = -EINVAL; 272 273 return err; 274} 275 276static ssize_t append_lo_table(ssize_t count, char *buf, const size_t bufsize, 277 struct b43_loctl table[B43_NR_BB][B43_NR_RF]) 278{ 279 unsigned int i, j; 280 struct b43_loctl *ctl; 281 282 for (i = 0; i < B43_NR_BB; i++) { 283 for (j = 0; j < B43_NR_RF; j++) { 284 ctl = &(table[i][j]); 285 fappend("(bbatt %2u, rfatt %2u) -> " 286 "(I %+3d, Q %+3d, Used: %d, Calibrated: %d)\n", 287 i, j, ctl->i, ctl->q, 288 ctl->used, 289 b43_loctl_is_calibrated(ctl)); 290 } 291 } 292 293 return count; 294} 295 296static ssize_t loctls_read_file(struct b43_wldev *dev, 297 char *buf, size_t bufsize) 298{ 299 ssize_t count = 0; 300 struct b43_txpower_lo_control *lo; 301 int i, err = 0; 302 303 if (dev->phy.type != B43_PHYTYPE_G) { 304 fappend("Device is not a G-PHY\n"); 305 err = -ENODEV; 306 goto out; 307 } 308 lo = dev->phy.lo_control; 309 fappend("-- Local Oscillator calibration data --\n\n"); 310 fappend("Measured: %d, Rebuild: %d, HW-power-control: %d\n", 311 lo->lo_measured, 312 lo->rebuild, 313 dev->phy.hardware_power_control); 314 fappend("TX Bias: 0x%02X, TX Magn: 0x%02X\n", 315 lo->tx_bias, lo->tx_magn); 316 fappend("Power Vector: 0x%08X%08X\n", 317 (unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32), 318 (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL)); 319 fappend("\nControl table WITH PADMIX:\n"); 320 count = append_lo_table(count, buf, bufsize, lo->with_padmix); 321 fappend("\nControl table WITHOUT PADMIX:\n"); 322 count = append_lo_table(count, buf, bufsize, lo->no_padmix); 323 fappend("\nUsed RF attenuation values: Value(WithPadmix flag)\n"); 324 for (i = 0; i < lo->rfatt_list.len; i++) { 325 fappend("%u(%d), ", 326 lo->rfatt_list.list[i].att, 327 lo->rfatt_list.list[i].with_padmix); 328 } 329 fappend("\n"); 330 fappend("\nUsed Baseband attenuation values:\n"); 331 for (i = 0; i < lo->bbatt_list.len; i++) { 332 fappend("%u, ", 333 lo->bbatt_list.list[i].att); 334 } 335 fappend("\n"); 336 337out: 338 return err ? err : count; 339} 340 341#undef fappend 342 343static int b43_debugfs_open(struct inode *inode, struct file *file) 344{ 345 file->private_data = inode->i_private; 346 return 0; 347} 348 349static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf, 350 size_t count, loff_t *ppos) 351{ 352 struct b43_wldev *dev; 353 struct b43_debugfs_fops *dfops; 354 struct b43_dfs_file *dfile; 355 ssize_t ret; 356 char *buf; 357 const size_t bufsize = 1024 * 128; 358 const size_t buforder = get_order(bufsize); 359 int err = 0; 360 361 if (!count) 362 return 0; 363 dev = file->private_data; 364 if (!dev) 365 return -ENODEV; 366 367 mutex_lock(&dev->wl->mutex); 368 if (b43_status(dev) < B43_STAT_INITIALIZED) { 369 err = -ENODEV; 370 goto out_unlock; 371 } 372 373 dfops = container_of(file->f_op, struct b43_debugfs_fops, fops); 374 if (!dfops->read) { 375 err = -ENOSYS; 376 goto out_unlock; 377 } 378 dfile = fops_to_dfs_file(dev, dfops); 379 380 if (!dfile->buffer) { 381 buf = (char *)__get_free_pages(GFP_KERNEL, buforder); 382 if (!buf) { 383 err = -ENOMEM; 384 goto out_unlock; 385 } 386 /* Sparse warns about the following memset, because it has a big 387 * size value. That warning is bogus, so I will ignore it. --mb */ 388 memset(buf, 0, bufsize); 389 if (dfops->take_irqlock) { 390 spin_lock_irq(&dev->wl->irq_lock); 391 ret = dfops->read(dev, buf, bufsize); 392 spin_unlock_irq(&dev->wl->irq_lock); 393 } else 394 ret = dfops->read(dev, buf, bufsize); 395 if (ret <= 0) { 396 free_pages((unsigned long)buf, buforder); 397 err = ret; 398 goto out_unlock; 399 } 400 dfile->data_len = ret; 401 dfile->buffer = buf; 402 } 403 404 ret = simple_read_from_buffer(userbuf, count, ppos, 405 dfile->buffer, 406 dfile->data_len); 407 if (*ppos >= dfile->data_len) { 408 free_pages((unsigned long)dfile->buffer, buforder); 409 dfile->buffer = NULL; 410 dfile->data_len = 0; 411 } 412out_unlock: 413 mutex_unlock(&dev->wl->mutex); 414 415 return err ? err : ret; 416} 417 418static ssize_t b43_debugfs_write(struct file *file, 419 const char __user *userbuf, 420 size_t count, loff_t *ppos) 421{ 422 struct b43_wldev *dev; 423 struct b43_debugfs_fops *dfops; 424 char *buf; 425 int err = 0; 426 427 if (!count) 428 return 0; 429 if (count > PAGE_SIZE) 430 return -E2BIG; 431 dev = file->private_data; 432 if (!dev) 433 return -ENODEV; 434 435 mutex_lock(&dev->wl->mutex); 436 if (b43_status(dev) < B43_STAT_INITIALIZED) { 437 err = -ENODEV; 438 goto out_unlock; 439 } 440 441 dfops = container_of(file->f_op, struct b43_debugfs_fops, fops); 442 if (!dfops->write) { 443 err = -ENOSYS; 444 goto out_unlock; 445 } 446 447 buf = (char *)get_zeroed_page(GFP_KERNEL); 448 if (!buf) { 449 err = -ENOMEM; 450 goto out_unlock; 451 } 452 if (copy_from_user(buf, userbuf, count)) { 453 err = -EFAULT; 454 goto out_freepage; 455 } 456 if (dfops->take_irqlock) { 457 spin_lock_irq(&dev->wl->irq_lock); 458 err = dfops->write(dev, buf, count); 459 spin_unlock_irq(&dev->wl->irq_lock); 460 } else 461 err = dfops->write(dev, buf, count); 462 if (err) 463 goto out_freepage; 464 465out_freepage: 466 free_page((unsigned long)buf); 467out_unlock: 468 mutex_unlock(&dev->wl->mutex); 469 470 return err ? err : count; 471} 472 473 474#define B43_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \ 475 static struct b43_debugfs_fops fops_##name = { \ 476 .read = _read, \ 477 .write = _write, \ 478 .fops = { \ 479 .open = b43_debugfs_open, \ 480 .read = b43_debugfs_read, \ 481 .write = b43_debugfs_write, \ 482 }, \ 483 .file_struct_offset = offsetof(struct b43_dfsentry, \ 484 file_##name), \ 485 .take_irqlock = _take_irqlock, \ 486 } 487 488B43_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1); 489B43_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1); 490B43_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1); 491B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0); 492B43_DEBUGFS_FOPS(txpower_g, txpower_g_read_file, txpower_g_write_file, 0); 493B43_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1); 494B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL, 0); 495 496 497int b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) 498{ 499 return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]); 500} 501 502static void b43_remove_dynamic_debug(struct b43_wldev *dev) 503{ 504 struct b43_dfsentry *e = dev->dfsentry; 505 int i; 506 507 for (i = 0; i < __B43_NR_DYNDBG; i++) 508 debugfs_remove(e->dyn_debug_dentries[i]); 509} 510 511static void b43_add_dynamic_debug(struct b43_wldev *dev) 512{ 513 struct b43_dfsentry *e = dev->dfsentry; 514 struct dentry *d; 515 516#define add_dyn_dbg(name, id, initstate) do { \ 517 e->dyn_debug[id] = (initstate); \ 518 d = debugfs_create_bool(name, 0600, e->subdir, \ 519 &(e->dyn_debug[id])); \ 520 if (!IS_ERR(d)) \ 521 e->dyn_debug_dentries[id] = d; \ 522 } while (0) 523 524 add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, 0); 525 add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, 0); 526 add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, 0); 527 add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, 0); 528 add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, 0); 529 530#undef add_dyn_dbg 531} 532 533void b43_debugfs_add_device(struct b43_wldev *dev) 534{ 535 struct b43_dfsentry *e; 536 struct b43_txstatus_log *log; 537 char devdir[16]; 538 539 B43_WARN_ON(!dev); 540 e = kzalloc(sizeof(*e), GFP_KERNEL); 541 if (!e) { 542 b43err(dev->wl, "debugfs: add device OOM\n"); 543 return; 544 } 545 e->dev = dev; 546 log = &e->txstatlog; 547 log->log = kcalloc(B43_NR_LOGGED_TXSTATUS, 548 sizeof(struct b43_txstatus), GFP_KERNEL); 549 if (!log->log) { 550 b43err(dev->wl, "debugfs: add device txstatus OOM\n"); 551 kfree(e); 552 return; 553 } 554 log->end = -1; 555 spin_lock_init(&log->lock); 556 557 dev->dfsentry = e; 558 559 snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy)); 560 e->subdir = debugfs_create_dir(devdir, rootdir); 561 if (!e->subdir || IS_ERR(e->subdir)) { 562 if (e->subdir == ERR_PTR(-ENODEV)) { 563 b43dbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not " 564 "enabled in kernel config\n"); 565 } else { 566 b43err(dev->wl, "debugfs: cannot create %s directory\n", 567 devdir); 568 } 569 dev->dfsentry = NULL; 570 kfree(log->log); 571 kfree(e); 572 return; 573 } 574 575#define ADD_FILE(name, mode) \ 576 do { \ 577 struct dentry *d; \ 578 d = debugfs_create_file(__stringify(name), \ 579 mode, e->subdir, dev, \ 580 &fops_##name.fops); \ 581 e->file_##name.dentry = NULL; \ 582 if (!IS_ERR(d)) \ 583 e->file_##name.dentry = d; \ 584 } while (0) 585 586 587 ADD_FILE(tsf, 0600); 588 ADD_FILE(ucode_regs, 0400); 589 ADD_FILE(shm, 0400); 590 ADD_FILE(txstat, 0400); 591 ADD_FILE(txpower_g, 0600); 592 ADD_FILE(restart, 0200); 593 ADD_FILE(loctls, 0400); 594 595#undef ADD_FILE 596 597 b43_add_dynamic_debug(dev); 598} 599 600void b43_debugfs_remove_device(struct b43_wldev *dev) 601{ 602 struct b43_dfsentry *e; 603 604 if (!dev) 605 return; 606 e = dev->dfsentry; 607 if (!e) 608 return; 609 b43_remove_dynamic_debug(dev); 610 611 debugfs_remove(e->file_tsf.dentry); 612 debugfs_remove(e->file_ucode_regs.dentry); 613 debugfs_remove(e->file_shm.dentry); 614 debugfs_remove(e->file_txstat.dentry); 615 debugfs_remove(e->file_txpower_g.dentry); 616 debugfs_remove(e->file_restart.dentry); 617 debugfs_remove(e->file_loctls.dentry); 618 619 debugfs_remove(e->subdir); 620 kfree(e->txstatlog.log); 621 kfree(e); 622} 623 624void b43_debugfs_log_txstat(struct b43_wldev *dev, 625 const struct b43_txstatus *status) 626{ 627 struct b43_dfsentry *e = dev->dfsentry; 628 struct b43_txstatus_log *log; 629 struct b43_txstatus *cur; 630 int i; 631 632 if (!e) 633 return; 634 log = &e->txstatlog; 635 B43_WARN_ON(!irqs_disabled()); 636 spin_lock(&log->lock); 637 i = log->end + 1; 638 if (i == B43_NR_LOGGED_TXSTATUS) 639 i = 0; 640 log->end = i; 641 cur = &(log->log[i]); 642 memcpy(cur, status, sizeof(*cur)); 643 spin_unlock(&log->lock); 644} 645 646void b43_debugfs_init(void) 647{ 648 rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); 649 if (IS_ERR(rootdir)) 650 rootdir = NULL; 651} 652 653void b43_debugfs_exit(void) 654{ 655 debugfs_remove(rootdir); 656}