diff mbox

RFC: rtl8139 improvements

Message ID alpine.LFD.2.02.1203050722230.12164@bbs.intern
State New
Headers show

Commit Message

Gerhard Wiesinger March 5, 2012, 6:48 a.m. UTC
Hello,

I'm trying to implement better emulation and wider OS support for the 
rtl8139 card. Therefore I want to see the following testcases to be successful:

  * Testcases and successful regression tests:
  * 1.) DOS RSET8139.EXE: EEPROM Test successful
  * 2.) DOS RSET8139.EXE: Local loopback Test (Run Diagnostics On Board)
  * 3.) DOS RSET8139.EXE: Remote loopback Test as Initiator (Run Diagnostics On Network)
  * 4.) DOS RSET8139.EXE: Remote loopback Test as Responder (Run Diagnostics On Network)
  * 5.) DOS driver: Loads and works
  * 6.) Linux tests
  * 7.) Windows tests

I fixed already a major bug in DMA handling and (real hardware doesn't 
reset DMA register to 0 on reset condition as DOS driver crashes OS, see 
patch for details) and improved EEPROM handling and checksumming as well 
as unimplemented register handling (As Jason did partially in latest 
patch).

But finally testcases 1-4 aren't successful, testcase 5 (DOS driver and 
MS SMB client) works but I think there are still problems, see below.

Details:
Ad 1.) EERPOM Test: I also copied a full EEPROM from real hardware but 
still no success. According to the logs everything is read correctly. Also 
verified checksumming to real hardware . Any ideas?
(Attached rtl8139-diag.c will also help to diagnose)

Ad 2.) Local Loopback Test: One packet succeeds, other fail. Any ideas 
what might be wrong?

Ad 5.) DOS driver loads and also works but I think there is still a 
strange thing in packet receiving and possible sending (e.g. DHCP request 
is done twice). I also did some change in packet handling. See patch.

To get this to work I'm a little bit lost now and I need your help and 
comments and suggestions.

Can someone also make tests under DOS with RSET8139.EXE?
I think it is a good testing program how good our emulation is.

RSET8139.EXE can be found at:
http://www.realtek.com.tw/downloads/downloadsView.aspx?Langid=1&PNid=14&PFid=6&Level=5&Conn=4&DownTypeID=3&GetDown=false
Look for DOS Diagnostic program (RSET8139).

Patch is attached as diff and not indented to be used as regular patch 
since not ready for commit.

Thnx.

Ciao,
Gerhard

--
http://www.wiesinger.com/

/* rtl8139-diag.c: Diagnostics/EEPROM setup for RealTek RTL8129/8139 chips.

   This is a diagnostic and EEPROM setup program for Ethernet adapters
   based on the RealTek RTL8129 and RTL8139 chips.

	Copyright 1998-2000 by Donald Becker.
	This software may be used and distributed according to the terms of
	the GNU General Public License (GPL), incorporated herein by reference.
	Contact the author for use under other terms.

	This program must be compiled with "-O"!
	See the bottom of this file for the suggested compile-command.

	The author may be reached as becker@scyld.com, or C/O
	 Scyld Computing Corporation
	 410 Severn Ave., Suite 210
	 Annapolis MD 21403

	References
	  http://www.scyld.com/diag/index.html
	  http://www.realtek.com.tw/cn/cn.html
	  http://scyld.com/expert/mii-status.html
	  http://scyld.com/expert/NWay.html
*/

/* modified Oct 4, 2000 by Laurent Itti: add implementation for 
   hwaddr setting */

static char *version_msg =
"rtl8139-diag.c:v2.00 4/19/2000 Donald Becker (becker@scyld.com)\n"
" http://www.scyld.com/diag/index.html\n";
static char *usage_msg =
"Usage: rtl8139-diag [-aEefFmqrRtsvVwW] [-p <IOport>] [-[AF] <media>]\n";

#if ! defined(__OPTIMIZE__)
#warning  You must compile this program with the correct options!
#warning  See the last lines of the source file.
#error You must compile this driver with "-O".
#endif
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <errno.h>

#if defined(__linux__)  &&  __GNU_LIBRARY__ == 1
#include <asm/io.h>			/* Newer libraries use <sys/io.h> instead. */
#else
#include <sys/io.h>
/* Use   extern iopl(int level);  if your glibc does not define it. */
#endif

/* No libmii.h or libflash.h yet. */
extern int show_mii_details(long ioaddr, int phy_id);
extern int monitor_mii(long ioaddr, int phy_id);

extern int flash_show(long addr_ioaddr, long data_ioaddr);
extern int flash_dump(long addr_ioaddr, long data_ioaddr, char *filename);
extern int flash_program(long addr_ioaddr, long data_ioaddr, char *filename);
extern int (*flash_in_hook)(long addr, int offset);
extern void (*flash_out_hook)(long addr, int offset, int val);

/* We should use __u8 .. __u32, but they are not always defined. */
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;

struct option longopts[] = {
 /* { name  has_arg  *flag  val } */
	{"Advertise", 1, 0, 'A'},
	{"base-address", 1, 0, 'p'},
	{"show_all_registers",	0, 0, 'a'},	/* Print all registers. */
	{"help",	0, 0, 'h'},	/* Give help */
	{"show-eeprom",  0, 0, 'e'}, /* Dump EEPROM contents (-ee valid). */
	{"emergency-rewrite",  0, 0, 'E'}, /* Re-write a corrupted EEPROM.  */
	{"force-detection",  0, 0, 'f'},
	{"new-interface",  1, 0, 'F'},	/* New interface (built-in, AUI, etc.) */
	{"new-hwaddr",  1, 0, 'H'},	/* Set a new hardware address. */
	{"show-mii",  0, 0, 'm'},	/* Dump MII management registers. */
	{"port-base",  1, 0, 'p'},	/* Use the specified I/O address. */
	{"quiet",	0, 0, 'q'},		/* Decrease verbosity */
	{"reset",	0, 0, 'R'},		/* Reset the transceiver. */
	{"chip-type",  1, 0, 't'},	/* Assume the specified chip type index. */
	{"test",	0, 0, 'T'},		/* Do register and SRAM test. */
	{"verbose",	0, 0, 'v'},		/* Verbose mode */
	{"version", 0, 0, 'V'},		/* Display version number */
	{"write-EEPROM", 1, 0, 'w'},/* Actually write the EEPROM with new vals */
	{ 0, 0, 0, 0 }
};

/* The diagnostic functions supported. */
extern int   rtl81x9_diag(int vend_id, int dev_id, long ioaddr, int part_idx);
enum realtek_flags { HasMII=1, };

/* The table of known chips.
   Because of the bogus /proc/pci interface we must have both the exact
   name and a PCI vendor/device IDs.
   This table is searched in order: place specific entries followed by
   'catch-all' general entries. */
struct pcidev_entry {
	char *proc_pci_name;
	char *part_name;
	int vendor, device, device_mask;
	int flags;
	int io_size;
	int (*diag_func)(int vendor_id, int device_id, long ioaddr, int part_num);
} pcidev_tbl[] = {
	{ "Realtek 8129", "RealTek RTL8129",
	  0x10ec, 0x8129, 0xffff, HasMII, 128, rtl81x9_diag },
	{ "Realtek 8139", "RealTek RTL8139",
	  0x10ec, 0x8139, 0xffff, 0, 128, rtl81x9_diag },
	{ "SMC 1211", "SMC1211TX EZCard 10/100 (RealTek RTL8139)",
	  0x1113, 0x1211, 0xffff, 0, 128, rtl81x9_diag},
	{ "Accton MPX5030", "Accton MPX5030 (RealTek RTL8139)",
	  0x1113, 0x1211, 0xffff, 0, 128, rtl81x9_diag},
	{ "Realtek", "RealTek (unknown chip type)",
	  0x10ec, 0x8100, 0xff00, 0, 128, rtl81x9_diag },
	{ 0, 0, 0, 0},
};

int verbose = 1, opt_f = 0, debug = 0;
int show_regs = 0, show_eeprom = 0, show_mii = 0;
unsigned int opt_a = 0,					/* Show-all-interfaces flag. */
	opt_restart = 0,
	opt_reset = 0,
	opt_watch = 0,
	opt_G = 0;
