2 * Copyright (c) 2004-2011 Atheros Communications Inc.
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <linux/circ_buf.h>
21 #include <linux/vmalloc.h>
26 struct ath6kl_fwlog_slot
{
30 /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */
34 #define ATH6KL_FWLOG_SIZE 32768
35 #define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
36 ATH6KL_FWLOG_PAYLOAD_SIZE)
37 #define ATH6KL_FWLOG_VALID_MASK 0x1ffff
39 int ath6kl_printk(const char *level
, const char *fmt
, ...)
50 rtn
= printk("%sath6kl: %pV", level
, &vaf
);
57 #ifdef CONFIG_ATH6KL_DEBUG
59 #define REG_OUTPUT_LEN_PER_LINE 25
60 #define REGTYPE_STR_LEN 100
62 struct ath6kl_diag_reg_info
{
68 static const struct ath6kl_diag_reg_info diag_reg
[] = {
69 { 0x20000, 0x200fc, "General DMA and Rx registers" },
70 { 0x28000, 0x28900, "MAC PCU register & keycache" },
71 { 0x20800, 0x20a40, "QCU" },
72 { 0x21000, 0x212f0, "DCU" },
73 { 0x4000, 0x42e4, "RTC" },
74 { 0x540000, 0x540000 + (256 * 1024), "RAM" },
75 { 0x29800, 0x2B210, "Base Band" },
76 { 0x1C000, 0x1C748, "Analog" },
79 void ath6kl_dump_registers(struct ath6kl_device
*dev
,
80 struct ath6kl_irq_proc_registers
*irq_proc_reg
,
81 struct ath6kl_irq_enable_reg
*irq_enable_reg
)
84 ath6kl_dbg(ATH6KL_DBG_ANY
, ("<------- Register Table -------->\n"));
86 if (irq_proc_reg
!= NULL
) {
87 ath6kl_dbg(ATH6KL_DBG_ANY
,
88 "Host Int status: 0x%x\n",
89 irq_proc_reg
->host_int_status
);
90 ath6kl_dbg(ATH6KL_DBG_ANY
,
91 "CPU Int status: 0x%x\n",
92 irq_proc_reg
->cpu_int_status
);
93 ath6kl_dbg(ATH6KL_DBG_ANY
,
94 "Error Int status: 0x%x\n",
95 irq_proc_reg
->error_int_status
);
96 ath6kl_dbg(ATH6KL_DBG_ANY
,
97 "Counter Int status: 0x%x\n",
98 irq_proc_reg
->counter_int_status
);
99 ath6kl_dbg(ATH6KL_DBG_ANY
,
100 "Mbox Frame: 0x%x\n",
101 irq_proc_reg
->mbox_frame
);
102 ath6kl_dbg(ATH6KL_DBG_ANY
,
103 "Rx Lookahead Valid: 0x%x\n",
104 irq_proc_reg
->rx_lkahd_valid
);
105 ath6kl_dbg(ATH6KL_DBG_ANY
,
106 "Rx Lookahead 0: 0x%x\n",
107 irq_proc_reg
->rx_lkahd
[0]);
108 ath6kl_dbg(ATH6KL_DBG_ANY
,
109 "Rx Lookahead 1: 0x%x\n",
110 irq_proc_reg
->rx_lkahd
[1]);
112 if (dev
->ar
->mbox_info
.gmbox_addr
!= 0) {
114 * If the target supports GMBOX hardware, dump some
117 ath6kl_dbg(ATH6KL_DBG_ANY
,
118 "GMBOX Host Int status 2: 0x%x\n",
119 irq_proc_reg
->host_int_status2
);
120 ath6kl_dbg(ATH6KL_DBG_ANY
,
121 "GMBOX RX Avail: 0x%x\n",
122 irq_proc_reg
->gmbox_rx_avail
);
123 ath6kl_dbg(ATH6KL_DBG_ANY
,
124 "GMBOX lookahead alias 0: 0x%x\n",
125 irq_proc_reg
->rx_gmbox_lkahd_alias
[0]);
126 ath6kl_dbg(ATH6KL_DBG_ANY
,
127 "GMBOX lookahead alias 1: 0x%x\n",
128 irq_proc_reg
->rx_gmbox_lkahd_alias
[1]);
133 if (irq_enable_reg
!= NULL
) {
134 ath6kl_dbg(ATH6KL_DBG_ANY
,
135 "Int status Enable: 0x%x\n",
136 irq_enable_reg
->int_status_en
);
137 ath6kl_dbg(ATH6KL_DBG_ANY
, "Counter Int status Enable: 0x%x\n",
138 irq_enable_reg
->cntr_int_status_en
);
140 ath6kl_dbg(ATH6KL_DBG_ANY
, "<------------------------------->\n");
143 static void dump_cred_dist(struct htc_endpoint_credit_dist
*ep_dist
)
145 ath6kl_dbg(ATH6KL_DBG_ANY
,
146 "--- endpoint: %d svc_id: 0x%X ---\n",
147 ep_dist
->endpoint
, ep_dist
->svc_id
);
148 ath6kl_dbg(ATH6KL_DBG_ANY
, " dist_flags : 0x%X\n",
149 ep_dist
->dist_flags
);
150 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_norm : %d\n",
152 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_min : %d\n",
154 ath6kl_dbg(ATH6KL_DBG_ANY
, " credits : %d\n",
156 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_assngd : %d\n",
157 ep_dist
->cred_assngd
);
158 ath6kl_dbg(ATH6KL_DBG_ANY
, " seek_cred : %d\n",
160 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_sz : %d\n",
162 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_per_msg : %d\n",
163 ep_dist
->cred_per_msg
);
164 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_to_dist : %d\n",
165 ep_dist
->cred_to_dist
);
166 ath6kl_dbg(ATH6KL_DBG_ANY
, " txq_depth : %d\n",
167 get_queue_depth(&((struct htc_endpoint
*)
168 ep_dist
->htc_rsvd
)->txq
));
169 ath6kl_dbg(ATH6KL_DBG_ANY
,
170 "----------------------------------\n");
173 void dump_cred_dist_stats(struct htc_target
*target
)
175 struct htc_endpoint_credit_dist
*ep_list
;
177 if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_TRC
))
180 list_for_each_entry(ep_list
, &target
->cred_dist_list
, list
)
181 dump_cred_dist(ep_list
);
183 ath6kl_dbg(ATH6KL_DBG_HTC_SEND
, "ctxt:%p dist:%p\n",
184 target
->cred_dist_cntxt
, NULL
);
185 ath6kl_dbg(ATH6KL_DBG_TRC
, "credit distribution, total : %d, free : %d\n",
186 target
->cred_dist_cntxt
->total_avail_credits
,
187 target
->cred_dist_cntxt
->cur_free_credits
);
190 static int ath6kl_debugfs_open(struct inode
*inode
, struct file
*file
)
192 file
->private_data
= inode
->i_private
;
196 void ath6kl_debug_war(struct ath6kl
*ar
, enum ath6kl_war war
)
199 case ATH6KL_WAR_INVALID_RATE
:
200 ar
->debug
.war_stats
.invalid_rate
++;
205 static ssize_t
read_file_war_stats(struct file
*file
, char __user
*user_buf
,
206 size_t count
, loff_t
*ppos
)
208 struct ath6kl
*ar
= file
->private_data
;
210 unsigned int len
= 0, buf_len
= 1500;
213 buf
= kzalloc(buf_len
, GFP_KERNEL
);
217 len
+= scnprintf(buf
+ len
, buf_len
- len
, "\n");
218 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
220 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n\n",
221 "=================");
222 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10u\n",
223 "Invalid rates", ar
->debug
.war_stats
.invalid_rate
);
225 if (WARN_ON(len
> buf_len
))
228 ret_cnt
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
234 static const struct file_operations fops_war_stats
= {
235 .read
= read_file_war_stats
,
236 .open
= ath6kl_debugfs_open
,
237 .owner
= THIS_MODULE
,
238 .llseek
= default_llseek
,
241 static void ath6kl_debug_fwlog_add(struct ath6kl
*ar
, const void *buf
,
244 struct circ_buf
*fwlog
= &ar
->debug
.fwlog_buf
;
248 /* entries must all be equal size */
249 if (WARN_ON(buf_len
!= ATH6KL_FWLOG_SLOT_SIZE
))
252 space
= CIRC_SPACE(fwlog
->head
, fwlog
->tail
, ATH6KL_FWLOG_SIZE
);
254 /* discard oldest slot */
255 fwlog
->tail
= (fwlog
->tail
+ ATH6KL_FWLOG_SLOT_SIZE
) &
256 (ATH6KL_FWLOG_SIZE
- 1);
258 for (i
= 0; i
< buf_len
; i
+= space
) {
259 space
= CIRC_SPACE_TO_END(fwlog
->head
, fwlog
->tail
,
262 if ((size_t) space
> buf_len
- i
)
265 memcpy(&fwlog
->buf
[fwlog
->head
], buf
, space
);
266 fwlog
->head
= (fwlog
->head
+ space
) & (ATH6KL_FWLOG_SIZE
- 1);
271 void ath6kl_debug_fwlog_event(struct ath6kl
*ar
, const void *buf
, size_t len
)
273 struct ath6kl_fwlog_slot
*slot
= ar
->debug
.fwlog_tmp
;
276 if (WARN_ON(len
> ATH6KL_FWLOG_PAYLOAD_SIZE
))
279 spin_lock_bh(&ar
->debug
.fwlog_lock
);
281 slot
->timestamp
= cpu_to_le32(jiffies
);
282 slot
->length
= cpu_to_le32(len
);
283 memcpy(slot
->payload
, buf
, len
);
285 slot_len
= sizeof(*slot
) + len
;
287 if (slot_len
< ATH6KL_FWLOG_SLOT_SIZE
)
288 memset(slot
->payload
+ len
, 0,
289 ATH6KL_FWLOG_SLOT_SIZE
- slot_len
);
291 ath6kl_debug_fwlog_add(ar
, slot
, ATH6KL_FWLOG_SLOT_SIZE
);
293 spin_unlock_bh(&ar
->debug
.fwlog_lock
);
296 static bool ath6kl_debug_fwlog_empty(struct ath6kl
*ar
)
298 return CIRC_CNT(ar
->debug
.fwlog_buf
.head
,
299 ar
->debug
.fwlog_buf
.tail
,
300 ATH6KL_FWLOG_SLOT_SIZE
) == 0;
303 static ssize_t
ath6kl_fwlog_read(struct file
*file
, char __user
*user_buf
,
304 size_t count
, loff_t
*ppos
)
306 struct ath6kl
*ar
= file
->private_data
;
307 struct circ_buf
*fwlog
= &ar
->debug
.fwlog_buf
;
308 size_t len
= 0, buf_len
= count
;
313 buf
= vmalloc(buf_len
);
317 /* read undelivered logs from firmware */
318 ath6kl_read_fwlogs(ar
);
320 spin_lock_bh(&ar
->debug
.fwlog_lock
);
322 while (len
< buf_len
&& !ath6kl_debug_fwlog_empty(ar
)) {
323 ccnt
= CIRC_CNT_TO_END(fwlog
->head
, fwlog
->tail
,
326 if ((size_t) ccnt
> buf_len
- len
)
327 ccnt
= buf_len
- len
;
329 memcpy(buf
+ len
, &fwlog
->buf
[fwlog
->tail
], ccnt
);
332 fwlog
->tail
= (fwlog
->tail
+ ccnt
) &
333 (ATH6KL_FWLOG_SIZE
- 1);
336 spin_unlock_bh(&ar
->debug
.fwlog_lock
);
338 if (WARN_ON(len
> buf_len
))
341 ret_cnt
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
348 static const struct file_operations fops_fwlog
= {
349 .open
= ath6kl_debugfs_open
,
350 .read
= ath6kl_fwlog_read
,
351 .owner
= THIS_MODULE
,
352 .llseek
= default_llseek
,
355 static ssize_t
ath6kl_fwlog_mask_read(struct file
*file
, char __user
*user_buf
,
356 size_t count
, loff_t
*ppos
)
358 struct ath6kl
*ar
= file
->private_data
;
362 len
= snprintf(buf
, sizeof(buf
), "0x%x\n", ar
->debug
.fwlog_mask
);
364 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
367 static ssize_t
ath6kl_fwlog_mask_write(struct file
*file
,
368 const char __user
*user_buf
,
369 size_t count
, loff_t
*ppos
)
371 struct ath6kl
*ar
= file
->private_data
;
374 ret
= kstrtou32_from_user(user_buf
, count
, 0, &ar
->debug
.fwlog_mask
);
378 ret
= ath6kl_wmi_config_debug_module_cmd(ar
->wmi
,
379 ATH6KL_FWLOG_VALID_MASK
,
380 ar
->debug
.fwlog_mask
);
387 static const struct file_operations fops_fwlog_mask
= {
388 .open
= ath6kl_debugfs_open
,
389 .read
= ath6kl_fwlog_mask_read
,
390 .write
= ath6kl_fwlog_mask_write
,
391 .owner
= THIS_MODULE
,
392 .llseek
= default_llseek
,
395 static ssize_t
read_file_tgt_stats(struct file
*file
, char __user
*user_buf
,
396 size_t count
, loff_t
*ppos
)
398 struct ath6kl
*ar
= file
->private_data
;
399 struct target_stats
*tgt_stats
= &ar
->target_stats
;
401 unsigned int len
= 0, buf_len
= 1500;
406 buf
= kzalloc(buf_len
, GFP_KERNEL
);
410 if (down_interruptible(&ar
->sem
)) {
415 set_bit(STATS_UPDATE_PEND
, &ar
->flag
);
417 if (ath6kl_wmi_get_stats_cmd(ar
->wmi
)) {
423 left
= wait_event_interruptible_timeout(ar
->event_wq
,
424 !test_bit(STATS_UPDATE_PEND
,
425 &ar
->flag
), WMI_TIMEOUT
);
434 len
+= scnprintf(buf
+ len
, buf_len
- len
, "\n");
435 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
437 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n\n",
438 "=================");
439 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
440 "Ucast packets", tgt_stats
->tx_ucast_pkt
);
441 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
442 "Bcast packets", tgt_stats
->tx_bcast_pkt
);
443 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
444 "Ucast byte", tgt_stats
->tx_ucast_byte
);
445 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
446 "Bcast byte", tgt_stats
->tx_bcast_byte
);
447 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
448 "Rts success cnt", tgt_stats
->tx_rts_success_cnt
);
449 for (i
= 0; i
< 4; i
++)
450 len
+= scnprintf(buf
+ len
, buf_len
- len
,
451 "%18s %d %10llu\n", "PER on ac",
452 i
, tgt_stats
->tx_pkt_per_ac
[i
]);
453 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
454 "Error", tgt_stats
->tx_err
);
455 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
456 "Fail count", tgt_stats
->tx_fail_cnt
);
457 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
458 "Retry count", tgt_stats
->tx_retry_cnt
);
459 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
460 "Multi retry cnt", tgt_stats
->tx_mult_retry_cnt
);
461 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
462 "Rts fail cnt", tgt_stats
->tx_rts_fail_cnt
);
463 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s %10llu\n\n",
464 "TKIP counter measure used",
465 tgt_stats
->tkip_cnter_measures_invoked
);
467 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
469 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
470 "=================");
472 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
473 "Ucast packets", tgt_stats
->rx_ucast_pkt
);
474 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10d\n",
475 "Ucast Rate", tgt_stats
->rx_ucast_rate
);
476 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
477 "Bcast packets", tgt_stats
->rx_bcast_pkt
);
478 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
479 "Ucast byte", tgt_stats
->rx_ucast_byte
);
480 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
481 "Bcast byte", tgt_stats
->rx_bcast_byte
);
482 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
483 "Fragmented pkt", tgt_stats
->rx_frgment_pkt
);
484 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
485 "Error", tgt_stats
->rx_err
);
486 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
487 "CRC Err", tgt_stats
->rx_crc_err
);
488 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
489 "Key chache miss", tgt_stats
->rx_key_cache_miss
);
490 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
491 "Decrypt Err", tgt_stats
->rx_decrypt_err
);
492 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
493 "Duplicate frame", tgt_stats
->rx_dupl_frame
);
494 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
495 "Tkip Mic failure", tgt_stats
->tkip_local_mic_fail
);
496 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
497 "TKIP format err", tgt_stats
->tkip_fmt_err
);
498 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
499 "CCMP format Err", tgt_stats
->ccmp_fmt_err
);
500 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n\n",
501 "CCMP Replay Err", tgt_stats
->ccmp_replays
);
503 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
504 "Misc Target stats");
505 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
506 "=================");
507 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
508 "Beacon Miss count", tgt_stats
->cs_bmiss_cnt
);
509 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
510 "Num Connects", tgt_stats
->cs_connect_cnt
);
511 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
512 "Num disconnects", tgt_stats
->cs_discon_cnt
);
513 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10d\n",
514 "Beacon avg rssi", tgt_stats
->cs_ave_beacon_rssi
);
519 ret_cnt
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
525 static const struct file_operations fops_tgt_stats
= {
526 .read
= read_file_tgt_stats
,
527 .open
= ath6kl_debugfs_open
,
528 .owner
= THIS_MODULE
,
529 .llseek
= default_llseek
,
532 #define print_credit_info(fmt_str, ep_list_field) \
533 (len += scnprintf(buf + len, buf_len - len, fmt_str, \
534 ep_list->ep_list_field))
535 #define CREDIT_INFO_DISPLAY_STRING_LEN 200
536 #define CREDIT_INFO_LEN 128
538 static ssize_t
read_file_credit_dist_stats(struct file
*file
,
539 char __user
*user_buf
,
540 size_t count
, loff_t
*ppos
)
542 struct ath6kl
*ar
= file
->private_data
;
543 struct htc_target
*target
= ar
->htc_target
;
544 struct htc_endpoint_credit_dist
*ep_list
;
546 unsigned int buf_len
, len
= 0;
549 buf_len
= CREDIT_INFO_DISPLAY_STRING_LEN
+
550 get_queue_depth(&target
->cred_dist_list
) * CREDIT_INFO_LEN
;
551 buf
= kzalloc(buf_len
, GFP_KERNEL
);
555 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s%5d\n",
556 "Total Avail Credits: ",
557 target
->cred_dist_cntxt
->total_avail_credits
);
558 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s%5d\n",
560 target
->cred_dist_cntxt
->cur_free_credits
);
562 len
+= scnprintf(buf
+ len
, buf_len
- len
,
563 " Epid Flags Cred_norm Cred_min Credits Cred_assngd"
564 " Seek_cred Cred_sz Cred_per_msg Cred_to_dist"
567 list_for_each_entry(ep_list
, &target
->cred_dist_list
, list
) {
568 print_credit_info(" %2d", endpoint
);
569 print_credit_info("%10x", dist_flags
);
570 print_credit_info("%8d", cred_norm
);
571 print_credit_info("%9d", cred_min
);
572 print_credit_info("%9d", credits
);
573 print_credit_info("%10d", cred_assngd
);
574 print_credit_info("%13d", seek_cred
);
575 print_credit_info("%12d", cred_sz
);
576 print_credit_info("%9d", cred_per_msg
);
577 print_credit_info("%14d", cred_to_dist
);
578 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%12d\n",
579 get_queue_depth(&((struct htc_endpoint
*)
580 ep_list
->htc_rsvd
)->txq
));
586 ret_cnt
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
591 static const struct file_operations fops_credit_dist_stats
= {
592 .read
= read_file_credit_dist_stats
,
593 .open
= ath6kl_debugfs_open
,
594 .owner
= THIS_MODULE
,
595 .llseek
= default_llseek
,
598 static unsigned int print_endpoint_stat(struct htc_target
*target
, char *buf
,
599 unsigned int buf_len
, unsigned int len
,
600 int offset
, const char *name
)
603 struct htc_endpoint_stats
*ep_st
;
606 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%s:", name
);
607 for (i
= 0; i
< ENDPOINT_MAX
; i
++) {
608 ep_st
= &target
->endpoint
[i
].ep_st
;
609 counter
= ((u32
*) ep_st
) + (offset
/ 4);
610 len
+= scnprintf(buf
+ len
, buf_len
- len
, " %u", *counter
);
612 len
+= scnprintf(buf
+ len
, buf_len
- len
, "\n");
617 static ssize_t
ath6kl_endpoint_stats_read(struct file
*file
,
618 char __user
*user_buf
,
619 size_t count
, loff_t
*ppos
)
621 struct ath6kl
*ar
= file
->private_data
;
622 struct htc_target
*target
= ar
->htc_target
;
624 unsigned int buf_len
, len
= 0;
627 buf_len
= 1000 + ENDPOINT_MAX
* 100;
628 buf
= kzalloc(buf_len
, GFP_KERNEL
);
632 #define EPSTAT(name) \
633 len = print_endpoint_stat(target, buf, buf_len, len, \
634 offsetof(struct htc_endpoint_stats, name), \
636 EPSTAT(cred_low_indicate
);
638 EPSTAT(tx_pkt_bundled
);
642 EPSTAT(cred_rpt_from_rx
);
643 EPSTAT(cred_rpt_ep0
);
644 EPSTAT(cred_from_rx
);
645 EPSTAT(cred_from_other
);
646 EPSTAT(cred_from_ep0
);
652 EPSTAT(rx_bundle_lkahd
);
653 EPSTAT(rx_bundle_from_hdr
);
654 EPSTAT(rx_alloc_thresh_hit
);
655 EPSTAT(rxalloc_thresh_byte
);
661 ret_cnt
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
666 static ssize_t
ath6kl_endpoint_stats_write(struct file
*file
,
667 const char __user
*user_buf
,
668 size_t count
, loff_t
*ppos
)
670 struct ath6kl
*ar
= file
->private_data
;
671 struct htc_target
*target
= ar
->htc_target
;
674 struct htc_endpoint_stats
*ep_st
;
676 ret
= kstrtou32_from_user(user_buf
, count
, 0, &val
);
680 for (i
= 0; i
< ENDPOINT_MAX
; i
++) {
681 ep_st
= &target
->endpoint
[i
].ep_st
;
682 memset(ep_st
, 0, sizeof(*ep_st
));
689 static const struct file_operations fops_endpoint_stats
= {
690 .open
= ath6kl_debugfs_open
,
691 .read
= ath6kl_endpoint_stats_read
,
692 .write
= ath6kl_endpoint_stats_write
,
693 .owner
= THIS_MODULE
,
694 .llseek
= default_llseek
,
697 static unsigned long ath6kl_get_num_reg(void)
700 unsigned long n_reg
= 0;
702 for (i
= 0; i
< ARRAY_SIZE(diag_reg
); i
++)
704 (diag_reg
[i
].reg_end
- diag_reg
[i
].reg_start
) / 4 + 1;
709 static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr
)
713 for (i
= 0; i
< ARRAY_SIZE(diag_reg
); i
++) {
714 if (reg_addr
>= diag_reg
[i
].reg_start
&&
715 reg_addr
<= diag_reg
[i
].reg_end
)
722 static ssize_t
ath6kl_regread_read(struct file
*file
, char __user
*user_buf
,
723 size_t count
, loff_t
*ppos
)
725 struct ath6kl
*ar
= file
->private_data
;
727 unsigned int len
= 0;
729 if (ar
->debug
.dbgfs_diag_reg
)
730 len
+= scnprintf(buf
+ len
, sizeof(buf
) - len
, "0x%x\n",
731 ar
->debug
.dbgfs_diag_reg
);
733 len
+= scnprintf(buf
+ len
, sizeof(buf
) - len
,
734 "All diag registers\n");
736 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
739 static ssize_t
ath6kl_regread_write(struct file
*file
,
740 const char __user
*user_buf
,
741 size_t count
, loff_t
*ppos
)
743 struct ath6kl
*ar
= file
->private_data
;
746 unsigned long reg_addr
;
748 len
= min(count
, sizeof(buf
) - 1);
749 if (copy_from_user(buf
, user_buf
, len
))
754 if (strict_strtoul(buf
, 0, ®_addr
))
757 if ((reg_addr
% 4) != 0)
760 if (reg_addr
&& !ath6kl_dbg_is_diag_reg_valid(reg_addr
))
763 ar
->debug
.dbgfs_diag_reg
= reg_addr
;
768 static const struct file_operations fops_diag_reg_read
= {
769 .read
= ath6kl_regread_read
,
770 .write
= ath6kl_regread_write
,
771 .open
= ath6kl_debugfs_open
,
772 .owner
= THIS_MODULE
,
773 .llseek
= default_llseek
,
776 static int ath6kl_regdump_open(struct inode
*inode
, struct file
*file
)
778 struct ath6kl
*ar
= inode
->i_private
;
780 unsigned long int reg_len
;
781 unsigned int len
= 0, n_reg
;
786 /* Dump all the registers if no register is specified */
787 if (!ar
->debug
.dbgfs_diag_reg
)
788 n_reg
= ath6kl_get_num_reg();
792 reg_len
= n_reg
* REG_OUTPUT_LEN_PER_LINE
;
794 reg_len
+= REGTYPE_STR_LEN
;
796 buf
= vmalloc(reg_len
);
801 addr
= ar
->debug
.dbgfs_diag_reg
;
803 status
= ath6kl_diag_read32(ar
,
804 TARG_VTOP(ar
->target_type
, addr
),
809 len
+= scnprintf(buf
+ len
, reg_len
- len
,
810 "0x%06x 0x%08x\n", addr
, le32_to_cpu(reg_val
));
814 for (i
= 0; i
< ARRAY_SIZE(diag_reg
); i
++) {
815 len
+= scnprintf(buf
+ len
, reg_len
- len
,
816 "%s\n", diag_reg
[i
].reg_info
);
817 for (addr
= diag_reg
[i
].reg_start
;
818 addr
<= diag_reg
[i
].reg_end
; addr
+= 4) {
819 status
= ath6kl_diag_read32(ar
,
820 TARG_VTOP(ar
->target_type
, addr
),
825 len
+= scnprintf(buf
+ len
, reg_len
- len
,
827 addr
, le32_to_cpu(reg_val
));
832 file
->private_data
= buf
;
836 ath6kl_warn("Unable to read memory:%u\n", addr
);
841 static ssize_t
ath6kl_regdump_read(struct file
*file
, char __user
*user_buf
,
842 size_t count
, loff_t
*ppos
)
844 u8
*buf
= file
->private_data
;
845 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, strlen(buf
));
848 static int ath6kl_regdump_release(struct inode
*inode
, struct file
*file
)
850 vfree(file
->private_data
);
854 static const struct file_operations fops_reg_dump
= {
855 .open
= ath6kl_regdump_open
,
856 .read
= ath6kl_regdump_read
,
857 .release
= ath6kl_regdump_release
,
858 .owner
= THIS_MODULE
,
859 .llseek
= default_llseek
,
862 static ssize_t
ath6kl_lrssi_roam_write(struct file
*file
,
863 const char __user
*user_buf
,
864 size_t count
, loff_t
*ppos
)
866 struct ath6kl
*ar
= file
->private_data
;
867 unsigned long lrssi_roam_threshold
;
871 len
= min(count
, sizeof(buf
) - 1);
872 if (copy_from_user(buf
, user_buf
, len
))
876 if (strict_strtoul(buf
, 0, &lrssi_roam_threshold
))
879 ar
->lrssi_roam_threshold
= lrssi_roam_threshold
;
881 ath6kl_wmi_set_roam_lrssi_cmd(ar
->wmi
, ar
->lrssi_roam_threshold
);
886 static ssize_t
ath6kl_lrssi_roam_read(struct file
*file
,
887 char __user
*user_buf
,
888 size_t count
, loff_t
*ppos
)
890 struct ath6kl
*ar
= file
->private_data
;
894 len
= snprintf(buf
, sizeof(buf
), "%u\n", ar
->lrssi_roam_threshold
);
896 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
899 static const struct file_operations fops_lrssi_roam_threshold
= {
900 .read
= ath6kl_lrssi_roam_read
,
901 .write
= ath6kl_lrssi_roam_write
,
902 .open
= ath6kl_debugfs_open
,
903 .owner
= THIS_MODULE
,
904 .llseek
= default_llseek
,
907 static ssize_t
ath6kl_regwrite_read(struct file
*file
,
908 char __user
*user_buf
,
909 size_t count
, loff_t
*ppos
)
911 struct ath6kl
*ar
= file
->private_data
;
913 unsigned int len
= 0;
915 len
= scnprintf(buf
, sizeof(buf
), "Addr: 0x%x Val: 0x%x\n",
916 ar
->debug
.diag_reg_addr_wr
, ar
->debug
.diag_reg_val_wr
);
918 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
921 static ssize_t
ath6kl_regwrite_write(struct file
*file
,
922 const char __user
*user_buf
,
923 size_t count
, loff_t
*ppos
)
925 struct ath6kl
*ar
= file
->private_data
;
928 unsigned int len
= 0;
929 u32 reg_addr
, reg_val
;
931 len
= min(count
, sizeof(buf
) - 1);
932 if (copy_from_user(buf
, user_buf
, len
))
938 token
= strsep(&sptr
, "=");
942 if (kstrtou32(token
, 0, ®_addr
))
945 if (!ath6kl_dbg_is_diag_reg_valid(reg_addr
))
948 if (kstrtou32(sptr
, 0, ®_val
))
951 ar
->debug
.diag_reg_addr_wr
= reg_addr
;
952 ar
->debug
.diag_reg_val_wr
= reg_val
;
954 if (ath6kl_diag_write32(ar
, ar
->debug
.diag_reg_addr_wr
,
955 cpu_to_le32(ar
->debug
.diag_reg_val_wr
)))
961 static const struct file_operations fops_diag_reg_write
= {
962 .read
= ath6kl_regwrite_read
,
963 .write
= ath6kl_regwrite_write
,
964 .open
= ath6kl_debugfs_open
,
965 .owner
= THIS_MODULE
,
966 .llseek
= default_llseek
,
969 int ath6kl_debug_init(struct ath6kl
*ar
)
971 ar
->debug
.fwlog_buf
.buf
= vmalloc(ATH6KL_FWLOG_SIZE
);
972 if (ar
->debug
.fwlog_buf
.buf
== NULL
)
975 ar
->debug
.fwlog_tmp
= kmalloc(ATH6KL_FWLOG_SLOT_SIZE
, GFP_KERNEL
);
976 if (ar
->debug
.fwlog_tmp
== NULL
) {
977 vfree(ar
->debug
.fwlog_buf
.buf
);
981 spin_lock_init(&ar
->debug
.fwlog_lock
);
984 * Actually we are lying here but don't know how to read the mask
985 * value from the firmware.
987 ar
->debug
.fwlog_mask
= 0;
989 ar
->debugfs_phy
= debugfs_create_dir("ath6kl",
990 ar
->wdev
->wiphy
->debugfsdir
);
991 if (!ar
->debugfs_phy
) {
992 vfree(ar
->debug
.fwlog_buf
.buf
);
993 kfree(ar
->debug
.fwlog_tmp
);
997 debugfs_create_file("tgt_stats", S_IRUSR
, ar
->debugfs_phy
, ar
,
1000 debugfs_create_file("credit_dist_stats", S_IRUSR
, ar
->debugfs_phy
, ar
,
1001 &fops_credit_dist_stats
);
1003 debugfs_create_file("endpoint_stats", S_IRUSR
| S_IWUSR
,
1004 ar
->debugfs_phy
, ar
, &fops_endpoint_stats
);
1006 debugfs_create_file("fwlog", S_IRUSR
, ar
->debugfs_phy
, ar
,
1009 debugfs_create_file("fwlog_mask", S_IRUSR
| S_IWUSR
, ar
->debugfs_phy
,
1010 ar
, &fops_fwlog_mask
);
1012 debugfs_create_file("reg_addr", S_IRUSR
| S_IWUSR
, ar
->debugfs_phy
, ar
,
1013 &fops_diag_reg_read
);
1015 debugfs_create_file("reg_dump", S_IRUSR
, ar
->debugfs_phy
, ar
,
1018 debugfs_create_file("lrssi_roam_threshold", S_IRUSR
| S_IWUSR
,
1019 ar
->debugfs_phy
, ar
, &fops_lrssi_roam_threshold
);
1021 debugfs_create_file("reg_write", S_IRUSR
| S_IWUSR
,
1022 ar
->debugfs_phy
, ar
, &fops_diag_reg_write
);
1024 debugfs_create_file("war_stats", S_IRUSR
, ar
->debugfs_phy
, ar
,
1030 void ath6kl_debug_cleanup(struct ath6kl
*ar
)
1032 vfree(ar
->debug
.fwlog_buf
.buf
);
1033 kfree(ar
->debug
.fwlog_tmp
);