debug.c 57.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
 * Copyright (c) 2005-2011 Atheros Communications Inc.
 * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <linux/module.h>
#include <linux/debugfs.h>
20
#include <linux/vmalloc.h>
21
#include <linux/utsname.h>
22
23
24

#include "core.h"
#include "debug.h"
Kalle Valo's avatar
Kalle Valo committed
25
#include "hif.h"
Michal Kazior's avatar
Michal Kazior committed
26
#include "wmi-ops.h"
27

28
29
30
/* ms */
#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#define ATH10K_FW_CRASH_DUMP_VERSION 1

/**
 * enum ath10k_fw_crash_dump_type - types of data in the dump file
 * @ATH10K_FW_CRASH_DUMP_REGDUMP: Register crash dump in binary format
 */
enum ath10k_fw_crash_dump_type {
	ATH10K_FW_CRASH_DUMP_REGISTERS = 0,

	ATH10K_FW_CRASH_DUMP_MAX,
};

struct ath10k_tlv_dump_data {
	/* see ath10k_fw_crash_dump_type above */
	__le32 type;

	/* in bytes */
	__le32 tlv_len;

	/* pad to 32-bit boundaries as needed */
	u8 tlv_data[];
} __packed;

struct ath10k_dump_file_data {
	/* dump file information */

	/* "ATH10K-FW-DUMP" */
	char df_magic[16];

	__le32 len;

	/* file dump version */
	__le32 version;

	/* some info we can get from ath10k struct that might help */

	u8 uuid[16];

	__le32 chip_id;

	/* 0 for now, in place for later hardware */
	__le32 bus_type;

	__le32 target_version;
	__le32 fw_version_major;
	__le32 fw_version_minor;
	__le32 fw_version_release;
	__le32 fw_version_build;
	__le32 phy_capability;
	__le32 hw_min_tx_power;
	__le32 hw_max_tx_power;
	__le32 ht_cap_info;
	__le32 vht_cap_info;
	__le32 num_rf_chains;

	/* firmware version string */
	char fw_ver[ETHTOOL_FWVERS_LEN];

	/* Kernel related information */

	/* time-of-day stamp */
	__le64 tv_sec;

	/* time-of-day stamp, nano-seconds */
	__le64 tv_nsec;

	/* LINUX_VERSION_CODE */
	__le32 kernel_ver_code;

	/* VERMAGIC_STRING */
	char kernel_ver[64];

	/* room for growth w/out changing binary format */
	u8 unused[128];

	/* struct ath10k_tlv_dump_data + more */
	u8 data[0];
} __packed;

110
void ath10k_info(struct ath10k *ar, const char *fmt, ...)
111
112
113
114
115
116
117
118
{
	struct va_format vaf = {
		.fmt = fmt,
	};
	va_list args;

	va_start(args, fmt);
	vaf.va = &args;
119
	dev_info(ar->dev, "%pV", &vaf);
120
	trace_ath10k_log_info(ar, &vaf);
121
122
123
124
	va_end(args);
}
EXPORT_SYMBOL(ath10k_info);

125
126
void ath10k_print_driver_info(struct ath10k *ar)
{
127
	char fw_features[128] = {};
128
129
130

	ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features));

131
	ath10k_info(ar, "%s (0x%08x, 0x%08x%s%s%s) fw %s api %d htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d features %s\n",
132
133
134
		    ar->hw_params.name,
		    ar->target_version,
		    ar->chip_id,
135
136
137
138
		    (strlen(ar->spec_board_id) > 0 ? ", " : ""),
		    ar->spec_board_id,
		    (strlen(ar->spec_board_id) > 0 && !ar->spec_board_loaded
		     ? " fallback" : ""),
139
140
141
		    ar->hw->wiphy->fw_version,
		    ar->fw_api,
		    ar->htt.target_version_major,
Michal Kazior's avatar
Michal Kazior committed
142
		    ar->htt.target_version_minor,
143
		    ar->wmi.op_version,
144
		    ar->htt.op_version,
145
		    ath10k_cal_mode_str(ar->cal_mode),
146
		    ar->max_num_stations,
147
148
		    test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags),
		    !test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags),
149
		    fw_features);
Kalle Valo's avatar
Kalle Valo committed
150
	ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n",
151
152
153
		    config_enabled(CONFIG_ATH10K_DEBUG),
		    config_enabled(CONFIG_ATH10K_DEBUGFS),
		    config_enabled(CONFIG_ATH10K_TRACING),
Kalle Valo's avatar
Kalle Valo committed
154
155
		    config_enabled(CONFIG_ATH10K_DFS_CERTIFIED),
		    config_enabled(CONFIG_NL80211_TESTMODE));
156
157
158
}
EXPORT_SYMBOL(ath10k_print_driver_info);

159
void ath10k_err(struct ath10k *ar, const char *fmt, ...)
160
161
162
163
164
165
166
167
{
	struct va_format vaf = {
		.fmt = fmt,
	};
	va_list args;

	va_start(args, fmt);
	vaf.va = &args;
168
	dev_err(ar->dev, "%pV", &vaf);
169
	trace_ath10k_log_err(ar, &vaf);
170
171
172
173
	va_end(args);
}
EXPORT_SYMBOL(ath10k_err);

