debug.c 25.0 KB
Newer Older
1
/*
2
 * Copyright (c) 2008-2009 Atheros Communications Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * 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.
 */

17 18
#include <asm/unaligned.h>

S
Sujith 已提交
19
#include "ath9k.h"
20

21 22 23 24 25
#define REG_WRITE_D(_ah, _reg, _val) \
	ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg))
#define REG_READ_D(_ah, _reg) \
	ath9k_hw_common(_ah)->ops->read((_ah), (_reg))

26 27
static struct dentry *ath9k_debugfs_root;

28 29 30 31 32 33
static int ath9k_debugfs_open(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;
	return 0;
}

34 35
#ifdef CONFIG_ATH_DEBUG

J
Jeff Hansen 已提交
36 37 38 39
static ssize_t read_file_debug(struct file *file, char __user *user_buf,
			     size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
40
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
J
Jeff Hansen 已提交
41
	char buf[32];
42 43
	unsigned int len;

44
	len = snprintf(buf, sizeof(buf), "0x%08x\n", common->debug_mask);
J
Jeff Hansen 已提交
45 46 47 48 49 50 51
	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static ssize_t write_file_debug(struct file *file, const char __user *user_buf,
			     size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
52
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
J
Jeff Hansen 已提交
53 54
	unsigned long mask;
	char buf[32];
55 56 57 58 59 60 61 62 63 64
	ssize_t len;

	len = min(count, sizeof(buf) - 1);
	if (copy_from_user(buf, user_buf, len))
		return -EINVAL;

	buf[len] = '\0';
	if (strict_strtoul(buf, 0, &mask))
		return -EINVAL;

65
	common->debug_mask = mask;
J
Jeff Hansen 已提交
66 67 68 69 70 71 72 73 74 75
	return count;
}

static const struct file_operations fops_debug = {
	.read = read_file_debug,
	.write = write_file_debug,
	.open = ath9k_debugfs_open,
	.owner = THIS_MODULE
};

76 77
#endif

78 79
#define DMA_BUF_LEN 1024

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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
static ssize_t read_file_tx_chainmask(struct file *file, char __user *user_buf,
			     size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
	char buf[32];
	unsigned int len;

	len = snprintf(buf, sizeof(buf), "0x%08x\n", common->tx_chainmask);
	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static ssize_t write_file_tx_chainmask(struct file *file, const char __user *user_buf,
			     size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
	unsigned long mask;
	char buf[32];
	ssize_t len;

	len = min(count, sizeof(buf) - 1);
	if (copy_from_user(buf, user_buf, len))
		return -EINVAL;

	buf[len] = '\0';
	if (strict_strtoul(buf, 0, &mask))
		return -EINVAL;

	common->tx_chainmask = mask;
	sc->sc_ah->caps.tx_chainmask = mask;
	return count;
}

static const struct file_operations fops_tx_chainmask = {
	.read = read_file_tx_chainmask,
	.write = write_file_tx_chainmask,
	.open = ath9k_debugfs_open,
	.owner = THIS_MODULE
};


static ssize_t read_file_rx_chainmask(struct file *file, char __user *user_buf,
			     size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
	char buf[32];
	unsigned int len;

	len = snprintf(buf, sizeof(buf), "0x%08x\n", common->rx_chainmask);
	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}

static ssize_t write_file_rx_chainmask(struct file *file, const char __user *user_buf,
			     size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
	unsigned long mask;
	char buf[32];
	ssize_t len;

	len = min(count, sizeof(buf) - 1);
	if (copy_from_user(buf, user_buf, len))
		return -EINVAL;

	buf[len] = '\0';
	if (strict_strtoul(buf, 0, &mask))
		return -EINVAL;

	common->rx_chainmask = mask;
	sc->sc_ah->caps.rx_chainmask = mask;
	return count;
}

static const struct file_operations fops_rx_chainmask = {
	.read = read_file_rx_chainmask,
	.write = write_file_rx_chainmask,
	.open = ath9k_debugfs_open,
	.owner = THIS_MODULE
};


164 165 166 167
static ssize_t read_file_dma(struct file *file, char __user *user_buf,
			     size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
168
	struct ath_hw *ah = sc->sc_ah;
169 170
	char *buf;
	int retval;
171 172 173 174 175
	unsigned int len = 0;
	u32 val[ATH9K_NUM_DMA_DEBUG_REGS];
	int i, qcuOffset = 0, dcuOffset = 0;
	u32 *qcuBase = &val[0], *dcuBase = &val[4];

176 177 178 179
	buf = kmalloc(DMA_BUF_LEN, GFP_KERNEL);
	if (!buf)
		return 0;

180 181
	ath9k_ps_wakeup(sc);

182
	REG_WRITE_D(ah, AR_MACMISC,
183 184 185 186
		  ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
		   (AR_MACMISC_MISC_OBS_BUS_1 <<
		    AR_MACMISC_MISC_OBS_BUS_MSB_S)));

187
	len += snprintf(buf + len, DMA_BUF_LEN - len,
188 189 190 191
			"Raw DMA Debug values:\n");

	for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) {
		if (i % 4 == 0)
192
			len += snprintf(buf + len, DMA_BUF_LEN - len, "\n");
193

194
		val[i] = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32)));
