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