Atheros AR5414A - Enable Write to EEPROM to fix bad Mac-Address

Here is a method to change mac address or PID/VID on a Atheros 5414A Mini-PCI Card. I had two identical cards and one of them had severed problems to be detected, both on a Linux (openwrt) Router and on a Windows Laptop...

After inspection of the two eeproms content, i've seen the macaddress was not set on the bad card (ff:ff:ff:ff:ff:ff). I don't remember how that happened, maybe i had a special driver before which was ignoring this bad mac address... Or maybe this card came from a dual pci card router to support Dual Range 2.4Ghz + 5Ghz

For your information the mac address ff:ff:ff:ff:ff:ff is used for network broadcast.

I have modified the 2.6.35 ubuntu ath5k kernel module to add abilities to write to I2C eeprom.

I had to remove the Write Protect 10K Resistor from the wifi card (which is a bad thing ;), but you just have to connect WP pin to GND (this pin is protected in I²C eeprom to be connected directly to GND or VCC)

wide_AR5414A.jpg
Click to enlarge

in /usr/src/linux-source-2.6.35/drivers/net/wireless/ath/ath5k/eeprom.c add :

/*
 * Write to eeprom
 */
static int ath5k_hw_eeprom_write(struct ath5k_hw *ah, u32 offset, u16 *data)
{
        u32 status, timeout;
        
        *data &= 0xffff;
 
        /*
         * Initialize EEPROM access
         */
        if (ah->ah_version == AR5K_AR5210) {
 
                // AR5210 not tested here but...
                AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE);
                 ath5k_hw_reg_write(ah, data, AR5K_EEPROM_BASE + (4 * offset));
 
        } else {
                ath5k_hw_reg_write(ah, 0, AR5K_EEPROM_CMD);
                udelay(30);
                AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_RESET);
                udelay(30);
                ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE);
                ath5k_hw_reg_write(ah, *data, AR5K_EEPROM_DATA);
                AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD, AR5K_EEPROM_CMD_WRITE);
                udelay(30);
        }
 
        for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) {
                status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS);
                if (status & AR5K_EEPROM_STAT_WRDONE) {
                        if (status & AR5K_EEPROM_STAT_WRERR) {
                                printk(KERN_ERR " TP: -EIO");
                                return -EIO;
                        }
                        ath5k_hw_eeprom_read(ah, offset, data);
                        printk(KERN_INFO " Check: %04x", *data);
                        return 0;
                }
                udelay(15);
        }
        
        status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS);
        printk(KERN_ERR " status: %x", status);
 
        printk(KERN_ERR " TP: -ETIMEDOUT");
 
        ath5k_hw_reg_write(ah, 0, AR5K_EEPROM_STATUS);
        
        ath5k_hw_eeprom_read(ah, offset, data);
        printk(KERN_ERR " Check: %x", (u32) *data);
 
        return -ETIMEDOUT;
}

Then write the macaddress to eeprom, before the read... here is the modified procedure (last proc in eeprom.c) :

/*
 * Read the MAC address from eeprom
 */
int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
{
        u8 mac_d[ETH_ALEN] = {};
        u32 total, offset;
        u16 data;
        int octet, ret;
 
        printk(KERN_INFO "TP: writing eeprom mac addr...");
 
        // mac address
        offset=0x00a5; data=0x0300;
        ret = ath5k_hw_eeprom_write(ah, offset, &data);
        offset=0x00a6; data=0x057f;
        ret = ath5k_hw_eeprom_write(ah, offset, &data);
        offset=0x00a7; data=0x1234;
        ret = ath5k_hw_eeprom_write(ah, offset, &data);
 
        // mac address is present at two places, this second offset is reversed (CRC ?)
        offset=0x001d; data=0x3412;
        ret = ath5k_hw_eeprom_write(ah, offset, &data);
        offset=0x001e; data=0x7f05;
        ret = ath5k_hw_eeprom_write(ah, offset, &data);
        offset=0x001f; data=0x0003;
        ret = ath5k_hw_eeprom_write(ah, offset, &data);
        
        
        printk(KERN_INFO "TP: reading eeprom mac addr...");
 
        //DUMP EEPROM TO SYSLOG
        for (offset = 0; offset <= 0x2ff; offset++) {
                data = 0;
                ret = ath5k_hw_eeprom_read(ah, offset, &data);
                if (ret)
                        printk (KERN_ERR "%04x:XXXX", offset);
                else
                        printk (KERN_ERR "%04x:%04x", offset, data);
        }
 
        ret = ath5k_hw_eeprom_read(ah, 0x20, &data);
        if (ret)
                printk(KERN_ERR "TP: eeprom read error : %d", ret);
 
        for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) {
                ret = ath5k_hw_eeprom_read(ah, offset, &data);
                if (ret)
                        printk(KERN_ERR "TP: eeprom read error offset %x : %d", offset, ret);
                        //return ret;
 
                total += data;
                mac_d[octet + 1] = data & 0xff;
                mac_d[octet] = data >> 8;
                octet += 2;
        }
 
        if (!total || total == 3 * 0xffff) {
                // prevent buggy ff:ff:ff:ff:ff:ff mac-address
                printk(KERN_ERR "TP: bad mac-adress = ff:ff:ff:ff:ff:ff");
                mac_d[0]=0x30;
                //return -EINVAL;
        }
 
        memcpy(mac, mac_d, ETH_ALEN);
 
        return 0;
}

Compile the module :

First, you need kernel sources and headers.
Go to your kernel base dir, here /usr/src/linux-source-2.6.35

make -C /lib/modules/2.6.35-27-generic/build \
SUBDIRS=/usr/src/linux-source-2.6.35/drivers/net/wireless/ath/ath5k modules

/lib/modules/2.6.35-27-generic/build -> /usr/src/linux-headers-2.6.35-27-generic

Check the module dependencies with modinfo or lsmod and load them in a wifi.sh script :

modprobe -r ath5k
modprobe cfg80211
modprobe led_class
modprobe mac80211
modprobe ath
insmod /usr/src/linux-source-2.6.35/drivers/net/wireless/ath/ath5k/ath5k.ko

With this method, vermagic could also be different.... the make modules_install is useless because its for a oneshot write.

You can download the content of my two cards eeproms, the bad one was fixed by this procedure, but its the original eeprom content, before the change.

There is no CRC for the beginning of the EEPROM (offset 0x00 to 0xC0)
check file eeprom.h for more info...

Links :
- http://wireless.kernel.org/en/users/Drivers/ath5k
- http://wireless.kernel.org/en/users/Drivers/ath
- http://kerneltrap.org/mailarchive/linux-ath5k-devel/2008/11/18/4148304
- http://lxr.free-electrons.com/source/drivers/net/wireless/ath/ath5k/
- http://www.mobilnews.cz/blog/?p=36

AttachmentSizeDownloadsLast Download
bad_eeprom_AR5414A_v5002.hex_.txt11 KB41141 week 2 days ago
good_eeprom_AR5414A_v5003.hex_.txt11 KB42631 week 2 days ago