195
		len += snprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ",
196 197 198
				i, val[i]);
	}

199 200
	len += snprintf(buf + len, DMA_BUF_LEN - len, "\n\n");
	len += snprintf(buf + len, DMA_BUF_LEN - len,
201 202 203 204 205 206 207 208 209 210 211 212 213
			"Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");

	for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) {
		if (i == 8) {
			qcuOffset = 0;
			qcuBase++;
		}

		if (i == 6) {
			dcuOffset = 0;
			dcuBase++;
		}

214
		len += snprintf(buf + len, DMA_BUF_LEN - len,
215 216 217 218 219 220 221
			"%2d          %2x      %1x     %2x           %2x\n",
			i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
			(*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
			val[2] & (0x7 << (i * 3)) >> (i * 3),
			(*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
	}

222
	len += snprintf(buf + len, DMA_BUF_LEN - len, "\n");
223

224
	len += snprintf(buf + len, DMA_BUF_LEN - len,
225 226
		"qcu_stitch state:   %2x    qcu_fetch state:        %2x\n",
		(val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22);
227
	len += snprintf(buf + len, DMA_BUF_LEN - len,
228 229
		"qcu_complete state: %2x    dcu_complete state:     %2x\n",
		(val[3] & 0x1c000000) >> 26, (val[6] & 0x3));
230
	len += snprintf(buf + len, DMA_BUF_LEN - len,
231 232
		"dcu_arb state:      %2x    dcu_fp state:           %2x\n",
		(val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27);
233
	len += snprintf(buf + len, DMA_BUF_LEN - len,
234 235
		"chan_idle_dur:     %3d    chan_idle_dur_valid:     %1d\n",
		(val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10);
236
	len += snprintf(buf + len, DMA_BUF_LEN - len,
237 238
		"txfifo_valid_0:      %1d    txfifo_valid_1:          %1d\n",
		(val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12);
239
	len += snprintf(buf + len, DMA_BUF_LEN - len,
240 241 242
		"txfifo_dcu_num_0:   %2d    txfifo_dcu_num_1:       %2d\n",
		(val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17);

243
	len += snprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x\n",
244
			REG_READ_D(ah, AR_OBS_BUS_1));
245
	len += snprintf(buf + len, DMA_BUF_LEN - len,
246
			"AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR));
247

248 249
	ath9k_ps_restore(sc);

250 251 252
	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
	kfree(buf);
	return retval;
253 254 255 256 257 258 259 260
}

static const struct file_operations fops_dma = {
	.read = read_file_dma,
	.open = ath9k_debugfs_open,
	.owner = THIS_MODULE
};

261 262 263 264

void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status)
{
	if (status)
S
Sujith 已提交
265
		sc->debug.stats.istats.total++;
266 267 268 269 270 271 272 273 274
	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
		if (status & ATH9K_INT_RXLP)
			sc->debug.stats.istats.rxlp++;
		if (status & ATH9K_INT_RXHP)
			sc->debug.stats.istats.rxhp++;
	} else {
		if (status & ATH9K_INT_RX)
			sc->debug.stats.istats.rxok++;
	}
275
	if (status & ATH9K_INT_RXEOL)
S
Sujith 已提交
276
		sc->debug.stats.istats.rxeol++;
277
	if (status & ATH9K_INT_RXORN)
S
Sujith 已提交
278
		sc->debug.stats.istats.rxorn++;
279
	if (status & ATH9K_INT_TX)
S
Sujith 已提交
280
		sc->debug.stats.istats.txok++;
281
	if (status & ATH9K_INT_TXURN)
S
Sujith 已提交
282
		sc->debug.stats.istats.txurn++;
283
	if (status & ATH9K_INT_MIB)
S
Sujith 已提交
284
		sc->debug.stats.istats.mib++;
285
	if (status & ATH9K_INT_RXPHY)
S
Sujith 已提交
286
		sc->debug.stats.istats.rxphyerr++;
287
	if (status & ATH9K_INT_RXKCM)
S
Sujith 已提交
288
		sc->debug.stats.istats.rx_keycache_miss++;
289
	if (status & ATH9K_INT_SWBA)
S
Sujith 已提交
290
		sc->debug.stats.istats.swba++;
291
	if (status & ATH9K_INT_BMISS)
S
Sujith 已提交
292
		sc->debug.stats.istats.bmiss++;
293
	if (status & ATH9K_INT_BNR)
S
Sujith 已提交
294
		sc->debug.stats.istats.bnr++;
295
	if (status & ATH9K_INT_CST)
S
Sujith 已提交
296
		sc->debug.stats.istats.cst++;
297
	if (status & ATH9K_INT_GTT)
S
Sujith 已提交
298
		sc->debug.stats.istats.gtt++;
299
	if (status & ATH9K_INT_TIM)
S
Sujith 已提交
300
		sc->debug.stats.istats.tim++;
301
	if (status & ATH9K_INT_CABEND)
S
Sujith 已提交
302
		sc->debug.stats.istats.cabend++;
303
	if (status & ATH9K_INT_DTIMSYNC)
S
Sujith 已提交
304
		sc->debug.stats.istats.dtimsync++;
305
	if (status & ATH9K_INT_DTIM)
S
Sujith 已提交
306
		sc->debug.stats.istats.dtim++;
307 308 309 310 311 312 313 314 315
}

static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
				   size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
	char buf[512];
	unsigned int len = 0;

316 317 318 319 320 321 322 323 324
	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
		len += snprintf(buf + len, sizeof(buf) - len,
			"%8s: %10u\n", "RXLP", sc->debug.stats.istats.rxlp);
		len += snprintf(buf + len, sizeof(buf) - len,
			"%8s: %10u\n", "RXHP", sc->debug.stats.istats.rxhp);
	} else {
		len += snprintf(buf + len, sizeof(buf) - len,
			"%8s: %10u\n", "RX", sc->debug.stats.istats.rxok);
	}
325
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
326
		"%8s: %10u\n", "RXEOL", sc->debug.stats.istats.rxeol);
327
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
328
		"%8s: %10u\n", "RXORN", sc->debug.stats.istats.rxorn);
329
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
330
		"%8s: %10u\n", "TX", sc->debug.stats.istats.txok);
331
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
332
		"%8s: %10u\n", "TXURN", sc->debug.stats.istats.txurn);
