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/slab.h>
25 #include <linux/kernel.h>
26 #include <linux/bitops.h>
27 #include <linux/debugfs.h>
37 } iwm_debug_module
[__IWM_DM_NR
] = {
38 {IWM_DM_BOOT
, "boot"},
40 {IWM_DM_SDIO
, "sdio"},
44 {IWM_DM_MLME
, "mlme"},
46 {IWM_DM_WEXT
, "wext"},
49 #define add_dbg_module(dbg, name, id, initlevel) \
52 dbg.dbg_module[id] = (initlevel); \
53 d = debugfs_create_x8(name, 0600, dbg.dbgdir, \
54 &(dbg.dbg_module[id])); \
56 dbg.dbg_module_dentries[id] = d; \
59 static int iwm_debugfs_u32_read(void *data
, u64
*val
)
61 struct iwm_priv
*iwm
= data
;
63 *val
= iwm
->dbg
.dbg_level
;
67 static int iwm_debugfs_dbg_level_write(void *data
, u64 val
)
69 struct iwm_priv
*iwm
= data
;
72 iwm
->dbg
.dbg_level
= val
;
74 for (i
= 0; i
< __IWM_DM_NR
; i
++)
75 iwm
->dbg
.dbg_module
[i
] = val
;
79 DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_level
,
80 iwm_debugfs_u32_read
, iwm_debugfs_dbg_level_write
,
83 static int iwm_debugfs_dbg_modules_write(void *data
, u64 val
)
85 struct iwm_priv
*iwm
= data
;
88 iwm
->dbg
.dbg_modules
= val
;
90 for (i
= 0; i
< __IWM_DM_NR
; i
++)
91 iwm
->dbg
.dbg_module
[i
] = 0;
93 for_each_set_bit(bit
, &iwm
->dbg
.dbg_modules
, __IWM_DM_NR
)
94 iwm
->dbg
.dbg_module
[bit
] = iwm
->dbg
.dbg_level
;
98 DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules
,
99 iwm_debugfs_u32_read
, iwm_debugfs_dbg_modules_write
,
102 static int iwm_generic_open(struct inode
*inode
, struct file
*filp
)
104 filp
->private_data
= inode
->i_private
;
109 static ssize_t
iwm_debugfs_txq_read(struct file
*filp
, char __user
*buffer
,
110 size_t count
, loff_t
*ppos
)
112 struct iwm_priv
*iwm
= filp
->private_data
;
114 int i
, buf_len
= 4096;
120 if (count
< sizeof(buf
))
123 buf
= kzalloc(buf_len
, GFP_KERNEL
);
127 for (i
= 0; i
< IWM_TX_QUEUES
; i
++) {
128 struct iwm_tx_queue
*txq
= &iwm
->txq
[i
];
133 spin_lock_irqsave(&txq
->queue
.lock
, flags
);
135 skb
= (struct sk_buff
*)&txq
->queue
;
137 len
+= snprintf(buf
+ len
, buf_len
- len
, "TXQ #%d\n", i
);
138 len
+= snprintf(buf
+ len
, buf_len
- len
, "\tStopped: %d\n",
139 __netif_subqueue_stopped(iwm_to_ndev(iwm
),
141 len
+= snprintf(buf
+ len
, buf_len
- len
, "\tConcat count:%d\n",
143 len
+= snprintf(buf
+ len
, buf_len
- len
, "\tQueue len: %d\n",
144 skb_queue_len(&txq
->queue
));
145 for (j
= 0; j
< skb_queue_len(&txq
->queue
); j
++) {
146 struct iwm_tx_info
*tx_info
;
149 tx_info
= skb_to_tx_info(skb
);
151 len
+= snprintf(buf
+ len
, buf_len
- len
,
153 len
+= snprintf(buf
+ len
, buf_len
- len
,
154 "\t\tsta: %d\n", tx_info
->sta
);
155 len
+= snprintf(buf
+ len
, buf_len
- len
,
156 "\t\tcolor: %d\n", tx_info
->color
);
157 len
+= snprintf(buf
+ len
, buf_len
- len
,
158 "\t\ttid: %d\n", tx_info
->tid
);
161 spin_unlock_irqrestore(&txq
->queue
.lock
, flags
);
163 spin_lock_irqsave(&txq
->stopped_queue
.lock
, flags
);
165 len
+= snprintf(buf
+ len
, buf_len
- len
,
166 "\tStopped Queue len: %d\n",
167 skb_queue_len(&txq
->stopped_queue
));
168 for (j
= 0; j
< skb_queue_len(&txq
->stopped_queue
); j
++) {
169 struct iwm_tx_info
*tx_info
;
172 tx_info
= skb_to_tx_info(skb
);
174 len
+= snprintf(buf
+ len
, buf_len
- len
,
176 len
+= snprintf(buf
+ len
, buf_len
- len
,
177 "\t\tsta: %d\n", tx_info
->sta
);
178 len
+= snprintf(buf
+ len
, buf_len
- len
,
179 "\t\tcolor: %d\n", tx_info
->color
);
180 len
+= snprintf(buf
+ len
, buf_len
- len
,
181 "\t\ttid: %d\n", tx_info
->tid
);
184 spin_unlock_irqrestore(&txq
->stopped_queue
.lock
, flags
);
187 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
193 static ssize_t
iwm_debugfs_tx_credit_read(struct file
*filp
,
195 size_t count
, loff_t
*ppos
)
197 struct iwm_priv
*iwm
= filp
->private_data
;
198 struct iwm_tx_credit
*credit
= &iwm
->tx_credit
;
200 int i
, buf_len
= 4096;
206 if (count
< sizeof(buf
))
209 buf
= kzalloc(buf_len
, GFP_KERNEL
);
213 len
+= snprintf(buf
+ len
, buf_len
- len
,
214 "NR pools: %d\n", credit
->pool_nr
);
215 len
+= snprintf(buf
+ len
, buf_len
- len
,
216 "pools map: 0x%lx\n", credit
->full_pools_map
);
218 len
+= snprintf(buf
+ len
, buf_len
- len
, "\n### POOLS ###\n");
219 for (i
= 0; i
< IWM_MACS_OUT_GROUPS
; i
++) {
220 len
+= snprintf(buf
+ len
, buf_len
- len
,
221 "pools entry #%d\n", i
);
222 len
+= snprintf(buf
+ len
, buf_len
- len
,
224 credit
->pools
[i
].id
);
225 len
+= snprintf(buf
+ len
, buf_len
- len
,
227 credit
->pools
[i
].sid
);
228 len
+= snprintf(buf
+ len
, buf_len
- len
,
230 credit
->pools
[i
].min_pages
);
231 len
+= snprintf(buf
+ len
, buf_len
- len
,
233 credit
->pools
[i
].max_pages
);
234 len
+= snprintf(buf
+ len
, buf_len
- len
,
235 "\talloc_pages: %d\n",
236 credit
->pools
[i
].alloc_pages
);
237 len
+= snprintf(buf
+ len
, buf_len
- len
,
238 "\tfreed_pages: %d\n",
239 credit
->pools
[i
].total_freed_pages
);
242 len
+= snprintf(buf
+ len
, buf_len
- len
, "\n### SPOOLS ###\n");
243 for (i
= 0; i
< IWM_MACS_OUT_SGROUPS
; i
++) {
244 len
+= snprintf(buf
+ len
, buf_len
- len
,
245 "spools entry #%d\n", i
);
246 len
+= snprintf(buf
+ len
, buf_len
- len
,
248 credit
->spools
[i
].id
);
249 len
+= snprintf(buf
+ len
, buf_len
- len
,
251 credit
->spools
[i
].max_pages
);
252 len
+= snprintf(buf
+ len
, buf_len
- len
,
253 "\talloc_pages: %d\n",
254 credit
->spools
[i
].alloc_pages
);
258 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
264 static ssize_t
iwm_debugfs_rx_ticket_read(struct file
*filp
,
266 size_t count
, loff_t
*ppos
)
268 struct iwm_priv
*iwm
= filp
->private_data
;
269 struct iwm_rx_ticket_node
*ticket
, *next
;
271 int buf_len
= 4096, i
;
277 if (count
< sizeof(buf
))
280 buf
= kzalloc(buf_len
, GFP_KERNEL
);
284 list_for_each_entry_safe(ticket
, next
, &iwm
->rx_tickets
, node
) {
285 len
+= snprintf(buf
+ len
, buf_len
- len
, "Ticket #%d\n",
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
);
293 for (i
= 0; i
< IWM_RX_ID_HASH
; i
++) {
294 struct iwm_rx_packet
*packet
, *nxt
;
295 struct list_head
*pkt_list
= &iwm
->rx_packets
[i
];
296 if (!list_empty(pkt_list
)) {
297 len
+= snprintf(buf
+ len
, buf_len
- len
,
298 "Packet hash #%d\n", i
);
299 list_for_each_entry_safe(packet
, nxt
, pkt_list
, node
) {
300 len
+= snprintf(buf
+ len
, buf_len
- len
,
303 len
+= snprintf(buf
+ len
, buf_len
- len
,
304 "\tPacket length: %lu\n",
310 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
316 static ssize_t
iwm_debugfs_fw_err_read(struct file
*filp
,
318 size_t count
, loff_t
*ppos
)
321 struct iwm_priv
*iwm
= filp
->private_data
;
328 if (count
< sizeof(buf
))
331 if (!iwm
->last_fw_err
)
334 if (iwm
->last_fw_err
->line_num
== 0)
337 len
+= snprintf(buf
+ len
, buf_len
- len
, "%cMAC FW ERROR:\n",
338 (le32_to_cpu(iwm
->last_fw_err
->category
) == UMAC_SYS_ERR_CAT_LMAC
)
340 len
+= snprintf(buf
+ len
, buf_len
- len
,
342 le32_to_cpu(iwm
->last_fw_err
->category
));
344 len
+= snprintf(buf
+ len
, buf_len
- len
,
346 le32_to_cpu(iwm
->last_fw_err
->status
));
348 len
+= snprintf(buf
+ len
, buf_len
- len
,
350 le32_to_cpu(iwm
->last_fw_err
->pc
));
352 len
+= snprintf(buf
+ len
, buf_len
- len
,
354 le32_to_cpu(iwm
->last_fw_err
->blink1
));
356 len
+= snprintf(buf
+ len
, buf_len
- len
,
358 le32_to_cpu(iwm
->last_fw_err
->blink2
));
360 len
+= snprintf(buf
+ len
, buf_len
- len
,
362 le32_to_cpu(iwm
->last_fw_err
->ilink1
));
364 len
+= snprintf(buf
+ len
, buf_len
- len
,
366 le32_to_cpu(iwm
->last_fw_err
->ilink2
));
368 len
+= snprintf(buf
+ len
, buf_len
- len
,
370 le32_to_cpu(iwm
->last_fw_err
->data1
));
372 len
+= snprintf(buf
+ len
, buf_len
- len
,
374 le32_to_cpu(iwm
->last_fw_err
->data2
));
376 len
+= snprintf(buf
+ len
, buf_len
- len
,
377 "\tLine number: %d\n",
378 le32_to_cpu(iwm
->last_fw_err
->line_num
));
380 len
+= snprintf(buf
+ len
, buf_len
- len
,
381 "\tUMAC status: 0x%x\n",
382 le32_to_cpu(iwm
->last_fw_err
->umac_status
));
384 len
+= snprintf(buf
+ len
, buf_len
- len
,
385 "\tLMAC status: 0x%x\n",
386 le32_to_cpu(iwm
->last_fw_err
->lmac_status
));
388 len
+= snprintf(buf
+ len
, buf_len
- len
,
389 "\tSDIO status: 0x%x\n",
390 le32_to_cpu(iwm
->last_fw_err
->sdio_status
));
394 return simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
397 static const struct file_operations iwm_debugfs_txq_fops
= {
398 .owner
= THIS_MODULE
,
399 .open
= iwm_generic_open
,
400 .read
= iwm_debugfs_txq_read
,
403 static const struct file_operations iwm_debugfs_tx_credit_fops
= {
404 .owner
= THIS_MODULE
,
405 .open
= iwm_generic_open
,
406 .read
= iwm_debugfs_tx_credit_read
,
409 static const struct file_operations iwm_debugfs_rx_ticket_fops
= {
410 .owner
= THIS_MODULE
,
411 .open
= iwm_generic_open
,
412 .read
= iwm_debugfs_rx_ticket_read
,
415 static const struct file_operations iwm_debugfs_fw_err_fops
= {
416 .owner
= THIS_MODULE
,
417 .open
= iwm_generic_open
,
418 .read
= iwm_debugfs_fw_err_read
,
421 int iwm_debugfs_init(struct iwm_priv
*iwm
)
426 iwm
->dbg
.rootdir
= debugfs_create_dir(KBUILD_MODNAME
, NULL
);
427 result
= PTR_ERR(iwm
->dbg
.rootdir
);
428 if (!result
|| IS_ERR(iwm
->dbg
.rootdir
)) {
429 if (result
== -ENODEV
) {
430 IWM_ERR(iwm
, "DebugFS (CONFIG_DEBUG_FS) not "
431 "enabled in kernel config\n");
432 result
= 0; /* No debugfs support */
434 IWM_ERR(iwm
, "Couldn't create rootdir: %d\n", result
);
438 snprintf(devdir
, sizeof(devdir
), "%s", wiphy_name(iwm_to_wiphy(iwm
)));
440 iwm
->dbg
.devdir
= debugfs_create_dir(devdir
, iwm
->dbg
.rootdir
);
441 result
= PTR_ERR(iwm
->dbg
.devdir
);
442 if (IS_ERR(iwm
->dbg
.devdir
) && (result
!= -ENODEV
)) {
443 IWM_ERR(iwm
, "Couldn't create devdir: %d\n", result
);
447 iwm
->dbg
.dbgdir
= debugfs_create_dir("debug", iwm
->dbg
.devdir
);
448 result
= PTR_ERR(iwm
->dbg
.dbgdir
);
449 if (IS_ERR(iwm
->dbg
.dbgdir
) && (result
!= -ENODEV
)) {
450 IWM_ERR(iwm
, "Couldn't create dbgdir: %d\n", result
);
454 iwm
->dbg
.rxdir
= debugfs_create_dir("rx", iwm
->dbg
.devdir
);
455 result
= PTR_ERR(iwm
->dbg
.rxdir
);
456 if (IS_ERR(iwm
->dbg
.rxdir
) && (result
!= -ENODEV
)) {
457 IWM_ERR(iwm
, "Couldn't create rx dir: %d\n", result
);
461 iwm
->dbg
.txdir
= debugfs_create_dir("tx", iwm
->dbg
.devdir
);
462 result
= PTR_ERR(iwm
->dbg
.txdir
);
463 if (IS_ERR(iwm
->dbg
.txdir
) && (result
!= -ENODEV
)) {
464 IWM_ERR(iwm
, "Couldn't create tx dir: %d\n", result
);
468 iwm
->dbg
.busdir
= debugfs_create_dir("bus", iwm
->dbg
.devdir
);
469 result
= PTR_ERR(iwm
->dbg
.busdir
);
470 if (IS_ERR(iwm
->dbg
.busdir
) && (result
!= -ENODEV
)) {
471 IWM_ERR(iwm
, "Couldn't create bus dir: %d\n", result
);
475 if (iwm
->bus_ops
->debugfs_init
) {
476 result
= iwm
->bus_ops
->debugfs_init(iwm
, iwm
->dbg
.busdir
);
478 IWM_ERR(iwm
, "Couldn't create bus entry: %d\n", result
);
484 iwm
->dbg
.dbg_level
= IWM_DL_NONE
;
485 iwm
->dbg
.dbg_level_dentry
=
486 debugfs_create_file("level", 0200, iwm
->dbg
.dbgdir
, iwm
,
487 &fops_iwm_dbg_level
);
488 result
= PTR_ERR(iwm
->dbg
.dbg_level_dentry
);
489 if (IS_ERR(iwm
->dbg
.dbg_level_dentry
) && (result
!= -ENODEV
)) {
490 IWM_ERR(iwm
, "Couldn't create dbg_level: %d\n", result
);
495 iwm
->dbg
.dbg_modules
= IWM_DM_DEFAULT
;
496 iwm
->dbg
.dbg_modules_dentry
=
497 debugfs_create_file("modules", 0200, iwm
->dbg
.dbgdir
, iwm
,
498 &fops_iwm_dbg_modules
);
499 result
= PTR_ERR(iwm
->dbg
.dbg_modules_dentry
);
500 if (IS_ERR(iwm
->dbg
.dbg_modules_dentry
) && (result
!= -ENODEV
)) {
501 IWM_ERR(iwm
, "Couldn't create dbg_modules: %d\n", result
);
505 for (i
= 0; i
< __IWM_DM_NR
; i
++)
506 add_dbg_module(iwm
->dbg
, iwm_debug_module
[i
].name
,
507 iwm_debug_module
[i
].id
, IWM_DL_DEFAULT
);
509 iwm
->dbg
.txq_dentry
= debugfs_create_file("queues", 0200,
511 &iwm_debugfs_txq_fops
);
512 result
= PTR_ERR(iwm
->dbg
.txq_dentry
);
513 if (IS_ERR(iwm
->dbg
.txq_dentry
) && (result
!= -ENODEV
)) {
514 IWM_ERR(iwm
, "Couldn't create tx queue: %d\n", result
);
518 iwm
->dbg
.tx_credit_dentry
= debugfs_create_file("credits", 0200,
520 &iwm_debugfs_tx_credit_fops
);
521 result
= PTR_ERR(iwm
->dbg
.tx_credit_dentry
);
522 if (IS_ERR(iwm
->dbg
.tx_credit_dentry
) && (result
!= -ENODEV
)) {
523 IWM_ERR(iwm
, "Couldn't create tx credit: %d\n", result
);
527 iwm
->dbg
.rx_ticket_dentry
= debugfs_create_file("tickets", 0200,
529 &iwm_debugfs_rx_ticket_fops
);
530 result
= PTR_ERR(iwm
->dbg
.rx_ticket_dentry
);
531 if (IS_ERR(iwm
->dbg
.rx_ticket_dentry
) && (result
!= -ENODEV
)) {
532 IWM_ERR(iwm
, "Couldn't create rx ticket: %d\n", result
);
536 iwm
->dbg
.fw_err_dentry
= debugfs_create_file("last_fw_err", 0200,
537 iwm
->dbg
.dbgdir
, iwm
,
538 &iwm_debugfs_fw_err_fops
);
539 result
= PTR_ERR(iwm
->dbg
.fw_err_dentry
);
540 if (IS_ERR(iwm
->dbg
.fw_err_dentry
) && (result
!= -ENODEV
)) {
541 IWM_ERR(iwm
, "Couldn't create last FW err: %d\n", result
);
552 void iwm_debugfs_exit(struct iwm_priv
*iwm
)
556 for (i
= 0; i
< __IWM_DM_NR
; i
++)
557 debugfs_remove(iwm
->dbg
.dbg_module_dentries
[i
]);
559 debugfs_remove(iwm
->dbg
.dbg_modules_dentry
);
560 debugfs_remove(iwm
->dbg
.dbg_level_dentry
);
561 debugfs_remove(iwm
->dbg
.txq_dentry
);
562 debugfs_remove(iwm
->dbg
.tx_credit_dentry
);
563 debugfs_remove(iwm
->dbg
.rx_ticket_dentry
);
564 debugfs_remove(iwm
->dbg
.fw_err_dentry
);
565 if (iwm
->bus_ops
->debugfs_exit
)
566 iwm
->bus_ops
->debugfs_exit(iwm
);
568 debugfs_remove(iwm
->dbg
.busdir
);
569 debugfs_remove(iwm
->dbg
.dbgdir
);
570 debugfs_remove(iwm
->dbg
.txdir
);
571 debugfs_remove(iwm
->dbg
.rxdir
);
572 debugfs_remove(iwm
->dbg
.devdir
);
573 debugfs_remove(iwm
->dbg
.rootdir
);