174
void ath10k_warn(struct ath10k *ar, const char *fmt, ...)
175
176
177
178
179
180
181
182
{
	struct va_format vaf = {
		.fmt = fmt,
	};
	va_list args;

	va_start(args, fmt);
	vaf.va = &args;
183
	dev_warn_ratelimited(ar->dev, "%pV", &vaf);
184
	trace_ath10k_log_warn(ar, &vaf);
185
186
187
188
189
190
191
192
193
194
195
196
197

	va_end(args);
}
EXPORT_SYMBOL(ath10k_warn);

#ifdef CONFIG_ATH10K_DEBUGFS

static ssize_t ath10k_read_wmi_services(struct file *file,
					char __user *user_buf,
					size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	char *buf;
198
199
	unsigned int len = 0, buf_len = 4096;
	const char *name;
200
	ssize_t ret_cnt;
201
	bool enabled;
202
203
204
205
206
207
208
209
210
211
212
	int i;

	buf = kzalloc(buf_len, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	mutex_lock(&ar->conf_mutex);

	if (len > buf_len)
		len = buf_len;

213
	spin_lock_bh(&ar->data_lock);
214
	for (i = 0; i < WMI_SERVICE_MAX; i++) {
215
		enabled = test_bit(i, ar->wmi.svc_map);
216
217
218
219
220
221
222
223
224
225
		name = wmi_service_name(i);

		if (!name) {
			if (enabled)
				len += scnprintf(buf + len, buf_len - len,
						 "%-40s %s (bit %d)\n",
						 "unknown", "enabled", i);

			continue;
		}
226
227

		len += scnprintf(buf + len, buf_len - len,
228
229
				 "%-40s %s\n",
				 name, enabled ? "enabled" : "-");
230
	}
231
	spin_unlock_bh(&ar->data_lock);
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

	ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);

	mutex_unlock(&ar->conf_mutex);

	kfree(buf);
	return ret_cnt;
}

static const struct file_operations fops_wmi_services = {
	.read = ath10k_read_wmi_services,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

248
249
250
251
252
253
254
255
256
257
static void ath10k_debug_fw_stats_pdevs_free(struct list_head *head)
{
	struct ath10k_fw_stats_pdev *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

258
259
260
261
262
263
264
265
266
267
static void ath10k_debug_fw_stats_vdevs_free(struct list_head *head)
{
	struct ath10k_fw_stats_vdev *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
static void ath10k_debug_fw_stats_peers_free(struct list_head *head)
{
	struct ath10k_fw_stats_peer *i, *tmp;

	list_for_each_entry_safe(i, tmp, head, list) {
		list_del(&i->list);
		kfree(i);
	}
}

static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
{
	spin_lock_bh(&ar->data_lock);
	ar->debug.fw_stats_done = false;
	ath10k_debug_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
283
	ath10k_debug_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
	ath10k_debug_fw_stats_peers_free(&ar->debug.fw_stats.peers);
	spin_unlock_bh(&ar->data_lock);
}

static size_t ath10k_debug_fw_stats_num_peers(struct list_head *head)
{
	struct ath10k_fw_stats_peer *i;
	size_t num = 0;

	list_for_each_entry(i, head, list)
		++num;

	return num;
}

299
300
301
302
303
304
305
306
307
308
309
static size_t ath10k_debug_fw_stats_num_vdevs(struct list_head *head)
{
	struct ath10k_fw_stats_vdev *i;
	size_t num = 0;

	list_for_each_entry(i, head, list)
		++num;

	return num;
}

310
void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
311
{
312
313
314
	struct ath10k_fw_stats stats = {};
	bool is_start, is_started, is_end;
	size_t num_peers;
315
	size_t num_vdevs;
316
	int ret;
317

318
	INIT_LIST_HEAD(&stats.pdevs);
319
	INIT_LIST_HEAD(&stats.vdevs);
320
	INIT_LIST_HEAD(&stats.peers);
321

322
323
	spin_lock_bh(&ar->data_lock);
	ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);
324
325
	if (ret) {
		ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);
326
		goto free;
327
328
	}

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
	/* Stat data may exceed htc-wmi buffer limit. In such case firmware
	 * splits the stats data and delivers it in a ping-pong fashion of
	 * request cmd-update event.
	 *
	 * However there is no explicit end-of-data. Instead start-of-data is
	 * used as an implicit one. This works as follows:
	 *  a) discard stat update events until one with pdev stats is
	 *     delivered - this skips session started at end of (b)
	 *  b) consume stat update events until another one with pdev stats is
	 *     delivered which is treated as end-of-data and is itself discarded
	 */

	if (ar->debug.fw_stats_done) {
		ath10k_warn(ar, "received unsolicited stats update event\n");
		goto free;
	}

	num_peers = ath10k_debug_fw_stats_num_peers(&ar->debug.fw_stats.peers);
347
	num_vdevs = ath10k_debug_fw_stats_num_vdevs(&ar->debug.fw_stats.vdevs);
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
	is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&
		    !list_empty(&stats.pdevs));
	is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&
		  !list_empty(&stats.pdevs));

	if (is_start)
		list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);

	if (is_end)
		ar->debug.fw_stats_done = true;

	is_started = !list_empty(&ar->debug.fw_stats.pdevs);

	if (is_started && !is_end) {
		if (num_peers >= ATH10K_MAX_NUM_PEER_IDS) {
			/* Although this is unlikely impose a sane limit to
			 * prevent firmware from DoS-ing the host.
			 */
			ath10k_warn(ar, "dropping fw peer stats\n");
			goto free;
		}

370
371
372
373
374
		if (num_vdevs >= BITS_PER_LONG) {
			ath10k_warn(ar, "dropping fw vdev stats\n");
			goto free;
		}

375
		list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
376
		list_splice_tail_init(&stats.vdevs, &ar->debug.fw_stats.vdevs);
377
378
	}