333
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
334
		"%8s: %10u\n", "MIB", sc->debug.stats.istats.mib);
335
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
336
		"%8s: %10u\n", "RXPHY", sc->debug.stats.istats.rxphyerr);
337
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
338
		"%8s: %10u\n", "RXKCM", sc->debug.stats.istats.rx_keycache_miss);
339
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
340
		"%8s: %10u\n", "SWBA", sc->debug.stats.istats.swba);
341
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
342
		"%8s: %10u\n", "BMISS", sc->debug.stats.istats.bmiss);
343
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
344
		"%8s: %10u\n", "BNR", sc->debug.stats.istats.bnr);
345
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
346
		"%8s: %10u\n", "CST", sc->debug.stats.istats.cst);
347
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
348
		"%8s: %10u\n", "GTT", sc->debug.stats.istats.gtt);
349
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
350
		"%8s: %10u\n", "TIM", sc->debug.stats.istats.tim);
351
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
352
		"%8s: %10u\n", "CABEND", sc->debug.stats.istats.cabend);
353
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
354
		"%8s: %10u\n", "DTIMSYNC", sc->debug.stats.istats.dtimsync);
355
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
356
		"%8s: %10u\n", "DTIM", sc->debug.stats.istats.dtim);
357
	len += snprintf(buf + len, sizeof(buf) - len,
S
Sujith 已提交
358
		"%8s: %10u\n", "TOTAL", sc->debug.stats.istats.total);
