/*
 * Copyright (c) 2002-2005 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part of
 * it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dagld.c 13453 2010-12-02 23:30:08Z jomi.gregory $
 */

/* Endace headers. */
#include "dagapi.h"
#include "dagclarg.h"
#include "dagimg.h"
#include "dagname.h"
#include "dagutil.h"
#include "dag_config_api.h"

/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagld.c 13453 2010-12-02 23:30:08Z jomi.gregory $";
static const char* const kRevisionString = "$Revision: 13453 $";


static char dagname_buf[DAGNAME_BUFSIZE] = "dag0";
static char dagname[DAGNAME_BUFSIZE];
static int dagstream;
static int dagfd;
static int dagarm;

static uint32_t startaddr = 0x8000;
static daginf_t *daginf;
static uint8_t *iom;
static int img_offset = 0;
static int copro_id = -1;
static int force = 0;
static int unknown = 0;


/* Internal routines. */
static void print_version(void);
static void print_usage(ClArgPtr clarg);
static void	dagldx(int argc, char *argv[]);
static void	dagrun(int argc, char *argv[]);
static void	report(char *label, int n);
static void	dagld(char *image);
static int	dagld_image(char *image, int len);
static void	dagarmwrite(unsigned addr, unsigned val);
static void	dagnoarmx(int argc, char *argv[]);
static unsigned	dagrreg(unsigned addr);
static void	dagwreg(unsigned addr, unsigned value);
static void phy_write(int phy_reg, uint32_t val);
static int phy_read(int phy_reg);

enum {
	CMD_NONE,
	CMD_XILINX,
	CMD_RUN
};


#define	XBUFSIZE 128
#define TIMEOUT_MAX 10000

