linux/drivers/net/wireless/ath/ath6kl/debug.c
<<
>>
Prefs
   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                        vfree(buf);
 406                        return ret;
 407                }
 408
 409                spin_lock(&ar->debug.fwlog_queue.lock);
 410        }
 411
 412        while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) {
 413                if (skb->len > count - len) {
 414                        /* not enough space, put skb back and leave */
 415                        __skb_queue_head(&ar->debug.fwlog_queue, skb);
 416                        break;
 417                }
 418
 419
 420                memcpy(buf + len, skb->data, skb->len);
 421                len += skb->len;
 422
 423                kfree_skb(skb);
 424        }
 425
 426        spin_unlock(&ar->debug.fwlog_queue.lock);
 427
 428        /* FIXME: what to do if len == 0? */
 429
 430        not_copied = copy_to_user(user_buf, buf, len);
 431        if (not_copied != 0) {
 432                ret_cnt = -EFAULT;
 433                goto out;
 434        }
 435
 436        *ppos = *ppos + len;
 437
 438        ret_cnt = len;
 439
 440out:
 441        vfree(buf);
 442
 443        return ret_cnt;
 444}
 445
 446static const struct file_operations fops_fwlog_block = {
 447        .open = ath6kl_fwlog_open,
 448        .release = ath6kl_fwlog_release,
 449        .read = ath6kl_fwlog_block_read,
 450        .owner = THIS_MODULE,
 451        .llseek = default_llseek,
 452};
 453
 454static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf,
 455                                      size_t count, loff_t *ppos)
 456{
 457        struct ath6kl *ar = file->private_data;
 458        char buf[16];
 459        int len;
 460
 461        len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask);
 462
 463        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 464}
 465
 466static ssize_t ath6kl_fwlog_mask_write(struct file *file,
 467                                       const char __user *user_buf,
 468                                       size_t count, loff_t *ppos)
 469{
 470        struct ath6kl *ar = file->private_data;
 471        int ret;
 472
 473        ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask);
 474        if (ret)
 475                return ret;
 476
 477        ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi,
 478                                                 ATH6KL_FWLOG_VALID_MASK,
 479                                                 ar->debug.fwlog_mask);
 480        if (ret)
 481                return ret;
 482
 483        return count;
 484}
 485
 486static const struct file_operations fops_fwlog_mask = {
 487        .open = simple_open,
 488        .read = ath6kl_fwlog_mask_read,
 489        .write = ath6kl_fwlog_mask_write,
 490        .owner = THIS_MODULE,
 491        .llseek = default_llseek,
 492};
 493
 494static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
 495                                   size_t count, loff_t *ppos)
 496{
 497        struct ath6kl *ar = file->private_data;
 498        struct ath6kl_vif *vif;
 499        struct target_stats *tgt_stats;
 500        char *buf;
 501        unsigned int len = 0, buf_len = 1500;
 502        int i;
 503        long left;
 504        ssize_t ret_cnt;
 505
 506        vif = ath6kl_vif_first(ar);
 507        if (!vif)
 508                return -EIO;
 509
 510        tgt_stats = &vif->target_stats;
 511
 512        buf = kzalloc(buf_len, GFP_KERNEL);
 513        if (!buf)
 514                return -ENOMEM;
 515
 516        if (down_interruptible(&ar->sem)) {
 517                kfree(buf);
 518                return -EBUSY;
 519        }
 520
 521        set_bit(STATS_UPDATE_PEND, &vif->flags);
 522
 523        if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) {
 524                up(&ar->sem);
 525                kfree(buf);
 526                return -EIO;
 527        }
 528
 529        left = wait_event_interruptible_timeout(ar->event_wq,
 530                                                !test_bit(STATS_UPDATE_PEND,
 531                                                &vif->flags), WMI_TIMEOUT);
 532
 533        up(&ar->sem);
 534
 535        if (left <= 0) {
 536                kfree(buf);
 537                return -ETIMEDOUT;
 538        }
 539
 540        len += scnprintf(buf + len, buf_len - len, "\n");
 541        len += scnprintf(buf + len, buf_len - len, "%25s\n",
 542                         "Target Tx stats");
 543        len += scnprintf(buf + len, buf_len - len, "%25s\n\n",
 544                         "=================");
 545        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 546                         "Ucast packets", tgt_stats->tx_ucast_pkt);
 547        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 548                         "Bcast packets", tgt_stats->tx_bcast_pkt);
 549        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 550                         "Ucast byte", tgt_stats->tx_ucast_byte);
 551        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 552                         "Bcast byte", tgt_stats->tx_bcast_byte);
 553        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 554                         "Rts success cnt", tgt_stats->tx_rts_success_cnt);
 555        for (i = 0; i < 4; i++)
 556                len += scnprintf(buf + len, buf_len - len,
 557                                 "%18s %d %10llu\n", "PER on ac",
 558                                 i, tgt_stats->tx_pkt_per_ac[i]);
 559        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 560                         "Error", tgt_stats->tx_err);
 561        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 562                         "Fail count", tgt_stats->tx_fail_cnt);
 563        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 564                         "Retry count", tgt_stats->tx_retry_cnt);
 565        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 566                         "Multi retry cnt", tgt_stats->tx_mult_retry_cnt);
 567        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 568                         "Rts fail cnt", tgt_stats->tx_rts_fail_cnt);
 569        len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n\n",
 570                         "TKIP counter measure used",
 571                         tgt_stats->tkip_cnter_measures_invoked);
 572
 573        len += scnprintf(buf + len, buf_len - len, "%25s\n",
 574                         "Target Rx stats");
 575        len += scnprintf(buf + len, buf_len - len, "%25s\n",
 576                         "=================");
 577
 578        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 579                         "Ucast packets", tgt_stats->rx_ucast_pkt);
 580        len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
 581                         "Ucast Rate", tgt_stats->rx_ucast_rate);
 582        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 583                         "Bcast packets", tgt_stats->rx_bcast_pkt);
 584        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 585                         "Ucast byte", tgt_stats->rx_ucast_byte);
 586        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 587                         "Bcast byte", tgt_stats->rx_bcast_byte);
 588        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 589                         "Fragmented pkt", tgt_stats->rx_frgment_pkt);
 590        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 591                         "Error", tgt_stats->rx_err);
 592        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 593                         "CRC Err", tgt_stats->rx_crc_err);
 594        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 595                         "Key chache miss", tgt_stats->rx_key_cache_miss);
 596        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 597                         "Decrypt Err", tgt_stats->rx_decrypt_err);
 598        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 599                         "Duplicate frame", tgt_stats->rx_dupl_frame);
 600        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 601                         "Tkip Mic failure", tgt_stats->tkip_local_mic_fail);
 602        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 603                         "TKIP format err", tgt_stats->tkip_fmt_err);
 604        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 605                         "CCMP format Err", tgt_stats->ccmp_fmt_err);
 606        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n\n",
 607                         "CCMP Replay Err", tgt_stats->ccmp_replays);
 608
 609        len += scnprintf(buf + len, buf_len - len, "%25s\n",
 610                         "Misc Target stats");
 611        len += scnprintf(buf + len, buf_len - len, "%25s\n",
 612                         "=================");
 613        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 614                         "Beacon Miss count", tgt_stats->cs_bmiss_cnt);
 615        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 616                         "Num Connects", tgt_stats->cs_connect_cnt);
 617        len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
 618                         "Num disconnects", tgt_stats->cs_discon_cnt);
 619        len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
 620                         "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi);
 621        len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
 622                         "ARP pkt received", tgt_stats->arp_received);
 623        len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
 624                         "ARP pkt matched", tgt_stats->arp_matched);
 625        len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
 626                         "ARP pkt replied", tgt_stats->arp_replied);
 627
 628        if (len > buf_len)
 629                len = buf_len;
 630
 631        ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 632
 633        kfree(buf);
 634        return ret_cnt;
 635}
 636
 637static const struct file_operations fops_tgt_stats = {
 638        .read = read_file_tgt_stats,
 639        .open = simple_open,
 640        .owner = THIS_MODULE,
 641        .llseek = default_llseek,
 642};
 643
 644#define print_credit_info(fmt_str, ep_list_field)               \
 645        (len += scnprintf(buf + len, buf_len - len, fmt_str,    \
 646                         ep_list->ep_list_field))
 647#define CREDIT_INFO_DISPLAY_STRING_LEN  200
 648#define CREDIT_INFO_LEN 128
 649
 650static ssize_t read_file_credit_dist_stats(struct file *file,
 651                                           char __user *user_buf,
 652                                           size_t count, loff_t *ppos)
 653{
 654        struct ath6kl *ar = file->private_data;
 655        struct htc_target *target = ar->htc_target;
 656        struct htc_endpoint_credit_dist *ep_list;
 657        char *buf;
 658        unsigned int buf_len, len = 0;
 659        ssize_t ret_cnt;
 660
 661        buf_len = CREDIT_INFO_DISPLAY_STRING_LEN +
 662                  get_queue_depth(&target->cred_dist_list) * CREDIT_INFO_LEN;
 663        buf = kzalloc(buf_len, GFP_KERNEL);
 664        if (!buf)
 665                return -ENOMEM;
 666
 667        len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
 668                         "Total Avail Credits: ",
 669                         target->credit_info->total_avail_credits);
 670        len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
 671                         "Free credits :",
 672                         target->credit_info->cur_free_credits);
 673
 674        len += scnprintf(buf + len, buf_len - len,
 675                         " Epid  Flags    Cred_norm  Cred_min  Credits  Cred_assngd"
 676                         "  Seek_cred  Cred_sz  Cred_per_msg  Cred_to_dist"
 677                         "  qdepth\n");
 678
 679        list_for_each_entry(ep_list, &target->cred_dist_list, list) {
 680                print_credit_info("  %2d", endpoint);
 681                print_credit_info("%10x", dist_flags);
 682                print_credit_info("%8d", cred_norm);
 683                print_credit_info("%9d", cred_min);
 684                print_credit_info("%9d", credits);
 685                print_credit_info("%10d", cred_assngd);
 686                print_credit_info("%13d", seek_cred);
 687                print_credit_info("%12d", cred_sz);
 688                print_credit_info("%9d", cred_per_msg);
 689                print_credit_info("%14d", cred_to_dist);
 690                len += scnprintf(buf + len, buf_len - len, "%12d\n",
 691                                 get_queue_depth(&ep_list->htc_ep->txq));
 692        }
 693
 694        if (len > buf_len)
 695                len = buf_len;
 696
 697        ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 698        kfree(buf);
 699        return ret_cnt;
 700}
 701
 702static const struct file_operations fops_credit_dist_stats = {
 703        .read = read_file_credit_dist_stats,
 704        .open = simple_open,
 705        .owner = THIS_MODULE,
 706        .llseek = default_llseek,
 707};
 708
 709static unsigned int print_endpoint_stat(struct htc_target *target, char *buf,
 710                                        unsigned int buf_len, unsigned int len,
 711                                        int offset, const char *name)
 712{
 713        int i;
 714        struct htc_endpoint_stats *ep_st;
 715        u32 *counter;
 716
 717        len += scnprintf(buf + len, buf_len - len, "%s:", name);
 718        for (i = 0; i < ENDPOINT_MAX; i++) {
 719                ep_st = &target->endpoint[i].ep_st;
 720                counter = ((u32 *) ep_st) + (offset / 4);
 721                len += scnprintf(buf + len, buf_len - len, " %u", *counter);
 722        }
 723        len += scnprintf(buf + len, buf_len - len, "\n");
 724
 725        return len;
 726}
 727
 728static ssize_t ath6kl_endpoint_stats_read(struct file *file,
 729                                          char __user *user_buf,
 730                                          size_t count, loff_t *ppos)
 731{
 732        struct ath6kl *ar = file->private_data;
 733        struct htc_target *target = ar->htc_target;
 734        char *buf;
 735        unsigned int buf_len, len = 0;
 736        ssize_t ret_cnt;
 737
 738        buf_len = sizeof(struct htc_endpoint_stats) / sizeof(u32) *
 739                (25 + ENDPOINT_MAX * 11);
 740        buf = kmalloc(buf_len, GFP_KERNEL);
 741        if (!buf)
 742                return -ENOMEM;
 743
 744#define EPSTAT(name)                                                    \
 745        do {                                                            \
 746                len = print_endpoint_stat(target, buf, buf_len, len,    \
 747                                          offsetof(struct htc_endpoint_stats, \
 748                                                   name),               \
 749                                          #name);                       \
 750        } while (0)
 751
 752        EPSTAT(cred_low_indicate);
 753        EPSTAT(tx_issued);
 754        EPSTAT(tx_pkt_bundled);
 755        EPSTAT(tx_bundles);
 756        EPSTAT(tx_dropped);
 757        EPSTAT(tx_cred_rpt);
 758        EPSTAT(cred_rpt_from_rx);
 759        EPSTAT(cred_rpt_from_other);
 760        EPSTAT(cred_rpt_ep0);
 761        EPSTAT(cred_from_rx);
 762        EPSTAT(cred_from_other);
 763        EPSTAT(cred_from_ep0);
 764        EPSTAT(cred_cosumd);
 765        EPSTAT(cred_retnd);
 766        EPSTAT(rx_pkts);
 767        EPSTAT(rx_lkahds);
 768        EPSTAT(rx_bundl);
 769        EPSTAT(rx_bundle_lkahd);
 770        EPSTAT(rx_bundle_from_hdr);
 771        EPSTAT(rx_alloc_thresh_hit);
 772        EPSTAT(rxalloc_thresh_byte);
 773#undef EPSTAT
 774
 775        if (len > buf_len)
 776                len = buf_len;
 777
 778        ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 779        kfree(buf);
 780        return ret_cnt;
 781}
 782
 783static ssize_t ath6kl_endpoint_stats_write(struct file *file,
 784                                           const char __user *user_buf,
 785                                           size_t count, loff_t *ppos)
 786{
 787        struct ath6kl *ar = file->private_data;
 788        struct htc_target *target = ar->htc_target;
 789        int ret, i;
 790        u32 val;
 791        struct htc_endpoint_stats *ep_st;
 792
 793        ret = kstrtou32_from_user(user_buf, count, 0, &val);
 794        if (ret)
 795                return ret;
 796        if (val == 0) {
 797                for (i = 0; i < ENDPOINT_MAX; i++) {
 798                        ep_st = &target->endpoint[i].ep_st;
 799                        memset(ep_st, 0, sizeof(*ep_st));
 800                }
 801        }
 802
 803        return count;
 804}
 805
 806static const struct file_operations fops_endpoint_stats = {
 807        .open = simple_open,
 808        .read = ath6kl_endpoint_stats_read,
 809        .write = ath6kl_endpoint_stats_write,
 810        .owner = THIS_MODULE,
 811        .llseek = default_llseek,
 812};
 813
 814static unsigned long ath6kl_get_num_reg(void)
 815{
 816        int i;
 817        unsigned long n_reg = 0;
 818
 819        for (i = 0; i < ARRAY_SIZE(diag_reg); i++)
 820                n_reg = n_reg +
 821                     (diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1;
 822
 823        return n_reg;
 824}
 825
 826static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr)
 827{
 828        int i;
 829
 830        for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
 831                if (reg_addr >= diag_reg[i].reg_start &&
 832                    reg_addr <= diag_reg[i].reg_end)
 833                        return true;
 834        }
 835
 836        return false;
 837}
 838
 839static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf,
 840                                    size_t count, loff_t *ppos)
 841{
 842        struct ath6kl *ar = file->private_data;
 843        u8 buf[50];
 844        unsigned int len = 0;
 845
 846        if (ar->debug.dbgfs_diag_reg)
 847                len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n",
 848                                ar->debug.dbgfs_diag_reg);
 849        else
 850                len += scnprintf(buf + len, sizeof(buf) - len,
 851                                 "All diag registers\n");
 852
 853        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 854}
 855
 856static ssize_t ath6kl_regread_write(struct file *file,
 857                                    const char __user *user_buf,
 858                                    size_t count, loff_t *ppos)
 859{
 860        struct ath6kl *ar = file->private_data;
 861        unsigned long reg_addr;
 862
 863        if (kstrtoul_from_user(user_buf, count, 0, &reg_addr))
 864                return -EINVAL;
 865
 866        if ((reg_addr % 4) != 0)
 867                return -EINVAL;
 868
 869        if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr))
 870                return -EINVAL;
 871
 872        ar->debug.dbgfs_diag_reg = reg_addr;
 873
 874        return count;
 875}
 876
 877static const struct file_operations fops_diag_reg_read = {
 878        .read = ath6kl_regread_read,
 879        .write = ath6kl_regread_write,
 880        .open = simple_open,
 881        .owner = THIS_MODULE,
 882        .llseek = default_llseek,
 883};
 884
 885static int ath6kl_regdump_open(struct inode *inode, struct file *file)
 886{
 887        struct ath6kl *ar = inode->i_private;
 888        u8 *buf;
 889        unsigned long int reg_len;
 890        unsigned int len = 0, n_reg;
 891        u32 addr;
 892        __le32 reg_val;
 893        int i, status;
 894
 895        /* Dump all the registers if no register is specified */
 896        if (!ar->debug.dbgfs_diag_reg)
 897                n_reg = ath6kl_get_num_reg();
 898        else
 899                n_reg = 1;
 900
 901        reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE;
 902        if (n_reg > 1)
 903                reg_len += REGTYPE_STR_LEN;
 904
 905        buf = vmalloc(reg_len);
 906        if (!buf)
 907                return -ENOMEM;
 908
 909        if (n_reg == 1) {
 910                addr = ar->debug.dbgfs_diag_reg;
 911
 912                status = ath6kl_diag_read32(ar,
 913                                TARG_VTOP(ar->target_type, addr),
 914                                (u32 *)&reg_val);
 915                if (status)
 916                        goto fail_reg_read;
 917
 918                len += scnprintf(buf + len, reg_len - len,
 919                                 "0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val));
 920                goto done;
 921        }
 922
 923        for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
 924                len += scnprintf(buf + len, reg_len - len,
 925                                "%s\n", diag_reg[i].reg_info);
 926                for (addr = diag_reg[i].reg_start;
 927                     addr <= diag_reg[i].reg_end; addr += 4) {
 928                        status = ath6kl_diag_read32(ar,
 929                                        TARG_VTOP(ar->target_type, addr),
 930                                        (u32 *)&reg_val);
 931                        if (status)
 932                                goto fail_reg_read;
 933
 934                        len += scnprintf(buf + len, reg_len - len,
 935                                        "0x%06x 0x%08x\n",
 936                                        addr, le32_to_cpu(reg_val));
 937                }
 938        }
 939
 940done:
 941        file->private_data = buf;
 942        return 0;
 943
 944fail_reg_read:
 945        ath6kl_warn("Unable to read memory:%u\n", addr);
 946        vfree(buf);
 947        return -EIO;
 948}
 949
 950static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf,
 951                                  size_t count, loff_t *ppos)
 952{
 953        u8 *buf = file->private_data;
 954        return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
 955}
 956
 957static int ath6kl_regdump_release(struct inode *inode, struct file *file)
 958{
 959        vfree(file->private_data);
 960        return 0;
 961}
 962
 963static const struct file_operations fops_reg_dump = {
 964        .open = ath6kl_regdump_open,
 965        .read = ath6kl_regdump_read,
 966        .release = ath6kl_regdump_release,
 967        .owner = THIS_MODULE,
 968        .llseek = default_llseek,
 969};
 970
 971static ssize_t ath6kl_lrssi_roam_write(struct file *file,
 972                                       const char __user *user_buf,
 973                                       size_t count, loff_t *ppos)
 974{
 975        struct ath6kl *ar = file->private_data;
 976        unsigned long lrssi_roam_threshold;
 977
 978        if (kstrtoul_from_user(user_buf, count, 0, &lrssi_roam_threshold))
 979                return -EINVAL;
 980
 981        ar->lrssi_roam_threshold = lrssi_roam_threshold;
 982
 983        ath6kl_wmi_set_roam_lrssi_cmd(ar->wmi, ar->lrssi_roam_threshold);
 984
 985        return count;
 986}
 987
 988static ssize_t ath6kl_lrssi_roam_read(struct file *file,
 989                                      char __user *user_buf,
 990                                      size_t count, loff_t *ppos)
 991{
 992        struct ath6kl *ar = file->private_data;
 993        char buf[32];
 994        unsigned int len;
 995
 996        len = snprintf(buf, sizeof(buf), "%u\n", ar->lrssi_roam_threshold);
 997
 998        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 999}