379
	complete(&ar->debug.fw_stats_complete);
380

381
382
383
384
385
free:
	/* In some cases lists have been spliced and cleared. Free up
	 * resources if that is not the case.
	 */
	ath10k_debug_fw_stats_pdevs_free(&stats.pdevs);
386
	ath10k_debug_fw_stats_vdevs_free(&stats.vdevs);
387
388
	ath10k_debug_fw_stats_peers_free(&stats.peers);

389
	spin_unlock_bh(&ar->data_lock);
390
391
}

392
static int ath10k_debug_fw_stats_request(struct ath10k *ar)
393
{
394
	unsigned long timeout, time_left;
395
396
	int ret;

397
	lockdep_assert_held(&ar->conf_mutex);
398

399
	timeout = jiffies + msecs_to_jiffies(1 * HZ);
400
401
402
403
404
405
406
407
408

	ath10k_debug_fw_stats_reset(ar);

	for (;;) {
		if (time_after(jiffies, timeout))
			return -ETIMEDOUT;

		reinit_completion(&ar->debug.fw_stats_complete);

409
		ret = ath10k_wmi_request_stats(ar, ar->fw_stats_req_mask);
410
411
412
413
		if (ret) {
			ath10k_warn(ar, "could not request stats (%d)\n", ret);
			return ret;
		}
414

415
416
417
418
		time_left =
		wait_for_completion_timeout(&ar->debug.fw_stats_complete,
					    1 * HZ);
		if (!time_left)
419
420
421
422
423
424
425
426
427
			return -ETIMEDOUT;

		spin_lock_bh(&ar->data_lock);
		if (ar->debug.fw_stats_done) {
			spin_unlock_bh(&ar->data_lock);
			break;
		}
		spin_unlock_bh(&ar->data_lock);
	}
428
429
430
431
432
433
434
435
436
437
438
439
440

	return 0;
}

/* FIXME: How to calculate the buffer size sanely? */
#define ATH10K_FW_STATS_BUF_SIZE (1024*1024)

static void ath10k_fw_stats_fill(struct ath10k *ar,
				 struct ath10k_fw_stats *fw_stats,
				 char *buf)
{
	unsigned int len = 0;
	unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
441
	const struct ath10k_fw_stats_pdev *pdev;
442
	const struct ath10k_fw_stats_vdev *vdev;
443
444
	const struct ath10k_fw_stats_peer *peer;
	size_t num_peers;
445
446
	size_t num_vdevs;
	int i;
447

448
	spin_lock_bh(&ar->data_lock);
449
450
451
452
453
454
455
456
457

	pdev = list_first_entry_or_null(&fw_stats->pdevs,
					struct ath10k_fw_stats_pdev, list);
	if (!pdev) {
		ath10k_warn(ar, "failed to get pdev stats\n");
		goto unlock;
	}

	num_peers = ath10k_debug_fw_stats_num_peers(&fw_stats->peers);
458
	num_vdevs = ath10k_debug_fw_stats_num_vdevs(&fw_stats->vdevs);
459

460
461
462
463
464
465
466
	len += scnprintf(buf + len, buf_len - len, "\n");
	len += scnprintf(buf + len, buf_len - len, "%30s\n",
			 "ath10k PDEV stats");
	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
				 "=================");

	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
467
			 "Channel noise floor", pdev->ch_noise_floor);
468
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
469
			 "Channel TX power", pdev->chan_tx_power);
470
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
471
			 "TX frame count", pdev->tx_frame_count);
472
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
473
			 "RX frame count", pdev->rx_frame_count);
474
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
475
			 "RX clear count", pdev->rx_clear_count);
476
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
477
			 "Cycle count", pdev->cycle_count);
478
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
479
			 "PHY error count", pdev->phy_err_count);
480
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
481
			 "RTS bad count", pdev->rts_bad);
482
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
483
			 "RTS good count", pdev->rts_good);
484
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
485
			 "FCS bad count", pdev->fcs_bad);
486
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
487
			 "No beacon count", pdev->no_beacons);
488
	len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
489
			 "MIB int count", pdev->mib_int_count);
490
491
492
493
494
495
496
497

	len += scnprintf(buf + len, buf_len - len, "\n");
	len += scnprintf(buf + len, buf_len - len, "%30s\n",
			 "ath10k PDEV TX stats");
	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
				 "=================");

	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
498
			 "HTT cookies queued", pdev->comp_queued);
499
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
500
			 "HTT cookies disp.", pdev->comp_delivered);
501
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
502
			 "MSDU queued", pdev->msdu_enqued);
503
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
504
			 "MPDU queued", pdev->mpdu_enqued);
505
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
506
			 "MSDUs dropped", pdev->wmm_drop);
507
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
508
			 "Local enqued", pdev->local_enqued);
509
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
510
			 "Local freed", pdev->local_freed);
511
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
512
			 "HW queued", pdev->hw_queued);