359 360 361 362 363 364 365 366 367 368

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

static const struct file_operations fops_interrupt = {
	.read = read_file_interrupt,
	.open = ath9k_debugfs_open,
	.owner = THIS_MODULE
};

369
void ath_debug_stat_rc(struct ath_softc *sc, int final_rate)
370
{
371
	struct ath_rc_stats *stats;
372

373
	stats = &sc->debug.stats.rcstats[final_rate];
374
	stats->success++;
375 376
}

377
void ath_debug_stat_retries(struct ath_softc *sc, int rix,
S
Sujith 已提交
378
			    int xretries, int retries, u8 per)
379
{
380
	struct ath_rc_stats *stats = &sc->debug.stats.rcstats[rix];
381

382 383 384
	stats->xretries += xretries;
	stats->retries += retries;
	stats->per = per;
385 386
}

387 388
static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
				size_t count, loff_t *ppos)
389 390
{
	struct ath_softc *sc = file->private_data;
391 392
	char *buf;
	unsigned int len = 0, max;
393
	int i = 0;
394
	ssize_t retval;
395

396 397
	if (sc->cur_rate_table == NULL)
		return 0;
398

399
	max = 80 + sc->cur_rate_table->rate_cnt * 1024;
400 401 402 403
	buf = kmalloc(max + 1, GFP_KERNEL);
	if (buf == NULL)
		return 0;
	buf[max] = 0;
404

405 406 407 408
	len += sprintf(buf, "%6s %6s %6s "
		       "%10s %10s %10s %10s\n",
		       "HT", "MCS", "Rate",
		       "Success", "Retries", "XRetries", "PER");
409 410

	for (i = 0; i < sc->cur_rate_table->rate_cnt; i++) {
411 412
		u32 ratekbps = sc->cur_rate_table->info[i].ratekbps;
		struct ath_rc_stats *stats = &sc->debug.stats.rcstats[i];
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
		char mcs[5];
		char htmode[5];
		int used_mcs = 0, used_htmode = 0;

		if (WLAN_RC_PHY_HT(sc->cur_rate_table->info[i].phy)) {
			used_mcs = snprintf(mcs, 5, "%d",
				sc->cur_rate_table->info[i].ratecode);

			if (WLAN_RC_PHY_40(sc->cur_rate_table->info[i].phy))
				used_htmode = snprintf(htmode, 5, "HT40");
			else if (WLAN_RC_PHY_20(sc->cur_rate_table->info[i].phy))
				used_htmode = snprintf(htmode, 5, "HT20");
			else
				used_htmode = snprintf(htmode, 5, "????");
		}

		mcs[used_mcs] = '\0';
		htmode[used_htmode] = '\0';
431 432

		len += snprintf(buf + len, max - len,
433 434 435 436 437 438 439 440 441
			"%6s %6s %3u.%d: "
			"%10u %10u %10u %10u\n",
			htmode,
			mcs,
			ratekbps / 1000,
			(ratekbps % 1000) / 100,
			stats->success,
			stats->retries,
			stats->xretries,
442
			stats->per);
443 444
	}

445 446 447
	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
	kfree(buf);
	return retval;
448 449 450 451 452 453 454
}

static const struct file_operations fops_rcstat = {
	.read = read_file_rcstat,
	.open = ath9k_debugfs_open,
	.owner = THIS_MODULE
};
455

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
static const char * ath_wiphy_state_str(enum ath_wiphy_state state)
{
	switch (state) {
	case ATH_WIPHY_INACTIVE:
		return "INACTIVE";
	case ATH_WIPHY_ACTIVE:
		return "ACTIVE";
	case ATH_WIPHY_PAUSING:
		return "PAUSING";
	case ATH_WIPHY_PAUSED:
		return "PAUSED";
	case ATH_WIPHY_SCAN:
		return "SCAN";
	}
	return "?";
}