1000
1001static const struct file_operations fops_lrssi_roam_threshold = {
1002        .read = ath6kl_lrssi_roam_read,
1003        .write = ath6kl_lrssi_roam_write,
1004        .open = simple_open,
1005        .owner = THIS_MODULE,
1006        .llseek = default_llseek,
1007};
1008
1009static ssize_t ath6kl_regwrite_read(struct file *file,
1010                                    char __user *user_buf,
1011                                    size_t count, loff_t *ppos)
1012{
1013        struct ath6kl *ar = file->private_data;
1014        u8 buf[32];
1015        unsigned int len = 0;
1016
1017        len = scnprintf(buf, sizeof(buf), "Addr: 0x%x Val: 0x%x\n",
1018                        ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr);
1019
1020        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1021}
1022
1023static ssize_t ath6kl_regwrite_write(struct file *file,
1024                                     const char __user *user_buf,
1025                                     size_t count, loff_t *ppos)
1026{
1027        struct ath6kl *ar = file->private_data;
1028        char buf[32];
1029        char *sptr, *token;
1030        unsigned int len = 0;
1031        u32 reg_addr, reg_val;
1032
1033        len = min(count, sizeof(buf) - 1);
1034        if (copy_from_user(buf, user_buf, len))
1035                return -EFAULT;
1036
1037        buf[len] = '\0';
1038        sptr = buf;
1039
1040        token = strsep(&sptr, "=");
1041        if (!token)
1042                return -EINVAL;
1043
1044        if (kstrtou32(token, 0, &reg_addr))
1045                return -EINVAL;
1046
1047        if (!ath6kl_dbg_is_diag_reg_valid(reg_addr))
1048                return -EINVAL;
1049
1050        if (kstrtou32(sptr, 0, &reg_val))
1051                return -EINVAL;
1052
1053        ar->debug.diag_reg_addr_wr = reg_addr;
1054        ar->debug.diag_reg_val_wr = reg_val;
1055
1056        if (ath6kl_diag_write32(ar, ar->debug.diag_reg_addr_wr,
1057                                cpu_to_le32(ar->debug.diag_reg_val_wr)))
1058                return -EIO;
1059
1060        return count;
1061}
1062
1063static const struct file_operations fops_diag_reg_write = {
1064        .read = ath6kl_regwrite_read,
1065        .write = ath6kl_regwrite_write,
1066        .open = simple_open,
1067        .owner = THIS_MODULE,
1068        .llseek = default_llseek,
1069};
1070
1071int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf,
1072                                size_t len)
1073{
1074        const struct wmi_target_roam_tbl *tbl;
1075        u16 num_entries;
1076
1077        if (len < sizeof(*tbl))
1078                return -EINVAL;
1079
1080        tbl = (const struct wmi_target_roam_tbl *) buf;
1081        num_entries = le16_to_cpu(tbl->num_entries);
1082        if (sizeof(*tbl) + num_entries * sizeof(struct wmi_bss_roam_info) >
1083            len)
1084                return -EINVAL;
1085
1086        if (ar->debug.roam_tbl == NULL ||
1087            ar->debug.roam_tbl_len < (unsigned int) len) {
1088                kfree(ar->debug.roam_tbl);
1089                ar->debug.roam_tbl = kmalloc(len, GFP_ATOMIC);
1090                if (ar->debug.roam_tbl == NULL)
1091                        return -ENOMEM;
1092        }
1093
1094        memcpy(ar->debug.roam_tbl, buf, len);
1095        ar->debug.roam_tbl_len = len;
1096
1097        if (test_bit(ROAM_TBL_PEND, &ar->flag)) {
1098                clear_bit(ROAM_TBL_PEND, &ar->flag);
1099                wake_up(&ar->event_wq);
1100        }
1101
1102        return 0;
1103}
1104
1105static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf,
1106                                      size_t count, loff_t *ppos)
1107{
1108        struct ath6kl *ar = file->private_data;
1109        int ret;
1110        long left;
1111        struct wmi_target_roam_tbl *tbl;
1112        u16 num_entries, i;
1113        char *buf;
1114        unsigned int len, buf_len;
1115        ssize_t ret_cnt;
1116
1117        if (down_interruptible(&ar->sem))
1118                return -EBUSY;
1119
1120        set_bit(ROAM_TBL_PEND, &ar->flag);
1121
1122        ret = ath6kl_wmi_get_roam_tbl_cmd(ar->wmi);
1123        if (ret) {
1124                up(&ar->sem);
1125                return ret;
1126        }
1127
1128        left = wait_event_interruptible_timeout(
1129                ar->event_wq, !test_bit(ROAM_TBL_PEND, &ar->flag), WMI_TIMEOUT);
1130        up(&ar->sem);
1131
1132        if (left <= 0)
1133                return -ETIMEDOUT;
1134
1135        if (ar->debug.roam_tbl == NULL)
1136                return -ENOMEM;
1137
1138        tbl = (struct wmi_target_roam_tbl *) ar->debug.roam_tbl;
1139        num_entries = le16_to_cpu(tbl->num_entries);
1140
1141        buf_len = 100 + num_entries * 100;
1142        buf = kzalloc(buf_len, GFP_KERNEL);
1143        if (buf == NULL)
1144                return -ENOMEM;
1145        len = 0;
1146        len += scnprintf(buf + len, buf_len - len,
1147                         "roam_mode=%u\n\n"
1148                         "# roam_util bssid rssi rssidt last_rssi util bias\n",
1149                         le16_to_cpu(tbl->roam_mode));
1150
1151        for (i = 0; i < num_entries; i++) {
1152                struct wmi_bss_roam_info *info = &tbl->info[i];
1153                len += scnprintf(buf + len, buf_len - len,
1154                                 "%d %pM %d %d %d %d %d\n",
1155                                 a_sle32_to_cpu(info->roam_util), info->bssid,
1156                                 info->rssi, info->rssidt, info->last_rssi,
1157                                 info->util, info->bias);
1158        }
1159
1160        if (len > buf_len)
1161                len = buf_len;
1162
1163        ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
1164
1165        kfree(buf);
1166        return ret_cnt;
1167}
1168
1169static const struct file_operations fops_roam_table = {
1170        .read = ath6kl_roam_table_read,
1171        .open = simple_open,
1172        .owner = THIS_MODULE,
1173        .llseek = default_llseek,
1174};
1175
1176static ssize_t ath6kl_force_roam_write(struct file *file,
1177                                       const char __user *user_buf,
1178                                       size_t count, loff_t *ppos)
1179{
1180        struct ath6kl *ar = file->private_data;
1181        int ret;
1182        char buf[20];
1183        size_t len;
1184        u8 bssid[ETH_ALEN];
1185        int i;
1186        int addr[ETH_ALEN];
1187
1188        len = min(count, sizeof(buf) - 1);
1189        if (copy_from_user(buf, user_buf, len))
1190                return -EFAULT;
1191        buf[len] = '\0';
1192
1193        if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
1194                   &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5])
1195            != ETH_ALEN)
1196                return -EINVAL;
1197        for (i = 0; i < ETH_ALEN; i++)
1198                bssid[i] = addr[i];
1199
1200        ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid);
1201        if (ret)
1202                return ret;
1203
1204        return count;
1205}
1206
1207static const struct file_operations fops_force_roam = {
1208        .write = ath6kl_force_roam_write,
1209        .open = simple_open,
1210        .owner = THIS_MODULE,
1211        .llseek = default_llseek,
1212};
1213
1214static ssize_t ath6kl_roam_mode_write(struct file *file,
1215                                      const char __user *user_buf,
1216                                      size_t count, loff_t *ppos)
1217{
1218        struct ath6kl *ar = file->private_data;
1219        int ret;
1220        char buf[20];
1221        size_t len;
1222        enum wmi_roam_mode mode;
1223
1224        len = min(count, sizeof(buf) - 1);
1225        if (copy_from_user(buf, user_buf, len))
1226                return -EFAULT;
1227        buf[len] = '\0';
1228        if (len > 0 && buf[len - 1] == '\n')
1229                buf[len - 1] = '\0';
1230
1231        if (strcasecmp(buf, "default") == 0)
1232                mode = WMI_DEFAULT_ROAM_MODE;
1233        else if (strcasecmp(buf, "bssbias") == 0)
1234                mode = WMI_HOST_BIAS_ROAM_MODE;
1235        else if (strcasecmp(buf, "lock") == 0)
1236                mode = WMI_LOCK_BSS_MODE;
1237        else
1238                return -EINVAL;
1239
1240        ret = ath6kl_wmi_set_roam_mode_cmd(ar->wmi, mode);
1241        if (ret)
1242                return ret;
1243
1244        return count;
1245}
1246
1247static const struct file_operations fops_roam_mode = {
1248        .write = ath6kl_roam_mode_write,
1249        .open = simple_open,
1250        .owner = THIS_MODULE,
1251        .llseek = default_llseek,
1252};
1253
1254void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive)
1255{
1256        ar->debug.keepalive = keepalive;
1257}
1258
1259static ssize_t ath6kl_keepalive_read(struct file *file, char __user *user_buf,
1260                                     size_t count, loff_t *ppos)
1261{
1262        struct ath6kl *ar = file->private_data;
1263        char buf[16];
1264        int len;
1265
1266        len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.keepalive);
1267
1268        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1269}
1270
1271static ssize_t ath6kl_keepalive_write(struct file *file,
1272                                      const char __user *user_buf,
1273                                      size_t count, loff_t *ppos)
1274{
1275        struct ath6kl *ar = file->private_data;
1276        int ret;
1277        u8 val;
1278
1279        ret = kstrtou8_from_user(user_buf, count, 0, &val);
1280        if (ret)
1281                return ret;
1282
1283        ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, 0, val);
1284        if (ret)
1285                return ret;
1286
1287        return count;
1288}
1289
1290static const struct file_operations fops_keepalive = {
1291        .open = simple_open,
1292        .read = ath6kl_keepalive_read,
1293        .write = ath6kl_keepalive_write,
1294        .owner = THIS_MODULE,
1295        .llseek = default_llseek,
1296};
1297
1298void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout)
1299{
1300        ar->debug.disc_timeout = timeout;
1301}
1302
1303static ssize_t ath6kl_disconnect_timeout_read(struct file *file,
1304                                              char __user *user_buf,
1305                                              size_t count, loff_t *ppos)
1306{
1307        struct ath6kl *ar = file->private_data;
1308        char buf[16];
1309        int len;
1310
1311        len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.disc_timeout);
1312
1313        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1314}
1315
1316static ssize_t ath6kl_disconnect_timeout_write(struct file *file,
1317                                               const char __user *user_buf,
1318                                               size_t count, loff_t *ppos)
1319{
1320        struct ath6kl *ar = file->private_data;
1321        int ret;
1322        u8 val;
1323
1324        ret = kstrtou8_from_user(user_buf, count, 0, &val);
1325        if (ret)
1326                return ret;
1327
1328        ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, 0, val);
1329        if (ret)
1330                return ret;
1331
1332        return count;
1333}
1334
1335static const struct file_operations fops_disconnect_timeout = {
1336        .open = simple_open,
1337        .read = ath6kl_disconnect_timeout_read,
1338        .write = ath6kl_disconnect_timeout_write,
1339        .owner = THIS_MODULE,
1340        .llseek = default_llseek,
1341};
1342
1343static ssize_t ath6kl_create_qos_write(struct file *file,
1344                                                const char __user *user_buf,
1345                                                size_t count, loff_t *ppos)
1346{
1347
1348        struct ath6kl *ar = file->private_data;
1349        struct ath6kl_vif *vif;
1350        char buf[200];
1351        ssize_t len;
1352        char *sptr, *token;
1353        struct wmi_create_pstream_cmd pstream;
1354        u32 val32;
1355        u16 val16;
1356
1357        vif = ath6kl_vif_first(ar);
1358        if (!vif)
1359                return -EIO;
1360
1361        len = min(count, sizeof(buf) - 1);
1362        if (copy_from_user(buf, user_buf, len))
1363                return -EFAULT;
1364        buf[len] = '\0';
1365        sptr = buf;
1366
1367        token = strsep(&sptr, " ");
1368        if (!token)
1369                return -EINVAL;
1370        if (kstrtou8(token, 0, &pstream.user_pri))
1371                return -EINVAL;
1372
1373        token = strsep(&sptr, " ");
1374        if (!token)
1375                return -EINVAL;
1376        if (kstrtou8(token, 0, &pstream.traffic_direc))
1377                return -EINVAL;
1378
1379        token = strsep(&sptr, " ");
1380        if (!token)
1381                return -EINVAL;
1382        if (kstrtou8(token, 0, &pstream.traffic_class))
1383                return -EINVAL;
1384
1385        token = strsep(&sptr, " ");
1386        if (!token)
1387                return -EINVAL;
1388        if (kstrtou8(token, 0, &pstream.traffic_type))
1389                return -EINVAL;
1390
1391        token = strsep(&sptr, " ");
1392        if (!token)
1393                return -EINVAL;
1394        if (kstrtou8(token, 0, &pstream.voice_psc_cap))
1395                return -EINVAL;
1396
1397        token = strsep(&sptr, " ");
1398        if (!token)
1399                return -EINVAL;
1400        if (kstrtou32(token, 0, &val32))
1401                return -EINVAL;
1402        pstream.min_service_int = cpu_to_le32(val32);
1403
1404        token = strsep(&sptr, " ");
1405        if (!token)
1406                return -EINVAL;
1407        if (kstrtou32(token, 0, &val32))
1408                return -EINVAL;
1409        pstream.max_service_int = cpu_to_le32(val32);
1410
1411        token = strsep(&sptr, " ");
1412        if (!token)
1413                return -EINVAL;
1414        if (kstrtou32(token, 0, &val32))
1415                return -EINVAL;
1416        pstream.inactivity_int = cpu_to_le32(val32);
1417
1418        token = strsep(&sptr, " ");
1419        if (!token)
1420                return -EINVAL;
1421        if (kstrtou32(token, 0, &val32))
1422                return -EINVAL;
1423        pstream.suspension_int = cpu_to_le32(val32);
1424
1425        token = strsep(&sptr, " ");
1426        if (!token)
1427                return -EINVAL;
1428        if (kstrtou32(token, 0, &val32))
1429                return -EINVAL;
1430        pstream.service_start_time = cpu_to_le32(val32);
1431
1432        token = strsep(&sptr, " ");
1433        if (!token)
1434                return -EINVAL;
1435        if (kstrtou8(token, 0, &pstream.tsid))
1436                return -EINVAL;
1437
1438        token = strsep(&sptr, " ");
1439        if (!token)
1440                return -EINVAL;
1441        if (kstrtou16(token, 0, &val16))
1442                return -EINVAL;
1443        pstream.nominal_msdu = cpu_to_le16(val16);
1444
1445        token = strsep(&sptr, " ");
1446        if (!token)
1447                return -EINVAL;
1448        if (kstrtou16(token, 0, &val16))
1449                return -EINVAL;
1450        pstream.max_msdu = cpu_to_le16(val16);
1451
1452        token = strsep(&sptr, " ");
1453        if (!token)
1454                return -EINVAL;
1455        if (kstrtou32(token, 0, &val32))
1456                return -EINVAL;
1457        pstream.min_data_rate = cpu_to_le32(val32);
1458
1459        token = strsep(&sptr, " ");
1460        if (!token)
1461                return -EINVAL;
1462        if (kstrtou32(token, 0, &val32))
1463                return -EINVAL;
1464        pstream.mean_data_rate = cpu_to_le32(val32);
1465
1466        token = strsep(&sptr, " ");
1467        if (!token)
1468                return -EINVAL;
1469        if (kstrtou32(token, 0, &val32))
1470                return -EINVAL;
1471        pstream.peak_data_rate = cpu_to_le32(val32);
1472
1473        token = strsep(&sptr, " ");
1474        if (!token)
1475                return -EINVAL;
1476        if (kstrtou32(token, 0, &val32))
1477                return -EINVAL;
1478        pstream.max_burst_size = cpu_to_le32(val32);
1479
1480        token = strsep(&sptr, " ");
1481        if (!token)
1482                return -EINVAL;
1483        if (kstrtou32(token, 0, &val32))
1484                return -EINVAL;
1485        pstream.delay_bound = cpu_to_le32(val32);
1486
1487        token = strsep(&sptr, " ");
1488        if (!token)
1489                return -EINVAL;
1490        if (kstrtou32(token, 0, &val32))
1491                return -EINVAL;
1492        pstream.min_phy_rate = cpu_to_le32(val32);
1493
1494        token = strsep(&sptr, " ");
1495        if (!token)
1496                return -EINVAL;
1497        if (kstrtou32(token, 0, &val32))
1498                return -EINVAL;
1499        pstream.sba = cpu_to_le32(val32);
1500
1501        token = strsep(&sptr, " ");
1502        if (!token)
1503                return -EINVAL;
1504        if (kstrtou32(token, 0, &val32))
1505                return -EINVAL;
1506        pstream.medium_time = cpu_to_le32(val32);
1507
1508        pstream.nominal_phy = le32_to_cpu(pstream.min_phy_rate) / 1000000;
1509
1510        ath6kl_wmi_create_pstream_cmd(ar->wmi, vif->fw_vif_idx, &pstream);
1511
1512        return count;
1513}
1514
1515static const struct file_operations fops_create_qos = {
1516        .write = ath6kl_create_qos_write,
1517        .open = simple_open,
1518        .owner = THIS_MODULE,
1519        .llseek = default_llseek,
1520};
1521
1522static ssize_t ath6kl_delete_qos_write(struct file *file,
1523                                const char __user *user_buf,
1524                                size_t count, loff_t *ppos)
1525{
1526
1527        struct ath6kl *ar = file->private_data;
1528        struct ath6kl_vif *vif;
1529        char buf[100];
1530        ssize_t len;
1531        char *sptr, *token;
1532        u8 traffic_class;
1533        u8 tsid;
1534
1535        vif = ath6kl_vif_first(ar);
1536        if (!vif)
1537                return -EIO;
1538
1539        len = min(count, sizeof(buf) - 1);
1540        if (copy_from_user(buf, user_buf, len))
1541                return -EFAULT;
1542        buf[len] = '\0';
1543        sptr = buf;
1544
1545        token = strsep(&sptr, " ");
1546        if (!token)
1547                return -EINVAL;
1548        if (kstrtou8(token, 0, &traffic_class))
1549                return -EINVAL;
1550
1551        token = strsep(&sptr, " ");
1552        if (!token)
1553                return -EINVAL;
1554        if (kstrtou8(token, 0, &tsid))
1555                return -EINVAL;
1556
1557        ath6kl_wmi_delete_pstream_cmd(ar->wmi, vif->fw_vif_idx,
1558                                      traffic_class, tsid);
1559
1560        return count;
1561}
1562
1563static const struct file_operations fops_delete_qos = {
1564        .write = ath6kl_delete_qos_write,
1565        .open = simple_open,
1566        .owner = THIS_MODULE,
1567        .llseek = default_llseek,
1568};
1569
1570static ssize_t ath6kl_bgscan_int_write(struct file *file,
1571                                const char __user *user_buf,
1572                                size_t count, loff_t *ppos)
1573{
1574        struct ath6kl *ar = file->private_data;
1575        struct ath6kl_vif *vif;
1576        u16 bgscan_int;
1577        char buf[32];
1578        ssize_t len;
1579
1580        vif = ath6kl_vif_first(ar);
1581        if (!vif)
1582                return -EIO;
1583
1584        len = min(count, sizeof(buf) - 1);
1585        if (copy_from_user(buf, user_buf, len))
1586                return -EFAULT;
1587
1588        buf[len] = '\0';
1589        if (kstrtou16(buf, 0, &bgscan_int))
1590                return -EINVAL;
1591
1592        if (bgscan_int == 0)
1593                bgscan_int = 0xffff;
1594
1595        vif->bg_scan_period = bgscan_int;
1596
1597        ath6kl_wmi_scanparams_cmd(ar->wmi, 0, 0, 0, bgscan_int, 0, 0, 0, 3,
1598                                  0, 0, 0);
1599
1600        return count;
1601}
1602
1603static const struct file_operations fops_bgscan_int = {
1604        .write = ath6kl_bgscan_int_write,
1605        .open = simple_open,
1606        .owner = THIS_MODULE,
1607        .llseek = default_llseek,
1608};
1609
1610static ssize_t ath6kl_listen_int_write(struct file *file,
1611                                       const char __user *user_buf,
1612                                       size_t count, loff_t *ppos)
1613{
1614        struct ath6kl *ar = file->private_data;
1615        struct ath6kl_vif *vif;
1616        u16 listen_interval;
1617        char buf[32];
1618        ssize_t len;
1619
1620        vif = ath6kl_vif_first(ar);
1621        if (!vif)
1622                return -EIO;
1623
1624        len = min(count, sizeof(buf) - 1);
1625        if (copy_from_user(buf, user_buf, len))
1626                return -EFAULT;
1627
1628        buf[len] = '\0';
1629        if (kstrtou16(buf, 0, &listen_interval))
1630                return -EINVAL;
1631
1632        if ((listen_interval < 15) || (listen_interval > 3000))
1633                return -EINVAL;
1634
1635        vif->listen_intvl_t = listen_interval;
1636        ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
1637                                      vif->listen_intvl_t, 0);
1638
1639        return count;
1640}
1641
1642static ssize_t ath6kl_listen_int_read(struct file *file,
1643                                      char __user *user_buf,
1644                                      size_t count, loff_t *ppos)
1645{
1646        struct ath6kl *ar = file->private_data;
1647        struct ath6kl_vif *vif;
1648        char buf[32];
1649        int len;
1650
1651        vif = ath6kl_vif_first(ar);
1652        if (!vif)
1653                return -EIO;
1654
1655        len = scnprintf(buf, sizeof(buf), "%u\n", vif->listen_intvl_t);
1656
1657        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1658}
1659
1660static const struct file_operations fops_listen_int = {
1661        .read = ath6kl_listen_int_read,
1662        .write = ath6kl_listen_int_write,
1663        .open = simple_open,
1664        .owner = THIS_MODULE,
1665        .llseek = default_llseek,
1666};
1667
1668static ssize_t ath6kl_power_params_write(struct file *file,
1669                                                const char __user *user_buf,
1670                                                size_t count, loff_t *ppos)
1671{
1672        struct ath6kl *ar = file->private_data;
1673        u8 buf[100];
1674        unsigned int len = 0;
1675        char *sptr, *token;
1676        u16 idle_period, ps_poll_num, dtim,
1677                tx_wakeup, num_tx;
1678
1679        len = min(count, sizeof(buf) - 1);
1680        if (copy_from_user(buf, user_buf, len))
1681                return -EFAULT;
1682        buf[len] = '\0';
1683        sptr = buf;
1684
1685        token = strsep(&sptr, " ");
1686        if (!token)
1687                return -EINVAL;
1688        if (kstrtou16(token, 0, &idle_period))
1689                return -EINVAL;
1690
1691        token = strsep(&sptr, " ");
1692        if (!token)
1693                return -EINVAL;
1694        if (kstrtou16(token, 0, &ps_poll_num))
1695                return -EINVAL;
1696
1697        token = strsep(&sptr, " ");
1698        if (!token)
1699                return -EINVAL;
1700        if (kstrtou16(token, 0, &dtim))
1701                return -EINVAL;
1702
1703        token = strsep(&sptr, " ");
1704        if (!token)
1705                return -EINVAL;
1706        if (kstrtou16(token, 0, &tx_wakeup))
1707                return -EINVAL;
1708
1709        token = strsep(&sptr, " ");
1710        if (!token)
1711                return -EINVAL;
1712        if (kstrtou16(token, 0, &num_tx))
1713                return -EINVAL;
1714
1715        ath6kl_wmi_pmparams_cmd(ar->wmi, 0, idle_period, ps_poll_num,
1716                                dtim, tx_wakeup, num_tx, 0);
1717
1718        return count;
1719}
1720
1721static const struct file_operations fops_power_params = {
1722        .write = ath6kl_power_params_write,
1723        .open = simple_open,
1724        .owner = THIS_MODULE,
1725        .llseek = default_llseek,
1726};
1727
1728void ath6kl_debug_init(struct ath6kl *ar)
1729{
1730        skb_queue_head_init(&ar->debug.fwlog_queue);
1731        init_completion(&ar->debug.fwlog_completion);
1732
1733        /*
1734         * Actually we are lying here but don't know how to read the mask
1735         * value from the firmware.
1736         */
1737        ar->debug.fwlog_mask = 0;
1738}
1739
1740/*
1741 * Initialisation needs to happen in two stages as fwlog events can come
1742 * before cfg80211 is initialised, and debugfs depends on cfg80211
1743 * initialisation.
1744 */
1745int ath6kl_debug_init_fs(struct ath6kl *ar)
1746{
1747        ar->debugfs_phy = debugfs_create_dir("ath6kl",
1748                                             ar->wiphy->debugfsdir);
1749        if (!ar->debugfs_phy)
1750                return -ENOMEM;
1751
1752        debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
1753                            &fops_tgt_stats);
1754
1755        debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar,
1756                            &fops_credit_dist_stats);
1757
1758        debugfs_create_file("endpoint_stats", S_IRUSR | S_IWUSR,
1759                            ar->debugfs_phy, ar, &fops_endpoint_stats);
1760
1761        debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
1762                            &fops_fwlog);
1763
1764        debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar,
1765                            &fops_fwlog_block);
1766
1767        debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
1768                            ar, &fops_fwlog_mask);
1769
1770        debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
1771                            &fops_diag_reg_read);
1772
1773        debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar,
1774                            &fops_reg_dump);
1775
1776        debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR,
1777                            ar->debugfs_phy, ar, &fops_lrssi_roam_threshold);
1778
1779        debugfs_create_file("reg_write", S_IRUSR | S_IWUSR,
1780                            ar->debugfs_phy, ar, &fops_diag_reg_write);
1781
1782        debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar,
1783                            &fops_war_stats);
1784
1785        debugfs_create_file("roam_table", S_IRUSR, ar->debugfs_phy, ar,
1786                            &fops_roam_table);
1787
1788        debugfs_create_file("force_roam", S_IWUSR, ar->debugfs_phy, ar,
1789                            &fops_force_roam);
1790
1791        debugfs_create_file("roam_mode", S_IWUSR, ar->debugfs_phy, ar,
1792                            &fops_roam_mode);
1793
1794        debugfs_create_file("keepalive", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
1795                            &fops_keepalive);
1796
1797        debugfs_create_file("disconnect_timeout", S_IRUSR | S_IWUSR,
1798                            ar->debugfs_phy, ar, &fops_disconnect_timeout);
1799
1800        debugfs_create_file("create_qos", S_IWUSR, ar->debugfs_phy, ar,
1801                            &fops_create_qos);
1802
1803        debugfs_create_file("delete_qos", S_IWUSR, ar->debugfs_phy, ar,
1804                            &fops_delete_qos);
1805
1806        debugfs_create_file("bgscan_interval", S_IWUSR,
1807                            ar->debugfs_phy, ar, &fops_bgscan_int);
1808
1809        debugfs_create_file("listen_interval", S_IRUSR | S_IWUSR,
1810                            ar->debugfs_phy, ar, &fops_listen_int);
1811
1812        debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar,
1813                            &fops_power_params);
1814
1815        return 0;
1816}
1817
1818void ath6kl_debug_cleanup(struct ath6kl *ar)
1819{
1820        skb_queue_purge(&ar->debug.fwlog_queue);
1821        complete(&ar->debug.fwlog_completion);
1822        kfree(ar->debug.roam_tbl);
1823}
1824
1825#endif
1826