unsigned int opt_GPIO = 0;		/* General purpose I/O setting. */
int do_write_eeprom = 0, do_test = 0;
int nway_advertise = 0, fixed_speed = -1;
int new_default_media = -1;
/* Valid with libflash only. */
static unsigned int opt_flash_show = 0;
static char	*opt_flash_dumpfile = NULL, *opt_flash_loadfile = NULL;

static unsigned char new_hwaddr[6], set_hwaddr = 0;

static int scan_proc_pci(int card_num);
static int parse_media_type(const char *capabilities);
static int get_media_index(const char *name);


int
main(int argc, char **argv)
{
	int port_base = 0, chip_type = 0;
	int errflag = 0, show_version = 0;
	int emergency_rewrite = 0;
	int c, longind;
	int card_num = 0;
	extern char *optarg;

	while ((c = getopt_long(argc, argv, "#:aA:DeEfF:G:mp:qrRst:vVwWH:BL:S:",
							longopts, &longind))
		   != -1)
		switch (c) {
		case '#': card_num = atoi(optarg);	break;
		case 'a': show_regs++; opt_a++;		break;
		case 'A': nway_advertise = parse_media_type(optarg); break;
		case 'D': debug++;			break;
		case 'e': show_eeprom++;	break;
		case 'E': emergency_rewrite++;	 break;
		case 'f': opt_f++;			break;
		case 'F': new_default_media = get_media_index(optarg);
			if (new_default_media < 0)
				errflag++;
			break;
		case 'G': opt_G++; opt_GPIO = strtol(optarg, NULL, 16); break;
		case 'H':
			{
				int hwaddr[6], i;
				if (sscanf(optarg, "%2x:%2x:%2x:%2x:%2x:%2x",
						   hwaddr, hwaddr + 1, hwaddr + 2,
						   hwaddr + 3, hwaddr + 4, hwaddr + 5) == 6) {
					for (i = 0; i < 6; i++)
						new_hwaddr[i] = hwaddr[i];
					set_hwaddr++;
				} else
					errflag++;
				break;
			}
		case 'm': show_mii++;	 break;
		case 'p':
			port_base = strtol(optarg, NULL, 16);
			break;
		case 'q': if (verbose) verbose--;		 break;
		case 'r': opt_restart++;	break;
		case 'R': opt_reset++;		break;
		case 't': chip_type = atoi(optarg);	break;
		case 'v': verbose++;		break;
		case 'V': show_version++;	break;
		case 'w': do_write_eeprom++;	break;
		case 'W': opt_watch++;		break;
		case 'B': opt_flash_show++;	break;
		case 'L': opt_flash_loadfile = optarg;	break;
		case 'S': opt_flash_dumpfile = optarg;	break;
		case '?':
			errflag++;
		}
	if (errflag) {
		fprintf(stderr, usage_msg);
		return 3;
	}

	if (verbose || show_version)
		printf(version_msg);

	if (chip_type < 0
		|| chip_type >= sizeof(pcidev_tbl)/sizeof(pcidev_tbl[0]) - 1) {
		int i;
		fprintf(stderr, "Valid numeric chip types are:\n");
		for (i = 0; pcidev_tbl[i].part_name; i++) {
			fprintf(stderr, "  %d\t%s\n", i, pcidev_tbl[i].part_name);
		}
		return 3;
	}

	/* Get access to all of I/O space. */
	if (iopl(3) < 0) {
		perror("rtl8139-diag: iopl()");
		fprintf(stderr, "This program must be run as root.\n");
		return 2;
	}

	/* Try to read a likely port_base value from /proc/pci. */
	if (port_base) {
		printf("Assuming a %s adapter at %#x.\n",
			   pcidev_tbl[chip_type].part_name, port_base);
		pcidev_tbl[chip_type].diag_func(0, 0, port_base, chip_type);
	} else if ( scan_proc_pci(card_num) == 0) {
		fprintf(stderr,
				"Unable to find a recognized card in /proc/pci.\nIf there is"
				" a card in the machine, explicitly set the I/O port"
				" address\n  using '-p <ioaddr> -t <chip_type_index>'\n"
				" Use '-t -1' to see the valid chip types.\n");
		return ENODEV;
	}

	if (show_regs == 0  &&  show_eeprom == 0  &&  show_mii == 0)
		printf(" Use '-a' or '-aa' to show device registers,\n"
			   "     '-e' to show EEPROM contents, -ee for parsed contents,\n"
			   "  or '-m' or '-mm' to show MII management registers.\n");

	return 0;
}


/* Generic (all PCI diags) code to find cards. */

static char bogus_iobase[] =
"This chip has not been assigned a valid I/O address, and will not function.\n"
" If you have warm-booted from another operating system, a complete \n"
" shut-down and power cycle may restore the card to normal operation.\n";

static char bogus_irq[] =
"This chip has not been assigned a valid IRQ, and will not function.\n"
" This must be fixed in the PCI BIOS setup.  The device driver has no way\n"
" of changing the PCI IRQ settings.\n";

static int scan_proc_bus_pci(int card_num)
{
	int card_cnt = 0, chip_idx = 0;
	int port_base;
	char buffer[514];
	unsigned int pci_bus, pci_devid, irq, pciaddr0, pciaddr1;
	int i;
	FILE *fp = fopen("/proc/bus/pci/devices", "r");

	if (fp == NULL) {
		if (debug) fprintf(stderr, "Failed to open /proc/bus/pci/devices.\n");
		return -1;
	}
	while (fgets(buffer, sizeof(buffer), fp)) {
		if (debug > 1)
			fprintf(stderr, " Parsing line -- %s", buffer);
		if (sscanf(buffer, "%x %x %x %x %x",
				   &pci_bus, &pci_devid, &irq, &pciaddr0, &pciaddr1) <= 0)
			break;
		for (i = 0; pcidev_tbl[i].vendor; i++) {
			if (pci_devid !=
				(pcidev_tbl[i].vendor << 16) + pcidev_tbl[i].device)
				continue;
			chip_idx = i;
			card_cnt++;
			/* Select the I/O address. */
			port_base = pciaddr0 & 1  ?  pciaddr0 & ~1 : pciaddr1 & ~1;
			if (card_num == 0 || card_num == card_cnt) {
				printf("Index #%d: Found a %s adapter at %#x.\n",
					   card_cnt, pcidev_tbl[chip_idx].part_name,
					   port_base);
				if (irq == 0  || irq == 255)
					printf(bogus_irq);
				if (port_base)
					pcidev_tbl[chip_idx].diag_func(0,0,port_base, i);
				else
					printf(bogus_iobase);
				break;
			}
		}
	}
	fclose(fp);
	return card_cnt;
}

static int scan_proc_pci(int card_num)
{
	int card_cnt = 0, chip_idx = 0;
	char chip_name[40];
	FILE *fp;
	int port_base;

	if ((card_cnt = scan_proc_bus_pci(card_num)) >= 0)
		return card_cnt;
	card_cnt = 0;

	fp = fopen("/proc/pci", "r");
	if (fp == NULL)
		return 0;
	{
		char buffer[514];
		int pci_bus, pci_device, pci_function, vendor_id, device_id;
		int state = 0;
		if (debug) printf("Done open of /proc/pci.\n");
		while (fgets(buffer, sizeof(buffer), fp)) {
			if (debug > 1)
				fprintf(stderr, " Parse state %d line -- %s", state, buffer);
			if (sscanf(buffer, " Bus %d, device %d, function %d",
					   &pci_bus, &pci_device, &pci_function) > 0) {
				chip_idx = 0;
				state = 1;
				continue;
			}
			if (state == 1) {
				if (sscanf(buffer, " Ethernet controller: %39[^\n]",
						   chip_name) > 0) {
					int i;
					if (debug)
						printf("Named ethernet controller %s.\n", chip_name);
					for (i = 0; pcidev_tbl[i].proc_pci_name; i++)
						if (strncmp(pcidev_tbl[i].proc_pci_name, chip_name,
									strlen(pcidev_tbl[i].proc_pci_name))
							== 0) {
							state = 2;
							chip_idx = i;
							continue;
						}
					continue;
				}
				/* Handle a /proc/pci that does not recognize the card. */
				if (sscanf(buffer, " Vendor id=%x. Device id=%x",
						   &vendor_id, &device_id) > 0) {
					int i;
					if (debug)
						printf("Found vendor 0x%4.4x device ID 0x%4.4x.\n",
							   vendor_id, device_id);
					for (i = 0; pcidev_tbl[i].vendor; i++)
						if (vendor_id == pcidev_tbl[i].vendor  &&
							(device_id & pcidev_tbl[i].device_mask)
							== pcidev_tbl[i].device)
							break;
					if (pcidev_tbl[i].vendor == 0)
						continue;
					chip_idx = i;
					state = 2;
				}
			}
			if (state == 2) {
				if (sscanf(buffer, "  I/O at %x", &port_base) > 0) {
					card_cnt++;
					state = 3;
					if (card_num == 0 || card_num == card_cnt) {
						printf("Index #%d: Found a %s adapter at %#x.\n",
							   card_cnt, pcidev_tbl[chip_idx].part_name,
							   port_base);
						if (port_base)
							pcidev_tbl[chip_idx].diag_func
								(vendor_id, device_id, port_base, chip_idx);
						else
							printf(bogus_iobase);
					}
				}
			}
		}
	}
	fclose(fp);
	return card_cnt;
}

