Loading...
Note: File does not exist in v3.1.
1/*
2 * Copyright (c) 2004-2011 Atheros Communications Inc.
3 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include "core.h"
19
20#include <linux/skbuff.h>
21#include <linux/fs.h>
22#include <linux/vmalloc.h>
23#include <linux/export.h>
24
25#include "debug.h"
26#include "target.h"
27
28struct ath6kl_fwlog_slot {
29 __le32 timestamp;
30 __le32 length;
31
32 /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */
33 u8 payload[0];
34};
35
36#define ATH6KL_FWLOG_MAX_ENTRIES 20
37
38#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
39
40int ath6kl_printk(const char *level, const char *fmt, ...)
41{
42 struct va_format vaf;
43 va_list args;
44 int rtn;
45
46 va_start(args, fmt);
47
48 vaf.fmt = fmt;
49 vaf.va = &args;
50
51 rtn = printk("%sath6kl: %pV", level, &vaf);
52
53 va_end(args);
54
55 return rtn;
56}
57EXPORT_SYMBOL(ath6kl_printk);
58
59int ath6kl_info(const char *fmt, ...)
60{
61 struct va_format vaf = {
62 .fmt = fmt,
63 };
64 va_list args;
65 int ret;
66
67 va_start(args, fmt);
68 vaf.va = &args;
69 ret = ath6kl_printk(KERN_INFO, "%pV", &vaf);
70 trace_ath6kl_log_info(&vaf);
71 va_end(args);
72
73 return ret;
74}
75EXPORT_SYMBOL(ath6kl_info);
76
77int ath6kl_err(const char *fmt, ...)
78{
79 struct va_format vaf = {
80 .fmt = fmt,
81 };
82 va_list args;
83 int ret;
84
85 va_start(args, fmt);
86 vaf.va = &args;
87 ret = ath6kl_printk(KERN_ERR, "%pV", &vaf);
88 trace_ath6kl_log_err(&vaf);
89 va_end(args);
90
91 return ret;
92}
93EXPORT_SYMBOL(ath6kl_err);
94
95int ath6kl_warn(const char *fmt, ...)
96{
97 struct va_format vaf = {
98 .fmt = fmt,
99 };
100 va_list args;
101 int ret;
102
103 va_start(args, fmt);
104 vaf.va = &args;
105 ret = ath6kl_printk(KERN_WARNING, "%pV", &vaf);
106 trace_ath6kl_log_warn(&vaf);
107 va_end(args);
108
109 return ret;
110}
111EXPORT_SYMBOL(ath6kl_warn);
112
113#ifdef CONFIG_ATH6KL_DEBUG
114
115void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...)
116{
117 struct va_format vaf;
118 va_list args;
119
120 va_start(args, fmt);
121
122 vaf.fmt = fmt;
123 vaf.va = &args;
124
125 if (debug_mask & mask)
126 ath6kl_printk(KERN_DEBUG, "%pV", &vaf);
127
128 trace_ath6kl_log_dbg(mask, &vaf);
129
130 va_end(args);
131}
132EXPORT_SYMBOL(ath6kl_dbg);
133
134void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
135 const char *msg, const char *prefix,
136 const void *buf, size_t len)
137{
138 if (debug_mask & mask) {
139 if (msg)
140 ath6kl_dbg(mask, "%s\n", msg);
141
142 print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
143 }
144
145 /* tracing code doesn't like null strings :/ */
146 trace_ath6kl_log_dbg_dump(msg ? msg : "", prefix ? prefix : "",
147 buf, len);
148}
149EXPORT_SYMBOL(ath6kl_dbg_dump);
150
151#define REG_OUTPUT_LEN_PER_LINE 25
152#define REGTYPE_STR_LEN 100
153
154struct ath6kl_diag_reg_info {
155 u32 reg_start;
156 u32 reg_end;
157 const char *reg_info;
158};
159
160static const struct ath6kl_diag_reg_info diag_reg[] = {
161 { 0x20000, 0x200fc, "General DMA and Rx registers" },
162 { 0x28000, 0x28900, "MAC PCU register & keycache" },
163 { 0x20800, 0x20a40, "QCU" },
164 { 0x21000, 0x212f0, "DCU" },
165 { 0x4000, 0x42e4, "RTC" },
166 { 0x540000, 0x540000 + (256 * 1024), "RAM" },
167 { 0x29800, 0x2B210, "Base Band" },
168 { 0x1C000, 0x1C748, "Analog" },
169};
170
171void ath6kl_dump_registers(struct ath6kl_device *dev,
172 struct ath6kl_irq_proc_registers *irq_proc_reg,
173 struct ath6kl_irq_enable_reg *irq_enable_reg)
174{
175
176 ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- Register Table -------->\n"));
177
178 if (irq_proc_reg != NULL) {
179 ath6kl_dbg(ATH6KL_DBG_IRQ,
180 "Host Int status: 0x%x\n",
181 irq_proc_reg->host_int_status);
182 ath6kl_dbg(ATH6KL_DBG_IRQ,
183 "CPU Int status: 0x%x\n",
184 irq_proc_reg->cpu_int_status);
185 ath6kl_dbg(ATH6KL_DBG_IRQ,
186 "Error Int status: 0x%x\n",
187 irq_proc_reg->error_int_status);
188 ath6kl_dbg(ATH6KL_DBG_IRQ,
189 "Counter Int status: 0x%x\n",
190 irq_proc_reg->counter_int_status);
191 ath6kl_dbg(ATH6KL_DBG_IRQ,
192 "Mbox Frame: 0x%x\n",
193 irq_proc_reg->mbox_frame);
194 ath6kl_dbg(ATH6KL_DBG_IRQ,
195 "Rx Lookahead Valid: 0x%x\n",
196 irq_proc_reg->rx_lkahd_valid);
197 ath6kl_dbg(ATH6KL_DBG_IRQ,
198 "Rx Lookahead 0: 0x%x\n",
199 irq_proc_reg->rx_lkahd[0]);
200 ath6kl_dbg(ATH6KL_DBG_IRQ,
201 "Rx Lookahead 1: 0x%x\n",
202 irq_proc_reg->rx_lkahd[1]);
203
204 if (dev->ar->mbox_info.gmbox_addr != 0) {
205 /*
206 * If the target supports GMBOX hardware, dump some
207 * additional state.
208 */
209 ath6kl_dbg(ATH6KL_DBG_IRQ,
210 "GMBOX Host Int status 2: 0x%x\n",
211 irq_proc_reg->host_int_status2);
212 ath6kl_dbg(ATH6KL_DBG_IRQ,
213 "GMBOX RX Avail: 0x%x\n",
214 irq_proc_reg->gmbox_rx_avail);
215 ath6kl_dbg(ATH6KL_DBG_IRQ,
216 "GMBOX lookahead alias 0: 0x%x\n",
217 irq_proc_reg->rx_gmbox_lkahd_alias[0]);
218 ath6kl_dbg(ATH6KL_DBG_IRQ,
219 "GMBOX lookahead alias 1: 0x%x\n",
220 irq_proc_reg->rx_gmbox_lkahd_alias[1]);
221 }
222
223 }
224
225 if (irq_enable_reg != NULL) {
226 ath6kl_dbg(ATH6KL_DBG_IRQ,
227 "Int status Enable: 0x%x\n",
228 irq_enable_reg->int_status_en);
229 ath6kl_dbg(ATH6KL_DBG_IRQ, "Counter Int status Enable: 0x%x\n",
230 irq_enable_reg->cntr_int_status_en);
231 }
232 ath6kl_dbg(ATH6KL_DBG_IRQ, "<------------------------------->\n");
233}
234
235static void dump_cred_dist(struct htc_endpoint_credit_dist *ep_dist)
236{
237 ath6kl_dbg(ATH6KL_DBG_CREDIT,
238 "--- endpoint: %d svc_id: 0x%X ---\n",
239 ep_dist->endpoint, ep_dist->svc_id);
240 ath6kl_dbg(ATH6KL_DBG_CREDIT, " dist_flags : 0x%X\n",
241 ep_dist->dist_flags);
242 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_norm : %d\n",
243 ep_dist->cred_norm);
244 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_min : %d\n",
245 ep_dist->cred_min);
246 ath6kl_dbg(ATH6KL_DBG_CREDIT, " credits : %d\n",
247 ep_dist->credits);
248 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_assngd : %d\n",
249 ep_dist->cred_assngd);
250 ath6kl_dbg(ATH6KL_DBG_CREDIT, " seek_cred : %d\n",
251 ep_dist->seek_cred);
252 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_sz : %d\n",
253 ep_dist->cred_sz);
254 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_per_msg : %d\n",
255 ep_dist->cred_per_msg);
256 ath6kl_dbg(ATH6KL_DBG_CREDIT, " cred_to_dist : %d\n",
257 ep_dist->cred_to_dist);
258 ath6kl_dbg(ATH6KL_DBG_CREDIT, " txq_depth : %d\n",
259 get_queue_depth(&ep_dist->htc_ep->txq));
260 ath6kl_dbg(ATH6KL_DBG_CREDIT,
261 "----------------------------------\n");
262}
263
264/* FIXME: move to htc.c */
265void dump_cred_dist_stats(struct htc_target *target)
266{
267 struct htc_endpoint_credit_dist *ep_list;
268
269 list_for_each_entry(ep_list, &target->cred_dist_list, list)
270 dump_cred_dist(ep_list);
271
272 ath6kl_dbg(ATH6KL_DBG_CREDIT,
273 "credit distribution total %d free %d\n",
274 target->credit_info->total_avail_credits,
275 target->credit_info->cur_free_credits);
276}
277
278void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
279{
280 switch (war) {
281 case ATH6KL_WAR_INVALID_RATE:
282 ar->debug.war_stats.invalid_rate++;
283 break;
284 }
285}
286
287static ssize_t read_file_war_stats(struct file *file, char __user *user_buf,
288 size_t count, loff_t *ppos)
289{
290 struct ath6kl *ar = file->private_data;
291 char *buf;
292 unsigned int len = 0, buf_len = 1500;
293 ssize_t ret_cnt;
294
295 buf = kzalloc(buf_len, GFP_KERNEL);
296 if (!buf)
297 return -ENOMEM;
298
299 len += scnprintf(buf + len, buf_len - len, "\n");
300 len += scnprintf(buf + len, buf_len - len, "%25s\n",
301 "Workaround stats");
302 len += scnprintf(buf + len, buf_len - len, "%25s\n\n",
303 "=================");
304 len += scnprintf(buf + len, buf_len - len, "%20s %10u\n",
305 "Invalid rates", ar->debug.war_stats.invalid_rate);
306
307 if (WARN_ON(len > buf_len))
308 len = buf_len;
309
310 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
311
312 kfree(buf);
313 return ret_cnt;
314}
315
316static const struct file_operations fops_war_stats = {
317 .read = read_file_war_stats,
318 .open = simple_open,
319 .owner = THIS_MODULE,
320 .llseek = default_llseek,
321};
322
323void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
324{
325 struct ath6kl_fwlog_slot *slot;
326 struct sk_buff *skb;
327 size_t slot_len;
328
329 if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
330 return;
331
332 slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE;
333
334 skb = alloc_skb(slot_len, GFP_KERNEL);
335 if (!skb)
336 return;
337
338 slot = (struct ath6kl_fwlog_slot *) skb_put(skb, slot_len);
339 slot->timestamp = cpu_to_le32(jiffies);
340 slot->length = cpu_to_le32(len);
341 memcpy(slot->payload, buf, len);
342
343 /* Need to pad each record to fixed length ATH6KL_FWLOG_PAYLOAD_SIZE */
344 memset(slot->payload + len, 0, ATH6KL_FWLOG_PAYLOAD_SIZE - len);
345
346 spin_lock(&ar->debug.fwlog_queue.lock);
347
348 __skb_queue_tail(&ar->debug.fwlog_queue, skb);
349 complete(&ar->debug.fwlog_completion);
350
351 /* drop oldest entries */
352 while (skb_queue_len(&ar->debug.fwlog_queue) >
353 ATH6KL_FWLOG_MAX_ENTRIES) {
354 skb = __skb_dequeue(&ar->debug.fwlog_queue);
355 kfree_skb(skb);
356 }
357
358 spin_unlock(&ar->debug.fwlog_queue.lock);
359
360 return;
361}
362
363static int ath6kl_fwlog_open(struct inode *inode, struct file *file)
364{
365 struct ath6kl *ar = inode->i_private;
366
367 if (ar->debug.fwlog_open)
368 return -EBUSY;
369
370 ar->debug.fwlog_open = true;
371
372 file->private_data = inode->i_private;
373 return 0;
374}
375
376static int ath6kl_fwlog_release(struct inode *inode, struct file *file)
377{
378 struct ath6kl *ar = inode->i_private;
379
380 ar->debug.fwlog_open = false;
381
382 return 0;
383}
384
385static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
386 size_t count, loff_t *ppos)
387{
388 struct ath6kl *ar = file->private_data;
389 struct sk_buff *skb;
390 ssize_t ret_cnt;
391 size_t len = 0;
392 char *buf;
393
394 buf = vmalloc(count);
395 if (!buf)
396 return -ENOMEM;
397
398 /* read undelivered logs from firmware */
399 ath6kl_read_fwlogs(ar);
400
401 spin_lock(&ar->debug.fwlog_queue.lock);
402
403 while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) {
404 if (skb->len > count - len) {
405 /* not enough space, put skb back and leave */
406 __skb_queue_head(&ar->debug.fwlog_queue, skb);
407 break;
408 }
409
410
411 memcpy(buf + len, skb->data, skb->len);
412 len += skb->len;
413
414 kfree_skb(skb);
415 }
416
417 spin_unlock(&ar->debug.fwlog_queue.lock);
418
419 /* FIXME: what to do if len == 0? */
420
421 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
422
423 vfree(buf);
424
425 return ret_cnt;
426}
427
428static const struct file_operations fops_fwlog = {
429 .open = ath6kl_fwlog_open,
430 .release = ath6kl_fwlog_release,
431 .read = ath6kl_fwlog_read,
432 .owner = THIS_MODULE,
433 .llseek = default_llseek,
434};
435
436static ssize_t ath6kl_fwlog_block_read(struct file *file,
437 char __user *user_buf,
438 size_t count,
439 loff_t *ppos)
440{
441 struct ath6kl *ar = file->private_data;
442 struct sk_buff *skb;
443 ssize_t ret_cnt;
444 size_t len = 0, not_copied;
445 char *buf;
446 int ret;
447
448 buf = vmalloc(count);
449 if (!buf)
450 return -ENOMEM;
451
452 spin_lock(&ar->debug.fwlog_queue.lock);
453
454 if (skb_queue_len(&ar->debug.fwlog_queue) == 0) {
455 /* we must init under queue lock */
456 init_completion(&ar->debug.fwlog_completion);
457
458 spin_unlock(&ar->debug.fwlog_queue.lock);
459
460 ret = wait_for_completion_interruptible(
461 &ar->debug.fwlog_completion);
462 if (ret == -ERESTARTSYS) {
463 vfree(buf);
464 return ret;
465 }
466
467 spin_lock(&ar->debug.fwlog_queue.lock);
468 }
469
470 while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) {
471 if (skb->len > count - len) {
472 /* not enough space, put skb back and leave */
473 __skb_queue_head(&ar->debug.fwlog_queue, skb);
474 break;
475 }
476
477
478 memcpy(buf + len, skb->data, skb->len);
479 len += skb->len;
480
481 kfree_skb(skb);
482 }
483
484 spin_unlock(&ar->debug.fwlog_queue.lock);
485
486 /* FIXME: what to do if len == 0? */
487
488 not_copied = copy_to_user(user_buf, buf, len);
489 if (not_copied != 0) {
490 ret_cnt = -EFAULT;
491 goto out;
492 }
493
494 *ppos = *ppos + len;
495
496 ret_cnt = len;
497
498out:
499 vfree(buf);
500
501 return ret_cnt;
502}
503
504static const struct file_operations fops_fwlog_block = {
505 .open = ath6kl_fwlog_open,
506 .release = ath6kl_fwlog_release,
507 .read = ath6kl_fwlog_block_read,
508 .owner = THIS_MODULE,
509 .llseek = default_llseek,
510};
511
512static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf,
513 size_t count, loff_t *ppos)
514{
515 struct ath6kl *ar = file->private_data;
516 char buf[16];
517 int len;
518
519 len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask);
520
521 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
522}
523
524static ssize_t ath6kl_fwlog_mask_write(struct file *file,
525 const char __user *user_buf,
526 size_t count, loff_t *ppos)
527{
528 struct ath6kl *ar = file->private_data;
529 int ret;
530
531 ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask);
532 if (ret)
533 return ret;
534
535 ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi,
536 ATH6KL_FWLOG_VALID_MASK,
537 ar->debug.fwlog_mask);
538 if (ret)
539 return ret;
540
541 return count;
542}
543
544static const struct file_operations fops_fwlog_mask = {
545 .open = simple_open,
546 .read = ath6kl_fwlog_mask_read,
547 .write = ath6kl_fwlog_mask_write,
548 .owner = THIS_MODULE,
549 .llseek = default_llseek,
550};
551
552static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
553 size_t count, loff_t *ppos)
554{
555 struct ath6kl *ar = file->private_data;
556 struct ath6kl_vif *vif;
557 struct target_stats *tgt_stats;
558 char *buf;
559 unsigned int len = 0, buf_len = 1500;
560 int i;
561 long left;
562 ssize_t ret_cnt;
563
564 vif = ath6kl_vif_first(ar);
565 if (!vif)
566 return -EIO;
567
568 tgt_stats = &vif->target_stats;
569
570 buf = kzalloc(buf_len, GFP_KERNEL);
571 if (!buf)
572 return -ENOMEM;
573
574 if (down_interruptible(&ar->sem)) {
575 kfree(buf);
576 return -EBUSY;
577 }
578
579 set_bit(STATS_UPDATE_PEND, &vif->flags);
580
581 if (ath6kl_wmi_get_stats_cmd(ar->wmi, 0)) {
582 up(&ar->sem);
583 kfree(buf);
584 return -EIO;
585 }
586
587 left = wait_event_interruptible_timeout(ar->event_wq,
588 !test_bit(STATS_UPDATE_PEND,
589 &vif->flags), WMI_TIMEOUT);
590
591 up(&ar->sem);
592
593 if (left <= 0) {
594 kfree(buf);
595 return -ETIMEDOUT;
596 }
597
598 len += scnprintf(buf + len, buf_len - len, "\n");
599 len += scnprintf(buf + len, buf_len - len, "%25s\n",
600 "Target Tx stats");
601 len += scnprintf(buf + len, buf_len - len, "%25s\n\n",
602 "=================");
603 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
604 "Ucast packets", tgt_stats->tx_ucast_pkt);
605 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
606 "Bcast packets", tgt_stats->tx_bcast_pkt);
607 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
608 "Ucast byte", tgt_stats->tx_ucast_byte);
609 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
610 "Bcast byte", tgt_stats->tx_bcast_byte);
611 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
612 "Rts success cnt", tgt_stats->tx_rts_success_cnt);
613 for (i = 0; i < 4; i++)
614 len += scnprintf(buf + len, buf_len - len,
615 "%18s %d %10llu\n", "PER on ac",
616 i, tgt_stats->tx_pkt_per_ac[i]);
617 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
618 "Error", tgt_stats->tx_err);
619 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
620 "Fail count", tgt_stats->tx_fail_cnt);
621 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
622 "Retry count", tgt_stats->tx_retry_cnt);
623 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
624 "Multi retry cnt", tgt_stats->tx_mult_retry_cnt);
625 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
626 "Rts fail cnt", tgt_stats->tx_rts_fail_cnt);
627 len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n\n",
628 "TKIP counter measure used",
629 tgt_stats->tkip_cnter_measures_invoked);
630
631 len += scnprintf(buf + len, buf_len - len, "%25s\n",
632 "Target Rx stats");
633 len += scnprintf(buf + len, buf_len - len, "%25s\n",
634 "=================");
635
636 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
637 "Ucast packets", tgt_stats->rx_ucast_pkt);
638 len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
639 "Ucast Rate", tgt_stats->rx_ucast_rate);
640 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
641 "Bcast packets", tgt_stats->rx_bcast_pkt);
642 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
643 "Ucast byte", tgt_stats->rx_ucast_byte);
644 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
645 "Bcast byte", tgt_stats->rx_bcast_byte);
646 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
647 "Fragmented pkt", tgt_stats->rx_frgment_pkt);
648 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
649 "Error", tgt_stats->rx_err);
650 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
651 "CRC Err", tgt_stats->rx_crc_err);
652 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
653 "Key chache miss", tgt_stats->rx_key_cache_miss);
654 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
655 "Decrypt Err", tgt_stats->rx_decrypt_err);
656 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
657 "Duplicate frame", tgt_stats->rx_dupl_frame);
658 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
659 "Tkip Mic failure", tgt_stats->tkip_local_mic_fail);
660 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
661 "TKIP format err", tgt_stats->tkip_fmt_err);
662 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
663 "CCMP format Err", tgt_stats->ccmp_fmt_err);
664 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n\n",
665 "CCMP Replay Err", tgt_stats->ccmp_replays);
666
667 len += scnprintf(buf + len, buf_len - len, "%25s\n",
668 "Misc Target stats");
669 len += scnprintf(buf + len, buf_len - len, "%25s\n",
670 "=================");
671 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
672 "Beacon Miss count", tgt_stats->cs_bmiss_cnt);
673 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
674 "Num Connects", tgt_stats->cs_connect_cnt);
675 len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
676 "Num disconnects", tgt_stats->cs_discon_cnt);
677 len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
678 "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi);
679 len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
680 "ARP pkt received", tgt_stats->arp_received);
681 len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
682 "ARP pkt matched", tgt_stats->arp_matched);
683 len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
684 "ARP pkt replied", tgt_stats->arp_replied);
685
686 if (len > buf_len)
687 len = buf_len;
688
689 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
690
691 kfree(buf);
692 return ret_cnt;
693}
694
695static const struct file_operations fops_tgt_stats = {
696 .read = read_file_tgt_stats,
697 .open = simple_open,
698 .owner = THIS_MODULE,
699 .llseek = default_llseek,
700};
701
702#define print_credit_info(fmt_str, ep_list_field) \
703 (len += scnprintf(buf + len, buf_len - len, fmt_str, \
704 ep_list->ep_list_field))
705#define CREDIT_INFO_DISPLAY_STRING_LEN 200
706#define CREDIT_INFO_LEN 128
707
708static ssize_t read_file_credit_dist_stats(struct file *file,
709 char __user *user_buf,
710 size_t count, loff_t *ppos)
711{
712 struct ath6kl *ar = file->private_data;
713 struct htc_target *target = ar->htc_target;
714 struct htc_endpoint_credit_dist *ep_list;
715 char *buf;
716 unsigned int buf_len, len = 0;
717 ssize_t ret_cnt;
718
719 buf_len = CREDIT_INFO_DISPLAY_STRING_LEN +
720 get_queue_depth(&target->cred_dist_list) * CREDIT_INFO_LEN;
721 buf = kzalloc(buf_len, GFP_KERNEL);
722 if (!buf)
723 return -ENOMEM;
724
725 len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
726 "Total Avail Credits: ",
727 target->credit_info->total_avail_credits);
728 len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
729 "Free credits :",
730 target->credit_info->cur_free_credits);
731
732 len += scnprintf(buf + len, buf_len - len,
733 " Epid Flags Cred_norm Cred_min Credits Cred_assngd"
734 " Seek_cred Cred_sz Cred_per_msg Cred_to_dist"
735 " qdepth\n");
736
737 list_for_each_entry(ep_list, &target->cred_dist_list, list) {
738 print_credit_info(" %2d", endpoint);
739 print_credit_info("%10x", dist_flags);
740 print_credit_info("%8d", cred_norm);
741 print_credit_info("%9d", cred_min);
742 print_credit_info("%9d", credits);
743 print_credit_info("%10d", cred_assngd);
744 print_credit_info("%13d", seek_cred);
745 print_credit_info("%12d", cred_sz);
746 print_credit_info("%9d", cred_per_msg);
747 print_credit_info("%14d", cred_to_dist);
748 len += scnprintf(buf + len, buf_len - len, "%12d\n",
749 get_queue_depth(&ep_list->htc_ep->txq));
750 }
751
752 if (len > buf_len)
753 len = buf_len;
754
755 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
756 kfree(buf);
757 return ret_cnt;
758}
759
760static const struct file_operations fops_credit_dist_stats = {
761 .read = read_file_credit_dist_stats,
762 .open = simple_open,
763 .owner = THIS_MODULE,
764 .llseek = default_llseek,
765};
766
767static unsigned int print_endpoint_stat(struct htc_target *target, char *buf,
768 unsigned int buf_len, unsigned int len,
769 int offset, const char *name)
770{
771 int i;
772 struct htc_endpoint_stats *ep_st;
773 u32 *counter;
774
775 len += scnprintf(buf + len, buf_len - len, "%s:", name);
776 for (i = 0; i < ENDPOINT_MAX; i++) {
777 ep_st = &target->endpoint[i].ep_st;
778 counter = ((u32 *) ep_st) + (offset / 4);
779 len += scnprintf(buf + len, buf_len - len, " %u", *counter);
780 }
781 len += scnprintf(buf + len, buf_len - len, "\n");
782
783 return len;
784}
785
786static ssize_t ath6kl_endpoint_stats_read(struct file *file,
787 char __user *user_buf,
788 size_t count, loff_t *ppos)
789{
790 struct ath6kl *ar = file->private_data;
791 struct htc_target *target = ar->htc_target;
792 char *buf;
793 unsigned int buf_len, len = 0;
794 ssize_t ret_cnt;
795
796 buf_len = sizeof(struct htc_endpoint_stats) / sizeof(u32) *
797 (25 + ENDPOINT_MAX * 11);
798 buf = kmalloc(buf_len, GFP_KERNEL);
799 if (!buf)
800 return -ENOMEM;
801
802#define EPSTAT(name) \
803 do { \
804 len = print_endpoint_stat(target, buf, buf_len, len, \
805 offsetof(struct htc_endpoint_stats, \
806 name), \
807 #name); \
808 } while (0)
809
810 EPSTAT(cred_low_indicate);
811 EPSTAT(tx_issued);
812 EPSTAT(tx_pkt_bundled);
813 EPSTAT(tx_bundles);
814 EPSTAT(tx_dropped);
815 EPSTAT(tx_cred_rpt);
816 EPSTAT(cred_rpt_from_rx);
817 EPSTAT(cred_rpt_from_other);
818 EPSTAT(cred_rpt_ep0);
819 EPSTAT(cred_from_rx);
820 EPSTAT(cred_from_other);
821 EPSTAT(cred_from_ep0);
822 EPSTAT(cred_cosumd);
823 EPSTAT(cred_retnd);
824 EPSTAT(rx_pkts);
825 EPSTAT(rx_lkahds);
826 EPSTAT(rx_bundl);
827 EPSTAT(rx_bundle_lkahd);
828 EPSTAT(rx_bundle_from_hdr);
829 EPSTAT(rx_alloc_thresh_hit);
830 EPSTAT(rxalloc_thresh_byte);
831#undef EPSTAT
832
833 if (len > buf_len)
834 len = buf_len;
835
836 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
837 kfree(buf);
838 return ret_cnt;
839}
840
841static ssize_t ath6kl_endpoint_stats_write(struct file *file,
842 const char __user *user_buf,
843 size_t count, loff_t *ppos)
844{
845 struct ath6kl *ar = file->private_data;
846 struct htc_target *target = ar->htc_target;
847 int ret, i;
848 u32 val;
849 struct htc_endpoint_stats *ep_st;
850
851 ret = kstrtou32_from_user(user_buf, count, 0, &val);
852 if (ret)
853 return ret;
854 if (val == 0) {
855 for (i = 0; i < ENDPOINT_MAX; i++) {
856 ep_st = &target->endpoint[i].ep_st;
857 memset(ep_st, 0, sizeof(*ep_st));
858 }
859 }
860
861 return count;
862}
863
864static const struct file_operations fops_endpoint_stats = {
865 .open = simple_open,
866 .read = ath6kl_endpoint_stats_read,
867 .write = ath6kl_endpoint_stats_write,
868 .owner = THIS_MODULE,
869 .llseek = default_llseek,
870};
871
872static unsigned long ath6kl_get_num_reg(void)
873{
874 int i;
875 unsigned long n_reg = 0;
876
877 for (i = 0; i < ARRAY_SIZE(diag_reg); i++)
878 n_reg = n_reg +
879 (diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1;
880
881 return n_reg;
882}
883
884static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr)
885{
886 int i;
887
888 for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
889 if (reg_addr >= diag_reg[i].reg_start &&
890 reg_addr <= diag_reg[i].reg_end)
891 return true;
892 }
893
894 return false;
895}
896
897static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf,
898 size_t count, loff_t *ppos)
899{
900 struct ath6kl *ar = file->private_data;
901 u8 buf[50];
902 unsigned int len = 0;
903
904 if (ar->debug.dbgfs_diag_reg)
905 len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n",
906 ar->debug.dbgfs_diag_reg);
907 else
908 len += scnprintf(buf + len, sizeof(buf) - len,
909 "All diag registers\n");
910
911 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
912}
913
914static ssize_t ath6kl_regread_write(struct file *file,
915 const char __user *user_buf,
916 size_t count, loff_t *ppos)
917{
918 struct ath6kl *ar = file->private_data;
919 unsigned long reg_addr;
920
921 if (kstrtoul_from_user(user_buf, count, 0, ®_addr))
922 return -EINVAL;
923
924 if ((reg_addr % 4) != 0)
925 return -EINVAL;
926
927 if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr))
928 return -EINVAL;
929
930 ar->debug.dbgfs_diag_reg = reg_addr;
931
932 return count;
933}
934
935static const struct file_operations fops_diag_reg_read = {
936 .read = ath6kl_regread_read,
937 .write = ath6kl_regread_write,
938 .open = simple_open,
939 .owner = THIS_MODULE,
940 .llseek = default_llseek,
941};
942
943static int ath6kl_regdump_open(struct inode *inode, struct file *file)
944{
945 struct ath6kl *ar = inode->i_private;
946 u8 *buf;
947 unsigned long int reg_len;
948 unsigned int len = 0, n_reg;
949 u32 addr;
950 __le32 reg_val;
951 int i, status;
952
953 /* Dump all the registers if no register is specified */
954 if (!ar->debug.dbgfs_diag_reg)
955 n_reg = ath6kl_get_num_reg();
956 else
957 n_reg = 1;
958
959 reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE;
960 if (n_reg > 1)
961 reg_len += REGTYPE_STR_LEN;
962
963 buf = vmalloc(reg_len);
964 if (!buf)
965 return -ENOMEM;
966
967 if (n_reg == 1) {
968 addr = ar->debug.dbgfs_diag_reg;
969
970 status = ath6kl_diag_read32(ar,
971 TARG_VTOP(ar->target_type, addr),
972 (u32 *)®_val);
973 if (status)
974 goto fail_reg_read;
975
976 len += scnprintf(buf + len, reg_len - len,
977 "0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val));
978 goto done;
979 }
980
981 for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
982 len += scnprintf(buf + len, reg_len - len,
983 "%s\n", diag_reg[i].reg_info);
984 for (addr = diag_reg[i].reg_start;
985 addr <= diag_reg[i].reg_end; addr += 4) {
986 status = ath6kl_diag_read32(ar,
987 TARG_VTOP(ar->target_type, addr),
988 (u32 *)®_val);
989 if (status)
990 goto fail_reg_read;
991
992 len += scnprintf(buf + len, reg_len - len,
993 "0x%06x 0x%08x\n",
994 addr, le32_to_cpu(reg_val));
995 }
996 }
997
998done:
999 file->private_data = buf;
1000 return 0;
1001
1002fail_reg_read:
1003 ath6kl_warn("Unable to read memory:%u\n", addr);
1004 vfree(buf);
1005 return -EIO;
1006}
1007
1008static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf,
1009 size_t count, loff_t *ppos)
1010{
1011 u8 *buf = file->private_data;
1012 return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
1013}
1014
1015static int ath6kl_regdump_release(struct inode *inode, struct file *file)
1016{
1017 vfree(file->private_data);
1018 return 0;
1019}
1020
1021static const struct file_operations fops_reg_dump = {
1022 .open = ath6kl_regdump_open,
1023 .read = ath6kl_regdump_read,
1024 .release = ath6kl_regdump_release,
1025 .owner = THIS_MODULE,
1026 .llseek = default_llseek,
1027};
1028
1029static ssize_t ath6kl_lrssi_roam_write(struct file *file,
1030 const char __user *user_buf,
1031 size_t count, loff_t *ppos)
1032{
1033 struct ath6kl *ar = file->private_data;
1034 unsigned long lrssi_roam_threshold;
1035
1036 if (kstrtoul_from_user(user_buf, count, 0, &lrssi_roam_threshold))
1037 return -EINVAL;
1038
1039 ar->lrssi_roam_threshold = lrssi_roam_threshold;
1040
1041 ath6kl_wmi_set_roam_lrssi_cmd(ar->wmi, ar->lrssi_roam_threshold);
1042
1043 return count;
1044}
1045
1046static ssize_t ath6kl_lrssi_roam_read(struct file *file,
1047 char __user *user_buf,
1048 size_t count, loff_t *ppos)
1049{
1050 struct ath6kl *ar = file->private_data;
1051 char buf[32];
1052 unsigned int len;
1053
1054 len = snprintf(buf, sizeof(buf), "%u\n", ar->lrssi_roam_threshold);
1055
1056 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1057}
1058
1059static const struct file_operations fops_lrssi_roam_threshold = {
1060 .read = ath6kl_lrssi_roam_read,
1061 .write = ath6kl_lrssi_roam_write,
1062 .open = simple_open,
1063 .owner = THIS_MODULE,
1064 .llseek = default_llseek,
1065};
1066
1067static ssize_t ath6kl_regwrite_read(struct file *file,
1068 char __user *user_buf,
1069 size_t count, loff_t *ppos)
1070{
1071 struct ath6kl *ar = file->private_data;
1072 u8 buf[32];
1073 unsigned int len = 0;
1074
1075 len = scnprintf(buf, sizeof(buf), "Addr: 0x%x Val: 0x%x\n",
1076 ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr);
1077
1078 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1079}
1080
1081static ssize_t ath6kl_regwrite_write(struct file *file,
1082 const char __user *user_buf,
1083 size_t count, loff_t *ppos)
1084{
1085 struct ath6kl *ar = file->private_data;
1086 char buf[32];
1087 char *sptr, *token;
1088 unsigned int len = 0;
1089 u32 reg_addr, reg_val;
1090
1091 len = min(count, sizeof(buf) - 1);
1092 if (copy_from_user(buf, user_buf, len))
1093 return -EFAULT;
1094
1095 buf[len] = '\0';
1096 sptr = buf;
1097
1098 token = strsep(&sptr, "=");
1099 if (!token)
1100 return -EINVAL;
1101
1102 if (kstrtou32(token, 0, ®_addr))
1103 return -EINVAL;
1104
1105 if (!ath6kl_dbg_is_diag_reg_valid(reg_addr))
1106 return -EINVAL;
1107
1108 if (kstrtou32(sptr, 0, ®_val))
1109 return -EINVAL;
1110
1111 ar->debug.diag_reg_addr_wr = reg_addr;
1112 ar->debug.diag_reg_val_wr = reg_val;
1113
1114 if (ath6kl_diag_write32(ar, ar->debug.diag_reg_addr_wr,
1115 cpu_to_le32(ar->debug.diag_reg_val_wr)))
1116 return -EIO;
1117
1118 return count;
1119}
1120
1121static const struct file_operations fops_diag_reg_write = {
1122 .read = ath6kl_regwrite_read,
1123 .write = ath6kl_regwrite_write,
1124 .open = simple_open,
1125 .owner = THIS_MODULE,
1126 .llseek = default_llseek,
1127};
1128
1129int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf,
1130 size_t len)
1131{
1132 const struct wmi_target_roam_tbl *tbl;
1133 u16 num_entries;
1134
1135 if (len < sizeof(*tbl))
1136 return -EINVAL;
1137
1138 tbl = (const struct wmi_target_roam_tbl *) buf;
1139 num_entries = le16_to_cpu(tbl->num_entries);
1140 if (sizeof(*tbl) + num_entries * sizeof(struct wmi_bss_roam_info) >
1141 len)
1142 return -EINVAL;
1143
1144 if (ar->debug.roam_tbl == NULL ||
1145 ar->debug.roam_tbl_len < (unsigned int) len) {
1146 kfree(ar->debug.roam_tbl);
1147 ar->debug.roam_tbl = kmalloc(len, GFP_ATOMIC);
1148 if (ar->debug.roam_tbl == NULL)
1149 return -ENOMEM;
1150 }
1151
1152 memcpy(ar->debug.roam_tbl, buf, len);
1153 ar->debug.roam_tbl_len = len;
1154
1155 if (test_bit(ROAM_TBL_PEND, &ar->flag)) {
1156 clear_bit(ROAM_TBL_PEND, &ar->flag);
1157 wake_up(&ar->event_wq);
1158 }
1159
1160 return 0;
1161}
1162
1163static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf,
1164 size_t count, loff_t *ppos)
1165{
1166 struct ath6kl *ar = file->private_data;
1167 int ret;
1168 long left;
1169 struct wmi_target_roam_tbl *tbl;
1170 u16 num_entries, i;
1171 char *buf;
1172 unsigned int len, buf_len;
1173 ssize_t ret_cnt;
1174
1175 if (down_interruptible(&ar->sem))
1176 return -EBUSY;
1177
1178 set_bit(ROAM_TBL_PEND, &ar->flag);
1179
1180 ret = ath6kl_wmi_get_roam_tbl_cmd(ar->wmi);
1181 if (ret) {
1182 up(&ar->sem);
1183 return ret;
1184 }
1185
1186 left = wait_event_interruptible_timeout(
1187 ar->event_wq, !test_bit(ROAM_TBL_PEND, &ar->flag), WMI_TIMEOUT);
1188 up(&ar->sem);
1189
1190 if (left <= 0)
1191 return -ETIMEDOUT;
1192
1193 if (ar->debug.roam_tbl == NULL)
1194 return -ENOMEM;
1195
1196 tbl = (struct wmi_target_roam_tbl *) ar->debug.roam_tbl;
1197 num_entries = le16_to_cpu(tbl->num_entries);
1198
1199 buf_len = 100 + num_entries * 100;
1200 buf = kzalloc(buf_len, GFP_KERNEL);
1201 if (buf == NULL)
1202 return -ENOMEM;
1203 len = 0;
1204 len += scnprintf(buf + len, buf_len - len,
1205 "roam_mode=%u\n\n"
1206 "# roam_util bssid rssi rssidt last_rssi util bias\n",
1207 le16_to_cpu(tbl->roam_mode));
1208
1209 for (i = 0; i < num_entries; i++) {
1210 struct wmi_bss_roam_info *info = &tbl->info[i];
1211 len += scnprintf(buf + len, buf_len - len,
1212 "%d %pM %d %d %d %d %d\n",
1213 a_sle32_to_cpu(info->roam_util), info->bssid,
1214 info->rssi, info->rssidt, info->last_rssi,
1215 info->util, info->bias);
1216 }
1217
1218 if (len > buf_len)
1219 len = buf_len;
1220
1221 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
1222
1223 kfree(buf);
1224 return ret_cnt;
1225}
1226
1227static const struct file_operations fops_roam_table = {
1228 .read = ath6kl_roam_table_read,
1229 .open = simple_open,
1230 .owner = THIS_MODULE,
1231 .llseek = default_llseek,
1232};
1233
1234static ssize_t ath6kl_force_roam_write(struct file *file,
1235 const char __user *user_buf,
1236 size_t count, loff_t *ppos)
1237{
1238 struct ath6kl *ar = file->private_data;
1239 int ret;
1240 char buf[20];
1241 size_t len;
1242 u8 bssid[ETH_ALEN];
1243
1244 len = min(count, sizeof(buf) - 1);
1245 if (copy_from_user(buf, user_buf, len))
1246 return -EFAULT;
1247 buf[len] = '\0';
1248
1249 if (!mac_pton(buf, bssid))
1250 return -EINVAL;
1251
1252 ret = ath6kl_wmi_force_roam_cmd(ar->wmi, bssid);
1253 if (ret)
1254 return ret;
1255
1256 return count;
1257}
1258
1259static const struct file_operations fops_force_roam = {
1260 .write = ath6kl_force_roam_write,
1261 .open = simple_open,
1262 .owner = THIS_MODULE,
1263 .llseek = default_llseek,
1264};
1265
1266static ssize_t ath6kl_roam_mode_write(struct file *file,
1267 const char __user *user_buf,
1268 size_t count, loff_t *ppos)
1269{
1270 struct ath6kl *ar = file->private_data;
1271 int ret;
1272 char buf[20];
1273 size_t len;
1274 enum wmi_roam_mode mode;
1275
1276 len = min(count, sizeof(buf) - 1);
1277 if (copy_from_user(buf, user_buf, len))
1278 return -EFAULT;
1279 buf[len] = '\0';
1280 if (len > 0 && buf[len - 1] == '\n')
1281 buf[len - 1] = '\0';
1282
1283 if (strcasecmp(buf, "default") == 0)
1284 mode = WMI_DEFAULT_ROAM_MODE;
1285 else if (strcasecmp(buf, "bssbias") == 0)
1286 mode = WMI_HOST_BIAS_ROAM_MODE;
1287 else if (strcasecmp(buf, "lock") == 0)
1288 mode = WMI_LOCK_BSS_MODE;
1289 else
1290 return -EINVAL;
1291
1292 ret = ath6kl_wmi_set_roam_mode_cmd(ar->wmi, mode);
1293 if (ret)
1294 return ret;
1295
1296 return count;
1297}
1298
1299static const struct file_operations fops_roam_mode = {
1300 .write = ath6kl_roam_mode_write,
1301 .open = simple_open,
1302 .owner = THIS_MODULE,
1303 .llseek = default_llseek,
1304};
1305
1306void ath6kl_debug_set_keepalive(struct ath6kl *ar, u8 keepalive)
1307{
1308 ar->debug.keepalive = keepalive;
1309}
1310
1311static ssize_t ath6kl_keepalive_read(struct file *file, char __user *user_buf,
1312 size_t count, loff_t *ppos)
1313{
1314 struct ath6kl *ar = file->private_data;
1315 char buf[16];
1316 int len;
1317
1318 len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.keepalive);
1319
1320 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1321}
1322
1323static ssize_t ath6kl_keepalive_write(struct file *file,
1324 const char __user *user_buf,
1325 size_t count, loff_t *ppos)
1326{
1327 struct ath6kl *ar = file->private_data;
1328 int ret;
1329 u8 val;
1330
1331 ret = kstrtou8_from_user(user_buf, count, 0, &val);
1332 if (ret)
1333 return ret;
1334
1335 ret = ath6kl_wmi_set_keepalive_cmd(ar->wmi, 0, val);
1336 if (ret)
1337 return ret;
1338
1339 return count;
1340}
1341
1342static const struct file_operations fops_keepalive = {
1343 .open = simple_open,
1344 .read = ath6kl_keepalive_read,
1345 .write = ath6kl_keepalive_write,
1346 .owner = THIS_MODULE,
1347 .llseek = default_llseek,
1348};
1349
1350void ath6kl_debug_set_disconnect_timeout(struct ath6kl *ar, u8 timeout)
1351{
1352 ar->debug.disc_timeout = timeout;
1353}
1354
1355static ssize_t ath6kl_disconnect_timeout_read(struct file *file,
1356 char __user *user_buf,
1357 size_t count, loff_t *ppos)
1358{
1359 struct ath6kl *ar = file->private_data;
1360 char buf[16];
1361 int len;
1362
1363 len = snprintf(buf, sizeof(buf), "%u\n", ar->debug.disc_timeout);
1364
1365 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1366}
1367
1368static ssize_t ath6kl_disconnect_timeout_write(struct file *file,
1369 const char __user *user_buf,
1370 size_t count, loff_t *ppos)
1371{
1372 struct ath6kl *ar = file->private_data;
1373 int ret;
1374 u8 val;
1375
1376 ret = kstrtou8_from_user(user_buf, count, 0, &val);
1377 if (ret)
1378 return ret;
1379
1380 ret = ath6kl_wmi_disctimeout_cmd(ar->wmi, 0, val);
1381 if (ret)
1382 return ret;
1383
1384 return count;
1385}
1386
1387static const struct file_operations fops_disconnect_timeout = {
1388 .open = simple_open,
1389 .read = ath6kl_disconnect_timeout_read,
1390 .write = ath6kl_disconnect_timeout_write,
1391 .owner = THIS_MODULE,
1392 .llseek = default_llseek,
1393};
1394
1395static ssize_t ath6kl_create_qos_write(struct file *file,
1396 const char __user *user_buf,
1397 size_t count, loff_t *ppos)
1398{
1399
1400 struct ath6kl *ar = file->private_data;
1401 struct ath6kl_vif *vif;
1402 char buf[200];
1403 ssize_t len;
1404 char *sptr, *token;
1405 struct wmi_create_pstream_cmd pstream;
1406 u32 val32;
1407 u16 val16;
1408
1409 vif = ath6kl_vif_first(ar);
1410 if (!vif)
1411 return -EIO;
1412
1413 len = min(count, sizeof(buf) - 1);
1414 if (copy_from_user(buf, user_buf, len))
1415 return -EFAULT;
1416 buf[len] = '\0';
1417 sptr = buf;
1418
1419 token = strsep(&sptr, " ");
1420 if (!token)
1421 return -EINVAL;
1422 if (kstrtou8(token, 0, &pstream.user_pri))
1423 return -EINVAL;
1424
1425 token = strsep(&sptr, " ");
1426 if (!token)
1427 return -EINVAL;
1428 if (kstrtou8(token, 0, &pstream.traffic_direc))
1429 return -EINVAL;
1430
1431 token = strsep(&sptr, " ");
1432 if (!token)
1433 return -EINVAL;
1434 if (kstrtou8(token, 0, &pstream.traffic_class))
1435 return -EINVAL;
1436
1437 token = strsep(&sptr, " ");
1438 if (!token)
1439 return -EINVAL;
1440 if (kstrtou8(token, 0, &pstream.traffic_type))
1441 return -EINVAL;
1442
1443 token = strsep(&sptr, " ");
1444 if (!token)
1445 return -EINVAL;
1446 if (kstrtou8(token, 0, &pstream.voice_psc_cap))
1447 return -EINVAL;
1448
1449 token = strsep(&sptr, " ");
1450 if (!token)
1451 return -EINVAL;
1452 if (kstrtou32(token, 0, &val32))
1453 return -EINVAL;
1454 pstream.min_service_int = cpu_to_le32(val32);
1455
1456 token = strsep(&sptr, " ");
1457 if (!token)
1458 return -EINVAL;
1459 if (kstrtou32(token, 0, &val32))
1460 return -EINVAL;
1461 pstream.max_service_int = cpu_to_le32(val32);
1462
1463 token = strsep(&sptr, " ");
1464 if (!token)
1465 return -EINVAL;
1466 if (kstrtou32(token, 0, &val32))
1467 return -EINVAL;
1468 pstream.inactivity_int = cpu_to_le32(val32);
1469
1470 token = strsep(&sptr, " ");
1471 if (!token)
1472 return -EINVAL;
1473 if (kstrtou32(token, 0, &val32))
1474 return -EINVAL;
1475 pstream.suspension_int = cpu_to_le32(val32);
1476
1477 token = strsep(&sptr, " ");
1478 if (!token)
1479 return -EINVAL;
1480 if (kstrtou32(token, 0, &val32))
1481 return -EINVAL;
1482 pstream.service_start_time = cpu_to_le32(val32);
1483
1484 token = strsep(&sptr, " ");
1485 if (!token)
1486 return -EINVAL;
1487 if (kstrtou8(token, 0, &pstream.tsid))
1488 return -EINVAL;
1489
1490 token = strsep(&sptr, " ");
1491 if (!token)
1492 return -EINVAL;
1493 if (kstrtou16(token, 0, &val16))
1494 return -EINVAL;
1495 pstream.nominal_msdu = cpu_to_le16(val16);
1496
1497 token = strsep(&sptr, " ");
1498 if (!token)
1499 return -EINVAL;
1500 if (kstrtou16(token, 0, &val16))
1501 return -EINVAL;
1502 pstream.max_msdu = cpu_to_le16(val16);
1503
1504 token = strsep(&sptr, " ");
1505 if (!token)
1506 return -EINVAL;
1507 if (kstrtou32(token, 0, &val32))
1508 return -EINVAL;
1509 pstream.min_data_rate = cpu_to_le32(val32);
1510
1511 token = strsep(&sptr, " ");
1512 if (!token)
1513 return -EINVAL;
1514 if (kstrtou32(token, 0, &val32))
1515 return -EINVAL;
1516 pstream.mean_data_rate = cpu_to_le32(val32);
1517
1518 token = strsep(&sptr, " ");
1519 if (!token)
1520 return -EINVAL;
1521 if (kstrtou32(token, 0, &val32))
1522 return -EINVAL;
1523 pstream.peak_data_rate = cpu_to_le32(val32);
1524
1525 token = strsep(&sptr, " ");
1526 if (!token)
1527 return -EINVAL;
1528 if (kstrtou32(token, 0, &val32))
1529 return -EINVAL;
1530 pstream.max_burst_size = cpu_to_le32(val32);
1531
1532 token = strsep(&sptr, " ");
1533 if (!token)
1534 return -EINVAL;
1535 if (kstrtou32(token, 0, &val32))
1536 return -EINVAL;
1537 pstream.delay_bound = cpu_to_le32(val32);
1538
1539 token = strsep(&sptr, " ");
1540 if (!token)
1541 return -EINVAL;
1542 if (kstrtou32(token, 0, &val32))
1543 return -EINVAL;
1544 pstream.min_phy_rate = cpu_to_le32(val32);
1545
1546 token = strsep(&sptr, " ");
1547 if (!token)
1548 return -EINVAL;
1549 if (kstrtou32(token, 0, &val32))
1550 return -EINVAL;
1551 pstream.sba = cpu_to_le32(val32);
1552
1553 token = strsep(&sptr, " ");
1554 if (!token)
1555 return -EINVAL;
1556 if (kstrtou32(token, 0, &val32))
1557 return -EINVAL;
1558 pstream.medium_time = cpu_to_le32(val32);
1559
1560 pstream.nominal_phy = le32_to_cpu(pstream.min_phy_rate) / 1000000;
1561
1562 ath6kl_wmi_create_pstream_cmd(ar->wmi, vif->fw_vif_idx, &pstream);
1563
1564 return count;
1565}
1566
1567static const struct file_operations fops_create_qos = {
1568 .write = ath6kl_create_qos_write,
1569 .open = simple_open,
1570 .owner = THIS_MODULE,
1571 .llseek = default_llseek,
1572};
1573
1574static ssize_t ath6kl_delete_qos_write(struct file *file,
1575 const char __user *user_buf,
1576 size_t count, loff_t *ppos)
1577{
1578
1579 struct ath6kl *ar = file->private_data;
1580 struct ath6kl_vif *vif;
1581 char buf[100];
1582 ssize_t len;
1583 char *sptr, *token;
1584 u8 traffic_class;
1585 u8 tsid;
1586
1587 vif = ath6kl_vif_first(ar);
1588 if (!vif)
1589 return -EIO;
1590
1591 len = min(count, sizeof(buf) - 1);
1592 if (copy_from_user(buf, user_buf, len))
1593 return -EFAULT;
1594 buf[len] = '\0';
1595 sptr = buf;
1596
1597 token = strsep(&sptr, " ");
1598 if (!token)
1599 return -EINVAL;
1600 if (kstrtou8(token, 0, &traffic_class))
1601 return -EINVAL;
1602
1603 token = strsep(&sptr, " ");
1604 if (!token)
1605 return -EINVAL;
1606 if (kstrtou8(token, 0, &tsid))
1607 return -EINVAL;
1608
1609 ath6kl_wmi_delete_pstream_cmd(ar->wmi, vif->fw_vif_idx,
1610 traffic_class, tsid);
1611
1612 return count;
1613}
1614
1615static const struct file_operations fops_delete_qos = {
1616 .write = ath6kl_delete_qos_write,
1617 .open = simple_open,
1618 .owner = THIS_MODULE,
1619 .llseek = default_llseek,
1620};
1621
1622static ssize_t ath6kl_bgscan_int_write(struct file *file,
1623 const char __user *user_buf,
1624 size_t count, loff_t *ppos)
1625{
1626 struct ath6kl *ar = file->private_data;
1627 struct ath6kl_vif *vif;
1628 u16 bgscan_int;
1629 char buf[32];
1630 ssize_t len;
1631
1632 vif = ath6kl_vif_first(ar);
1633 if (!vif)
1634 return -EIO;
1635
1636 len = min(count, sizeof(buf) - 1);
1637 if (copy_from_user(buf, user_buf, len))
1638 return -EFAULT;
1639
1640 buf[len] = '\0';
1641 if (kstrtou16(buf, 0, &bgscan_int))
1642 return -EINVAL;
1643
1644 if (bgscan_int == 0)
1645 bgscan_int = 0xffff;
1646
1647 vif->bg_scan_period = bgscan_int;
1648
1649 ath6kl_wmi_scanparams_cmd(ar->wmi, 0, 0, 0, bgscan_int, 0, 0, 0, 3,
1650 0, 0, 0);
1651
1652 return count;
1653}
1654
1655static const struct file_operations fops_bgscan_int = {
1656 .write = ath6kl_bgscan_int_write,
1657 .open = simple_open,
1658 .owner = THIS_MODULE,
1659 .llseek = default_llseek,
1660};
1661
1662static ssize_t ath6kl_listen_int_write(struct file *file,
1663 const char __user *user_buf,
1664 size_t count, loff_t *ppos)
1665{
1666 struct ath6kl *ar = file->private_data;
1667 struct ath6kl_vif *vif;
1668 u16 listen_interval;
1669 char buf[32];
1670 ssize_t len;
1671
1672 vif = ath6kl_vif_first(ar);
1673 if (!vif)
1674 return -EIO;
1675
1676 len = min(count, sizeof(buf) - 1);
1677 if (copy_from_user(buf, user_buf, len))
1678 return -EFAULT;
1679
1680 buf[len] = '\0';
1681 if (kstrtou16(buf, 0, &listen_interval))
1682 return -EINVAL;
1683
1684 if ((listen_interval < 15) || (listen_interval > 3000))
1685 return -EINVAL;
1686
1687 vif->listen_intvl_t = listen_interval;
1688 ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
1689 vif->listen_intvl_t, 0);
1690
1691 return count;
1692}
1693
1694static ssize_t ath6kl_listen_int_read(struct file *file,
1695 char __user *user_buf,
1696 size_t count, loff_t *ppos)
1697{
1698 struct ath6kl *ar = file->private_data;
1699 struct ath6kl_vif *vif;
1700 char buf[32];
1701 int len;
1702
1703 vif = ath6kl_vif_first(ar);
1704 if (!vif)
1705 return -EIO;
1706
1707 len = scnprintf(buf, sizeof(buf), "%u\n", vif->listen_intvl_t);
1708
1709 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1710}
1711
1712static const struct file_operations fops_listen_int = {
1713 .read = ath6kl_listen_int_read,
1714 .write = ath6kl_listen_int_write,
1715 .open = simple_open,
1716 .owner = THIS_MODULE,
1717 .llseek = default_llseek,
1718};
1719
1720static ssize_t ath6kl_power_params_write(struct file *file,
1721 const char __user *user_buf,
1722 size_t count, loff_t *ppos)
1723{
1724 struct ath6kl *ar = file->private_data;
1725 u8 buf[100];
1726 unsigned int len = 0;
1727 char *sptr, *token;
1728 u16 idle_period, ps_poll_num, dtim,
1729 tx_wakeup, num_tx;
1730
1731 len = min(count, sizeof(buf) - 1);
1732 if (copy_from_user(buf, user_buf, len))
1733 return -EFAULT;
1734 buf[len] = '\0';
1735 sptr = buf;
1736
1737 token = strsep(&sptr, " ");
1738 if (!token)
1739 return -EINVAL;
1740 if (kstrtou16(token, 0, &idle_period))
1741 return -EINVAL;
1742
1743 token = strsep(&sptr, " ");
1744 if (!token)
1745 return -EINVAL;
1746 if (kstrtou16(token, 0, &ps_poll_num))
1747 return -EINVAL;
1748
1749 token = strsep(&sptr, " ");
1750 if (!token)
1751 return -EINVAL;
1752 if (kstrtou16(token, 0, &dtim))
1753 return -EINVAL;
1754
1755 token = strsep(&sptr, " ");
1756 if (!token)
1757 return -EINVAL;
1758 if (kstrtou16(token, 0, &tx_wakeup))
1759 return -EINVAL;
1760
1761 token = strsep(&sptr, " ");
1762 if (!token)
1763 return -EINVAL;
1764 if (kstrtou16(token, 0, &num_tx))
1765 return -EINVAL;
1766
1767 ath6kl_wmi_pmparams_cmd(ar->wmi, 0, idle_period, ps_poll_num,
1768 dtim, tx_wakeup, num_tx, 0);
1769
1770 return count;
1771}
1772
1773static const struct file_operations fops_power_params = {
1774 .write = ath6kl_power_params_write,
1775 .open = simple_open,
1776 .owner = THIS_MODULE,
1777 .llseek = default_llseek,
1778};
1779
1780void ath6kl_debug_init(struct ath6kl *ar)
1781{
1782 skb_queue_head_init(&ar->debug.fwlog_queue);
1783 init_completion(&ar->debug.fwlog_completion);
1784
1785 /*
1786 * Actually we are lying here but don't know how to read the mask
1787 * value from the firmware.
1788 */
1789 ar->debug.fwlog_mask = 0;
1790}
1791
1792/*
1793 * Initialisation needs to happen in two stages as fwlog events can come
1794 * before cfg80211 is initialised, and debugfs depends on cfg80211
1795 * initialisation.
1796 */
1797int ath6kl_debug_init_fs(struct ath6kl *ar)
1798{
1799 ar->debugfs_phy = debugfs_create_dir("ath6kl",
1800 ar->wiphy->debugfsdir);
1801 if (!ar->debugfs_phy)
1802 return -ENOMEM;
1803
1804 debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
1805 &fops_tgt_stats);
1806
1807 if (ar->hif_type == ATH6KL_HIF_TYPE_SDIO)
1808 debugfs_create_file("credit_dist_stats", S_IRUSR,
1809 ar->debugfs_phy, ar,
1810 &fops_credit_dist_stats);
1811
1812 debugfs_create_file("endpoint_stats", S_IRUSR | S_IWUSR,
1813 ar->debugfs_phy, ar, &fops_endpoint_stats);
1814
1815 debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
1816 &fops_fwlog);
1817
1818 debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar,
1819 &fops_fwlog_block);
1820
1821 debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
1822 ar, &fops_fwlog_mask);
1823
1824 debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
1825 &fops_diag_reg_read);
1826
1827 debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar,
1828 &fops_reg_dump);
1829
1830 debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR,
1831 ar->debugfs_phy, ar, &fops_lrssi_roam_threshold);
1832
1833 debugfs_create_file("reg_write", S_IRUSR | S_IWUSR,
1834 ar->debugfs_phy, ar, &fops_diag_reg_write);
1835
1836 debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar,
1837 &fops_war_stats);
1838
1839 debugfs_create_file("roam_table", S_IRUSR, ar->debugfs_phy, ar,
1840 &fops_roam_table);
1841
1842 debugfs_create_file("force_roam", S_IWUSR, ar->debugfs_phy, ar,
1843 &fops_force_roam);
1844
1845 debugfs_create_file("roam_mode", S_IWUSR, ar->debugfs_phy, ar,
1846 &fops_roam_mode);
1847
1848 debugfs_create_file("keepalive", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
1849 &fops_keepalive);
1850
1851 debugfs_create_file("disconnect_timeout", S_IRUSR | S_IWUSR,
1852 ar->debugfs_phy, ar, &fops_disconnect_timeout);
1853
1854 debugfs_create_file("create_qos", S_IWUSR, ar->debugfs_phy, ar,
1855 &fops_create_qos);
1856
1857 debugfs_create_file("delete_qos", S_IWUSR, ar->debugfs_phy, ar,
1858 &fops_delete_qos);
1859
1860 debugfs_create_file("bgscan_interval", S_IWUSR,
1861 ar->debugfs_phy, ar, &fops_bgscan_int);
1862
1863 debugfs_create_file("listen_interval", S_IRUSR | S_IWUSR,
1864 ar->debugfs_phy, ar, &fops_listen_int);
1865
1866 debugfs_create_file("power_params", S_IWUSR, ar->debugfs_phy, ar,
1867 &fops_power_params);
1868
1869 return 0;
1870}
1871
1872void ath6kl_debug_cleanup(struct ath6kl *ar)
1873{
1874 skb_queue_purge(&ar->debug.fwlog_queue);
1875 complete(&ar->debug.fwlog_completion);
1876 kfree(ar->debug.roam_tbl);
1877}
1878
1879#endif