/* hp100vg-diag.c: Diagnostic and setup program for the Lucent ATT2MD01.

   This is a diagnostic and EEPROM configuration program for 100VG network
   adapters using the Lucent (nee AT&T) ATT2MD01 chip.

   Instructions are at
	http://www.scyld.com/diag/index.html

   Rewritten 2003 by Donald Becker, to replace the lost previous
   diagnostic for this chip.
   Derived from previous Linux diagnostic code that were Copyright
   Donald Becker

	This version of the 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 includes subsets and other derivative works.

	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
	 914 Bay Ridge Road, Suite 220
	 Annapolis MD 21403

	Support and updates available at
	 http://www.scyld.com/diag/index.html

	References
	The AT&T ATT2MD01 datasheet dated Feb. 25, 1994

	Common-sense licensing statement: Using any portion of this program in
	your own program means that you must give credit to the original author
	and release the resulting code under the GPL.  To use this code under
	other terms requires an explicit license from the copyright holder.
*/

static const char cvs_id[] =
"$Id: hp100vg-diag.c,v 1.5 2005/03/22 04:48:01 becker Exp $";

static const char version_msg[] =
"hp100vg-diag.c:v1.05 3/21/2005 Donald Becker (becker@scyld.com)\n"
" http://www.scyld.com/diag/index.html\n";

static const char usage_msg[] =
"Usage: 100vg-diag [-aEfFsvVw] [-p <IOport>] [-Q <IRQ>]\n";

static const char long_usage_msg[] =
"Usage: %s [-p IOADDR] [-aDfrRvVw] <other-options>\n\
\n\
   Show the internal state of a network adapter.\n\
\n\
   The common usage is\n\
      hp100vg-diag -p 0x300\n\
\n\
 The following option is almost always required\n\
   -p  --port-base IOADDR	Find the adapter at the specified I/O address.\n\
\n\
 Frequently used options are\n\
   -a  --show_all_registers	Print all registers.\n\
   -e  --show-eeprom	Show the EEPROM contents, \"-eee\" shows the details.\n\
   -f  --force		Perform operations even on a running NIC.\n\
\n\
 To change the persistent configuration EEPROM settings\n\
   -P  --new-base-address IOPORT	Set the ISA base I/O address.  This board\n\
         requires 32 (0x20) contiguous locations between 0x200 and 0x3df.\n\
         Valid settings 0x200, 0x220 ...0x3c0.  0x300 or 0x2C0 are suggested.\n\
   -Q  --new-irq IRQ 	Valid IRQ settings are 3,4,5,7,9,10,11,12, or 15.\n\
   -H  --new-hwaddr 01:23:45:67:89:ab\n\
        Set a new hardware station address. (Do not use, never required.)\n\
   -w  --write-EEPROM\n\
	    Confirm that the new settings should be written into the EEPROM.\n\
\n\
   -D  --debug\n\
   -v  --verbose	Report each action taken.\n\
   -V  --version	Emit version information.\n\
\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 <getopt.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <ctype.h>

#if defined(__linux__)  &&  __GNU_LIBRARY__ == 1
#include <asm/io.h>			/* Newer libraries use <sys/io.h> instead. */
#else
#include <sys/io.h>
#endif

/* We should use __u8 .. __u32, but they are not always defined. */
typedef u_int32_t u32;
typedef u_int16_t u16;
typedef u_int8_t u8;

