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>
22 #include <linux/export.h>
27 struct ath6kl_fwlog_slot
{
31 /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */
35 #define ATH6KL_FWLOG_SIZE 32768
36 #define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
37 ATH6KL_FWLOG_PAYLOAD_SIZE)
38 #define ATH6KL_FWLOG_VALID_MASK 0x1ffff
40 int ath6kl_printk(const char *level
, const char *fmt
, ...)
51 rtn
= printk("%sath6kl: %pV", level
, &vaf
);
58 #ifdef CONFIG_ATH6KL_DEBUG
60 #define REG_OUTPUT_LEN_PER_LINE 25
61 #define REGTYPE_STR_LEN 100
63 struct ath6kl_diag_reg_info
{
69 static const struct ath6kl_diag_reg_info diag_reg
[] = {
70 { 0x20000, 0x200fc, "General DMA and Rx registers" },
71 { 0x28000, 0x28900, "MAC PCU register & keycache" },
72 { 0x20800, 0x20a40, "QCU" },
73 { 0x21000, 0x212f0, "DCU" },
74 { 0x4000, 0x42e4, "RTC" },
75 { 0x540000, 0x540000 + (256 * 1024), "RAM" },
76 { 0x29800, 0x2B210, "Base Band" },
77 { 0x1C000, 0x1C748, "Analog" },
80 void ath6kl_dump_registers(struct ath6kl_device
*dev
,
81 struct ath6kl_irq_proc_registers
*irq_proc_reg
,
82 struct ath6kl_irq_enable_reg
*irq_enable_reg
)
85 ath6kl_dbg(ATH6KL_DBG_ANY
, ("<------- Register Table -------->\n"));
87 if (irq_proc_reg
!= NULL
) {
88 ath6kl_dbg(ATH6KL_DBG_ANY
,
89 "Host Int status: 0x%x\n",
90 irq_proc_reg
->host_int_status
);
91 ath6kl_dbg(ATH6KL_DBG_ANY
,
92 "CPU Int status: 0x%x\n",
93 irq_proc_reg
->cpu_int_status
);
94 ath6kl_dbg(ATH6KL_DBG_ANY
,
95 "Error Int status: 0x%x\n",
96 irq_proc_reg
->error_int_status
);
97 ath6kl_dbg(ATH6KL_DBG_ANY
,
98 "Counter Int status: 0x%x\n",
99 irq_proc_reg
->counter_int_status
);
100 ath6kl_dbg(ATH6KL_DBG_ANY
,
101 "Mbox Frame: 0x%x\n",
102 irq_proc_reg
->mbox_frame
);
103 ath6kl_dbg(ATH6KL_DBG_ANY
,
104 "Rx Lookahead Valid: 0x%x\n",
105 irq_proc_reg
->rx_lkahd_valid
);
106 ath6kl_dbg(ATH6KL_DBG_ANY
,
107 "Rx Lookahead 0: 0x%x\n",
108 irq_proc_reg
->rx_lkahd
[0]);
109 ath6kl_dbg(ATH6KL_DBG_ANY
,
110 "Rx Lookahead 1: 0x%x\n",
111 irq_proc_reg
->rx_lkahd
[1]);
113 if (dev
->ar
->mbox_info
.gmbox_addr
!= 0) {
115 * If the target supports GMBOX hardware, dump some
118 ath6kl_dbg(ATH6KL_DBG_ANY
,
119 "GMBOX Host Int status 2: 0x%x\n",
120 irq_proc_reg
->host_int_status2
);
121 ath6kl_dbg(ATH6KL_DBG_ANY
,
122 "GMBOX RX Avail: 0x%x\n",
123 irq_proc_reg
->gmbox_rx_avail
);
124 ath6kl_dbg(ATH6KL_DBG_ANY
,
125 "GMBOX lookahead alias 0: 0x%x\n",
126 irq_proc_reg
->rx_gmbox_lkahd_alias
[0]);
127 ath6kl_dbg(ATH6KL_DBG_ANY
,
128 "GMBOX lookahead alias 1: 0x%x\n",
129 irq_proc_reg
->rx_gmbox_lkahd_alias
[1]);
134 if (irq_enable_reg
!= NULL
) {
135 ath6kl_dbg(ATH6KL_DBG_ANY
,
136 "Int status Enable: 0x%x\n",
137 irq_enable_reg
->int_status_en
);
138 ath6kl_dbg(ATH6KL_DBG_ANY
, "Counter Int status Enable: 0x%x\n",
139 irq_enable_reg
->cntr_int_status_en
);
141 ath6kl_dbg(ATH6KL_DBG_ANY
, "<------------------------------->\n");
144 static void dump_cred_dist(struct htc_endpoint_credit_dist
*ep_dist
)
146 ath6kl_dbg(ATH6KL_DBG_ANY
,
147 "--- endpoint: %d svc_id: 0x%X ---\n",
148 ep_dist
->endpoint
, ep_dist
->svc_id
);
149 ath6kl_dbg(ATH6KL_DBG_ANY
, " dist_flags : 0x%X\n",
150 ep_dist
->dist_flags
);
151 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_norm : %d\n",
153 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_min : %d\n",
155 ath6kl_dbg(ATH6KL_DBG_ANY
, " credits : %d\n",
157 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_assngd : %d\n",
158 ep_dist
->cred_assngd
);
159 ath6kl_dbg(ATH6KL_DBG_ANY
, " seek_cred : %d\n",
161 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_sz : %d\n",
163 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_per_msg : %d\n",
164 ep_dist
->cred_per_msg
);
165 ath6kl_dbg(ATH6KL_DBG_ANY
, " cred_to_dist : %d\n",
166 ep_dist
->cred_to_dist
);
167 ath6kl_dbg(ATH6KL_DBG_ANY
, " txq_depth : %d\n",
168 get_queue_depth(&((struct htc_endpoint
*)
169 ep_dist
->htc_rsvd
)->txq
));
170 ath6kl_dbg(ATH6KL_DBG_ANY
,
171 "----------------------------------\n");
174 void dump_cred_dist_stats(struct htc_target
*target
)
176 struct htc_endpoint_credit_dist
*ep_list
;
178 if (!AR_DBG_LVL_CHECK(ATH6KL_DBG_TRC
))
181 list_for_each_entry(ep_list
, &target
->cred_dist_list
, list
)
182 dump_cred_dist(ep_list
);
184 ath6kl_dbg(ATH6KL_DBG_HTC_SEND
, "ctxt:%p dist:%p\n",
185 target
->cred_dist_cntxt
, NULL
);
186 ath6kl_dbg(ATH6KL_DBG_TRC
, "credit distribution, total : %d, free : %d\n",
187 target
->cred_dist_cntxt
->total_avail_credits
,
188 target
->cred_dist_cntxt
->cur_free_credits
);
191 static int ath6kl_debugfs_open(struct inode
*inode
, struct file
*file
)
193 file
->private_data
= inode
->i_private
;
197 void ath6kl_debug_war(struct ath6kl
*ar
, enum ath6kl_war war
)
200 case ATH6KL_WAR_INVALID_RATE
:
201 ar
->debug
.war_stats
.invalid_rate
++;
206 static ssize_t
read_file_war_stats(struct file
*file
, char __user
*user_buf
,
207 size_t count
, loff_t
*ppos
)
209 struct ath6kl
*ar
= file
->private_data
;
211 unsigned int len
= 0, buf_len
= 1500;
214 buf
= kzalloc(buf_len
, GFP_KERNEL
);
218 len
+= scnprintf(buf
+ len
, buf_len
- len
, "\n");
219 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
221 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n\n",
222 "=================");
223 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10u\n",
224 "Invalid rates", ar
->debug
.war_stats
.invalid_rate
);
226 if (WARN_ON(len
> buf_len
))
229 ret_cnt
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
235 static const struct file_operations fops_war_stats
= {
236 .read
= read_file_war_stats
,
237 .open
= ath6kl_debugfs_open
,
238 .owner
= THIS_MODULE
,
239 .llseek
= default_llseek
,
242 static void ath6kl_debug_fwlog_add(struct ath6kl
*ar
, const void *buf
,
245 struct circ_buf
*fwlog
= &ar
->debug
.fwlog_buf
;
249 /* entries must all be equal size */
250 if (WARN_ON(buf_len
!= ATH6KL_FWLOG_SLOT_SIZE
))
253 space
= CIRC_SPACE(fwlog
->head
, fwlog
->tail
, ATH6KL_FWLOG_SIZE
);
255 /* discard oldest slot */
256 fwlog
->tail
= (fwlog
->tail
+ ATH6KL_FWLOG_SLOT_SIZE
) &
257 (ATH6KL_FWLOG_SIZE
- 1);
259 for (i
= 0; i
< buf_len
; i
+= space
) {
260 space
= CIRC_SPACE_TO_END(fwlog
->head
, fwlog
->tail
,
263 if ((size_t) space
> buf_len
- i
)
266 memcpy(&fwlog
->buf
[fwlog
->head
], buf
, space
);
267 fwlog
->head
= (fwlog
->head
+ space
) & (ATH6KL_FWLOG_SIZE
- 1);
272 void ath6kl_debug_fwlog_event(struct ath6kl
*ar
, const void *buf
, size_t len
)
274 struct ath6kl_fwlog_slot
*slot
= ar
->debug
.fwlog_tmp
;
277 if (WARN_ON(len
> ATH6KL_FWLOG_PAYLOAD_SIZE
))
280 spin_lock_bh(&ar
->debug
.fwlog_lock
);
282 slot
->timestamp
= cpu_to_le32(jiffies
);
283 slot
->length
= cpu_to_le32(len
);
284 memcpy(slot
->payload
, buf
, len
);
286 slot_len
= sizeof(*slot
) + len
;
288 if (slot_len
< ATH6KL_FWLOG_SLOT_SIZE
)
289 memset(slot
->payload
+ len
, 0,
290 ATH6KL_FWLOG_SLOT_SIZE
- slot_len
);
292 ath6kl_debug_fwlog_add(ar
, slot
, ATH6KL_FWLOG_SLOT_SIZE
);
294 spin_unlock_bh(&ar
->debug
.fwlog_lock
);
297 static bool ath6kl_debug_fwlog_empty(struct ath6kl
*ar
)
299 return CIRC_CNT(ar
->debug
.fwlog_buf
.head
,
300 ar
->debug
.fwlog_buf
.tail
,
301 ATH6KL_FWLOG_SLOT_SIZE
) == 0;
304 static ssize_t
ath6kl_fwlog_read(struct file
*file
, char __user
*user_buf
,
305 size_t count
, loff_t
*ppos
)
307 struct ath6kl
*ar
= file
->private_data
;
308 struct circ_buf
*fwlog
= &ar
->debug
.fwlog_buf
;
309 size_t len
= 0, buf_len
= count
;
314 buf
= vmalloc(buf_len
);
318 /* read undelivered logs from firmware */
319 ath6kl_read_fwlogs(ar
);
321 spin_lock_bh(&ar
->debug
.fwlog_lock
);
323 while (len
< buf_len
&& !ath6kl_debug_fwlog_empty(ar
)) {
324 ccnt
= CIRC_CNT_TO_END(fwlog
->head
, fwlog
->tail
,
327 if ((size_t) ccnt
> buf_len
- len
)
328 ccnt
= buf_len
- len
;
330 memcpy(buf
+ len
, &fwlog
->buf
[fwlog
->tail
], ccnt
);
333 fwlog
->tail
= (fwlog
->tail
+ ccnt
) &
334 (ATH6KL_FWLOG_SIZE
- 1);
337 spin_unlock_bh(&ar
->debug
.fwlog_lock
);
339 if (WARN_ON(len
> buf_len
))
342 ret_cnt
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
349 static const struct file_operations fops_fwlog
= {
350 .open
= ath6kl_debugfs_open
,
351 .read
= ath6kl_fwlog_read
,
352 .owner
= THIS_MODULE
,
353 .llseek
= default_llseek
,
356 static ssize_t
ath6kl_fwlog_mask_read(struct file
*file
, char __user
*user_buf
,
357 size_t count
, loff_t
*ppos
)
359 struct ath6kl
*ar
= file
->private_data
;
363 len
= snprintf(buf
, sizeof(buf
), "0x%x\n", ar
->debug
.fwlog_mask
);
365 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
368 static ssize_t
ath6kl_fwlog_mask_write(struct file
*file
,
369 const char __user
*user_buf
,
370 size_t count
, loff_t
*ppos
)
372 struct ath6kl
*ar
= file
->private_data
;
375 ret
= kstrtou32_from_user(user_buf
, count
, 0, &ar
->debug
.fwlog_mask
);
379 ret
= ath6kl_wmi_config_debug_module_cmd(ar
->wmi
,
380 ATH6KL_FWLOG_VALID_MASK
,
381 ar
->debug
.fwlog_mask
);
388 static const struct file_operations fops_fwlog_mask
= {
389 .open
= ath6kl_debugfs_open
,
390 .read
= ath6kl_fwlog_mask_read
,
391 .write
= ath6kl_fwlog_mask_write
,
392 .owner
= THIS_MODULE
,
393 .llseek
= default_llseek
,
396 static ssize_t
read_file_tgt_stats(struct file
*file
, char __user
*user_buf
,
397 size_t count
, loff_t
*ppos
)
399 struct ath6kl
*ar
= file
->private_data
;
400 struct target_stats
*tgt_stats
= &ar
->target_stats
;
402 unsigned int len
= 0, buf_len
= 1500;
407 buf
= kzalloc(buf_len
, GFP_KERNEL
);
411 if (down_interruptible(&ar
->sem
)) {
416 set_bit(STATS_UPDATE_PEND
, &ar
->flag
);
418 if (ath6kl_wmi_get_stats_cmd(ar
->wmi
)) {
424 left
= wait_event_interruptible_timeout(ar
->event_wq
,
425 !test_bit(STATS_UPDATE_PEND
,
426 &ar
->flag
), WMI_TIMEOUT
);
435 len
+= scnprintf(buf
+ len
, buf_len
- len
, "\n");
436 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
438 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n\n",
439 "=================");
440 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
441 "Ucast packets", tgt_stats
->tx_ucast_pkt
);
442 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
443 "Bcast packets", tgt_stats
->tx_bcast_pkt
);
444 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
445 "Ucast byte", tgt_stats
->tx_ucast_byte
);
446 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
447 "Bcast byte", tgt_stats
->tx_bcast_byte
);
448 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
449 "Rts success cnt", tgt_stats
->tx_rts_success_cnt
);
450 for (i
= 0; i
< 4; i
++)
451 len
+= scnprintf(buf
+ len
, buf_len
- len
,
452 "%18s %d %10llu\n", "PER on ac",
453 i
, tgt_stats
->tx_pkt_per_ac
[i
]);
454 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
455 "Error", tgt_stats
->tx_err
);
456 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
457 "Fail count", tgt_stats
->tx_fail_cnt
);
458 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
459 "Retry count", tgt_stats
->tx_retry_cnt
);
460 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
461 "Multi retry cnt", tgt_stats
->tx_mult_retry_cnt
);
462 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
463 "Rts fail cnt", tgt_stats
->tx_rts_fail_cnt
);
464 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s %10llu\n\n",
465 "TKIP counter measure used",
466 tgt_stats
->tkip_cnter_measures_invoked
);
468 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
470 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
471 "=================");
473 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
474 "Ucast packets", tgt_stats
->rx_ucast_pkt
);
475 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10d\n",
476 "Ucast Rate", tgt_stats
->rx_ucast_rate
);
477 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
478 "Bcast packets", tgt_stats
->rx_bcast_pkt
);
479 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
480 "Ucast byte", tgt_stats
->rx_ucast_byte
);
481 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
482 "Bcast byte", tgt_stats
->rx_bcast_byte
);
483 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
484 "Fragmented pkt", tgt_stats
->rx_frgment_pkt
);
485 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
486 "Error", tgt_stats
->rx_err
);
487 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
488 "CRC Err", tgt_stats
->rx_crc_err
);
489 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
490 "Key chache miss", tgt_stats
->rx_key_cache_miss
);
491 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
492 "Decrypt Err", tgt_stats
->rx_decrypt_err
);
493 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
494 "Duplicate frame", tgt_stats
->rx_dupl_frame
);
495 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
496 "Tkip Mic failure", tgt_stats
->tkip_local_mic_fail
);
497 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
498 "TKIP format err", tgt_stats
->tkip_fmt_err
);
499 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
500 "CCMP format Err", tgt_stats
->ccmp_fmt_err
);
501 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n\n",
502 "CCMP Replay Err", tgt_stats
->ccmp_replays
);
504 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
505 "Misc Target stats");
506 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s\n",
507 "=================");
508 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
509 "Beacon Miss count", tgt_stats
->cs_bmiss_cnt
);
510 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
511 "Num Connects", tgt_stats
->cs_connect_cnt
);
512 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10llu\n",
513 "Num disconnects", tgt_stats
->cs_discon_cnt
);
514 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%20s %10d\n",
515 "Beacon avg rssi", tgt_stats
->cs_ave_beacon_rssi
);
520 ret_cnt
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
526 static const struct file_operations fops_tgt_stats
= {
527 .read
= read_file_tgt_stats
,
528 .open
= ath6kl_debugfs_open
,
529 .owner
= THIS_MODULE
,
530 .llseek
= default_llseek
,
533 #define print_credit_info(fmt_str, ep_list_field) \
534 (len += scnprintf(buf + len, buf_len - len, fmt_str, \
535 ep_list->ep_list_field))
536 #define CREDIT_INFO_DISPLAY_STRING_LEN 200
537 #define CREDIT_INFO_LEN 128
539 static ssize_t
read_file_credit_dist_stats(struct file
*file
,
540 char __user
*user_buf
,
541 size_t count
, loff_t
*ppos
)
543 struct ath6kl
*ar
= file
->private_data
;
544 struct htc_target
*target
= ar
->htc_target
;
545 struct htc_endpoint_credit_dist
*ep_list
;
547 unsigned int buf_len
, len
= 0;
550 buf_len
= CREDIT_INFO_DISPLAY_STRING_LEN
+
551 get_queue_depth(&target
->cred_dist_list
) * CREDIT_INFO_LEN
;
552 buf
= kzalloc(buf_len
, GFP_KERNEL
);
556 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s%5d\n",
557 "Total Avail Credits: ",
558 target
->cred_dist_cntxt
->total_avail_credits
);
559 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%25s%5d\n",
561 target
->cred_dist_cntxt
->cur_free_credits
);
563 len
+= scnprintf(buf
+ len
, buf_len
- len
,
564 " Epid Flags Cred_norm Cred_min Credits Cred_assngd"
565 " Seek_cred Cred_sz Cred_per_msg Cred_to_dist"
568 list_for_each_entry(ep_list
, &target
->cred_dist_list
, list
) {
569 print_credit_info(" %2d", endpoint
);
570 print_credit_info("%10x", dist_flags
);
571 print_credit_info("%8d", cred_norm
);
572 print_credit_info("%9d", cred_min
);
573 print_credit_info("%9d", credits
);
574 print_credit_info("%10d", cred_assngd
);
575 print_credit_info("%13d", seek_cred
);
576 print_credit_info("%12d", cred_sz
);
577 print_credit_info("%9d", cred_per_msg
);
578 print_credit_info("%14d", cred_to_dist
);
579 len
+= scnprintf(buf
+ len
, buf_len
- len
, "%12d\n",
580 get_queue_depth(&((struct htc_endpoint
*)
581 ep_list
->htc_rsvd
)->txq
));
587 ret_cnt
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
592 static const struct file_operations fops_credit_dist_stats
= {
593 .read
= read_file_credit_dist_stats
,
594 .open
= ath6kl_debugfs_open
,
595 .owner
= THIS_MODULE
,
596 .llseek
= default_llseek
,
599 static unsigned long ath6kl_get_num_reg(void)
602 unsigned long n_reg
= 0;
604 for (i
= 0; i
< ARRAY_SIZE(diag_reg
); i
++)
606 (diag_reg
[i
].reg_end
- diag_reg
[i
].reg_start
) / 4 + 1;
611 static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr
)
615 for (i
= 0; i
< ARRAY_SIZE(diag_reg
); i
++) {
616 if (reg_addr
>= diag_reg
[i
].reg_start
&&
617 reg_addr
<= diag_reg
[i
].reg_end
)
624 static ssize_t
ath6kl_regread_read(struct file
*file
, char __user
*user_buf
,
625 size_t count
, loff_t
*ppos
)
627 struct ath6kl
*ar
= file
->private_data
;
629 unsigned int len
= 0;
631 if (ar
->debug
.dbgfs_diag_reg
)
632 len
+= scnprintf(buf
+ len
, sizeof(buf
) - len
, "0x%x\n",
633 ar
->debug
.dbgfs_diag_reg
);
635 len
+= scnprintf(buf
+ len
, sizeof(buf
) - len
,
636 "All diag registers\n");
638 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
641 static ssize_t
ath6kl_regread_write(struct file
*file
,
642 const char __user
*user_buf
,
643 size_t count
, loff_t
*ppos
)
645 struct ath6kl
*ar
= file
->private_data
;
648 unsigned long reg_addr
;
650 len
= min(count
, sizeof(buf
) - 1);
651 if (copy_from_user(buf
, user_buf
, len
))
656 if (strict_strtoul(buf
, 0, ®_addr
))
659 if ((reg_addr
% 4) != 0)
662 if (reg_addr
&& !ath6kl_dbg_is_diag_reg_valid(reg_addr
))
665 ar
->debug
.dbgfs_diag_reg
= reg_addr
;
670 static const struct file_operations fops_diag_reg_read
= {
671 .read
= ath6kl_regread_read
,
672 .write
= ath6kl_regread_write
,
673 .open
= ath6kl_debugfs_open
,
674 .owner
= THIS_MODULE
,
675 .llseek
= default_llseek
,
678 static int ath6kl_regdump_open(struct inode
*inode
, struct file
*file
)
680 struct ath6kl
*ar
= inode
->i_private
;
682 unsigned long int reg_len
;
683 unsigned int len
= 0, n_reg
;
688 /* Dump all the registers if no register is specified */
689 if (!ar
->debug
.dbgfs_diag_reg
)
690 n_reg
= ath6kl_get_num_reg();
694 reg_len
= n_reg
* REG_OUTPUT_LEN_PER_LINE
;
696 reg_len
+= REGTYPE_STR_LEN
;
698 buf
= vmalloc(reg_len
);
703 addr
= ar
->debug
.dbgfs_diag_reg
;
705 status
= ath6kl_diag_read32(ar
,
706 TARG_VTOP(ar
->target_type
, addr
),
711 len
+= scnprintf(buf
+ len
, reg_len
- len
,
712 "0x%06x 0x%08x\n", addr
, le32_to_cpu(reg_val
));
716 for (i
= 0; i
< ARRAY_SIZE(diag_reg
); i
++) {
717 len
+= scnprintf(buf
+ len
, reg_len
- len
,
718 "%s\n", diag_reg
[i
].reg_info
);
719 for (addr
= diag_reg
[i
].reg_start
;
720 addr
<= diag_reg
[i
].reg_end
; addr
+= 4) {
721 status
= ath6kl_diag_read32(ar
,
722 TARG_VTOP(ar
->target_type
, addr
),
727 len
+= scnprintf(buf
+ len
, reg_len
- len
,
729 addr
, le32_to_cpu(reg_val
));
734 file
->private_data
= buf
;
738 ath6kl_warn("Unable to read memory:%u\n", addr
);
743 static ssize_t
ath6kl_regdump_read(struct file
*file
, char __user
*user_buf
,
744 size_t count
, loff_t
*ppos
)
746 u8
*buf
= file
->private_data
;
747 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, strlen(buf
));
750 static int ath6kl_regdump_release(struct inode
*inode
, struct file
*file
)
752 vfree(file
->private_data
);
756 static const struct file_operations fops_reg_dump
= {
757 .open
= ath6kl_regdump_open
,
758 .read
= ath6kl_regdump_read
,
759 .release
= ath6kl_regdump_release
,
760 .owner
= THIS_MODULE
,
761 .llseek
= default_llseek
,
764 static ssize_t
ath6kl_lrssi_roam_write(struct file
*file
,
765 const char __user
*user_buf
,
766 size_t count
, loff_t
*ppos
)
768 struct ath6kl
*ar
= file
->private_data
;
769 unsigned long lrssi_roam_threshold
;
773 len
= min(count
, sizeof(buf
) - 1);
774 if (copy_from_user(buf
, user_buf
, len
))
778 if (strict_strtoul(buf
, 0, &lrssi_roam_threshold
))
781 ar
->lrssi_roam_threshold
= lrssi_roam_threshold
;
783 ath6kl_wmi_set_roam_lrssi_cmd(ar
->wmi
, ar
->lrssi_roam_threshold
);
788 static ssize_t
ath6kl_lrssi_roam_read(struct file
*file
,
789 char __user
*user_buf
,
790 size_t count
, loff_t
*ppos
)
792 struct ath6kl
*ar
= file
->private_data
;
796 len
= snprintf(buf
, sizeof(buf
), "%u\n", ar
->lrssi_roam_threshold
);
798 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
801 static const struct file_operations fops_lrssi_roam_threshold
= {
802 .read
= ath6kl_lrssi_roam_read
,
803 .write
= ath6kl_lrssi_roam_write
,
804 .open
= ath6kl_debugfs_open
,
805 .owner
= THIS_MODULE
,
806 .llseek
= default_llseek
,
809 static ssize_t
ath6kl_regwrite_read(struct file
*file
,
810 char __user
*user_buf
,
811 size_t count
, loff_t
*ppos
)
813 struct ath6kl
*ar
= file
->private_data
;
815 unsigned int len
= 0;
817 len
= scnprintf(buf
, sizeof(buf
), "Addr: 0x%x Val: 0x%x\n",
818 ar
->debug
.diag_reg_addr_wr
, ar
->debug
.diag_reg_val_wr
);
820 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
823 static ssize_t
ath6kl_regwrite_write(struct file
*file
,
824 const char __user
*user_buf
,
825 size_t count
, loff_t
*ppos
)
827 struct ath6kl
*ar
= file
->private_data
;
830 unsigned int len
= 0;
831 u32 reg_addr
, reg_val
;
833 len
= min(count
, sizeof(buf
) - 1);
834 if (copy_from_user(buf
, user_buf
, len
))
840 token
= strsep(&sptr
, "=");
844 if (kstrtou32(token
, 0, ®_addr
))
847 if (!ath6kl_dbg_is_diag_reg_valid(reg_addr
))
850 if (kstrtou32(sptr
, 0, ®_val
))
853 ar
->debug
.diag_reg_addr_wr
= reg_addr
;
854 ar
->debug
.diag_reg_val_wr
= reg_val
;
856 if (ath6kl_diag_write32(ar
, ar
->debug
.diag_reg_addr_wr
,
857 cpu_to_le32(ar
->debug
.diag_reg_val_wr
)))
863 static const struct file_operations fops_diag_reg_write
= {
864 .read
= ath6kl_regwrite_read
,
865 .write
= ath6kl_regwrite_write
,
866 .open
= ath6kl_debugfs_open
,
867 .owner
= THIS_MODULE
,
868 .llseek
= default_llseek
,
871 int ath6kl_debug_init(struct ath6kl
*ar
)
873 ar
->debug
.fwlog_buf
.buf
= vmalloc(ATH6KL_FWLOG_SIZE
);
874 if (ar
->debug
.fwlog_buf
.buf
== NULL
)
877 ar
->debug
.fwlog_tmp
= kmalloc(ATH6KL_FWLOG_SLOT_SIZE
, GFP_KERNEL
);
878 if (ar
->debug
.fwlog_tmp
== NULL
) {
879 vfree(ar
->debug
.fwlog_buf
.buf
);
883 spin_lock_init(&ar
->debug
.fwlog_lock
);
886 * Actually we are lying here but don't know how to read the mask
887 * value from the firmware.
889 ar
->debug
.fwlog_mask
= 0;
891 ar
->debugfs_phy
= debugfs_create_dir("ath6kl",
892 ar
->wdev
->wiphy
->debugfsdir
);
893 if (!ar
->debugfs_phy
) {
894 vfree(ar
->debug
.fwlog_buf
.buf
);
895 kfree(ar
->debug
.fwlog_tmp
);
899 debugfs_create_file("tgt_stats", S_IRUSR
, ar
->debugfs_phy
, ar
,
902 debugfs_create_file("credit_dist_stats", S_IRUSR
, ar
->debugfs_phy
, ar
,
903 &fops_credit_dist_stats
);
905 debugfs_create_file("fwlog", S_IRUSR
, ar
->debugfs_phy
, ar
,
908 debugfs_create_file("fwlog_mask", S_IRUSR
| S_IWUSR
, ar
->debugfs_phy
,
909 ar
, &fops_fwlog_mask
);
911 debugfs_create_file("reg_addr", S_IRUSR
| S_IWUSR
, ar
->debugfs_phy
, ar
,
912 &fops_diag_reg_read
);
914 debugfs_create_file("reg_dump", S_IRUSR
, ar
->debugfs_phy
, ar
,
917 debugfs_create_file("lrssi_roam_threshold", S_IRUSR
| S_IWUSR
,
918 ar
->debugfs_phy
, ar
, &fops_lrssi_roam_threshold
);
920 debugfs_create_file("reg_write", S_IRUSR
| S_IWUSR
,
921 ar
->debugfs_phy
, ar
, &fops_diag_reg_write
);
923 debugfs_create_file("war_stats", S_IRUSR
, ar
->debugfs_phy
, ar
,
929 void ath6kl_debug_cleanup(struct ath6kl
*ar
)
931 vfree(ar
->debug
.fwlog_buf
.buf
);
932 kfree(ar
->debug
.fwlog_tmp
);