madwifi/tools/ath_info.c
2007-11-02 02:05:22 +00:00

1112 lines
30 KiB
C

/* -*- linux-c -*- */
/*-
* Copyright (c) 2007 Nick Kossifidis <mickflemm@gmail.com>
* Copyright (c) 2007 Joerg Albert <jal2 *at* gmx.de>
*
* 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
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY, without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* So here is how it works:
*
* First compile...
*
* gcc ath_info.c -o ath_info
*
* then find card's physical address
*
* lspci -v
*
* 02:02.0 Ethernet controller: Atheros Communications, Inc. AR5212 802.11abg NIC (rev 01)
* Subsystem: Fujitsu Limited. Unknown device 1234
* Flags: bus master, medium devsel, latency 168, IRQ 23
* Memory at c2000000 (32-bit, non-prefetchable) [size=64K]
* Capabilities: [44] Power Management version 2
*
* address here is 0xc2000000
*
* load madwifi-ng or madwifi-old if not already loaded (be sure the
* interface is down!)
*
* modprobe ath_pci
*
* OR
*
* call:
* setpci -s 02:02.0 command=0x41f cache_line_size=0x10
*
* to enable access to the PCI device.
*
* and we run the thing...
*
* ./ath_info 0xc2000000
*
* In order to change the regdomain to 0, call:
*
* ./ath_info -w 0xc2000000 regdomain 0
*
* to change any PCI ID value, say:
*
* ./ath_info -w 0xc2000000 <name> X
*
* with <name> ::= pci_dev_id | pci_vendor_id | pci_class |
* pci_subsys_dev_id | pci_subsys_vendor_id
*
* With newer chipsets (>= AR5004x, i.e. MAC >= AR5213), Atheros introduced
* 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 the PCB layout,
* so there may be different GPIO used.
* This program currently sets GPIO 4 to low for a MAC >= AR5213, but you can
* override this with the -g option:
*
* ./ath_info -g 5:0 -w 0xc2000000 regdomain X
*
* would set GPIO 5 to low (and wouldn't touch GPIO 4). -g can be given several times.
*
* The write function is currently not tested with 5210 devices.
*
* Use at your own risk, entering a false device address will have really
* nasty results!
*
* Writing wrong values to the PCI id fields may prevent the driver from
* detecting the card!
*
* Transmitting on illegal frequencies may violate state laws. Stick to the local
* regulations!
*
* DISCLAIMER:
* The authors are in no case responsible for damaged hardware or violation of
* local laws by operating modified hardware.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <endian.h>
#include <byteswap.h>
#define dbg(fmt, __args__...) \
do { \
if (verbose) \
printf("#DBG %s: " fmt "\n", __FUNCTION__, ##__args__); \
} while (0)
#define err(fmt, __args__...) \
fprintf(stderr, "#ERR %s: " fmt "\n", __FUNCTION__, ##__args__)
#define AR5K_PCI_MEM_SIZE 0x10000
#define AR5K_ELEMENTS(_array) (sizeof(_array) / sizeof(_array[0]))
#define AR5K_NUM_GPIO 6
#define AR5K_GPIOCR 0x4014 /* Register Address */
#define AR5K_GPIOCR_OUT(n) (3 << ((n) * 2)) /* Mode 3 for pin n */
#define AR5K_GPIOCR_INT_SEL(n) ((n) << 12) /* Interrupt for GPIO pin n */
/*
* GPIO (General Purpose Input/Output) data output register
*/
#define AR5K_GPIODO 0x4018
/*
* GPIO (General Purpose Input/Output) data input register
*/
#define AR5K_GPIODI 0x401c
/*
* Common silicon revision/version values
*/
enum ath5k_srev_type {
AR5K_VERSION_VER,
AR5K_VERSION_REV,
AR5K_VERSION_RAD,
};
struct ath5k_srev_name {
const char *sr_name;
enum ath5k_srev_type sr_type;
u_int sr_val;
};
#define AR5K_SREV_UNKNOWN 0xffff
/* Known MAC revision numbers */
#define AR5K_SREV_VER_AR5210 0x00
#define AR5K_SREV_VER_AR5311 0x10
#define AR5K_SREV_VER_AR5311A 0x20
#define AR5K_SREV_VER_AR5311B 0x30
#define AR5K_SREV_VER_AR5211 0x40
#define AR5K_SREV_VER_AR5212 0x50
#define AR5K_SREV_VER_AR5213 0x55
#define AR5K_SREV_VER_AR5213A 0x59
#define AR5K_SREV_VER_AR2424 0xa0
#define AR5K_SREV_VER_AR5424 0xa3
#define AR5K_SREV_VER_AR5413 0xa4
#define AR5K_SREV_VER_AR5414 0xa5
#define AR5K_SREV_VER_AR5416 0xc0
#define AR5K_SREV_VER_AR5418 0xca
#define AR5K_SREV_VER_AR2425 0xe0
/* Known PHY revision nymbers */
#define AR5K_SREV_RAD_5110 0x00
#define AR5K_SREV_RAD_5111 0x10
#define AR5K_SREV_RAD_5111A 0x15
#define AR5K_SREV_RAD_2111 0x20
#define AR5K_SREV_RAD_5112 0x30
#define AR5K_SREV_RAD_5112A 0x35
#define AR5K_SREV_RAD_2112 0x40
#define AR5K_SREV_RAD_2112A 0x45
#define AR5K_SREV_RAD_SC1 0x63 /* Found on 5413/5414 */
#define AR5K_SREV_RAD_SC2 0xa2 /* Found on 2424/5424 */
#define AR5K_SREV_RAD_5133 0xc0 /* MIMO found on 5418 */
static const struct ath5k_srev_name ath5k_srev_names[] = {
{"5210", AR5K_VERSION_VER, AR5K_SREV_VER_AR5210},
{"5311", AR5K_VERSION_VER, AR5K_SREV_VER_AR5311},
{"5311A", AR5K_VERSION_VER, AR5K_SREV_VER_AR5311A},
{"5311B", AR5K_VERSION_VER, AR5K_SREV_VER_AR5311B},
{"5211", AR5K_VERSION_VER, AR5K_SREV_VER_AR5211},
{"5212", AR5K_VERSION_VER, AR5K_SREV_VER_AR5212},
{"5213", AR5K_VERSION_VER, AR5K_SREV_VER_AR5213},
{"5213A", AR5K_VERSION_VER, AR5K_SREV_VER_AR5213A},
{"2424", AR5K_VERSION_VER, AR5K_SREV_VER_AR2424},
{"5424", AR5K_VERSION_VER, AR5K_SREV_VER_AR5424},
{"5413", AR5K_VERSION_VER, AR5K_SREV_VER_AR5413},
{"5414", AR5K_VERSION_VER, AR5K_SREV_VER_AR5414},
{"5416", AR5K_VERSION_VER, AR5K_SREV_VER_AR5416},
{"5418", AR5K_VERSION_VER, AR5K_SREV_VER_AR5418},
{"2425", AR5K_VERSION_VER, AR5K_SREV_VER_AR2425},
{"xxxxx", AR5K_VERSION_VER, AR5K_SREV_UNKNOWN},
{"5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110},
{"5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111},
{"2111", AR5K_VERSION_RAD, AR5K_SREV_RAD_2111},
{"5112", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112},
{"5112a", AR5K_VERSION_RAD, AR5K_SREV_RAD_5112A},
{"2112", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112},
{"2112a", AR5K_VERSION_RAD, AR5K_SREV_RAD_2112A},
{"SChip", AR5K_VERSION_RAD, AR5K_SREV_RAD_SC1},
{"SChip", AR5K_VERSION_RAD, AR5K_SREV_RAD_SC2},
{"5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133},
{"xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN},
};
/*
* Silicon revision register
*/
#define AR5K_SREV 0x4020 /* Register Address */
#define AR5K_SREV_REV 0x0000000f /* Mask for revision */
#define AR5K_SREV_REV_S 0
#define AR5K_SREV_VER 0x000000ff /* Mask for version */
#define AR5K_SREV_VER_S 4
/*
* PHY chip revision register
*/
#define AR5K_PHY_CHIP_ID 0x9818
/*
* PHY register
*/
#define AR5K_PHY_BASE 0x9800
#define AR5K_PHY(_n) (AR5K_PHY_BASE + ((_n) << 2))
#define AR5K_PHY_SHIFT_2GHZ 0x00004007
#define AR5K_PHY_SHIFT_5GHZ 0x00000007
#define AR5K_RESET_CTL 0x4000 /* Register Address */
#define AR5K_RESET_CTL_PCU 0x00000001 /* Protocol Control Unit reset */
#define AR5K_RESET_CTL_DMA 0x00000002 /* DMA (Rx/Tx) reset -5210 only */
#define AR5K_RESET_CTL_BASEBAND 0x00000002 /* Baseband reset (5211/5212) */
#define AR5K_RESET_CTL_MAC 0x00000004 /* MAC reset (PCU+Baseband?) -5210 only */
#define AR5K_RESET_CTL_PHY 0x00000008 /* PHY reset -5210 only */
#define AR5K_RESET_CTL_PCI 0x00000010 /* PCI Core reset (interrupts etc) */
#define AR5K_RESET_CTL_CHIP (AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA | \
AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY)
/*
* Sleep control register
*/
#define AR5K_SLEEP_CTL 0x4004 /* Register Address */
#define AR5K_SLEEP_CTL_SLDUR 0x0000ffff /* Sleep duration mask */
#define AR5K_SLEEP_CTL_SLDUR_S 0
#define AR5K_SLEEP_CTL_SLE 0x00030000 /* Sleep enable mask */
#define AR5K_SLEEP_CTL_SLE_S 16
#define AR5K_SLEEP_CTL_SLE_WAKE 0x00000000 /* Force chip awake */
#define AR5K_SLEEP_CTL_SLE_SLP 0x00010000 /* Force chip sleep */
#define AR5K_SLEEP_CTL_SLE_ALLOW 0x00020000
#define AR5K_SLEEP_CTL_SLE_UNITS 0x00000008 /* not on 5210 */
#define AR5K_PCICFG 0x4010 /* Register Address */
#define AR5K_PCICFG_EEAE 0x00000001 /* Eeprom access enable [5210] */
#define AR5K_PCICFG_CLKRUNEN 0x00000004 /* CLKRUN enable [5211+] */
#define AR5K_PCICFG_EESIZE 0x00000018 /* Mask for EEPROM size [5211+] */
#define AR5K_PCICFG_EESIZE_S 3
#define AR5K_PCICFG_EESIZE_4K 0 /* 4K */
#define AR5K_PCICFG_EESIZE_8K 1 /* 8K */
#define AR5K_PCICFG_EESIZE_16K 2 /* 16K */
#define AR5K_PCICFG_EESIZE_FAIL 3 /* Failed to get size (?) [5211+] */
#define AR5K_PCICFG_SPWR_DN 0x00010000 /* Mask for power status (5210) */
#define AR5K_EEPROM_BASE 0x6000
#define AR5K_EEPROM_MAGIC 0x003d /* Offset for EEPROM Magic number */
#define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */
#define AR5K_EEPROM_MAGIC_5212 0x0000145c /* 5212 */
#define AR5K_EEPROM_MAGIC_5211 0x0000145b /* 5211 */
#define AR5K_EEPROM_MAGIC_5210 0x0000145a /* 5210 */
/*
* EEPROM data register
*/
#define AR5K_EEPROM_DATA_5211 0x6004
#define AR5K_EEPROM_DATA_5210 0x6800
#define AR5K_EEPROM_DATA (mac_version == AR5K_SREV_VER_AR5210 ? \
AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211)
/*
* EEPROM command register
*/
#define AR5K_EEPROM_CMD 0x6008 /* Register Addres */
#define AR5K_EEPROM_CMD_READ 0x00000001 /* EEPROM read */
#define AR5K_EEPROM_CMD_WRITE 0x00000002 /* EEPROM write */
#define AR5K_EEPROM_CMD_RESET 0x00000004 /* EEPROM reset */
/*
* EEPROM status register
*/
#define AR5K_EEPROM_STAT_5210 0x6c00 /* Register Address [5210] */
#define AR5K_EEPROM_STAT_5211 0x600c /* Register Address [5211+] */
#define AR5K_EEPROM_STATUS (mac_version == AR5K_SREV_VER_AR5210 ? \
AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211)
#define AR5K_EEPROM_STAT_RDERR 0x00000001 /* EEPROM read failed */
#define AR5K_EEPROM_STAT_RDDONE 0x00000002 /* EEPROM read successful */
#define AR5K_EEPROM_STAT_WRERR 0x00000004 /* EEPROM write failed */
#define AR5K_EEPROM_STAT_WRDONE 0x00000008 /* EEPROM write successful */
#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* Offset for EEPROM regulatory domain */
#define AR5K_EEPROM_INFO_BASE 0x00c0 /* Offset for EEPROM header */
#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE)
#define AR5K_EEPROM_INFO_CKSUM 0xffff
#define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n))
#define AR5K_EEPROM_MODE_11A 0
#define AR5K_EEPROM_MODE_11B 1
#define AR5K_EEPROM_MODE_11G 2
#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1)
#define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */
#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1) /* Device has a support */
#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1) /* Device has b support */
#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1) /* Device has g support */
#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2Ghz (?) */
#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for a/XR mode (eeprom_init) */
#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7)
#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz (?) */
#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */
/* Misc values available since EEPROM 4.0 */
#define AR5K_EEPROM_MISC0 0x00c4
#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff)
#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3)
#define AR5K_EEPROM_MISC1 0x00c5
#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff)
#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1)
/*
* Read data by masking
*/
#define AR5K_REG_MS(_val, _flags) \
(((_val) & (_flags)) >> _flags##_S)
/*
* Access device registers
*/
#if __BYTE_ORDER == __BIG_ENDIAN
#define AR5K_REG_READ(_reg) \
__bswap_32(*((volatile u_int32_t *)(mem + (_reg))))
#define AR5K_REG_WRITE(_reg, _val) \
(*((volatile u_int32_t *)(mem + (_reg))) = __bswap_32(_val))
#else
#define AR5K_REG_READ(_reg) \
(*((volatile u_int32_t *)(mem + (_reg))))
#define AR5K_REG_WRITE(_reg, _val) \
(*((volatile u_int32_t *)(mem + (_reg))) = (_val))
#endif
#define AR5K_REG_ENABLE_BITS(_reg, _flags) \
AR5K_REG_WRITE(_reg, AR5K_REG_READ(_reg) | (_flags))
#define AR5K_REG_DISABLE_BITS(_reg, _flags) \
AR5K_REG_WRITE(_reg, AR5K_REG_READ(_reg) & ~(_flags))
#define AR5K_TUNE_REGISTER_TIMEOUT 20000
/* names for eeprom fields */
struct eeprom_entry {
const char *name;
int addr;
};
static const struct eeprom_entry eeprom_addr[] = {
{"pci_dev_id", 0},
{"pci_vendor_id", 1},
{"pci_class", 2},
{"pci_rev_id", 3},
{"pci_subsys_dev_id", 7},
{"pci_subsys_vendor_id", 8},
{"regdomain", AR5K_EEPROM_REG_DOMAIN},
};
static const int eeprom_addr_len = sizeof(eeprom_addr) / sizeof(eeprom_addr[0]);
static int force_write = 0;
static int verbose = 0;
/* forward decl. */
static void usage(const char *n);
static u_int32_t ath5k_hw_bitswap(u_int32_t val, u_int bits)
{
u_int32_t retval = 0, bit, i;
for (i = 0; i < bits; i++) {
bit = (val >> i) & 1;
retval = (retval << 1) | bit;
}
return (retval);
}
/*
* Get the PHY Chip revision
*/
static u_int16_t
ath5k_hw_radio_revision(u_int16_t mac_version, void *mem, u_int8_t chip)
{
int i;
u_int32_t srev;
u_int16_t ret;
/*
* Set the radio chip access register
*/
switch (chip) {
case 0:
AR5K_REG_WRITE(AR5K_PHY(0), AR5K_PHY_SHIFT_2GHZ);
break;
case 1:
AR5K_REG_WRITE(AR5K_PHY(0), AR5K_PHY_SHIFT_5GHZ);
break;
default:
return (0);
}
usleep(2000);
/* ...wait until PHY is ready and read the selected radio revision */
AR5K_REG_WRITE(AR5K_PHY(0x34), 0x00001c16);
for (i = 0; i < 8; i++)
AR5K_REG_WRITE(AR5K_PHY(0x20), 0x00010000);
if (mac_version == AR5K_SREV_VER_AR5210) {
srev = AR5K_REG_READ(AR5K_PHY(256) >> 28) & 0xf;
ret = (u_int16_t)ath5k_hw_bitswap(srev, 4) + 1;
} else {
srev = (AR5K_REG_READ(AR5K_PHY(0x100)) >> 24) & 0xff;
ret = (u_int16_t)ath5k_hw_bitswap(((srev & 0xf0) >> 4) |
((srev & 0x0f) << 4), 8);
}
/* Reset to the 5GHz mode */
AR5K_REG_WRITE(AR5K_PHY(0), AR5K_PHY_SHIFT_5GHZ);
return (ret);
}
/*
* Write to EEPROM
*/
static int
ath5k_hw_eeprom_write(void *mem, u_int32_t offset, u_int16_t data,
u_int8_t mac_version)
{
u_int32_t status, timeout;
/*
* Initialize EEPROM access
*/
if (mac_version == AR5K_SREV_VER_AR5210) {
AR5K_REG_ENABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_EEAE);
/* data to write */
(void)AR5K_REG_WRITE(AR5K_EEPROM_BASE + (4 * offset), data);
} else {
/* not 5210 */
/* reset eeprom access */
AR5K_REG_WRITE(AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_RESET);
usleep(5);
AR5K_REG_WRITE(AR5K_EEPROM_DATA, data);
/* set offset in EEPROM to write to */
AR5K_REG_WRITE(AR5K_EEPROM_BASE, offset);
usleep(5);
/* issue write command */
AR5K_REG_WRITE(AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_WRITE);
}
for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
status = AR5K_REG_READ(AR5K_EEPROM_STATUS);
if (status & AR5K_EEPROM_STAT_WRDONE) {
if (status & AR5K_EEPROM_STAT_WRERR) {
err("eeprom write access to 0x%04x failed",
offset);
return 1;
}
return 0;
}
usleep(15);
}
return 1;
}
/*
* Read from EEPROM
*/
static int
ath5k_hw_eeprom_read(void *mem, u_int32_t offset, u_int16_t *data,
u_int8_t mac_version)
{
u_int32_t status, timeout;
/*
* Initialize EEPROM access
*/
if (mac_version == AR5K_SREV_VER_AR5210) {
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;
}
static const char *ath5k_hw_get_part_name(enum ath5k_srev_type type,
u_int32_t val)
{
const char *name = "xxxxx";
int i;
for (i = 0; i < AR5K_ELEMENTS(ath5k_srev_names); i++) {
if (ath5k_srev_names[i].sr_type != type ||
ath5k_srev_names[i].sr_val == AR5K_SREV_UNKNOWN)
continue;
if ((val & 0xff) < ath5k_srev_names[i + 1].sr_val) {
name = ath5k_srev_names[i].sr_name;
break;
}
}
return (name);
}
/* returns -1 on unknown name */
static int eeprom_name2addr(const char *name)
{
int i;
if (!name || !name[0])
return -1;
for (i = 0; i < eeprom_addr_len; i++)
if (!strcmp(name, eeprom_addr[i].name))
return eeprom_addr[i].addr;
return -1;
} /* eeprom_name2addr */
/* returns "<unknown>" on unknown address */
static const char *eeprom_addr2name(int addr)
{
int i;
for (i = 0; i < eeprom_addr_len; i++)
if (eeprom_addr[i].addr == addr)
return eeprom_addr[i].name;
return "<unknown>";
} /* eeprom_addr2name */
static int
do_write_pairs(int anr, int argc, char **argv, unsigned char *mem,
int mac_version)
{
#define MAX_NR_WRITES 16
struct {
int addr;
unsigned int val;
} wr_ops[MAX_NR_WRITES];
int wr_ops_len = 0;
int i;
char *end;
int errors = 0; /* count errors during write/verify */
if (anr >= argc) {
err("missing values to write.");
usage(argv[0]);
return 1;
}
if ((argc - anr) % 2) {
err("write spec. needs an even number of arguments.");
usage(argv[0]);
return 2;
}
if ((argc - anr) / 2 > MAX_NR_WRITES) {
err("too many values to write (max. %d)", MAX_NR_WRITES);
return 3;
}
/* get the (addr,val) pairs we have to write */
i = 0;
while (anr < (argc - 1)) {
wr_ops[i].addr = strtoul(argv[anr], &end, 16);
if (end == argv[anr]) {
/* maybe a symbolic name for the address? */
if ((wr_ops[i].addr =
eeprom_name2addr(argv[anr])) == -1) {
err("pair %d: bad address %s", i, argv[anr]);
return 4;
}
}
if (wr_ops[i].addr >= AR5K_EEPROM_INFO_BASE) {
err("offset 0x%04x in CRC protected area is "
"not supported", wr_ops[i].addr);
return 5;
}
anr++;
wr_ops[i].val = strtoul(argv[anr], &end, 16);
if (end == argv[anr]) {
err("pair %d: bad val %s", i, argv[anr]);
return 5;
}
if (wr_ops[i].val > 0xffff) {
err("pair %d: value %u too large", i, wr_ops[i].val);
return 6;
}
anr++;
i++;
} /* while (anr < (argc-1)) */
if (!(wr_ops_len = i)) {
err("no (addr,val) pairs given");
return 7;
}
if (verbose || !force_write) {
for (i = 0; i < wr_ops_len; i++)
printf("%20s (0x%04x) := 0x%04x\n",
eeprom_addr2name(wr_ops[i].addr), wr_ops[i].addr,
wr_ops[i].val);
}
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;
}
}
for (i = 0; i < wr_ops_len; i++) {
u_int16_t oldval, u;
if (ath5k_hw_eeprom_read
(mem, wr_ops[i].addr, &oldval, mac_version)) {
err("failed to read old value from offset 0x%04x ",
wr_ops[i].addr);
errors++;
}
if (oldval == wr_ops[i].val) {
dbg("pair %d: skipped, value already there", i);
continue;
}
dbg("writing *0x%04x := 0x%04x", wr_ops[i].addr, wr_ops[i].val);
if (ath5k_hw_eeprom_write
(mem, wr_ops[i].addr, wr_ops[i].val, mac_version)) {
err("failed to write 0x%04x to offset 0x%04x",
wr_ops[i].val, wr_ops[i].addr);
errors++;
} else {
if (ath5k_hw_eeprom_read
(mem, wr_ops[i].addr, &u, mac_version)) {
err("failed to read offset 0x%04x for "
"verification", wr_ops[i].addr);
errors++;
} else {
if (u != wr_ops[i].val) {
err("offset 0x%04x: wrote 0x%04x but "
"read 0x%04x", wr_ops[i].addr,
wr_ops[i].val, u);
errors++;
}
}
}
}
return errors ? 11 : 0;
} /* do_write_pairs */
static void usage(const char *n)
{
int i;
fprintf(stderr, "%s [-w [-g N:M]] [-v] [-f] [-d] <base_address> "
"[<name1> <val1> [<name2> <val2> ...]]\n\n", n);
fprintf(stderr,
"-w write values into EEPROM\n"
"-g N:M set GPIO N to level M (only used with -w)\n"
"-v verbose output\n"
"-f force; suppress question before writing\n"
"-d dump eeprom (file 'ath-eeprom-dump.bin' and screen)\n"
"<base_address> device base address (see lspci output)\n\n");
fprintf(stderr,
"- read info:\n"
" %s <base_address>\n\n"
"- set regdomain to N:\n"
" %s -w <base_address> regdomain N\n\n"
"- set a PCI id field to value N:\n"
" %s -w <base_address> <field> N\n"
" where <field> is on of:\n ", n, n, n);
for (i = 0; i < eeprom_addr_len; i++)
fprintf(stderr, " %s", eeprom_addr[i].name);
fprintf(stderr, "\n\n");
fprintf(stderr,
"You may need to set a GPIO to a certain value in order to enable\n"
"writing to the EEPROM with newer chipsets, e.g. set GPIO 4 to low:\n"
" %s -g 4:0 -w <base_address> regdomain N\n", n);
fprintf(stderr,
"\nDISCLAIMER: The authors are not responsible for any damages caused by\n"
"this program. Writing improper values may damage the card or cause\n"
"unlawful radio transmissions!\n\n");
}
int main(int argc, char *argv[])
{
u_int32_t dev_addr;
u_int16_t eeprom_header, srev, phy_rev_5ghz, phy_rev_2ghz;
u_int16_t eeprom_version, mac_version, regdomain, has_crystal, ee_magic;
u_int8_t error, has_a, has_b, has_g, has_rfkill, eeprom_size;
int byte_size = 0;
void *mem;
int fd;
int i, anr = 1;
int do_write = 0; /* default: read only */
int do_dump = 0;
struct {
int valid;
int value;
} gpio_set[AR5K_NUM_GPIO];
int nr_gpio_set = 0;
for (i = 0; i < sizeof(gpio_set) / sizeof(gpio_set[0]); i++)
gpio_set[i].valid = 0;
if (argc < 2) {
usage(argv[0]);
return -1;
}
while (anr < argc && argv[anr][0] == '-') {
switch (argv[anr][1]) {
case 'w':
do_write = 1;
break;
case 'g':
anr++;
if (strlen(argv[anr]) != 3 || argv[anr][1] != ':' ||
argv[anr][0] < '0' || argv[anr][0] > '5' ||
(argv[anr][2] != '0' && argv[anr][2] != '1')) {
err("invalid gpio spec. %s", argv[anr]);
return 2;
}
gpio_set[argv[anr][0] - '0'].valid = 1;
gpio_set[argv[anr][0] - '0'].value = argv[anr][2] - '0';
nr_gpio_set++;
break;
case 'f':
force_write = 1;
break;
case 'v':
verbose = 1;
break;
case 'd':
do_dump = 1;
break;
case 'h':
usage(argv[0]);
return 0;
break;
default:
err("unknown option %s", argv[anr]);
return 2;
} /* switch (argv[anr][1]) */
anr++;
} /* while (anr < argc && ...) */
if (anr >= argc) {
err("missing device address");
usage(argv[0]);
return 3;
}
dev_addr = strtoul(argv[anr], NULL, 16);
fd = open("/dev/mem", O_RDWR);
if (fd < 0) {
printf("Opening /dev/mem failed!\n");
return -2;
}
mem = mmap(NULL, AR5K_PCI_MEM_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FILE, fd, dev_addr);
if (mem == MAP_FAILED) {
printf("Mmap of device at 0x%08X for 0x%X bytes failed - "
"%s\n", dev_addr, AR5K_PCI_MEM_SIZE, strerror(errno));
return -3;
}
/* wake from power-down and remove reset (in case the driver isn't running) */
{
u_int32_t
sleep_ctl = AR5K_REG_READ(AR5K_SLEEP_CTL),
reset_ctl = AR5K_REG_READ(AR5K_RESET_CTL);
dbg("sleep_ctl reg %08x reset_ctl reg %08x",
sleep_ctl, reset_ctl);
if (sleep_ctl & AR5K_SLEEP_CTL_SLE_SLP) {
dbg("waking up the chip");
AR5K_REG_WRITE(AR5K_SLEEP_CTL,
(sleep_ctl & ~AR5K_SLEEP_CTL_SLE_SLP));
}
if (reset_ctl) {
dbg("removing resets");
AR5K_REG_WRITE(AR5K_RESET_CTL, 0);
}
}
AR5K_REG_DISABLE_BITS(AR5K_PCICFG, AR5K_PCICFG_SPWR_DN);
usleep(500);
srev = AR5K_REG_READ(AR5K_SREV);
mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER) << 4;
/* Verify eeprom magic value first */
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_MAGIC, &ee_magic,
mac_version);
if (error) {
printf("Unable to read EEPROM Magic value!\n");
return -1;
}
if (ee_magic != AR5K_EEPROM_MAGIC_VALUE) {
printf("Warning: Invalid EEPROM Magic number!\n");
}
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_HDR, &eeprom_header,
mac_version);
if (error) {
printf("Unable to read EEPROM Header!\n");
return -1;
}
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_VERSION, &eeprom_version,
mac_version);
if (error) {
printf("Unable to read EEPROM version!\n");
return -1;
}
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_REG_DOMAIN, &regdomain,
mac_version);
if (error) {
printf("Unable to read Regdomain!\n");
return -1;
}
if (eeprom_version >= 0x4000) {
error = ath5k_hw_eeprom_read(mem, AR5K_EEPROM_MISC0,
&has_crystal, mac_version);
if (error) {
printf("Unable to read EEPROM Misc data!\n");
return -1;
}
has_crystal = AR5K_EEPROM_HAS32KHZCRYSTAL(has_crystal);
} else {
has_crystal = 2;
}
eeprom_size = AR5K_REG_MS(AR5K_REG_READ(AR5K_PCICFG),
AR5K_PCICFG_EESIZE);
has_a = AR5K_EEPROM_HDR_11A(eeprom_header);
has_b = AR5K_EEPROM_HDR_11B(eeprom_header);
has_g = AR5K_EEPROM_HDR_11G(eeprom_header);
has_rfkill = AR5K_EEPROM_HDR_RFKILL(eeprom_header);
if (has_a)
phy_rev_5ghz = ath5k_hw_radio_revision(mac_version, mem, 1);
else
phy_rev_5ghz = 0;
if (has_b)
phy_rev_2ghz = ath5k_hw_radio_revision(mac_version, mem, 0);
else
phy_rev_2ghz = 0;
printf(" -==Device Information==-\n");
printf("MAC Version: %-5s (0x%02x)\n",
ath5k_hw_get_part_name(AR5K_VERSION_VER, mac_version),
mac_version);
printf("MAC Revision: %-5s (0x%02x)\n",
ath5k_hw_get_part_name(AR5K_VERSION_VER, srev), srev);
/* Single-chip PHY with a/b/g support */
if (has_b && !phy_rev_2ghz) {
printf("PHY Revision: %-5s (0x%02x)\n",
ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_5ghz),
phy_rev_5ghz);
phy_rev_5ghz = 0;
}
/* Single-chip PHY with b/g support */
if (!has_a) {
printf("PHY Revision: %-5s (0x%02x)\n",
ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_2ghz),
phy_rev_2ghz);
phy_rev_2ghz = 0;
}
/* Different chip for 5Ghz and 2Ghz */
if (phy_rev_5ghz) {
printf("5Ghz PHY Revision: %-5s (0x%2x)\n",
ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_5ghz),
phy_rev_5ghz);
}
if (phy_rev_2ghz) {
printf("2Ghz PHY Revision: %-5s (0x%2x)\n",
ath5k_hw_get_part_name(AR5K_VERSION_RAD, phy_rev_2ghz),
phy_rev_2ghz);
}
printf(" -==EEPROM Information==-\n");
printf("EEPROM Version: %x.%x\n",
(eeprom_version & 0xF000) >> 12, eeprom_version & 0xFFF);
printf("EEPROM Size: ");
if (eeprom_size == 0) {
printf(" 4K\n");
byte_size = 4096;
} else if (eeprom_size == 1) {
printf(" 8K\n");
byte_size = 8192;
} else if (eeprom_size == 2) {
printf(" 16K\n");
byte_size = 16384;
} else
printf(" ??\n");
printf("Regulatory Domain: 0x%X\n", regdomain);
printf(" -==== Capabilities ====-\n");
printf("| 802.11a Support: ");
if (has_a)
printf("yes |\n");
else
printf("no |\n");
printf("| 802.11b Support: ");
if (has_b)
printf("yes |\n");
else
printf("no |\n");
printf("| 802.11g Support: ");
if (has_g)
printf("yes |\n");
else
printf("no |\n");
printf("| RFKill Support: ");
if (has_rfkill)
printf("yes |\n");
else
printf("no |\n");
if (has_crystal != 2) {
printf("| 32KHz Crystal: ");
if (has_crystal)
printf("yes |\n");
else
printf("no |\n");
}
printf(" ========================\n");
/* print current GPIO settings */
printf("GPIO registers: CR %08x DO %08x DI %08x\n",
AR5K_REG_READ(AR5K_GPIOCR), AR5K_REG_READ(AR5K_GPIODO),
AR5K_REG_READ(AR5K_GPIODI));
if (do_dump) {
u_int16_t data;
FILE *dumpfile = fopen("ath-eeprom-dump.bin", "w");
printf("\nEEPROM dump (%d byte)\n", byte_size);
printf("==============================================");
for (i = 1; i <= (byte_size / 2); i++) {
error =
ath5k_hw_eeprom_read(mem, i, &data, mac_version);
if (error) {
printf("\nUnable to read at %04x\n", i);
continue;
}
if (!((i - 1) % 8))
printf("\n%04x: ", i);
printf("%04x ", data);
fwrite(&data, 2, 1, dumpfile);
}
printf("\n==============================================\n");
fclose(dumpfile);
}
if (do_write) {
u_int32_t rcr = AR5K_REG_READ(AR5K_GPIOCR),
rdo = AR5K_REG_READ(AR5K_GPIODO);
u_int32_t old_cr = rcr, old_do = rdo;
int rc;
if (mac_version >= AR5K_SREV_VER_AR5213 && !nr_gpio_set) {
dbg("new MAC %x (>= AR5213) set gpio4 to low",
mac_version);
gpio_set[4].valid = 1;
gpio_set[4].value = 0;
}
/* set gpios */
dbg("old GPIO CR %08x DO %08x DI %08x",
rcr, rdo, AR5K_REG_READ(AR5K_GPIODI));
for (i = 0; i < sizeof(gpio_set) / sizeof(gpio_set[0]); i++) {
if (gpio_set[i].valid) {
rcr |= AR5K_GPIOCR_OUT(i); /* we use mode 3 */
rcr &= ~AR5K_GPIOCR_INT_SEL(i);
rdo &= ~(1 << i);
rdo |= (gpio_set[i].value << i);
}
}
if (rcr != old_cr) {
dbg("GPIO CR %x -> %x", old_cr, rcr);
AR5K_REG_WRITE(AR5K_GPIOCR, rcr);
}
usleep(5);
if (rdo != old_do) {
dbg("GPIO CR %x -> %x", old_do, rdo);
AR5K_REG_WRITE(AR5K_GPIODO, rdo);
}
/* dump current values again if we have written anything */
if (rcr != old_cr || rdo != old_do)
dbg("new GPIO CR %08x DO %08x DI %08x",
AR5K_REG_READ(AR5K_GPIOCR),
AR5K_REG_READ(AR5K_GPIODO),
AR5K_REG_READ(AR5K_GPIODI));
/* let argv[anr] be the first write parameter */
anr++;
rc = do_write_pairs(anr, argc, argv, mem, mac_version);
/* restore old GPIO settings */
if (rcr != old_cr) {
dbg("restoring GPIO CR %x -> %x", rcr, old_cr);
AR5K_REG_WRITE(AR5K_GPIOCR, old_cr);
}
usleep(5);
if (rdo != old_do) {
dbg("restoring GPIO CR %x -> %x", rdo, old_do);
AR5K_REG_WRITE(AR5K_GPIODO, old_do);
}
return rc;
}
return 0;
}