linux/drivers/net/wireless/iwmc3200wifi/debugfs.c
<<
>>
Prefs
   1/*
   2 * Intel Wireless Multicomm 3200 WiFi driver
   3 *
   4 * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
   5 * Samuel Ortiz <samuel.ortiz@intel.com>
   6 * Zhu Yi <yi.zhu@intel.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License version
  10 * 2 as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20 * 02110-1301, USA.
  21 *
  22 */
  23
  24#include <linux/kernel.h>
  25#include <linux/bitops.h>
  26#include <linux/debugfs.h>
  27
  28#include "iwm.h"
  29#include "bus.h"
  30#include "rx.h"
  31#include "debug.h"
  32
  33static struct {
  34        u8 id;
  35        char *name;
  36} iwm_debug_module[__IWM_DM_NR] = {
  37         {IWM_DM_BOOT, "boot"},
  38         {IWM_DM_FW,   "fw"},
  39         {IWM_DM_SDIO, "sdio"},
  40         {IWM_DM_NTF,  "ntf"},
  41         {IWM_DM_RX,   "rx"},
  42         {IWM_DM_TX,   "tx"},
  43         {IWM_DM_MLME, "mlme"},
  44         {IWM_DM_CMD,  "cmd"},
  45         {IWM_DM_WEXT,  "wext"},
  46};
  47
  48#define add_dbg_module(dbg, name, id, initlevel)        \
  49do {                                                    \
  50        struct dentry *d;                               \
  51        dbg.dbg_module[id] = (initlevel);               \
  52        d = debugfs_create_x8(name, 0600, dbg.dbgdir,   \
  53                             &(dbg.dbg_module[id]));    \
  54        if (!IS_ERR(d))                                 \
  55                dbg.dbg_module_dentries[id] = d;        \
  56} while (0)
  57
  58static int iwm_debugfs_u32_read(void *data, u64 *val)
  59{
  60        struct iwm_priv *iwm = data;
  61
  62        *val = iwm->dbg.dbg_level;
  63        return 0;
  64}
  65
  66static int iwm_debugfs_dbg_level_write(void *data, u64 val)
  67{
  68        struct iwm_priv *iwm = data;
  69        int i;
  70
  71        iwm->dbg.dbg_level = val;
  72
  73        for (i = 0; i < __IWM_DM_NR; i++)
  74                iwm->dbg.dbg_module[i] = val;
  75
  76        return 0;
  77}
  78DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_level,
  79                        iwm_debugfs_u32_read, iwm_debugfs_dbg_level_write,
  80                        "%llu\n");
  81
  82static int iwm_debugfs_dbg_modules_write(void *data, u64 val)
  83{
  84        struct iwm_priv *iwm = data;
  85        int i, bit;
  86
  87        iwm->dbg.dbg_modules = val;
  88
  89        for (i = 0; i < __IWM_DM_NR; i++)
  90                iwm->dbg.dbg_module[i] = 0;
  91
  92        for_each_bit(bit, &iwm->dbg.dbg_modules, __IWM_DM_NR)
  93                iwm->dbg.dbg_module[bit] = iwm->dbg.dbg_level;
  94
  95        return 0;
  96}
  97DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules,
  98                        iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write,
  99                        "%llu\n");
 100
 101static int iwm_generic_open(struct inode *inode, struct file *filp)
 102{
 103        filp->private_data = inode->i_private;
 104        return 0;
 105}
 106
 107
 108static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer,
 109                                   size_t count, loff_t *ppos)
 110{
 111        struct iwm_priv *iwm = filp->private_data;
 112        char *buf;
 113        int i, buf_len = 4096;
 114        size_t len = 0;
 115        ssize_t ret;
 116
 117        if (*ppos != 0)
 118                return 0;
 119        if (count < sizeof(buf))
 120                return -ENOSPC;
 121
 122        buf = kzalloc(buf_len, GFP_KERNEL);
 123        if (!buf)
 124                return -ENOMEM;
 125
 126        for (i = 0; i < IWM_TX_QUEUES; i++) {
 127                struct iwm_tx_queue *txq = &iwm->txq[i];
 128                struct sk_buff *skb;
 129                int j;
 130                unsigned long flags;
 131
 132                spin_lock_irqsave(&txq->queue.lock, flags);
 133
 134                skb = (struct sk_buff *)&txq->queue;
 135
 136                len += snprintf(buf + len, buf_len - len, "TXQ #%d\n", i);
 137                len += snprintf(buf + len, buf_len - len, "\tStopped:     %d\n",
 138                                __netif_subqueue_stopped(iwm_to_ndev(iwm),
 139                                                         txq->id));
 140                len += snprintf(buf + len, buf_len - len, "\tConcat count:%d\n",
 141                                txq->concat_count);
 142                len += snprintf(buf + len, buf_len - len, "\tQueue len:   %d\n",
 143                                skb_queue_len(&txq->queue));
 144                for (j = 0; j < skb_queue_len(&txq->queue); j++) {
 145                        struct iwm_tx_info *tx_info;
 146
 147                        skb = skb->next;
 148                        tx_info = skb_to_tx_info(skb);
 149
 150                        len += snprintf(buf + len, buf_len - len,
 151                                        "\tSKB #%d\n", j);
 152                        len += snprintf(buf + len, buf_len - len,
 153                                        "\t\tsta:   %d\n", tx_info->sta);
 154                        len += snprintf(buf + len, buf_len - len,
 155                                        "\t\tcolor: %d\n", tx_info->color);
 156                        len += snprintf(buf + len, buf_len - len,
 157                                        "\t\ttid:   %d\n", tx_info->tid);
 158                }
 159
 160                spin_unlock_irqrestore(&txq->queue.lock, flags);
 161        }
 162
 163        ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
 164        kfree(buf);
 165
 166        return ret;
 167}
 168
 169static ssize_t iwm_debugfs_tx_credit_read(struct file *filp,
 170                                          char __user *buffer,
 171                                          size_t count, loff_t *ppos)
 172{
 173        struct iwm_priv *iwm = filp->private_data;
 174        struct iwm_tx_credit *credit = &iwm->tx_credit;
 175        char *buf;
 176        int i, buf_len = 4096;
 177        size_t len = 0;
 178        ssize_t ret;
 179
 180        if (*ppos != 0)
 181                return 0;
 182        if (count < sizeof(buf))
 183                return -ENOSPC;
 184
 185        buf = kzalloc(buf_len, GFP_KERNEL);
 186        if (!buf)
 187                return -ENOMEM;
 188
 189        len += snprintf(buf + len, buf_len - len,
 190                        "NR pools:  %d\n", credit->pool_nr);
 191        len += snprintf(buf + len, buf_len - len,
 192                        "pools map: 0x%lx\n", credit->full_pools_map);
 193
 194        len += snprintf(buf + len, buf_len - len, "\n### POOLS ###\n");
 195        for (i = 0; i < IWM_MACS_OUT_GROUPS; i++) {
 196                len += snprintf(buf + len, buf_len - len,
 197                                "pools entry #%d\n", i);
 198                len += snprintf(buf + len, buf_len - len,
 199                                "\tid:          %d\n",
 200                                credit->pools[i].id);
 201                len += snprintf(buf + len, buf_len - len,
 202                                "\tsid:         %d\n",
 203                                credit->pools[i].sid);
 204                len += snprintf(buf + len, buf_len - len,
 205                                "\tmin_pages:   %d\n",
 206                                credit->pools[i].min_pages);
 207                len += snprintf(buf + len, buf_len - len,
 208                                "\tmax_pages:   %d\n",
 209                                credit->pools[i].max_pages);
 210                len += snprintf(buf + len, buf_len - len,
 211                                "\talloc_pages: %d\n",
 212                                credit->pools[i].alloc_pages);
 213                len += snprintf(buf + len, buf_len - len,
 214                                "\tfreed_pages: %d\n",
 215                                credit->pools[i].total_freed_pages);
 216        }
 217
 218        len += snprintf(buf + len, buf_len - len, "\n### SPOOLS ###\n");
 219        for (i = 0; i < IWM_MACS_OUT_SGROUPS; i++) {
 220                len += snprintf(buf + len, buf_len - len,
 221                                "spools entry #%d\n", i);
 222                len += snprintf(buf + len, buf_len - len,
 223                                "\tid:          %d\n",
 224                                credit->spools[i].id);
 225                len += snprintf(buf + len, buf_len - len,
 226                                "\tmax_pages:   %d\n",
 227                                credit->spools[i].max_pages);
 228                len += snprintf(buf + len, buf_len - len,
 229                                "\talloc_pages: %d\n",
 230                                credit->spools[i].alloc_pages);
 231
 232        }
 233
 234        ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
 235        kfree(buf);
 236
 237        return ret;
 238}
 239
 240static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp,
 241                                          char __user *buffer,
 242                                          size_t count, loff_t *ppos)
 243{
 244        struct iwm_priv *iwm = filp->private_data;
 245        struct iwm_rx_ticket_node *ticket, *next;
 246        char *buf;
 247        int buf_len = 4096, i;
 248        size_t len = 0;
 249        ssize_t ret;
 250
 251        if (*ppos != 0)
 252                return 0;
 253        if (count < sizeof(buf))
 254                return -ENOSPC;
 255
 256        buf = kzalloc(buf_len, GFP_KERNEL);
 257        if (!buf)
 258                return -ENOMEM;
 259
 260        list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {
 261                len += snprintf(buf + len, buf_len - len, "Ticket #%d\n",
 262                                ticket->ticket->id);
 263                len += snprintf(buf + len, buf_len - len, "\taction: 0x%x\n",
 264                                ticket->ticket->action);
 265                len += snprintf(buf + len, buf_len - len, "\tflags:  0x%x\n",
 266                                ticket->ticket->flags);
 267        }
 268
 269        for (i = 0; i < IWM_RX_ID_HASH; i++) {
 270                struct iwm_rx_packet *packet, *nxt;
 271                struct list_head *pkt_list = &iwm->rx_packets[i];
 272                if (!list_empty(pkt_list)) {
 273                        len += snprintf(buf + len, buf_len - len,
 274                                        "Packet hash #%d\n", i);
 275                        list_for_each_entry_safe(packet, nxt, pkt_list, node) {
 276                                len += snprintf(buf + len, buf_len - len,
 277                                                "\tPacket id:     %d\n",
 278                                                packet->id);
 279                                len += snprintf(buf + len, buf_len - len,
 280                                                "\tPacket length: %lu\n",
 281                                                packet->pkt_size);
 282                        }
 283                }
 284        }
 285
 286        ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
 287        kfree(buf);
 288
 289        return ret;
 290}
 291
 292static ssize_t iwm_debugfs_fw_err_read(struct file *filp,
 293                                       char __user *buffer,
 294                                       size_t count, loff_t *ppos)
 295{
 296
 297        struct iwm_priv *iwm = filp->private_data;
 298        char buf[512];
 299        int buf_len = 512;
 300        size_t len = 0;
 301
 302        if (*ppos != 0)
 303                return 0;
 304        if (count < sizeof(buf))
 305                return -ENOSPC;
 306
 307        if (!iwm->last_fw_err)
 308                return -ENOMEM;
 309
 310        if (iwm->last_fw_err->line_num == 0)
 311                goto out;
 312
 313        len += snprintf(buf + len, buf_len - len, "%cMAC FW ERROR:\n",
 314             (le32_to_cpu(iwm->last_fw_err->category) == UMAC_SYS_ERR_CAT_LMAC)
 315                        ? 'L' : 'U');
 316        len += snprintf(buf + len, buf_len - len,
 317                        "\tCategory:    %d\n",
 318                        le32_to_cpu(iwm->last_fw_err->category));
 319
 320        len += snprintf(buf + len, buf_len - len,
 321                        "\tStatus:      0x%x\n",
 322                        le32_to_cpu(iwm->last_fw_err->status));
 323
 324        len += snprintf(buf + len, buf_len - len,
 325                        "\tPC:          0x%x\n",
 326                        le32_to_cpu(iwm->last_fw_err->pc));
 327
 328        len += snprintf(buf + len, buf_len - len,
 329                        "\tblink1:      %d\n",
 330                        le32_to_cpu(iwm->last_fw_err->blink1));
 331
 332        len += snprintf(buf + len, buf_len - len,
 333                        "\tblink2:      %d\n",
 334                        le32_to_cpu(iwm->last_fw_err->blink2));
 335
 336        len += snprintf(buf + len, buf_len - len,
 337                        "\tilink1:      %d\n",
 338                        le32_to_cpu(iwm->last_fw_err->ilink1));
 339
 340        len += snprintf(buf + len, buf_len - len,
 341                        "\tilink2:      %d\n",
 342                        le32_to_cpu(iwm->last_fw_err->ilink2));
 343
 344        len += snprintf(buf + len, buf_len - len,
 345                        "\tData1:       0x%x\n",
 346                        le32_to_cpu(iwm->last_fw_err->data1));
 347
 348        len += snprintf(buf + len, buf_len - len,
 349                        "\tData2:       0x%x\n",
 350                        le32_to_cpu(iwm->last_fw_err->data2));
 351
 352        len += snprintf(buf + len, buf_len - len,
 353                        "\tLine number: %d\n",
 354                        le32_to_cpu(iwm->last_fw_err->line_num));
 355
 356        len += snprintf(buf + len, buf_len - len,
 357                        "\tUMAC status: 0x%x\n",
 358                        le32_to_cpu(iwm->last_fw_err->umac_status));
 359
 360        len += snprintf(buf + len, buf_len - len,
 361                        "\tLMAC status: 0x%x\n",
 362                        le32_to_cpu(iwm->last_fw_err->lmac_status));
 363
 364        len += snprintf(buf + len, buf_len - len,
 365                        "\tSDIO status: 0x%x\n",
 366                        le32_to_cpu(iwm->last_fw_err->sdio_status));
 367
 368out:
 369
 370        return simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
 371}
 372
 373static const struct file_operations iwm_debugfs_txq_fops = {
 374        .owner =        THIS_MODULE,
 375        .open =         iwm_generic_open,
 376        .read =         iwm_debugfs_txq_read,
 377};
 378
 379static const struct file_operations iwm_debugfs_tx_credit_fops = {
 380        .owner =        THIS_MODULE,
 381        .open =         iwm_generic_open,
 382        .read =         iwm_debugfs_tx_credit_read,
 383};
 384
 385static const struct file_operations iwm_debugfs_rx_ticket_fops = {
 386        .owner =        THIS_MODULE,
 387        .open =         iwm_generic_open,
 388        .read =         iwm_debugfs_rx_ticket_read,
 389};
 390
 391static const struct file_operations iwm_debugfs_fw_err_fops = {
 392        .owner =        THIS_MODULE,
 393        .open =         iwm_generic_open,
 394        .read =         iwm_debugfs_fw_err_read,
 395};
 396
 397int iwm_debugfs_init(struct iwm_priv *iwm)
 398{
 399        int i, result;
 400        char devdir[16];
 401
 402        iwm->dbg.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
 403        result = PTR_ERR(iwm->dbg.rootdir);
 404        if (!result || IS_ERR(iwm->dbg.rootdir)) {
 405                if (result == -ENODEV) {
 406                        IWM_ERR(iwm, "DebugFS (CONFIG_DEBUG_FS) not "
 407                                "enabled in kernel config\n");
 408                        result = 0;     /* No debugfs support */
 409                }
 410                IWM_ERR(iwm, "Couldn't create rootdir: %d\n", result);
 411                goto error;
 412        }
 413
 414        snprintf(devdir, sizeof(devdir), "%s", wiphy_name(iwm_to_wiphy(iwm)));
 415
 416        iwm->dbg.devdir = debugfs_create_dir(devdir, iwm->dbg.rootdir);
 417        result = PTR_ERR(iwm->dbg.devdir);
 418        if (IS_ERR(iwm->dbg.devdir) && (result != -ENODEV)) {
 419                IWM_ERR(iwm, "Couldn't create devdir: %d\n", result);
 420                goto error;
 421        }
 422
 423        iwm->dbg.dbgdir = debugfs_create_dir("debug", iwm->dbg.devdir);
 424        result = PTR_ERR(iwm->dbg.dbgdir);
 425        if (IS_ERR(iwm->dbg.dbgdir) && (result != -ENODEV)) {
 426                IWM_ERR(iwm, "Couldn't create dbgdir: %d\n", result);
 427                goto error;
 428        }
 429
 430        iwm->dbg.rxdir = debugfs_create_dir("rx", iwm->dbg.devdir);
 431        result = PTR_ERR(iwm->dbg.rxdir);
 432        if (IS_ERR(iwm->dbg.rxdir) && (result != -ENODEV)) {
 433                IWM_ERR(iwm, "Couldn't create rx dir: %d\n", result);
 434                goto error;
 435        }
 436
 437        iwm->dbg.txdir = debugfs_create_dir("tx", iwm->dbg.devdir);
 438        result = PTR_ERR(iwm->dbg.txdir);
 439        if (IS_ERR(iwm->dbg.txdir) && (result != -ENODEV)) {
 440                IWM_ERR(iwm, "Couldn't create tx dir: %d\n", result);
 441                goto error;
 442        }
 443
 444        iwm->dbg.busdir = debugfs_create_dir("bus", iwm->dbg.devdir);
 445        result = PTR_ERR(iwm->dbg.busdir);
 446        if (IS_ERR(iwm->dbg.busdir) && (result != -ENODEV)) {
 447                IWM_ERR(iwm, "Couldn't create bus dir: %d\n", result);
 448                goto error;
 449        }
 450
 451        if (iwm->bus_ops->debugfs_init) {
 452                result = iwm->bus_ops->debugfs_init(iwm, iwm->dbg.busdir);
 453                if (result < 0) {
 454                        IWM_ERR(iwm, "Couldn't create bus entry: %d\n", result);
 455                        goto error;
 456                }
 457        }
 458
 459
 460        iwm->dbg.dbg_level = IWM_DL_NONE;
 461        iwm->dbg.dbg_level_dentry =
 462                debugfs_create_file("level", 0200, iwm->dbg.dbgdir, iwm,
 463                                    &fops_iwm_dbg_level);
 464        result = PTR_ERR(iwm->dbg.dbg_level_dentry);
 465        if (IS_ERR(iwm->dbg.dbg_level_dentry) && (result != -ENODEV)) {
 466                IWM_ERR(iwm, "Couldn't create dbg_level: %d\n", result);
 467                goto error;
 468        }
 469
 470
 471        iwm->dbg.dbg_modules = IWM_DM_DEFAULT;
 472        iwm->dbg.dbg_modules_dentry =
 473                debugfs_create_file("modules", 0200, iwm->dbg.dbgdir, iwm,
 474                                    &fops_iwm_dbg_modules);
 475        result = PTR_ERR(iwm->dbg.dbg_modules_dentry);
 476        if (IS_ERR(iwm->dbg.dbg_modules_dentry) && (result != -ENODEV)) {
 477                IWM_ERR(iwm, "Couldn't create dbg_modules: %d\n", result);
 478                goto error;
 479        }
 480
 481        for (i = 0; i < __IWM_DM_NR; i++)
 482                add_dbg_module(iwm->dbg, iwm_debug_module[i].name,
 483                               iwm_debug_module[i].id, IWM_DL_DEFAULT);
 484
 485        iwm->dbg.txq_dentry = debugfs_create_file("queues", 0200,
 486                                                  iwm->dbg.txdir, iwm,
 487                                                  &iwm_debugfs_txq_fops);
 488        result = PTR_ERR(iwm->dbg.txq_dentry);
 489        if (IS_ERR(iwm->dbg.txq_dentry) && (result != -ENODEV)) {
 490                IWM_ERR(iwm, "Couldn't create tx queue: %d\n", result);
 491                goto error;
 492        }
 493
 494        iwm->dbg.tx_credit_dentry = debugfs_create_file("credits", 0200,
 495                                                   iwm->dbg.txdir, iwm,
 496                                                   &iwm_debugfs_tx_credit_fops);
 497        result = PTR_ERR(iwm->dbg.tx_credit_dentry);
 498        if (IS_ERR(iwm->dbg.tx_credit_dentry) && (result != -ENODEV)) {
 499                IWM_ERR(iwm, "Couldn't create tx credit: %d\n", result);
 500                goto error;
 501        }
 502
 503        iwm->dbg.rx_ticket_dentry = debugfs_create_file("tickets", 0200,
 504                                                  iwm->dbg.rxdir, iwm,
 505                                                  &iwm_debugfs_rx_ticket_fops);
 506        result = PTR_ERR(iwm->dbg.rx_ticket_dentry);
 507        if (IS_ERR(iwm->dbg.rx_ticket_dentry) && (result != -ENODEV)) {
 508                IWM_ERR(iwm, "Couldn't create rx ticket: %d\n", result);
 509                goto error;
 510        }
 511
 512        iwm->dbg.fw_err_dentry = debugfs_create_file("last_fw_err", 0200,
 513                                                     iwm->dbg.dbgdir, iwm,
 514                                                     &iwm_debugfs_fw_err_fops);
 515        result = PTR_ERR(iwm->dbg.fw_err_dentry);
 516        if (IS_ERR(iwm->dbg.fw_err_dentry) && (result != -ENODEV)) {
 517                IWM_ERR(iwm, "Couldn't create last FW err: %d\n", result);
 518                goto error;
 519        }
 520
 521
 522        return 0;
 523
 524 error:
 525        return result;
 526}
 527
 528void iwm_debugfs_exit(struct iwm_priv *iwm)
 529{
 530        int i;
 531
 532        for (i = 0; i < __IWM_DM_NR; i++)
 533                debugfs_remove(iwm->dbg.dbg_module_dentries[i]);
 534
 535        debugfs_remove(iwm->dbg.dbg_modules_dentry);
 536        debugfs_remove(iwm->dbg.dbg_level_dentry);
 537        debugfs_remove(iwm->dbg.txq_dentry);
 538        debugfs_remove(iwm->dbg.tx_credit_dentry);
 539        debugfs_remove(iwm->dbg.rx_ticket_dentry);
 540        debugfs_remove(iwm->dbg.fw_err_dentry);
 541        if (iwm->bus_ops->debugfs_exit)
 542                iwm->bus_ops->debugfs_exit(iwm);
 543
 544        debugfs_remove(iwm->dbg.busdir);
 545        debugfs_remove(iwm->dbg.dbgdir);
 546        debugfs_remove(iwm->dbg.txdir);
 547        debugfs_remove(iwm->dbg.rxdir);
 548        debugfs_remove(iwm->dbg.devdir);
 549        debugfs_remove(iwm->dbg.rootdir);
 550}
 551