static ssize_t read_file_wiphy(struct file *file, char __user *user_buf,
			       size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
	char buf[512];
	unsigned int len = 0;
	int i;
	u8 addr[ETH_ALEN];

	len += snprintf(buf + len, sizeof(buf) - len,
			"primary: %s (%s chan=%d ht=%d)\n",
			wiphy_name(sc->pri_wiphy->hw->wiphy),
			ath_wiphy_state_str(sc->pri_wiphy->state),
			sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht);
	for (i = 0; i < sc->num_sec_wiphy; i++) {
		struct ath_wiphy *aphy = sc->sec_wiphy[i];
		if (aphy == NULL)
			continue;
		len += snprintf(buf + len, sizeof(buf) - len,
				"secondary: %s (%s chan=%d ht=%d)\n",
				wiphy_name(aphy->hw->wiphy),
				ath_wiphy_state_str(aphy->state),
				aphy->chan_idx, aphy->chan_is_ht);
	}

498 499
	put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr);
	put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
500 501
	len += snprintf(buf + len, sizeof(buf) - len,
			"addr: %pM\n", addr);
502 503
	put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr);
	put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4);
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 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
	len += snprintf(buf + len, sizeof(buf) - len,
			"addrmask: %pM\n", addr);

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

static struct ath_wiphy * get_wiphy(struct ath_softc *sc, const char *name)
{
	int i;
	if (strcmp(name, wiphy_name(sc->pri_wiphy->hw->wiphy)) == 0)
		return sc->pri_wiphy;
	for (i = 0; i < sc->num_sec_wiphy; i++) {
		struct ath_wiphy *aphy = sc->sec_wiphy[i];
		if (aphy && strcmp(name, wiphy_name(aphy->hw->wiphy)) == 0)
			return aphy;
	}
	return NULL;
}

static int del_wiphy(struct ath_softc *sc, const char *name)
{
	struct ath_wiphy *aphy = get_wiphy(sc, name);
	if (!aphy)
		return -ENOENT;
	return ath9k_wiphy_del(aphy);
}

static int pause_wiphy(struct ath_softc *sc, const char *name)
{
	struct ath_wiphy *aphy = get_wiphy(sc, name);
	if (!aphy)
		return -ENOENT;
	return ath9k_wiphy_pause(aphy);
}

static int unpause_wiphy(struct ath_softc *sc, const char *name)
{
	struct ath_wiphy *aphy = get_wiphy(sc, name);
	if (!aphy)
		return -ENOENT;
	return ath9k_wiphy_unpause(aphy);
}

static int select_wiphy(struct ath_softc *sc, const char *name)
{
	struct ath_wiphy *aphy = get_wiphy(sc, name);
	if (!aphy)
		return -ENOENT;
	return ath9k_wiphy_select(aphy);
}

static int schedule_wiphy(struct ath_softc *sc, const char *msec)
{
	ath9k_wiphy_set_scheduler(sc, simple_strtoul(msec, NULL, 0));
	return 0;
}

static ssize_t write_file_wiphy(struct file *file, const char __user *user_buf,
				size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
	char buf[50];
	size_t len;

	len = min(count, sizeof(buf) - 1);
	if (copy_from_user(buf, user_buf, len))
		return -EFAULT;
	buf[len] = '\0';
	if (len > 0 && buf[len - 1] == '\n')
		buf[len - 1] = '\0';

	if (strncmp(buf, "add", 3) == 0) {
		int res = ath9k_wiphy_add(sc);
		if (res < 0)
			return res;
	} else if (strncmp(buf, "del=", 4) == 0) {
		int res = del_wiphy(sc, buf + 4);
		if (res < 0)
			return res;
	} else if (strncmp(buf, "pause=", 6) == 0) {
		int res = pause_wiphy(sc, buf + 6);
		if (res < 0)
			return res;
	} else if (strncmp(buf, "unpause=", 8) == 0) {
		int res = unpause_wiphy(sc, buf + 8);
		if (res < 0)
			return res;
	} else if (strncmp(buf, "select=", 7) == 0) {
		int res = select_wiphy(sc, buf + 7);
		if (res < 0)
			return res;
	} else if (strncmp(buf, "schedule=", 9) == 0) {
		int res = schedule_wiphy(sc, buf + 9);
		if (res < 0)
			return res;
	} else
		return -EOPNOTSUPP;

	return count;
}

