1/* 2 * Copyright (c) 2004-2011 Atheros Communications Inc. 3 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include "core.h" 19 20#include <linux/skbuff.h> 21#include <linux/fs.h> 22#include <linux/vmalloc.h> 23#include <linux/export.h> 24 25#include "debug.h" 26#include "target.h" 27 28struct ath6kl_fwlog_slot { 29 __le32 timestamp; 30 __le32 length; 31 32 /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */ 33 u8 payload[0]; 34}; 35 36#define ATH6KL_FWLOG_MAX_ENTRIES 20 37 38#define ATH6KL_FWLOG_VALID_MASK 0x1ffff 39 40int ath6kl_printk(const char *level, const char *fmt, ...) 41{ 42 struct va_format vaf; 43 va_list args; 44 int rtn; 45 46 va_start(args, fmt); 47 48 vaf.fmt = fmt; 49 vaf.va = &args; 50 51 rtn = printk("%sath6kl: %pV", level, &vaf); 52 53 va_end(args); 54 55 return rtn; 56} 57EXPORT_SYMBOL(ath6kl_printk); 58 59#ifdef CONFIG_ATH6KL_DEBUG 60 61void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...) 62{ 63 struct va_format vaf; 64 va_list args; 65 66 if (!(debug_mask & mask)) 67 return; 68 69 va_start(args, fmt); 70 71 vaf.fmt = fmt; 72 vaf.va = &args; 73 74 ath6kl_printk(KERN_DEBUG, "%pV", &vaf); 75 76 va_end(args); 77} 78EXPORT_SYMBOL(ath6kl_dbg); 79 80void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, 81 const char *msg, const char *prefix, 82 const void *buf, size_t len) 83{ 84 if (debug_mask & mask) { 85 if (msg) 86 ath6kl_dbg(mask, "%s\n", msg); 87 88 print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); 89 } 90} 91EXPORT_SYMBOL(ath6kl_dbg_dump); 92 93#define REG_OUTPUT_LEN_PER_LINE 25 94#define REGTYPE_STR_LEN 100 95 96struct ath6kl_diag_reg_info { 97 u32 reg_start; 98 u32 reg_end; 99 const char *reg_info; 100}; 101 102static const struct ath6kl_diag_reg_info diag_reg[] = { 103 { 0x20000, 0x200fc, "General DMA and Rx registers" }, 104 { 0x28000, 0x28900, "MAC PCU register & keycache" }, 105 { 0x20800, 0x20a40, "QCU" }, 106 { 0x21000, 0x212f0, "DCU" }, 107 { 0x4000, 0x42e4, "RTC" }, 108 { 0x540000, 0x540000 + (256 * 1024), "RAM" }, 109 { 0x29800, 0x2B210, "Base Band" }, 110 { 0x1C000, 0x1C748, "Analog" }, 111}; 112 113void ath6kl_dump_registers(struct ath6kl_device *dev, 114 struct ath6kl_irq_proc_registers *irq_proc_reg, 115 struct ath6kl_irq_enable_reg *irq_enable_reg) 116{ 117 118 ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- Register Table -------->\n")); 119 120 if (irq_proc_reg != NULL) { 121 ath6kl_dbg(ATH6KL_DBG_IRQ, 122 "Host Int status: 0x%x\n", 123 irq_proc_reg->host_int_status); 124 ath6kl_dbg(ATH6KL_DBG_IRQ, 125 "CPU Int status: 0x%x\n", 126 irq_proc_reg->cpu_int_status); 127 ath6kl_dbg(ATH6KL_DBG_IRQ, 128 "Error Int status: 0x%x\n", 129 irq_proc_reg->error_int_status); 130 ath6kl_dbg(ATH6KL_DBG_IRQ, 131 "Counter Int status: 0x%x\n", 132 irq_proc_reg->counter_int_status); 133 ath6kl_dbg(ATH6KL_DBG_IRQ, 134 "Mbox Frame: 0x%x\n", 135 irq_proc_reg->mbox_frame); 136 ath6kl_dbg(ATH6KL_DBG_IRQ, 137 "Rx Lookahead Valid: 0x%x\n", 138 irq_proc_reg->rx_lkahd_valid); 139 ath6kl_dbg(ATH6KL_DBG_IRQ, 140 "Rx Lookahead 0: 0x%x\n", 141 irq_proc_reg->rx_lkahd[0]); 142 ath6kl_dbg(ATH6KL_DBG_IRQ, 143 "Rx Lookahead 1: 0x%x\n", 144 irq_proc_reg->rx_lkahd[1]); 145 146 if (dev->ar->mbox_info.gmbox_addr != 0) { 147 /* 148 * If the target supports GMBOX hardware, dump some 149 * additional state. 150 */ 151 ath6kl_dbg(ATH6KL_DBG_IRQ, 152 "GMBOX Host Int status 2: 0x%x\n", 153 irq_proc_reg->host_int_status2); 154 ath6kl_dbg(ATH6KL_DBG_IRQ, 155 "GMBOX RX Avail: 0x%x\n", 156 irq_proc_reg->gmbox_rx_avail); 157 ath6kl_dbg(ATH6KL_DBG_IRQ, 158 "GMBOX lookahead alias 0: 0x%x\n", 159 irq_proc_reg->rx_gmbox_lkahd_alias[0]); 160 ath6kl_dbg(ATH6KL_DBG_IRQ, 161 "GMBOX lookahead alias 1: 0x%x\n", 162 irq_proc_reg->rx_gmbox_lkahd_alias[1]); 163 } 164 165 } 166 167 if (irq_enable_reg != NULL) { 168 ath6kl_dbg(ATH6KL_DBG_IRQ, 169 "Int status Enable: 0x%x\n", 170 irq_enable_reg->int_status_en); 171 ath6kl_dbg(ATH6KL_DBG_IRQ, "Counter Int status Enable: 0x%x\n", 172 irq_enable_reg->cntr_int_status_en); 173 } 174 ath6kl_dbg(ATH6KL_DBG_IRQ, "<------------------------------->\n"); 175} 176 177static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist) 178{ 179 ath6kl_dbg(ATH6KL_DBG_CREDIT, 180 "--- endpoint: %d svc_id: 0x%X ---\n", 181 ep_dist->endpoint, ep_dist->svc_id); 182 ath6kl_dbg(ATH6KL_DBG_CREDIT, " dist_flags : 0x%X\n", 183 ep_dist->dist_flags); 184 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_norm : %d\n", 185 ep_dist->cred_norm); 186 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_min : %d\n", 187 ep_dist->cred_min); 188 ath6kl_dbg(ATH6KL_DBG_CREDIT, " credits : %d\n", 189 ep_dist->credits); 190 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_assngd : %d\n", 191 ep_dist->cred_assngd); 192 ath6kl_dbg(ATH6KL_DBG_CREDIT, " seek_cred : %d\n", 193 ep_dist->seek_cred); 194 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_sz : %d\n", 195 ep_dist->cred_sz); 196 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_per_msg : %d\n", 197 ep_dist->cred_per_msg); 198 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_to_dist : %d\n", 199 ep_dist->cred_to_dist); 200 ath6kl_dbg(ATH6KL_DBG_CREDIT, " txq_depth : %d\n", 201 get_queue_depth(&ep_dist->htc_ep->txq)); 202 ath6kl_dbg(ATH6KL_DBG_CREDIT, 203 "----------------------------------\n"); 204} 205 206/* FIXME: move to htc.c */ 207void dump_cred_dist_stats(struct htc_target *target) 208{ 209 struct htc_endpoint_credit_dist *ep_list; 210 211 list_for_each_entry(ep_list, &target->cred_dist_list, list) 212 dump_cred_dist(ep_list); 213 214 ath6kl_dbg(ATH6KL_DBG_CREDIT, 215 "credit distribution total %d free %d\n", 216 target->credit_info->total_avail_credits, 217 target->credit_info->cur_free_credits); 218} 219 220void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war) 221{ 222 switch (war) { 223 case ATH6KL_WAR_INVALID_RATE: 224 ar->debug.war_stats.invalid_rate++; 225 break; 226 } 227} 228 229static ssize_t read_file_war_stats(struct file *file, char __user *user_buf, 230 size_t count, loff_t *ppos) 231{ 232 struct ath6kl *ar = file->private_data; 233 char *buf; 234 unsigned int len = 0, buf_len = 1500; 235 ssize_t ret_cnt; 236 237 buf = kzalloc(buf_len, GFP_KERNEL); 238 if (!buf) 239 return -ENOMEM; 240 241 len += scnprintf(buf + len, buf_len - len, "\n"); 242 len += scnprintf(buf + len, buf_len - len, "%25s\n", 243 "Workaround stats"); 244 len += scnprintf(buf + len, buf_len - len, "%25s\n\n", 245 "================="); 246 len += scnprintf(buf + len, buf_len - len, "%20s %10u\n", 247 "Invalid rates", ar->debug.war_stats.invalid_rate); 248 249 if (WARN_ON(len > buf_len)) 250 len = buf_len; 251 252 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); 253 254 kfree(buf); 255 return ret_cnt; 256} 257 258static const struct file_operations fops_war_stats = { 259 .read = read_file_war_stats, 260 .open = simple_open, 261 .owner = THIS_MODULE, 262 .llseek = default_llseek, 263}; 264 265void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) 266{ 267 struct ath6kl_fwlog_slot *slot; 268 struct sk_buff *skb; 269 size_t slot_len; 270 271 if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE)) 272 return; 273 274 slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE; 275 276 skb = alloc_skb(slot_len, GFP_KERNEL); 277 if (!skb) 278 return; 279 280 slot = (struct ath6kl_fwlog_slot *) skb_put(skb, slot_len); 281 slot->timestamp = cpu_to_le32(jiffies); 282 slot->length = cpu_to_le32(len); 283 memcpy(slot->payload, buf, len); 284 285 /* Need to pad each record to fixed length ATH6KL_FWLOG_PAYLOAD_SIZE */ 286 memset(slot->payload + len, 0, ATH6KL_FWLOG_PAYLOAD_SIZE - len); 287 288 spin_lock(&ar->debug.fwlog_queue.lock); 289 290 __skb_queue_tail(&ar->debug.fwlog_queue, skb); 291 complete(&ar->debug.fwlog_completion); 292 293 /* drop oldest entries */ 294 while (skb_queue_len(&ar->debug.fwlog_queue) > 295 ATH6KL_FWLOG_MAX_ENTRIES) { 296 skb = __skb_dequeue(&ar->debug.fwlog_queue); 297 kfree_skb(skb); 298 } 299 300 spin_unlock(&ar->debug.fwlog_queue.lock); 301 302 return; 303} 304 305static int ath6kl_fwlog_open(struct inode *inode, struct file *file) 306{ 307 struct ath6kl *ar = inode->i_private; 308 309 if (ar->debug.fwlog_open) 310 return -EBUSY; 311 312 ar->debug.fwlog_open = true; 313 314 file->private_data = inode->i_private; 315 return 0; 316} 317 318static int ath6kl_fwlog_release(struct inode *inode, struct file *file) 319{ 320 struct ath6kl *ar = inode->i_private; 321 322 ar->debug.fwlog_open = false; 323 324 return 0; 325} 326 327static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, 328 size_t count, loff_t *ppos) 329{ 330 struct ath6kl *ar = file->private_data; 331 struct sk_buff *skb; 332 ssize_t ret_cnt; 333 size_t len = 0; 334 char *buf; 335 336 buf = vmalloc(count); 337 if (!buf) 338 return -ENOMEM; 339 340 /* read undelivered logs from firmware */ 341 ath6kl_read_fwlogs(ar); 342 343 spin_lock(&ar->debug.fwlog_queue.lock); 344 345 while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) { 346 if (skb->len > count - len) { 347 /* not enough space, put skb back and leave */ 348 __skb_queue_head(&ar->debug.fwlog_queue, skb); 349 break; 350 } 351 352 353 memcpy(buf + len, skb->data, skb->len); 354 len += skb->len; 355 356 kfree_skb(skb); 357 } 358 359 spin_unlock(&ar->debug.fwlog_queue.lock); 360 361 /* FIXME: what to do if len == 0? */ 362 363 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); 364 365 vfree(buf); 366 367 return ret_cnt; 368} 369 370static const struct file_operations fops_fwlog = { 371 .open = ath6kl_fwlog_open, 372 .release = ath6kl_fwlog_release, 373 .read = ath6kl_fwlog_read, 374 .owner = THIS_MODULE, 375 .llseek = default_llseek, 376}; 377 378static ssize_t ath6kl_fwlog_block_read(struct file *file, 379 char __user *user_buf, 380 size_t count, 381 loff_t *ppos) 382{ 383 struct ath6kl *ar = file->private_data; 384 struct sk_buff *skb; 385 ssize_t ret_cnt; 386 size_t len = 0, not_copied; 387 char *buf; 388 int ret; 389 390 buf = vmalloc(count); 391 if (!buf) 392 return -ENOMEM; 393 394 spin_lock(&ar->debug.fwlog_queue.lock); 395 396 if (skb_queue_len(&ar->debug.fwlog_queue) == 0) { 397 /* we must init under queue lock */ 398 init_completion(&ar->debug.fwlog_completion); 399 400 spin_unlock(&ar->debug.fwlog_queue.lock); 401 402 ret = wait_for_completion_interruptible( 403 &ar->debug.fwlog_completion); 404 if (ret == -ERESTARTSYS) 405 return ret; 406 407 spin_lock(&ar->debug.fwlog_queue.lock); 408 } 409 410 while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) { 411 if (skb->len > count - len) { 412 /* not enough space, put skb back and leave */ 413 __skb_queue_head(&ar->debug.fwlog_queue, skb); 414 break; 415 } 416 417 418 memcpy(buf + len, skb->data, skb->len); 419 len += skb->len; 420 421 kfree_skb(skb); 422 } 423 424 spin_unlock(&ar->debug.fwlog_queue.lock); 425 426 /* FIXME: what to do if len == 0? */ 427 428 not_copied = copy_to_user(user_buf, buf, len); 429 if (not_copied != 0) { 430 ret_cnt = -EFAULT; 431 goto out; 432 } 433 434 *ppos = *ppos + len; 435 436 ret_cnt = len; 437 438out: 439 vfree(buf); 440 441 return ret_cnt; 442} 443 444static const struct file_operations fops_fwlog_block = { 445 .open = ath6kl_fwlog_open, 446 .release = ath6kl_fwlog_release, 447 .read = ath6kl_fwlog_block_read, 448 .owner = THIS_MODULE, 449 .llseek = default_llseek, 450}; 451 452static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf, 453 size_t count, loff_t *ppos) 454{ 455 struct ath6kl *ar = file->private_data; 456 char buf[16]; 457 int len; 458 459 len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask); 460 461 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 462} 463 464static ssize_t ath6kl_fwlog_mask_write(struct file *file, 465 const char __user *user_buf, 466 size_t count, loff_t *ppos) 467{ 468 struct ath6kl *ar = file->private_data; 469 int ret; 470 471 ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask); 472 if (ret) 473 return ret; 474 475 ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi, 476 ATH6KL_FWLOG_VALID_MASK, 477 ar->debug.fwlog_mask); 478 if (ret) 479 return ret; 480 481 return count; 482} 483 484static const struct file_operations fops_fwlog_mask = { 485 .open = simple_open, 486 .read = ath6kl_fwlog_mask_read, 487 .write = ath6kl_fwlog_mask_write, 488 .owner = THIS_MODULE, 489 .llseek = default_llseek, 490}; 491 492static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, 493 size_t count, loff_t *ppos) 494{ 495 struct ath6kl *ar = file->private_data; 496 struct ath6kl_vif *vif; 497 struct target_stats *tgt_stats; 498 char *buf; 499 unsigned int len = 0, buf_len = 1500; 500 int i; 501 long left; 502 ssize_t ret_cnt; 503 504 vif = ath6kl_vif_first(ar); 505 if (!vif) 506 return -EIO; 507 508 tgt_stats = &vif->target_stats; 509 510 buf = kzalloc(buf_len, GFP_KERNEL); 511 if (!buf) 512 return -ENOMEM; 513 514 if (down_interruptible(&ar->sem)) { 515 kfree(buf); 516 return -EBUSY; 517 } 518 519 set_bit(STATS_UPDATE_PEND, &vif->flags); 520 521 if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) { 522 up(&ar->sem); 523 kfree(buf); 524 return -EIO; 525 } 526 527 left = wait_event_interruptible_timeout(ar->event_wq, 528 !test_bit(STATS_UPDATE_PEND, 529 &vif->flags), WMI_TIMEOUT); 530 531 up(&ar->sem); 532 533 if (left <= 0) { 534 kfree(buf); 535 return -ETIMEDOUT; 536 } 537 538 len += scnprintf(buf + len, buf_len - len, "\n"); 539 len += scnprintf(buf + len, buf_len - len, "%25s\n", 540 "Target Tx stats"); 541 len += scnprintf(buf + len, buf_len - len, "%25s\n\n", 542 "================="); 543 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 544 "Ucast packets", tgt_stats->tx_ucast_pkt); 545 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 546 "Bcast packets", tgt_stats->tx_bcast_pkt); 547 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 548 "Ucast byte", tgt_stats->tx_ucast_byte); 549 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 550 "Bcast byte", tgt_stats->tx_bcast_byte); 551 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 552 "Rts success cnt", tgt_stats->tx_rts_success_cnt); 553 for (i = 0; i < 4; i++) 554 len += scnprintf(buf + len, buf_len - len, 555 "%18s %d %10llu\n", "PER on ac", 556 i, tgt_stats->tx_pkt_per_ac[i]); 557 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 558 "Error", tgt_stats->tx_err); 559 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 560 "Fail count", tgt_stats->tx_fail_cnt); 561 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 562 "Retry count", tgt_stats->tx_retry_cnt); 563 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 564 "Multi retry cnt", tgt_stats->tx_mult_retry_cnt); 565 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 566 "Rts fail cnt", tgt_stats->tx_rts_fail_cnt); 567 len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n\n", 568 "TKIP counter measure used", 569 tgt_stats->tkip_cnter_measures_invoked); 570 571 len += scnprintf(buf + len, buf_len - len, "%25s\n", 572 "Target Rx stats"); 573 len += scnprintf(buf + len, buf_len - len, "%25s\n", 574 "================="); 575 576 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 577 "Ucast packets", tgt_stats->rx_ucast_pkt); 578 len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", 579 "Ucast Rate", tgt_stats->rx_ucast_rate); 580 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 581 "Bcast packets", tgt_stats->rx_bcast_pkt); 582 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 583 "Ucast byte", tgt_stats->rx_ucast_byte); 584 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 585 "Bcast byte", tgt_stats->rx_bcast_byte); 586 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 587 "Fragmented pkt", tgt_stats->rx_frgment_pkt); 588 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 589 "Error", tgt_stats->rx_err); 590 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 591 "CRC Err", tgt_stats->rx_crc_err); 592 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 593 "Key chache miss", tgt_stats->rx_key_cache_miss); 594 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 595 "Decrypt Err", tgt_stats->rx_decrypt_err); 596 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 597 "Duplicate frame", tgt_stats->rx_dupl_frame); 598 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 599 "Tkip Mic failure", tgt_stats->tkip_local_mic_fail); 600 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 601 "TKIP format err", tgt_stats->tkip_fmt_err); 602 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 603 "CCMP format Err", tgt_stats->ccmp_fmt_err); 604 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n\n", 605 "CCMP Replay Err", tgt_stats->ccmp_replays); 606 607 len += scnprintf(buf + len, buf_len - len, "%25s\n", 608 "Misc Target stats"); 609 len += scnprintf(buf + len, buf_len - len, "%25s\n", 610 "================="); 611 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 612 "Beacon Miss count", tgt_stats->cs_bmiss_cnt); 613 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 614 "Num Connects", tgt_stats->cs_connect_cnt); 615 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n", 616 "Num disconnects", tgt_stats->cs_discon_cnt); 617 len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", 618 "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi); 619 620 if (len > buf_len) 621 len = buf_len; 622 623 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); 624 625 kfree(buf); 626 return ret_cnt; 627} 628 629static const struct file_operations fops_tgt_stats = { 630 .read = read_file_tgt_stats, 631 .open = simple_open, 632 .owner = THIS_MODULE, 633 .llseek = default_llseek, 634}; 635 636#define print_credit_info(fmt_str, ep_list_field) \ 637 (len += scnprintf(buf + len, buf_len - len, fmt_str, \ 638 ep_list->ep_list_field)) 639#define CREDIT_INFO_DISPLAY_STRING_LEN 200 640#define CREDIT_INFO_LEN 128 641 642static ssize_t read_file_credit_dist_stats(struct file *file, 643 char __user *user_buf, 644 size_t count, loff_t *ppos) 645{ 646 struct ath6kl *ar = file->private_data; 647 struct htc_target *target = ar->htc_target; 648 struct htc_endpoint_credit_dist *ep_list; 649 char *buf; 650 unsigned int buf_len, len = 0; 651 ssize_t ret_cnt; 652 653 buf_len = CREDIT_INFO_DISPLAY_STRING_LEN + 654 get_queue_depth(&target->cred_dist_list) * CREDIT_INFO_LEN; 655 buf = kzalloc(buf_len, GFP_KERNEL); 656 if (!buf) 657 return -ENOMEM; 658 659 len += scnprintf(buf + len, buf_len - len, "%25s%5d\n", 660 "Total Avail Credits: ", 661 target->credit_info->total_avail_credits); 662 len += scnprintf(buf + len, buf_len - len, "%25s%5d\n", 663 "Free credits :", 664 target->credit_info->cur_free_credits); 665 666 len += scnprintf(buf + len, buf_len - len, 667 " Epid Flags Cred_norm Cred_min Credits Cred_assngd" 668 " Seek_cred Cred_sz Cred_per_msg Cred_to_dist" 669 " qdepth\n"); 670 671 list_for_each_entry(ep_list, &target->cred_dist_list, list) { 672 print_credit_info(" %2d", endpoint); 673 print_credit_info("%10x", dist_flags); 674 print_credit_info("%8d", cred_norm); 675 print_credit_info("%9d", cred_min); 676 print_credit_info("%9d", credits); 677 print_credit_info("%10d", cred_assngd); 678 print_credit_info("%13d", seek_cred); 679 print_credit_info("%12d", cred_sz); 680 print_credit_info("%9d", cred_per_msg); 681 print_credit_info("%14d", cred_to_dist); 682 len += scnprintf(buf + len, buf_len - len, "%12d\n", 683 get_queue_depth(&ep_list->htc_ep->txq)); 684 } 685 686 if (len > buf_len) 687 len = buf_len; 688 689 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); 690 kfree(buf); 691 return ret_cnt; 692} 693 694static const struct file_operations fops_credit_dist_stats = { 695 .read = read_file_credit_dist_stats, 696 .open = simple_open, 697 .owner = THIS_MODULE, 698 .llseek = default_llseek, 699}; 700 701static unsigned int print_endpoint_stat(struct htc_target *target, char *buf, 702 unsigned int buf_len, unsigned int len, 703 int offset, const char *name) 704{ 705 int i; 706 struct htc_endpoint_stats *ep_st; 707 u32 *counter; 708 709 len += scnprintf(buf + len, buf_len - len, "%s:", name); 710 for (i = 0; i < ENDPOINT_MAX; i++) { 711 ep_st = &target->endpoint[i].ep_st; 712 counter = ((u32 *) ep_st) + (offset / 4); 713 len += scnprintf(buf + len, buf_len - len, " %u", *counter); 714 } 715 len += scnprintf(buf + len, buf_len - len, "\n"); 716 717 return len; 718} 719 720static ssize_t ath6kl_endpoint_stats_read(struct file *file, 721 char __user *user_buf, 722 size_t count, loff_t *ppos) 723{ 724 struct ath6kl *ar = file->private_data; 725 struct htc_target *target = ar->htc_target; 726 char *buf; 727 unsigned int buf_len, len = 0; 728 ssize_t ret_cnt; 729 730 buf_len = sizeof(struct htc_endpoint_stats) / sizeof(u32) * 731 (25 + ENDPOINT_MAX * 11); 732 buf = kmalloc(buf_len, GFP_KERNEL); 733 if (!buf) 734 return -ENOMEM; 735 736#define EPSTAT(name) \ 737 do { \ 738 len = print_endpoint_stat(target, buf, buf_len, len, \ 739 offsetof(struct htc_endpoint_stats, \ 740 name), \ 741 #name); \ 742 } while (0) 743 744 EPSTAT(cred_low_indicate); 745 EPSTAT(tx_issued); 746 EPSTAT(tx_pkt_bundled); 747 EPSTAT(tx_bundles); 748 EPSTAT(tx_dropped); 749 EPSTAT(tx_cred_rpt); 750 EPSTAT(cred_rpt_from_rx); 751 EPSTAT(cred_rpt_from_other); 752 EPSTAT(cred_rpt_ep0); 753 EPSTAT(cred_from_rx); 754 EPSTAT(cred_from_other); 755 EPSTAT(cred_from_ep0); 756 EPSTAT(cred_cosumd); 757 EPSTAT(cred_retnd); 758 EPSTAT(rx_pkts); 759 EPSTAT(rx_lkahds); 760 EPSTAT(rx_bundl); 761 EPSTAT(rx_bundle_lkahd); 762 EPSTAT(rx_bundle_from_hdr); 763 EPSTAT(rx_alloc_thresh_hit); 764 EPSTAT(rxalloc_thresh_byte); 765#undef EPSTAT 766 767 if (len > buf_len) 768 len = buf_len; 769 770 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); 771 kfree(buf); 772 return ret_cnt; 773} 774 775static ssize_t ath6kl_endpoint_stats_write(struct file *file, 776 const char __user *user_buf, 777 size_t count, loff_t *ppos) 778{ 779 struct ath6kl *ar = file->private_data; 780 struct htc_target *target = ar->htc_target; 781 int ret, i; 782 u32 val; 783 struct htc_endpoint_stats *ep_st; 784 785 ret = kstrtou32_from_user(user_buf, count, 0, &val); 786 if (ret) 787 return ret; 788 if (val == 0) { 789 for (i = 0; i < ENDPOINT_MAX; i++) { 790 ep_st = &target->endpoint[i].ep_st; 791 memset(ep_st, 0, sizeof(*ep_st)); 792 } 793 } 794 795 return count; 796} 797 798static const struct file_operations fops_endpoint_stats = { 799 .open = simple_open, 800 .read = ath6kl_endpoint_stats_read, 801 .write = ath6kl_endpoint_stats_write, 802 .owner = THIS_MODULE, 803 .llseek = default_llseek, 804}; 805 806static unsigned long ath6kl_get_num_reg(void) 807{ 808 int i; 809 unsigned long n_reg = 0; 810 811 for (i = 0; i < ARRAY_SIZE(diag_reg); i++) 812 n_reg = n_reg + 813 (diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1; 814 815 return n_reg; 816} 817 818static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr) 819{ 820 int i; 821 822 for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { 823 if (reg_addr >= diag_reg[i].reg_start && 824 reg_addr <= diag_reg[i].reg_end) 825 return true; 826 } 827 828 return false; 829} 830 831static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf, 832 size_t count, loff_t *ppos) 833{ 834 struct ath6kl *ar = file->private_data; 835 u8 buf[50]; 836 unsigned int len = 0; 837 838 if (ar->debug.dbgfs_diag_reg) 839 len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", 840 ar->debug.dbgfs_diag_reg); 841 else 842 len += scnprintf(buf + len, sizeof(buf) - len, 843 "All diag registers\n"); 844 845 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 846} 847 848static ssize_t ath6kl_regread_write(struct file *file, 849 const char __user *user_buf, 850 size_t count, loff_t *ppos) 851{ 852 struct ath6kl *ar = file->private_data; 853 unsigned long reg_addr; 854 855 if (kstrtoul_from_user(user_buf, count, 0, ®_addr)) 856 return -EINVAL; 857 858 if ((reg_addr % 4) != 0) 859 return -EINVAL; 860 861 if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr)) 862 return -EINVAL; 863 864 ar->debug.dbgfs_diag_reg = reg_addr; 865 866 return count; 867} 868 869static const struct file_operations fops_diag_reg_read = { 870 .read = ath6kl_regread_read, 871 .write = ath6kl_regread_write, 872 .open = simple_open, 873 .owner = THIS_MODULE, 874 .llseek = default_llseek, 875}; 876 877static int ath6kl_regdump_open(struct inode *inode, struct file *file) 878{ 879 struct ath6kl *ar = inode->i_private; 880 u8 *buf; 881 unsigned long int reg_len; 882 unsigned int len = 0, n_reg; 883 u32 addr; 884 __le32 reg_val; 885 int i, status; 886 887 /* Dump all the registers if no register is specified */ 888 if (!ar->debug.dbgfs_diag_reg) 889 n_reg = ath6kl_get_num_reg(); 890 else 891 n_reg = 1; 892 893 reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE; 894 if (n_reg > 1) 895 reg_len += REGTYPE_STR_LEN; 896 897 buf = vmalloc(reg_len); 898 if (!buf) 899 return -ENOMEM; 900 901 if (n_reg == 1) { 902 addr = ar->debug.dbgfs_diag_reg; 903 904 status = ath6kl_diag_read32(ar, 905 TARG_VTOP(ar->target_type, addr), 906 (u32 *)®_val); 907 if (status) 908 goto fail_reg_read; 909 910 len += scnprintf(buf + len, reg_len - len, 911 "0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val)); 912 goto done; 913 } 914 915 for (i = 0; i < ARRAY_SIZE(diag_reg); i++) { 916 len += scnprintf(buf + len, reg_len - len, 917 "%s\n", diag_reg[i].reg_info); 918 for (addr = diag_reg[i].reg_start; 919 addr <= diag_reg[i].reg_end; addr += 4) { 920 status = ath6kl_diag_read32(ar, 921 TARG_VTOP(ar->target_type, addr), 922 (u32 *)®_val); 923 if (status) 924 goto fail_reg_read; 925 926 len += scnprintf(buf + len, reg_len - len, 927 "0x%06x 0x%08x\n", 928 addr, le32_to_cpu(reg_val)); 929 } 930 } 931 932done: 933 file->private_data = buf; 934 return 0; 935 936fail_reg_read: 937 ath6kl_warn("Unable to read memory:%u\n", addr); 938 vfree(buf); 939 return -EIO; 940} 941 942static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf, 943 size_t count, loff_t *ppos) 944{ 945 u8 *buf = file->private_data; 946 return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); 947} 948 949static int ath6kl_regdump_release(struct inode *inode, struct file *file) 950{ 951 vfree(file->private_data); 952 return 0; 953} 954 955static const struct file_operations fops_reg_dump = { 956 .open = ath6kl_regdump_open, 957 .read = ath6kl_regdump_read, 958 .release = ath6kl_regdump_release, 959 .owner = THIS_MODULE, 960 .llseek = default_llseek, 961}; 962 963static ssize_t ath6kl_lrssi_roam_write(struct file *file, 964 const char __user *user_buf, 965 size_t count, loff_t *ppos) 966{ 967 struct ath6kl *ar = file->private_data; 968 unsigned long lrssi_roam_threshold; 969 970 if (kstrtoul_from_user(user_buf, count, 0, &lrssi_roam_threshold)) 971 return -EINVAL; 972 973 ar->lrssi_roam_threshold = lrssi_roam_threshold; 974 975 ath6kl_wmi_set_roam_lrssi_cmd(ar->wmi, ar->lrssi_roam_threshold); 976 977 return count; 978} 979 980static ssize_t ath6kl_lrssi_roam_read(struct file *file, 981 char __user *user_buf, 982 size_t count, loff_t *ppos) 983{ 984 struct ath6kl *ar = file->private_data; 985 char buf[32]; 986 unsigned int len; 987 988 len = snprintf(buf, sizeof(buf), "%u\n", ar->lrssi_roam_threshold); 989 990 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 991} 992 993static const struct file_operations fops_lrssi_roam_threshold = { 994 .read = ath6kl_lrssi_roam_read, 995 .write = ath6kl_lrssi_roam_write, 996 .open = simple_open, 997 .owner = THIS_MODULE, 998 .llseek = default_llseek, 999}; 1000 1001static ssize_t ath6kl_regwrite_read(struct file *file, 1002 char __user *user_buf, 1003 size_t count, loff_t *ppos) 1004{ 1005 struct ath6kl *ar = file->private_data; 1006 u8 buf[32]; 1007 unsigned int len = 0; 1008 1009 len = scnprintf(buf, sizeof(buf), "Addr: 0x%x Val: 0x%x\n", 1010 ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr); 1011 1012 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 1013} 1014 1015static ssize_t ath6kl_regwrite_write(struct file *file, 1016 const char __user *user_buf, 1017 size_t count, loff_t *ppos) 1018{ 1019 struct ath6kl *ar = file->private_data; 1020 char buf[32]; 1021 char *sptr, *token; 1022 unsigned int len = 0; 1023 u32 reg_addr, reg_val; 1024 1025 len = min(count, sizeof(buf) - 1); 1026 if (copy_from_user(buf, user_buf, len)) 1027 return -EFAULT; 1028 1029 buf[len] = '\0'; 1030 sptr = buf; 1031 1032 token = strsep(&sptr, "="); 1033 if (!token) 1034 return -EINVAL; 1035 1036 if (kstrtou32(token, 0, ®_addr)) 1037 return -EINVAL; 1038 1039 if (!ath6kl_dbg_is_diag_reg_valid(reg_addr)) 1040 return -EINVAL; 1041 1042 if (kstrtou32(sptr, 0, ®_val)) 1043 return -EINVAL; 1044 1045 ar->debug.diag_reg_addr_wr = reg_addr; 1046 ar->debug.diag_reg_val_wr = reg_val; 1047 1048 if (ath6kl_diag_write32(ar, ar->debug.diag_reg_addr_wr, 1049 cpu_to_le32(ar->debug.diag_reg_val_wr))) 1050 return -EIO; 1051 1052 return count; 1053} 1054 1055static const struct file_operations fops_diag_reg_write = { 1056 .read = ath6kl_regwrite_read, 1057 .write = ath6kl_regwrite_write, 1058 .open = simple_open, 1059 .owner = THIS_MODULE, 1060 .llseek = default_llseek, 1061}; 1062 1063int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf, 1064 size_t len) 1065{ 1066 const struct wmi_target_roam_tbl *tbl; 1067 u16 num_entries; 1068 1069 if (len < sizeof(*tbl)) 1070 return -EINVAL; 1071 1072 tbl = (const struct wmi_target_roam_tbl *) buf; 1073 num_entries = le16_to_cpu(tbl->num_entries); 1074 if (sizeof(*tbl) + num_entries * sizeof(struct wmi_bss_roam_info) > 1075 len) 1076 return -EINVAL; 1077 1078 if (ar->debug.roam_tbl == NULL || 1079 ar->debug.roam_tbl_len < (unsigned int) len) { 1080 kfree(ar->debug.roam_tbl); 1081 ar->debug.roam_tbl = kmalloc(len, GFP_ATOMIC); 1082 if (ar->debug.roam_tbl == NULL) 1083 return -ENOMEM; 1084 } 1085 1086 memcpy(ar->debug.roam_tbl, buf, len); 1087 ar->debug.roam_tbl_len = len; 1088 1089 if (test_bit(ROAM_TBL_PEND, &ar->flag)) { 1090 clear_bit(ROAM_TBL_PEND, &ar->flag); 1091 wake_up(&ar->event_wq); 1092 } 1093 1094 return 0; 1095} 1096 1097static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf, 1098 size_t count, loff_t *ppos) 1099{ 1100 struct ath6kl *ar = file->private_data; 1101 int ret; 1102 long left; 1103 struct wmi_target_roam_tbl *tbl; 1104 u16 num_entries, i; 1105 char *buf; 1106 unsigned int len, buf_len; 1107 ssize_t ret_cnt; 1108 1109 if (down_interruptible(&ar->sem)) 1110 return -EBUSY; 1111 1112 set_bit(ROAM_TBL_PEND, &ar->flag); 1113 1114 ret = ath6kl_wmi_get_roam_tbl_cmd(ar->wmi); 1115 if (ret) { 1116 up(&ar->sem); 1117 return ret; 1118 } 1119 1120 left = wait_event_interruptible_timeout( 1121 ar->event_wq, !test_bit(ROAM_TBL_PEND, &ar->flag), WMI_TIMEOUT); 1122 up(&ar->sem); 1123 1124 if (left <= 0) 1125 return -ETIMEDOUT; 1126 1127 if (ar->debug.roam_tbl == NULL) 1128 return -ENOMEM; 1129 1130 tbl = (struct wmi_target_roam_tbl *) ar->debug.roam_tbl; 1131 num_entries = le16_to_cpu(tbl->num_entries); 1132 1133 buf_len = 100 + num_entries * 100; 1134 buf = kzalloc(buf_len, GFP_KERNEL); 1135 if (buf == NULL) 1136 return -ENOMEM; 1137 len = 0; 1138 len += scnprintf(buf + len, buf_len - len, 1139 "roam_mode=%u\n\n" 1140 "# roam_util bssid rssi rssidt last_rssi util bias\n", 1141 le16_to_cpu(tbl->roam_mode)); 1142 1143 for (i = 0; i < num_entries; i++) { 1144 struct wmi_bss_roam_info *info = &tbl->info[i]; 1145 len += scnprintf(buf + len, buf_len - len, 1146 "%d %pM %d %d %d %d %d\n", 1147 a_sle32_to_cpu(info->roam_util), info->bssid, 1148 info->rssi, info->rssidt, info->last_rssi, 1149 info->util, info->bias); 1150 } 1151 1152 if (len > buf_len) 1153 len = buf_len; 1154 1155 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); 1156 1157 kfree(buf); 1158 return ret_cnt; 1159} 1160 1161static const struct file_operations fops_roam_table = { 1162 .read = ath6kl_roam_table_read, 1163 .open = simple_open, 1164 .owner = THIS_MODULE, 1165 .llseek = default_llseek, 1166}; 1167 1168static ssize_t ath6kl_force_roam_write(struct file *file, 1169 const char __user *user_buf, 1170 size_t count, loff_t *ppos) 1171{ 1172 struct ath6kl *ar = file->private_data; 1173 int ret; 1174 char buf[20]; 1175 size_t len; 1176 u8 bssid[ETH_ALEN]; 1177 int i; 1178 int addr[ETH_ALEN]; 1179 1180 len = min(count, sizeof(buf) - 1); 1181 if (copy_from_user(buf, user_buf, len)) 1182 return -EFAULT; 1183 buf[len] = '\0'; 1184 1185 if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 1186 &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) 1187 != ETH_ALEN) 1188 return -EINVAL; 1189 for (i = 0; i < ETH_ALEN; i++) 1190 bssid[i] = addr[i]; 1191 1192 ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid); 1193 if (ret) 1194 return ret; 1195 1196 return count; 1197} 1198 1199static const struct file_operations fops_force_roam = { 1200 .write = ath6kl_force_roam_write, 1201 .open = simple_open, 1202 .owner = THIS_MODULE, 1203 .llseek = default_llseek, 1204}; 1205 1206static ssize_t ath6kl_roam_mode_write(struct file *file, 1207 const char __user *user_buf, 1208 size_t count, loff_t *ppos) 1209{ 1210 struct ath6kl *ar = file->private_data; 1211 int ret; 1212 char buf[20]; 1213 size_t len; 1214 enum wmi_roam_mode mode; 1215 1216 len = min(count, sizeof(buf) - 1); 1217 if (copy_from_user(buf, user_buf, len)) 1218 return -EFAULT; 1219 buf[len] = '\0'; 1220 if (len > 0 && buf[len - 1] == '\n') 1221 buf[len - 1] = '\0'; 1222 1223 if (strcasecmp(buf, "default") == 0) 1224 mode = WMI_DEFAULT_ROAM_MODE; 1225 else if (strcasecmp(buf, "bssbias") == 0) 1226 mode = WMI_HOST_BIAS_ROAM_MODE; 1227 else if (strcasecmp(buf, "lock") == 0) 1228 mode = WMI_LOCK_BSS_MODE; 1229 else 1230 return -EINVAL; 1231 1232 ret = ath6kl_wmi_set_roam_mode_cmd(ar->wmi, mode); 1233 if (ret) 1234 return ret; 1235 1236 return count; 1237} 1238 1239static const struct file_operations fops_roam_mode = { 1240 .write = ath6kl_roam_mode_write, 1241 .open = simple_open, 1242 .owner = THIS_MODULE, 1243 .llseek = default_llseek, 1244}; 1245 1246void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive) 1247{ 1248 ar->debug.keepalive = keepalive; 1249} 1250 1251static ssize_t ath6kl_keepalive_read(struct file *file, char __user *user_buf, 1252 size_t count, loff_t *ppos) 1253{ 1254 struct ath6kl *ar = file->private_data; 1255 char buf[16]; 1256 int len; 1257 1258 len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.keepalive); 1259 1260 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 1261} 1262 1263static ssize_t ath6kl_keepalive_write(struct file *file, 1264 const char __user *user_buf, 1265 size_t count, loff_t *ppos) 1266{ 1267 struct ath6kl *ar = file->private_data; 1268 int ret; 1269 u8 val; 1270 1271 ret = kstrtou8_from_user(user_buf, count, 0, &val); 1272 if (ret) 1273 return ret; 1274 1275 ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, 0, val); 1276 if (ret) 1277 return ret; 1278 1279 return count; 1280} 1281 1282static const struct file_operations fops_keepalive = { 1283 .open = simple_open, 1284 .read = ath6kl_keepalive_read, 1285 .write = ath6kl_keepalive_write, 1286 .owner = THIS_MODULE, 1287 .llseek = default_llseek, 1288}; 1289 1290void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout) 1291{ 1292 ar->debug.disc_timeout = timeout; 1293} 1294 1295static ssize_t ath6kl_disconnect_timeout_read(struct file *file, 1296 char __user *user_buf, 1297 size_t count, loff_t *ppos) 1298{ 1299 struct ath6kl *ar = file->private_data; 1300 char buf[16]; 1301 int len; 1302 1303 len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.disc_timeout); 1304 1305 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 1306} 1307 1308static ssize_t ath6kl_disconnect_timeout_write(struct file *file, 1309 const char __user *user_buf, 1310 size_t count, loff_t *ppos) 1311{ 1312 struct ath6kl *ar = file->private_data; 1313 int ret; 1314 u8 val; 1315 1316 ret = kstrtou8_from_user(user_buf, count, 0, &val); 1317 if (ret) 1318 return ret; 1319 1320 ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, 0, val); 1321 if (ret) 1322 return ret; 1323 1324 return count; 1325} 1326 1327static const struct file_operations fops_disconnect_timeout = { 1328 .open = simple_open, 1329 .read = ath6kl_disconnect_timeout_read, 1330 .write = ath6kl_disconnect_timeout_write, 1331 .owner = THIS_MODULE, 1332 .llseek = default_llseek, 1333}; 1334 1335static ssize_t ath6kl_create_qos_write(struct file *file, 1336 const char __user *user_buf, 1337 size_t count, loff_t *ppos) 1338{ 1339 1340 struct ath6kl *ar = file->private_data; 1341 struct ath6kl_vif *vif; 1342 char buf[200]; 1343 ssize_t len; 1344 char *sptr, *token; 1345 struct wmi_create_pstream_cmd pstream; 1346 u32 val32; 1347 u16 val16; 1348 1349 vif = ath6kl_vif_first(ar); 1350 if (!vif) 1351 return -EIO; 1352 1353 len = min(count, sizeof(buf) - 1); 1354 if (copy_from_user(buf, user_buf, len)) 1355 return -EFAULT; 1356 buf[len] = '\0'; 1357 sptr = buf; 1358 1359 token = strsep(&sptr, " "); 1360 if (!token) 1361 return -EINVAL; 1362 if (kstrtou8(token, 0, &pstream.user_pri)) 1363 return -EINVAL; 1364 1365 token = strsep(&sptr, " "); 1366 if (!token) 1367 return -EINVAL; 1368 if (kstrtou8(token, 0, &pstream.traffic_direc)) 1369 return -EINVAL; 1370 1371 token = strsep(&sptr, " "); 1372 if (!token) 1373 return -EINVAL; 1374 if (kstrtou8(token, 0, &pstream.traffic_class)) 1375 return -EINVAL; 1376 1377 token = strsep(&sptr, " "); 1378 if (!token) 1379 return -EINVAL; 1380 if (kstrtou8(token, 0, &pstream.traffic_type)) 1381 return -EINVAL; 1382 1383 token = strsep(&sptr, " "); 1384 if (!token) 1385 return -EINVAL; 1386 if (kstrtou8(token, 0, &pstream.voice_psc_cap)) 1387 return -EINVAL; 1388 1389 token = strsep(&sptr, " "); 1390 if (!token) 1391 return -EINVAL; 1392 if (kstrtou32(token, 0, &val32)) 1393 return -EINVAL; 1394 pstream.min_service_int = cpu_to_le32(val32); 1395 1396 token = strsep(&sptr, " "); 1397 if (!token) 1398 return -EINVAL; 1399 if (kstrtou32(token, 0, &val32)) 1400 return -EINVAL; 1401 pstream.max_service_int = cpu_to_le32(val32); 1402 1403 token = strsep(&sptr, " "); 1404 if (!token) 1405 return -EINVAL; 1406 if (kstrtou32(token, 0, &val32)) 1407 return -EINVAL; 1408 pstream.inactivity_int = cpu_to_le32(val32); 1409 1410 token = strsep(&sptr, " "); 1411 if (!token) 1412 return -EINVAL; 1413 if (kstrtou32(token, 0, &val32)) 1414 return -EINVAL; 1415 pstream.suspension_int = cpu_to_le32(val32); 1416 1417 token = strsep(&sptr, " "); 1418 if (!token) 1419 return -EINVAL; 1420 if (kstrtou32(token, 0, &val32)) 1421 return -EINVAL; 1422 pstream.service_start_time = cpu_to_le32(val32); 1423 1424 token = strsep(&sptr, " "); 1425 if (!token) 1426 return -EINVAL; 1427 if (kstrtou8(token, 0, &pstream.tsid)) 1428 return -EINVAL; 1429 1430 token = strsep(&sptr, " "); 1431 if (!token) 1432 return -EINVAL; 1433 if (kstrtou16(token, 0, &val16)) 1434 return -EINVAL; 1435 pstream.nominal_msdu = cpu_to_le16(val16); 1436 1437 token = strsep(&sptr, " "); 1438 if (!token) 1439 return -EINVAL; 1440 if (kstrtou16(token, 0, &val16)) 1441 return -EINVAL; 1442 pstream.max_msdu = cpu_to_le16(val16); 1443 1444 token = strsep(&sptr, " "); 1445 if (!token) 1446 return -EINVAL; 1447 if (kstrtou32(token, 0, &val32)) 1448 return -EINVAL; 1449 pstream.min_data_rate = cpu_to_le32(val32); 1450 1451 token = strsep(&sptr, " "); 1452 if (!token) 1453 return -EINVAL; 1454 if (kstrtou32(token, 0, &val32)) 1455 return -EINVAL; 1456 pstream.mean_data_rate = cpu_to_le32(val32); 1457 1458 token = strsep(&sptr, " "); 1459 if (!token) 1460 return -EINVAL; 1461 if (kstrtou32(token, 0, &val32)) 1462 return -EINVAL; 1463 pstream.peak_data_rate = cpu_to_le32(val32); 1464 1465 token = strsep(&sptr, " "); 1466 if (!token) 1467 return -EINVAL; 1468 if (kstrtou32(token, 0, &val32)) 1469 return -EINVAL; 1470 pstream.max_burst_size = cpu_to_le32(val32); 1471 1472 token = strsep(&sptr, " "); 1473 if (!token) 1474 return -EINVAL; 1475 if (kstrtou32(token, 0, &val32)) 1476 return -EINVAL; 1477 pstream.delay_bound = cpu_to_le32(val32); 1478 1479 token = strsep(&sptr, " "); 1480 if (!token) 1481 return -EINVAL; 1482 if (kstrtou32(token, 0, &val32)) 1483 return -EINVAL; 1484 pstream.min_phy_rate = cpu_to_le32(val32); 1485 1486 token = strsep(&sptr, " "); 1487 if (!token) 1488 return -EINVAL; 1489 if (kstrtou32(token, 0, &val32)) 1490 return -EINVAL; 1491 pstream.sba = cpu_to_le32(val32); 1492 1493 token = strsep(&sptr, " "); 1494 if (!token) 1495 return -EINVAL; 1496 if (kstrtou32(token, 0, &val32)) 1497 return -EINVAL; 1498 pstream.medium_time = cpu_to_le32(val32); 1499 1500 pstream.nominal_phy = le32_to_cpu(pstream.min_phy_rate) / 1000000; 1501 1502 ath6kl_wmi_create_pstream_cmd(ar->wmi, vif->fw_vif_idx, &pstream); 1503 1504 return count; 1505} 1506 1507static const struct file_operations fops_create_qos = { 1508 .write = ath6kl_create_qos_write, 1509 .open = simple_open, 1510 .owner = THIS_MODULE, 1511 .llseek = default_llseek, 1512}; 1513 1514static ssize_t ath6kl_delete_qos_write(struct file *file, 1515 const char __user *user_buf, 1516 size_t count, loff_t *ppos) 1517{ 1518 1519 struct ath6kl *ar = file->private_data; 1520 struct ath6kl_vif *vif; 1521 char buf[100]; 1522 ssize_t len; 1523 char *sptr, *token; 1524 u8 traffic_class; 1525 u8 tsid; 1526 1527 vif = ath6kl_vif_first(ar); 1528 if (!vif) 1529 return -EIO; 1530 1531 len = min(count, sizeof(buf) - 1); 1532 if (copy_from_user(buf, user_buf, len)) 1533 return -EFAULT; 1534 buf[len] = '\0'; 1535 sptr = buf; 1536 1537 token = strsep(&sptr, " "); 1538 if (!token) 1539 return -EINVAL; 1540 if (kstrtou8(token, 0, &traffic_class)) 1541 return -EINVAL; 1542 1543 token = strsep(&sptr, " "); 1544 if (!token) 1545 return -EINVAL; 1546 if (kstrtou8(token, 0, &tsid)) 1547 return -EINVAL; 1548 1549 ath6kl_wmi_delete_pstream_cmd(ar->wmi, vif->fw_vif_idx, 1550 traffic_class, tsid); 1551 1552 return count; 1553} 1554 1555static const struct file_operations fops_delete_qos = { 1556 .write = ath6kl_delete_qos_write, 1557 .open = simple_open, 1558 .owner = THIS_MODULE, 1559 .llseek = default_llseek, 1560}; 1561 1562static ssize_t ath6kl_bgscan_int_write(struct file *file, 1563 const char __user *user_buf, 1564 size_t count, loff_t *ppos) 1565{ 1566 struct ath6kl *ar = file->private_data; 1567 u16 bgscan_int; 1568 char buf[32]; 1569 ssize_t len; 1570 1571 len = min(count, sizeof(buf) - 1); 1572 if (copy_from_user(buf, user_buf, len)) 1573 return -EFAULT; 1574 1575 buf[len] = '\0'; 1576 if (kstrtou16(buf, 0, &bgscan_int)) 1577 return -EINVAL; 1578 1579 if (bgscan_int == 0) 1580 bgscan_int = 0xffff; 1581 1582 ath6kl_wmi_scanparams_cmd(ar->wmi, 0, 0, 0, bgscan_int, 0, 0, 0, 3, 1583 0, 0, 0); 1584 1585 return count; 1586} 1587 1588static const struct file_operations fops_bgscan_int = { 1589 .write = ath6kl_bgscan_int_write, 1590 .open = simple_open, 1591 .owner = THIS_MODULE, 1592 .llseek = default_llseek, 1593}; 1594 1595static ssize_t ath6kl_listen_int_write(struct file *file, 1596 const char __user *user_buf, 1597 size_t count, loff_t *ppos) 1598{ 1599 struct ath6kl *ar = file->private_data; 1600 struct ath6kl_vif *vif; 1601 u16 listen_interval; 1602 char buf[32]; 1603 ssize_t len; 1604 1605 vif = ath6kl_vif_first(ar); 1606 if (!vif) 1607 return -EIO; 1608 1609 len = min(count, sizeof(buf) - 1); 1610 if (copy_from_user(buf, user_buf, len)) 1611 return -EFAULT; 1612 1613 buf[len] = '\0'; 1614 if (kstrtou16(buf, 0, &listen_interval)) 1615 return -EINVAL; 1616 1617 if ((listen_interval < 15) || (listen_interval > 3000)) 1618 return -EINVAL; 1619 1620 vif->listen_intvl_t = listen_interval; 1621 ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, 1622 vif->listen_intvl_t, 0); 1623 1624 return count; 1625} 1626 1627static ssize_t ath6kl_listen_int_read(struct file *file, 1628 char __user *user_buf, 1629 size_t count, loff_t *ppos) 1630{ 1631 struct ath6kl *ar = file->private_data; 1632 struct ath6kl_vif *vif; 1633 char buf[32]; 1634 int len; 1635 1636 vif = ath6kl_vif_first(ar); 1637 if (!vif) 1638 return -EIO; 1639 1640 len = scnprintf(buf, sizeof(buf), "%u\n", vif->listen_intvl_t); 1641 1642 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 1643} 1644 1645static const struct file_operations fops_listen_int = { 1646 .read = ath6kl_listen_int_read, 1647 .write = ath6kl_listen_int_write, 1648 .open = simple_open, 1649 .owner = THIS_MODULE, 1650 .llseek = default_llseek, 1651}; 1652 1653static ssize_t ath6kl_power_params_write(struct file *file, 1654 const char __user *user_buf, 1655 size_t count, loff_t *ppos) 1656{ 1657 struct ath6kl *ar = file->private_data; 1658 u8 buf[100]; 1659 unsigned int len = 0; 1660 char *sptr, *token; 1661 u16 idle_period, ps_poll_num, dtim, 1662 tx_wakeup, num_tx; 1663 1664 len = min(count, sizeof(buf) - 1); 1665 if (copy_from_user(buf, user_buf, len)) 1666 return -EFAULT; 1667 buf[len] = '\0'; 1668 sptr = buf; 1669 1670 token = strsep(&sptr, " "); 1671 if (!token) 1672 return -EINVAL; 1673 if (kstrtou16(token, 0, &idle_period)) 1674 return -EINVAL; 1675 1676 token = strsep(&sptr, " "); 1677 if (!token) 1678 return -EINVAL; 1679 if (kstrtou16(token, 0, &ps_poll_num)) 1680 return -EINVAL; 1681 1682 token = strsep(&sptr, " "); 1683 if (!token) 1684 return -EINVAL; 1685 if (kstrtou16(token, 0, &dtim)) 1686 return -EINVAL; 1687 1688 token = strsep(&sptr, " "); 1689 if (!token) 1690 return -EINVAL; 1691 if (kstrtou16(token, 0, &tx_wakeup)) 1692 return -EINVAL; 1693 1694 token = strsep(&sptr, " "); 1695 if (!token) 1696 return -EINVAL; 1697 if (kstrtou16(token, 0, &num_tx)) 1698 return -EINVAL; 1699 1700 ath6kl_wmi_pmparams_cmd(ar->wmi, 0, idle_period, ps_poll_num, 1701 dtim, tx_wakeup, num_tx, 0); 1702 1703 return count; 1704} 1705 1706static const struct file_operations fops_power_params = { 1707 .write = ath6kl_power_params_write, 1708 .open = simple_open, 1709 .owner = THIS_MODULE, 1710 .llseek = default_llseek, 1711}; 1712 1713void ath6kl_debug_init(struct ath6kl *ar) 1714{ 1715 skb_queue_head_init(&ar->debug.fwlog_queue); 1716 init_completion(&ar->debug.fwlog_completion); 1717 1718 /* 1719 * Actually we are lying here but don't know how to read the mask 1720 * value from the firmware. 1721 */ 1722 ar->debug.fwlog_mask = 0; 1723} 1724 1725/* 1726 * Initialisation needs to happen in two stages as fwlog events can come 1727 * before cfg80211 is initialised, and debugfs depends on cfg80211 1728 * initialisation. 1729 */ 1730int ath6kl_debug_init_fs(struct ath6kl *ar) 1731{ 1732 ar->debugfs_phy = debugfs_create_dir("ath6kl", 1733 ar->wiphy->debugfsdir); 1734 if (!ar->debugfs_phy) 1735 return -ENOMEM; 1736 1737 debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar, 1738 &fops_tgt_stats); 1739 1740 debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar, 1741 &fops_credit_dist_stats); 1742 1743 debugfs_create_file("endpoint_stats", S_IRUSR | S_IWUSR, 1744 ar->debugfs_phy, ar, &fops_endpoint_stats); 1745 1746 debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar, 1747 &fops_fwlog); 1748 1749 debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar, 1750 &fops_fwlog_block); 1751 1752 debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy, 1753 ar, &fops_fwlog_mask); 1754 1755 debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, 1756 &fops_diag_reg_read); 1757 1758 debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar, 1759 &fops_reg_dump); 1760 1761 debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR, 1762 ar->debugfs_phy, ar, &fops_lrssi_roam_threshold); 1763 1764 debugfs_create_file("reg_write", S_IRUSR | S_IWUSR, 1765 ar->debugfs_phy, ar, &fops_diag_reg_write); 1766 1767 debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar, 1768 &fops_war_stats); 1769 1770 debugfs_create_file("roam_table", S_IRUSR, ar->debugfs_phy, ar, 1771 &fops_roam_table); 1772 1773 debugfs_create_file("force_roam", S_IWUSR, ar->debugfs_phy, ar, 1774 &fops_force_roam); 1775 1776 debugfs_create_file("roam_mode", S_IWUSR, ar->debugfs_phy, ar, 1777 &fops_roam_mode); 1778 1779 debugfs_create_file("keepalive", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar, 1780 &fops_keepalive); 1781 1782 debugfs_create_file("disconnect_timeout", S_IRUSR | S_IWUSR, 1783 ar->debugfs_phy, ar, &fops_disconnect_timeout); 1784 1785 debugfs_create_file("create_qos", S_IWUSR, ar->debugfs_phy, ar, 1786 &fops_create_qos); 1787 1788 debugfs_create_file("delete_qos", S_IWUSR, ar->debugfs_phy, ar, 1789 &fops_delete_qos); 1790 1791 debugfs_create_file("bgscan_interval", S_IWUSR, 1792 ar->debugfs_phy, ar, &fops_bgscan_int); 1793 1794 debugfs_create_file("listen_interval", S_IRUSR | S_IWUSR, 1795 ar->debugfs_phy, ar, &fops_listen_int); 1796 1797 debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar, 1798 &fops_power_params); 1799 1800 return 0; 1801} 1802 1803void ath6kl_debug_cleanup(struct ath6kl *ar) 1804{ 1805 skb_queue_purge(&ar->debug.fwlog_queue); 1806 kfree(ar->debug.roam_tbl); 1807} 1808 1809#endif 1810