/* Convert a text media name to a NWay capability word. */
static int parse_media_type(const char *capabilities)
{
	const char *mtypes[] = {
		"100baseT4", "100baseTx", "100baseTx-FD", "100baseTx-HD",
		"10baseT", "10baseT-FD", "10baseT-HD", 0,
	};
	int cap_map[] = { 0x0200, 0x0180, 0x0100, 0x0080, 0x0060, 0x0040, 0x0020,};
	int i;
	if (debug)
		fprintf(stderr, "Advertise string is '%s'.\n", capabilities);
	for (i = 0; mtypes[i]; i++)
		if (strcasecmp(mtypes[i], capabilities) == 0)
			return cap_map[i];
	if ((i = strtol(capabilities, NULL, 16)) <= 0xffff)
		return i;
	fprintf(stderr, "Invalid media advertisement '%s'.\n", capabilities);
	return 0;
}

/* Return the index of a valid media name.
   0x0800	Power up autosense (check speed only once)
   0x8000	Dynamic Autosense
*/
/* A table of media names to indices.  This matches the Digital Tulip
   SROM numbering, primarily because that is the most complete list.
   Other chips will have to map these number to their internal values.
*/
struct { char *name; int value; } mediamap[] = {
	{ "10baseT", 0 },
	{ "10base2", 1 },
	{ "AUI", 2 },
	{ "100baseTx", 3 },
	{ "10baseT-FDX", 0x204 },
	{ "100baseTx-FDX", 0x205 },
	{ "100baseT4", 6 },
	{ "100baseFx", 7 },
	{ "100baseFx-FDX", 8 },
	{ "MII", 11 },
	{ "Autosense", 0x0800 },
	{ 0, 0 },
};

static int get_media_index(const char *name)
{
	int i;
	for (i = 0; mediamap[i].name; i++)
		if (strcasecmp(name, mediamap[i].name) == 0)
			return i;
	if (name  &&  atoi(name) >= 00)
		return atoi(name);
	fprintf(stderr, "Invalid interface specified: it must be one of\n  ");
	for (i = 0; mediamap[i].name; i++)
		fprintf(stderr, "  %s", mediamap[i].name);
	fprintf(stderr, ".\n");
	return -1;
}


/* Chip-specific section. */

/* The chip-specific section for the RealTek 8129/8139 diagnostic. */
static int read_eeprom(long ioaddr, int location, int addr_len);
static void parse_eeprom(unsigned short *eeprom);
static int do_update(long ioaddr, unsigned short *ee_values,
			  int index, char *field_name, int new_value);
static void mdio_sync(long ioaddr);
int mdio_read(long ioaddr, int phy_id, int location);

const char *intr_names[16] ={
	"Rx Complete", "Rx Error", "Transmit OK", "Transmit Error",
	"Rx Buffer Overflow", "Rx Buffer Underrun", "Rx FIFO Overflow",
	"unknown-0080",
	"unknown-0100", "unknown-0200", "PCS timeout - packet too long",
	"PCI System Error",
};

/* Symbolic offsets to registers. */
enum RTL8129_registers {
	MAC0=0,						/* Ethernet hardware address. */
	MAR0=8,						/* Multicast filter. */
	TxStat0=0x10,				/* Transmit status (Four 32bit registers). */
	TxAddr0=0x20,				/* Tx descriptors (also four 32bit). */
	RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36,
	ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A,
	IntrMask=0x3C, IntrStatus=0x3E,
	TxConfig=0x40, RxConfig=0x44, /* Must enable Tx/Rx before writing. */
	Timer=0x48,					/* A general-purpose counter. */
	RxMissed=0x4C,				/* 24 bits valid, write clears. */
	Cfg9346=0x50, Config0=0x51, Config1=0x52,
	FlashReg=0x54, GPPinData=0x58, GPPinDir=0x59, MII_SMI=0x5A, HltClk=0x5B,
	MultiIntr=0x5C, TxSummary=0x60,
	MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68,
	NWayExpansion=0x6A, FlashAccess=0x74,
};

/* Values read from the EEPROM, and the new image. */
static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len);
#define EE_READ_CMD		(6)
#define EEPROM_SIZE 64			/* Really the maximum index we read. */
unsigned short eeprom_contents[EEPROM_SIZE];
unsigned short new_ee_contents[EEPROM_SIZE];

#ifdef LIBFLASH
/* Support for Flash operations. */
static int rtl_flash_in(long ioaddr, int offset) {
	outl(0x1C0000 | (offset & 0x1ffff), ioaddr + FlashAccess);
	return inl(ioaddr + FlashAccess) >> 24;
}
static void rtl_flash_out(long ioaddr, int offset, int val) {
	outl((val<<24) | 0x1a0000 | (offset & 0x1ffff), ioaddr + FlashAccess);
}
#endif


