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) \
51 dbg.dbg_module[id] = (initlevel); \
52 dbg.dbg_module_dentries[id] = \
53 debugfs_create_x8(name, 0600, \
55 &(dbg.dbg_module[id])); \
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_set_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
);
162 spin_lock_irqsave(&txq
->stopped_queue
.lock
, flags
);
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
;
171 tx_info
= skb_to_tx_info(skb
);
173 len
+= snprintf(buf
+ len
, buf_len
- len
,
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
);
183 spin_unlock_irqrestore(&txq
->stopped_queue
.lock
, flags
);
186 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
192 static ssize_t
iwm_debugfs_tx_credit_read(struct file
*filp
,
194 size_t count
, loff_t
*ppos
)
196 struct iwm_priv
*iwm
= filp
->private_data
;
197 struct iwm_tx_credit
*credit
= &iwm
->tx_credit
;
199 int i
, buf_len
= 4096;
205 if (count
< sizeof(buf
))
208 buf
= kzalloc(buf_len
, GFP_KERNEL
);
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
);
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
,
223 credit
->pools
[i
].id
);
224 len
+= snprintf(buf
+ len
, buf_len
- len
,
226 credit
->pools
[i
].sid
);
227 len
+= snprintf(buf
+ len
, buf_len
- len
,
229 credit
->pools
[i
].min_pages
);
230 len
+= snprintf(buf
+ len
, buf_len
- len
,
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
);
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
,
247 credit
->spools
[i
].id
);
248 len
+= snprintf(buf
+ len
, buf_len
- len
,
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
);
257 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
263 static ssize_t
iwm_debugfs_rx_ticket_read(struct file
*filp
,
265 size_t count
, loff_t
*ppos
)
267 struct iwm_priv
*iwm
= filp
->private_data
;
268 struct iwm_rx_ticket_node
*ticket
;
270 int buf_len
= 4096, i
;
276 if (count
< sizeof(buf
))
279 buf
= kzalloc(buf_len
, GFP_KERNEL
);
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",
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
);
292 spin_unlock(&iwm
->ticket_lock
);
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
];
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
,
306 len
+= snprintf(buf
+ len
, buf_len
- len
,
307 "\tPacket length: %lu\n",
310 spin_unlock(&iwm
->packet_lock
[i
]);
314 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
320 static ssize_t
iwm_debugfs_fw_err_read(struct file
*filp
,
322 size_t count
, loff_t
*ppos
)
325 struct iwm_priv
*iwm
= filp
->private_data
;
332 if (count
< sizeof(buf
))
335 if (!iwm
->last_fw_err
)
338 if (iwm
->last_fw_err
->line_num
== 0)
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
)
344 len
+= snprintf(buf
+ len
, buf_len
- len
,
346 le32_to_cpu(iwm
->last_fw_err
->category
));
348 len
+= snprintf(buf
+ len
, buf_len
- len
,
350 le32_to_cpu(iwm
->last_fw_err
->status
));
352 len
+= snprintf(buf
+ len
, buf_len
- len
,
354 le32_to_cpu(iwm
->last_fw_err
->pc
));
356 len
+= snprintf(buf
+ len
, buf_len
- len
,
358 le32_to_cpu(iwm
->last_fw_err
->blink1
));
360 len
+= snprintf(buf
+ len
, buf_len
- len
,
362 le32_to_cpu(iwm
->last_fw_err
->blink2
));
364 len
+= snprintf(buf
+ len
, buf_len
- len
,
366 le32_to_cpu(iwm
->last_fw_err
->ilink1
));
368 len
+= snprintf(buf
+ len
, buf_len
- len
,
370 le32_to_cpu(iwm
->last_fw_err
->ilink2
));
372 len
+= snprintf(buf
+ len
, buf_len
- len
,
374 le32_to_cpu(iwm
->last_fw_err
->data1
));
376 len
+= snprintf(buf
+ len
, buf_len
- len
,
378 le32_to_cpu(iwm
->last_fw_err
->data2
));
380 len
+= snprintf(buf
+ len
, buf_len
- len
,
381 "\tLine number: %d\n",
382 le32_to_cpu(iwm
->last_fw_err
->line_num
));
384 len
+= snprintf(buf
+ len
, buf_len
- len
,
385 "\tUMAC status: 0x%x\n",
386 le32_to_cpu(iwm
->last_fw_err
->umac_status
));
388 len
+= snprintf(buf
+ len
, buf_len
- len
,
389 "\tLMAC status: 0x%x\n",
390 le32_to_cpu(iwm
->last_fw_err
->lmac_status
));
392 len
+= snprintf(buf
+ len
, buf_len
- len
,
393 "\tSDIO status: 0x%x\n",
394 le32_to_cpu(iwm
->last_fw_err
->sdio_status
));
398 return simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
401 static const struct file_operations iwm_debugfs_txq_fops
= {
402 .owner
= THIS_MODULE
,
403 .open
= iwm_generic_open
,
404 .read
= iwm_debugfs_txq_read
,
405 .llseek
= default_llseek
,
408 static const struct file_operations iwm_debugfs_tx_credit_fops
= {
409 .owner
= THIS_MODULE
,
410 .open
= iwm_generic_open
,
411 .read
= iwm_debugfs_tx_credit_read
,
412 .llseek
= default_llseek
,
415 static const struct file_operations iwm_debugfs_rx_ticket_fops
= {
416 .owner
= THIS_MODULE
,
417 .open
= iwm_generic_open
,
418 .read
= iwm_debugfs_rx_ticket_read
,
419 .llseek
= default_llseek
,
422 static const struct file_operations iwm_debugfs_fw_err_fops
= {
423 .owner
= THIS_MODULE
,
424 .open
= iwm_generic_open
,
425 .read
= iwm_debugfs_fw_err_read
,
426 .llseek
= default_llseek
,
429 void iwm_debugfs_init(struct iwm_priv
*iwm
)
433 iwm
->dbg
.rootdir
= debugfs_create_dir(KBUILD_MODNAME
, NULL
);
434 iwm
->dbg
.devdir
= debugfs_create_dir(wiphy_name(iwm_to_wiphy(iwm
)),
436 iwm
->dbg
.dbgdir
= debugfs_create_dir("debug", iwm
->dbg
.devdir
);
437 iwm
->dbg
.rxdir
= debugfs_create_dir("rx", iwm
->dbg
.devdir
);
438 iwm
->dbg
.txdir
= debugfs_create_dir("tx", iwm
->dbg
.devdir
);
439 iwm
->dbg
.busdir
= debugfs_create_dir("bus", iwm
->dbg
.devdir
);
440 if (iwm
->bus_ops
->debugfs_init
)
441 iwm
->bus_ops
->debugfs_init(iwm
, iwm
->dbg
.busdir
);
443 iwm
->dbg
.dbg_level
= IWM_DL_NONE
;
444 iwm
->dbg
.dbg_level_dentry
=
445 debugfs_create_file("level", 0200, iwm
->dbg
.dbgdir
, iwm
,
446 &fops_iwm_dbg_level
);
448 iwm
->dbg
.dbg_modules
= IWM_DM_DEFAULT
;
449 iwm
->dbg
.dbg_modules_dentry
=
450 debugfs_create_file("modules", 0200, iwm
->dbg
.dbgdir
, iwm
,
451 &fops_iwm_dbg_modules
);
453 for (i
= 0; i
< __IWM_DM_NR
; i
++)
454 add_dbg_module(iwm
->dbg
, iwm_debug_module
[i
].name
,
455 iwm_debug_module
[i
].id
, IWM_DL_DEFAULT
);
457 iwm
->dbg
.txq_dentry
= debugfs_create_file("queues", 0200,
459 &iwm_debugfs_txq_fops
);
460 iwm
->dbg
.tx_credit_dentry
= debugfs_create_file("credits", 0200,
462 &iwm_debugfs_tx_credit_fops
);
463 iwm
->dbg
.rx_ticket_dentry
= debugfs_create_file("tickets", 0200,
465 &iwm_debugfs_rx_ticket_fops
);
466 iwm
->dbg
.fw_err_dentry
= debugfs_create_file("last_fw_err", 0200,
467 iwm
->dbg
.dbgdir
, iwm
,
468 &iwm_debugfs_fw_err_fops
);
471 void iwm_debugfs_exit(struct iwm_priv
*iwm
)
475 for (i
= 0; i
< __IWM_DM_NR
; i
++)
476 debugfs_remove(iwm
->dbg
.dbg_module_dentries
[i
]);
478 debugfs_remove(iwm
->dbg
.dbg_modules_dentry
);
479 debugfs_remove(iwm
->dbg
.dbg_level_dentry
);
480 debugfs_remove(iwm
->dbg
.txq_dentry
);
481 debugfs_remove(iwm
->dbg
.tx_credit_dentry
);
482 debugfs_remove(iwm
->dbg
.rx_ticket_dentry
);
483 debugfs_remove(iwm
->dbg
.fw_err_dentry
);
484 if (iwm
->bus_ops
->debugfs_exit
)
485 iwm
->bus_ops
->debugfs_exit(iwm
);
487 debugfs_remove(iwm
->dbg
.busdir
);
488 debugfs_remove(iwm
->dbg
.dbgdir
);
489 debugfs_remove(iwm
->dbg
.txdir
);
490 debugfs_remove(iwm
->dbg
.rxdir
);
491 debugfs_remove(iwm
->dbg
.devdir
);
492 debugfs_remove(iwm
->dbg
.rootdir
);