513
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
514
			 "PPDUs reaped", pdev->hw_reaped);
515
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
516
			 "Num underruns", pdev->underrun);
517
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
518
			 "PPDUs cleaned", pdev->tx_abort);
519
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
520
			 "MPDUs requed", pdev->mpdus_requed);
521
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
522
			 "Excessive retries", pdev->tx_ko);
523
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
524
			 "HW rate", pdev->data_rc);
525
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
526
			 "Sched self tiggers", pdev->self_triggers);
527
528
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
			 "Dropped due to SW retries",
529
			 pdev->sw_retry_failure);
530
531
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
			 "Illegal rate phy errors",
532
			 pdev->illgl_rate_phy_err);
533
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
534
			 "Pdev continous xretry", pdev->pdev_cont_xretry);
535
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
536
			 "TX timeout", pdev->pdev_tx_timeout);
537
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
538
			 "PDEV resets", pdev->pdev_resets);
539
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
540
			 "PHY underrun", pdev->phy_underrun);
541
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
542
			 "MPDU is more than txop limit", pdev->txop_ovf);
543
544
545
546
547
548
549
550
551

	len += scnprintf(buf + len, buf_len - len, "\n");
	len += scnprintf(buf + len, buf_len - len, "%30s\n",
			 "ath10k PDEV RX stats");
	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
				 "=================");

	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
			 "Mid PPDU route change",
552
			 pdev->mid_ppdu_route_change);
553
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
554
			 "Tot. number of statuses", pdev->status_rcvd);
555
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
556
			 "Extra frags on rings 0", pdev->r0_frags);
557
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
558
			 "Extra frags on rings 1", pdev->r1_frags);
559
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
560
			 "Extra frags on rings 2", pdev->r2_frags);
561
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
562
			 "Extra frags on rings 3", pdev->r3_frags);
563
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
564
			 "MSDUs delivered to HTT", pdev->htt_msdus);
565
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
566
			 "MPDUs delivered to HTT", pdev->htt_mpdus);
567
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
568
			 "MSDUs delivered to stack", pdev->loc_msdus);
569
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
570
			 "MPDUs delivered to stack", pdev->loc_mpdus);
571
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
572
			 "Oversized AMSUs", pdev->oversize_amsdu);
573
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
574
			 "PHY errors", pdev->phy_errs);
575
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
576
			 "PHY errors drops", pdev->phy_err_drop);
577
	len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
578
			 "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs);
579

580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
	len += scnprintf(buf + len, buf_len - len, "\n");
	len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
			 "ath10k VDEV stats", num_vdevs);
	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
				 "=================");

	list_for_each_entry(vdev, &fw_stats->vdevs, list) {
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
				 "vdev id", vdev->vdev_id);
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
				 "beacon snr", vdev->beacon_snr);
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
				 "data snr", vdev->data_snr);
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
				 "num rx frames", vdev->num_rx_frames);
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
				 "num rts fail", vdev->num_rts_fail);
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
				 "num rts success", vdev->num_rts_success);
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
				 "num rx err", vdev->num_rx_err);
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
				 "num rx discard", vdev->num_rx_discard);
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
				 "num tx not acked", vdev->num_tx_not_acked);

		for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames); i++)
			len += scnprintf(buf + len, buf_len - len,
					"%25s [%02d] %u\n",
					 "num tx frames", i,
					 vdev->num_tx_frames[i]);

		for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_retries); i++)
			len += scnprintf(buf + len, buf_len - len,
					"%25s [%02d] %u\n",
					 "num tx frames retries", i,
					 vdev->num_tx_frames_retries[i]);

		for (i = 0 ; i < ARRAY_SIZE(vdev->num_tx_frames_failures); i++)
			len += scnprintf(buf + len, buf_len - len,
					"%25s [%02d] %u\n",
					 "num tx frames failures", i,
					 vdev->num_tx_frames_failures[i]);

		for (i = 0 ; i < ARRAY_SIZE(vdev->tx_rate_history); i++)
			len += scnprintf(buf + len, buf_len - len,
					"%25s [%02d] 0x%08x\n",
					 "tx rate history", i,
					 vdev->tx_rate_history[i]);

		for (i = 0 ; i < ARRAY_SIZE(vdev->beacon_rssi_history); i++)
			len += scnprintf(buf + len, buf_len - len,
					"%25s [%02d] %u\n",
					 "beacon rssi history", i,
					 vdev->beacon_rssi_history[i]);

		len += scnprintf(buf + len, buf_len - len, "\n");
	}

639
	len += scnprintf(buf + len, buf_len - len, "\n");
640
641
	len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
			 "ath10k PEER stats", num_peers);
642
643
644
	len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
				 "=================");

645
	list_for_each_entry(peer, &fw_stats->peers, list) {
646
		len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
647
				 "Peer MAC address", peer->peer_macaddr);
648
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
649
				 "Peer RSSI", peer->peer_rssi);
650
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
651
				 "Peer TX rate", peer->peer_tx_rate);
652
		len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
653
				 "Peer RX rate", peer->peer_rx_rate);
654
655
		len += scnprintf(buf + len, buf_len - len, "\n");
	}
656
657

unlock:
658
	spin_unlock_bh(&ar->data_lock);
659

660
661
662
663
664
	if (len >= buf_len)
		buf[len - 1] = 0;
	else
		buf[len] = 0;
}
665