static const struct file_operations fops_wiphy = {
	.read = read_file_wiphy,
	.write = write_file_wiphy,
	.open = ath9k_debugfs_open,
	.owner = THIS_MODULE
};

S
Sujith 已提交
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 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
#define PR(str, elem)							\
	do {								\
		len += snprintf(buf + len, size - len,			\
				"%s%13u%11u%10u%10u\n", str,		\
		sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_BE]].elem, \
		sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_BK]].elem, \
		sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_VI]].elem, \
		sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_VO]].elem); \
} while(0)

static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
			      size_t count, loff_t *ppos)
{
	struct ath_softc *sc = file->private_data;
	char *buf;
	unsigned int len = 0, size = 2048;
	ssize_t retval = 0;

	buf = kzalloc(size, GFP_KERNEL);
	if (buf == NULL)
		return 0;

	len += sprintf(buf, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO");

	PR("MPDUs Queued:    ", queued);
	PR("MPDUs Completed: ", completed);
	PR("Aggregates:      ", a_aggr);
	PR("AMPDUs Queued:   ", a_queued);
	PR("AMPDUs Completed:", a_completed);
	PR("AMPDUs Retried:  ", a_retries);
	PR("AMPDUs XRetried: ", a_xretries);
	PR("FIFO Underrun:   ", fifo_underrun);
	PR("TXOP Exceeded:   ", xtxop);
	PR("TXTIMER Expiry:  ", timer_exp);
	PR("DESC CFG Error:  ", desc_cfg_err);
	PR("DATA Underrun:   ", data_underrun);
	PR("DELIM Underrun:  ", delim_underrun);

	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
	kfree(buf);

	return retval;
}

void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq,
657
		       struct ath_buf *bf, struct ath_tx_status *ts)
S
Sujith 已提交
658 659 660 661 662 663 664 665 666 667
{
	if (bf_isampdu(bf)) {
		if (bf_isxretried(bf))
			TX_STAT_INC(txq->axq_qnum, a_xretries);
		else
			TX_STAT_INC(txq->axq_qnum, a_completed);
	} else {
		TX_STAT_INC(txq->axq_qnum, completed);
	}

668
	if (ts->ts_status & ATH9K_TXERR_FIFO)
S
Sujith 已提交
669
		TX_STAT_INC(txq->axq_qnum, fifo_underrun);
670
	if (ts->ts_status & ATH9K_TXERR_XTXOP)
S
Sujith 已提交
671
		TX_STAT_INC(txq->axq_qnum, xtxop);
672
	if (ts->ts_status & ATH9K_TXERR_TIMER_EXPIRED)
S
Sujith 已提交
673
		TX_STAT_INC(txq->axq_qnum, timer_exp);
674
	if (ts->ts_flags & ATH9K_TX_DESC_CFG_ERR)
S
Sujith 已提交
675
		TX_STAT_INC(txq->axq_qnum, desc_cfg_err);
676
	if (ts->ts_flags & ATH9K_TX_DATA_UNDERRUN)
S
Sujith 已提交
677
		TX_STAT_INC(txq->axq_qnum, data_underrun);
678
	if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN)
S
Sujith 已提交
679 680 681 682 683 684 685 686
		TX_STAT_INC(txq->axq_qnum, delim_underrun);
}

static const struct file_operations fops_xmit = {
	.read = read_file_xmit,
	.open = ath9k_debugfs_open,
	.owner = THIS_MODULE
};
687

