Skip to content

Commit

Permalink
Add EEPROM restore feature
Browse files Browse the repository at this point in the history
Add change MAC feature
Update README
  • Loading branch information
mickflemm committed Sep 17, 2011
1 parent 707e99f commit d60d090
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 56 deletions.
47 changes: 38 additions & 9 deletions README
@@ -1,10 +1,23 @@
-= INTRO =-

ABOUT:
ath_info is a tool created to help us reverse engineer EEPROM layout on atheros AR5xxx
series of cards and also to debug our drivers. It can also be used to tweak various
EEPROM values or registers even at runtime (be careful). Please be sure that you know
what you are doing before playing with this thing, it can render your card useless !!!

DISCLAIMER:
The authors are in no case responsible for damaged hardware or violation
of local laws by operating modified hardware.


So here is how it works: So here is how it works:


First compile... First compile...


gcc ath_info.c -o ath_info gcc ath_info.c -o ath_info


then find card's physical address then find your card's physical address


lspci -v lspci -v


Expand All @@ -16,8 +29,8 @@ lspci -v


address here is 0xc2000000 address here is 0xc2000000


load madwifi-ng or madwifi-old if not already loaded (be sure the If you don't have a driver loaded for your card (check out using e.g. lsmod), load
interface is down!) madwifi-ng/madwifi-old/ath5k (be sure the interface is down for safety!) e.g.:


modprobe ath_pci modprobe ath_pci


Expand All @@ -28,21 +41,41 @@ setpci -s 02:02.0 command=0x41f cache_line_size=0x10


to enable access to the PCI device. to enable access to the PCI device.


and we run the thing... and then run the thing...


./ath_info 0xc2000000 ./ath_info 0xc2000000



-= EXAMPLES =-

In order to change the regdomain to 0, call: In order to change the regdomain to 0, call:


./ath_info -w 0xc2000000 regdomain 0 ./ath_info -w 0xc2000000 regdomain 0


to change any PCI ID value, say: To change any PCI ID value, say:


./ath_info -w 0xc2000000 <name> X ./ath_info -w 0xc2000000 <name> X


with <name> ::= pci_dev_id | pci_vendor_id | pci_class | with <name> ::= pci_dev_id | pci_vendor_id | pci_class |
pci_subsys_dev_id | pci_subsys_vendor_id pci_subsys_dev_id | pci_subsys_vendor_id


To change mac address to 00:11:22:33:44:55, call:

./ath_info -M 0xc2000000 00:11:22:33:44:55

To dump the contents of your EEPROM to a dumpfile, call:

./ath_info -d 0xc2000000

and your dump will be saved on ath-eeprom-dump.bin.

To restore your EEPROM from a dumpfile, call:

./ath_info -r 0xc2000000 <dumpfile>


-= EEPROM WRITE PROTECTION =-

With newer chipsets (>= AR5004x, i.e. MAC >= AR5213), Atheros introduced With newer chipsets (>= AR5004x, i.e. MAC >= AR5213), Atheros introduced
write protection on the EEPROM. On a GIGABYTE GN-WI01HT you can set write protection on the EEPROM. On a GIGABYTE GN-WI01HT you can set
GPIO 4 to low to be able to write the EEPROM. This depends highly on GPIO 4 to low to be able to write the EEPROM. This depends highly on
Expand All @@ -63,7 +96,3 @@ the driver from detecting the card!


Transmitting on illegal frequencies may violate state laws. Stick to Transmitting on illegal frequencies may violate state laws. Stick to
the local regulations! the local regulations!

DISCLAIMER:
The authors are in no case responsible for damaged hardware or violation
of local laws by operating modified hardware.
188 changes: 141 additions & 47 deletions ath_info.c
@@ -1,7 +1,7 @@
/* -*- linux-c -*- */ /* -*- linux-c -*- */
/*- /*-
* Copyright (c) 2007 Nick Kossifidis <mickflemm@gmail.com> * Copyright (c) 2011 Nick Kossifidis <mickflemm@gmail.com>
* Copyright (c) 2007 Joerg Albert <jal2 *at* gmx.de> * Copyright (c) 2011 Joerg Albert <jal2 *at* gmx.de>
* *
* This program is free software you can redistribute it and/or modify * This program is free software you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
Expand Down Expand Up @@ -377,12 +377,46 @@ static u_int16_t ath5k_hw_radio_revision(u_int8_t chip)
return (ret); return (ret);
} }


/*
* Read from EEPROM
*/
static int ath5k_hw_eeprom_read(u_int32_t offset, u_int16_t *data)
{
u_int32_t status, timeout;

/*
* Initialize EEPROM access
*/
if (eeprom_access == AR5K_EEPROM_ACCESS_5210) {
AR5K_REG_ENABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_EEAE);
(void)AR5K_REG_READ(AR5K_EEPROM_BASE + (4 * offset));
} else {
AR5K_REG_WRITE(AR5K_EEPROM_BASE, offset);
AR5K_REG_ENABLE_BITS(AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_READ);
}

