Commit 11a3cf2a authored by li hongming's avatar li hongming

Add tools gensdbfs/sdb-read.

    Make gensdbfs support the file order. sdb files should starts with xxfilename,
like 00wr-init. (In gensdbfs.c, I change the readdir() to scandir())
   Add two sdb files on 0x2e0000 to support the dualport's calibration and sfp
database. (db-calibration and db-sfp-database)
   Bug fix in shell/cmd_sfp.c.

   Now the one WR port function works well.
parent 0a497ec0
......@@ -68,7 +68,7 @@ struct wrc_device devs[] = {
{&BASE_MINIC[0], VID_CERN, 0xab28633a},
{&BASE_MINIC[1], VID_CERN, 0xa224633b},
{&BASE_EP[0], VID_CERN, 0x650c2d4f},
{&BASE_EP[1], VID_CERN, 0x621c2d4e},
{&BASE_EP[1], VID_CERN, 0x650c2d4e},
{&BASE_SOFTPLL, VID_CERN, 0x65158dc0},
{&BASE_PPS_GEN, VID_CERN, 0xde0d8ced},
{&BASE_SYSCON, VID_CERN, 0xff07fc47},
......
......@@ -72,7 +72,9 @@ void get_mac_addr(uint8_t dev_addr[], int port)
void ep_init(uint8_t mac_addr[], int port)
{
EP[port] = (volatile struct EP_WB *)BASE_EP[port];
pp_printf("Port %d address is %x", port, EP[port]);
set_mac_addr(mac_addr,port);
pp_printf("Port %d address is %x", port, EP[port]);
ep_sfp_enable(1,port);
if (!IS_WR_NODE_SIM){
......
......@@ -220,7 +220,7 @@ static int calib_t24p_master(uint32_t *value, int port)
rv = storage_phtrans(value, 0, port);
if(rv < 0) {
pp_printf("Error %d while reading t24p from storage\n", rv);
pp_printf("port %d Error %d while reading t24p from storage\n", port, rv);
return rv;
}
pp_printf("port %d t24p read from storage: %d ps\n", port,*value);
......
......@@ -31,9 +31,9 @@
#define SDB_DEV_INIT 0x77722d69 /* wr-i (nit) */
#define SDB_DEV_MAC 0x6d61632d /* mac- (address) */
#define SDB_DEV_SFP 0x7366702d /* sfp- (database) */
#define SDB_DEV_DP_SFP 0x8366702d /* sfp1- (database) */
#define SDB_DEV_DP_SFP 0x64702d73 /* dp-s (fp-database) */
#define SDB_DEV_CALIB 0x63616c69 /* cali (bration) */
#define SDB_DEV_DP_CALIB 0x73616c69 /* cali (bration) */
#define SDB_DEV_DP_CALIB 0x64702d63 /* dp-c (alibration) */
/* constants for scanning I2C EEPROMs */
#define EEPROM_START_ADR 0
......@@ -178,7 +178,7 @@ static void storage_sdb_list(struct sdbfs *fs)
while ((d = sdbfs_scan(fs, new)) != NULL) {
d->sdb_component.product.record_type = '\0';
pp_printf("file 0x%08x @ %4i, name %s\n",
pp_printf("file 0x%08x @ %4x, name %s\n",
(int)(d->sdb_component.product.device_id),
(int)(d->sdb_component.addr_first),
(char *)(d->sdb_component.product.name));
......
......@@ -89,7 +89,7 @@ extern struct storage_config storage_cfg;
#define MEM_FLASH 0
#define MEM_EEPROM 1
#define MEM_1W_EEPROM 2
#define SDBFS_REC 5
#define SDBFS_REC 7
int storage_read_hdl_cfg(void);
......
......@@ -35,7 +35,7 @@
static int cmd_sfp(const char *args[])
{
int8_t sfpcount[2] = {1,1};
int8_t i, temp, ret[2];
int8_t i, temp, ret[2]={0,0};
int port;
struct s_sfpinfo sfp;
......@@ -45,11 +45,19 @@ static int cmd_sfp(const char *args[])
return -EINVAL;
}
if (!strcasecmp(args[0], "erase")) {
if (storage_sfpdb_erase(0) == EE_RET_I2CERR) {
pp_printf("Could not erase DB\n");
return -EIO;
if (args[1])
port = atoi(args[1]);
else
port = 0;
if (port > 1) return -EINVAL;
if (storage_sfpdb_erase(port) == EE_RET_I2CERR) {
pp_printf("Port %d Could not erase DB\n", port);
ret[port] = -EIO;
}
return 0;
return (ret[0]||ret[1]);
} else if (args[4] && !strcasecmp(args[0], "add")) {
temp = strnlen(args[1], SFP_PN_LEN);
for (i = 0; i < temp; ++i)
......@@ -64,6 +72,9 @@ static int cmd_sfp(const char *args[])
sfp.port = atoi(args[5]);
else
sfp.port = 0;
if (sfp.port > 1) return -EINVAL;
temp = storage_get_sfp(&sfp, SFP_ADD, 0, sfp.port);
if (temp == EE_RET_DBFULL) {
pp_printf("SFP DB is full\n");
......@@ -119,6 +130,9 @@ static int cmd_sfp(const char *args[])
sfp.port = atoi(args[2]);
else
sfp.port = 0;
if (sfp.port > 1) return -EINVAL;
ep_sfp_enable(atoi(args[1]), sfp.port);
return 0;
} else {
......
EB ?= no
SDBFS ?= ../../fpga-config-space/sdbfs/userspace/
SDBFS ?= ./sdbfs-tools
CFLAGS = -Wall -ggdb -I../include -I../liblinux -I../liblinux/extest
CFLAGS += -D__GIT_VER__="\"$(GIT_VER)\"" -D__GIT_USR__="\"$(GIT_USR)\""
LDFLAGS = -lutil -L../liblinux -ldevmap -L../liblinux/extest -lextest
LDFLAGS += -lreadline
ALL = genraminit genramvhd genrammif
ALL = genraminit genramvhd genrammif gensdbfs
ALL += wrpc-w1-read wrpc-w1-write
ALL += pfilter-builder
ALL += wrpc-dump mapper
......@@ -18,6 +18,8 @@ ALL += eb-w1-write
endif
ifneq ($(SDBFS),no)
ALL += sdb-wrpc.bin
ALL += sdb-wrpc-flash.bin
ALL += sdb-wrpc-standalone.bin
endif
AS = as
......@@ -47,6 +49,12 @@ eb-w1-write: eb-w1-write.c ../dev/w1.c ../dev/w1-eeprom.c eb-w1.c
sdb-wrpc.bin: sdbfs
$(SDBFS)/gensdbfs $< $@
sdb-wrpc-flash.bin: sdbfs-flash
$(SDBFS)/gensdbfs -b 65536 $< $@
sdb-wrpc-standalone.bin : sdb-wrpc-flash.bin
cut -b 3014657- $< > $@
wrpc-dump: wrpc-dump.c dump-info-host.o
$(CC) $(CFLAGS) -I../ppsi/include -I../ppsi/arch-wrpc/include -I.. \
-I ../softpll \
......@@ -73,6 +81,10 @@ dump-info-host.o: ../dump-info.o
../dump-info.o: ../dump-info.c
$(MAKE) -C .. dump-info.o
gensdbfs:
$(MAKE) -C $(SDBFS)
clean:
rm -f $(ALL) *.o *~ dump-info-host.bin
No preview for this file type
......@@ -3,7 +3,7 @@
# FLASH. That is why our default position for various parameters is right after
# the bitstream.
.
position = 1507328
position = 0x2e0000
# Allocation granularity is 64 bytes
# We start with bitstream file at position 0, later the same set of files as for
......@@ -12,7 +12,7 @@
bitstream
write = 1
position = 0
maxsize = 1507328
maxsize = 0x2e0000
mac-address
write = 1
......@@ -22,11 +22,19 @@ wr-init
write = 1
maxsize = 256
# each sfp takes 29 bytes, 4 of them fit in 128 bytes
# each sfp takes 29 bytes, 8 of them fit in 256 bytes
sfp-database
write = 1
maxsize = 128
maxsize = 256
calibration
write = 1
maxsize = 128
dp-sfp-database
write = 1
maxsize = 256
dp-calibration
write = 1
maxsize = 128
\ No newline at end of file
gensdbfs
sdb-read
sdb-extract
AS = as
LD = ld
CC = gcc
CPP = $(CC) -E
AR = ar
NM = nm
STRIP = strip
OBJCOPY = objcopy
OBJDUMP = objdump
CFLAGS = -Wall -ggdb
CFLAGS += -I ./lib -I ./include
LDFLAGS = -L ./lib -lsdbfs
PROG = gensdbfs sdb-read sdb-extract
all: $(PROG)
%: %.c
$(CC) $(CFLAGS) -o $@ $*.c $(LDFLAGS)
$(PROG): ./lib/libsdbfs.a
./lib/libsdbfs.a:
$(MAKE) -C ./lib
clean:
rm -f $(PROG) *.o *~ core
$(MAKE) -C ./lib clean
# add the other unused targets, so the rule in ../Makefile works
modules install modules_install:
This diff is collapsed.
#ifndef __GENSDBFS_H__
#define __GENSDBFS_H__
#include <stdint.h>
#define CFG_NAME "--SDB-CONFIG--"
#define DEFAULT_VENDOR htonll(0x46696c6544617461LL) /* "FileData" */
/* We need to keep track of each file as both unix and sdb entity*/
struct sdbf {
struct stat stbuf;
struct dirent de;
union {
struct sdb_device s_d;
struct sdb_interconnect s_i;
struct sdb_bridge s_b;
};
char *fullname;
char *basename;
unsigned long ustart, rstart; /* user (mandated), relative */
unsigned long base, size; /* base is absolute, for output */
int nfiles, totsize; /* for dirs */
struct sdbf *dot; /* for files, pointer to owning dir */
struct sdbf *parent; /* for dirs, current dir in ../ */
struct sdbf *subdir; /* for files that are dirs */
int level; /* subdir level */
int userpos; /* only allowed at level 0 */
};
static inline uint64_t htonll(uint64_t ll)
{
uint64_t res;
if (htonl(1) == 1)
return ll;
res = htonl(ll >> 32);
res |= (uint64_t)(htonl((uint32_t)ll)) << 32;
return res;
}
#define ntohll htonll
#endif /* __GENSDBFS_H__ */
/*
* This is the official version 1.1 of sdb.h
*/
#ifndef __SDB_H__
#define __SDB_H__
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
/*
* All structures are 64 bytes long and are expected
* to live in an array, one for each interconnect.
* Most fields of the structures are shared among the
* various types, and most-specific fields are at the
* beginning (for alignment reasons, and to keep the
* magic number at the head of the interconnect record
*/
/* Product, 40 bytes at offset 24, 8-byte aligned
*
* device_id is vendor-assigned; version is device-specific,
* date is hex (e.g 0x20120501), name is UTF-8, blank-filled
* and not terminated with a 0 byte.
*/
struct sdb_product {
uint64_t vendor_id; /* 0x18..0x1f */
uint32_t device_id; /* 0x20..0x23 */
uint32_t version; /* 0x24..0x27 */
uint32_t date; /* 0x28..0x2b */
uint8_t name[19]; /* 0x2c..0x3e */
uint8_t record_type; /* 0x3f */
};
/*
* Component, 56 bytes at offset 8, 8-byte aligned
*
* The address range is first to last, inclusive
* (for example 0x100000 - 0x10ffff)
*/
struct sdb_component {
uint64_t addr_first; /* 0x08..0x0f */
uint64_t addr_last; /* 0x10..0x17 */
struct sdb_product product; /* 0x18..0x3f */
};
/* Type of the SDB record */
enum sdb_record_type {
sdb_type_interconnect = 0x00,
sdb_type_device = 0x01,
sdb_type_bridge = 0x02,
sdb_type_integration = 0x80,
sdb_type_repo_url = 0x81,
sdb_type_synthesis = 0x82,
sdb_type_empty = 0xFF,
};
/* Type 0: interconnect (first of the array)
*
* sdb_records is the length of the table including this first
* record, version is 1. The bus type is enumerated later.
*/
#define SDB_MAGIC 0x5344422d /* "SDB-" */
struct sdb_interconnect {
uint32_t sdb_magic; /* 0x00-0x03 */
uint16_t sdb_records; /* 0x04-0x05 */
uint8_t sdb_version; /* 0x06 */
uint8_t sdb_bus_type; /* 0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 1: device
*
* class is 0 for "custom device", other values are
* to be standardized; ABI version is for the driver,
* bus-specific bits are defined by each bus (see below)
*/
struct sdb_device {
uint16_t abi_class; /* 0x00-0x01 */
uint8_t abi_ver_major; /* 0x02 */
uint8_t abi_ver_minor; /* 0x03 */
uint32_t bus_specific; /* 0x04-0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 2: bridge
*
* child is the address of the nested SDB table
*/
struct sdb_bridge {
uint64_t sdb_child; /* 0x00-0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 0x80: integration
*
* all types with bit 7 set are meta-information, so
* software can ignore the types it doesn't know. Here we
* just provide product information for an aggregate device
*/
struct sdb_integration {
uint8_t reserved[24]; /* 0x00-0x17 */
struct sdb_product product; /* 0x08-0x3f */
};
/* Type 0x81: Top module repository url
*
* again, an informative field that software can ignore
*/
struct sdb_repo_url {
uint8_t repo_url[63]; /* 0x00-0x3e */
uint8_t record_type; /* 0x3f */
};
/* Type 0x82: Synthesis tool information
*
* this informative record
*/
struct sdb_synthesis {
uint8_t syn_name[16]; /* 0x00-0x0f */
uint8_t commit_id[16]; /* 0x10-0x1f */
uint8_t tool_name[8]; /* 0x20-0x27 */
uint32_t tool_version; /* 0x28-0x2b */
uint32_t date; /* 0x2c-0x2f */
uint8_t user_name[15]; /* 0x30-0x3e */
uint8_t record_type; /* 0x3f */
};
/* Type 0xff: empty
*
* this allows keeping empty slots during development,
* so they can be filled later with minimal efforts and
* no misleading description is ever shipped -- hopefully.
* It can also be used to pad a table to a desired length.
*/
struct sdb_empty {
uint8_t reserved[63]; /* 0x00-0x3e */
uint8_t record_type; /* 0x3f */
};
/* The type of bus, for bus-specific flags */
enum sdb_bus_type {
sdb_wishbone = 0x00,
sdb_data = 0x01,
};
#define SDB_WB_WIDTH_MASK 0x0f
#define SDB_WB_ACCESS8 0x01
#define SDB_WB_ACCESS16 0x02
#define SDB_WB_ACCESS32 0x04
#define SDB_WB_ACCESS64 0x08
#define SDB_WB_LITTLE_ENDIAN 0x80
#define SDB_DATA_READ 0x04
#define SDB_DATA_WRITE 0x02
#define SDB_DATA_EXEC 0x01
#endif /* __SDB_H__ */
LINUX ?= /lib/modules/$(shell uname -r)/build
# If we compile for the kernel, we need to include real kernel headers.
# The thing is enough a mess that I moved it to a different file
include Makefile.arch
AS = as
LD = ld
CC = gcc
CPP = $(CC) -E
AR = ar
NM = nm
STRIP = strip
OBJCOPY = objcopy
OBJDUMP = objdump
# calculate endianness at compile time
ENDIAN := $(shell ./check-endian $(CC))
CFLAGS = -Wall -ggdb -O2
CFLAGS += -I../include/linux -I../include # for <sdb.h>
CFLAGS += -ffunction-sections -fdata-sections
CFLAGS += -Wno-pointer-sign
CFLAGS += $(ENDIAN) $(LINUXINCLUDE)
LIB = libsdbfs.a
OBJS = glue.o access.o
all: $(LIB)
$(OBJS): $(wildcard *.h)
$(LIB): $(OBJS)
$(AR) r $@ $(OBJS)
clean:
rm -f $(OBJS) $(LIB) *~ core
# add the other unused targets, so the rule in ../Makefile works
modules install modules_install:
srctree = $(LINUX)
#
# This set of contortions comes from the kernel Makefile. We need this
# in order to properly compile libsdbfs for the kernel without being
# in a kernel build environment (for example, to check for compile errors).
#
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
-e s/arm.*/arm/ -e s/sa110/arm/ \
-e s/s390x/s390/ -e s/parisc64/parisc/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ )
SRCARCH := $(ARCH)
# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
SRCARCH := x86
endif
ifeq ($(ARCH),x86_64)
SRCARCH := x86
endif
# Additional ARCH settings for sparc
ifeq ($(ARCH),sparc32)
SRCARCH := sparc
endif
ifeq ($(ARCH),sparc64)
SRCARCH := sparc
endif
# Additional ARCH settings for sh
ifeq ($(ARCH),sh64)
SRCARCH := sh
endif
# Additional ARCH settings for tile
ifeq ($(ARCH),tilepro)
SRCARCH := tile
endif
ifeq ($(ARCH),tilegx)
SRCARCH := tile
endif
# Where to locate arch specific headers
hdr-arch := $(SRCARCH)
ifeq ($(ARCH),m68knommu)
hdr-arch := m68k
endif
# Use LINUXINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \
-Iarch/$(hdr-arch)/include/generated \
-I$(srctree)/include
/*
* Copyright (C) 2012,2013 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
/* To avoid many #ifdef and associated mess, all headers are included there */
#include "libsdbfs.h"
int sdbfs_fstat(struct sdbfs *fs, struct sdb_device *record_return)
{
if (!fs->currentp)
return -ENOENT;
memcpy(record_return, fs->currentp, sizeof(*record_return));
return 0;
}
int sdbfs_fread(struct sdbfs *fs, int offset, void *buf, int count)
{
int ret;
if (!fs->currentp)
return -ENOENT;
if (offset < 0)
offset = fs->read_offset;
if (offset + count > fs->f_len)
count = fs->f_len - offset;
ret = count;
if (fs->data)
memcpy(buf, fs->data + fs->f_offset + offset, count);
else
ret = fs->read(fs, fs->f_offset + offset, buf, count);
if (ret > 0)
fs->read_offset = offset + ret;
return ret;
}
int sdbfs_fwrite(struct sdbfs *fs, int offset, void *buf, int count)
{
int ret;
if (!fs->currentp)
return -ENOENT;
if (offset < 0)
offset = fs->read_offset;
if (offset + count > fs->f_len)
count = fs->f_len - offset;
ret = count;
if (fs->data)
memcpy(buf, fs->data + fs->f_offset + offset, count);
else
ret = fs->write(fs, fs->f_offset + offset, buf, count);
if (ret > 0)
fs->read_offset = offset + ret;
return ret;
}
#!/bin/bash
# Check endianness at compile time, so we can pass the -D to CFLAGS
CC=$1
if [ "x$CC" == "x" ]; then
echo "$0: pass the compiler path (\$CC) as argument" >& 2
exit 1
fi
# Check endianness, by making an object file
TMPC=$(mktemp /tmp/endian-c-XXXXXX)
TMPO=$(mktemp /tmp/endian-o-XXXXXX)
echo "int i = 0xbbee;" > $TMPC
$CC -x c -c $TMPC -o $TMPO
OBJCOPY=$(echo $CC | sed 's/gcc$/objcopy/')
if $OBJCOPY -O binary $TMPO /dev/stdout | od -t x1 -An | \
grep -q 'bb ee'; then
echo " -DSDBFS_BIG_ENDIAN"
else
echo " -DSDBFS_LITTLE_ENDIAN"
fi
rm -f $TMPC $TMPO
/*
* Copyright (C) 2012,2014 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
/* To avoid many #ifdef and associated mess, all headers are included there */
#include "libsdbfs.h"
static struct sdbfs *sdbfs_list;
/* All fields unused by the caller are expected to be zeroed */
int sdbfs_dev_create(struct sdbfs *fs)
{
unsigned int magic;
/* First, check we have the magic */
if (fs->data || (fs->flags & SDBFS_F_ZEROBASED))
magic = *(unsigned int *)(fs->data + fs->entrypoint);
else
fs->read(fs, fs->entrypoint, &magic, sizeof(magic));
if (magic == SDB_MAGIC) {
/* Uh! If we are little-endian, we must convert */
if (ntohl(1) != 1)
fs->flags |= SDBFS_F_CONVERT32;
} else if (htonl(magic) == SDB_MAGIC) {
/* ok, don't convert */
} else {
return -ENOTDIR;
}
fs->next = sdbfs_list;
sdbfs_list = fs;
return 0;
}
int sdbfs_dev_destroy(struct sdbfs *fs)
{
struct sdbfs **p;
for (p = &sdbfs_list; *p && *p != fs; p = &(*p)->next)
;
if (!*p)
return -ENOENT;
*p = fs->next;
return 0;
}
struct sdbfs *sdbfs_dev_find(const char *name)
{
struct sdbfs *l;
for (l = sdbfs_list; l && strcmp(l->name, name); l = l->next)
;
if (!l)
return NULL;
return l;
}
/*
* To open by name or by ID we need to scan the tree. The scan
* function is also exported in order for "sdb-ls" to use it
*/
static struct sdb_device *sdbfs_readentry(struct sdbfs *fs,
unsigned long offset)
{
/*
* This function reads an entry from a known good offset. It
* returns the pointer to the entry, which may be stored in
* the fs structure itself. Only touches fs->current_record.
*/
if (fs->data || (fs->flags & SDBFS_F_ZEROBASED)) {
if (!(fs->flags & SDBFS_F_CONVERT32))
return (struct sdb_device *)(fs->data + offset);
/* copy to local storage for conversion */
memcpy(&fs->current_record, fs->data + offset,
sizeof(fs->current_record));
} else {
if (!fs->read)
return NULL;
fs->read(fs, offset, &fs->current_record,
sizeof(fs->current_record));
}
if (fs->flags & SDBFS_F_CONVERT32) {
uint32_t *p = (void *)&fs->current_record;
int i;
for (i = 0; i < sizeof(fs->current_record) / sizeof(*p); i++)
p[i] = ntohl(p[i]);
}
return &fs->current_record;
}
/* Helper for scanning: we enter a new directory, and we must validate */
static struct sdb_device *scan_newdir(struct sdbfs *fs, int depth)
{
struct sdb_device *dev;
struct sdb_interconnect *intercon;
dev = fs->currentp = sdbfs_readentry(fs, fs->this[depth]);
if (dev->sdb_component.product.record_type != sdb_type_interconnect)
return NULL;
intercon = (typeof(intercon))dev;
if (ntohl(intercon->sdb_magic) != SDB_MAGIC)
return NULL;
fs->nleft[depth] = ntohs(intercon->sdb_records) - 1;
fs->this[depth] += sizeof(*intercon);
fs->depth = depth;
return dev;
}
struct sdb_device *sdbfs_scan(struct sdbfs *fs, int newscan)
{
/*
* This returns a pointer to the next sdb record, or the first one.
* Subdirectories (bridges) are returned before their contents.
* It only uses internal fields.
*/
struct sdb_device *dev;
struct sdb_bridge *bridge;
int depth, type, newdir = 0; /* check there's the magic */
if (newscan) {
fs->base[0] = 0;
fs->this[0] = fs->entrypoint;
depth = fs->depth = 0;
newdir = 1;
goto scan;
}
/* If we already returned a bridge, go inside it (check type) */
depth = fs->depth;
type = fs->currentp->sdb_component.product.record_type;
if (type == sdb_type_bridge && depth + 1 < SDBFS_DEPTH) {
bridge = (typeof(bridge))fs->currentp;
fs->this[depth + 1] = fs->base[depth]
+ ntohll(bridge->sdb_child);
fs->base[depth + 1] = fs->base[depth]
+ ntohll(bridge->sdb_component.addr_first);
depth++;
newdir++;
}
scan:
/* If entering a new directory, verify magic and set nleft */
if (newdir) {
dev = scan_newdir(fs, depth);
if (dev)
goto out;
/* Otherwise the directory is not there: no intercon */
if (!depth)
return NULL; /* no entries at all */
depth--;
}
while (fs->nleft[depth] == 0) {
/* No more at this level, "cd .." if possible */
if (!depth)
return NULL;
fs->depth = --depth;
}
/* so, read the next entry */
dev = fs->currentp = sdbfs_readentry(fs, fs->this[depth]);
fs->this[depth] += sizeof(*dev);
fs->nleft[depth]--;
out:
fs->f_offset = fs->base[fs->depth]
+ htonll(fs->currentp->sdb_component.addr_first);
return dev;
}
static void __open(struct sdbfs *fs)
{
fs->f_offset = fs->base[fs->depth]
+ htonll(fs->currentp->sdb_component.addr_first);
fs->f_len = htonll(fs->currentp->sdb_component.addr_last)
+ 1 - htonll(fs->currentp->sdb_component.addr_first);
fs->read_offset = 0;
}
int sdbfs_open_name(struct sdbfs *fs, const char *name)
{
struct sdb_device *d;
int len = strlen(name);
if (len > 19)
return -ENOENT;
sdbfs_scan(fs, 1); /* new scan: get the interconnect and igore it */
while ( (d = sdbfs_scan(fs, 0)) != NULL) {
if (strncmp(name, d->sdb_component.product.name, len))
continue;
if (len < 19 && d->sdb_component.product.name[len] != ' ')
continue;
fs->currentp = d;
__open(fs);
return 0;
}
return -ENOENT;
}
int sdbfs_open_id(struct sdbfs *fs, uint64_t vid, uint32_t did)
{
struct sdb_device *d;
sdbfs_scan(fs, 1); /* new scan: get the interconnect and igore it */
while ( (d = sdbfs_scan(fs, 0)) != NULL) {
if (vid != d->sdb_component.product.vendor_id)
continue;
if (did != d->sdb_component.product.device_id)
continue;
fs->currentp = d;
__open(fs);
return 0;
}
return -ENOENT;
}
int sdbfs_close(struct sdbfs *fs)
{
fs->currentp = NULL;
return 0;
}
/* to "find" a device, open it, get the current offset, then close */
unsigned long sdbfs_find_name(struct sdbfs *fs, const char *name)
{
unsigned long offset;
int ret;
ret = sdbfs_open_name(fs, name);
if (ret < 0)
return (unsigned long)ret;
offset = fs->f_offset;
sdbfs_close(fs);
return offset;
}
unsigned long sdbfs_find_id(struct sdbfs *fs, uint64_t vid, uint32_t did)
{
unsigned long offset;
int ret;
ret = sdbfs_open_id(fs, vid, did);
if (ret < 0)
return (unsigned long)ret;
offset = fs->f_offset;
sdbfs_close(fs);
return offset;
}
/* Though freestanding, some minimal headers are expected to exist */
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#define SDB_KERNEL 0
#define SDB_USER 0
#define SDB_FREESTAND 1
#ifdef SDBFS_BIG_ENDIAN
# define ntohs(x) (x)
# define htons(x) (x)
# define ntohl(x) (x)
# define htonl(x) (x)
#else
# error "No support, yet, for little-endian freestanding library"
#endif
/*
* This supports both the Linux kernel and barebox, that is similar
* by design, and defines __KERNEL__ too.
*/
#ifdef __BAREBOX__
# include <errno.h>
#else /* really linux */
# include <linux/errno.h>
#endif
#include <linux/types.h>
#include <linux/string.h>
#include <asm/byteorder.h>
/*
* The default installed /usr/include/linux stuff misses the __KERNEL__ parts.
* For libsdbfs it means we won't get uint32_t and similar types.
*
* So, check if we got the information we need before strange errors happen.
* The DECLARE_BITMAP macro is in <linux/types.h> since the epoch, but it
* is not installed in /usr/include/linux/types.h, so use it to check.
*
* If building for barebox, we miss the macro, but we are sure that
* we are picking the correct header, because the library is only built
* within the barebox source tree.
*/
#if !defined(DECLARE_BITMAP) && !defined(__BAREBOX__)
# error "Please point LINUX to a source tree if you define __KERNEL__"
#endif
#define SDB_KERNEL 1
#define SDB_USER 0
#define SDB_FREESTAND 0
#define sdb_print(format, ...) printk(format, __VA_ARGS__)
#ifndef __LIBSDBFS_USER_H__
#define __LIBSDBFS_USER_H__
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h> /* htonl */
#define SDB_KERNEL 0
#define SDB_USER 1
#define SDB_FREESTAND 0
#define sdb_print(format, ...) fprintf(stderr, format, __VA_ARGS__)
#endif /* __LIBSDBFS_USER_H__ */
#ifndef __LIBSDBFS_H__
#define __LIBSDBFS_H__
/* The library can work in different environments, take care of them */
#ifdef __KERNEL__
# include "libsdbfs-kernel.h"
#elif defined(__unix__)
# include "libsdbfs-user.h"
#else
# include "libsdbfs-freestanding.h"
#endif
#include <sdb.h> /* Please point your "-I" to some sensible place */
#define SDBFS_DEPTH 4 /* Max number of subdirectory depth */
/*
* Data structures: please not that the library intself doesn't use
* malloc, so it's the caller who must deal withallocation/removal.
* For this reason we can have no opaque structures, but some fields
* are private
*/
struct sdbfs {
/* Some fields are informative */
char *name; /* may be null */
void *drvdata; /* driver may need some detail.. */
unsigned long blocksize;
unsigned long entrypoint;
unsigned long flags;
/* The "driver" must offer some methods */
void *data; /* Use this if directly mapped */
unsigned long datalen; /* Length of the above array */
int (*read)(struct sdbfs *fs, int offset, void *buf, int count);
int (*write)(struct sdbfs *fs, int offset, void *buf, int count);
int (*erase)(struct sdbfs *fs, int offset, int count);
/* The following fields are library-private */
struct sdb_device *currentp;
struct sdb_device current_record;
unsigned long f_len;
unsigned long f_offset; /* start of file */
unsigned long read_offset; /* current location */
struct sdbfs *next;
/* The following ones are directory-aware */
unsigned long base[SDBFS_DEPTH]; /* for relative addresses */
unsigned long this[SDBFS_DEPTH]; /* current sdb record */
int nleft[SDBFS_DEPTH];
int depth;
};
/* Some flags are set by the user, some (convert32) by the library */
#define SDBFS_F_VERBOSE 0x0001 /* not really used yet */
#define SDBFS_F_CONVERT32 0x0002 /* swap SDB words as they are read */
#define SDBFS_F_ZEROBASED 0x0004 /* zero is a valid data pointer */
/* Defined in glue.c */
int sdbfs_dev_create(struct sdbfs *fs);
int sdbfs_dev_destroy(struct sdbfs *fs);
struct sdbfs *sdbfs_dev_find(const char *name);
unsigned long sdbfs_find_name(struct sdbfs *fs, const char *name);
unsigned long sdbfs_find_id(struct sdbfs *fs, uint64_t vid, uint32_t did);
int sdbfs_open_name(struct sdbfs *fs, const char *name);
int sdbfs_open_id(struct sdbfs *fs, uint64_t vid, uint32_t did);
int sdbfs_close(struct sdbfs *fs);
struct sdb_device *sdbfs_scan(struct sdbfs *fs, int newscan);
/* Defined in access.c */
int sdbfs_fstat(struct sdbfs *fs, struct sdb_device *record_return);
int sdbfs_fread(struct sdbfs *fs, int offset, void *buf, int count);
int sdbfs_fwrite(struct sdbfs *fs, int offset, void *buf, int count);
/* This is needed to convert endianness. Hoping it is not defined elsewhere */
static inline uint64_t htonll(uint64_t ll)
{
uint64_t res;
if (htonl(1) == 1)
return ll;
res = htonl(ll >> 32);
res |= (uint64_t)(htonl((uint32_t)ll)) << 32;
return res;
}
static inline uint64_t ntohll(uint64_t ll)
{
return htonll(ll);
}
#endif /* __LIBSDBFS_H__ */
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "libsdbfs.h"
#define CFG_NAME "--SDB-CONFIG--"
/*
* This is similar to ./sdb-read, so some code duplication is there,
* but I'd better keep the tools separate and simple
*/
char *prgname;
static int opt_force, opt_entry;
static int create_file(struct sdbfs *fs, struct sdb_device *d, FILE *cfgf)
{
FILE *f;
struct sdb_product *p;
struct sdb_component *c;
char name[32];
int mode = 0444;
c = &d->sdb_component;
p = &c->product;
/* Remove trailing spaces from the name */
strncpy(name, (char *)p->name, sizeof(p->name));
name[sizeof(p->name)] = '\0';
while (name[strlen(name) - 1] == ' ')
name[strlen(name) - 1] = '\0';
/* Print cfgfile information */
fprintf(cfgf, "%s\n" "\tvendor = 0x%016llx\n" "\tdevice = 0x%08x\n",
name, ntohll(p->vendor_id), ntohl(p->device_id));
fprintf(cfgf, "\tposition = 0x%llx\n", ntohll(c->addr_first));
if (ntohl(d->bus_specific) & SDB_DATA_WRITE) {
fprintf(cfgf, "\twrite = 1\n");
mode |= 0222;
}
if (ntohl(d->bus_specific) & SDB_DATA_EXEC)
mode |= 0111;
fprintf(cfgf, "\n");
/* Create the actual file unless it is the root directory */
if (!strcmp(name, "."))
return 0;
f = fopen(name, "w");
if (!f) {
fprintf(stderr, "%s: open(%s): %s\n", prgname, name,
strerror(errno));
return -1;
}
fwrite(fs->data + ntohll(c->addr_first), 1,
ntohll(c->addr_last) + 1 - ntohll(c->addr_first), f);
fclose(f);
chmod(name, mode);
return 0;
}
/* As promised, here's the user-interface glue (and initialization, I admit) */
int main(int argc, char **argv)
{
int n, new, c, err, cfgfd;
FILE *f, *cfgf;
struct sdbfs _fs;
struct sdbfs *fs = &_fs; /* I like to type "fs->" */
struct sdb_device *d;
struct stat stbuf;
void *mapaddr;
char *fsname, *dirname;
struct dirent **namelist;
int i, pagesize = getpagesize();
prgname = argv[0];
while ( (c = getopt(argc, argv, "e:f")) != -1) {
switch (c) {
case 'f':
opt_force = 1;
break;
case 'e':
if (sscanf(optarg, "%i", &opt_entry) != 1) {
fprintf(stderr, "%s: not a number \"%s\"\n",
prgname, optarg);
exit(1);
}
}
}
if (optind != argc - 2) {
fprintf(stderr, "%s: Use: \"%s [-f] [-e <entry>] "
"<output-dir> <sdb-file>\n", prgname, prgname);
exit(1);
}
fsname = argv[optind + 1];
dirname = argv[optind];
if ( !(f = fopen(fsname, "r")) || fstat(fileno(f), &stbuf) < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, fsname,
strerror(errno));
exit(1);
}
stbuf.st_size += pagesize - 1;
stbuf.st_size &= ~(pagesize - 1);
mapaddr = mmap(0, stbuf.st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0);
if (mapaddr == MAP_FAILED) {
/* I used to complain, but sysfs doesn't allow mmapping... */
mapaddr = malloc(stbuf.st_size);
if (!mapaddr) {
fprintf(stderr, "%s: out of memory reading \"%s\"\n",
argv[0], fsname);
exit(1);
}
i = fread(mapaddr, 1, stbuf.st_size, f);
if (i < stbuf.st_size) {
fprintf(stderr, "%s: %s: short read\n",
argv[0], fsname);
exit(1);
}
}
/* Check output dir is empty, open config file */
/* Open the filesystem */
memset(fs, 0, sizeof(*fs));
fs->name = fsname; /* not mandatory */
fs->blocksize = 256; /* only used for writing, actually */
fs->entrypoint = opt_entry;
fs->data = mapaddr;
err = sdbfs_dev_create(fs);
if (err) {
fprintf(stderr, "%s: sdbfs_dev_create(): %s\n", prgname,
strerror(-err));
fprintf(stderr, "\t(wrong entry point 0x%08lx?)\n",
fs->entrypoint);
exit(1);
}
/* We are sure the fs is good: create output dir and cfgfile */
if (mkdir(dirname, 0777) < 0 && errno != EEXIST) {
fprintf(stderr, "%s: %s: %s\n", prgname, dirname,
strerror(errno));
exit(1);
}
if (chdir(dirname) < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, dirname,
strerror(errno));
exit(1);
}
n = scandir(".", &namelist, 0, 0);
if (!opt_force && n != 2) {
fprintf(stderr, "%s: %s: not empty\n", prgname, dirname);
exit(1);
}
cfgfd = open(CFG_NAME, O_RDWR | O_CREAT | O_EXCL, 0666);
if (cfgfd < 0) {
fprintf(stderr, "%s: Warning: %s/%s: %s\n", prgname, dirname,
CFG_NAME, strerror(errno));
cfgf = fopen("/dev/null", "w");
} else {
cfgf = fdopen(cfgfd, "w");
}
/* Save the header */
fprintf(cfgf, "# Configuration file generated by %s, reading %s\n\n",
prgname, fsname);
/* The root directory is a file like the other ones */
while ( (d = sdbfs_scan(fs, new)) != NULL) {
create_file(fs, d, cfgf);
new = 0;
}
sdbfs_dev_destroy(fs);
return 0;
}
/*
* Copyright (C) 2012,2014 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "libsdbfs.h"
char *prgname;
int opt_long, opt_verbose, opt_read, opt_entry, opt_mem;
unsigned long opt_memaddr, opt_memsize;
static void help(void)
{
fprintf(stderr, "%s: Use: \"%s [options] <image-file> [<file>]\n",
prgname, prgname);
fprintf(stderr, " -l long listing (like ls -l)\n");
fprintf(stderr, " -v verbose\n");
fprintf(stderr, " -r force use of read(2), not mmap(2)\n");
fprintf(stderr, " -e <num> entry point offset\n");
fprintf(stderr, " -m <size>@<addr> memory subset to use\n");
fprintf(stderr, " -m <addr>+<size> memory subset to use\n");
exit(1);
}
struct sdbr_drvdata {
void *mapaddr;
FILE *f;
unsigned long memaddr;
unsigned long memsize;
};
/*
* This read method is needed for non-mmappable files, or stuff that
* you can't know the size of (e.g., char devices). You can force use of
* read, to exercise the library procedures, using "-r"
*/
static int do_read(struct sdbfs *fs, int offset, void *buf, int count)
{
struct sdbr_drvdata *drvdata = fs->drvdata;
if (opt_verbose)
fprintf(stderr, "%s @ 0x%08x - size 0x%x (%i)\n", __func__,
offset, count, count);
if (drvdata->mapaddr) {
memcpy(buf, drvdata->mapaddr + offset, count);
return count;
}
/* not mmapped: seek and read */
if (fseek(drvdata->f, drvdata->memaddr + offset, SEEK_SET) < 0)
return -1;
return fread(buf, 1, count, drvdata->f);
}
/* Boring ascii representation of a device */
static int list_device(struct sdb_device *d, int depth, int base)
{
struct sdb_product *p;
struct sdb_component *c;
struct sdb_synthesis *s;
unsigned char *data;
static int warned;
char *warn;
int i, ret;
c = &d->sdb_component;
p = &c->product;
s = (void *)d;
if (!warned && opt_long) {
fprintf(stderr, "%s: listing format is to be defined\n",
prgname);
warned = 1;
}
/* Different sdb items are listed in different ways */
switch(p->record_type) {
/* The following items are components, and are listed as such */
case sdb_type_interconnect:
case sdb_type_device:
case sdb_type_bridge:
if (!opt_long) {
printf("%.19s\n", p->name);
return 0;
}
/* hack: show directory level looking at the internals */
printf("%016llx:%08x @ %08llx-%08llx ",
(long long)ntohll(p->vendor_id), ntohl(p->device_id),
(long long)base + ntohll(c->addr_first),
(long long)base + ntohll(c->addr_last));
for (i = 0; i < depth; i++)
printf(" ");
printf("%.19s\n", p->name);
return 0;
/* A product, but not a component (no address range) */
case sdb_type_integration:
if (!opt_long) {
printf("%.19s\n", p->name);
return 0;
}
printf("%016llx:%08x ",
(long long)ntohll(p->vendor_id), ntohl(p->device_id));
/* like above, show directory level */
for (i = 0; i < depth; i++)
printf(" ");
printf("%.19s\n", p->name);
return 0;
/* Just a string */
case sdb_type_repo_url:
if (opt_long)
printf("repo-url: %.63s\n",
((struct sdb_repo_url *)d)->repo_url);
return 0;
/* Some metadata */
case sdb_type_synthesis:
if (!opt_long)
return 0;
printf("synthesis-name: %.16s\n", s->syn_name);
printf(" commit-id: ");
for (i = 0; i < sizeof(s->commit_id); i++)
printf("%02x", s->commit_id[i]);
printf("\n");
/* Some of the following fields are sometimes empty */
if (s->tool_name[0] && s->tool_name[0] != ' ')
printf(" tool-name: %.8s\n", s->tool_name);
if (s->tool_version)
printf(" tool-version: 0x%08x\n",
ntohl(s->tool_version));
if (s->date)
printf(" build-date: %08x\n", ntohl(s->date));
if (s->user_name[0] && s->tool_name[0] != ' ')
printf(" build-user: %.15s\n", s->user_name);
return 0;
case sdb_type_empty:
return 0;
default:
break;
}
/* Unknown record type */
if (p->record_type & 0x80) {
warn = "Warning";
ret = 0;
} else {
warn = "Error";
ret = -1;
}
fprintf(stderr, "%s: unknown record type 0x%02x\n", warn,
p->record_type);
if (!opt_long) {
printf("Unknown-record\n");
return ret;
}
/* long listing of unknown record */
printf("Unknown-record:\n");
data = (void *)d;
for (i = 0; i < sizeof(struct sdb_empty); i++)
printf("%s%02x%c",
(i & 0xf) == 0 ? " " : "",
data[i],
(i & 0xf) == 0xf ? '\n' : ' ');
return ret;
}
/* The following three function perform the real work, main() is just glue */
static int do_list(struct sdbfs *fs)
{
struct sdb_device *d;
int new = 1;
int err = 0;
while ( (d = sdbfs_scan(fs, new)) != NULL) {
err += list_device(d, fs->depth, fs->base[fs->depth]);
new = 0;
}
return err;
}
static int do_cat_name(struct sdbfs *fs, char *name)
{
char buf[4096];
int i;
i = sdbfs_open_name(fs, name);
if (i < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, name, strerror(-i));
exit(1);
}
while ( (i = sdbfs_fread(fs, -1, buf, sizeof(buf))) > 0)
fwrite(buf, 1, i, stdout);
sdbfs_close(fs);
return 0;
}
static int do_cat_id(struct sdbfs *fs, uint64_t vendor, uint32_t dev)
{
char buf[4096];
int i;
i = sdbfs_open_id(fs, htonll(vendor), htonl(dev));
if (i < 0) {
fprintf(stderr, "%s: %016llx-%08x: %s\n", prgname,
(long long)vendor, dev, strerror(-i));
exit(1);
}
while ( (i = sdbfs_fread(fs, -1, buf, sizeof(buf))) > 0)
fwrite(buf, 1, i, stdout);
sdbfs_close(fs);
return 0;
}
/* As promised, here's the user-interface glue (and initialization, I admit) */
int main(int argc, char **argv)
{
int c, err;
FILE *f;
struct sdbfs _fs;
struct sdbfs *fs = &_fs; /* I like to type "fs->" */
struct stat stbuf;
struct sdbr_drvdata *drvdata;
void *mapaddr;
char *fsname;
char *filearg = NULL;
unsigned long int32;
unsigned long long int64;
int pagesize = getpagesize();
prgname = argv[0];
while ( (c = getopt(argc, argv, "lvre:m:")) != -1) {
switch (c) {
case 'l':
opt_long = 1;
break;
case 'v':
opt_verbose = 1;
break;
case 'r':
opt_read = 1;
break;
case 'e':
if (sscanf(optarg, "%i", &opt_entry) != 1) {
fprintf(stderr, "%s: not a number \"%s\"\n",
prgname, optarg);
exit(1);
}
break;
case 'm':
/* memory: "size@addr", "addr+size" (blanks ok) */
if (sscanf(optarg, "%li @ %li", &opt_memsize,
&opt_memaddr) == 2)
break;
if (sscanf(optarg, "%li + %li", &opt_memaddr,
&opt_memsize) == 2)
break;
fprintf(stderr, "%s: \"%s\" must be <size>@<addr> "
"or <addr>+<size>\n", prgname, optarg);
exit(1);
break;
}
}
if (optind < argc - 2 || optind > argc - 1)
help();
fsname = argv[optind];
if (optind + 1 < argc)
filearg = argv[optind + 1];
if ( !(f = fopen(fsname, "r")) || fstat(fileno(f), &stbuf) < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, fsname,
strerror(errno));
exit(1);
}
stbuf.st_size += pagesize - 1;
stbuf.st_size &= ~(pagesize - 1);
mapaddr = mmap(0,
opt_memsize ? opt_memsize : stbuf.st_size,
PROT_READ, MAP_PRIVATE, fileno(f),
opt_memaddr /* 0 by default */);
if (mapaddr == MAP_FAILED)
mapaddr = NULL; /* We'll seek/read */
/* So, describe the filesystem instance and give it to the library */
memset(fs, 0, sizeof(*fs));
drvdata = calloc(1, sizeof(*drvdata));
if (!drvdata) {perror("malloc"); exit(1);}
drvdata->f = f;
drvdata->memaddr = opt_memaddr;
drvdata->memsize = opt_memsize;
drvdata->mapaddr = mapaddr;
fs->drvdata = drvdata;
fs->name = fsname; /* not mandatory */
fs->blocksize = 256; /* only used for writing, actually */
fs->entrypoint = opt_entry;
if (opt_read || !drvdata->mapaddr)
fs->read = do_read;
else
fs->data = mapaddr;
if (opt_verbose)
fs->flags |= SDBFS_F_VERBOSE;
err = sdbfs_dev_create(fs);
if (err) {
fprintf(stderr, "%s: sdbfs_dev_create(): %s\n", prgname,
strerror(-err));
fprintf(stderr, "\t(wrong entry point 0x%08lx?)\n",
fs->entrypoint);
exit(1);
}
/* Now use the thing: either scan, or look for name, or look for id */
if (!filearg)
err = do_list(fs);
else if (sscanf(filearg, "%llx:%lx", &int64, &int32) != 2)
err = do_cat_name(fs, filearg);
else
err = do_cat_id(fs, int64, int32);
sdbfs_dev_destroy(fs);
return err;
}
......@@ -28,3 +28,11 @@ sfp-database
calibration
write = 1
maxsize = 128
dp-sfp-database
write = 1
maxsize = 128
dp-calibration
write = 1
maxsize = 128
\ No newline at end of file
......@@ -107,6 +107,7 @@ static void wrc_initialize(void)
mac_addr[1][4], mac_addr[1][5]);
net_rst();
//Duplicate the configuration for both ports.
for (port=0; port<wr_num_ports;port++)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment