Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.30 559 lines 17 kB view raw
1/* 2 * Copyright (c) 2008-2009 Atheros Communications Inc. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include <asm/unaligned.h> 18 19#include "ath9k.h" 20 21static unsigned int ath9k_debug = DBG_DEFAULT; 22module_param_named(debug, ath9k_debug, uint, 0); 23 24static struct dentry *ath9k_debugfs_root; 25 26void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...) 27{ 28 if (!sc) 29 return; 30 31 if (sc->debug.debug_mask & dbg_mask) { 32 va_list args; 33 34 va_start(args, fmt); 35 printk(KERN_DEBUG "ath9k: "); 36 vprintk(fmt, args); 37 va_end(args); 38 } 39} 40 41static int ath9k_debugfs_open(struct inode *inode, struct file *file) 42{ 43 file->private_data = inode->i_private; 44 return 0; 45} 46 47static ssize_t read_file_dma(struct file *file, char __user *user_buf, 48 size_t count, loff_t *ppos) 49{ 50 struct ath_softc *sc = file->private_data; 51 struct ath_hw *ah = sc->sc_ah; 52 char buf[1024]; 53 unsigned int len = 0; 54 u32 val[ATH9K_NUM_DMA_DEBUG_REGS]; 55 int i, qcuOffset = 0, dcuOffset = 0; 56 u32 *qcuBase = &val[0], *dcuBase = &val[4]; 57 58 REG_WRITE(ah, AR_MACMISC, 59 ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | 60 (AR_MACMISC_MISC_OBS_BUS_1 << 61 AR_MACMISC_MISC_OBS_BUS_MSB_S))); 62 63 len += snprintf(buf + len, sizeof(buf) - len, 64 "Raw DMA Debug values:\n"); 65 66 for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) { 67 if (i % 4 == 0) 68 len += snprintf(buf + len, sizeof(buf) - len, "\n"); 69 70 val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32))); 71 len += snprintf(buf + len, sizeof(buf) - len, "%d: %08x ", 72 i, val[i]); 73 } 74 75 len += snprintf(buf + len, sizeof(buf) - len, "\n\n"); 76 len += snprintf(buf + len, sizeof(buf) - len, 77 "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); 78 79 for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) { 80 if (i == 8) { 81 qcuOffset = 0; 82 qcuBase++; 83 } 84 85 if (i == 6) { 86 dcuOffset = 0; 87 dcuBase++; 88 } 89 90 len += snprintf(buf + len, sizeof(buf) - len, 91 "%2d %2x %1x %2x %2x\n", 92 i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, 93 (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3), 94 val[2] & (0x7 << (i * 3)) >> (i * 3), 95 (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); 96 } 97 98 len += snprintf(buf + len, sizeof(buf) - len, "\n"); 99 100 len += snprintf(buf + len, sizeof(buf) - len, 101 "qcu_stitch state: %2x qcu_fetch state: %2x\n", 102 (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); 103 len += snprintf(buf + len, sizeof(buf) - len, 104 "qcu_complete state: %2x dcu_complete state: %2x\n", 105 (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); 106 len += snprintf(buf + len, sizeof(buf) - len, 107 "dcu_arb state: %2x dcu_fp state: %2x\n", 108 (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); 109 len += snprintf(buf + len, sizeof(buf) - len, 110 "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", 111 (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); 112 len += snprintf(buf + len, sizeof(buf) - len, 113 "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", 114 (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); 115 len += snprintf(buf + len, sizeof(buf) - len, 116 "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", 117 (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); 118 119 len += snprintf(buf + len, sizeof(buf) - len, "pcu observe: 0x%x \n", 120 REG_READ(ah, AR_OBS_BUS_1)); 121 len += snprintf(buf + len, sizeof(buf) - len, 122 "AR_CR: 0x%x \n", REG_READ(ah, AR_CR)); 123 124 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 125} 126 127static const struct file_operations fops_dma = { 128 .read = read_file_dma, 129 .open = ath9k_debugfs_open, 130 .owner = THIS_MODULE 131}; 132 133 134void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) 135{ 136 if (status) 137 sc->debug.stats.istats.total++; 138 if (status & ATH9K_INT_RX) 139 sc->debug.stats.istats.rxok++; 140 if (status & ATH9K_INT_RXEOL) 141 sc->debug.stats.istats.rxeol++; 142 if (status & ATH9K_INT_RXORN) 143 sc->debug.stats.istats.rxorn++; 144 if (status & ATH9K_INT_TX) 145 sc->debug.stats.istats.txok++; 146 if (status & ATH9K_INT_TXURN) 147 sc->debug.stats.istats.txurn++; 148 if (status & ATH9K_INT_MIB) 149 sc->debug.stats.istats.mib++; 150 if (status & ATH9K_INT_RXPHY) 151 sc->debug.stats.istats.rxphyerr++; 152 if (status & ATH9K_INT_RXKCM) 153 sc->debug.stats.istats.rx_keycache_miss++; 154 if (status & ATH9K_INT_SWBA) 155 sc->debug.stats.istats.swba++; 156 if (status & ATH9K_INT_BMISS) 157 sc->debug.stats.istats.bmiss++; 158 if (status & ATH9K_INT_BNR) 159 sc->debug.stats.istats.bnr++; 160 if (status & ATH9K_INT_CST) 161 sc->debug.stats.istats.cst++; 162 if (status & ATH9K_INT_GTT) 163 sc->debug.stats.istats.gtt++; 164 if (status & ATH9K_INT_TIM) 165 sc->debug.stats.istats.tim++; 166 if (status & ATH9K_INT_CABEND) 167 sc->debug.stats.istats.cabend++; 168 if (status & ATH9K_INT_DTIMSYNC) 169 sc->debug.stats.istats.dtimsync++; 170 if (status & ATH9K_INT_DTIM) 171 sc->debug.stats.istats.dtim++; 172} 173 174static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, 175 size_t count, loff_t *ppos) 176{ 177 struct ath_softc *sc = file->private_data; 178 char buf[512]; 179 unsigned int len = 0; 180 181 len += snprintf(buf + len, sizeof(buf) - len, 182 "%8s: %10u\n", "RX", sc->debug.stats.istats.rxok); 183 len += snprintf(buf + len, sizeof(buf) - len, 184 "%8s: %10u\n", "RXEOL", sc->debug.stats.istats.rxeol); 185 len += snprintf(buf + len, sizeof(buf) - len, 186 "%8s: %10u\n", "RXORN", sc->debug.stats.istats.rxorn); 187 len += snprintf(buf + len, sizeof(buf) - len, 188 "%8s: %10u\n", "TX", sc->debug.stats.istats.txok); 189 len += snprintf(buf + len, sizeof(buf) - len, 190 "%8s: %10u\n", "TXURN", sc->debug.stats.istats.txurn); 191 len += snprintf(buf + len, sizeof(buf) - len, 192 "%8s: %10u\n", "MIB", sc->debug.stats.istats.mib); 193 len += snprintf(buf + len, sizeof(buf) - len, 194 "%8s: %10u\n", "RXPHY", sc->debug.stats.istats.rxphyerr); 195 len += snprintf(buf + len, sizeof(buf) - len, 196 "%8s: %10u\n", "RXKCM", sc->debug.stats.istats.rx_keycache_miss); 197 len += snprintf(buf + len, sizeof(buf) - len, 198 "%8s: %10u\n", "SWBA", sc->debug.stats.istats.swba); 199 len += snprintf(buf + len, sizeof(buf) - len, 200 "%8s: %10u\n", "BMISS", sc->debug.stats.istats.bmiss); 201 len += snprintf(buf + len, sizeof(buf) - len, 202 "%8s: %10u\n", "BNR", sc->debug.stats.istats.bnr); 203 len += snprintf(buf + len, sizeof(buf) - len, 204 "%8s: %10u\n", "CST", sc->debug.stats.istats.cst); 205 len += snprintf(buf + len, sizeof(buf) - len, 206 "%8s: %10u\n", "GTT", sc->debug.stats.istats.gtt); 207 len += snprintf(buf + len, sizeof(buf) - len, 208 "%8s: %10u\n", "TIM", sc->debug.stats.istats.tim); 209 len += snprintf(buf + len, sizeof(buf) - len, 210 "%8s: %10u\n", "CABEND", sc->debug.stats.istats.cabend); 211 len += snprintf(buf + len, sizeof(buf) - len, 212 "%8s: %10u\n", "DTIMSYNC", sc->debug.stats.istats.dtimsync); 213 len += snprintf(buf + len, sizeof(buf) - len, 214 "%8s: %10u\n", "DTIM", sc->debug.stats.istats.dtim); 215 len += snprintf(buf + len, sizeof(buf) - len, 216 "%8s: %10u\n", "TOTAL", sc->debug.stats.istats.total); 217 218 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 219} 220 221static const struct file_operations fops_interrupt = { 222 .read = read_file_interrupt, 223 .open = ath9k_debugfs_open, 224 .owner = THIS_MODULE 225}; 226 227static void ath_debug_stat_11n_rc(struct ath_softc *sc, struct sk_buff *skb) 228{ 229 struct ath_tx_info_priv *tx_info_priv = NULL; 230 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); 231 struct ieee80211_tx_rate *rates = tx_info->status.rates; 232 int final_ts_idx, idx; 233 234 tx_info_priv = ATH_TX_INFO_PRIV(tx_info); 235 final_ts_idx = tx_info_priv->tx.ts_rateindex; 236 idx = sc->cur_rate_table->info[rates[final_ts_idx].idx].dot11rate; 237 238 sc->debug.stats.n_rcstats[idx].success++; 239} 240 241static void ath_debug_stat_legacy_rc(struct ath_softc *sc, struct sk_buff *skb) 242{ 243 struct ath_tx_info_priv *tx_info_priv = NULL; 244 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); 245 struct ieee80211_tx_rate *rates = tx_info->status.rates; 246 int final_ts_idx, idx; 247 248 tx_info_priv = ATH_TX_INFO_PRIV(tx_info); 249 final_ts_idx = tx_info_priv->tx.ts_rateindex; 250 idx = rates[final_ts_idx].idx; 251 252 sc->debug.stats.legacy_rcstats[idx].success++; 253} 254 255void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb) 256{ 257 if (conf_is_ht(&sc->hw->conf)) 258 ath_debug_stat_11n_rc(sc, skb); 259 else 260 ath_debug_stat_legacy_rc(sc, skb); 261} 262 263/* FIXME: legacy rates, later on .. */ 264void ath_debug_stat_retries(struct ath_softc *sc, int rix, 265 int xretries, int retries, u8 per) 266{ 267 if (conf_is_ht(&sc->hw->conf)) { 268 int idx = sc->cur_rate_table->info[rix].dot11rate; 269 270 sc->debug.stats.n_rcstats[idx].xretries += xretries; 271 sc->debug.stats.n_rcstats[idx].retries += retries; 272 sc->debug.stats.n_rcstats[idx].per = per; 273 } 274} 275 276static ssize_t ath_read_file_stat_11n_rc(struct file *file, 277 char __user *user_buf, 278 size_t count, loff_t *ppos) 279{ 280 struct ath_softc *sc = file->private_data; 281 char buf[1024]; 282 unsigned int len = 0; 283 int i = 0; 284 285 len += sprintf(buf, "%7s %13s %8s %8s %6s\n\n", "Rate", "Success", 286 "Retries", "XRetries", "PER"); 287 288 for (i = 0; i <= 15; i++) { 289 len += snprintf(buf + len, sizeof(buf) - len, 290 "%5s%3d: %8u %8u %8u %8u\n", "MCS", i, 291 sc->debug.stats.n_rcstats[i].success, 292 sc->debug.stats.n_rcstats[i].retries, 293 sc->debug.stats.n_rcstats[i].xretries, 294 sc->debug.stats.n_rcstats[i].per); 295 } 296 297 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 298} 299 300static ssize_t ath_read_file_stat_legacy_rc(struct file *file, 301 char __user *user_buf, 302 size_t count, loff_t *ppos) 303{ 304 struct ath_softc *sc = file->private_data; 305 char buf[512]; 306 unsigned int len = 0; 307 int i = 0; 308 309 len += sprintf(buf, "%7s %13s\n\n", "Rate", "Success"); 310 311 for (i = 0; i < sc->cur_rate_table->rate_cnt; i++) { 312 len += snprintf(buf + len, sizeof(buf) - len, "%5u: %12u\n", 313 sc->cur_rate_table->info[i].ratekbps / 1000, 314 sc->debug.stats.legacy_rcstats[i].success); 315 } 316 317 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 318} 319 320static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, 321 size_t count, loff_t *ppos) 322{ 323 struct ath_softc *sc = file->private_data; 324 325 if (sc->cur_rate_table == NULL) 326 return 0; 327 328 if (conf_is_ht(&sc->hw->conf)) 329 return ath_read_file_stat_11n_rc(file, user_buf, count, ppos); 330 else 331 return ath_read_file_stat_legacy_rc(file, user_buf, count ,ppos); 332} 333 334static const struct file_operations fops_rcstat = { 335 .read = read_file_rcstat, 336 .open = ath9k_debugfs_open, 337 .owner = THIS_MODULE 338}; 339 340static const char * ath_wiphy_state_str(enum ath_wiphy_state state) 341{ 342 switch (state) { 343 case ATH_WIPHY_INACTIVE: 344 return "INACTIVE"; 345 case ATH_WIPHY_ACTIVE: 346 return "ACTIVE"; 347 case ATH_WIPHY_PAUSING: 348 return "PAUSING"; 349 case ATH_WIPHY_PAUSED: 350 return "PAUSED"; 351 case ATH_WIPHY_SCAN: 352 return "SCAN"; 353 } 354 return "?"; 355} 356 357static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, 358 size_t count, loff_t *ppos) 359{ 360 struct ath_softc *sc = file->private_data; 361 char buf[512]; 362 unsigned int len = 0; 363 int i; 364 u8 addr[ETH_ALEN]; 365 366 len += snprintf(buf + len, sizeof(buf) - len, 367 "primary: %s (%s chan=%d ht=%d)\n", 368 wiphy_name(sc->pri_wiphy->hw->wiphy), 369 ath_wiphy_state_str(sc->pri_wiphy->state), 370 sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht); 371 for (i = 0; i < sc->num_sec_wiphy; i++) { 372 struct ath_wiphy *aphy = sc->sec_wiphy[i]; 373 if (aphy == NULL) 374 continue; 375 len += snprintf(buf + len, sizeof(buf) - len, 376 "secondary: %s (%s chan=%d ht=%d)\n", 377 wiphy_name(aphy->hw->wiphy), 378 ath_wiphy_state_str(aphy->state), 379 aphy->chan_idx, aphy->chan_is_ht); 380 } 381 382 put_unaligned_le32(REG_READ(sc->sc_ah, AR_STA_ID0), addr); 383 put_unaligned_le16(REG_READ(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4); 384 len += snprintf(buf + len, sizeof(buf) - len, 385 "addr: %pM\n", addr); 386 put_unaligned_le32(REG_READ(sc->sc_ah, AR_BSSMSKL), addr); 387 put_unaligned_le16(REG_READ(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4); 388 len += snprintf(buf + len, sizeof(buf) - len, 389 "addrmask: %pM\n", addr); 390 391 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 392} 393 394static struct ath_wiphy * get_wiphy(struct ath_softc *sc, const char *name) 395{ 396 int i; 397 if (strcmp(name, wiphy_name(sc->pri_wiphy->hw->wiphy)) == 0) 398 return sc->pri_wiphy; 399 for (i = 0; i < sc->num_sec_wiphy; i++) { 400 struct ath_wiphy *aphy = sc->sec_wiphy[i]; 401 if (aphy && strcmp(name, wiphy_name(aphy->hw->wiphy)) == 0) 402 return aphy; 403 } 404 return NULL; 405} 406 407static int del_wiphy(struct ath_softc *sc, const char *name) 408{ 409 struct ath_wiphy *aphy = get_wiphy(sc, name); 410 if (!aphy) 411 return -ENOENT; 412 return ath9k_wiphy_del(aphy); 413} 414 415static int pause_wiphy(struct ath_softc *sc, const char *name) 416{ 417 struct ath_wiphy *aphy = get_wiphy(sc, name); 418 if (!aphy) 419 return -ENOENT; 420 return ath9k_wiphy_pause(aphy); 421} 422 423static int unpause_wiphy(struct ath_softc *sc, const char *name) 424{ 425 struct ath_wiphy *aphy = get_wiphy(sc, name); 426 if (!aphy) 427 return -ENOENT; 428 return ath9k_wiphy_unpause(aphy); 429} 430 431static int select_wiphy(struct ath_softc *sc, const char *name) 432{ 433 struct ath_wiphy *aphy = get_wiphy(sc, name); 434 if (!aphy) 435 return -ENOENT; 436 return ath9k_wiphy_select(aphy); 437} 438 439static int schedule_wiphy(struct ath_softc *sc, const char *msec) 440{ 441 ath9k_wiphy_set_scheduler(sc, simple_strtoul(msec, NULL, 0)); 442 return 0; 443} 444 445static ssize_t write_file_wiphy(struct file *file, const char __user *user_buf, 446 size_t count, loff_t *ppos) 447{ 448 struct ath_softc *sc = file->private_data; 449 char buf[50]; 450 size_t len; 451 452 len = min(count, sizeof(buf) - 1); 453 if (copy_from_user(buf, user_buf, len)) 454 return -EFAULT; 455 buf[len] = '\0'; 456 if (len > 0 && buf[len - 1] == '\n') 457 buf[len - 1] = '\0'; 458 459 if (strncmp(buf, "add", 3) == 0) { 460 int res = ath9k_wiphy_add(sc); 461 if (res < 0) 462 return res; 463 } else if (strncmp(buf, "del=", 4) == 0) { 464 int res = del_wiphy(sc, buf + 4); 465 if (res < 0) 466 return res; 467 } else if (strncmp(buf, "pause=", 6) == 0) { 468 int res = pause_wiphy(sc, buf + 6); 469 if (res < 0) 470 return res; 471 } else if (strncmp(buf, "unpause=", 8) == 0) { 472 int res = unpause_wiphy(sc, buf + 8); 473 if (res < 0) 474 return res; 475 } else if (strncmp(buf, "select=", 7) == 0) { 476 int res = select_wiphy(sc, buf + 7); 477 if (res < 0) 478 return res; 479 } else if (strncmp(buf, "schedule=", 9) == 0) { 480 int res = schedule_wiphy(sc, buf + 9); 481 if (res < 0) 482 return res; 483 } else 484 return -EOPNOTSUPP; 485 486 return count; 487} 488 489static const struct file_operations fops_wiphy = { 490 .read = read_file_wiphy, 491 .write = write_file_wiphy, 492 .open = ath9k_debugfs_open, 493 .owner = THIS_MODULE 494}; 495 496 497int ath9k_init_debug(struct ath_softc *sc) 498{ 499 sc->debug.debug_mask = ath9k_debug; 500 501 sc->debug.debugfs_phy = debugfs_create_dir(wiphy_name(sc->hw->wiphy), 502 ath9k_debugfs_root); 503 if (!sc->debug.debugfs_phy) 504 goto err; 505 506 sc->debug.debugfs_dma = debugfs_create_file("dma", S_IRUGO, 507 sc->debug.debugfs_phy, sc, &fops_dma); 508 if (!sc->debug.debugfs_dma) 509 goto err; 510 511 sc->debug.debugfs_interrupt = debugfs_create_file("interrupt", 512 S_IRUGO, 513 sc->debug.debugfs_phy, 514 sc, &fops_interrupt); 515 if (!sc->debug.debugfs_interrupt) 516 goto err; 517 518 sc->debug.debugfs_rcstat = debugfs_create_file("rcstat", 519 S_IRUGO, 520 sc->debug.debugfs_phy, 521 sc, &fops_rcstat); 522 if (!sc->debug.debugfs_rcstat) 523 goto err; 524 525 sc->debug.debugfs_wiphy = debugfs_create_file( 526 "wiphy", S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc, 527 &fops_wiphy); 528 if (!sc->debug.debugfs_wiphy) 529 goto err; 530 531 return 0; 532err: 533 ath9k_exit_debug(sc); 534 return -ENOMEM; 535} 536 537void ath9k_exit_debug(struct ath_softc *sc) 538{ 539 debugfs_remove(sc->debug.debugfs_wiphy); 540 debugfs_remove(sc->debug.debugfs_rcstat); 541 debugfs_remove(sc->debug.debugfs_interrupt); 542 debugfs_remove(sc->debug.debugfs_dma); 543 debugfs_remove(sc->debug.debugfs_phy); 544} 545 546int ath9k_debug_create_root(void) 547{ 548 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); 549 if (!ath9k_debugfs_root) 550 return -ENOENT; 551 552 return 0; 553} 554 555void ath9k_debug_remove_root(void) 556{ 557 debugfs_remove(ath9k_debugfs_root); 558 ath9k_debugfs_root = NULL; 559}