int rtl81x9_diag(int vendor_id, int device_id, long ioaddr, int part_idx)
{
	int flags = pcidev_tbl[part_idx].flags; 		/* Capabilities. */
	int chip_active = 0;
	int i;

	/* It's mostly safe to examine the registers and EEPROM during
	   operation.  But warn the user, and make then pass '-f'. */
	if ((inb(ioaddr + ChipCmd) & 0x000C) != 0x0000)
		chip_active = 1;

	if (show_regs) {
		unsigned intr_status;

		if (chip_active && !opt_f) {
			printf("The RealTek chip appears to be active, so some registers"
				   " will not be read.\n"
				   "To see all register values use the '-f' flag.\n");
		} else
			chip_active = 0;		/* Ignore the chip status with -f */

		printf("RealTek chip registers at %#lx", ioaddr);
		for (i = 0; i < 0x80; i += 4) {
			if ((i & 0x1f) == 0)
				printf("\n 0x%3.3X:", i);
			printf(" %8.8x", inl(ioaddr + i));
		}
		printf(".\n");

		intr_status = inw(ioaddr + IntrStatus);
		printf("  %snterrupt sources are pending.\n",
			   (intr_status & 0x03ff) ? "I": "No i");
		if (intr_status) {
			for (i = 0; i < 16; i++)
				if (intr_status & (1<<i))
					printf("   %s indication.\n", intr_names[i]);
		}
		{
			unsigned char cfg0 = inb(ioaddr + Config0);
			unsigned char cfg1 = inb(ioaddr + Config1);
			const char *xcvr_mode[] = {
				"MII", "an invalid transceiver", "MII/symbol",
				"4B/5B scambler"};
			printf(" The chip configuration is 0x%2.2x 0x%2.2x, %s %s-duplex"
				   " mode.\n",
				   cfg0, cfg1,
				   cfg0 & 0x20 ? "10baseT" : xcvr_mode[cfg0>>6],
				   cfg1 & 0x40 ? "full" : "half");
		}
	}

	/* Read the EEPROM. */
	{
		int testval = do_eeprom_cmd(ioaddr,
									(((EE_READ_CMD<<8)|0) << 16) | 0xffff,
									3 + 8 + 16);
		int addr_size = (read_eeprom(ioaddr, 0, 6) & 0xffff) == 0x8129 ? 6 : 8;
		printf("EEPROM size test returned %d, %#x / %#x.\n", addr_size,
			   testval, read_eeprom(ioaddr, 0xff, 8));
		for (i = 0; i < EEPROM_SIZE; i++)
			eeprom_contents[i] = read_eeprom(ioaddr, i, addr_size);
	}

	/* The user will usually want to see the interpreted EEPROM contents. */
	if (show_eeprom)
		parse_eeprom(eeprom_contents);
	if (show_eeprom > 1) {
		unsigned short sum = 0;
		printf("EEPROM contents:");
		for (i = 0; i < EEPROM_SIZE; i++) {
			printf("%s %4.4x", (i & 7) == 0 ? "\n ":"",
				   eeprom_contents[i]);
			sum += eeprom_contents[i];
		}
		printf("\n The word-wide EEPROM checksum is %#4.4x.\n", sum);
	}

	/********** INSERTED BY LI *********************/
	if (set_hwaddr) {
		printf("\n ****** OK, setting the HWADDR! *****\n");
		do_update(ioaddr, eeprom_contents, 7, "hw1",
				  ((int)(new_hwaddr[1]))*256+((int)(new_hwaddr[0])));
		do_update(ioaddr, eeprom_contents, 8, "hw2",
				  ((int)(new_hwaddr[3]))*256+((int)(new_hwaddr[2])));
		do_update(ioaddr, eeprom_contents, 9, "hw3",
				  ((int)(new_hwaddr[5]))*256+((int)(new_hwaddr[4])));
		printf(" ****** New HWADDR set! *****\n");

		if (show_eeprom)
			parse_eeprom(eeprom_contents);
		if (show_eeprom > 1) {
			unsigned short sum = 0;
			printf("EEPROM contents:");
			for (i = 0; i < EEPROM_SIZE; i++) {
				printf("%s %4.4x", (i & 7) == 0 ? "\n ":"",
					   eeprom_contents[i]);
				sum += eeprom_contents[i];
			}
			printf("\n The word-wide EEPROM checksum is %#4.4x.\n", sum);
		}
	}
	/*******************************/



	/* Show up to four (not just the on-board) PHYs. */
	if (show_mii  &&  (flags & HasMII)) {
		int phys[4], phy, phy_idx = 0;
		mdio_sync(ioaddr);
		for (phy = 0; phy < 32 && phy_idx < 4; phy++) {
			int mii_status = mdio_read(ioaddr, phy, 0);
			if (mii_status != 0xffff) {
				phys[phy_idx++] = phy;
				printf(" MII PHY found at address %d (%4.4x).\n",
					   phy, mii_status);
			}
		}
		if (phy_idx == 0)
			printf(" ***WARNING***: No MII transceivers found!\n");
#ifdef LIBMII
		else if (show_mii > 1) {
			show_mii_details(ioaddr, phys[0]);
			if (show_mii > 2) monitor_mii(ioaddr, phys[0]);
		}
#else
		else for (phy = 0; phy < phy_idx; phy++) {
			int mii_reg;
			printf(" MII PHY #%d transceiver registers:", phys[phy]);
			for (mii_reg = 0; mii_reg < 32; mii_reg++)
				printf("%s %4.4x", (mii_reg % 8) == 0 ? "\n  " : "",
					   mdio_read(ioaddr, phys[phy], mii_reg));
			printf(".\n");
		}
#endif
	}
	if (show_mii  &&  (flags & HasMII)) {
		printf(" The RTL8139 does not use a MII transceiver.\n"
			   " It does have internal MII-compatible registers:\n"
			   "   Basic mode control register   0x%4.4x.\n"
			   "   Basic mode status register    0x%4.4x.\n"
			   "   Autonegotiation Advertisement 0x%4.4x.\n"
			   "   Link Partner Ability register 0x%4.4x.\n"
			   "   Autonegotiation expansion     0x%4.4x.\n"
			   "   Disconnects                   0x%4.4x.\n"
			   "   False carrier sense counter   0x%4.4x.\n"
			   "   NWay test register            0x%4.4x.\n"
			   "   Receive frame error count     0x%4.4x.\n",
			   inw(ioaddr + MII_BMSR), inw(ioaddr + MII_BMCR),
			   inw(ioaddr + NWayAdvert), inw(ioaddr + NWayLPAR),
			   inw(ioaddr + NWayExpansion), inw(ioaddr + 0x6C),
			   inw(ioaddr + 0x6E), inw(ioaddr + 0x70), inw(ioaddr + 0x72));
#ifdef LIBMII
		if (show_mii > 1) {
			show_mii_details(ioaddr, -1);
			if (show_mii > 2) monitor_mii(ioaddr, 32);
		}
#endif
	}

#ifdef LIBFLASH
	{
		flash_in_hook = rtl_flash_in;
		flash_out_hook = rtl_flash_out;
		/* Turn on the Flash chip. */
		outl(0x020000, ioaddr + FlashAccess);
		if (opt_flash_show)
			flash_show(ioaddr, 0);
		if (opt_flash_dumpfile)
			if (flash_dump(ioaddr, 0, opt_flash_dumpfile) < 0) {
				fprintf(stderr, "Failed to save the old Flash BootROM image "
						"into file '%s'.\n", opt_flash_dumpfile);
				return 3;
			}
		if (opt_flash_loadfile)
			if (flash_program(ioaddr, 0, opt_flash_loadfile) < 0) {
				fprintf(stderr, "Failed to load the new Flash BootROM image "
						"from file '%s'.\n", opt_flash_loadfile);
				return 4;
			}
		/* Turn off the Flash chip. */
		outl(0x000000, ioaddr + FlashAccess);
	}
#else
	if (opt_flash_loadfile  || opt_flash_dumpfile  ||  opt_flash_show)
		printf("Flash operations not configured into this program.\n");
#endif

	if (do_test) {
		printf("FIFO buffer test not yet available.\n");
		/* Here just to prevent compile warnings. */
		if (0)
			do_update(0, 0, 0, 0, 0);
	}
	return 0;
}


/* Serial EEPROM section. */

/*  EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK	0x04	/* EEPROM shift clock. */
#define EE_CS			0x08	/* EEPROM chip select. */
#define EE_DATA_WRITE	0x02	/* EEPROM chip data in. */
#define EE_WRITE_0		(0x00 | EE_ENB)
#define EE_WRITE_1		(0x02 | EE_ENB)
#define EE_DATA_READ	0x01	/* EEPROM chip data out. */
#define EE_ENB			(0x80 | EE_CS)

/* Delay between EEPROM clock transitions.
   No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
 */

#define eeprom_delay()	inl(ee_addr)

/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD	(5)
#define EE_READ_CMD		(6)
#define EE_ERASE_CMD	(7)

static int read_eeprom(long ioaddr, int location, int addr_len)
{
	int i;
	unsigned retval = 0;
	long ee_addr = ioaddr + Cfg9346;
	int read_cmd = location | (EE_READ_CMD << addr_len);

	outb(EE_ENB & ~EE_CS, ee_addr);
	outb(EE_ENB, ee_addr);

	/* Shift the read command bits out. */
	for (i = 4 + addr_len; i >= 0; i--) {
		int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
		outb(EE_ENB | dataval, ee_addr);
		eeprom_delay();
		outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
		eeprom_delay();
		retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
	}
	outb(EE_ENB, ee_addr);
	eeprom_delay();

	for (i = 16; i > 0; i--) {
		outb(EE_ENB | EE_SHIFT_CLK, ee_addr);
		eeprom_delay();
		retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
		outb(EE_ENB, ee_addr);
		eeprom_delay();
	}

	/* Terminate the EEPROM access. */
	outb(~EE_CS, ee_addr);
	return retval;
}

/* This executes a generic EEPROM command, typically a write or write enable.
   It returns the data output from the EEPROM, and thus may also be used for
   reads. */