S
Sujith 已提交
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 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
static ssize_t read_file_recv(struct file *file, char __user *user_buf,
			      size_t count, loff_t *ppos)
{
#define PHY_ERR(s, p) \
	len += snprintf(buf + len, size - len, "%18s : %10u\n", s, \
			sc->debug.stats.rxstats.phy_err_stats[p]);

	struct ath_softc *sc = file->private_data;
	char *buf;
	unsigned int len = 0, size = 1152;
	ssize_t retval = 0;

	buf = kzalloc(size, GFP_KERNEL);
	if (buf == NULL)
		return 0;

	len += snprintf(buf + len, size - len,
			"%18s : %10u\n", "CRC ERR",
			sc->debug.stats.rxstats.crc_err);
	len += snprintf(buf + len, size - len,
			"%18s : %10u\n", "DECRYPT CRC ERR",
			sc->debug.stats.rxstats.decrypt_crc_err);
	len += snprintf(buf + len, size - len,
			"%18s : %10u\n", "PHY ERR",
			sc->debug.stats.rxstats.phy_err);
	len += snprintf(buf + len, size - len,
			"%18s : %10u\n", "MIC ERR",
			sc->debug.stats.rxstats.mic_err);
	len += snprintf(buf + len, size - len,
			"%18s : %10u\n", "PRE-DELIM CRC ERR",
			sc->debug.stats.rxstats.pre_delim_crc_err);
	len += snprintf(buf + len, size - len,
			"%18s : %10u\n", "POST-DELIM CRC ERR",
			sc->debug.stats.rxstats.post_delim_crc_err);
	len += snprintf(buf + len, size - len,
			"%18s : %10u\n", "DECRYPT BUSY ERR",
			sc->debug.stats.rxstats.decrypt_busy_err);

	PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN);
	PHY_ERR("TIMING", ATH9K_PHYERR_TIMING);
	PHY_ERR("PARITY", ATH9K_PHYERR_PARITY);
	PHY_ERR("RATE", ATH9K_PHYERR_RATE);
	PHY_ERR("LENGTH", ATH9K_PHYERR_LENGTH);
	PHY_ERR("RADAR", ATH9K_PHYERR_RADAR);
	PHY_ERR("SERVICE", ATH9K_PHYERR_SERVICE);
	PHY_ERR("TOR", ATH9K_PHYERR_TOR);
	PHY_ERR("OFDM-TIMING", ATH9K_PHYERR_OFDM_TIMING);
	PHY_ERR("OFDM-SIGNAL-PARITY", ATH9K_PHYERR_OFDM_SIGNAL_PARITY);
	PHY_ERR("OFDM-RATE", ATH9K_PHYERR_OFDM_RATE_ILLEGAL);
	PHY_ERR("OFDM-LENGTH", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL);
	PHY_ERR("OFDM-POWER-DROP", ATH9K_PHYERR_OFDM_POWER_DROP);
	PHY_ERR("OFDM-SERVICE", ATH9K_PHYERR_OFDM_SERVICE);
	PHY_ERR("OFDM-RESTART", ATH9K_PHYERR_OFDM_RESTART);
	PHY_ERR("FALSE-RADAR-EXT", ATH9K_PHYERR_FALSE_RADAR_EXT);
	PHY_ERR("CCK-TIMING", ATH9K_PHYERR_CCK_TIMING);
	PHY_ERR("CCK-HEADER-CRC", ATH9K_PHYERR_CCK_HEADER_CRC);
	PHY_ERR("CCK-RATE", ATH9K_PHYERR_CCK_RATE_ILLEGAL);
	PHY_ERR("CCK-SERVICE", ATH9K_PHYERR_CCK_SERVICE);
	PHY_ERR("CCK-RESTART", ATH9K_PHYERR_CCK_RESTART);
	PHY_ERR("CCK-LENGTH", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL);
	PHY_ERR("CCK-POWER-DROP", ATH9K_PHYERR_CCK_POWER_DROP);
	PHY_ERR("HT-CRC", ATH9K_PHYERR_HT_CRC_ERROR);
	PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL);
	PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL);

	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
	kfree(buf);

	return retval;

#undef PHY_ERR
}

761
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
S
Sujith 已提交
762 763 764 765 766 767
{
#define RX_STAT_INC(c) sc->debug.stats.rxstats.c++
#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++

	u32 phyerr;

768
	if (rs->rs_status & ATH9K_RXERR_CRC)
S
Sujith 已提交
769
		RX_STAT_INC(crc_err);
770
	if (rs->rs_status & ATH9K_RXERR_DECRYPT)
S
Sujith 已提交
771
		RX_STAT_INC(decrypt_crc_err);
772
	if (rs->rs_status & ATH9K_RXERR_MIC)
S
Sujith 已提交
773
		RX_STAT_INC(mic_err);
774
	if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE)