666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
static int ath10k_fw_stats_open(struct inode *inode, struct file *file)
{
	struct ath10k *ar = inode->i_private;
	void *buf = NULL;
	int ret;

	mutex_lock(&ar->conf_mutex);

	if (ar->state != ATH10K_STATE_ON) {
		ret = -ENETDOWN;
		goto err_unlock;
	}

	buf = vmalloc(ATH10K_FW_STATS_BUF_SIZE);
	if (!buf) {
		ret = -ENOMEM;
		goto err_unlock;
	}

685
	ret = ath10k_debug_fw_stats_request(ar);
686
687
688
689
690
691
692
	if (ret) {
		ath10k_warn(ar, "failed to request fw stats: %d\n", ret);
		goto err_free;
	}

	ath10k_fw_stats_fill(ar, &ar->debug.fw_stats, buf);
	file->private_data = buf;
693
694

	mutex_unlock(&ar->conf_mutex);
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
	return 0;

err_free:
	vfree(buf);

err_unlock:
	mutex_unlock(&ar->conf_mutex);
	return ret;
}

static int ath10k_fw_stats_release(struct inode *inode, struct file *file)
{
	vfree(file->private_data);

	return 0;
}

static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,
				    size_t count, loff_t *ppos)
{
	const char *buf = file->private_data;
	unsigned int len = strlen(buf);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
719
720
721
}

static const struct file_operations fops_fw_stats = {
722
723
	.open = ath10k_fw_stats_open,
	.release = ath10k_fw_stats_release,
724
	.read = ath10k_fw_stats_read,
725
726
727
728
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file,
						char __user *user_buf,
						size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	int ret, len, buf_len;
	char *buf;

	buf_len = 500;
	buf = kmalloc(buf_len, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	spin_lock_bh(&ar->data_lock);

	len = 0;
	len += scnprintf(buf + len, buf_len - len,
			 "fw_crash_counter\t\t%d\n", ar->stats.fw_crash_counter);
	len += scnprintf(buf + len, buf_len - len,
			 "fw_warm_reset_counter\t\t%d\n",
			 ar->stats.fw_warm_reset_counter);
	len += scnprintf(buf + len, buf_len - len,
			 "fw_cold_reset_counter\t\t%d\n",
			 ar->stats.fw_cold_reset_counter);

	spin_unlock_bh(&ar->data_lock);

	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);

	kfree(buf);

	return ret;
}

static const struct file_operations fops_fw_reset_stats = {
	.open = simple_open,
	.read = ath10k_debug_fw_reset_stats_read,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
/* This is a clean assert crash in firmware. */
static int ath10k_debug_fw_assert(struct ath10k *ar)
{
	struct wmi_vdev_install_key_cmd *cmd;
	struct sk_buff *skb;

	skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + 16);
	if (!skb)
		return -ENOMEM;

	cmd = (struct wmi_vdev_install_key_cmd *)skb->data;
	memset(cmd, 0, sizeof(*cmd));

	/* big enough number so that firmware asserts */
	cmd->vdev_id = __cpu_to_le32(0x7ffe);

	return ath10k_wmi_cmd_send(ar, skb,
				   ar->wmi.cmd->vdev_install_key_cmdid);
}

790
791
792
793
static ssize_t ath10k_read_simulate_fw_crash(struct file *file,
					     char __user *user_buf,
					     size_t count, loff_t *ppos)
{
794
795
796
797
	const char buf[] =
		"To simulate firmware crash write one of the keywords to this file:\n"
		"`soft` - this will send WMI_FORCE_FW_HANG_ASSERT to firmware if FW supports that command.\n"
		"`hard` - this will send to firmware command with illegal parameters causing firmware crash.\n"
798
799
		"`assert` - this will send special illegal parameter to firmware to cause assert failure and crash.\n"
		"`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";
800

801
802
803
	return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
}

804
805
806
807
808
809
810
/* Simulate firmware crash:
 * 'soft': Call wmi command causing firmware hang. This firmware hang is
 * recoverable by warm firmware reset.
 * 'hard': Force firmware crash by setting any vdev parameter for not allowed
 * vdev id. This is hard firmware crash because it is recoverable only by cold
 * firmware reset.
 */
811
812
813
814
815
static ssize_t ath10k_write_simulate_fw_crash(struct file *file,
					      const char __user *user_buf,
					      size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
816
	char buf[32];
817
818
819
820
821
	int ret;

	mutex_lock(&ar->conf_mutex);

	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
822
823
824

	/* make sure that buf is null terminated */
	buf[sizeof(buf) - 1] = 0;
825
826
827
828
829
830
831

	if (ar->state != ATH10K_STATE_ON &&
	    ar->state != ATH10K_STATE_RESTARTED) {
		ret = -ENETDOWN;
		goto exit;
	}

832
833
834
835
836
837
838
	/* drop the possible '\n' from the end */
	if (buf[count - 1] == '\n') {
		buf[count - 1] = 0;
		count--;
	}

	if (!strcmp(buf, "soft")) {
839
		ath10k_info(ar, "simulating soft firmware crash\n");
840
841
		ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);
	} else if (!strcmp(buf, "hard")) {
842
		ath10k_info(ar, "simulating hard firmware crash\n");
843
844
845
846
		/* 0x7fff is vdev id, and it is always out of range for all
		 * firmware variants in order to force a firmware crash.
		 */
		ret = ath10k_wmi_vdev_set_param(ar, 0x7fff,
847
848
						ar->wmi.vdev_param->rts_threshold,
						0);
849
850
851
	} else if (!strcmp(buf, "assert")) {
		ath10k_info(ar, "simulating firmware assert crash\n");
		ret = ath10k_debug_fw_assert(ar);
852
853
854
855
	} else if (!strcmp(buf, "hw-restart")) {
		ath10k_info(ar, "user requested hw restart\n");
		queue_work(ar->workqueue, &ar->restart_work);
		ret = 0;
856
857
858
859
	} else {
		ret = -EINVAL;
		goto exit;
	}