static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len)
{
	unsigned retval = 0;
	long ee_addr = ioaddr + Cfg9346;

	if (debug > 1)
		printf(" EEPROM op 0x%x: ", cmd);

	outb(EE_ENB | EE_SHIFT_CLK, ee_addr);

	/* Shift the command bits out. */
	do {
		short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
		outb(dataval, ee_addr);
		eeprom_delay();
		if (debug > 2)
			printf("%X", inl(ee_addr) & 15);
		outb(dataval | EE_SHIFT_CLK, ee_addr);
		eeprom_delay();
		retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
	} while (--cmd_len >= 0);
	outb(EE_ENB, ee_addr);

	/* Terminate the EEPROM access. */
	outb(EE_ENB & ~EE_CS, ee_addr);
	outb(~EE_CS, ee_addr);
	if (debug > 1)
		printf(" EEPROM result is 0x%5.5x.\n", retval);
	return retval;
}

/* Caution!  This routine works!
   It will permanently change the EEPROM contents.
   "You could put an eye out with that thing." */
static int do_update(long ioaddr, unsigned short *ee_values,
					 int index, char *field_name, int new_value)
{
	if (ee_values[index] == new_value)
		return 0;
	if (do_write_eeprom) {
		int ee_addr_size = read_eeprom(ioaddr, 0, 8) == 0x8129 ? 8 : 6;
		int i;
		u16 newval;
		printf("Writing new %s entry 0x%4.4x to offset %d.\n",
			   field_name, new_value, index);
		/* Enable programming modes. */
		do_eeprom_cmd(ioaddr, (0x4f << (ee_addr_size-4)), 3+ee_addr_size);
		/* Do the actual write. */ 
		do_eeprom_cmd(ioaddr,
					  (((EE_WRITE_CMD<<ee_addr_size)|index) << 16) | new_value,
					  3 + ee_addr_size + 16);
		/* Poll for write finished. */
		outb(EE_ENB, ioaddr + Cfg9346);
		for (i = 0; i < 10000; i++)			/* Typical 2000 ticks */
			if (inb(ioaddr + Cfg9346) & EE_DATA_READ)
				break;
		if (debug)
			printf(" Write finished after %d ticks.\n", i);
		/* Disable programming. */
		do_eeprom_cmd(ioaddr, (0x40 << (ee_addr_size-4)), 3 + ee_addr_size);
		/* And read the result. */
		newval = do_eeprom_cmd(ioaddr,
							   (((EE_READ_CMD<<ee_addr_size)|index) << 16)
							   | 0xffff, 3 + ee_addr_size + 16);
		printf("  New %s value at offset %d is %4.4x.\n",
			   field_name, index, newval);
	} else
		printf(" Would write new %s entry 0x%4.4x to offset %d, the "
			   "current value is 0x%4.4x.\n",
			   field_name, new_value, index, ee_values[index]);
	ee_values[index] = new_value;
	return 1;
}


/* MII serial management: mostly bogus for now. */
/* Read and write the MII management registers using software-generated
   serial MDIO protocol.
   The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
   met by back-to-back PCI I/O cycles, but we insert a delay to avoid
   "overclocking" issues. */
#define MDIO_DIR		0x80
#define MDIO_DATA_OUT	0x04
#define MDIO_DATA_IN	0x02
#define MDIO_CLK		0x01
#define MDIO_WRITE0 (MDIO_DIR)
#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT)

#define mdio_delay()	inl(mdio_addr)

/* A map from MII-like registers to the RTL8139 equivalent. */
static char mii_2_8139_map[8] = {
	MII_BMCR, MII_BMSR, 0, 0, NWayAdvert, NWayLPAR, NWayExpansion, 0 };

/* Syncronize the MII management interface by shifting 32 one bits out. */
static void mdio_sync(long mdio_addr)
{
	int i;

	for (i = 32; i >= 0; i--) {
		outb(MDIO_WRITE1, mdio_addr);
		mdio_delay();
		outb(MDIO_WRITE1 | MDIO_CLK, mdio_addr);
		mdio_delay();
	}
	return;
}
int mdio_read(long ioaddr, int phy_id, int location)
{
	long mdio_addr = ioaddr + MII_SMI;
	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
	int retval = 0;
	int i;

	if (phy_id == 32) {			/* Really a 8139.  Use internal registers. */
		return location < 8 && mii_2_8139_map[location] ?
			inw(ioaddr + mii_2_8139_map[location]) : 0;
	}
	mdio_sync(mdio_addr);
	/* Shift the read command bits out. */
	for (i = 15; i >= 0; i--) {
		int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0;

		if (verbose > 3)		/* Debug: 5 */
			printf("%d", (mii_cmd & (1 << i)) ? 1 : 0);

		outb(MDIO_DIR | dataval, mdio_addr);
		mdio_delay();
		outb(MDIO_DIR | dataval | MDIO_CLK, mdio_addr);
		if (verbose > 4) printf(" %x ", (inb(mdio_addr) & 0x0f));
		mdio_delay();
	}
	if (verbose > 3) printf("-> %x", inb(mdio_addr) & 0x0f);

	/* Read the two transition, 16 data, and wire-idle bits. */
	for (i = 19; i > 0; i--) {
		outb(0, mdio_addr);
		mdio_delay();
		retval = (retval << 1) | ((inb(mdio_addr) & MDIO_DATA_IN) ? 1 : 0);
		outb(MDIO_CLK, mdio_addr);
		mdio_delay();
		if (verbose > 3) printf("%x", inb(mdio_addr) & 0x0f);
	}
	if (verbose > 2)
		printf("  MII read of %d:%d -> %4.4x.\n", phy_id, location, retval);
	return (retval>>1) & 0xffff;
}

void mdio_write(long ioaddr, int phy_id, int location, int value)
{
	long mdio_addr = ioaddr + MII_SMI;
	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
	int i;

	if (phy_id == 32) {			/* Really a 8139.  Use internal registers. */
		if (location < 8 && mii_2_8139_map[location])
 			outw(value, ioaddr + mii_2_8139_map[location]);
		return;
	}
	mdio_sync(mdio_addr);

	/* Shift the command bits out. */
	for (i = 31; i >= 0; i--) {
		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
		outb(dataval, mdio_addr);
		mdio_delay();
		outb(dataval | MDIO_CLK, mdio_addr);
		if (verbose > 4) printf(" %x ", (inb(mdio_addr) & 0x0f));
		mdio_delay();
	}
	/* Clear out extra bits. */
	for (i = 2; i > 0; i--) {
		outb(0, mdio_addr);
		mdio_delay();
		outb(MDIO_CLK, mdio_addr);
		mdio_delay();
	}
	return;
}

static void parse_eeprom(unsigned short *eeprom)
{
	unsigned char *p = (void *)eeprom;
	int i, sum = 0;

	printf("Parsing the EEPROM of a RealTek chip:\n"
		   "  PCI IDs -- Vendor %#4.4x, Device %#4.4x, Subsystem %#4.4x.\n"
		   "  PCI timer settings -- minimum grant %d, maximum latency %d.\n"
		   "  General purpose pins --  direction 0x%2.2x  value 0x%2.2x.\n"
		   "  Station Address ",
		   eeprom[1], eeprom[2], eeprom[3], p[10], p[11], p[13], p[12] );
	for (i = 14; i < 19; i++)
		printf("%2.2X:", p[i]);
	printf("%2.2X.\n"
		   "  Configuration register 0/1 -- 0x%2.2x / 0x%2.2x.\n",
		   p[i], p[21], p[22]);
	for (i = 0; i < 24; i++)
		sum += p[i];
	printf(" EEPROM active region checksum is %4.4x.\n", sum);
	return;
}


/*
 * Local variables:
 *  compile-command: "cc -O -Wall -o rtl8139-diag rtl8139-diag.c `[ -f libmii.c ] && echo -DLIBMII libmii.c`"
 *  simple-compile-command: "cc -O -Wall -o rtl8139-diag rtl8139-diag.c"
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 * End:
 */

Comments