S
Sujith 已提交
775
		RX_STAT_INC(pre_delim_crc_err);
776
	if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST)
S
Sujith 已提交
777
		RX_STAT_INC(post_delim_crc_err);
778
	if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY)
S
Sujith 已提交
779 780
		RX_STAT_INC(decrypt_busy_err);

781
	if (rs->rs_status & ATH9K_RXERR_PHY) {
S
Sujith 已提交
782
		RX_STAT_INC(phy_err);
783
		phyerr = rs->rs_phyerr & 0x24;
S
Sujith 已提交
784 785 786 787 788 789 790 791 792 793 794 795 796
		RX_PHY_ERR_INC(phyerr);
	}

#undef RX_STAT_INC
#undef RX_PHY_ERR_INC
}

static const struct file_operations fops_recv = {
	.read = read_file_recv,
	.open = ath9k_debugfs_open,
	.owner = THIS_MODULE
};

797
int ath9k_init_debug(struct ath_hw *ah)
798
{
799 800
	struct ath_common *common = ath9k_hw_common(ah);
	struct ath_softc *sc = (struct ath_softc *) common->priv;
801

S
Sujith 已提交
802 803 804
	if (!ath9k_debugfs_root)
		return -ENOENT;

S
Sujith 已提交
805
	sc->debug.debugfs_phy = debugfs_create_dir(wiphy_name(sc->hw->wiphy),
806
						      ath9k_debugfs_root);
S
Sujith 已提交
807
	if (!sc->debug.debugfs_phy)
808
		return -ENOMEM;
809

810
#ifdef CONFIG_ATH_DEBUG
811 812
	if (!debugfs_create_file("debug", S_IRUSR | S_IWUSR,
			sc->debug.debugfs_phy, sc, &fops_debug))
J
Jeff Hansen 已提交
813
		goto err;
814
#endif
J
Jeff Hansen 已提交
815

816 817
	if (!debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy,
			sc, &fops_dma))
818 819
		goto err;

820 821
	if (!debugfs_create_file("interrupt", S_IRUSR, sc->debug.debugfs_phy,
			sc, &fops_interrupt))
822 823
		goto err;

824 825
	if (!debugfs_create_file("rcstat", S_IRUSR, sc->debug.debugfs_phy,
			sc, &fops_rcstat))
826 827
		goto err;

828 829
	if (!debugfs_create_file("wiphy", S_IRUSR | S_IWUSR,
			sc->debug.debugfs_phy, sc, &fops_wiphy))
830 831
		goto err;

832 833
	if (!debugfs_create_file("xmit", S_IRUSR, sc->debug.debugfs_phy,
			sc, &fops_xmit))
S
Sujith 已提交
834 835
		goto err;

836 837
	if (!debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy,
			sc, &fops_recv))
S
Sujith 已提交
838 839
		goto err;

840 841 842 843 844 845 846 847
	if (!debugfs_create_file("rx_chainmask", S_IRUSR | S_IWUSR,
			sc->debug.debugfs_phy, sc, &fops_rx_chainmask))
		goto err;

	if (!debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR,
			sc->debug.debugfs_phy, sc, &fops_tx_chainmask))
		goto err;

848 849
	return 0;
err:
850
	ath9k_exit_debug(ah);
851 852 853
	return -ENOMEM;
}

854
void ath9k_exit_debug(struct ath_hw *ah)
855
{
856 857
	struct ath_common *common = ath9k_hw_common(ah);
	struct ath_softc *sc = (struct ath_softc *) common->priv;
858

859
	debugfs_remove_recursive(sc->debug.debugfs_phy);
860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
}

int ath9k_debug_create_root(void)
{
	ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
	if (!ath9k_debugfs_root)
		return -ENOENT;

	return 0;
}

void ath9k_debug_remove_root(void)
{
	debugfs_remove(ath9k_debugfs_root);
	ath9k_debugfs_root = NULL;
875
}