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>
28 #include <linux/export.h>
38 } iwm_debug_module
[__IWM_DM_NR
] = {
39 {IWM_DM_BOOT
, "boot"},
41 {IWM_DM_SDIO
, "sdio"},
45 {IWM_DM_MLME
, "mlme"},
47 {IWM_DM_WEXT
, "wext"},
50 #define add_dbg_module(dbg, name, id, initlevel) \
52 dbg.dbg_module[id] = (initlevel); \
53 dbg.dbg_module_dentries[id] = \
54 debugfs_create_x8(name, 0600, \
56 &(dbg.dbg_module[id])); \
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
;
271 int buf_len
= 4096, i
;
277 if (count
< sizeof(buf
))
280 buf
= kzalloc(buf_len
, GFP_KERNEL
);
284 spin_lock(&iwm
->ticket_lock
);
285 list_for_each_entry(ticket
, &iwm
->rx_tickets
, node
) {
286 len
+= snprintf(buf
+ len
, buf_len
- len
, "Ticket #%d\n",
288 len
+= snprintf(buf
+ len
, buf_len
- len
, "\taction: 0x%x\n",
289 ticket
->ticket
->action
);
290 len
+= snprintf(buf
+ len
, buf_len
- len
, "\tflags: 0x%x\n",
291 ticket
->ticket
->flags
);
293 spin_unlock(&iwm
->ticket_lock
);
295 for (i
= 0; i
< IWM_RX_ID_HASH
; i
++) {
296 struct iwm_rx_packet
*packet
;
297 struct list_head
*pkt_list
= &iwm
->rx_packets
[i
];
299 if (!list_empty(pkt_list
)) {
300 len
+= snprintf(buf
+ len
, buf_len
- len
,
301 "Packet hash #%d\n", i
);
302 spin_lock(&iwm
->packet_lock
[i
]);
303 list_for_each_entry(packet
, pkt_list
, node
) {
304 len
+= snprintf(buf
+ len
, buf_len
- len
,
307 len
+= snprintf(buf
+ len
, buf_len
- len
,
308 "\tPacket length: %lu\n",
311 spin_unlock(&iwm
->packet_lock
[i
]);
315 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
321 static ssize_t
iwm_debugfs_fw_err_read(struct file
*filp
,
323 size_t count
, loff_t
*ppos
)
326 struct iwm_priv
*iwm
= filp
->private_data
;
333 if (count
< sizeof(buf
))
336 if (!iwm
->last_fw_err
)
339 if (iwm
->last_fw_err
->line_num
== 0)
342 len
+= snprintf(buf
+ len
, buf_len
- len
, "%cMAC FW ERROR:\n",
343 (le32_to_cpu(iwm
->last_fw_err
->category
) == UMAC_SYS_ERR_CAT_LMAC
)
345 len
+= snprintf(buf
+ len
, buf_len
- len
,
347 le32_to_cpu(iwm
->last_fw_err
->category
));
349 len
+= snprintf(buf
+ len
, buf_len
- len
,
351 le32_to_cpu(iwm
->last_fw_err
->status
));
353 len
+= snprintf(buf
+ len
, buf_len
- len
,
355 le32_to_cpu(iwm
->last_fw_err
->pc
));
357 len
+= snprintf(buf
+ len
, buf_len
- len
,
359 le32_to_cpu(iwm
->last_fw_err
->blink1
));
361 len
+= snprintf(buf
+ len
, buf_len
- len
,
363 le32_to_cpu(iwm
->last_fw_err
->blink2
));
365 len
+= snprintf(buf
+ len
, buf_len
- len
,
367 le32_to_cpu(iwm
->last_fw_err
->ilink1
));
369 len
+= snprintf(buf
+ len
, buf_len
- len
,
371 le32_to_cpu(iwm
->last_fw_err
->ilink2
));
373 len
+= snprintf(buf
+ len
, buf_len
- len
,
375 le32_to_cpu(iwm
->last_fw_err
->data1
));
377 len
+= snprintf(buf
+ len
, buf_len
- len
,
379 le32_to_cpu(iwm
->last_fw_err
->data2
));
381 len
+= snprintf(buf
+ len
, buf_len
- len
,
382 "\tLine number: %d\n",
383 le32_to_cpu(iwm
->last_fw_err
->line_num
));
385 len
+= snprintf(buf
+ len
, buf_len
- len
,
386 "\tUMAC status: 0x%x\n",
387 le32_to_cpu(iwm
->last_fw_err
->umac_status
));
389 len
+= snprintf(buf
+ len
, buf_len
- len
,
390 "\tLMAC status: 0x%x\n",
391 le32_to_cpu(iwm
->last_fw_err
->lmac_status
));
393 len
+= snprintf(buf
+ len
, buf_len
- len
,
394 "\tSDIO status: 0x%x\n",
395 le32_to_cpu(iwm
->last_fw_err
->sdio_status
));
399 return simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
402 static const struct file_operations iwm_debugfs_txq_fops
= {
403 .owner
= THIS_MODULE
,
404 .open
= iwm_generic_open
,
405 .read
= iwm_debugfs_txq_read
,
406 .llseek
= default_llseek
,
409 static const struct file_operations iwm_debugfs_tx_credit_fops
= {
410 .owner
= THIS_MODULE
,
411 .open
= iwm_generic_open
,
412 .read
= iwm_debugfs_tx_credit_read
,
413 .llseek
= default_llseek
,
416 static const struct file_operations iwm_debugfs_rx_ticket_fops
= {
417 .owner
= THIS_MODULE
,
418 .open
= iwm_generic_open
,
419 .read
= iwm_debugfs_rx_ticket_read
,
420 .llseek
= default_llseek
,
423 static const struct file_operations iwm_debugfs_fw_err_fops
= {
424 .owner
= THIS_MODULE
,
425 .open
= iwm_generic_open
,
426 .read
= iwm_debugfs_fw_err_read
,
427 .llseek
= default_llseek
,
430 void iwm_debugfs_init(struct iwm_priv
*iwm
)
434 iwm
->dbg
.rootdir
= debugfs_create_dir(KBUILD_MODNAME
, NULL
);
435 iwm
->dbg
.devdir
= debugfs_create_dir(wiphy_name(iwm_to_wiphy(iwm
)),
437 iwm
->dbg
.dbgdir
= debugfs_create_dir("debug", iwm
->dbg
.devdir
);
438 iwm
->dbg
.rxdir
= debugfs_create_dir("rx", iwm
->dbg
.devdir
);
439 iwm
->dbg
.txdir
= debugfs_create_dir("tx", iwm
->dbg
.devdir
);
440 iwm
->dbg
.busdir
= debugfs_create_dir("bus", iwm
->dbg
.devdir
);
441 if (iwm
->bus_ops
->debugfs_init
)
442 iwm
->bus_ops
->debugfs_init(iwm
, iwm
->dbg
.busdir
);
444 iwm
->dbg
.dbg_level
= IWM_DL_NONE
;
445 iwm
->dbg
.dbg_level_dentry
=
446 debugfs_create_file("level", 0200, iwm
->dbg
.dbgdir
, iwm
,
447 &fops_iwm_dbg_level
);
449 iwm
->dbg
.dbg_modules
= IWM_DM_DEFAULT
;
450 iwm
->dbg
.dbg_modules_dentry
=
451 debugfs_create_file("modules", 0200, iwm
->dbg
.dbgdir
, iwm
,
452 &fops_iwm_dbg_modules
);
454 for (i
= 0; i
< __IWM_DM_NR
; i
++)
455 add_dbg_module(iwm
->dbg
, iwm_debug_module
[i
].name
,
456 iwm_debug_module
[i
].id
, IWM_DL_DEFAULT
);
458 iwm
->dbg
.txq_dentry
= debugfs_create_file("queues", 0200,
460 &iwm_debugfs_txq_fops
);
461 iwm
->dbg
.tx_credit_dentry
= debugfs_create_file("credits", 0200,
463 &iwm_debugfs_tx_credit_fops
);
464 iwm
->dbg
.rx_ticket_dentry
= debugfs_create_file("tickets", 0200,
466 &iwm_debugfs_rx_ticket_fops
);
467 iwm
->dbg
.fw_err_dentry
= debugfs_create_file("last_fw_err", 0200,
468 iwm
->dbg
.dbgdir
, iwm
,
469 &iwm_debugfs_fw_err_fops
);
472 void iwm_debugfs_exit(struct iwm_priv
*iwm
)
476 for (i
= 0; i
< __IWM_DM_NR
; i
++)
477 debugfs_remove(iwm
->dbg
.dbg_module_dentries
[i
]);
479 debugfs_remove(iwm
->dbg
.dbg_modules_dentry
);
480 debugfs_remove(iwm
->dbg
.dbg_level_dentry
);
481 debugfs_remove(iwm
->dbg
.txq_dentry
);
482 debugfs_remove(iwm
->dbg
.tx_credit_dentry
);
483 debugfs_remove(iwm
->dbg
.rx_ticket_dentry
);
484 debugfs_remove(iwm
->dbg
.fw_err_dentry
);
485 if (iwm
->bus_ops
->debugfs_exit
)
486 iwm
->bus_ops
->debugfs_exit(iwm
);
488 debugfs_remove(iwm
->dbg
.busdir
);
489 debugfs_remove(iwm
->dbg
.dbgdir
);
490 debugfs_remove(iwm
->dbg
.txdir
);
491 debugfs_remove(iwm
->dbg
.rxdir
);
492 debugfs_remove(iwm
->dbg
.devdir
);
493 debugfs_remove(iwm
->dbg
.rootdir
);