Jason Wang March 6, 2012, 3:15 a.m. UTC | #1
On 03/05/2012 02:48 PM, Gerhard Wiesinger wrote:
> Hello,
>
> I'm trying to implement better emulation and wider OS support for the 
> rtl8139 card. Therefore I want to see the following testcases to be 
> successful:
>
>  * Testcases and successful regression tests:
>  * 1.) DOS RSET8139.EXE: EEPROM Test successful
>  * 2.) DOS RSET8139.EXE: Local loopback Test (Run Diagnostics On Board)
>  * 3.) DOS RSET8139.EXE: Remote loopback Test as Initiator (Run 
> Diagnostics On Network)
>  * 4.) DOS RSET8139.EXE: Remote loopback Test as Responder (Run 
> Diagnostics On Network)
>  * 5.) DOS driver: Loads and works
>  * 6.) Linux tests
>  * 7.) Windows tests
>
> I fixed already a major bug in DMA handling and (real hardware doesn't 
> reset DMA register to 0 on reset condition as DOS driver crashes OS, 
> see patch for details) and improved EEPROM handling and checksumming 
> as well as unimplemented register handling (As Jason did partially in 
> latest patch).
>
> But finally testcases 1-4 aren't successful, testcase 5 (DOS driver 
> and MS SMB client) works but I think there are still problems, see below.
>
> Details:
> Ad 1.) EERPOM Test: I also copied a full EEPROM from real hardware but 
> still no success. According to the logs everything is read correctly. 
> Also verified checksumming to real hardware . Any ideas?
> (Attached rtl8139-diag.c will also help to diagnose)
>
> Ad 2.) Local Loopback Test: One packet succeeds, other fail. Any ideas 
> what might be wrong?
>
> Ad 5.) DOS driver loads and also works but I think there is still a 
> strange thing in packet receiving and possible sending (e.g. DHCP 
> request is done twice). I also did some change in packet handling. See 
> patch.
>
> To get this to work I'm a little bit lost now and I need your help and 
> comments and suggestions.

Hi, it's better to split the patch into small ones, each one for a 
dedicated feature/fixe. This would let the reviewing and bisecting more 
easier, and you could easily check which patch does the wrong thing.
>
> Can someone also make tests under DOS with RSET8139.EXE?
> I think it is a good testing program how good our emulation is.
>
> RSET8139.EXE can be found at:
> http://www.realtek.com.tw/downloads/downloadsView.aspx?Langid=1&PNid=14&PFid=6&Level=5&Conn=4&DownTypeID=3&GetDown=false 
>
> Look for DOS Diagnostic program (RSET8139).
>
> Patch is attached as diff and not indented to be used as regular patch 
> since not ready for commit.
>
> Thnx.
>
> Ciao,
> Gerhard
>
> -- 
> http://www.wiesinger.com/
Gerhard Wiesinger March 7, 2012, 6:40 a.m. UTC | #2
Ping. Any comments?

Thnx.

Ciao,
Gerhard

--
http://www.wiesinger.com/


On Mon, 5 Mar 2012, Gerhard Wiesinger wrote:

> Hello,
>
> I'm trying to implement better emulation and wider OS support for the rtl8139 
> card. Therefore I want to see the following testcases to be successful:
>
> * Testcases and successful regression tests:
> * 1.) DOS RSET8139.EXE: EEPROM Test successful
> * 2.) DOS RSET8139.EXE: Local loopback Test (Run Diagnostics On Board)
> * 3.) DOS RSET8139.EXE: Remote loopback Test as Initiator (Run Diagnostics 
> On Network)
> * 4.) DOS RSET8139.EXE: Remote loopback Test as Responder (Run Diagnostics 
> On Network)
> * 5.) DOS driver: Loads and works
> * 6.) Linux tests
> * 7.) Windows tests
>
> I fixed already a major bug in DMA handling and (real hardware doesn't reset 
> DMA register to 0 on reset condition as DOS driver crashes OS, see patch for 
> details) and improved EEPROM handling and checksumming as well as 
> unimplemented register handling (As Jason did partially in latest patch).
>
> But finally testcases 1-4 aren't successful, testcase 5 (DOS driver and MS 
> SMB client) works but I think there are still problems, see below.
>
> Details:
> Ad 1.) EERPOM Test: I also copied a full EEPROM from real hardware but still 
> no success. According to the logs everything is read correctly. Also verified 
> checksumming to real hardware . Any ideas?
> (Attached rtl8139-diag.c will also help to diagnose)
>
> Ad 2.) Local Loopback Test: One packet succeeds, other fail. Any ideas what 
> might be wrong?
>
> Ad 5.) DOS driver loads and also works but I think there is still a strange 
> thing in packet receiving and possible sending (e.g. DHCP request is done 
> twice). I also did some change in packet handling. See patch.
>
> To get this to work I'm a little bit lost now and I need your help and 
> comments and suggestions.
>
> Can someone also make tests under DOS with RSET8139.EXE?
> I think it is a good testing program how good our emulation is.
>
> RSET8139.EXE can be found at:
> http://www.realtek.com.tw/downloads/downloadsView.aspx?Langid=1&PNid=14&PFid=6&Level=5&Conn=4&DownTypeID=3&GetDown=false
> Look for DOS Diagnostic program (RSET8139).
>
> Patch is attached as diff and not indented to be used as regular patch since 
> not ready for commit.
>
> Thnx.
>
> Ciao,
> Gerhard
>
> --
> http://www.wiesinger.com/
>
diff mbox

Patch

diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 05b8e1e..b15f66d 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -48,6 +48,17 @@ 
  *  2011-Mar-22  Benjamin Poirier:  Implemented VLAN offloading
  */
 
+/*
+ * Testcases and successful regression tests:
+ * 1.) DOS RSET8139.EXE: EEPROM Test successful
+ * 2.) DOS RSET8139.EXE: Local loopback Test (Run Diagnostics On Board)
+ * 3.) DOS RSET8139.EXE: Remote loopback Test as Initiator (Run Diagnostics On Network)
+ * 4.) DOS RSET8139.EXE: Remote loopback Test as Responder (Run Diagnostics On Network)
+ * 5.) DOS driver: Loads and works
+ * 6.) Linux tests
+ * 7.) Windows tests
+ */
+
 /* For crc32 */
 #include <zlib.h>
 
@@ -61,7 +72,7 @@ 
 #include "iov.h"
 
 /* debug RTL8139 card */
-//#define DEBUG_RTL8139 1
+#define DEBUG_RTL8139 1
 
 #define PCI_FREQUENCY 33000000L
 
@@ -130,6 +141,7 @@  enum RTL8139_registers {
     NWayExpansion = 0x6A,
     /* Undocumented registers, but required for proper operation. */
     FIFOTMS = 0x70,        /* FIFO Control and test. */
+    RX_ER = 0x72,       /* RX_ER Counter */
     CSCR = 0x74,        /* Chip Status and Configuration Register. */
     PARA78 = 0x78,
     PARA7c = 0x7c,        /* Magic transceiver parameter register. */
@@ -467,6 +479,8 @@  typedef struct RTL8139State {
     uint16_t NWayLPAR;
     uint16_t NWayExpansion;
 
+    uint16_t Fifo_TMS;
+
     uint16_t CpCmd;
     uint8_t  TxThresh;
 
@@ -774,6 +788,12 @@  static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
 
             if (size > wrapped)
             {
+                DPRINTF(">>> rx packet pci dma write "
+                        "RxBuf=0x%x, RxBufAddr=0x%x, RxBuf+RxBufAddr=0x%x, "
+                        "buf=%p, size=%i, wrapped=%i, size-wrapped=%i\n",
+                        s->RxBuf, s->RxBufAddr, s->RxBuf + s->RxBufAddr,
+                        buf, size, wrapped, size-wrapped
+                       );
                 pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr,
                               buf, size-wrapped);
             }
@@ -781,6 +801,12 @@  static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
             /* reset buffer pointer */
             s->RxBufAddr = 0;
 
+            DPRINTF(">>> rx packet pci dma write "
+                    "RxBuf=0x%x, RxBufAddr=0x%x, RxBuf+RxBufAddr=0x%x, "
+                    "buf=%p, size=%i, wrapped=%i, size-wrapped=%i\n",
+                    s->RxBuf, s->RxBufAddr, s->RxBuf + s->RxBufAddr,
+                    buf, size, wrapped, size-wrapped
+                   );
             pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr,
                           buf + (size-wrapped), wrapped);
 
