initial checkin of project
commit
c3b046a817
|
@ -0,0 +1,11 @@
|
|||
CFLAGS=-Wall -g `pkg-config --cflags libpci`
|
||||
LDFLAGS=`pkg-config --libs libpci`
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $^
|
||||
|
||||
rtl8168-eeprom: rtl8168-eeprom.o
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -f rtl8168-eeprom rtl8168-eeprom.o
|
|
@ -0,0 +1,12 @@
|
|||
rtl8168-mac-eeprom utility
|
||||
|
||||
Usage:
|
||||
|
||||
./rtl8168_mac_eeprom
|
||||
will generate an EEPROM backup file for all supported cards
|
||||
|
||||
./rtl8168_mac_eeprom -s 1:0
|
||||
will generate an EEPROM backup file for PCI slot 1:0
|
||||
|
||||
./rtl8168_mac_eeprom -s 1:0 -m 01:02:03:04:05:06
|
||||
will set the new mac address 01:02:03:04:05:06 to PCI slot 1:0
|
|
@ -0,0 +1,523 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pci/pci.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
/* utility to program MAC address of RTL8168 / RTL8111E EEPROM
|
||||
* (C) 2015 by Harald Welte <hwelte@sysmocom.de>
|
||||
*/
|
||||
|
||||
static void die(char *msg, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, msg);
|
||||
vfprintf(stderr, msg, args);
|
||||
fputc('\n', stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int macaddr_parse(uint8_t *out, const char *in)
|
||||
{
|
||||
/* 00:00:00:00:00:00 */
|
||||
char tmp[18];
|
||||
char *tok;
|
||||
unsigned int i = 0;
|
||||
|
||||
if (strlen(in) < 17)
|
||||
return -1;
|
||||
|
||||
strncpy(tmp, in, sizeof(tmp)-1);
|
||||
tmp[sizeof(tmp)-1] = '\0';
|
||||
|
||||
for (tok = strtok(tmp, ":"); tok && (i < 6); tok = strtok(NULL, ":")) {
|
||||
unsigned long ul = strtoul(tok, NULL, 16);
|
||||
out[i++] = ul & 0xff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RTL8188E SPI Access
|
||||
*/
|
||||
|
||||
enum rtlspi_pin {
|
||||
RTLSPI_CS,
|
||||
RTLSPI_CK,
|
||||
RTLSPI_SI,
|
||||
RTLSPI_SO,
|
||||
};
|
||||
|
||||
const uint8_t pin2bit[] = {
|
||||
#if 0
|
||||
/* according to RTL8111F Series EEPROM & eFuse data sheet */
|
||||
[RTLSPI_CS] = 0x40,
|
||||
[RTLSPI_CK] = 0x20,
|
||||
[RTLSPI_SI] = 0x10,
|
||||
[RTLSPI_SO] = 0x08,
|
||||
#define RTLSPI_C0_REG 0x51
|
||||
#else
|
||||
/* according to non-mainline driver patch, works on 8111E */
|
||||
[RTLSPI_CS] = 0x08,
|
||||
[RTLSPI_CK] = 0x04,
|
||||
[RTLSPI_SI] = 0x02,
|
||||
[RTLSPI_SO] = 0x01,
|
||||
#define RTLSPI_C0_REG 0x50
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
static void rtlspi_delay(void)
|
||||
{
|
||||
usleep(3);
|
||||
}
|
||||
|
||||
/* this is ugly and should go */
|
||||
int g_memfd;
|
||||
void *g_mapped_base;
|
||||
|
||||
#define rtl_reg_readb(offs) *((uint8_t *) g_mapped_base + offs)
|
||||
#define rtl_reg_writeb(offs, val) *((uint8_t *) g_mapped_base + offs) = (val)
|
||||
|
||||
int rtlspi_init(struct pci_dev *d)
|
||||
{
|
||||
size_t page_size = (size_t) sysconf (_SC_PAGESIZE);
|
||||
|
||||
g_memfd = open("/dev/mem", O_RDWR | O_SYNC);
|
||||
if (g_memfd < 0) {
|
||||
perror("open");
|
||||
return -1;
|
||||
}
|
||||
g_mapped_base = mmap(0, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, g_memfd, d->base_addr[2] & PCI_ADDR_MEM_MASK);
|
||||
if (g_mapped_base == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
close(g_memfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
uint8_t v;
|
||||
v = rtl_reg_readb(0x52);
|
||||
v &= ~0xc0;
|
||||
rtl_reg_writeb(0x52, v);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtlspi_fini(struct pci_dev *d)
|
||||
{
|
||||
munmap(g_mapped_base, d->size[2]);
|
||||
close(g_memfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtlspi_pin_set(struct pci_dev *d, enum rtlspi_pin pin, int lvl)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
if (pin > (sizeof(pin2bit)/sizeof(pin2bit[0])))
|
||||
return -1;
|
||||
|
||||
val = rtl_reg_readb(RTLSPI_C0_REG);
|
||||
val |= 0x80;
|
||||
val &= ~0x40;
|
||||
if (lvl)
|
||||
val |= pin2bit[pin];
|
||||
else
|
||||
val &= ~pin2bit[pin];
|
||||
|
||||
rtl_reg_writeb(RTLSPI_C0_REG, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtlspi_pin_get(struct pci_dev *d, enum rtlspi_pin pin)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
if (pin > (sizeof(pin2bit)/sizeof(pin2bit[0])))
|
||||
return -1;
|
||||
|
||||
val = rtl_reg_readb(RTLSPI_C0_REG);
|
||||
|
||||
if (val & pin2bit[pin])
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtlspi_xceive_bit(struct pci_dev *d, int lvl)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* set the bit */
|
||||
rc = rtlspi_pin_set(d, RTLSPI_SI, lvl);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rtlspi_delay();
|
||||
|
||||
/* raising edge on CK */
|
||||
rc = rtlspi_pin_set(d, RTLSPI_CK, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rtlspi_delay();
|
||||
|
||||
/* falling edge of clock */
|
||||
rc = rtlspi_pin_set(d, RTLSPI_CK, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = rtlspi_pin_get(d, RTLSPI_SO);
|
||||
|
||||
rtlspi_delay();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int rtlspi_xceive_bits(struct pci_dev *d, uint32_t data, int num_bits)
|
||||
{
|
||||
int i, rc;
|
||||
uint32_t in = 0;
|
||||
|
||||
for (i = 0; i < num_bits; i ++) {
|
||||
uint32_t bit_out = (data >> (num_bits - i -1)) & 1;
|
||||
in <<= 1;
|
||||
rc = rtlspi_xceive_bit(d, bit_out);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
in |= rc & 1;
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* AT93 EEPROM
|
||||
*/
|
||||
|
||||
enum at93_op {
|
||||
AT93_OP_MISC = 0,
|
||||
AT93_OP_WRITE = 1,
|
||||
AT93_OP_READ = 2,
|
||||
AT93_OP_ERASE = 3,
|
||||
};
|
||||
|
||||
static int at93c46_op(struct pci_dev *d, enum at93_op op, uint8_t addr, uint16_t data)
|
||||
{
|
||||
uint32_t in;
|
||||
int rc;
|
||||
|
||||
/* set initial state of pins: CS/SK/DI low */
|
||||
rc = rtlspi_pin_set(d, RTLSPI_CS, 0);
|
||||
rc |= rtlspi_pin_set(d, RTLSPI_CK, 0);
|
||||
rc |= rtlspi_pin_set(d, RTLSPI_SI, 0);
|
||||
if (rc)
|
||||
return -1;
|
||||
|
||||
rtlspi_delay();
|
||||
|
||||
/* start with raising edge on CS */
|
||||
rc = rtlspi_pin_set(d, RTLSPI_CS, 1);
|
||||
|
||||
rtlspi_delay();
|
||||
|
||||
/* start bit */
|
||||
rc = rtlspi_xceive_bit(d, 1);
|
||||
|
||||
/* READ command bits */
|
||||
rc = rtlspi_xceive_bit(d, op & 2);
|
||||
rc = rtlspi_xceive_bit(d, op & 1);
|
||||
|
||||
/* send address */
|
||||
rc = rtlspi_xceive_bits(d, addr, 6);
|
||||
|
||||
switch (op) {
|
||||
case AT93_OP_ERASE:
|
||||
case AT93_OP_MISC:
|
||||
in = 0;
|
||||
break;
|
||||
default:
|
||||
/* read/write data */
|
||||
in = rtlspi_xceive_bits(d, data, 16);
|
||||
break;
|
||||
}
|
||||
|
||||
/* stop with falling edge on CS */
|
||||
rc = rtlspi_pin_set(d, RTLSPI_CS, 0);
|
||||
|
||||
if (rc)
|
||||
return -1;
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
/* read one word */
|
||||
int at93c46_op_read(struct pci_dev *d, uint8_t addr)
|
||||
{
|
||||
return at93c46_op(d, AT93_OP_READ, addr, 0);
|
||||
}
|
||||
|
||||
/* write one word */
|
||||
int at93c46_op_write(struct pci_dev *d, uint8_t addr, uint16_t data)
|
||||
{
|
||||
int rc = at93c46_op(d, AT93_OP_WRITE, addr, data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* endable write access */
|
||||
int at93c46_op_ewen(struct pci_dev *d)
|
||||
{
|
||||
return at93c46_op(d, AT93_OP_MISC, 0x30, 0);
|
||||
}
|
||||
|
||||
/* endable write access */
|
||||
int at93c46_op_erase(struct pci_dev *d, uint8_t addr)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
rc = at93c46_op(d, AT93_OP_ERASE, addr, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* wait until ERASE cycle has finished */
|
||||
rtlspi_delay();
|
||||
rtlspi_pin_set(d, RTLSPI_CS, 1);
|
||||
for (i = 0; i < 0xffff; i++) {
|
||||
rc = rtlspi_pin_get(d, RTLSPI_SO);
|
||||
//rc = rtlspi_xceive_bit(d, 0);
|
||||
if (rc == 1)
|
||||
break;
|
||||
}
|
||||
rtlspi_pin_set(d, RTLSPI_CS, 0);
|
||||
|
||||
if (i == 0xffff) {
|
||||
fprintf(stderr, "timeout during ERASE\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* disable write access */
|
||||
int at93c46_op_ewds(struct pci_dev *d)
|
||||
{
|
||||
return at93c46_op(d, AT93_OP_MISC, 0x00, 0);
|
||||
}
|
||||
|
||||
static int eeprom_magic_ok(struct pci_dev *p)
|
||||
{
|
||||
if (at93c46_op_read(p, 0) != 0x8129)
|
||||
return 0;
|
||||
if (at93c46_op_read(p, 1) != 0x10ec)
|
||||
return 0;
|
||||
if (at93c46_op_read(p, 2) != 0x8168)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int eeprom_get_mac(struct pci_dev *p, uint8_t *mac_addr)
|
||||
{
|
||||
uint16_t tmp;
|
||||
|
||||
if (!eeprom_magic_ok(p))
|
||||
return -1;
|
||||
|
||||
tmp = at93c46_op_read(p, 7);
|
||||
mac_addr[0] = tmp & 0xff;
|
||||
mac_addr[1] = tmp >> 8;
|
||||
tmp = at93c46_op_read(p, 8);
|
||||
mac_addr[2] = tmp & 0xff;
|
||||
mac_addr[3] = tmp >> 8;
|
||||
tmp = at93c46_op_read(p, 9);
|
||||
mac_addr[4] = tmp & 0xff;
|
||||
mac_addr[5] = tmp >> 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eeprom_set_mac(struct pci_dev *p, const uint8_t *mac_addr)
|
||||
{
|
||||
uint16_t tmp;
|
||||
int rc = 0;
|
||||
|
||||
if (!eeprom_magic_ok(p))
|
||||
return -1;
|
||||
|
||||
printf("Writing new MAC address %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]);
|
||||
|
||||
at93c46_op_ewen(p);
|
||||
|
||||
tmp = mac_addr[0] | mac_addr[1] << 8;
|
||||
rc |= at93c46_op_erase(p, 7);
|
||||
rc |= at93c46_op_write(p, 7, tmp);
|
||||
|
||||
tmp = mac_addr[2] | mac_addr[3] << 8;
|
||||
rc |= at93c46_op_erase(p, 8);
|
||||
rc |= at93c46_op_write(p, 8, tmp);
|
||||
tmp = mac_addr[4] | mac_addr[5] << 8;
|
||||
|
||||
rc |= at93c46_op_erase(p, 9);
|
||||
rc |= at93c46_op_write(p, 9, tmp);
|
||||
if (rc)
|
||||
die("Error during EEPROM WRITE, MAC address is corrupt!\n");
|
||||
|
||||
at93c46_op_ewds(p);
|
||||
|
||||
printf("You need to COLD BOOT for the new address to be used\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* generate a full dump */
|
||||
static int eeprom_backup(struct pci_dev *p)
|
||||
{
|
||||
uint8_t mac_addr[6];
|
||||
char fname[PATH_MAX];
|
||||
int i, rc, outfd;
|
||||
|
||||
rc = eeprom_get_mac(p, mac_addr);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
snprintf(fname, sizeof(fname), "%02X%02X%02X%02X%02X%02X.backup",
|
||||
mac_addr[0], mac_addr[1], mac_addr[2],
|
||||
mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
|
||||
outfd = open(fname, O_CREAT|O_WRONLY);
|
||||
if (!outfd)
|
||||
die("Can't open/create %s\n", fname);
|
||||
|
||||
printf("Saving EEPROM backup to %s\n", fname);
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
uint8_t tmp[2];
|
||||
int rc = at93c46_op_read(p, i);
|
||||
if (rc < 0)
|
||||
die("Error reading EEPROM addr %d\n", i);
|
||||
|
||||
tmp[0] = rc & 0xff;
|
||||
tmp[1] = rc >> 8;
|
||||
if (write(outfd, tmp, 2) != 2)
|
||||
die("Error writing to backup file %s\n", fname);
|
||||
}
|
||||
close(outfd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iterate_devices(struct pci_access *pa, char *filter_id, char *filter_slot,
|
||||
const uint8_t *new_mac)
|
||||
{
|
||||
struct pci_filter filt;
|
||||
struct pci_dev *p;
|
||||
char *msg;
|
||||
|
||||
/* filter for specific devices only */
|
||||
printf("building filter\n");
|
||||
pci_filter_init(pa, &filt);
|
||||
|
||||
if (filter_id) {
|
||||
msg = pci_filter_parse_id(&filt, filter_id);
|
||||
if (msg)
|
||||
die(msg);
|
||||
}
|
||||
if (filter_slot) {
|
||||
msg = pci_filter_parse_slot(&filt, filter_slot);
|
||||
if (msg)
|
||||
die(msg);
|
||||
}
|
||||
|
||||
printf("starting bus iteration\n");
|
||||
for (p = pa->devices; p; p = p->next) {
|
||||
uint8_t old_mac[6];
|
||||
if (!pci_filter_match(&filt, p))
|
||||
continue;
|
||||
|
||||
/* our own clumsy implementation of filtering */
|
||||
if (p->vendor_id != 0x10ec || p->device_id != 0x8168)
|
||||
continue;
|
||||
|
||||
printf("found matching device (%02x:%02x.%d), ", p->bus, p->dev, p->func);
|
||||
printf("base_addr=0x%lx (len=%lu)\n", p->base_addr[2] & PCI_ADDR_MEM_MASK, p->size[2]);
|
||||
|
||||
if (rtlspi_init(p) < 0)
|
||||
die("Cannot initialize RTL SPI mode\n");
|
||||
|
||||
if (!eeprom_magic_ok(p))
|
||||
die("EEPROM Magic !OK\n");
|
||||
|
||||
if (eeprom_get_mac(p, old_mac) < 0)
|
||||
die("Cannot read existing MAC addr from EEPROM\n");
|
||||
|
||||
printf("Existing/Old MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
old_mac[0], old_mac[1], old_mac[2],
|
||||
old_mac[3], old_mac[4], old_mac[5]);
|
||||
|
||||
/* will die itself */
|
||||
eeprom_backup(p);
|
||||
|
||||
if (new_mac)
|
||||
eeprom_set_mac(p, new_mac);
|
||||
|
||||
rtlspi_fini(p);
|
||||
|
||||
/* we support only one device per execution (for now) */
|
||||
if (new_mac)
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
char *filter_id = NULL;
|
||||
char *filter_slot = NULL;
|
||||
uint8_t new_macbuf[6];
|
||||
uint8_t *new_mac = NULL;
|
||||
struct pci_access *pa = pci_alloc();
|
||||
|
||||
/* same syntax as lspci */
|
||||
while ((i = getopt(argc, argv, "d:s:m:")) != -1) {
|
||||
switch (i) {
|
||||
case 'd':
|
||||
filter_id = optarg;
|
||||
break;
|
||||
case 's':
|
||||
filter_slot = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
if (macaddr_parse(new_macbuf, optarg))
|
||||
die("Unable to parse `%s' as mac address\n", optarg);
|
||||
new_mac = new_macbuf;
|
||||
break;
|
||||
default:
|
||||
die("Syntax error");
|
||||
}
|
||||
}
|
||||
|
||||
printf("initializing pci access\n");
|
||||
pa->error = die;
|
||||
pa->writeable = 1;
|
||||
pci_init(pa);
|
||||
pci_scan_bus(pa);
|
||||
|
||||
iterate_devices(pa, filter_id, filter_slot, new_mac);
|
||||
|
||||
exit(0);
|
||||
}
|
Loading…
Reference in New Issue