860

861
	if (ret) {
862
		ath10k_warn(ar, "failed to simulate firmware crash: %d\n", ret);
863
864
		goto exit;
	}
865

866
	ret = count;
867
868
869
870
871
872
873
874
875
876
877
878
879
880

exit:
	mutex_unlock(&ar->conf_mutex);
	return ret;
}

static const struct file_operations fops_simulate_fw_crash = {
	.read = ath10k_read_simulate_fw_crash,
	.write = ath10k_write_simulate_fw_crash,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
static ssize_t ath10k_read_chip_id(struct file *file, char __user *user_buf,
				   size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	unsigned int len;
	char buf[50];

	len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->chip_id);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static const struct file_operations fops_chip_id = {
	.read = ath10k_read_chip_id,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
struct ath10k_fw_crash_data *
ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
{
	struct ath10k_fw_crash_data *crash_data = ar->debug.fw_crash_data;

	lockdep_assert_held(&ar->data_lock);

	crash_data->crashed_since_read = true;
	uuid_le_gen(&crash_data->uuid);
	getnstimeofday(&crash_data->timestamp);

	return crash_data;
}
EXPORT_SYMBOL(ath10k_debug_get_new_fw_crash_data);

static struct ath10k_dump_file_data *ath10k_build_dump_file(struct ath10k *ar)
{
	struct ath10k_fw_crash_data *crash_data = ar->debug.fw_crash_data;
	struct ath10k_dump_file_data *dump_data;
	struct ath10k_tlv_dump_data *dump_tlv;
	int hdr_len = sizeof(*dump_data);
	unsigned int len, sofar = 0;
	unsigned char *buf;

	len = hdr_len;
	len += sizeof(*dump_tlv) + sizeof(crash_data->registers);

	sofar += hdr_len;

	/* This is going to get big when we start dumping FW RAM and such,
	 * so go ahead and use vmalloc.
	 */
	buf = vzalloc(len);
	if (!buf)
		return NULL;

	spin_lock_bh(&ar->data_lock);

	if (!crash_data->crashed_since_read) {
		spin_unlock_bh(&ar->data_lock);
		vfree(buf);
		return NULL;
	}

	dump_data = (struct ath10k_dump_file_data *)(buf);
	strlcpy(dump_data->df_magic, "ATH10K-FW-DUMP",
		sizeof(dump_data->df_magic));
	dump_data->len = cpu_to_le32(len);

	dump_data->version = cpu_to_le32(ATH10K_FW_CRASH_DUMP_VERSION);

	memcpy(dump_data->uuid, &crash_data->uuid, sizeof(dump_data->uuid));
	dump_data->chip_id = cpu_to_le32(ar->chip_id);
	dump_data->bus_type = cpu_to_le32(0);
	dump_data->target_version = cpu_to_le32(ar->target_version);
	dump_data->fw_version_major = cpu_to_le32(ar->fw_version_major);
	dump_data->fw_version_minor = cpu_to_le32(ar->fw_version_minor);
	dump_data->fw_version_release = cpu_to_le32(ar->fw_version_release);
	dump_data->fw_version_build = cpu_to_le32(ar->fw_version_build);
	dump_data->phy_capability = cpu_to_le32(ar->phy_capability);
	dump_data->hw_min_tx_power = cpu_to_le32(ar->hw_min_tx_power);
	dump_data->hw_max_tx_power = cpu_to_le32(ar->hw_max_tx_power);
	dump_data->ht_cap_info = cpu_to_le32(ar->ht_cap_info);
	dump_data->vht_cap_info = cpu_to_le32(ar->vht_cap_info);
	dump_data->num_rf_chains = cpu_to_le32(ar->num_rf_chains);

	strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version,
		sizeof(dump_data->fw_ver));

969
970
	dump_data->kernel_ver_code = 0;
	strlcpy(dump_data->kernel_ver, init_utsname()->release,
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
		sizeof(dump_data->kernel_ver));

	dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec);
	dump_data->tv_nsec = cpu_to_le64(crash_data->timestamp.tv_nsec);

	/* Gather crash-dump */
	dump_tlv = (struct ath10k_tlv_dump_data *)(buf + sofar);
	dump_tlv->type = cpu_to_le32(ATH10K_FW_CRASH_DUMP_REGISTERS);
	dump_tlv->tlv_len = cpu_to_le32(sizeof(crash_data->registers));
	memcpy(dump_tlv->tlv_data, &crash_data->registers,
	       sizeof(crash_data->registers));
	sofar += sizeof(*dump_tlv) + sizeof(crash_data->registers);

	ar->debug.fw_crash_data->crashed_since_read = false;

	spin_unlock_bh(&ar->data_lock);

	return dump_data;
}