@@ -791,6 +817,13 @@  static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
     }
 
     /* non-wrapping path or overwrapping enabled */
+    DPRINTF(">>> rx packet pci dma write "
+            "RxBuf=0x%x, RxBufAddr=0x%x, RxBuf+RxBufAddr=0x%x, "
+            "buf=%p, size=%i\n",
+            s->RxBuf, s->RxBufAddr, s->RxBuf + s->RxBufAddr,
+            buf, size
+           );
+
     pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr, buf, size);
 
     s->RxBufAddr += size;
@@ -1209,15 +1242,18 @@  static ssize_t rtl8139_receive(VLANClientState *nc, const uint8_t *buf, size_t s
 static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize)
 {
     s->RxBufferSize = bufferSize;
+    /* Why not 0xFFF0? */
     s->RxBufPtr  = 0;
     s->RxBufAddr = 0;
 }
 
-static void rtl8139_reset(DeviceState *d)
+static void rtl8139_reset_delegate(DeviceState *d, int hard_reset)
 {
     RTL8139State *s = container_of(d, RTL8139State, dev.qdev);
     int i;
 
+    DPRINTF("rtl8139_reset_delegate, hard_reset=%i\n", hard_reset);
+
     /* restore MAC address */
     memcpy(s->phys, s->conf.macaddr.a, 6);
 
@@ -1240,7 +1276,18 @@  static void rtl8139_reset(DeviceState *d)
     s->RxRingAddrLO = 0;
     s->RxRingAddrHI = 0;
 
-    s->RxBuf = 0;
+    /*
+     * DOS driver sets the RxBuf and then resets again.
+     * Afterwards RxBuf is not set anymore. Looks like real hardware
+     * also doesn't reset RxBuf on reset.
+     * When set to 0 DOS OS crashed because of adress 0 is overwritten ...
+     *
+     * On the other hand this must be done on a hardware triggered
+     * reset (a DMA enabled receiver might overwrite some areas!).
+     */
+
+    if (hard_reset)
+        s->RxBuf = 0;
 
     rtl8139_reset_rxring(s, 8192);
 
@@ -1271,7 +1318,8 @@  static void rtl8139_reset(DeviceState *d)
 
 //    s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
 //    s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
-    s->BasicModeCtrl = 0x1000; // autonegotiation
+//    s->BasicModeCtrl = 0x1000; // autonegotiation
+    s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
 
     s->BasicModeStatus  = 0x7809;
     //s->BasicModeStatus |= 0x0040; /* UTP medium */
@@ -1282,6 +1330,8 @@  static void rtl8139_reset(DeviceState *d)
     s->NWayLPAR      = 0x05e1; /* all modes, full duplex */
     s->NWayExpansion = 0x0001; /* autonegotiation supported */
 
+    s->Fifo_TMS      = 0x0000; /* Phy N-Way Test Register */
+
     /* also reset timer and disable timer interrupt */
     s->TCTR = 0;
     s->TimerInt = 0;
@@ -1291,6 +1341,11 @@  static void rtl8139_reset(DeviceState *d)
     RTL8139TallyCounters_clear(&s->tally_counters);
 }
 
+static void rtl8139_reset(DeviceState *d)
+{
+    rtl8139_reset_delegate(d, 1);
+}
+
 static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters)
 {
     counters->TxOk = 0;
@@ -1388,7 +1443,7 @@  static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val)
     if (val & CmdReset)
     {
         DPRINTF("ChipCmd reset\n");
-        rtl8139_reset(&s->dev.qdev);
+        rtl8139_reset_delegate(&s->dev.qdev, 0);
     }
     if (val & CmdRxEnb)
     {
@@ -1561,7 +1616,7 @@  static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val)
     } else if (opmode == 0x40) {
         /* Reset.  */
         val = 0;
-        rtl8139_reset(&s->dev.qdev);
+        rtl8139_reset_delegate(&s->dev.qdev, 0);
     }
 
     s->Cfg9346 = val;
@@ -1726,6 +1781,8 @@  static void rtl8139_TxConfig_write(RTL8139State *s, uint32_t val)
     val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig);
 
     s->TxConfig = val;
+
+    s->currTxDesc = 0;
 }
 
 static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val)
@@ -1804,7 +1861,8 @@  static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
         }
 
         DPRINTF("+++ transmit loopback mode\n");
-        rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt);
+	/* Interrupt must be triggered here! */
+        rtl8139_do_receive(&s->nic->nc, buf, size, 1);
 
         if (iov) {
             g_free(buf2);
@@ -1846,12 +1904,14 @@  static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
 
     pci_dma_read(&s->dev, s->TxAddr[descriptor], txbuffer, txsize);
 
-    /* Mark descriptor as transferred */
+    /* Send frame */
+    rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL);
+
+    /* Mark descriptor as transferred but after sending */
+    /* (now correct and avoids race conditions!) */
     s->TxStatus[descriptor] |= TxHostOwns;
     s->TxStatus[descriptor] |= TxStatOK;
 
-    rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL);
-
     DPRINTF("+++ transmitted %d bytes from descriptor %d\n", txsize,
         descriptor);
 
@@ -2488,12 +2548,16 @@  static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32
 
     DPRINTF("TxStatus write offset=0x%x val=0x%08x descriptor=%d\n",
         txRegOffset, val, descriptor);
+    DPRINTF("TxStatus write old value=0x%08x descriptor=%d\n",
+        s->TxStatus[descriptor], descriptor);
 
     /* mask only reserved bits */
     val &= ~0xff00c000; /* these bits are reset on write */
     val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]);
 
     s->TxStatus[descriptor] = val;
+    DPRINTF("TxStatus write new value=0x%08x descriptor=%d\n",
+        s->TxStatus[descriptor], descriptor);
 
     /* attempt to start transmission */
     rtl8139_transmit(s);
