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 long ath6kl_get_num_reg(void)
601 unsigned long n_reg
= 0;
603 for (i
= 0; i
< ARRAY_SIZE(diag_reg
); i
++)
605 (diag_reg
[i
].reg_end
- diag_reg
[i
].reg_start
) / 4 + 1;
610 static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr
)
614 for (i
= 0; i
< ARRAY_SIZE(diag_reg
); i
++) {
615 if (reg_addr
>= diag_reg
[i
].reg_start
&&
616 reg_addr
<= diag_reg
[i
].reg_end
)
623 static ssize_t
ath6kl_regread_read(struct file
*file
, char __user
*user_buf
,
624 size_t count
, loff_t
*ppos
)
626 struct ath6kl
*ar
= file
->private_data
;
628 unsigned int len
= 0;
630 if (ar
->debug
.dbgfs_diag_reg
)
631 len
+= scnprintf(buf
+ len
, sizeof(buf
) - len
, "0x%x\n",
632 ar
->debug
.dbgfs_diag_reg
);
634 len
+= scnprintf(buf
+ len
, sizeof(buf
) - len
,
635 "All diag registers\n");
637 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
640 static ssize_t
ath6kl_regread_write(struct file
*file
,
641 const char __user
*user_buf
,
642 size_t count
, loff_t
*ppos
)
644 struct ath6kl
*ar
= file
->private_data
;
647 unsigned long reg_addr
;
649 len
= min(count
, sizeof(buf
) - 1);
650 if (copy_from_user(buf
, user_buf
, len
))
655 if (strict_strtoul(buf
, 0, ®_addr
))
658 if ((reg_addr
% 4) != 0)
661 if (reg_addr
&& !ath6kl_dbg_is_diag_reg_valid(reg_addr
))
664 ar
->debug
.dbgfs_diag_reg
= reg_addr
;
669 static const struct file_operations fops_diag_reg_read
= {
670 .read
= ath6kl_regread_read
,
671 .write
= ath6kl_regread_write
,
672 .open
= ath6kl_debugfs_open
,
673 .owner
= THIS_MODULE
,
674 .llseek
= default_llseek
,
677 static int ath6kl_regdump_open(struct inode
*inode
, struct file
*file
)
679 struct ath6kl
*ar
= inode
->i_private
;
681 unsigned long int reg_len
;
682 unsigned int len
= 0, n_reg
;
687 /* Dump all the registers if no register is specified */
688 if (!ar
->debug
.dbgfs_diag_reg
)
689 n_reg
= ath6kl_get_num_reg();
693 reg_len
= n_reg
* REG_OUTPUT_LEN_PER_LINE
;
695 reg_len
+= REGTYPE_STR_LEN
;
697 buf
= vmalloc(reg_len
);
702 addr
= ar
->debug
.dbgfs_diag_reg
;
704 status
= ath6kl_diag_read32(ar
,
705 TARG_VTOP(ar
->target_type
, addr
),
710 len
+= scnprintf(buf
+ len
, reg_len
- len
,
711 "0x%06x 0x%08x\n", addr
, le32_to_cpu(reg_val
));
715 for (i
= 0; i
< ARRAY_SIZE(diag_reg
); i
++) {
716 len
+= scnprintf(buf
+ len
, reg_len
- len
,
717 "%s\n", diag_reg
[i
].reg_info
);
718 for (addr
= diag_reg
[i
].reg_start
;
719 addr
<= diag_reg
[i
].reg_end
; addr
+= 4) {
720 status
= ath6kl_diag_read32(ar
,
721 TARG_VTOP(ar
->target_type
, addr
),
726 len
+= scnprintf(buf
+ len
, reg_len
- len
,
728 addr
, le32_to_cpu(reg_val
));
733 file
->private_data
= buf
;
737 ath6kl_warn("Unable to read memory:%u\n", addr
);
742 static ssize_t
ath6kl_regdump_read(struct file
*file
, char __user
*user_buf
,
743 size_t count
, loff_t
*ppos
)
745 u8
*buf
= file
->private_data
;
746 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, strlen(buf
));
749 static int ath6kl_regdump_release(struct inode
*inode
, struct file
*file
)
751 vfree(file
->private_data
);
755 static const struct file_operations fops_reg_dump
= {
756 .open
= ath6kl_regdump_open
,
757 .read
= ath6kl_regdump_read
,
758 .release
= ath6kl_regdump_release
,
759 .owner
= THIS_MODULE
,
760 .llseek
= default_llseek
,
763 static ssize_t
ath6kl_lrssi_roam_write(struct file
*file
,
764 const char __user
*user_buf
,
765 size_t count
, loff_t
*ppos
)
767 struct ath6kl
*ar
= file
->private_data
;
768 unsigned long lrssi_roam_threshold
;
772 len
= min(count
, sizeof(buf
) - 1);
773 if (copy_from_user(buf
, user_buf
, len
))
777 if (strict_strtoul(buf
, 0, &lrssi_roam_threshold
))
780 ar
->lrssi_roam_threshold
= lrssi_roam_threshold
;
782 ath6kl_wmi_set_roam_lrssi_cmd(ar
->wmi
, ar
->lrssi_roam_threshold
);
787 static ssize_t
ath6kl_lrssi_roam_read(struct file
*file
,
788 char __user
*user_buf
,
789 size_t count
, loff_t
*ppos
)
791 struct ath6kl
*ar
= file
->private_data
;
795 len
= snprintf(buf
, sizeof(buf
), "%u\n", ar
->lrssi_roam_threshold
);
797 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
800 static const struct file_operations fops_lrssi_roam_threshold
= {
801 .read
= ath6kl_lrssi_roam_read
,
802 .write
= ath6kl_lrssi_roam_write
,
803 .open
= ath6kl_debugfs_open
,
804 .owner
= THIS_MODULE
,
805 .llseek
= default_llseek
,
808 static ssize_t
ath6kl_regwrite_read(struct file
*file
,
809 char __user
*user_buf
,
810 size_t count
, loff_t
*ppos
)
812 struct ath6kl
*ar
= file
->private_data
;
814 unsigned int len
= 0;
816 len
= scnprintf(buf
, sizeof(buf
), "Addr: 0x%x Val: 0x%x\n",
817 ar
->debug
.diag_reg_addr_wr
, ar
->debug
.diag_reg_val_wr
);
819 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
822 static ssize_t
ath6kl_regwrite_write(struct file
*file
,
823 const char __user
*user_buf
,
824 size_t count
, loff_t
*ppos
)
826 struct ath6kl
*ar
= file
->private_data
;
829 unsigned int len
= 0;
830 u32 reg_addr
, reg_val
;
832 len
= min(count
, sizeof(buf
) - 1);
833 if (copy_from_user(buf
, user_buf
, len
))
839 token
= strsep(&sptr
, "=");
843 if (kstrtou32(token
, 0, ®_addr
))
846 if (!ath6kl_dbg_is_diag_reg_valid(reg_addr
))
849 if (kstrtou32(sptr
, 0, ®_val
))
852 ar
->debug
.diag_reg_addr_wr
= reg_addr
;
853 ar
->debug
.diag_reg_val_wr
= reg_val
;
855 if (ath6kl_diag_write32(ar
, ar
->debug
.diag_reg_addr_wr
,
856 cpu_to_le32(ar
->debug
.diag_reg_val_wr
)))
862 static const struct file_operations fops_diag_reg_write
= {
863 .read
= ath6kl_regwrite_read
,
864 .write
= ath6kl_regwrite_write
,
865 .open
= ath6kl_debugfs_open
,
866 .owner
= THIS_MODULE
,
867 .llseek
= default_llseek
,
870 int ath6kl_debug_init(struct ath6kl
*ar
)
872 ar
->debug
.fwlog_buf
.buf
= vmalloc(ATH6KL_FWLOG_SIZE
);
873 if (ar
->debug
.fwlog_buf
.buf
== NULL
)
876 ar
->debug
.fwlog_tmp
= kmalloc(ATH6KL_FWLOG_SLOT_SIZE
, GFP_KERNEL
);
877 if (ar
->debug
.fwlog_tmp
== NULL
) {
878 vfree(ar
->debug
.fwlog_buf
.buf
);
882 spin_lock_init(&ar
->debug
.fwlog_lock
);
885 * Actually we are lying here but don't know how to read the mask
886 * value from the firmware.
888 ar
->debug
.fwlog_mask
= 0;
890 ar
->debugfs_phy
= debugfs_create_dir("ath6kl",
891 ar
->wdev
->wiphy
->debugfsdir
);
892 if (!ar
->debugfs_phy
) {
893 vfree(ar
->debug
.fwlog_buf
.buf
);
894 kfree(ar
->debug
.fwlog_tmp
);
898 debugfs_create_file("tgt_stats", S_IRUSR
, ar
->debugfs_phy
, ar
,
901 debugfs_create_file("credit_dist_stats", S_IRUSR
, ar
->debugfs_phy
, ar
,
902 &fops_credit_dist_stats
);
904 debugfs_create_file("fwlog", S_IRUSR
, ar
->debugfs_phy
, ar
,
907 debugfs_create_file("fwlog_mask", S_IRUSR
| S_IWUSR
, ar
->debugfs_phy
,
908 ar
, &fops_fwlog_mask
);
910 debugfs_create_file("reg_addr", S_IRUSR
| S_IWUSR
, ar
->debugfs_phy
, ar
,
911 &fops_diag_reg_read
);
913 debugfs_create_file("reg_dump", S_IRUSR
, ar
->debugfs_phy
, ar
,
916 debugfs_create_file("lrssi_roam_threshold", S_IRUSR
| S_IWUSR
,
917 ar
->debugfs_phy
, ar
, &fops_lrssi_roam_threshold
);
919 debugfs_create_file("reg_write", S_IRUSR
| S_IWUSR
,
920 ar
->debugfs_phy
, ar
, &fops_diag_reg_write
);
922 debugfs_create_file("war_stats", S_IRUSR
, ar
->debugfs_phy
, ar
,
928 void ath6kl_debug_cleanup(struct ath6kl
*ar
)
930 vfree(ar
->debug
.fwlog_buf
.buf
);
931 kfree(ar
->debug
.fwlog_tmp
);