static int ath10k_fw_crash_dump_open(struct inode *inode, struct file *file)
{
	struct ath10k *ar = inode->i_private;
	struct ath10k_dump_file_data *dump;

	dump = ath10k_build_dump_file(ar);
	if (!dump)
		return -ENODATA;

	file->private_data = dump;

	return 0;
}

static ssize_t ath10k_fw_crash_dump_read(struct file *file,
					 char __user *user_buf,
					 size_t count, loff_t *ppos)
{
	struct ath10k_dump_file_data *dump_file = file->private_data;

	return simple_read_from_buffer(user_buf, count, ppos,
				       dump_file,
				       le32_to_cpu(dump_file->len));
}

static int ath10k_fw_crash_dump_release(struct inode *inode,
					struct file *file)
{
	vfree(file->private_data);

	return 0;
}

static const struct file_operations fops_fw_crash_dump = {
	.open = ath10k_fw_crash_dump_open,
	.read = ath10k_fw_crash_dump_read,
	.release = ath10k_fw_crash_dump_release,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
static ssize_t ath10k_reg_addr_read(struct file *file,
				    char __user *user_buf,
				    size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	u8 buf[32];
	unsigned int len = 0;
	u32 reg_addr;

	mutex_lock(&ar->conf_mutex);
	reg_addr = ar->debug.reg_addr;
	mutex_unlock(&ar->conf_mutex);

	len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_addr);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static ssize_t ath10k_reg_addr_write(struct file *file,
				     const char __user *user_buf,
				     size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	u32 reg_addr;
	int ret;

	ret = kstrtou32_from_user(user_buf, count, 0, &reg_addr);
	if (ret)
		return ret;

	if (!IS_ALIGNED(reg_addr, 4))
		return -EFAULT;

	mutex_lock(&ar->conf_mutex);
	ar->debug.reg_addr = reg_addr;
	mutex_unlock(&ar->conf_mutex);

	return count;
}

static const struct file_operations fops_reg_addr = {
	.read = ath10k_reg_addr_read,
	.write = ath10k_reg_addr_write,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

static ssize_t ath10k_reg_value_read(struct file *file,
				     char __user *user_buf,
				     size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	u8 buf[48];
	unsigned int len;
	u32 reg_addr, reg_val;
	int ret;

	mutex_lock(&ar->conf_mutex);

	if (ar->state != ATH10K_STATE_ON &&
	    ar->state != ATH10K_STATE_UTF) {
		ret = -ENETDOWN;
		goto exit;
	}

	reg_addr = ar->debug.reg_addr;

	reg_val = ath10k_hif_read32(ar, reg_addr);
	len = scnprintf(buf, sizeof(buf), "0x%08x:0x%08x\n", reg_addr, reg_val);

	ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);

exit:
	mutex_unlock(&ar->conf_mutex);

	return ret;
}

static ssize_t ath10k_reg_value_write(struct file *file,
				      const char __user *user_buf,
				      size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	u32 reg_addr, reg_val;
	int ret;

	mutex_lock(&ar->conf_mutex);

	if (ar->state != ATH10K_STATE_ON &&
	    ar->state != ATH10K_STATE_UTF) {
		ret = -ENETDOWN;
		goto exit;
	}

	reg_addr = ar->debug.reg_addr;

	ret = kstrtou32_from_user(user_buf, count, 0, &reg_val);
	if (ret)
		goto exit;

	ath10k_hif_write32(ar, reg_addr, reg_val);

	ret = count;

exit:
	mutex_unlock(&ar->conf_mutex);

	return ret;
}

static const struct file_operations fops_reg_value = {
	.read = ath10k_reg_value_read,
	.write = ath10k_reg_value_write,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
static ssize_t ath10k_mem_value_read(struct file *file,
				     char __user *user_buf,
				     size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	u8 *buf;
	int ret;

	if (*ppos < 0)
		return -EINVAL;

	if (!count)
		return 0;

	mutex_lock(&ar->conf_mutex);

	buf = vmalloc(count);
	if (!buf) {
		ret = -ENOMEM;
		goto exit;
	}

	if (ar->state != ATH10K_STATE_ON &&
	    ar->state != ATH10K_STATE_UTF) {
		ret = -ENETDOWN;
		goto exit;
	}

	ret = ath10k_hif_diag_read(ar, *ppos, buf, count);
	if (ret) {
		ath10k_warn(ar, "failed to read address 0x%08x via diagnose window fnrom debugfs: %d\n",
			    (u32)(*ppos), ret);
		goto exit;
	}

	ret = copy_to_user(user_buf, buf, count);
	if (ret) {
		ret = -EFAULT;
		goto exit;
	}

	count -= ret;
	*ppos += count;
	ret = count;

exit:
	vfree(buf);
	mutex_unlock(&ar->conf_mutex);

	return ret;
}

static ssize_t ath10k_mem_value_write(struct file *file,
				      const char __user *user_buf,
				      size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	u8 *buf;
	int ret;

	if (*ppos < 0)
		return -EINVAL;

	if (!count)
		return 0;

	mutex_lock(&ar->conf_mutex);

	buf = vmalloc(count);
	if (!buf) {
		ret = -ENOMEM;
		goto exit;
	}

	if (ar->state != ATH10K_STATE_ON &&
	    ar->state != ATH10K_STATE_UTF) {
		ret = -ENETDOWN;
		goto exit;
	}

	ret = copy_from_user(buf, user_buf, count);
	if (ret) {
		ret = -EFAULT;
		goto exit;
	}

	ret = ath10k_hif_diag_write(ar, *ppos, buf, count);
	if (ret) {
		ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",
			    (u32)(*ppos), ret);
		goto exit;
	}

	*ppos += count;
	ret = count;

exit:
	vfree(buf);
	mutex_unlock(&ar->conf_mutex);

	return ret;
}

static const struct file_operations fops_mem_value = {
	.read = ath10k_mem_value_read,
	.write = ath10k_mem_value_write,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
static int ath10k_debug_htt_stats_req(struct ath10k *ar)
{
	u64 cookie;
	int ret;

	lockdep_assert_held(&ar->conf_mutex);

	if (ar->debug.htt_stats_mask == 0)
		/* htt stats are disabled */
		return 0;

	if (ar->state != ATH10K_STATE_ON)
		return 0;

	cookie = get_jiffies_64();

	ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask,
				       cookie);
	if (ret) {
1281
		ath10k_warn(ar, "failed to send htt stats request: %d\n", ret);
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
		return ret;
	}

	queue_delayed_work(ar->workqueue, &ar->debug.htt_stats_dwork,
			   msecs_to_jiffies(ATH10K_DEBUG_HTT_STATS_INTERVAL));

	return 0;
}

static void ath10k_debug_htt_stats_dwork(struct work_struct *work)
{
	struct ath10k *ar = container_of(work, struct ath10k,
					 debug.htt_stats_dwork.work);

	mutex_lock(&ar->conf_mutex);

	ath10k_debug_htt_stats_req(ar);

	mutex_unlock(&ar->conf_mutex);
}

static ssize_t ath10k_read_htt_stats_mask(struct file *file,
1304
1305
					  char __user *user_buf,
					  size_t count, loff_t *ppos)
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
{
	struct ath10k *ar = file->private_data;
	char buf[32];
	unsigned int len;

	len = scnprintf(buf, sizeof(buf), "%lu\n", ar->debug.htt_stats_mask);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static ssize_t ath10k_write_htt_stats_mask(struct file *file,
1317
1318
					   const char __user *user_buf,
					   size_t count, loff_t *ppos)
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
{
	struct ath10k *ar = file->private_data;
	unsigned long mask;
	int ret;

	ret = kstrtoul_from_user(user_buf, count, 0, &mask);
	if (ret)
		return ret;

	/* max 8 bit masks (for now) */
	if (mask > 0xff)
		return -E2BIG;

	mutex_lock(&ar->conf_mutex);

	ar->debug.htt_stats_mask = mask;

	ret = ath10k_debug_htt_stats_req(ar);
	if (ret)
		goto out;

	ret = count;

out:
	mutex_unlock(&ar->conf_mutex);

	return ret;
}

static const struct file_operations fops_htt_stats_mask = {
	.read = ath10k_read_htt_stats_mask,
	.write = ath10k_write_htt_stats_mask,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file,
					       char __user *user_buf,
					       size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	char buf[64];
	u8 amsdu = 3, ampdu = 64;
	unsigned int len;

	mutex_lock(&ar->conf_mutex);

1367
1368
	amsdu = ar->htt.max_num_amsdu;
	ampdu = ar->htt.max_num_ampdu;
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
	mutex_unlock(&ar->conf_mutex);

	len = scnprintf(buf, sizeof(buf), "%u %u\n", amsdu, ampdu);

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static ssize_t ath10k_write_htt_max_amsdu_ampdu(struct file *file,
						const char __user *user_buf,
						size_t count, loff_t *ppos)
{
	struct ath10k *ar = file->private_data;
	int res;
	char buf[64];
	unsigned int amsdu, ampdu;

	simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);

	/* make sure that buf is null terminated */
	buf[sizeof(buf) - 1] = 0;

	res = sscanf(buf, "%u %u", &amsdu, &ampdu);

	if (res != 2)
		return -EINVAL;

	mutex_lock(&ar->conf_mutex);

	res = ath10k_htt_h2t_aggr_cfg_msg(&ar->htt, ampdu, amsdu);
	if (res)
		goto out;

	res = count;
1402
1403
	ar->htt.max_num_amsdu = amsdu;
	ar->htt.max_num_ampdu = ampdu;
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417

out:
	mutex_unlock(&ar->conf_mutex);
	return res;
}

static const struct file_operations fops_htt_max_amsdu_ampdu = {
	.read = ath10k_read_htt_max_amsdu_ampdu,
	.write = ath10k_write_htt_max_amsdu_ampdu,
	.open = simple_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

1418
static ssize_t ath10k_read_fw_dbglog(struct file *file,
1419
1420
				     char __user *user_buf,
				     size_t count, loff_t *ppos)
1421
1422
1423
{
	struct ath10k *ar = file->private_data;
	unsigned int len;
1424
	char buf[64];
1425

1426
1427
	len = scnprintf(buf, sizeof(buf), "0x%08x %u\n",
			ar->debug.fw_dbglog_mask, ar->debug.fw_dbglog_level);
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437

	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static ssize_t ath10k_write_fw_dbglog(struct file *file,