for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
status = AR5K_REG_READ(AR5K_EEPROM_STATUS);
if (status & AR5K_EEPROM_STAT_RDDONE) {
if (status & AR5K_EEPROM_STAT_RDERR)
return 1;
*data = (u_int16_t)
(AR5K_REG_READ(AR5K_EEPROM_DATA) & 0xffff);
return (0);
}
usleep(15);
}

return 1;
}

/* /*
* Write to EEPROM * Write to EEPROM
*/ */
static int ath5k_hw_eeprom_write(u_int32_t offset, u_int16_t data) static int ath5k_hw_eeprom_write(u_int32_t offset, u_int16_t data)
{ {
u_int32_t status, timeout; u_int32_t status, timeout;
u_int16_t read_data;


/* /*
* Initialize EEPROM access * Initialize EEPROM access
Expand Down Expand Up @@ -419,40 +453,13 @@ static int ath5k_hw_eeprom_write(u_int32_t offset, u_int16_t data)
offset); offset);
return 1; return 1;
} }
return 0; ath5k_hw_eeprom_read( offset, &read_data);
} if (read_data != data) {
usleep(15); err("data doesn't match, write failed at 0x%04x\n",
} offset);

return 1;
}

/*
* Read from EEPROM
*/
static int ath5k_hw_eeprom_read(u_int32_t offset, u_int16_t *data)
{
u_int32_t status, timeout;

/*
* Initialize EEPROM access
*/
if (eeprom_access == AR5K_EEPROM_ACCESS_5210) {
AR5K_REG_ENABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_EEAE);
(void)AR5K_REG_READ(AR5K_EEPROM_BASE + (4 * offset));
} else {
AR5K_REG_WRITE(AR5K_EEPROM_BASE, offset);
AR5K_REG_ENABLE_BITS(AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_READ);
}

for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
status = AR5K_REG_READ(AR5K_EEPROM_STATUS);
if (status & AR5K_EEPROM_STAT_RDDONE) {
if (status & AR5K_EEPROM_STAT_RDERR)
return 1; return 1;
*data = (u_int16_t) }
(AR5K_REG_READ(AR5K_EEPROM_DATA) & 0xffff); return 0;
return (0);
} }
usleep(15); usleep(15);
} }
Expand Down Expand Up @@ -1887,14 +1894,16 @@ static void usage(const char *n)
{ {
unsigned int i; unsigned int i;


fprintf(stderr, "%s [-w [-g N:M]] [-v] [-f] [-d] [-R addr] [-W addr val] <base_address> " fprintf(stderr, "%s [-w [-g N:M]] [-v] [-f] [-d] [-r] [-R addr] [-W addr val] <base_address> [dumpfile] "
"[<name1> <val1> [<name2> <val2> ...]]\n\n", n); "[<name1> <val1> [<name2> <val2> ...]]\n\n", n);
fprintf(stderr, fprintf(stderr,
"-w write values into EEPROM\n" "-w write values into EEPROM\n"
"-g N:M set GPIO N to level M (only used with -w)\n" "-g N:M set GPIO N to level M (only used with -w)\n"
"-v verbose output\n" "-v verbose output\n"
"-f force; suppress question before writing\n" "-f force; suppress question before writing\n"
"-d dump EEPROM (file 'ath-eeprom-dump.bin' and screen)\n" "-d dump EEPROM (file 'ath-eeprom-dump.bin' and screen)\n"
"-r restore EEPROM from dumpfile provided\n"
"-M <mac_addr> write provided mac address on EEPROM\n"
"-R <addr> read register at <addr> (hex)\n" "-R <addr> read register at <addr> (hex)\n"
"-W <addr> <val> write <val> (hex) into register at <addr> (hex)\n" "-W <addr> <val> write <val> (hex) into register at <addr> (hex)\n"
"<base_address> device base address (see lspci output)\n\n"); "<base_address> device base address (see lspci output)\n\n");
Expand Down Expand Up @@ -2510,8 +2519,12 @@ int main(int argc, char *argv[])
int anr = 1; int anr = 1;
int do_write = 0; /* default: read only */ int do_write = 0; /* default: read only */
int do_dump = 0; int do_dump = 0;
int do_restore = 0;
int change_mac = 0;
int reg_read = 0; int reg_read = 0;
int reg_write = 0; int reg_write = 0;
u_int8_t *mac_addr = NULL;
char* colon = NULL;
unsigned int reg_write_val = 0; unsigned int reg_write_val = 0;
unsigned int timer_count = 1; unsigned int timer_count = 1;
int do_keycache_dump = 0; int do_keycache_dump = 0;
Expand Down Expand Up @@ -2566,10 +2579,37 @@ int main(int argc, char *argv[])
verbose = 1; verbose = 1;
break; break;


case 'r':
do_restore = 1;
break;

case 'd': case 'd':
do_dump = 1; do_dump = 1;
break; break;


case 'M':
change_mac = 1;
anr++;
colon = strchr(argv[anr], ':');
if(!colon) {
printf("Invalid MAC address\n");
return -1;
}
mac_addr = malloc(sizeof(u_int8_t) * 6);
strcpy(colon, "\0");
mac_addr[0] = (u_int8_t) strtoul(argv[anr], NULL, 16);
for (i = 1; i < 5; i++) {
mac_addr[i] = (u_int8_t) strtoul(colon + 1, NULL, 16);
colon = strchr(colon + 1, ':');
if(!colon) {
printf("Invalid MAC address\n");
return -1;
}
strcpy(colon, "\0");
}
mac_addr[5] = (u_int8_t) strtoul(colon + 1, NULL, 16);
break;

case 'R': case 'R':
anr++; anr++;
reg_read = strtoul(argv[anr], NULL, 16); reg_read = strtoul(argv[anr], NULL, 16);
Expand Down Expand Up @@ -2797,6 +2837,17 @@ int main(int argc, char *argv[])
AR5K_REG_READ(AR5K_GPIOCR), AR5K_REG_READ(AR5K_GPIODO), AR5K_REG_READ(AR5K_GPIOCR), AR5K_REG_READ(AR5K_GPIODO),
AR5K_REG_READ(AR5K_GPIODI)); AR5K_REG_READ(AR5K_GPIODI));