struct option longopts[] = {
 /* { name  has_arg  *flag  val } */
	{"base-address", 1, 0, 'p'},
	{"new-base-address", 1, 0, 'P'},
	{"show-all-registers",	0, 0, 'a'},	/* Print all registers. */
	{"debug",			0, 0, 'D'},
	{"show-eeprom",		0, 0, 'e'}, /* Dump EEPROM contents (-ee valid). */
	{"emergency-rewrite",  0, 0, 'E'}, /* Re-write a corrupted EEPROM.  */
	{"help",			0, 0, 'h'},	/* Give help */
	{"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. */
	{"new-IOaddress",	1, 0, 'P'},	/* New base I/O address. */
	{"new-irq",	1, 0, 'Q'},		/* New interrupt number */
	{"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 }
};

/* Offsets from base I/O address. */
enum register_offsets {
	HW_ID_1=0, HW_ID_2=2, OptionReg1=4, OptionReg2=6,
};

#define VG_PAGE 0x02			/* Chip ID and paging register  */

/* The chip uses windowed access to eight register banks.
   The documentation refers to these as "swap page banks".
   The power-up window is 6, the operational window is 0.
*/
/* Register window 0 offsets, the window used in normal operation. */
enum Window0 {
	Wn0IntrStatus=0x08, Wn0IntrMask=0x0A, Wn0FragCnt=0x0C,
};
enum Window3 {
	Wn3EepromCmd = 0x08,
};

const char *intr_names[12] ={
	"", "", "Tx Complete", "Tx Available", "Tx PDA Zero", "", "", "",
	"", "Rx Error", "Rx Complete", "Rx PDL Filled",
};

/* Values read from the EEPROM, and the new image. */
#define EEPROM_SPACE 256
unsigned short eeprom_contents[EEPROM_SPACE];
unsigned short new_ee_contents[EEPROM_SPACE];

int verbose = 1, opt_f = 0, debug = 0;
int show_regs = 0, show_eeprom = 0;
int do_write_eeprom = 0;
int ioaddr;

static int new_irq = 0, new_ioaddr = 0; 
static unsigned char new_hwaddr[6], set_hwaddr = 0;
static int emergency_rewrite = 0;

const char *intrs_pending_msg = 
" This network adapter has unhandled interrupts!\n"
" This should never occur with properly configured adapter.  You may have\n"
"   a hardware interrupt conflict.\n"
" Check /proc/interrupts to verify that the interrupt is properly\n"
"   registered, and that the count is increasing.\n"
" This problem is frequently solved by moving the adapter to different IRQ.\n"
" For ISA cards, verify that the BIOS setup has assigned the IRQ line to\n"
"  ISA bus.\n"
" For PCMCIA cards, change the configuration file, typically named\n"
"  /etc/pcmcia/config.opts, to use a different IRQ.\n";

#if 0
static void print_eeprom(unsigned short *eeprom_contents);
static void write_eeprom(short ioaddr, int index, int value);
static unsigned int calculate_checksum(unsigned short *values);
static int do_update(unsigned short *ee_values,
					 int index, char *field_name, int new_value);
#endif
static void handle_eeprom(long ioaddr);
static int  vg_show_status(long ioaddr);


int
main(int argc, char **argv)
{
	int port_base = 0x300;
	int new_interface = -1;
	int errflag = 0, show_version = 0;
	int show_regs = 0, opt_a = 0;
	int c, longind, saved_window;
	extern char *optarg;

	while ((c = getopt_long(argc, argv, "aDeEfF:hH:i:p:P:Q:svVwX:",
							longopts, &longind))
		   != -1)
		switch (c) {
		case 'a': show_regs++; opt_a++;		break;
		case 'D': debug++;				break;
		case 'e': show_eeprom++;		break;
		case 'E': emergency_rewrite++;	break;
		case 'f': opt_f++;				break;
		case 'F': case 'X':
			if (strncmp(optarg, "10base", 6) == 0) {
				switch (optarg[6]) {
				case 'T':  new_interface = 0; break;
				case '2':  new_interface = 3; break;
				case '5':  new_interface = 1; break;
				default: errflag++;
				}
			} else if (strcmp(optarg, "AUI") == 0)
				new_interface = 1;
			else if (optarg[0] >= '0' &&  optarg[0] <= '3'
					   &&  optarg[1] == 0)
				new_interface = optarg[0] - '0';
			else {
				fprintf(stderr, "Invalid interface specified: it must be"
						" 0..3, '10base{T,2,5}' or 'AUI'.\n");
				errflag++;
			}
			break;
		case 'h': fprintf(stderr, long_usage_msg, argv[0]); return 0;
		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 'Q': {
			char *endptr;
			new_irq = strtoul(optarg, &endptr, 0);
			if (*endptr) {
				fprintf(stderr, "Invalid new IRQ number '%s'.\n", optarg);
				errflag++;
			} else if (new_irq < 3 || new_irq > 15 || new_irq == 8 ||
					   new_irq == 13 || new_irq == 14) {
				fprintf(stderr, "Invalid new IRQ %#x.  Valid values: "
						"3-5,7,9-12,15.\n", new_irq);
				errflag++;
			}
			break;
		}
		case 'p':
			port_base = strtol(optarg, NULL, 16);
			break;
		case 'P': {
			char *endptr;
			new_ioaddr = strtoul(optarg, &endptr, 16);
			if (*endptr) {
				fprintf(stderr, "Invalid new I/O port '%s'.\n", optarg);
				errflag++;
			} else if (new_ioaddr & ~0x3e0) {
				fprintf(stderr, "Invalid new I/O address %#x.  Valid settings "
						"0x200,0x220,0x240,...,0x3c0.\n", new_ioaddr);
				errflag++;
			}
			break;
		}
		case 'v': verbose++;		break;
		case 'V': show_version++;	break;
		case 'w': do_write_eeprom++;	break;
		case '?':
			errflag++;
		}
	if (errflag) {
		fprintf(stderr, usage_msg);
		return 3;
	}

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

	if (ioperm(port_base, 32, 1) < 0) {
		perror("hp100vg-diag: ioperm()");
		fprintf(stderr, "This program must be run as root.\n");
		return 2;
	}

	ioaddr = port_base;

	saved_window = inw(ioaddr + VG_PAGE);
	if (saved_window == 0xffff) {
		printf("No device exists at address 0x%X.\n", ioaddr);
		if ( ! opt_f) {
			printf("Use the '-f' option proceed anyway.\n");
			return 2;
		}
	}

	if (inw(ioaddr + HW_ID_1) != 0x4850) {
		printf("The device at 0x%X does not appear to be a 100VG NIC "
			   "(ID 0x%4.4x).\n",
			   ioaddr, inw(ioaddr + HW_ID_1));
		if ( ! opt_f) {
			printf("Use the '-f' option proceed anyway.\n");
			return 2;
		}
	}

	if (!opt_f  &&
		(saved_window & 0x000f) == 0 && inw(ioaddr + Wn0IntrMask) != 0) {
		printf("A potential 100VG chip has been found, but it appears to "
			   "be active.\nOnly limited information is available without "
			   "disturbing network operation.\n"
			   " Either shutdown the network, or use the '-f' flag to see all "
			   "registers.\n");
		return 1;
	}

	vg_show_status(ioaddr);
	handle_eeprom(ioaddr);
	outw(saved_window, ioaddr + VG_PAGE);

	return 0;
}

static const char *window_name[8] = {
	"Performance", "MAC address and multicast hash", "Hardware mapping",
	"EEPROM/Boot ROM configuration", "LAN config and status",
	"MMU config and status", "Card ID", "MMU current pointers",
};

static int vg_show_status(long ioaddr)
{
	int id2 = inw(ioaddr + HW_ID_2);
	const char *id_str = 0;
	int i;

	switch(id2 & 0xfff0){
	case 0x5350: id_str = "ATT2MD01 chip rev. A"; break;
	case 0x4360: id_str = "ATT2MD01 chip rev. B"; break;
	default: id_str = "ATT2MD01 unknown chip revision"; break;
	}
	printf("Base registers of %s at I/O port 0x%lX:\n ", id_str, ioaddr);
	for (i = 0; i < 8; i+=2) {
		printf(" %4.4x", inw(ioaddr + i));
	}
	printf(".\n Current window %d: %s", id2 & 7, window_name[id2 & 7]);
	for (i = 0; i < 8; i++) {
		int j;
		outw(i, ioaddr + VG_PAGE);
		printf(".\n  Window %d:", i);
		for (j = 8; j < 32; j+=2) {
			printf(" %4.4x", inw(ioaddr + j));
		}
	}
	printf(".\n");

	/* Describe the chip's status. */
	/* Window 1 station address. */
	outw(1, ioaddr + VG_PAGE);
	printf(" Current MAC station address ");
	for (i = 0; i < 5; i++)
		printf("%2.2x:", inb(ioaddr + 8 + i));
	printf("%2.2x.\n", inb(ioaddr + 8 + i));
	printf(" Multicast hash filter table ");
	for (i = 0x10; i < 0x18; i++)
		printf(" %2.2x", inb(ioaddr + i));
	printf(".\n");
	/* Window 2 hardware mapping. */
	outw(2, ioaddr + VG_PAGE);
	printf(" ISA I/O location (%2.2x) 0x%3.3X,  IRQ %d%s.\n",
		   inb(ioaddr + 12), inb(ioaddr + 12) << 2, inb(ioaddr + 13) & 15,
		   inb(ioaddr + 13) & 0x10 ? " (EISA level-triggered)" : "");
	outw(6, ioaddr + VG_PAGE);
	printf(" EISA ID %4.4X %4.4X\n", inw(ioaddr + 8), inw(ioaddr + 10));
	printf(" Board ID (suggested MAC station address) ");
	for (i = 0; i < 5; i++)
		printf("%2.2x:", inb(ioaddr + 16 + i));
	printf("%2.2x.\n", inb(ioaddr + 16 + i));

	return 0;
}

/* Serial EEPROM section.
   A "bit" grungy, but we work our way through bit-by-bit :->. */

/* The EEPROM commands always start with 01.. preamble bits.
   Commands are prepended to the variable-length address. */
enum EEPROM_Cmds { EE_WriteCmd=5, EE_ReadCmd=6, EE_EraseCmd=7, };

/*  Chip-specific EEPROM_Ctrl bits.
    Some implementations have a data pin direction bit instead of
    separate data in and out bits.
	Note that EE_DataIn is the input to the NIC, EE_DataOut is output to
	the EEPROM.
*/
#define EE_OFFSET		Wn3EepromCmd	/* Register offset in I/O space. */
enum EEPROM_Ctrl_Bits {
	EE_ShiftClk=0x10, EE_ChipSelect=0x02, EE_DataOut=0x04, EE_DataIn=0x08,
	EE_Write0=0x02, EE_Write1=0x06,
};
#define eeprom_in(ee_addr)		inw(ee_addr)
#define eeprom_out(data, ee_addr)	outw(data, ee_addr)
/* Delay between EEPROM clock transitions.
   This is not needed for ISA bus transactions.
*/
#define eeprom_delay(ee_addr)	eeprom_in(ee_addr)

/* Execute a generic EEPROM command.
   Return all data output from the EEPROM, and thus may be used for
   EEPROM sizing, read, erase or write. */
static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len)
{
	unsigned retval = 0;
	long ee_addr = ioaddr + EE_OFFSET;

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

	eeprom_out(EE_ChipSelect, ee_addr);
	eeprom_out(EE_ChipSelect | EE_ShiftClk, ee_addr);

	/* Shift the command bits out. */
	do {
		short dataval = (cmd & (1 << cmd_len)) ? EE_Write1 : EE_Write0;
		eeprom_out(dataval, ee_addr);
		eeprom_delay(ee_addr);
		if (debug > 2)
			printf("%X", eeprom_in(ee_addr) & 15);
		eeprom_out(dataval | EE_ShiftClk, ee_addr);
		eeprom_delay(ee_addr);
		retval = (retval << 1) | ((eeprom_in(ee_addr) & EE_DataIn) ? 1 : 0);
	} while (--cmd_len >= 0);
	eeprom_out(EE_ChipSelect, ee_addr);

	/* Terminate the EEPROM access. */
	eeprom_out(0, ee_addr);
	/* Special for 100VG chip: disable EEPROM accesses. */
	outw(0x0000, ioaddr + OptionReg1);
	if (debug > 1)
		printf(" EEPROM result is 0x%5.5x.\n", retval);

	return retval;
}

/* Wait for the EEPROM to finish what it is doing. */
static int eeprom_busy_poll(long ee_ioaddr)
{
	int i;
	eeprom_out(EE_ChipSelect, ee_ioaddr);
	for (i = 0; i < 10000; i++)			/* Typical 2000 ticks */
		if (eeprom_in(ee_ioaddr) & EE_DataIn)
			break;
	return i;
}

/* The abstracted functions for EEPROM access. */
/* Return the number of address bits this EEPROM accepts. */
static int eeprom_addr_len(long ioaddr)
{
	return do_eeprom_cmd(ioaddr, (EE_ReadCmd << (6+16)) + 0x40000, 3 + 6 + 16)
		& 0x10000 ? 8 : 6;
}

static int read_eeprom(long ioaddr, int location, int addr_len)
{
	return do_eeprom_cmd(ioaddr, ((EE_ReadCmd << addr_len) | location) << 16,
						 3 + addr_len + 16) & 0xffff;
}

static void write_eeprom(long ioaddr, int index, int value, int addr_len)
{
	long ee_ioaddr = ioaddr + EE_OFFSET;
	int i;

	/* Poll for previous op finished. */
	eeprom_busy_poll(ee_ioaddr);

	/* Enable programming modes. */
	do_eeprom_cmd(ioaddr, (0x4f << (addr_len-4)), 3 + addr_len);
	/* Do the actual write. */ 
	do_eeprom_cmd(ioaddr,
				  (((EE_WriteCmd<<addr_len) | index) << 16) | (value & 0xffff),
				  3 + addr_len + 16);
	i = eeprom_busy_poll(ee_ioaddr);
	if (debug)
		printf(" Write finished after %d ticks.\n", i);
	/* Disable programming.  Note: this command is not instantaneous, so
	   we check for busy before the next op. */
	do_eeprom_cmd(ioaddr, (0x40 << (addr_len-4)), 3 + addr_len);
	eeprom_busy_poll(ee_ioaddr);
}

/* The board-specific configuration EEPROM actions. */
/* Where the station address is located in the EEPROM. */
#define EEPROM_SA_OFFSET 3

static void interpret_eeprom(unsigned short *eeprom_contents, int eeprom_size);

static void handle_eeprom(long ioaddr)
{
	int ee_addr_len = 6, eeprom_size = 64;
	int saved_opt1 = inw(ioaddr + OptionReg1);
	int change_eeprom = 0;
	int i;

	if (set_hwaddr || new_irq || new_ioaddr || emergency_rewrite)
		change_eeprom = 1;
	if (! show_eeprom  &&  ! change_eeprom)
		return;

	/* Switch to window 3 for EEPROM. */
	outw(3, ioaddr + VG_PAGE);
	/* Enable EEPROM accesses and suspend chip operation. */
	outw(0x1102, ioaddr + OptionReg1);

	ee_addr_len = eeprom_addr_len(ioaddr);
	eeprom_size = 1 << ee_addr_len;
	printf(" EEPROM address length %d, %d words.\n",
		   ee_addr_len, eeprom_size);

	/* Read the whole EEPROM and make a copy. */
	for (i = 0; i < eeprom_size; i++)
		eeprom_contents[i] = read_eeprom(ioaddr, i, ee_addr_len);
	memcpy(new_ee_contents, eeprom_contents, eeprom_size << 1);

	interpret_eeprom(eeprom_contents, eeprom_size);

	if (emergency_rewrite  &&  ! set_hwaddr)
		printf("*** Emergency EEPROM rewrite is only valid when you also "
			   "specify a new\n*** station address with -H <xx:xx:...>\n");

	if (set_hwaddr) {
		u16 backup_ee_contents[] = {
			0xe060, 0x0020, 0xffff, 0x0008, 0x5c09, 0x5048, 0x00fa, 0x0000,
			0x000d, 0x6cb0, 0x80c0, 0xffff, 0xf022, 0x5019, 0x0084, 0xffff,
		};
		if (emergency_rewrite)
			memcpy(new_ee_contents, backup_ee_contents,
				   sizeof backup_ee_contents);
		for (i = 0; i < 3; i++)
			new_ee_contents[i + EEPROM_SA_OFFSET] =
				(new_hwaddr[i*2+1]<<8) + new_hwaddr[i*2];
		change_eeprom++;
	}
	if (new_irq) {
		new_ee_contents[9] &= 0xf0ff;
		new_ee_contents[9] |= new_irq << 8;
		change_eeprom++;
	}
	if (new_ioaddr) {
		new_ee_contents[9] &= 0xff00;
		new_ee_contents[9] |= (new_ioaddr >> 2) & 0xff;
		change_eeprom++;
	}
	if (change_eeprom) {
		/* First recalculate the EEPROM checksums in order. */
		u16 sum = 0xffff;
		u8 macsum = 0xff;	/* Just the station address. */
		for (i = 3; i < 6; i++)
			macsum -= new_ee_contents[i] + (new_ee_contents[i] >> 8);
		new_ee_contents[6] = macsum;
		for (i = 0; i < 0x1f; i++)
			sum -= new_ee_contents[i];
		new_ee_contents[0x1f] = sum;
		for (i = 0; i < eeprom_size; i++)
			if (new_ee_contents[i] != eeprom_contents[i]) {
				if (do_write_eeprom) {
					if (verbose)
						printf(" !!! Writing %4.4x to replace %4.4x at %d.\n",
							   new_ee_contents[i], eeprom_contents[i], i);
					write_eeprom(ioaddr, i, new_ee_contents[i],
								 ee_addr_len);
				} else
					printf("Would write %4.4x to replace %4.4x at %d.\n",
						   new_ee_contents[i], eeprom_contents[i], i);
			}
	}

	/* Disable EEPROM accesses. */
	outw(0x1000, ioaddr + OptionReg1);
	/* Restore settings. Note unusual enable/level semantics. */
	outw((~saved_opt1) & 0xeefe, ioaddr + OptionReg1);
	return;
}

/* This should be updated to show either the current chip register state
 *  or what the EEPROM programming would set in the same format.
 *
 * Missing fields from output:
 *  0  Boot ROM control
 *  1,2  Option registers 1 and 2
 *  6  MAC checksum ???
 *   9  Paging register (???)
 *  10  10Mbps LAN control register
 *  11  100Mbps LAN control register
 *  14  Unused, restored with EISA ID registers.
 *
 */
static void interpret_eeprom(unsigned short *eeprom_contents, int eeprom_size)
{
	u16 sum = 0xffff;
	u8 macsum = 0xff;	/* Just the station address. */
	int i;

	if (show_eeprom > 1) {
		printf("EEPROM contents (%d words):", eeprom_size);
		for (i = 0; i < eeprom_size; i += 8) {
			int j;
			printf("\n0x%2.2x: ", i);
			for (j = 0; j < 8; j++)
				printf(" %4.4x", eeprom_contents[i + j]);
			if (show_eeprom > 2) {
				printf("  ");
				for (j = 0; j < 8; j++) {
					int ew = eeprom_contents[i + j];
					printf("%c%c",
						   isprint(ew & 0xff) ? ew & 0xff : '_',
						   isprint(ew >>   8) ? ew >> 8   : '_' );
				}
			}
		}
		printf("\n");
	}
	printf("EEPROM settings:\n"
		   "  Station address %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X.\n"
		   "  ISA I/O 0x%3.3x,  IRQ %d.\n"
		   "  Memory map 0x%x%4.4x.\n"
		   "  EISA ID %4.4X %4.4X, EISA info %4.4X.\n"
		   "  SRAM parameters %4.4x, Bus-master settings %4.4x.\n",
		   eeprom_contents[3] & 0xff, eeprom_contents[3] >> 8,
		   eeprom_contents[4] & 0xff, eeprom_contents[4] >> 8,
		   eeprom_contents[5] & 0xff, eeprom_contents[5] >> 8,
		   (eeprom_contents[9] << 2) & 0x3e0, (eeprom_contents[9] >> 8) & 0x0f,
		   eeprom_contents[8], eeprom_contents[7],
		   eeprom_contents[12], eeprom_contents[13], eeprom_contents[14],
		   eeprom_contents[15], eeprom_contents[16]);
	/* EEPROM checksum calculations. */
	for (i = 0; i < 0x1f; i++)
		sum -= eeprom_contents[i];
	for (i = 3; i < 6; i++)
		macsum -= eeprom_contents[i] + (eeprom_contents[i] >> 8);
	printf("  Computed MAC checksum of 0x%2.2x %s the stored"
		   " value 0x%2.2x.\n",
		   macsum, macsum == eeprom_contents[6] ? "matches":"does not match",
		   eeprom_contents[6]);
	printf("  Computed region checksum of 0x%4.4x %s the stored"
		   " value 0x%4.4x.\n",
		   sum, sum == eeprom_contents[0x1f] ? "matches":"does not match",
		   eeprom_contents[0x1f]);
	if (show_eeprom < 2)
		printf("    Use -ee or -eee to see the full EEPROM settings.\n");

	return;
}


/*
 * Local variables:
 *  compile-command: "cc -O -Wall -o hp100vg-diag hp100vg-diag.c"
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 * End:
 */