@@ -2641,6 +2705,16 @@  static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val)
 {
     DPRINTF("IntrStatus write(w) val=0x%04x\n", val);
 
+    /*
+     * According to the specification writing to ISR must
+     * have no effect: "Writing to the ISR has no effect."
+     * http://www.cs.usfca.edu/~cruse/cs326/RTL8139D_DataSheet.pdf
+     *
+     * According to the newer specification writing to ISR clears
+     * ones bits!!
+     * http://realtek.info/pdf/rtl8139cp.pdf
+     * See also: http://www.lowlevel.eu/wiki/RTL8139
+     */
 #if 0
 
     /* writing to ISR has no effect */
@@ -2678,6 +2752,16 @@  static uint32_t rtl8139_IntrStatus_read(RTL8139State *s)
 
     DPRINTF("IntrStatus read(w) val=0x%04x\n", ret);
 
+    /*
+     * According to the specification interrupts have to be cleared.
+     * "Reading the ISR clears all interrupts"
+     * http://www.cs.usfca.edu/~cruse/cs326/RTL8139D_DataSheet.pdf
+     *
+     * But according to newer specifications all interrupt bits
+     * are not cleared!!!
+     * http://realtek.info/pdf/rtl8139cp.pdf
+     * See also: http://www.lowlevel.eu/wiki/RTL8139
+     */
 #if 0
 
     /* reading ISR clears all interrupts */
@@ -2727,6 +2811,12 @@  static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
         case ChipCmd:
             rtl8139_ChipCmd_write(s, val);
             break;
+        case IntrMask:
+            s->IntrMask = (s->IntrMask & 0xFF00) | (val & 0xFF);
+            break;
+        case IntrStatus:
+            rtl8139_IntrStatus_write(s, val & 0xFF);
+            break;
         case Cfg9346:
             rtl8139_Cfg9346_write(s, val);
             break;
@@ -2748,6 +2838,29 @@  static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
         case Config5:
             rtl8139_Config5_write(s, val);
             break;
+        case RX_ER:
+            s->tally_counters.RxERR = (s->tally_counters.RxERR & 0xFF00) | (val & 0xFF);
+            break;
+        case RX_ER+1:
+            s->tally_counters.RxERR = ((val & 0xFF)<<8) | (s->tally_counters.RxERR & 0xFF);
+            break;
+        case FIFOTMS:
+            s->Fifo_TMS = (s->Fifo_TMS & 0xFF00) | (val & 0xFF);
+            break;
+        case FIFOTMS+1:
+            s->Fifo_TMS = ((val & 0xFF)<<8) | (s->Fifo_TMS & 0xFF);
+            break;
+        case PARA78:
+        case PARA78+1:
+        case PARA78+2:
+        case PARA78+3:
+        case PARA7c:
+        case PARA7c+1:
+        case PARA7c+2:
+        case PARA7c+3:
+            DPRINTF("not implemented write(b) to PARA%02x val=0x%02x\n",
+                addr, val);
+            break;
         case MediaStatus:
             /* ignore */
             DPRINTF("not implemented write(b) to MediaStatus val=0x%02x\n",
@@ -2834,6 +2947,10 @@  static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
             s->NWayExpansion = val;
             break;
 
+        case FIFOTMS:
+            DPRINTF("Fifo_TMS write(w) val=0x%04x\n", val);
+            s->Fifo_TMS = val;
+            break;
         case CpCmd:
             rtl8139_CpCmd_write(s, val);
             break;
@@ -2974,9 +3091,29 @@  static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
         case MAR0 ... MAR0+7:
             ret = s->mult[addr - MAR0];
             break;
+        case TxAddr0 ... TxAddr0 + 4*4-1:
+            {
+                int offset = (addr-TxAddr0)/4;
+                int shift = ((addr-TxAddr0)%4) << 3;
+                ret = (s->TxAddr[offset] >> shift ) & 0xFF; 
+            }
+            break;
+
         case ChipCmd:
             ret = rtl8139_ChipCmd_read(s);
             break;
+        case RxBufPtr:
+            ret = s->RxBufPtr & 0xFF;
+            break;
+        case RxBufAddr:
+            ret = s->RxBufAddr & 0xFF;
+            break;
+        case IntrMask:
+            ret = s->IntrMask & 0xFF;
+            break;
+        case IntrStatus:
+            ret = s->IntrStatus & 0xFF;
+            break;
         case Cfg9346:
             ret = rtl8139_Cfg9346_read(s);
             break;
@@ -2984,7 +3121,10 @@  static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
             ret = rtl8139_Config0_read(s);
             break;
         case Config1:
-            ret = rtl8139_Config1_read(s);
+            ret = rtl8139_Config1_read(s) & 0xFF;
+            break;
+        case Config1+1:
+            ret = (rtl8139_Config1_read(s) >> 8) & 0xFF;
             break;
         case Config3:
             ret = rtl8139_Config3_read(s);
@@ -2995,7 +3135,30 @@  static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
         case Config5:
             ret = rtl8139_Config5_read(s);
             break;
-
+        case RX_ER:
+            ret = s->tally_counters.RxERR & 0xFF;
+            break;
+        case RX_ER+1:
+            ret = (s->tally_counters.RxERR >> 8) & 0xFF;
+            break;
+        case RxConfig:
+            ret = s->RxConfig & 0xFF;
+            break;
+        case RxConfig+1:
+            ret = (s->RxConfig >> 8) & 0xFF;
+            break;
+        case FIFOTMS:
+            ret = s->Fifo_TMS & 0xFF;
+            break;
+        case FIFOTMS+1:
+            ret = (s->Fifo_TMS >> 8) & 0xFF;
+            break;
+        case BasicModeCtrl:
+            ret = s->BasicModeCtrl & 0xFF;
+            break;
+        case BasicModeCtrl+1:
+            ret = (s->BasicModeCtrl >> 8) & 0xFF;
+            break;
         case MediaStatus:
             ret = 0xd0;
             DPRINTF("MediaStatus read 0x%x\n", ret);
@@ -3075,6 +3238,10 @@  static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr)
             ret = s->NWayExpansion;
             DPRINTF("NWayExpansion read(w) val=0x%04x\n", ret);
             break;
+        case FIFOTMS:
+            ret = s->Fifo_TMS;
+            DPRINTF("Fifo_TMS read(w) val=0x%04x\n", ret);
+            break;
 
         case CpCmd:
             ret = rtl8139_CpCmd_read(s);
@@ -3322,6 +3489,7 @@  static const VMStateDescription vmstate_rtl8139 = {
         VMSTATE_UINT16(NWayAdvert, RTL8139State),
         VMSTATE_UINT16(NWayLPAR, RTL8139State),
         VMSTATE_UINT16(NWayExpansion, RTL8139State),
+        VMSTATE_UINT16(Fifo_TMS, RTL8139State),
 
         VMSTATE_UINT16(CpCmd, RTL8139State),
         VMSTATE_UINT8(TxThresh, RTL8139State),
@@ -3452,6 +3620,8 @@  static int pci_rtl8139_init(PCIDevice *dev)
 {
     RTL8139State * s = DO_UPCAST(RTL8139State, dev, dev);
     uint8_t *pci_conf;
+    int i = 0;
+    uint16_t checksum = 0;
 
     pci_conf = s->dev.config;
     pci_conf[PCI_INTERRUPT_PIN] = 1;    /* interrupt pin A */
@@ -3473,10 +3643,47 @@  static int pci_rtl8139_init(PCIDevice *dev)
     s->eeprom.contents[1] = PCI_VENDOR_ID_REALTEK;
     s->eeprom.contents[2] = PCI_DEVICE_ID_REALTEK_8139;
 #endif
+
+    /* PCI subsystem vendor and device ID should be mirrored here */
+    s->eeprom.contents[3] = PCI_VENDOR_ID_REALTEK;
+    s->eeprom.contents[4] = PCI_DEVICE_ID_REALTEK_8139;
+    s->eeprom.contents[5] = 0x4020;
+    s->eeprom.contents[6] = 0xE110;
     s->eeprom.contents[7] = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8;
     s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8;
     s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
 
+    s->eeprom.contents[10] = 0x4D10;
+    s->eeprom.contents[11] = 0xF7C2;
+    s->eeprom.contents[12] = 0x8001;
+    s->eeprom.contents[13] = 0xB388;
+    s->eeprom.contents[14] = 0x58FA;
+    s->eeprom.contents[15] = 0x0708;
+    s->eeprom.contents[16] = 0xD843;
+    s->eeprom.contents[17] = 0xA438;
+    s->eeprom.contents[18] = 0xD843;
+    s->eeprom.contents[19] = 0xA438;
+    s->eeprom.contents[20] = 0xD843;
+    s->eeprom.contents[21] = 0xA438;
+    s->eeprom.contents[22] = 0xD843;
+    s->eeprom.contents[23] = 0xA438;
+
+    s->eeprom.contents[31] = 0x2000;
+
+    for (i = 0; i < 24; i++) checksum += s->eeprom.contents[i];
+    checksum = (~checksum + 1) & 0xFFFF;
+    DPRINTF("EEPROM checksum=0x%04X\n", checksum);
+    s->eeprom.contents[25] = checksum; /* Checksum */
+
+    DPRINTF("EEPROM contents\n");
+    for (i = 0; i < 64; i++) {
+#if 0
+        DPRINTF("0x%04X,%s", s->eeprom.contents[i], ((i+1)%8) == 0 ? "\n" : " ");
+#else
+        printf("0x%04X,%s", s->eeprom.contents[i], ((i+1)%8) == 0 ? "\n" : " ");
+#endif
+    }
+
     s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
     qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
@@ -3509,6 +3716,13 @@  static void rtl8139_class_init(ObjectClass *klass, void *data)
     k->romfile = "pxe-rtl8139.rom";
     k->vendor_id = PCI_VENDOR_ID_REALTEK;
     k->device_id = PCI_DEVICE_ID_REALTEK_8139;
+#ifndef ALTERNATE_EEPROM_CONTENTS
+    k->subsystem_vendor_id = PCI_VENDOR_ID_REALTEK;
+    k->subsystem_id = PCI_DEVICE_ID_REALTEK_8139;
+#else
+    k->subsystem_vendor_id = 0x10BD;
+    k->subsystem_id = 0x0320;
+#endif
     k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */
     k->class_id = PCI_CLASS_NETWORK_ETHERNET;
     dc->reset = rtl8139_reset;