sta_id0_id1_dump();

for (i = 0; i < timer_count; i++)
dump_timers_register();

if (do_keycache_dump)
keycache_dump();

if (keycache_copy_idx > 0)
keycache_copy(keycache_copy_idx);

if (do_dump) { if (do_dump) {
u_int16_t data; u_int16_t data;
FILE *dumpfile = fopen("ath-eeprom-dump.bin", "w"); FILE *dumpfile = fopen("ath-eeprom-dump.bin", "w");
Expand All @@ -2819,6 +2870,60 @@ int main(int argc, char *argv[])
fclose(dumpfile); fclose(dumpfile);
} }


if (do_restore) {
u_int16_t data;
if (argc < 2) {
fprintf(stderr, "No dumpfile provided\n");
return -1;
}
FILE *dumpfile = fopen(argv[anr + 1], "rb");
printf("\nEEPROM restore (%d bytes)\n", eeprom_size);
printf("==============================================");
for (i = 0; i < eeprom_size / 2; i++) {
fread(&data, 2, 1, dumpfile);
error =
ath5k_hw_eeprom_write(i, data);
if (error) {
printf("\nUnable to write at %04x\n", i);
continue;
}
if (!(i % 8))
printf("\n%04x: ", i);
printf(" %04x", data);
}
printf("\n==============================================\n");
fclose(dumpfile);
}

if (change_mac){
printf("MAC address to write: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac_addr[0], mac_addr[1], mac_addr[2],
mac_addr[3], mac_addr[4], mac_addr[5]);

if (!force_write) {
int c;
printf
("WARNING: The write function may easy brick your device or\n"
"violate state regulation on frequency usage.\n"
"Proceed on your own risk!\n"
"Shall I write the above value(s)? (y/n)\n");
c = getchar();
if (c != 'y' && c != 'Y') {
printf("user abort\n");
return 0;
}
}
/* Write MAC octets */
ath5k_hw_eeprom_write(0xa5, (mac_addr[0] | (mac_addr[1] << 8)));
ath5k_hw_eeprom_write(0xa6, (mac_addr[2] | (mac_addr[3] << 8)));
ath5k_hw_eeprom_write(0xa7, (mac_addr[4] | (mac_addr[5] << 8)));

/* And again reversed */
ath5k_hw_eeprom_write(0x1d, (mac_addr[5] | (mac_addr[4] << 8)));
ath5k_hw_eeprom_write(0x1e, (mac_addr[3] | (mac_addr[2] << 8)));
ath5k_hw_eeprom_write(0x1f, (mac_addr[1] | (mac_addr[0] << 8)));
}

if (do_write) { if (do_write) {
u_int32_t rcr = AR5K_REG_READ(AR5K_GPIOCR), u_int32_t rcr = AR5K_REG_READ(AR5K_GPIOCR),
rdo = AR5K_REG_READ(AR5K_GPIODO); rdo = AR5K_REG_READ(AR5K_GPIODO);
Expand Down Expand Up @@ -2883,16 +2988,5 @@ int main(int argc, char *argv[])
return rc; return rc;
} }


sta_id0_id1_dump();

for (i = 0; i < timer_count; i++)
dump_timers_register();

if (do_keycache_dump)
keycache_dump();

if (keycache_copy_idx > 0)
keycache_copy(keycache_copy_idx);

return 0; return 0;
} }

0 comments on commit d60d090

Please sign in to comment.