static void
print_version(void)
{
	printf("dagld (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}


static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagld - Endace DAG Loader utility.\n");
	printf("Usage: dagld [options]\n");
	dagclarg_display_usage(clarg, stdout);
}


/* Commandline argument codes. */
enum
{
	CLA_DEVICE,
	CLA_FORCE,
	CLA_HELP,
	CLA_STRONGARM,
	CLA_UNKNOWN,
	CLA_VERBOSE,
	CLA_VERSION,
	CLA_XILINX
};


int
dagld_main(int argc, char *argv[])
{
	ClArgPtr clarg;
	FILE* errorfile = NULL;
	int index;
	int code;

	int clarg_result;
	int argindex;
	int	cmd = CMD_NONE;
	dag_reg_t result[DAG_REG_MAX_ENTRIES];
	uint32_t	coprodet_base;
	int results;
	uint32_t val;
	
	dagutil_set_progname("dagld");

	/* Set up default DAG device. */
	if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
	{
		dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
	}

	/* Set up the command line options. */
	clarg = dagclarg_init(argc, (const char * const *) argv);

	dagclarg_add_string(clarg, "DAG device to use.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add(clarg, "File contains Xilinx FPGA image.", "--xilinx", 'x', CLA_XILINX);
	dagclarg_add(clarg, "Load and run StrongARM binary (DAG 3.5S).", "--strongarm", 'r', CLA_STRONGARM);
	
	dagclarg_add(clarg, "This page.", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option(clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option(clarg, CLA_HELP, '?');

	dagclarg_add(clarg, "Increase verbosity.", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add(clarg, "Display version information.", "--version", 'V', CLA_VERSION);
	dagclarg_add(clarg, "Force loading firmware.  Dangerous.", "--force", 0, CLA_FORCE);
	dagclarg_add(clarg, "Force loading firmware.  Dangerous.", "--unknown", 0, CLA_UNKNOWN);

	/* Parse the command line options. */
	clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
		case CLA_DEVICE:
			if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
			{
				dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
			}
			dagutil_verbose("device=%s\n", dagname);
			break;

		case CLA_FORCE:
			force = 1;
			break;

		case CLA_HELP:
			print_usage(clarg);
			return EXIT_SUCCESS;
			break;

		case CLA_STRONGARM:
			if (cmd != CMD_NONE)
			{
				dagutil_panic("conflicting options\n");
			}
			cmd = CMD_RUN;
			break;

		case CLA_UNKNOWN:
			unknown = 1;
			break;

		case CLA_VERBOSE:
			dagutil_inc_verbosity();
			errorfile = stderr;
			break;

		case CLA_VERSION:
			print_version();
			return EXIT_SUCCESS;
			break;

		case CLA_XILINX:
			if (cmd != CMD_NONE)
			{
				dagutil_panic("conflicting options\n");
			}
			cmd = CMD_XILINX;
			break;

		default:
			if (argv[argindex][0] == '-')
			{
				/* Unknown option. */
				dagutil_error("unknown option %s\n", argv[argindex]);
				print_usage(clarg);
			    return EXIT_FAILURE;
			}
			break;
		}

		clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}

	if (-1 == clarg_result)
	{
		/* Error occurred. */
		if (argindex < argc)
		{
			dagutil_error("while processing option %s\n", argv[argindex]); 
		}
		dagclarg_display_usage(clarg, stderr);
		return EXIT_FAILURE;
	}
	
    /* Display unprocessed arguments if verbose flag was given. */
	argv = (char**) dagclarg_get_unprocessed_args(clarg, &argc);
	if ((NULL != argv) && (0 < argc) && (0 < dagutil_get_verbosity()))
	{
		for (index = 0; index < argc; index++)
		{
			dagutil_verbose("unprocessed argument: '%s'\n", argv[index]);
		}
	}

	dagfd = dag_open(dagname);
	if (dagfd < 0)
	{
		dagutil_panic("cannot open %s: %s\n", dagname, strerror(errno));
	}

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
	dagarm = dag_clone(dagfd, DAGMINOR_ARM);
#endif

	daginf = dag_info(dagfd);
	iom = dag_iom(dagfd);

	/* due to hardware bug, reset AMCC. this only for 7.1S */	
	if ( daginf->device_code == PCI_DEVICE_ID_DAG7_10 ) {

		/* Set the electric modes */
    		val = phy_read(0x01);
    		val |= (BIT1|BIT3|BIT6);
    		val &= ~(BIT2|BIT5|BIT7);
    		phy_write(0x01, val);	
		
		/* Set the REFCLK to 155.52MHz */
    		val = phy_read(0x00);
    		val |= (BIT0 | BIT1);
    		phy_write(0x00, val);

		 /* Turn OFF the SER/DES Cores */
    		val = phy_read(0x02);
    		val |= BIT0;
    		phy_write(0x02, val);

    		for (index = 0; index < 4; index++)
    		{
        		val = phy_read(0x08 + (index*2));
        		val |= BIT0;
        		phy_write(0x08 + (index*2), val);
    		}
		
		/* Turn ON the SER/DES Cores */
    		val = phy_read(0x02);
    		val &= ~BIT0;
    		phy_write(0x02, val);

    		for (index = 0; index < 4; index++)
    		{
        		val = phy_read(0x08 + (index*2));
        		val &= ~BIT0;
        		phy_write(0x08 + (index*2), val);
    		}

    		/* Turn ON the Transmit Clocks */
    		for (index = 0; index < 4; index++)
    		{
        		val = phy_read(0x08 + (index*2));
        		val &= ~BIT3;
        		phy_write(0x08 + (index*2), val);
    		}

		val = phy_read(0x00);
		val |= (BIT7 | BIT5 | BIT4 | BIT3);
       		phy_write(0x00, val);
		
		val = phy_read( 0x00);
		val &= (~BIT4 & ~BIT5);	
		phy_write(0x0, val);
		
	}

	/* If one or more ROM programming blocks are present,
	 * assume that FPGA index zero is programmed via a ROM.
	 * Increment img_offset by 1 to compensate.
	 */
	results = dag_reg_find((char*) iom, DAG_REG_ROM, result);
	if (results < 0)
		dagutil_panic("Internal error line %d\n", __LINE__);
	else if (results > 0)
		img_offset = 1;

	/* img_offset should be zero for cards without a DAG_REG_ROM,
	 * e.g. DAG 3.5S, should be 1 for cards with one or more
	 * DAG_REG_ROMs, e.g. DAG 3.8S, DAG 7.1S */
	dagutil_verbose("img_offset=%d\n", img_offset); /* XXX */

	/* Detect copro support and type if fitted. */
	/* Find COPRO_DETECT. */
	if (dag_reg_find((char*) iom, DAG_REG_COPRO, result) < 1)
	{
		copro_id = -1;
	}
	else
	{
		coprodet_base = DAG_REG_ADDR(*result);
	
		copro_id = *(volatile unsigned *)(iom + coprodet_base);
		if ((copro_id & 0xff00) == 0xff00)
		{
			/* no support */
			copro_id = -1;
		}
		else
		{
			int timeout = TIMEOUT_MAX;
			
			/* read copro_id */
			*(volatile unsigned *)(iom + coprodet_base) = 0;
			while (0 == (*(volatile unsigned *)(iom + coprodet_base) & BIT31))
			{
				usleep(1000);
				timeout--;
				if (0 == timeout)
				{
					dagutil_panic("timeout reading Coprocessor ID.");
				}
			}
			
			/* Read result of type probe */
			copro_id = (*(volatile unsigned *)(iom + coprodet_base)) & 0xff;
		}
	}
	dagutil_verbose("Coprocessor ID = %d\n", copro_id); // XXX

	if (dag_reg_find((char*) iom, DAG_REG_ARM, result))
	{
		/* ARM present */
		switch (cmd)
		{
		case CMD_NONE:
			if (argc != 0)
				dagld(argv[0]);		/* nothing else makes sense */
			break;

		case CMD_XILINX:
			dagldx(argc, argv);
			break;

		case CMD_RUN:
			dagrun(argc, argv);	/* probably only needs one arg */
			break;

		default:
			dagutil_panic("internal error at %s line %u\n", __FILE__, __LINE__);
			break;
		}
	}
	else
	{
		/* No ARM */
		switch (cmd)
		{
		case CMD_NONE:
		case CMD_RUN:
			dagutil_panic("This DAG only supports -x option for FPGA loading\n");
			break;

		case CMD_XILINX:
			dagnoarmx(argc, argv);
			break;

		default:
			dagutil_panic("internal error at %s line %u\n", __FILE__, __LINE__);
			break;
		}
	}
	
	return EXIT_SUCCESS;
}

static void
dagldx(int argc, char *argv[])
{
	uint32_t words[16];
	int i;
	int xlxno = 0;
	int img_idx;
	char *fname = NULL;
	char* remain_state = NULL;
	char *remain;
	uint8_t *p;
	uint32_t len;
	char *flags = "r";


#if defined(_WIN32)
	ULONG   BytesTransfered;
	flags = "rb";
#endif /* _WIN32 */

	for (i = 0 ; i < argc ; i++)
	{
		remain = argv[i];
		for (fname = strtok_r(remain, ":", &remain_state);
			fname != NULL ;
		    fname = strtok_r(NULL, ":", &remain_state), xlxno++)
		{
			if (fname[0] == '\0')
				continue;
			
			p = dagutil_readfile(fname, flags, &len);
			if (p == NULL)
				dagutil_panic("dagutil_readfile %s: %s\n", fname, strerror(errno));
			
			/* Check image is correct for location */
			/* find location */
			img_idx = dag_get_img_idx(daginf->device_code, img_offset+xlxno);
			dagutil_verbose("dag%d dev 0x%04x pos %d got idx %d\n",
					daginf->id, daginf->device_code, img_offset+xlxno, img_idx); // XXX
			/* check against image */
			if (img_idx < 0)
			{
				if (unknown)
					fprintf(stderr, "Warning, forced loading on unknown hardware!\n");
				else
					dagutil_panic("Unknown hardware, not loading!\nOverride with --unknown only if you are sure, damage may occur.\n");
			}
			else
			{
				switch (dag_check_img_ptr(img_idx, copro_id, (char*) p, len, daginf->brd_rev)) {
				case 0:
					break;
				case 1:
					if (force) {
						fprintf(stderr, "Warning, forcing load to non-matching image type!\n");
						break;
					}
					else
						dagutil_panic("*** IMAGE TYPE MISMATCH, not loading! ***\nEnsure you are loading the correct firmware for this device.\nOverride with --force only if you are sure, damage may occur.\n");
				case 2:
					dagutil_panic("*** DEVICE MISMATCH, not loading! ***\nEnsure you are loading the correct firmware for this device.\n");
				default:
					dagutil_panic("internal error line %d", __LINE__);
				}
			}
			if (dagld_image((char*) p, len))
				dagutil_panic("write %s to %s: %s\n", fname, dagname, strerror(errno));
			

			report("before:\t", xlxno);

			// XXX dagld(fname);

			words[0] = DAGMON_PROGRAM_XILINX;
			words[1] = startaddr;
			words[2] = xlxno;	/* which Xilinx */

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

			if (ioctl(dagfd, DAGIOCMONITOR, &words) < 0)
				dagutil_panic("ioctl dag DAGIOCMONITOR: %s\n", strerror(errno));

#elif defined(_WIN32)

			if (DeviceIoControl(dag_gethandle(dagfd),
				IOCTL_MONITOR,
				words,
				sizeof(words),
				words,
				sizeof(words),
				&BytesTransfered,
				NULL) == FALSE)
				dagutil_panic("DeviceIoControl dag DAGIOCMONITOR: %s\n", strerror(errno));

#endif /* _WIN32 */

			report("after:\t", xlxno);
			printf("\n");
		}
	}
}

/*
 * Infoblock for handshake with ARM code
 */
enum {
	DATA_HOLE_BASE	= 0x3100,
	DATA_HOLE_TOP	= 0x3104
};

static void
dagrun(int argc, char *argv[])
{
	uint32_t words[16];
#if defined(_WIN32)
   ULONG   BytesTransfered;
#endif /* _WIN32 */

	/*
	 * This is a standard handshake for ARM code
	 */
	dagarmwrite(DATA_HOLE_BASE, daginf->phy_addr);
	dagarmwrite(DATA_HOLE_TOP,  daginf->phy_addr+daginf->buf_size);

	dagld(argv[0]);

	words[0] = DAGMON_ENTER_ADDRESS;
	words[1] = startaddr;

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	if (ioctl(dagfd, DAGIOCMONITOR, &words) < 0)
		dagutil_panic("ioctl dag DAGIOCMONITOR: %s\n", strerror(errno));

#elif defined(_WIN32)

	if (DeviceIoControl(dag_gethandle(dagfd),
		IOCTL_MONITOR,
		words,
		sizeof(words),
		words,
		sizeof(words),
		&BytesTransfered,
		NULL) == FALSE)
		dagutil_panic("DeviceIoControl dag DAGIOCMONITOR: %s\n", strerror(errno));

#endif /* Platform-specific code. */
}

/*
 * XXX needs update on how many Xilinxen there are
 */
static void
report(char *label, int n)
{
	off_t off = (off_t) 0x3800;
	char buffer[128];
#if defined(_WIN32)
   ULONG   BytesTransfered;
#endif /* _WIN32 */

   off += n*0x80;

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	if (lseek(dagarm, off, SEEK_SET) == (off_t)-1)
		dagutil_panic("lseek 0x%lx %s: %s\n", (long)off, dagname, strerror(errno));

	if (read(dagarm, buffer, sizeof(buffer)) < 0)
		dagutil_panic("read %s: %s\n", dagname, strerror(errno));

#elif defined(_WIN32)

   if (DeviceIoControl(dag_gethandle(dagfd),
      IOCTL_ARM_READ,
      &off,
      sizeof(off),
      buffer,
      sizeof(buffer),
      &BytesTransfered,
      NULL) == FALSE)
      dagutil_panic("DeviceIoControl %s\n", dagname);

#endif /* Platform-specific code. */

	printf("Xilinx%d\t%s%s\n", n, label, dag_xrev_name((uint8_t*) buffer, sizeof(buffer)));
}

static void
dagld(char *filename)
{
	uint8_t *p;
	uint32_t len;
	char *flags = "r";

#if defined(_WIN32)
	flags = "rb";
#endif /* _WIN32 */

	p = dagutil_readfile(filename, flags, &len);
	if (p == NULL)
		dagutil_panic("dagutil_readfile %s: %s\n", filename, strerror(errno));

	if (dagld_image((char*) p, len))
		dagutil_panic("write %s to %s: %s\n", filename, dagname, strerror(errno));
		
}

static int
dagld_image(char *image, int len)
{

#if defined(_WIN32)
	ULONG   BytesTransfered;
#endif /* _WIN32 */

	/*
	 * Rounded to a multiple of four
	 */
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	if (lseek(dagarm, (off_t)startaddr, SEEK_SET) == (off_t)-1)
		dagutil_panic("lseek %u %s: %s\n", startaddr, dagname, strerror(errno));

	if (write(dagarm, image, ((len + 3) & ~3)) < 0)
		return -1;

#elif defined(_WIN32)

   if (DeviceIoControl(dag_gethandle(dagfd),
      IOCTL_ARM_WRITE,
      image,
      ((len + 3) & ~3),
      &startaddr,
      sizeof(startaddr),
      &BytesTransfered,
      NULL) == FALSE)
      dagutil_panic("DeviceIoControl %s\n", dagname);

#endif /* Platform-specific code. */

   return 0;
}

static void
dagarmwrite(unsigned addr, unsigned val)
{
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	if (lseek(dagarm, (off_t)addr, SEEK_SET) == (off_t)-1)
		dagutil_panic("dagarmwrite lseek 0x%x: %s\n", addr, strerror(errno));

	if (write(dagarm, &val, sizeof(val)) < 0)
		dagutil_panic("dagarmwrite at 0x%x: %s\n", addr, strerror(errno));

#elif defined(_WIN32)
 
  ULONG BytesTransfered;   

   if (DeviceIoControl(dag_gethandle(dagfd),
      IOCTL_ARM_WRITE,
      &val,
      sizeof(val),
      &addr,
      sizeof(addr),
      &BytesTransfered,
      NULL) == FALSE)
      panic("dagarmwrite at 0x%x: %s\n", addr, strerror(errno));

#endif /* Platform-specific code. */
}

static void
dagnoarmx(int argc, char *argv[])
{
	char *fname = NULL;
	int seenerr = 0;
	char* remain_state;
	char *remain;
	char *flags = "r";
	int xlxno = 0;
	uint32_t len;
	char *ntype = NULL;
	char *nfunc;
	char *av[5];
	uint8_t *bp;
	int count = 0;
	unsigned fpgap_base = 0;
	int fpga_stat_cmd;
	int fpga_config_data;
	int magic = DAGRESET_DUCK;
	dag_reg_t result[DAG_REG_MAX_ENTRIES];
	uint8_t xnew[XBUFSIZE];
	int img_idx;

#if defined(_WIN32)
	ULONG BytesTransfered;
	flags = "rb";
#endif /* _WIN32*/

	/* If you don't find a FPGAP, then either the card is one that doesn't have
	 * further fpgas (4.2x), or it can have them but the current firmware doesn't
	 * support loading to them (4.3x with copro and wrong pci-x image).
	 * Unfortunately it is quite hard to tell these cases apart and give a clear
	 * error message. Try our best.
	 */
	if (dag_reg_find((char*) iom, DAG_REG_FPGAP, result)<1)
		dagutil_panic("The firmware loaded on this DAG does not support loading further xilinx images, please see dagrom.\n");

	fpgap_base = DAG_REG_ADDR(*result);
	
	for (count=0; count<argc; count++)
	{
		remain = argv[count];
		while ((remain[0] == ':') && (remain[0] != '\0'))
		{
			xlxno++;
			remain++;
		}
		
		for (fname = strtok_r(remain, ":", &remain_state);
			fname != NULL;
			fname = strtok_r(NULL, ":", &remain_state), xlxno++)
		{
			if (fname[0] == '\0')
				continue;
			
			fpga_stat_cmd = fpgap_base + 8*xlxno;
			fpga_config_data = fpgap_base+ 8*xlxno + 4;
			
			bp = dagutil_readfile(fname, flags, &len);
			if (bp == NULL)
				dagutil_panic("error reading %s: %s\n", fname, strerror(errno));
			if (len == 0)
				dagutil_panic("file %s appears to be of zero size\n", fname);
			memcpy(xnew, bp, XBUFSIZE);
			
			ntype = dag_xrev_parse(xnew, XBUFSIZE, av)[1];
			nfunc = av[0];
			
			/* Check image is correct for location */
			/* find location */
			img_idx = dag_get_img_idx(daginf->device_code, img_offset+xlxno);
			dagutil_verbose("dag%d dev 0x%04x pos %d got idx %d\n",
					daginf->id, daginf->device_code, img_offset+xlxno, img_idx); // XXX
			/* check against image */
			if (img_idx < 0) {
				if (unknown)
					fprintf(stderr, "Warning, forced loading on unknown hardware!\n");
				else
					dagutil_panic("Unknown hardware, not loading!\nOverride with --unknown only if you are sure, damage may occur.\n");
			} else {
				switch (dag_check_img_ptr(img_idx, copro_id, (char*) xnew, XBUFSIZE, daginf->brd_rev)) {
				case 0:
					break;
				case 1:
					if (force) {
						fprintf(stderr, "Warning, forcing load to non-matching image type!\n");
						break;
					}
					else
						dagutil_panic("Image type mismatch, not loading!\nEnsure you are loading the correct firmware for this device.\nOverride with --force only if you are sure, damage may occur.\n");
				case 2:
					dagutil_panic("Image type and device mismatch, not loading!\nEnsure you are loading the correct firmware for this device.\n");
				default:
					dagutil_panic("internal error line %d", __LINE__);
				}
			}

			/* pre-charge programming logic (for 3.8s and 4.3s) */
			dagwreg(fpga_stat_cmd, 0x20);
			dagwreg(fpga_stat_cmd, 0x10);
			usleep(100000);

			/*
			 * Programming loop here
			 */
			if ((dagrreg(fpga_stat_cmd) == 0xffffffff) || (dagrreg(fpga_stat_cmd) == 0x2))
				dagutil_panic("Xilinx%d is not available!\n", xlxno+1);
			
			dagwreg(fpga_stat_cmd, 0x20); /* erase fpga */
			
			printf ("Waiting for Xilinx%d (%s %s) to program ... \n",
					xlxno+1, nfunc, ntype);
			fflush(stdout);
			
			while (0 == (dagrreg(fpga_stat_cmd) & 0x4))
			{
				/* wait for INIT_N, ready to program */
			}
			printf("FPGA Initialized.\nStarting to program \n");
			seenerr = 0;
			while (len-- > 0) {
				dagwreg(fpga_config_data, *bp++);
				if ((0 == seenerr) && (dagrreg(fpga_stat_cmd) & 0x5) == 0)
				{
					seenerr = 1;
					printf("\nCRC error at byte %d\n", len);
				}
				if (0 == (len % 16384))
				{
					printf("%c", (seenerr == 0) ? '.' : '!');
					fflush( stdout );
				}
			}
			printf("\nFile loaded.\n");
			if (dagrreg(fpga_stat_cmd) & 1)
			{
				printf ("Done.\n");
			}
			else
			{
				printf("Finished, but status 0x%x\n", dagrreg(fpga_stat_cmd));
			}
		}
	}

	/*
	 * We could check for a duck being present here first, but we can
	 * also be lazy and let the driver check for us.
	 */
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	if (ioctl(dagfd, DAGIOCRESET, &magic) != 0)
	{
		dagutil_panic("ioctl DAG_IOCRESET DUCK: %s\n", strerror(errno));
	}

#elif defined(_WIN32)

	if (DeviceIoControl(dag_gethandle(dagfd),
		IOCTL_DUCK_RESET,
		NULL,
		0,
		NULL,
		0,
		&BytesTransfered,
	    NULL) == FALSE)
	{
		dagutil_panic("DeviceIoControl IOCTL_DUCK_RESET: %s\n", strerror(errno));
	}
#endif /* _WIN32 */
}

static unsigned
dagrreg(unsigned addr)
{
	return *(volatile unsigned*)((uintptr_t)iom+addr);
}

static void
dagwreg(unsigned addr, unsigned value)
{
	*(volatile unsigned*) ((uintptr_t)iom+addr) = value;
}

static void 
phy_write(int phy_reg, uint32_t val)
{
	int phy_val = 0;
	phy_val |= ((phy_reg & 0x01F) << 16);
	phy_val |= (val & 0xFFFF);
	dagwreg(0x360, phy_val);
	usleep(1000);
}

static int 
phy_read(int phy_reg)
{
	int wr_val, rd_val;

	wr_val = 0;
	wr_val |= BIT31;
	wr_val |= ((phy_reg & 0x01F) << 16);
	dagwreg(0x360, wr_val);
	usleep(1000);

	rd_val = dagrreg(0x360);
	return (rd_val & 0x000FFFF);
}

#ifndef ENDACE_UNIT_TEST
int
main(int argc, const char* const * argv)
{
	return dagld_main(argc, (char**) argv);
}
#endif /* ENDACE_UNIT_TEST */
