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
,
103 static ssize_t
iwm_debugfs_txq_read(struct file
*filp
, char __user
*buffer
,
104 size_t count
, loff_t
*ppos
)
106 struct iwm_priv
*iwm
= filp
->private_data
;
108 int i
, buf_len
= 4096;
114 if (count
< sizeof(buf
))
117 buf
= kzalloc(buf_len
, GFP_KERNEL
);
121 for (i
= 0; i
< IWM_TX_QUEUES
; i
++) {
122 struct iwm_tx_queue
*txq
= &iwm
->txq
[i
];
127 spin_lock_irqsave(&txq
->queue
.lock
, flags
);
129 skb
= (struct sk_buff
*)&txq
->queue
;
131 len
+= snprintf(buf
+ len
, buf_len
- len
, "TXQ #%d\n", i
);
132 len
+= snprintf(buf
+ len
, buf_len
- len
, "\tStopped: %d\n",
133 __netif_subqueue_stopped(iwm_to_ndev(iwm
),
135 len
+= snprintf(buf
+ len
, buf_len
- len
, "\tConcat count:%d\n",
137 len
+= snprintf(buf
+ len
, buf_len
- len
, "\tQueue len: %d\n",
138 skb_queue_len(&txq
->queue
));
139 for (j
= 0; j
< skb_queue_len(&txq
->queue
); j
++) {
140 struct iwm_tx_info
*tx_info
;
143 tx_info
= skb_to_tx_info(skb
);
145 len
+= snprintf(buf
+ len
, buf_len
- len
,
147 len
+= snprintf(buf
+ len
, buf_len
- len
,
148 "\t\tsta: %d\n", tx_info
->sta
);
149 len
+= snprintf(buf
+ len
, buf_len
- len
,
150 "\t\tcolor: %d\n", tx_info
->color
);
151 len
+= snprintf(buf
+ len
, buf_len
- len
,
152 "\t\ttid: %d\n", tx_info
->tid
);
155 spin_unlock_irqrestore(&txq
->queue
.lock
, flags
);
157 spin_lock_irqsave(&txq
->stopped_queue
.lock
, flags
);
159 len
+= snprintf(buf
+ len
, buf_len
- len
,
160 "\tStopped Queue len: %d\n",
161 skb_queue_len(&txq
->stopped_queue
));
162 for (j
= 0; j
< skb_queue_len(&txq
->stopped_queue
); j
++) {
163 struct iwm_tx_info
*tx_info
;
166 tx_info
= skb_to_tx_info(skb
);
168 len
+= snprintf(buf
+ len
, buf_len
- len
,
170 len
+= snprintf(buf
+ len
, buf_len
- len
,
171 "\t\tsta: %d\n", tx_info
->sta
);
172 len
+= snprintf(buf
+ len
, buf_len
- len
,
173 "\t\tcolor: %d\n", tx_info
->color
);
174 len
+= snprintf(buf
+ len
, buf_len
- len
,
175 "\t\ttid: %d\n", tx_info
->tid
);
178 spin_unlock_irqrestore(&txq
->stopped_queue
.lock
, flags
);
181 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
187 static ssize_t
iwm_debugfs_tx_credit_read(struct file
*filp
,
189 size_t count
, loff_t
*ppos
)
191 struct iwm_priv
*iwm
= filp
->private_data
;
192 struct iwm_tx_credit
*credit
= &iwm
->tx_credit
;
194 int i
, buf_len
= 4096;
200 if (count
< sizeof(buf
))
203 buf
= kzalloc(buf_len
, GFP_KERNEL
);
207 len
+= snprintf(buf
+ len
, buf_len
- len
,
208 "NR pools: %d\n", credit
->pool_nr
);
209 len
+= snprintf(buf
+ len
, buf_len
- len
,
210 "pools map: 0x%lx\n", credit
->full_pools_map
);
212 len
+= snprintf(buf
+ len
, buf_len
- len
, "\n### POOLS ###\n");
213 for (i
= 0; i
< IWM_MACS_OUT_GROUPS
; i
++) {
214 len
+= snprintf(buf
+ len
, buf_len
- len
,
215 "pools entry #%d\n", i
);
216 len
+= snprintf(buf
+ len
, buf_len
- len
,
218 credit
->pools
[i
].id
);
219 len
+= snprintf(buf
+ len
, buf_len
- len
,
221 credit
->pools
[i
].sid
);
222 len
+= snprintf(buf
+ len
, buf_len
- len
,
224 credit
->pools
[i
].min_pages
);
225 len
+= snprintf(buf
+ len
, buf_len
- len
,
227 credit
->pools
[i
].max_pages
);
228 len
+= snprintf(buf
+ len
, buf_len
- len
,
229 "\talloc_pages: %d\n",
230 credit
->pools
[i
].alloc_pages
);
231 len
+= snprintf(buf
+ len
, buf_len
- len
,
232 "\tfreed_pages: %d\n",
233 credit
->pools
[i
].total_freed_pages
);
236 len
+= snprintf(buf
+ len
, buf_len
- len
, "\n### SPOOLS ###\n");
237 for (i
= 0; i
< IWM_MACS_OUT_SGROUPS
; i
++) {
238 len
+= snprintf(buf
+ len
, buf_len
- len
,
239 "spools entry #%d\n", i
);
240 len
+= snprintf(buf
+ len
, buf_len
- len
,
242 credit
->spools
[i
].id
);
243 len
+= snprintf(buf
+ len
, buf_len
- len
,
245 credit
->spools
[i
].max_pages
);
246 len
+= snprintf(buf
+ len
, buf_len
- len
,
247 "\talloc_pages: %d\n",
248 credit
->spools
[i
].alloc_pages
);
252 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
258 static ssize_t
iwm_debugfs_rx_ticket_read(struct file
*filp
,
260 size_t count
, loff_t
*ppos
)
262 struct iwm_priv
*iwm
= filp
->private_data
;
263 struct iwm_rx_ticket_node
*ticket
;
265 int buf_len
= 4096, i
;
271 if (count
< sizeof(buf
))
274 buf
= kzalloc(buf_len
, GFP_KERNEL
);
278 spin_lock(&iwm
->ticket_lock
);
279 list_for_each_entry(ticket
, &iwm
->rx_tickets
, node
) {
280 len
+= snprintf(buf
+ len
, buf_len
- len
, "Ticket #%d\n",
282 len
+= snprintf(buf
+ len
, buf_len
- len
, "\taction: 0x%x\n",
283 ticket
->ticket
->action
);
284 len
+= snprintf(buf
+ len
, buf_len
- len
, "\tflags: 0x%x\n",
285 ticket
->ticket
->flags
);
287 spin_unlock(&iwm
->ticket_lock
);
289 for (i
= 0; i
< IWM_RX_ID_HASH
; i
++) {
290 struct iwm_rx_packet
*packet
;
291 struct list_head
*pkt_list
= &iwm
->rx_packets
[i
];
293 if (!list_empty(pkt_list
)) {
294 len
+= snprintf(buf
+ len
, buf_len
- len
,
295 "Packet hash #%d\n", i
);
296 spin_lock(&iwm
->packet_lock
[i
]);
297 list_for_each_entry(packet
, pkt_list
, node
) {
298 len
+= snprintf(buf
+ len
, buf_len
- len
,
301 len
+= snprintf(buf
+ len
, buf_len
- len
,
302 "\tPacket length: %lu\n",
305 spin_unlock(&iwm
->packet_lock
[i
]);
309 ret
= simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
315 static ssize_t
iwm_debugfs_fw_err_read(struct file
*filp
,
317 size_t count
, loff_t
*ppos
)
320 struct iwm_priv
*iwm
= filp
->private_data
;
327 if (count
< sizeof(buf
))
330 if (!iwm
->last_fw_err
)
333 if (iwm
->last_fw_err
->line_num
== 0)
336 len
+= snprintf(buf
+ len
, buf_len
- len
, "%cMAC FW ERROR:\n",
337 (le32_to_cpu(iwm
->last_fw_err
->category
) == UMAC_SYS_ERR_CAT_LMAC
)
339 len
+= snprintf(buf
+ len
, buf_len
- len
,
341 le32_to_cpu(iwm
->last_fw_err
->category
));
343 len
+= snprintf(buf
+ len
, buf_len
- len
,
345 le32_to_cpu(iwm
->last_fw_err
->status
));
347 len
+= snprintf(buf
+ len
, buf_len
- len
,
349 le32_to_cpu(iwm
->last_fw_err
->pc
));
351 len
+= snprintf(buf
+ len
, buf_len
- len
,
353 le32_to_cpu(iwm
->last_fw_err
->blink1
));
355 len
+= snprintf(buf
+ len
, buf_len
- len
,
357 le32_to_cpu(iwm
->last_fw_err
->blink2
));
359 len
+= snprintf(buf
+ len
, buf_len
- len
,
361 le32_to_cpu(iwm
->last_fw_err
->ilink1
));
363 len
+= snprintf(buf
+ len
, buf_len
- len
,
365 le32_to_cpu(iwm
->last_fw_err
->ilink2
));
367 len
+= snprintf(buf
+ len
, buf_len
- len
,
369 le32_to_cpu(iwm
->last_fw_err
->data1
));
371 len
+= snprintf(buf
+ len
, buf_len
- len
,
373 le32_to_cpu(iwm
->last_fw_err
->data2
));
375 len
+= snprintf(buf
+ len
, buf_len
- len
,
376 "\tLine number: %d\n",
377 le32_to_cpu(iwm
->last_fw_err
->line_num
));
379 len
+= snprintf(buf
+ len
, buf_len
- len
,
380 "\tUMAC status: 0x%x\n",
381 le32_to_cpu(iwm
->last_fw_err
->umac_status
));
383 len
+= snprintf(buf
+ len
, buf_len
- len
,
384 "\tLMAC status: 0x%x\n",
385 le32_to_cpu(iwm
->last_fw_err
->lmac_status
));
387 len
+= snprintf(buf
+ len
, buf_len
- len
,
388 "\tSDIO status: 0x%x\n",
389 le32_to_cpu(iwm
->last_fw_err
->sdio_status
));
393 return simple_read_from_buffer(buffer
, len
, ppos
, buf
, buf_len
);
396 static const struct file_operations iwm_debugfs_txq_fops
= {
397 .owner
= THIS_MODULE
,
399 .read
= iwm_debugfs_txq_read
,
400 .llseek
= default_llseek
,
403 static const struct file_operations iwm_debugfs_tx_credit_fops
= {
404 .owner
= THIS_MODULE
,
406 .read
= iwm_debugfs_tx_credit_read
,
407 .llseek
= default_llseek
,
410 static const struct file_operations iwm_debugfs_rx_ticket_fops
= {
411 .owner
= THIS_MODULE
,
413 .read
= iwm_debugfs_rx_ticket_read
,
414 .llseek
= default_llseek
,
417 static const struct file_operations iwm_debugfs_fw_err_fops
= {
418 .owner
= THIS_MODULE
,
420 .read
= iwm_debugfs_fw_err_read
,
421 .llseek
= default_llseek
,
424 void iwm_debugfs_init(struct iwm_priv
*iwm
)
428 iwm
->dbg
.rootdir
= debugfs_create_dir(KBUILD_MODNAME
, NULL
);
429 iwm
->dbg
.devdir
= debugfs_create_dir(wiphy_name(iwm_to_wiphy(iwm
)),
431 iwm
->dbg
.dbgdir
= debugfs_create_dir("debug", iwm
->dbg
.devdir
);
432 iwm
->dbg
.rxdir
= debugfs_create_dir("rx", iwm
->dbg
.devdir
);
433 iwm
->dbg
.txdir
= debugfs_create_dir("tx", iwm
->dbg
.devdir
);
434 iwm
->dbg
.busdir
= debugfs_create_dir("bus", iwm
->dbg
.devdir
);
435 if (iwm
->bus_ops
->debugfs_init
)
436 iwm
->bus_ops
->debugfs_init(iwm
, iwm
->dbg
.busdir
);
438 iwm
->dbg
.dbg_level
= IWM_DL_NONE
;
439 iwm
->dbg
.dbg_level_dentry
=
440 debugfs_create_file("level", 0200, iwm
->dbg
.dbgdir
, iwm
,
441 &fops_iwm_dbg_level
);
443 iwm
->dbg
.dbg_modules
= IWM_DM_DEFAULT
;
444 iwm
->dbg
.dbg_modules_dentry
=
445 debugfs_create_file("modules", 0200, iwm
->dbg
.dbgdir
, iwm
,
446 &fops_iwm_dbg_modules
);
448 for (i
= 0; i
< __IWM_DM_NR
; i
++)
449 add_dbg_module(iwm
->dbg
, iwm_debug_module
[i
].name
,
450 iwm_debug_module
[i
].id
, IWM_DL_DEFAULT
);
452 iwm
->dbg
.txq_dentry
= debugfs_create_file("queues", 0200,
454 &iwm_debugfs_txq_fops
);
455 iwm
->dbg
.tx_credit_dentry
= debugfs_create_file("credits", 0200,
457 &iwm_debugfs_tx_credit_fops
);
458 iwm
->dbg
.rx_ticket_dentry
= debugfs_create_file("tickets", 0200,
460 &iwm_debugfs_rx_ticket_fops
);
461 iwm
->dbg
.fw_err_dentry
= debugfs_create_file("last_fw_err", 0200,
462 iwm
->dbg
.dbgdir
, iwm
,
463 &iwm_debugfs_fw_err_fops
);
466 void iwm_debugfs_exit(struct iwm_priv
*iwm
)
470 for (i
= 0; i
< __IWM_DM_NR
; i
++)
471 debugfs_remove(iwm
->dbg
.dbg_module_dentries
[i
]);
473 debugfs_remove(iwm
->dbg
.dbg_modules_dentry
);
474 debugfs_remove(iwm
->dbg
.dbg_level_dentry
);
475 debugfs_remove(iwm
->dbg
.txq_dentry
);
476 debugfs_remove(iwm
->dbg
.tx_credit_dentry
);
477 debugfs_remove(iwm
->dbg
.rx_ticket_dentry
);
478 debugfs_remove(iwm
->dbg
.fw_err_dentry
);
479 if (iwm
->bus_ops
->debugfs_exit
)
480 iwm
->bus_ops
->debugfs_exit(iwm
);
482 debugfs_remove(iwm
->dbg
.busdir
);
483 debugfs_remove(iwm
->dbg
.dbgdir
);
484 debugfs_remove(iwm
->dbg
.txdir
);
485 debugfs_remove(iwm
->dbg
.rxdir
);
486 debugfs_remove(iwm
->dbg
.devdir
);
487 debugfs_remove(iwm
->dbg
.rootdir
);