#include "diag.h"
#include "ah.h"
#include "ah_internal.h"
#include "ar5212/ar5212reg.h"
#include "dumpregs.h"
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <err.h>
#include "ctrl.h"
typedef struct {
HAL_REVS revs;
u_int32_t regdata[0xffff / sizeof(u_int32_t)];
#define MAXREGS 5*1024
struct dumpreg *regs[MAXREGS];
u_int nregs;
u_int show_names : 1,
show_addrs : 1;
} dumpregs_t;
static dumpregs_t state;
#undef OS_REG_READ
#define OS_REG_READ(ah, off) state.regdata[(off) >> 2]
static int ath_hal_anyregs(int what);
static int ath_hal_setupregs(struct ath_diag *atd, int what);
static u_int ath_hal_setupdiagregs(const HAL_REGRANGE regs[], u_int nr);
static void ath_hal_dumpregs(FILE *fd, int what);
static void ath_hal_dumprange(FILE *fd, u_int a, u_int b);
static void ath_hal_dumpkeycache(FILE *fd, int nkeys);
static void ath_hal_dumpint(FILE *fd, int what);
static void ath_hal_dumpqcu(FILE *fd, int what);
static void ath_hal_dumpdcu(FILE *fd, int what);
static void ath_hal_dumpbb(FILE *fd, int what);
static void
usage(void)
{
fprintf(stderr, "usage: athregs [-i interface] [-abdkmqxz]\n");
fprintf(stderr, "-a display all registers\n");
fprintf(stderr, "-b display baseband registers\n");
fprintf(stderr, "-d display DCU registers\n");
fprintf(stderr, "-k display key cache registers\n");
fprintf(stderr, "-m display \"MAC\" registers (default)\n");
fprintf(stderr, "-q display QCU registers\n");
fprintf(stderr, "-x display XR registers\n");
fprintf(stderr, "-z display interrupt registers\n");
fprintf(stderr, "\n");
fprintf(stderr, "-A display register address\n");
fprintf(stderr, "-N suppress display of register name\n");
exit(-1);
}
int
main(int argc, char *argv[])
{
struct ath_diag atd;
const char *ifname;
u_int32_t *data;
u_int32_t *dp, *ep;
int what, c, i;
struct ath_driver_req req;
ath_driver_req_init(&req);
ifname = getenv("ATH");
if (!ifname)
ifname = ATH_DEFAULT;
what = 0;
state.show_addrs = 0;
state.show_names = 1;
while ((c = getopt(argc, argv, "i:aAbdkmNqxz")) != -1)
switch (c) {
case 'a':
what |= DUMP_ALL;
break;
case 'A':
state.show_addrs = 1;
break;
case 'b':
what |= DUMP_BASEBAND;
break;
case 'd':
what |= DUMP_DCU;
break;
case 'k':
what |= DUMP_KEYCACHE;
break;
case 'i':
ifname = optarg;
break;
case 'm':
what |= DUMP_BASIC;
break;
case 'N':
state.show_names = 0;
break;
case 'q':
what |= DUMP_QCU;
break;
case 'x':
what |= DUMP_XR;
break;
case 'z':
what |= DUMP_INTERRUPT;
break;
default:
usage();
}
if (ath_driver_req_open(&req, ifname) < 0) {
exit(127);
}
strncpy(atd.ad_name, ifname, sizeof (atd.ad_name));
argc -= optind;
argv += optind;
if (what == 0)
what = DUMP_BASIC;
atd.ad_id = HAL_DIAG_REVS;
atd.ad_out_data = (caddr_t) &state.revs;
atd.ad_out_size = sizeof(state.revs);
if (ath_driver_req_fetch_diag(&req, SIOCGATHDIAG, &atd) < 0)
err(1, "%s", atd.ad_name);
if (ath_hal_setupregs(&atd, what) == 0)
errx(-1, "no registers are known for this part "
"(devid 0x%x mac %d.%d phy %d)", state.revs.ah_devid,
state.revs.ah_macVersion, state.revs.ah_macRev,
state.revs.ah_phyRev);
atd.ad_out_size = ath_hal_setupdiagregs((HAL_REGRANGE *) atd.ad_in_data,
atd.ad_in_size / sizeof(HAL_REGRANGE));
atd.ad_out_data = (caddr_t) malloc(atd.ad_out_size);
if (atd.ad_out_data == NULL) {
fprintf(stderr, "Cannot malloc output buffer, size %u\n",
atd.ad_out_size);
exit(-1);
}
atd.ad_id = HAL_DIAG_REGS | ATH_DIAG_IN | ATH_DIAG_DYN;
if (ath_driver_req_fetch_diag(&req, SIOCGATHDIAG, &atd) < 0)
err(1, "%s", atd.ad_name);
dp = (u_int32_t *)atd.ad_out_data;
ep = (u_int32_t *)(atd.ad_out_data + atd.ad_out_size);
while (dp < ep) {
u_int r = dp[0];
u_int e = dp[1];
dp++;
dp++;
r >>= 2; e >>= 2;
do {
if (dp >= ep) {
fprintf(stderr, "Warning, botched return data;"
"register at offset 0x%x not present\n",
r << 2);
break;
}
state.regdata[r++] = *dp++;
} while (r <= e);
}
if (what & DUMP_BASIC)
ath_hal_dumpregs(stdout, DUMP_BASIC);
if ((what & DUMP_INTERRUPT) && ath_hal_anyregs(DUMP_INTERRUPT)) {
if (what & DUMP_BASIC)
putchar('\n');
if (state.show_addrs)
ath_hal_dumpregs(stdout, DUMP_INTERRUPT);
else
ath_hal_dumpint(stdout, what);
}
if ((what & DUMP_QCU) && ath_hal_anyregs(DUMP_QCU)) {
if (what & (DUMP_BASIC|DUMP_INTERRUPT))
putchar('\n');
if (state.show_addrs)
ath_hal_dumpregs(stdout, DUMP_QCU);
else
ath_hal_dumpqcu(stdout, what);
}
if ((what & DUMP_DCU) && ath_hal_anyregs(DUMP_DCU)) {
if (what & (DUMP_BASIC|DUMP_INTERRUPT|DUMP_QCU))
putchar('\n');
if (state.show_addrs)
ath_hal_dumpregs(stdout, DUMP_DCU);
else
ath_hal_dumpdcu(stdout, what);
}
if (what & DUMP_KEYCACHE) {
if (state.show_addrs) {
if (what & (DUMP_BASIC|DUMP_INTERRUPT|DUMP_QCU|DUMP_DCU))
putchar('\n');
ath_hal_dumpregs(stdout, DUMP_KEYCACHE);
} else
ath_hal_dumpkeycache(stdout, 128);
}
if (what & DUMP_BASEBAND) {
if (what &~ DUMP_BASEBAND)
fprintf(stdout, "\n");
ath_hal_dumpbb(stdout, what);
}
ath_driver_req_close(&req);
return 0;
}
static int
regcompar(const void *a, const void *b)
{
const struct dumpreg *ra = *(const struct dumpreg **)a;
const struct dumpreg *rb = *(const struct dumpreg **)b;
return ra->addr - rb->addr;
}
void
register_regs(struct dumpreg *chipregs, u_int nchipregs,
int def_srev_min, int def_srev_max, int def_phy_min, int def_phy_max)
{
const int existing_regs = state.nregs;
int i, j;
for (i = 0; i < nchipregs; i++) {
struct dumpreg *nr = &chipregs[i];
if (nr->srevMin == 0)
nr->srevMin = def_srev_min;
if (nr->srevMax == 0)
nr->srevMax = def_srev_max;
if (nr->phyMin == 0)
nr->phyMin = def_phy_min;
if (nr->phyMax == 0)
nr->phyMax = def_phy_max;
for (j = 0; j < existing_regs; j++) {
struct dumpreg *r = state.regs[j];
if (nr->addr == r->addr &&
(nr->name == r->name ||
nr->name != NULL && r->name != NULL &&
strcmp(nr->name, r->name) == 0)) {
if (nr->srevMin < r->srevMin &&
(r->srevMin <= nr->srevMax &&
nr->srevMax+1 <= r->srevMax)) {
r->srevMin = nr->srevMin;
goto skip;
}
if (nr->srevMax > r->srevMax &&
(r->srevMin <= nr->srevMin &&
nr->srevMin <= r->srevMax)) {
r->srevMax = nr->srevMax;
goto skip;
}
}
if (r->addr > nr->addr)
break;
}
if (state.nregs == MAXREGS)
errx(-1, "too many registers; bump MAXREGS");
state.regs[state.nregs++] = nr;
skip:
;
}
qsort(state.regs, state.nregs, sizeof(struct dumpreg *), regcompar);
}
void
register_keycache(u_int nslots,
int def_srev_min, int def_srev_max, int def_phy_min, int def_phy_max)
{
#define SET(r, a) do { \
r->addr = a; r->type = DUMP_KEYCACHE; r++; \
} while(0)
struct dumpreg *keyregs, *r;
int i;
keyregs = (struct dumpreg *) calloc(nslots, 8*sizeof(struct dumpreg));
if (keyregs == NULL)
errx(-1, "no space to %d keycache slots\n", nslots);
r = keyregs;
for (i = 0; i < nslots; i++) {
SET(r, AR_KEYTABLE_KEY0(i));
SET(r, AR_KEYTABLE_KEY1(i));
SET(r, AR_KEYTABLE_KEY2(i));
SET(r, AR_KEYTABLE_KEY3(i));
SET(r, AR_KEYTABLE_KEY4(i));
SET(r, AR_KEYTABLE_TYPE(i));
SET(r, AR_KEYTABLE_MAC0(i));
SET(r, AR_KEYTABLE_MAC1(i));
}
register_regs(keyregs, 8*nslots,
def_srev_min, def_srev_max, def_phy_min, def_phy_max);
#undef SET
}
void
register_range(u_int brange, u_int erange, int type,
int def_srev_min, int def_srev_max, int def_phy_min, int def_phy_max)
{
struct dumpreg *bbregs, *r;
int i, nregs;
nregs = (erange - brange) / sizeof(uint32_t);
bbregs = (struct dumpreg *) calloc(nregs, sizeof(struct dumpreg));
if (bbregs == NULL)
errx(-1, "no space for %d register slots (type %d)\n",
nregs, type);
r = bbregs;
for (i = 0; i < nregs; i++) {
r->addr = brange + (i<<2);
r->type = type;
r++;
}
register_regs(bbregs, nregs,
def_srev_min, def_srev_max, def_phy_min, def_phy_max);
}
static __inline int
match(const struct dumpreg *dr, const HAL_REVS *revs)
{
if (!MAC_MATCH(dr, revs->ah_macVersion, revs->ah_macRev))
return 0;
if ((dr->type & DUMP_BASEBAND) && !PHY_MATCH(dr, revs->ah_phyRev))
return 0;
return 1;
}
static int
ath_hal_anyregs(int what)
{
const HAL_REVS *revs = &state.revs;
int i;
for (i = 0; i < state.nregs; i++) {
const struct dumpreg *dr = state.regs[i];
if ((what & dr->type) && match(dr, revs))
return 1;
}
return 0;
}
static int
ath_hal_setupregs(struct ath_diag *atd, int what)
{
const HAL_REVS *revs = &state.revs;
HAL_REGRANGE r;
size_t space = 0;
u_int8_t *cp;
int i, brun, erun;
brun = erun = -1;
for (i = 0; i < state.nregs; i++) {
const struct dumpreg *dr = state.regs[i];
if ((what & dr->type) && match(dr, revs)) {
if (erun + 4 != dr->addr) {
if (brun != -1)
space += sizeof(HAL_REGRANGE);
brun = erun = dr->addr;
} else
erun = dr->addr;
}
}
space += sizeof(HAL_REGRANGE);
atd->ad_in_data = (caddr_t) malloc(space);
if (atd->ad_in_data == NULL) {
fprintf(stderr, "Cannot malloc memory for registers!\n");
exit(-1);
}
atd->ad_in_size = space;
cp = (u_int8_t *) atd->ad_in_data;
brun = erun = -1;
for (i = 0; i < state.nregs; i++) {
const struct dumpreg *dr = state.regs[i];
if ((what & dr->type) && match(dr, revs)) {
if (erun + 4 != dr->addr) {
if (brun != -1) {
r.start = brun, r.end = erun;
memcpy(cp, &r, sizeof(r));
cp += sizeof(r);
}
brun = erun = dr->addr;
} else
erun = dr->addr;
}
}
if (brun != -1) {
r.start = brun, r.end = erun;
memcpy(cp, &r, sizeof(r));
cp += sizeof(r);
}
return space / sizeof(uint32_t);
}
static void
ath_hal_dumpregs(FILE *fd, int what)
{
const HAL_REVS *revs = &state.revs;
const char *sep = "";
int i, count, itemsperline;
count = 0;
itemsperline = 4;
if (state.show_names && state.show_addrs)
itemsperline--;
for (i = 0; i < state.nregs; i++) {
const struct dumpreg *dr = state.regs[i];
if ((what & dr->type) && match(dr, revs)) {
if (state.show_names && dr->name != NULL) {
fprintf(fd, "%s%-8s", sep, dr->name);
if (state.show_addrs)
fprintf(fd, " [%04x]", dr->addr);
} else
fprintf(fd, "%s%04x", sep, dr->addr);
fprintf(fd, " %08x", OS_REG_READ(ah, dr->addr));
sep = " ";
if ((++count % itemsperline) == 0)
sep = "\n";
}
}
if (count)
fprintf(fd, "\n");
}
static void
ath_hal_dumprange(FILE *fd, u_int a, u_int b)
{
u_int r;
for (r = a; r+16 <= b; r += 5*4)
fprintf(fd,
"%04x %08x %04x %08x %04x %08x %04x %08x %04x %08x\n"
, r, OS_REG_READ(ah, r)
, r+4, OS_REG_READ(ah, r+4)
, r+8, OS_REG_READ(ah, r+8)
, r+12, OS_REG_READ(ah, r+12)
, r+16, OS_REG_READ(ah, r+16)
);
switch (b-r) {
case 16:
fprintf(fd
, "%04x %08x %04x %08x %04x %08x %04x %08x\n"
, r, OS_REG_READ(ah, r)
, r+4, OS_REG_READ(ah, r+4)
, r+8, OS_REG_READ(ah, r+8)
, r+12, OS_REG_READ(ah, r+12)
);
break;
case 12:
fprintf(fd, "%04x %08x %04x %08x %04x %08x\n"
, r, OS_REG_READ(ah, r)
, r+4, OS_REG_READ(ah, r+4)
, r+8, OS_REG_READ(ah, r+8)
);
break;
case 8:
fprintf(fd, "%04x %08x %04x %08x\n"
, r, OS_REG_READ(ah, r)
, r+4, OS_REG_READ(ah, r+4)
);
break;
case 4:
fprintf(fd, "%04x %08x\n"
, r, OS_REG_READ(ah, r)
);
break;
}
}
static void
ath_hal_dumpint(FILE *fd, int what)
{
int i;
fprintf(fd, "IMR: %08x S0 %08x S1 %08x S2 %08x S3 %08x S4 %08x\n"
, OS_REG_READ(ah, AR_IMR)
, OS_REG_READ(ah, AR_IMR_S0)
, OS_REG_READ(ah, AR_IMR_S1)
, OS_REG_READ(ah, AR_IMR_S2)
, OS_REG_READ(ah, AR_IMR_S3)
, OS_REG_READ(ah, AR_IMR_S4)
);
fprintf(fd, "ISR: %08x S0 %08x S1 %08x S2 %08x S3 %08x S4 %08x\n"
, OS_REG_READ(ah, AR_ISR)
, OS_REG_READ(ah, AR_ISR_S0)
, OS_REG_READ(ah, AR_ISR_S1)
, OS_REG_READ(ah, AR_ISR_S2)
, OS_REG_READ(ah, AR_ISR_S3)
, OS_REG_READ(ah, AR_ISR_S4)
);
}
static void
ath_hal_dumpqcu(FILE *fd, int what)
{
int i;
fprintf(fd, "%-8s %08x %-8s %08x %-8s %08x\n"
, "Q_TXE", OS_REG_READ(ah, AR_Q_TXE)
, "Q_TXD", OS_REG_READ(ah, AR_Q_TXD)
, "Q_RDYTIMSHD", OS_REG_READ(ah, AR_Q_RDYTIMESHDN)
);
fprintf(fd, "Q_ONESHOTARM_SC %08x Q_ONESHOTARM_CC %08x\n"
, OS_REG_READ(ah, AR_Q_ONESHOTARM_SC)
, OS_REG_READ(ah, AR_Q_ONESHOTARM_CC)
);
for (i = 0; i < 10; i++)
fprintf(fd, "Q[%u] TXDP %08x CBR %08x RDYT %08x MISC %08x STS %08x\n"
, i
, OS_REG_READ(ah, AR_QTXDP(i))
, OS_REG_READ(ah, AR_QCBRCFG(i))
, OS_REG_READ(ah, AR_QRDYTIMECFG(i))
, OS_REG_READ(ah, AR_QMISC(i))
, OS_REG_READ(ah, AR_QSTS(i))
);
}
static void
ath_hal_dumpdcu(FILE *fd, int what)
{
int i;
for (i = 0; i < 10; i++)
fprintf(fd, "D[%u] MASK %08x IFS %08x RTRY %08x CHNT %08x MISC %06x\n"
, i
, OS_REG_READ(ah, AR_DQCUMASK(i))
, OS_REG_READ(ah, AR_DLCL_IFS(i))
, OS_REG_READ(ah, AR_DRETRY_LIMIT(i))
, OS_REG_READ(ah, AR_DCHNTIME(i))
, OS_REG_READ(ah, AR_DMISC(i))
);
}
static void
ath_hal_dumpbb(FILE *fd, int what)
{
const HAL_REVS *revs = &state.revs;
int i, brun, erun;
brun = erun = 0;
for (i = 0; i < state.nregs; i++) {
const struct dumpreg *dr = state.regs[i];
if (!match(dr, revs))
continue;
if (dr->type & DUMP_BASEBAND) {
if (brun == 0) {
brun = erun = dr->addr;
} else if (dr->addr == erun + sizeof(uint32_t)) {
erun = dr->addr;
} else {
ath_hal_dumprange(fd, brun, erun);
brun = erun = dr->addr;
}
} else {
if (brun != 0)
ath_hal_dumprange(fd, brun, erun);
brun = erun = 0;
}
}
if (brun != 0)
ath_hal_dumprange(fd, brun, erun);
}
static u_int
ath_hal_setupdiagregs(const HAL_REGRANGE regs[], u_int nr)
{
u_int space;
int i;
space = 0;
for (i = 0; i < nr; i++) {
u_int n = sizeof(HAL_REGRANGE) + sizeof(u_int32_t);
if (regs[i].end) {
if (regs[i].end < regs[i].start) {
fprintf(stderr, "%s: bad register range, "
"end 0x%x < start 0x%x\n",
__func__, regs[i].end, regs[i].end);
exit(-1);
}
n += regs[i].end - regs[i].start;
}
space += n;
}
return space;
}
static const char*
ether_sprintf(const u_int8_t *mac)
{
static char etherbuf[18];
snprintf(etherbuf, sizeof(etherbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return etherbuf;
}
#ifndef isclr
#define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
#define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
#define isset(a,i) ((a)[(i)/NBBY] & (1<<((i)%NBBY)))
#define isclr(a,i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
#endif
static void
ath_hal_dumpkeycache(FILE *fd, int nkeys)
{
static const char *keytypenames[] = {
"WEP-40",
"WEP-104",
"#2",
"WEP-128",
"TKIP",
"AES-OCB",
"AES-CCM",
"CLR",
};
int micEnabled = SREV(state.revs.ah_macVersion, state.revs.ah_macRev) < SREV(4,8) ? 0 :
OS_REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_CRPT_MIC_ENABLE;
u_int8_t mac[IEEE80211_ADDR_LEN];
u_int8_t ismic[128/NBBY];
int entry;
int first = 1;
memset(ismic, 0, sizeof(ismic));
for (entry = 0; entry < nkeys; entry++) {
u_int32_t macLo, macHi, type;
u_int32_t key0, key1, key2, key3, key4;
macHi = OS_REG_READ(ah, AR_KEYTABLE_MAC1(entry));
if ((macHi & AR_KEYTABLE_VALID) == 0 && isclr(ismic, entry))
continue;
macLo = OS_REG_READ(ah, AR_KEYTABLE_MAC0(entry));
macHi <<= 1;
if (macLo & (1<<31))
macHi |= 1;
macLo <<= 1;
mac[4] = macHi & 0xff;
mac[5] = macHi >> 8;
mac[0] = macLo & 0xff;
mac[1] = macLo >> 8;
mac[2] = macLo >> 16;
mac[3] = macLo >> 24;
type = OS_REG_READ(ah, AR_KEYTABLE_TYPE(entry));
if ((type & 7) == AR_KEYTABLE_TYPE_TKIP && micEnabled)
setbit(ismic, entry+64);
key0 = OS_REG_READ(ah, AR_KEYTABLE_KEY0(entry));
key1 = OS_REG_READ(ah, AR_KEYTABLE_KEY1(entry));
key2 = OS_REG_READ(ah, AR_KEYTABLE_KEY2(entry));
key3 = OS_REG_READ(ah, AR_KEYTABLE_KEY3(entry));
key4 = OS_REG_READ(ah, AR_KEYTABLE_KEY4(entry));
if (first) {
fprintf(fd, "\n");
first = 0;
}
fprintf(fd, "KEY[%03u] MAC %s %-7s %02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x\n"
, entry
, ether_sprintf(mac)
, isset(ismic, entry) ? "MIC" : keytypenames[type & 7]
, (key0 >> 0) & 0xff
, (key0 >> 8) & 0xff
, (key0 >> 16) & 0xff
, (key0 >> 24) & 0xff
, (key1 >> 0) & 0xff
, (key1 >> 8) & 0xff
, (key2 >> 0) & 0xff
, (key2 >> 8) & 0xff
, (key2 >> 16) & 0xff
, (key2 >> 24) & 0xff
, (key3 >> 0) & 0xff
, (key3 >> 8) & 0xff
, (key4 >> 0) & 0xff
, (key4 >> 8) & 0xff
, (key4 >> 16) & 0xff
, (key4 >> 24) & 0xff
);
}
}