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