2 * Intel Wireless Multicomm 3200 WiFi driver
4 * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
5 * Samuel Ortiz <samuel.ortiz@intel.com>
6 * Zhu Yi <yi.zhu@intel.com>
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.
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.
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
24 #include <linux/kernel.h>
25 #include <linux/bitops.h>
26 #include <linux/debugfs.h>
36 } iwm_debug_module
[__IWM_DM_NR
] = {
37 {IWM_DM_BOOT
, "boot"},
39 {IWM_DM_SDIO
, "sdio"},
43 {IWM_DM_MLME
, "mlme"},
45 {IWM_DM_WEXT
, "wext"},
48 #define add_dbg_module(dbg, name, id, initlevel) \
51 dbg.dbg_module[id] = (initlevel); \
52 d = debugfs_create_x8(name, 0600, dbg.dbgdir, \
53 &(dbg.dbg_module[id])); \
55 dbg.dbg_module_dentries[id] = d; \
58 static int iwm_debugfs_u32_read(void *data
, u64
*val
)
60 struct iwm_priv
*iwm
= data
;
62 *val
= iwm
->dbg
.dbg_level
;
66 static int iwm_debugfs_dbg_level_write(void *data
, u64 val
)
68 struct iwm_priv
*iwm
= data
;
71 iwm
->dbg
.dbg_level
= val
;
73 for (i
= 0; i
< __IWM_DM_NR
; i
++)
74 iwm
->dbg
.dbg_module
[i
] = val
;
78 DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_level
,
79 iwm_debugfs_u32_read
, iwm_debugfs_dbg_level_write
,
82 static int iwm_debugfs_dbg_modules_write(void *data
, u64 val
)
84 struct iwm_priv
*iwm
= data
;
87 iwm
->dbg
.dbg_modules
= val
;
89 for (i
= 0; i
< __IWM_DM_NR
; i
++)
90 iwm
->dbg
.dbg_module
[i
] = 0;
92 for_each_bit(bit
, &iwm
->dbg
.dbg_modules
, __IWM_DM_NR
)
93 iwm
->dbg
.dbg_module
[bit
] = iwm
->dbg
.dbg_level
;
97 DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules
,
98 iwm_debugfs_u32_read
, iwm_debugfs_dbg_modules_write
,
101 static int iwm_generic_open(struct inode
*inode
, struct file
*filp
)
103 filp
->private_data
= inode
->i_private
;
108 static ssize_t
iwm_debugfs_txq_read(struct file
*filp
, char __user
*buffer
,
109 size_t count
, loff_t
*ppos
)
111 struct iwm_priv
*iwm
= filp
->private_data
;
113 int i
, buf_len
= 4096;
119 if (count
< sizeof(buf
))
122 buf
= kzalloc(buf_len
, GFP_KERNEL
);
126 for (i
= 0; i
< IWM_TX_QUEUES
; i
++) {
127 struct iwm_tx_queue
*txq
= &iwm
->txq
[i
];
132 spin_lock_irqsave(&txq
->queue
.lock
, flags
);
134 skb
= (struct sk_buff
*)&txq
->queue
;
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
),
140 len
+= snprintf(buf
+ len
, buf_len
- len
, "\tConcat count:%d\n",
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
;
148 tx_info
= skb_to_tx_info(skb
);
150 len
+= snprintf(buf
+ len
, buf_len
- len
,
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
);
160 spin_unlock_irqrestore(&txq
->queue
.lock
, flags
);
163 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
169 static ssize_t
iwm_debugfs_tx_credit_read(struct file
*filp
,
171 size_t count
, loff_t
*ppos
)
173 struct iwm_priv
*iwm
= filp
->private_data
;
174 struct iwm_tx_credit
*credit
= &iwm
->tx_credit
;
176 int i
, buf_len
= 4096;
182 if (count
< sizeof(buf
))
185 buf
= kzalloc(buf_len
, GFP_KERNEL
);
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
);
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
,
200 credit
->pools
[i
].id
);
201 len
+= snprintf(buf
+ len
, buf_len
- len
,
203 credit
->pools
[i
].sid
);
204 len
+= snprintf(buf
+ len
, buf_len
- len
,
206 credit
->pools
[i
].min_pages
);
207 len
+= snprintf(buf
+ len
, buf_len
- len
,
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
);
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
,
224 credit
->spools
[i
].id
);
225 len
+= snprintf(buf
+ len
, buf_len
- len
,
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
);
234 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
240 static ssize_t
iwm_debugfs_rx_ticket_read(struct file
*filp
,
242 size_t count
, loff_t
*ppos
)
244 struct iwm_priv
*iwm
= filp
->private_data
;
245 struct iwm_rx_ticket_node
*ticket
, *next
;
247 int buf_len
= 4096, i
;
253 if (count
< sizeof(buf
))
256 buf
= kzalloc(buf_len
, GFP_KERNEL
);
260 list_for_each_entry_safe(ticket
, next
, &iwm
->rx_tickets
, node
) {
261 len
+= snprintf(buf
+ len
, buf_len
- len
, "Ticket #%d\n",
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
);
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
,
279 len
+= snprintf(buf
+ len
, buf_len
- len
,
280 "\tPacket length: %lu\n",
286 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
292 static ssize_t
iwm_debugfs_fw_err_read(struct file
*filp
,
294 size_t count
, loff_t
*ppos
)
297 struct iwm_priv
*iwm
= filp
->private_data
;
304 if (count
< sizeof(buf
))
307 if (!iwm
->last_fw_err
)
310 if (iwm
->last_fw_err
->line_num
== 0)
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
)
316 len
+= snprintf(buf
+ len
, buf_len
- len
,
318 le32_to_cpu(iwm
->last_fw_err
->category
));
320 len
+= snprintf(buf
+ len
, buf_len
- len
,
322 le32_to_cpu(iwm
->last_fw_err
->status
));
324 len
+= snprintf(buf
+ len
, buf_len
- len
,
326 le32_to_cpu(iwm
->last_fw_err
->pc
));
328 len
+= snprintf(buf
+ len
, buf_len
- len
,
330 le32_to_cpu(iwm
->last_fw_err
->blink1
));
332 len
+= snprintf(buf
+ len
, buf_len
- len
,
334 le32_to_cpu(iwm
->last_fw_err
->blink2
));
336 len
+= snprintf(buf
+ len
, buf_len
- len
,
338 le32_to_cpu(iwm
->last_fw_err
->ilink1
));
340 len
+= snprintf(buf
+ len
, buf_len
- len
,
342 le32_to_cpu(iwm
->last_fw_err
->ilink2
));
344 len
+= snprintf(buf
+ len
, buf_len
- len
,
346 le32_to_cpu(iwm
->last_fw_err
->data1
));
348 len
+= snprintf(buf
+ len
, buf_len
- len
,
350 le32_to_cpu(iwm
->last_fw_err
->data2
));
352 len
+= snprintf(buf
+ len
, buf_len
- len
,
353 "\tLine number: %d\n",
354 le32_to_cpu(iwm
->last_fw_err
->line_num
));
356 len
+= snprintf(buf
+ len
, buf_len
- len
,
357 "\tUMAC status: 0x%x\n",
358 le32_to_cpu(iwm
->last_fw_err
->umac_status
));
360 len
+= snprintf(buf
+ len
, buf_len
- len
,
361 "\tLMAC status: 0x%x\n",
362 le32_to_cpu(iwm
->last_fw_err
->lmac_status
));
364 len
+= snprintf(buf
+ len
, buf_len
- len
,
365 "\tSDIO status: 0x%x\n",
366 le32_to_cpu(iwm
->last_fw_err
->sdio_status
));
370 return simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
373 static const struct file_operations iwm_debugfs_txq_fops
= {
374 .owner
= THIS_MODULE
,
375 .open
= iwm_generic_open
,
376 .read
= iwm_debugfs_txq_read
,
379 static 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
,
385 static 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
,
391 static 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
,
397 int iwm_debugfs_init(struct iwm_priv
*iwm
)
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 */
410 IWM_ERR(iwm
, "Couldn't create rootdir: %d\n", result
);
414 snprintf(devdir
, sizeof(devdir
), "%s", wiphy_name(iwm_to_wiphy(iwm
)));
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
);
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
);
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
);
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
);
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
);
451 if (iwm
->bus_ops
->debugfs_init
) {
452 result
= iwm
->bus_ops
->debugfs_init(iwm
, iwm
->dbg
.busdir
);
454 IWM_ERR(iwm
, "Couldn't create bus entry: %d\n", result
);
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
);
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
);
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
);
485 iwm
->dbg
.txq_dentry
= debugfs_create_file("queues", 0200,
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
);
494 iwm
->dbg
.tx_credit_dentry
= debugfs_create_file("credits", 0200,
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
);
503 iwm
->dbg
.rx_ticket_dentry
= debugfs_create_file("tickets", 0200,
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
);
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
);
528 void iwm_debugfs_exit(struct iwm_priv
*iwm
)
532 for (i
= 0; i
< __IWM_DM_NR
; i
++)
533 debugfs_remove(iwm
->dbg
.dbg_module_dentries
[i
]);
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
);
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
);