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/slab.h> 25#include <linux/kernel.h> 26#include <linux/bitops.h> 27#include <linux/debugfs.h> 28 29#include "iwm.h" 30#include "bus.h" 31#include "rx.h" 32#include "debug.h" 33 34static struct { 35 u8 id; 36 char *name; 37} iwm_debug_module[__IWM_DM_NR] = { 38 {IWM_DM_BOOT, "boot"}, 39 {IWM_DM_FW, "fw"}, 40 {IWM_DM_SDIO, "sdio"}, 41 {IWM_DM_NTF, "ntf"}, 42 {IWM_DM_RX, "rx"}, 43 {IWM_DM_TX, "tx"}, 44 {IWM_DM_MLME, "mlme"}, 45 {IWM_DM_CMD, "cmd"}, 46 {IWM_DM_WEXT, "wext"}, 47}; 48 49#define add_dbg_module(dbg, name, id, initlevel) \ 50do { \ 51 dbg.dbg_module[id] = (initlevel); \ 52 dbg.dbg_module_dentries[id] = \ 53 debugfs_create_x8(name, 0600, \ 54 dbg.dbgdir, \ 55 &(dbg.dbg_module[id])); \ 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_set_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 spin_lock_irqsave(&txq->stopped_queue.lock, flags); 163 164 len += snprintf(buf + len, buf_len - len, 165 "\tStopped Queue len: %d\n", 166 skb_queue_len(&txq->stopped_queue)); 167 for (j = 0; j < skb_queue_len(&txq->stopped_queue); j++) { 168 struct iwm_tx_info *tx_info; 169 170 skb = skb->next; 171 tx_info = skb_to_tx_info(skb); 172 173 len += snprintf(buf + len, buf_len - len, 174 "\tSKB #%d\n", j); 175 len += snprintf(buf + len, buf_len - len, 176 "\t\tsta: %d\n", tx_info->sta); 177 len += snprintf(buf + len, buf_len - len, 178 "\t\tcolor: %d\n", tx_info->color); 179 len += snprintf(buf + len, buf_len - len, 180 "\t\ttid: %d\n", tx_info->tid); 181 } 182 183 spin_unlock_irqrestore(&txq->stopped_queue.lock, flags); 184 } 185 186 ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); 187 kfree(buf); 188 189 return ret; 190} 191 192static ssize_t iwm_debugfs_tx_credit_read(struct file *filp, 193 char __user *buffer, 194 size_t count, loff_t *ppos) 195{ 196 struct iwm_priv *iwm = filp->private_data; 197 struct iwm_tx_credit *credit = &iwm->tx_credit; 198 char *buf; 199 int i, buf_len = 4096; 200 size_t len = 0; 201 ssize_t ret; 202 203 if (*ppos != 0) 204 return 0; 205 if (count < sizeof(buf)) 206 return -ENOSPC; 207 208 buf = kzalloc(buf_len, GFP_KERNEL); 209 if (!buf) 210 return -ENOMEM; 211 212 len += snprintf(buf + len, buf_len - len, 213 "NR pools: %d\n", credit->pool_nr); 214 len += snprintf(buf + len, buf_len - len, 215 "pools map: 0x%lx\n", credit->full_pools_map); 216 217 len += snprintf(buf + len, buf_len - len, "\n### POOLS ###\n"); 218 for (i = 0; i < IWM_MACS_OUT_GROUPS; i++) { 219 len += snprintf(buf + len, buf_len - len, 220 "pools entry #%d\n", i); 221 len += snprintf(buf + len, buf_len - len, 222 "\tid: %d\n", 223 credit->pools[i].id); 224 len += snprintf(buf + len, buf_len - len, 225 "\tsid: %d\n", 226 credit->pools[i].sid); 227 len += snprintf(buf + len, buf_len - len, 228 "\tmin_pages: %d\n", 229 credit->pools[i].min_pages); 230 len += snprintf(buf + len, buf_len - len, 231 "\tmax_pages: %d\n", 232 credit->pools[i].max_pages); 233 len += snprintf(buf + len, buf_len - len, 234 "\talloc_pages: %d\n", 235 credit->pools[i].alloc_pages); 236 len += snprintf(buf + len, buf_len - len, 237 "\tfreed_pages: %d\n", 238 credit->pools[i].total_freed_pages); 239 } 240 241 len += snprintf(buf + len, buf_len - len, "\n### SPOOLS ###\n"); 242 for (i = 0; i < IWM_MACS_OUT_SGROUPS; i++) { 243 len += snprintf(buf + len, buf_len - len, 244 "spools entry #%d\n", i); 245 len += snprintf(buf + len, buf_len - len, 246 "\tid: %d\n", 247 credit->spools[i].id); 248 len += snprintf(buf + len, buf_len - len, 249 "\tmax_pages: %d\n", 250 credit->spools[i].max_pages); 251 len += snprintf(buf + len, buf_len - len, 252 "\talloc_pages: %d\n", 253 credit->spools[i].alloc_pages); 254 255 } 256 257 ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); 258 kfree(buf); 259 260 return ret; 261} 262 263static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp, 264 char __user *buffer, 265 size_t count, loff_t *ppos) 266{ 267 struct iwm_priv *iwm = filp->private_data; 268 struct iwm_rx_ticket_node *ticket; 269 char *buf; 270 int buf_len = 4096, i; 271 size_t len = 0; 272 ssize_t ret; 273 274 if (*ppos != 0) 275 return 0; 276 if (count < sizeof(buf)) 277 return -ENOSPC; 278 279 buf = kzalloc(buf_len, GFP_KERNEL); 280 if (!buf) 281 return -ENOMEM; 282 283 spin_lock(&iwm->ticket_lock); 284 list_for_each_entry(ticket, &iwm->rx_tickets, node) { 285 len += snprintf(buf + len, buf_len - len, "Ticket #%d\n", 286 ticket->ticket->id); 287 len += snprintf(buf + len, buf_len - len, "\taction: 0x%x\n", 288 ticket->ticket->action); 289 len += snprintf(buf + len, buf_len - len, "\tflags: 0x%x\n", 290 ticket->ticket->flags); 291 } 292 spin_unlock(&iwm->ticket_lock); 293 294 for (i = 0; i < IWM_RX_ID_HASH; i++) { 295 struct iwm_rx_packet *packet; 296 struct list_head *pkt_list = &iwm->rx_packets[i]; 297 298 if (!list_empty(pkt_list)) { 299 len += snprintf(buf + len, buf_len - len, 300 "Packet hash #%d\n", i); 301 spin_lock(&iwm->packet_lock[i]); 302 list_for_each_entry(packet, pkt_list, node) { 303 len += snprintf(buf + len, buf_len - len, 304 "\tPacket id: %d\n", 305 packet->id); 306 len += snprintf(buf + len, buf_len - len, 307 "\tPacket length: %lu\n", 308 packet->pkt_size); 309 } 310 spin_unlock(&iwm->packet_lock[i]); 311 } 312 } 313 314 ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); 315 kfree(buf); 316 317 return ret; 318} 319 320static ssize_t iwm_debugfs_fw_err_read(struct file *filp, 321 char __user *buffer, 322 size_t count, loff_t *ppos) 323{ 324 325 struct iwm_priv *iwm = filp->private_data; 326 char buf[512]; 327 int buf_len = 512; 328 size_t len = 0; 329 330 if (*ppos != 0) 331 return 0; 332 if (count < sizeof(buf)) 333 return -ENOSPC; 334 335 if (!iwm->last_fw_err) 336 return -ENOMEM; 337 338 if (iwm->last_fw_err->line_num == 0) 339 goto out; 340 341 len += snprintf(buf + len, buf_len - len, "%cMAC FW ERROR:\n", 342 (le32_to_cpu(iwm->last_fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) 343 ? 'L' : 'U'); 344 len += snprintf(buf + len, buf_len - len, 345 "\tCategory: %d\n", 346 le32_to_cpu(iwm->last_fw_err->category)); 347 348 len += snprintf(buf + len, buf_len - len, 349 "\tStatus: 0x%x\n", 350 le32_to_cpu(iwm->last_fw_err->status)); 351 352 len += snprintf(buf + len, buf_len - len, 353 "\tPC: 0x%x\n", 354 le32_to_cpu(iwm->last_fw_err->pc)); 355 356 len += snprintf(buf + len, buf_len - len, 357 "\tblink1: %d\n", 358 le32_to_cpu(iwm->last_fw_err->blink1)); 359 360 len += snprintf(buf + len, buf_len - len, 361 "\tblink2: %d\n", 362 le32_to_cpu(iwm->last_fw_err->blink2)); 363 364 len += snprintf(buf + len, buf_len - len, 365 "\tilink1: %d\n", 366 le32_to_cpu(iwm->last_fw_err->ilink1)); 367 368 len += snprintf(buf + len, buf_len - len, 369 "\tilink2: %d\n", 370 le32_to_cpu(iwm->last_fw_err->ilink2)); 371 372 len += snprintf(buf + len, buf_len - len, 373 "\tData1: 0x%x\n", 374 le32_to_cpu(iwm->last_fw_err->data1)); 375 376 len += snprintf(buf + len, buf_len - len, 377 "\tData2: 0x%x\n", 378 le32_to_cpu(iwm->last_fw_err->data2)); 379 380 len += snprintf(buf + len, buf_len - len, 381 "\tLine number: %d\n", 382 le32_to_cpu(iwm->last_fw_err->line_num)); 383 384 len += snprintf(buf + len, buf_len - len, 385 "\tUMAC status: 0x%x\n", 386 le32_to_cpu(iwm->last_fw_err->umac_status)); 387 388 len += snprintf(buf + len, buf_len - len, 389 "\tLMAC status: 0x%x\n", 390 le32_to_cpu(iwm->last_fw_err->lmac_status)); 391 392 len += snprintf(buf + len, buf_len - len, 393 "\tSDIO status: 0x%x\n", 394 le32_to_cpu(iwm->last_fw_err->sdio_status)); 395 396out: 397 398 return simple_read_from_buffer(buffer, len, ppos, buf, buf_len); 399} 400 401static const struct file_operations iwm_debugfs_txq_fops = { 402 .owner = THIS_MODULE, 403 .open = iwm_generic_open, 404 .read = iwm_debugfs_txq_read, 405}; 406 407static const struct file_operations iwm_debugfs_tx_credit_fops = { 408 .owner = THIS_MODULE, 409 .open = iwm_generic_open, 410 .read = iwm_debugfs_tx_credit_read, 411}; 412 413static const struct file_operations iwm_debugfs_rx_ticket_fops = { 414 .owner = THIS_MODULE, 415 .open = iwm_generic_open, 416 .read = iwm_debugfs_rx_ticket_read, 417}; 418 419static const struct file_operations iwm_debugfs_fw_err_fops = { 420 .owner = THIS_MODULE, 421 .open = iwm_generic_open, 422 .read = iwm_debugfs_fw_err_read, 423}; 424 425void iwm_debugfs_init(struct iwm_priv *iwm) 426{ 427 int i; 428 429 iwm->dbg.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); 430 iwm->dbg.devdir = debugfs_create_dir(wiphy_name(iwm_to_wiphy(iwm)), 431 iwm->dbg.rootdir); 432 iwm->dbg.dbgdir = debugfs_create_dir("debug", iwm->dbg.devdir); 433 iwm->dbg.rxdir = debugfs_create_dir("rx", iwm->dbg.devdir); 434 iwm->dbg.txdir = debugfs_create_dir("tx", iwm->dbg.devdir); 435 iwm->dbg.busdir = debugfs_create_dir("bus", iwm->dbg.devdir); 436 if (iwm->bus_ops->debugfs_init) 437 iwm->bus_ops->debugfs_init(iwm, iwm->dbg.busdir); 438 439 iwm->dbg.dbg_level = IWM_DL_NONE; 440 iwm->dbg.dbg_level_dentry = 441 debugfs_create_file("level", 0200, iwm->dbg.dbgdir, iwm, 442 &fops_iwm_dbg_level); 443 444 iwm->dbg.dbg_modules = IWM_DM_DEFAULT; 445 iwm->dbg.dbg_modules_dentry = 446 debugfs_create_file("modules", 0200, iwm->dbg.dbgdir, iwm, 447 &fops_iwm_dbg_modules); 448 449 for (i = 0; i < __IWM_DM_NR; i++) 450 add_dbg_module(iwm->dbg, iwm_debug_module[i].name, 451 iwm_debug_module[i].id, IWM_DL_DEFAULT); 452 453 iwm->dbg.txq_dentry = debugfs_create_file("queues", 0200, 454 iwm->dbg.txdir, iwm, 455 &iwm_debugfs_txq_fops); 456 iwm->dbg.tx_credit_dentry = debugfs_create_file("credits", 0200, 457 iwm->dbg.txdir, iwm, 458 &iwm_debugfs_tx_credit_fops); 459 iwm->dbg.rx_ticket_dentry = debugfs_create_file("tickets", 0200, 460 iwm->dbg.rxdir, iwm, 461 &iwm_debugfs_rx_ticket_fops); 462 iwm->dbg.fw_err_dentry = debugfs_create_file("last_fw_err", 0200, 463 iwm->dbg.dbgdir, iwm, 464 &iwm_debugfs_fw_err_fops); 465} 466 467void iwm_debugfs_exit(struct iwm_priv *iwm) 468{ 469 int i; 470 471 for (i = 0; i < __IWM_DM_NR; i++) 472 debugfs_remove(iwm->dbg.dbg_module_dentries[i]); 473 474 debugfs_remove(iwm->dbg.dbg_modules_dentry); 475 debugfs_remove(iwm->dbg.dbg_level_dentry); 476 debugfs_remove(iwm->dbg.txq_dentry); 477 debugfs_remove(iwm->dbg.tx_credit_dentry); 478 debugfs_remove(iwm->dbg.rx_ticket_dentry); 479 debugfs_remove(iwm->dbg.fw_err_dentry); 480 if (iwm->bus_ops->debugfs_exit) 481 iwm->bus_ops->debugfs_exit(iwm); 482 483 debugfs_remove(iwm->dbg.busdir); 484 debugfs_remove(iwm->dbg.dbgdir); 485 debugfs_remove(iwm->dbg.txdir); 486 debugfs_remove(iwm->dbg.rxdir); 487 debugfs_remove(iwm->dbg.devdir); 488 debugfs_remove(iwm->dbg.rootdir); 489} 490