1/*-
2 * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 *
29 * $FreeBSD$
30 */
31#include "diag.h"
32
33#include "ah.h"
34#include "ah_internal.h"
35/* XXX cheat, 5212 has a superset of the key table defs */
36#include "ar5212/ar5212reg.h"
37
38#include "dumpregs.h"
39
40#include <getopt.h>
41#include <stdlib.h>
42#include <string.h>
43#include <ctype.h>
44#include <err.h>
45
46#include "ctrl.h"
47
48typedef struct {
49	HAL_REVS revs;
50	u_int32_t regdata[0xffff / sizeof(u_int32_t)];
51#define	MAXREGS	5*1024
52	struct dumpreg *regs[MAXREGS];
53	u_int nregs;
54	u_int	show_names	: 1,
55		show_addrs	: 1;
56} dumpregs_t;
57static	dumpregs_t state;
58
59#undef OS_REG_READ
60#define	OS_REG_READ(ah, off)	state.regdata[(off) >> 2]
61
62static	int ath_hal_anyregs(int what);
63static	int ath_hal_setupregs(struct ath_diag *atd, int what);
64static	u_int ath_hal_setupdiagregs(const HAL_REGRANGE regs[], u_int nr);
65static	void ath_hal_dumpregs(FILE *fd, int what);
66static	void ath_hal_dumprange(FILE *fd, u_int a, u_int b);
67static	void ath_hal_dumpkeycache(FILE *fd, int nkeys);
68static	void ath_hal_dumpint(FILE *fd, int what);
69static	void ath_hal_dumpqcu(FILE *fd, int what);
70static	void ath_hal_dumpdcu(FILE *fd, int what);
71static	void ath_hal_dumpbb(FILE *fd, int what);
72
73static void
74usage(void)
75{
76	fprintf(stderr, "usage: athregs [-i interface] [-abdkmqxz]\n");
77	fprintf(stderr, "-a	display all registers\n");
78	fprintf(stderr, "-b	display baseband registers\n");
79	fprintf(stderr, "-d	display DCU registers\n");
80	fprintf(stderr, "-k	display key cache registers\n");
81	fprintf(stderr, "-m	display \"MAC\" registers (default)\n");
82	fprintf(stderr, "-q	display QCU registers\n");
83	fprintf(stderr, "-x	display XR registers\n");
84	fprintf(stderr, "-z	display interrupt registers\n");
85	fprintf(stderr, "\n");
86	fprintf(stderr, "-A	display register address\n");
87	fprintf(stderr, "-N	suppress display of register name\n");
88	exit(-1);
89}
90
91int
92main(int argc, char *argv[])
93{
94	struct ath_diag atd;
95	const char *ifname;
96	u_int32_t *data;
97	u_int32_t *dp, *ep;
98	int what, c, i;
99	struct ath_driver_req req;
100
101	ath_driver_req_init(&req);
102
103	ifname = getenv("ATH");
104	if (!ifname)
105		ifname = ATH_DEFAULT;
106
107	what = 0;
108	state.show_addrs = 0;
109	state.show_names = 1;
110	while ((c = getopt(argc, argv, "i:aAbdkmNqxz")) != -1)
111		switch (c) {
112		case 'a':
113			what |= DUMP_ALL;
114			break;
115		case 'A':
116			state.show_addrs = 1;
117			break;
118		case 'b':
119			what |= DUMP_BASEBAND;
120			break;
121		case 'd':
122			what |= DUMP_DCU;
123			break;
124		case 'k':
125			what |= DUMP_KEYCACHE;
126			break;
127		case 'i':
128			ifname = optarg;
129			break;
130		case 'm':
131			what |= DUMP_BASIC;
132			break;
133		case 'N':
134			state.show_names = 0;
135			break;
136		case 'q':
137			what |= DUMP_QCU;
138			break;
139		case 'x':
140			what |= DUMP_XR;
141			break;
142		case 'z':
143			what |= DUMP_INTERRUPT;
144			break;
145		default:
146			usage();
147			/*NOTREACHED*/
148		}
149
150	/* Initialise the driver interface */
151	if (ath_driver_req_open(&req, ifname) < 0) {
152		exit(127);
153	}
154
155	/*
156	 * Whilst we're doing the ath_diag pieces, we have to set this
157	 * ourselves.
158	 */
159	strncpy(atd.ad_name, ifname, sizeof (atd.ad_name));
160
161	argc -= optind;
162	argv += optind;
163	if (what == 0)
164		what = DUMP_BASIC;
165
166	atd.ad_id = HAL_DIAG_REVS;
167	atd.ad_out_data = (caddr_t) &state.revs;
168	atd.ad_out_size = sizeof(state.revs);
169
170	if (ath_driver_req_fetch_diag(&req, SIOCGATHDIAG, &atd) < 0)
171		err(1, "%s", atd.ad_name);
172
173	if (ath_hal_setupregs(&atd, what) == 0)
174		errx(-1, "no registers are known for this part "
175		    "(devid 0x%x mac %d.%d phy %d)", state.revs.ah_devid,
176		    state.revs.ah_macVersion, state.revs.ah_macRev,
177		    state.revs.ah_phyRev);
178
179	atd.ad_out_size = ath_hal_setupdiagregs((HAL_REGRANGE *) atd.ad_in_data,
180		atd.ad_in_size / sizeof(HAL_REGRANGE));
181	atd.ad_out_data = (caddr_t) malloc(atd.ad_out_size);
182	if (atd.ad_out_data == NULL) {
183		fprintf(stderr, "Cannot malloc output buffer, size %u\n",
184			atd.ad_out_size);
185		exit(-1);
186	}
187	atd.ad_id = HAL_DIAG_REGS | ATH_DIAG_IN | ATH_DIAG_DYN;
188
189	if (ath_driver_req_fetch_diag(&req, SIOCGATHDIAG, &atd) < 0)
190		err(1, "%s", atd.ad_name);
191
192	/*
193	 * Expand register data into global space that can be
194	 * indexed directly by register offset.
195	 */
196	dp = (u_int32_t *)atd.ad_out_data;
197	ep = (u_int32_t *)(atd.ad_out_data + atd.ad_out_size);
198	while (dp < ep) {
199		u_int r = dp[0];	/* start of range */
200		u_int e = dp[1];	/* end of range */
201		dp++;
202		dp++;
203		/* convert offsets to indices */
204		r >>= 2; e >>= 2;
205		do {
206			if (dp >= ep) {
207				fprintf(stderr, "Warning, botched return data;"
208					"register at offset 0x%x not present\n",
209					r << 2);
210				break;
211			}
212			state.regdata[r++] = *dp++;
213		} while (r <= e);
214	}
215
216	if (what & DUMP_BASIC)
217		ath_hal_dumpregs(stdout, DUMP_BASIC);
218	if ((what & DUMP_INTERRUPT) && ath_hal_anyregs(DUMP_INTERRUPT)) {
219		if (what & DUMP_BASIC)
220			putchar('\n');
221		if (state.show_addrs)
222			ath_hal_dumpregs(stdout, DUMP_INTERRUPT);
223		else
224			ath_hal_dumpint(stdout, what);
225	}
226	if ((what & DUMP_QCU) && ath_hal_anyregs(DUMP_QCU)) {
227		if (what & (DUMP_BASIC|DUMP_INTERRUPT))
228			putchar('\n');
229		if (state.show_addrs)
230			ath_hal_dumpregs(stdout, DUMP_QCU);
231		else
232			ath_hal_dumpqcu(stdout, what);
233	}
234	if ((what & DUMP_DCU) && ath_hal_anyregs(DUMP_DCU)) {
235		if (what & (DUMP_BASIC|DUMP_INTERRUPT|DUMP_QCU))
236			putchar('\n');
237		if (state.show_addrs)
238			ath_hal_dumpregs(stdout, DUMP_DCU);
239		else
240			ath_hal_dumpdcu(stdout, what);
241	}
242	if (what & DUMP_KEYCACHE) {
243		if (state.show_addrs) {
244			if (what & (DUMP_BASIC|DUMP_INTERRUPT|DUMP_QCU|DUMP_DCU))
245				putchar('\n');
246			ath_hal_dumpregs(stdout, DUMP_KEYCACHE);
247		} else
248			ath_hal_dumpkeycache(stdout, 128);
249	}
250	if (what & DUMP_BASEBAND) {
251		if (what &~ DUMP_BASEBAND)
252			fprintf(stdout, "\n");
253		ath_hal_dumpbb(stdout, what);
254	}
255	ath_driver_req_close(&req);
256	return 0;
257}
258
259static int
260regcompar(const void *a, const void *b)
261{
262	const struct dumpreg *ra = *(const struct dumpreg **)a;
263	const struct dumpreg *rb = *(const struct dumpreg **)b;
264	return ra->addr - rb->addr;
265}
266
267void
268register_regs(struct dumpreg *chipregs, u_int nchipregs,
269	int def_srev_min, int def_srev_max, int def_phy_min, int def_phy_max)
270{
271	const int existing_regs = state.nregs;
272	int i, j;
273
274	for (i = 0; i < nchipregs; i++) {
275		struct dumpreg *nr = &chipregs[i];
276		if (nr->srevMin == 0)
277			nr->srevMin = def_srev_min;
278		if (nr->srevMax == 0)
279			nr->srevMax = def_srev_max;
280		if (nr->phyMin == 0)
281			nr->phyMin = def_phy_min;
282		if (nr->phyMax == 0)
283			nr->phyMax = def_phy_max;
284		for (j = 0; j < existing_regs; j++) {
285			struct dumpreg *r = state.regs[j];
286			/*
287			 * Check if we can just expand the mac+phy
288			 * coverage for the existing entry.
289			 */
290			if (nr->addr == r->addr &&
291			    (nr->name == r->name ||
292			     nr->name != NULL && r->name != NULL &&
293			     strcmp(nr->name, r->name) == 0)) {
294				if (nr->srevMin < r->srevMin &&
295				    (r->srevMin <= nr->srevMax &&
296				     nr->srevMax+1 <= r->srevMax)) {
297					r->srevMin = nr->srevMin;
298					goto skip;
299				}
300				if (nr->srevMax > r->srevMax &&
301				    (r->srevMin <= nr->srevMin &&
302				     nr->srevMin <= r->srevMax)) {
303					r->srevMax = nr->srevMax;
304					goto skip;
305				}
306			}
307			if (r->addr > nr->addr)
308				break;
309		}
310		/*
311		 * New item, add to the end, it'll be sorted below.
312		 */
313		if (state.nregs == MAXREGS)
314			errx(-1, "too many registers; bump MAXREGS");
315		state.regs[state.nregs++] = nr;
316	skip:
317		;
318	}
319	qsort(state.regs, state.nregs, sizeof(struct dumpreg *), regcompar);
320}
321
322void
323register_keycache(u_int nslots,
324	int def_srev_min, int def_srev_max, int def_phy_min, int def_phy_max)
325{
326#define	SET(r, a) do { \
327	r->addr = a; r->type = DUMP_KEYCACHE; r++; \
328} while(0)
329	struct dumpreg *keyregs, *r;
330	int i;
331
332	keyregs = (struct dumpreg *) calloc(nslots, 8*sizeof(struct dumpreg));
333	if (keyregs == NULL)
334		errx(-1, "no space to %d keycache slots\n", nslots);
335	r = keyregs;
336	for (i = 0; i < nslots; i++) {
337		SET(r, AR_KEYTABLE_KEY0(i));
338		SET(r, AR_KEYTABLE_KEY1(i));
339		SET(r, AR_KEYTABLE_KEY2(i));
340		SET(r, AR_KEYTABLE_KEY3(i));
341		SET(r, AR_KEYTABLE_KEY4(i));
342		SET(r, AR_KEYTABLE_TYPE(i));
343		SET(r, AR_KEYTABLE_MAC0(i));
344		SET(r, AR_KEYTABLE_MAC1(i));
345	}
346	register_regs(keyregs, 8*nslots,
347	    def_srev_min, def_srev_max, def_phy_min, def_phy_max);
348#undef SET
349}
350
351void
352register_range(u_int brange, u_int erange, int type,
353	int def_srev_min, int def_srev_max, int def_phy_min, int def_phy_max)
354{
355	struct dumpreg *bbregs, *r;
356	int i, nregs;
357
358	nregs = (erange - brange) / sizeof(uint32_t);
359	bbregs = (struct dumpreg *) calloc(nregs, sizeof(struct dumpreg));
360	if (bbregs == NULL)
361		errx(-1, "no space for %d register slots (type %d)\n",
362		    nregs, type);
363	r = bbregs;
364	for (i = 0; i < nregs; i++) {
365		r->addr = brange + (i<<2);
366		r->type = type;
367		r++;
368	}
369	register_regs(bbregs, nregs,
370	    def_srev_min, def_srev_max, def_phy_min, def_phy_max);
371}
372
373static __inline int
374match(const struct dumpreg *dr, const HAL_REVS *revs)
375{
376	if (!MAC_MATCH(dr, revs->ah_macVersion, revs->ah_macRev))
377		return 0;
378	if ((dr->type & DUMP_BASEBAND) && !PHY_MATCH(dr, revs->ah_phyRev))
379		return 0;
380	return 1;
381}
382
383static int
384ath_hal_anyregs(int what)
385{
386	const HAL_REVS *revs = &state.revs;
387	int i;
388
389	for (i = 0; i < state.nregs; i++) {
390		const struct dumpreg *dr = state.regs[i];
391		if ((what & dr->type) && match(dr, revs))
392			return 1;
393	}
394	return 0;
395}
396
397static int
398ath_hal_setupregs(struct ath_diag *atd, int what)
399{
400	const HAL_REVS *revs = &state.revs;
401	HAL_REGRANGE r;
402	size_t space = 0;
403	u_int8_t *cp;
404	int i, brun, erun;
405
406	brun = erun = -1;
407	for (i = 0; i < state.nregs; i++) {
408		const struct dumpreg *dr = state.regs[i];
409		if ((what & dr->type) && match(dr, revs)) {
410			if (erun + 4 != dr->addr) {
411				if (brun != -1)
412					space += sizeof(HAL_REGRANGE);
413				brun = erun = dr->addr;
414			} else
415				erun = dr->addr;
416		}
417	}
418	space += sizeof(HAL_REGRANGE);
419
420	atd->ad_in_data = (caddr_t) malloc(space);
421	if (atd->ad_in_data == NULL) {
422		fprintf(stderr, "Cannot malloc memory for registers!\n");
423		exit(-1);
424	}
425	atd->ad_in_size = space;
426	cp = (u_int8_t *) atd->ad_in_data;
427	brun = erun = -1;
428	for (i = 0; i < state.nregs; i++) {
429		const struct dumpreg *dr = state.regs[i];
430		if ((what & dr->type) && match(dr, revs)) {
431			if (erun + 4 != dr->addr) {
432				if (brun != -1) {
433					r.start = brun, r.end = erun;
434					memcpy(cp, &r, sizeof(r));
435					cp += sizeof(r);
436				}
437				brun = erun = dr->addr;
438			} else
439				erun = dr->addr;
440		}
441	}
442	if (brun != -1) {
443		r.start = brun, r.end = erun;
444		memcpy(cp, &r, sizeof(r));
445		cp += sizeof(r);
446	}
447	return space / sizeof(uint32_t);
448}
449
450static void
451ath_hal_dumpregs(FILE *fd, int what)
452{
453	const HAL_REVS *revs = &state.revs;
454	const char *sep = "";
455	int i, count, itemsperline;
456
457	count = 0;
458	itemsperline = 4;
459	if (state.show_names && state.show_addrs)
460		itemsperline--;
461	for (i = 0; i < state.nregs; i++) {
462		const struct dumpreg *dr = state.regs[i];
463		if ((what & dr->type) && match(dr, revs)) {
464			if (state.show_names && dr->name != NULL) {
465				fprintf(fd, "%s%-8s", sep, dr->name);
466				if (state.show_addrs)
467					fprintf(fd, " [%04x]", dr->addr);
468			} else
469				fprintf(fd, "%s%04x", sep, dr->addr);
470			fprintf(fd, " %08x", OS_REG_READ(ah, dr->addr));
471			sep = " ";
472			if ((++count % itemsperline) == 0)
473				sep = "\n";
474		}
475	}
476	if (count)
477		fprintf(fd, "\n");
478}
479
480static void
481ath_hal_dumprange(FILE *fd, u_int a, u_int b)
482{
483	u_int r;
484
485	for (r = a; r+16 <= b; r += 5*4)
486		fprintf(fd,
487			"%04x %08x  %04x %08x  %04x %08x  %04x %08x  %04x %08x\n"
488			, r, OS_REG_READ(ah, r)
489			, r+4, OS_REG_READ(ah, r+4)
490			, r+8, OS_REG_READ(ah, r+8)
491			, r+12, OS_REG_READ(ah, r+12)
492			, r+16, OS_REG_READ(ah, r+16)
493		);
494	switch (b-r) {
495	case 16:
496		fprintf(fd
497			, "%04x %08x  %04x %08x  %04x %08x  %04x %08x\n"
498			, r, OS_REG_READ(ah, r)
499			, r+4, OS_REG_READ(ah, r+4)
500			, r+8, OS_REG_READ(ah, r+8)
501			, r+12, OS_REG_READ(ah, r+12)
502		);
503		break;
504	case 12:
505		fprintf(fd, "%04x %08x  %04x %08x  %04x %08x\n"
506			, r, OS_REG_READ(ah, r)
507			, r+4, OS_REG_READ(ah, r+4)
508			, r+8, OS_REG_READ(ah, r+8)
509		);
510		break;
511	case 8:
512		fprintf(fd, "%04x %08x  %04x %08x\n"
513			, r, OS_REG_READ(ah, r)
514			, r+4, OS_REG_READ(ah, r+4)
515		);
516		break;
517	case 4:
518		fprintf(fd, "%04x %08x\n"
519			, r, OS_REG_READ(ah, r)
520		);
521		break;
522	}
523}
524
525static void
526ath_hal_dumpint(FILE *fd, int what)
527{
528	int i;
529
530	/* Interrupt registers */
531	fprintf(fd, "IMR: %08x S0 %08x S1 %08x S2 %08x S3 %08x S4 %08x\n"
532		, OS_REG_READ(ah, AR_IMR)
533		, OS_REG_READ(ah, AR_IMR_S0)
534		, OS_REG_READ(ah, AR_IMR_S1)
535		, OS_REG_READ(ah, AR_IMR_S2)
536		, OS_REG_READ(ah, AR_IMR_S3)
537		, OS_REG_READ(ah, AR_IMR_S4)
538	);
539	fprintf(fd, "ISR: %08x S0 %08x S1 %08x S2 %08x S3 %08x S4 %08x\n"
540		, OS_REG_READ(ah, AR_ISR)
541		, OS_REG_READ(ah, AR_ISR_S0)
542		, OS_REG_READ(ah, AR_ISR_S1)
543		, OS_REG_READ(ah, AR_ISR_S2)
544		, OS_REG_READ(ah, AR_ISR_S3)
545		, OS_REG_READ(ah, AR_ISR_S4)
546	);
547}
548
549static void
550ath_hal_dumpqcu(FILE *fd, int what)
551{
552	int i;
553
554	/* QCU registers */
555	fprintf(fd, "%-8s %08x  %-8s %08x  %-8s %08x\n"
556		, "Q_TXE", OS_REG_READ(ah, AR_Q_TXE)
557		, "Q_TXD", OS_REG_READ(ah, AR_Q_TXD)
558		, "Q_RDYTIMSHD", OS_REG_READ(ah, AR_Q_RDYTIMESHDN)
559	);
560	fprintf(fd, "Q_ONESHOTARM_SC %08x  Q_ONESHOTARM_CC %08x\n"
561		, OS_REG_READ(ah, AR_Q_ONESHOTARM_SC)
562		, OS_REG_READ(ah, AR_Q_ONESHOTARM_CC)
563	);
564	for (i = 0; i < 10; i++)
565		fprintf(fd, "Q[%u] TXDP %08x CBR %08x RDYT %08x MISC %08x STS %08x\n"
566			, i
567			, OS_REG_READ(ah, AR_QTXDP(i))
568			, OS_REG_READ(ah, AR_QCBRCFG(i))
569			, OS_REG_READ(ah, AR_QRDYTIMECFG(i))
570			, OS_REG_READ(ah, AR_QMISC(i))
571			, OS_REG_READ(ah, AR_QSTS(i))
572		);
573}
574
575static void
576ath_hal_dumpdcu(FILE *fd, int what)
577{
578	int i;
579
580	/* DCU registers */
581	for (i = 0; i < 10; i++)
582		fprintf(fd, "D[%u] MASK %08x IFS %08x RTRY %08x CHNT %08x MISC %06x\n"
583			, i
584			, OS_REG_READ(ah, AR_DQCUMASK(i))
585			, OS_REG_READ(ah, AR_DLCL_IFS(i))
586			, OS_REG_READ(ah, AR_DRETRY_LIMIT(i))
587			, OS_REG_READ(ah, AR_DCHNTIME(i))
588			, OS_REG_READ(ah, AR_DMISC(i))
589		);
590}
591
592static void
593ath_hal_dumpbb(FILE *fd, int what)
594{
595	const HAL_REVS *revs = &state.revs;
596	int i, brun, erun;
597
598	brun = erun = 0;
599	for (i = 0; i < state.nregs; i++) {
600		const struct dumpreg *dr = state.regs[i];
601		if (!match(dr, revs))
602			continue;
603		if (dr->type & DUMP_BASEBAND) {
604			if (brun == 0) {
605				brun = erun = dr->addr;
606			} else if (dr->addr == erun + sizeof(uint32_t)) {
607				erun = dr->addr;
608			} else {
609				ath_hal_dumprange(fd, brun, erun);
610				brun = erun = dr->addr;
611			}
612		} else {
613			if (brun != 0)
614				ath_hal_dumprange(fd, brun, erun);
615			brun = erun = 0;
616		}
617	}
618	if (brun != 0)
619		ath_hal_dumprange(fd, brun, erun);
620}
621
622static u_int
623ath_hal_setupdiagregs(const HAL_REGRANGE regs[], u_int nr)
624{
625	u_int space;
626	int i;
627
628	space = 0;
629	for (i = 0; i < nr; i++) {
630		u_int n = sizeof(HAL_REGRANGE) + sizeof(u_int32_t);	/* reg range + first */
631		if (regs[i].end) {
632			if (regs[i].end < regs[i].start) {
633				fprintf(stderr, "%s: bad register range, "
634					"end 0x%x < start 0x%x\n",
635					__func__, regs[i].end, regs[i].end);
636				exit(-1);
637			}
638			n += regs[i].end - regs[i].start;
639		}
640		space += n;
641	}
642	return space;
643}
644
645/*
646 * Format an Ethernet MAC for printing.
647 */
648static const char*
649ether_sprintf(const u_int8_t *mac)
650{
651	static char etherbuf[18];
652	snprintf(etherbuf, sizeof(etherbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
653		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
654	return etherbuf;
655}
656
657#ifndef isclr
658#define	setbit(a,i)	((a)[(i)/NBBY] |= 1<<((i)%NBBY))
659#define	clrbit(a,i)	((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
660#define	isset(a,i)	((a)[(i)/NBBY] & (1<<((i)%NBBY)))
661#define	isclr(a,i)	(((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
662#endif
663
664static void
665ath_hal_dumpkeycache(FILE *fd, int nkeys)
666{
667	static const char *keytypenames[] = {
668		"WEP-40", 	/* AR_KEYTABLE_TYPE_40 */
669		"WEP-104",	/* AR_KEYTABLE_TYPE_104 */
670		"#2",
671		"WEP-128",	/* AR_KEYTABLE_TYPE_128 */
672		"TKIP",		/* AR_KEYTABLE_TYPE_TKIP */
673		"AES-OCB",	/* AR_KEYTABLE_TYPE_AES */
674		"AES-CCM",	/* AR_KEYTABLE_TYPE_CCM */
675		"CLR",		/* AR_KEYTABLE_TYPE_CLR */
676	};
677	int micEnabled = SREV(state.revs.ah_macVersion, state.revs.ah_macRev) < SREV(4,8) ? 0 :
678	       OS_REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_CRPT_MIC_ENABLE;
679	u_int8_t mac[IEEE80211_ADDR_LEN];
680	u_int8_t ismic[128/NBBY];
681	int entry;
682	int first = 1;
683
684	memset(ismic, 0, sizeof(ismic));
685	for (entry = 0; entry < nkeys; entry++) {
686		u_int32_t macLo, macHi, type;
687		u_int32_t key0, key1, key2, key3, key4;
688
689		macHi = OS_REG_READ(ah, AR_KEYTABLE_MAC1(entry));
690		if ((macHi & AR_KEYTABLE_VALID) == 0 && isclr(ismic, entry))
691			continue;
692		macLo = OS_REG_READ(ah, AR_KEYTABLE_MAC0(entry));
693		macHi <<= 1;
694		if (macLo & (1<<31))
695			macHi |= 1;
696		macLo <<= 1;
697		mac[4] = macHi & 0xff;
698		mac[5] = macHi >> 8;
699		mac[0] = macLo & 0xff;
700		mac[1] = macLo >> 8;
701		mac[2] = macLo >> 16;
702		mac[3] = macLo >> 24;
703		type = OS_REG_READ(ah, AR_KEYTABLE_TYPE(entry));
704		if ((type & 7) == AR_KEYTABLE_TYPE_TKIP && micEnabled)
705			setbit(ismic, entry+64);
706		key0 = OS_REG_READ(ah, AR_KEYTABLE_KEY0(entry));
707		key1 = OS_REG_READ(ah, AR_KEYTABLE_KEY1(entry));
708		key2 = OS_REG_READ(ah, AR_KEYTABLE_KEY2(entry));
709		key3 = OS_REG_READ(ah, AR_KEYTABLE_KEY3(entry));
710		key4 = OS_REG_READ(ah, AR_KEYTABLE_KEY4(entry));
711		if (first) {
712			fprintf(fd, "\n");
713			first = 0;
714		}
715		fprintf(fd, "KEY[%03u] MAC %s %-7s %02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x\n"
716			, entry
717			, ether_sprintf(mac)
718			, isset(ismic, entry) ? "MIC" : keytypenames[type & 7]
719			, (key0 >>  0) & 0xff
720			, (key0 >>  8) & 0xff
721			, (key0 >> 16) & 0xff
722			, (key0 >> 24) & 0xff
723			, (key1 >>  0) & 0xff
724			, (key1 >>  8) & 0xff
725			, (key2 >>  0) & 0xff
726			, (key2 >>  8) & 0xff
727			, (key2 >> 16) & 0xff
728			, (key2 >> 24) & 0xff
729			, (key3 >>  0) & 0xff
730			, (key3 >>  8) & 0xff
731			, (key4 >>  0) & 0xff
732			, (key4 >>  8) & 0xff
733			, (key4 >> 16) & 0xff
734			, (key4 >> 24) & 0xff
735		);
736	}
737}
738