/*
 * Copyright (c) 2002-2006 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: dagbug.c 13453 2010-12-02 23:30:08Z jomi.gregory $
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

/* Endace headers. */
#include "dagapi.h"
#include "dagclarg.h"
#include "dagnew.h"
#include "dagreg.h"
#include "dag_platform.h"
#include "dagutil.h"

#if HAVE_EDITLINE
#include <editline/readline.h>
#include <histedit.h>
#endif /* HAVE_EDITLINE */


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


extern char* dag_xrev_name(unsigned char *xrev, unsigned int xlen);


#define BASELIMIT (1<<28)
#define ADDRSPACE(X) ((X)&0xf0000000)
#define MAXARGV  64

static void dagattach(void);
static void dagbug(void);
static void dagreport(void);
static unsigned int dagpeek(unsigned int);
static void dagpoke(unsigned int, unsigned int);
static unsigned int dagrbox(unsigned int);
static void dagwbox(unsigned int, unsigned int);
static void dagbaseupdate(void);
static void dag_read_history(const char* filename);
static void dag_write_history(const char* filename);

static char *dgetline(char *prompt);

#define DEFAULT_DAGNAME "dag0"
#define DEFAULT_DAGBUG_HISTORY ".dagbug_history"
#define DEFAULT_DAGBUG_RCFILE ".dagbugrc"

static char dagname_buf[DAGNAME_BUFSIZE] = DEFAULT_DAGNAME;
static char dagname[DAGNAME_BUFSIZE];

#define COMMAND_BUFSIZE 128
#define FILENAME_BUFSIZE 128
static char command_buf[COMMAND_BUFSIZE] = "";
static char history_filename_buf[FILENAME_BUFSIZE] = DEFAULT_DAGBUG_HISTORY;
static char rc_filename_buf[FILENAME_BUFSIZE] = DEFAULT_DAGBUG_RCFILE;

static int dagstream;
static int dagfd;
static int dagarm;

static daginf_t *daginfo;
static int radix = 16; /* less confusing to most people */
static int scramble; /* Special DAG4.0 address scrambling for PHY access */
static int force = 0; /* do not force open dag */

static int uHaveReadCommandLine = 0; /* Boolean - read command line? */
static int uHistoryCount; /* where we are in the history. */
static int uInteractive = 1; /* the default is to go interactive. */
static int uReadFromKeyboard; /* Whether to read commands from keyboard (including the commandline option '-c') or from an rc file. */
static int uSaveHistory = 1;
static int uForceNoDRBAccess = 0;

static volatile uint8_t* iom;
static dag_reg_t* regs;
static unsigned int phy_base, ram_base, pci_base, xilinx1_base, xilinx2_base, xilinx3_base, eeprom_base, has_arm;

static char white[] = " \t\n";
/* added 3 global variables - previously gettting from sheep_t in dagapi.c  */
/* file descriptor for DAGMINOR_IOM */
static int dagiom ;
static dag_reg_t       reg_global[DAG_REG_MAX_ENTRIES];
static daginf_t daginfo_global;

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


static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagbug - debugging shell for Endace DAG cards.\n");
	printf("Usage: dagbug [options]\n");
	dagclarg_display_usage(clarg, stdout);
}


/* Commandline argument codes. */
enum
{
	CLA_COMMANDLINE,
	CLA_DEVICE,
	CLA_FORCE,
	CLA_HELP,
	CLA_HISTORY_FILE,
	CLA_INTERACTIVE,
	CLA_NO_HISTORY,
	CLA_RADIX,
	CLA_RC_FILE,
	CLA_VERBOSE,
	CLA_VERSION,
    CLA_FORCE_NO_DRB
};


int
dagbug_main(int argc, const char* const * argv)
{
	FILE* errorfile = NULL;
	ClArgPtr clarg = NULL;
	int argindex = 0;
	int result;
	int code;

	dagutil_set_progname("dagbug");

	/* 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, argv);

	dagclarg_add_string(clarg, "Execute commandline (instead of stdin processing).", "--command", 'c', "command", command_buf, COMMAND_BUFSIZE, CLA_COMMANDLINE);
	dagclarg_add_string(clarg, "DAG device to use.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add(clarg, "Force open without expecting monitor to be ready.", "--force", 'f', CLA_FORCE);
	
	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_string(clarg, "Read history file (default is '" DEFAULT_DAGBUG_HISTORY "').", "--historyfile", 't', "histfile", history_filename_buf, FILENAME_BUFSIZE, CLA_HISTORY_FILE);
	dagclarg_add(clarg, "Interactive, even when reading from a file or command line.", "--interactive", 'i', CLA_INTERACTIVE);
	dagclarg_add(clarg, "Bypasses DRB access during loading", "--nodrb", 'b', CLA_FORCE_NO_DRB);
	dagclarg_add(clarg, "No history (turn off reading and writing '" DEFAULT_DAGBUG_HISTORY "').", "--no-history", 'n', CLA_NO_HISTORY);
	dagclarg_add_int(clarg, "Radix to use (default is 16).", "--radix", 'x', "radix", &radix, CLA_RADIX);
	dagclarg_add_string(clarg, "Read initial commands from rcfile (default is '" DEFAULT_DAGBUG_RCFILE "').", "--rcfile", 'r', "rcfile", rc_filename_buf, FILENAME_BUFSIZE, CLA_RC_FILE);
	dagclarg_add(clarg, "Increase verbosity.", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add(clarg, "Display version information.", "--version", 'V', CLA_VERSION);


	/* Parse the command line options. */
	result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == result)
	{
		switch (code)
		{
			case CLA_COMMANDLINE:
				/* Commandline has been copied into command_buf. */
				break;
				
			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++;
				break;
	
			case CLA_HELP:
				print_usage(clarg);
				return EXIT_SUCCESS;
				break;
				
			case CLA_HISTORY_FILE:
				/* History file has been copied into history_filename_buf. */
				break;
				
			case CLA_INTERACTIVE:
				uInteractive++;
				break;
				
			case CLA_NO_HISTORY:
				uSaveHistory = 0;
				break;
				
			case CLA_RADIX:
				/* Radix is in the 'radix' variable. */
				break;
				
			case CLA_RC_FILE:
				/* RC file has been copied into rc_filename_buf. */
				break;
	
			case CLA_VERBOSE:
				dagutil_inc_verbosity();
				errorfile = stderr;
				break;
	
			case CLA_VERSION:
				print_version();
				return EXIT_SUCCESS;
				break;
	        case CLA_FORCE_NO_DRB:
                uForceNoDRBAccess = 1;
                break;
			default:
				if (argv[argindex][0] == '-')
				{
					/* Unknown option. */
					dagutil_error("unknown option %s\n", argv[argindex]); 
					print_usage(clarg);
					return EXIT_FAILURE;
				}
				break;
		}

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

	if (-1 == 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 = (const char* const*) dagclarg_get_unprocessed_args(clarg, &argc);
	if ((NULL != argv) && (0 < argc) && (0 < dagutil_get_verbosity()))
	{
		int index;
		
		for (index = 0; index < argc; index++)
		{
			dagutil_verbose("unprocessed argument: '%s'\n", argv[index]);
		}
	}

	/*
	 * This is the case when a command line argument is taken
	 * as the input when dagbug running as shell interpreter.
	 */
	if (0 != argc) {
		if (freopen(argv[0], "r", stdin) != stdin)
			dagutil_panic("cannot open %s: %s\n", argv[0], strerror(errno));
		argc--;
		argv++;
	}
	
	if (0 != argc)
	{
		dagutil_warning("excessive command line arguments: '%s'%s\n", argv[0], (argc>1) ? " and further" : "");
	}

	/* ClargPtr should no longer be necessary. */
	dagclarg_dispose(clarg);

	dagattach();

	/* A single command line command to execute. */
	if (strlen(command_buf) > 0)
	{
		uReadFromKeyboard = 1;
		uInteractive = 0;
		uSaveHistory = 0;
	}

	/*
	 * Multiple commands, either file or interactive.
	 */
	if (0 == isatty(fileno(stdin)))
		uInteractive = 0;

#if HAVE_EDITLINE
	using_history();
#endif /* HAVE_EDITLINE */

	/*
	 * Initialization.
	 */
	if (1 == uInteractive)
	{
		int safed;
		FILE * fp;

		dagreport();
		/*
		 * Read in rc file without closing stdin, stdout is untouched.
		 * Since readline is not used here we don't have any problems.
		 */
		if ((safed = dup(STDIN_FILENO)) < 0)
			dagutil_panic("cannot duplicate stdin: %s\n", strerror(errno));
		
		if (freopen(rc_filename_buf, "r", stdin) != stdin)
		{
			/*
			 * Probably doesn't exist.
			 */
			(void)fclose(stdin); /* paranoia ? */
		}
		else
		{
			dagbug();
			if (fclose(stdin) < 0)
				dagutil_panic("cannot fclose %s on stdin: %s\n", rc_filename_buf, strerror(errno));
		}
		if (dup(safed) != STDIN_FILENO)
			dagutil_panic("cannot replicate stdin: %s\n", strerror(errno));
		
		if ((fp = fdopen(STDIN_FILENO, "r")) == NULL)
			dagutil_panic("fdopen on old stdin failed: %s\n", strerror(errno));
		
		*stdin = *fp;   /* interesting to see that this works */

#if HAVE_EDITLINE
		clear_history();
#endif /* HAVE_EDITLINE */
		uReadFromKeyboard = 1;
	}
	else
	{
		uSaveHistory = 0;
	}

	/*
	 * Main processing
	 */
	dag_read_history(history_filename_buf);
#if HAVE_EDITLINE
	uHistoryCount = where_history();
#endif /* HAVE_EDITLINE */

	dagbug();

	dag_write_history(history_filename_buf);

	return EXIT_SUCCESS;
}


static void
dag_read_history(const char* filename)
{
#if HAVE_EDITLINE
	if (0 == uSaveHistory)
	{
		return;
	}

	(void) read_history(filename);
#endif /* HAVE_EDITLINE */
}


static void
dag_write_history(const char* filename)
{
#if HAVE_EDITLINE
	if (0 == uSaveHistory)
	{
		return;
	}

	if (write_history(filename))
	{
		dagutil_warning("couldn't save history file %s: %s\n", filename, strerror(errno));
	}
#endif /* HAVE_EDITLINE */
}


static unsigned int
d__byte_swap_long(unsigned int x)
{
	register unsigned int __X = (x);

#if defined(__ia64__)
//FIXME: Optimized version in assemmbler for Itanium 
    __X =  ( ( ( x & 0xFF ) << 24) | ((( x >> 8) & 0xFF) << 16) | (((x>>16)& 0xFF)<<8) | ((x>>24)& 0xFF) );

#elif defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun))

	__asm ("bswap %0" : "=r" (__X) : "0" (__X));

#elif defined(_WIN32)

	_asm
	{
		push eax
		mov eax, __X
		bswap eax
		mov __X, eax
		pop eax
	}

#endif /* _WIN32 */

	return __X;
}


static unsigned int
d__byte_long(unsigned int x)
{
	return x;
}


static unsigned int (*dagswap)(unsigned int) = d__byte_long;


static void
dagattach(void)
{
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun))
    char name_dagiom[16] ={'\0'};
    char name_dagarm[16] ={'\0'};
    char num_dagiom[16] ={'\0'};

    if((dagfd = open(dagname, O_RDWR)) < 0)
        dagutil_panic("dag open %s: %s\n", dagname, strerror(errno));
   
    strncpy(num_dagiom, dagname, strlen(dagname)); 
    sscanf(num_dagiom, "/dev/dag%s", num_dagiom);
   
    /* find which dagiom needs to be opened - could not use dag_clone as its uses herd fields*/
    sprintf(name_dagiom, "/dev/dagiom%s", num_dagiom);
    
    //printf("dagname :%s name_dagiom:%s ,num_dagiom%s \n", dagname, name_dagiom, num_dagiom) ;
    if((dagiom = open(name_dagiom, O_RDWR)) < 0)
        dagutil_panic("open for dagiom %s: %s\n", dagname, strerror(errno));

    /* get dag info  and assigning to daginfo pointer - previously it was assinged from sheep_t  */
    if(ioctl(dagfd, DAGIOCINFO, &daginfo_global) < 0 )
        dagutil_panic("daginfo for dagiom %s: %s\n", dagname, strerror(errno));
    daginfo = &daginfo_global;

    if((iom = mmap(NULL, daginfo->iom_size, PROT_READ | PROT_WRITE,
					MAP_SHARED, dagiom, 0)) == MAP_FAILED)
        dagutil_panic("mmap failed for dagiom %s: %s\n", dagname, strerror(errno));
        
    if ( 0 == uForceNoDRBAccess )
    {
         /* fill dag reg  as done in dag_update */
        if(dag_reg_find((char*) iom, 0, reg_global)<0)
            dagutil_warning("Could not find DAG_REG_START \n");
        regs = reg_global;
        dagbaseupdate();
    }

#elif defined(_WIN32)
    if ((dagfd = dag_open(dagname)) < 0) 
		dagutil_panic("dag_open %s: %s\n", dagname, strerror(errno));

	iom = dag_iom(dagfd);
	daginfo = dag_info(dagfd);
    regs = dag_regs(dagfd);
    dagbaseupdate();
#endif



#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun))
	/* 4.1s etc want the arm inode */
	if (has_arm)
    {
		//dagarm = dag_clone(dagfd, DAGMINOR_ARM);
        /* dag_clone removed because dag_open is not used to get dagfd .So the interanl structure (sheep_t) is not intitalized */
        sprintf(name_dagarm, "/dev/dagarm%c", dagname[strlen(dagname) - 1]);
        dagarm = open(name_dagiom, O_RDWR);
    }
#endif 
	/* 4.0 addresses were wrong */
	if (daginfo->device_code == 0x4000)
		scramble++;
}


static void
dagreport(void)
{
	int bsize;
	char *mult;
	char rev[16];
	char date[16];
	char time[16];

	bsize = daginfo->buf_size;
	mult = "";
	if ((bsize % ONE_KIBI) == 0) {
		bsize /= ONE_KIBI;
		mult = "K";
	}
	if ((bsize % ONE_KIBI) == 0) {
		bsize /= ONE_KIBI;
		mult = "M";
	}
	printf("%s\n", dagname);
	printf("\tid=%u, code=0x%.4x, bsize=%d%s\n",
		   daginfo->id, daginfo->device_code,
		   bsize, mult);
	if (has_arm) {
		printf("\tram=0x%.8x\n", ram_base);
		if (pci_base)
			printf("\tpci=0x%.8x\n", pci_base);
		if (xilinx1_base)
			printf("\txlx1=0x%.8x\n", xilinx1_base);
		if (xilinx2_base)
			printf("\txlx2=0x%.8x\n", xilinx2_base);
		if (xilinx3_base)
			printf("\txlx3=0x%.8x\n", xilinx3_base);
		if (eeprom_base)
			printf("\teeprom=0x%.8x\n", eeprom_base);
	}
	printf("\tphy=0x%.8x\n", phy_base);
	(void)sscanf(kCvsHeader, "%*s %*s %s %s %s", rev, date, time);
	printf("\t%s revision %s %s %s UTC\n", dagutil_get_progname(), rev, date, time);
	if (scramble)
		printf("\tDAG4.0 PHY address scrambling is on\n");
}

static unsigned int
dagpeek(unsigned int addr)
{
	unsigned int value = 0;

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

	if (scramble && (ADDRSPACE(addr) == phy_base))
		addr = ((addr & ~0x03) | ((addr & 0x3) << 8));

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

	if (lseek(dagarm, (off_t)addr, SEEK_SET) < 0)
		dagutil_panic("dagphyread %s lseek 0x%x: %s\n", dagname, addr, strerror(errno));
	if (read(dagarm, &value, sizeof(value)) != sizeof(value))
		dagutil_panic("read %s at 0x%x: %s\n", dagname, addr, strerror(errno));

#elif defined(_WIN32)
	
	if (DeviceIoControl(dag_gethandle(dagfd),
		IOCTL_ARM_READ,
		&addr,
		sizeof(addr),
		&value,
		sizeof(value),
		&BytesTransfered,
		NULL) == FALSE)
		dagutil_panic("DeviceIoControl %s\n", dagname);

#endif

	return value;
}

static void
dagpoke(unsigned int addr, unsigned int value)
{
#if defined(_WIN32)
	ULONG   BytesTransfered;
#endif /* _WIN32 */

	if (scramble && (ADDRSPACE(addr) == phy_base))
		addr = ((addr & ~0x03) | ((addr & 0x3) << 8));

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

	if (lseek(dagarm, (off_t)addr, SEEK_SET) < 0)
		dagutil_panic("dagphywrite %s lseek 0x%x: %s\n", dagname, addr, strerror(errno));
	if (write(dagarm, &value, sizeof(value)) != sizeof(value))
		dagutil_panic("write %s at 0x%x: %s\n", dagname, addr, strerror(errno));

#elif defined(_WIN32)

	if (DeviceIoControl(dag_gethandle(dagfd),
		IOCTL_ARM_WRITE,
		&value,
		sizeof(&value),
		&addr,
		sizeof(addr),
		&BytesTransfered,
		NULL) == FALSE)
		dagutil_panic("DeviceIoControl %s\n", dagname);
#endif /* _WIN32 */
}

static unsigned int
dagrbox(unsigned int addr)
{
	return *(volatile unsigned int*)(iom+addr);
}

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

static void
cmd_help(int argc, char *argv[])
{
	printf(
		"Usage:\n"
		"        help - this page\n"
		"\n"
		"        read/r4  <addr> [<repeat>]\n"
		"        mem/m4   <addr> [<repeat>]\n"
		"        write/w4 <addr> [<contents> [<repeat>]]\n"
		"        base     <baseaddr>|ram|pci|xlx|xlx1|xlx2|dsp|xlx3|cpp|phy|rom\n"
		"        radix    [<newradix>]\n"
		"        swap     [on|off]\n"
		"        scramble [on|off]\n"
		"        xrev     [<addr>]\n"
		"        iomem    <offset> [<repeat>]\n"
		"        ioread   <offset> [<repeat>]\n"
		"        iowrite  <offset> [<contents> [<repeat>]]\n"
		"        mbox\n"
		"\n"
		"        report - displays current debugging environment\n"
		"        history - prints the current history list\n"
		"        quit - exits dagbug\n"
		"\n"
		"        !shell command\n"
		"        #comment\n"
		"\n"
		"        bugs - gives details on how to send bug reports\n"
		"\n"
		"    Conventions:\n"
		"        <addr>, <offset> and <baseaddr> to be specified in hex\n"
		"        other integer values depend on radix\n"
		"        <newradix> is specified base 10 always\n"
		"        default for <repeat> is 1\n"
		"        default for <contents> is 0\n"
		"        multiple commands on a single line can be separated by ; (semicolon)\n"
		);
}

static unsigned int base;

static void
cmd_base(int argc, char *argv[])
{
	unsigned int nbase;
	char *ap;
	struct magic {
		char *name;
		unsigned int *valuep;
	} magic[] = {
		{ "ram",	&ram_base     },
		{ "pci",	&pci_base     },
		{ "xlx",	&xilinx1_base },
		{ "xlx1",	&xilinx1_base },
		{ "dsp",	&xilinx2_base },
		{ "xlx2",	&xilinx2_base },
		{ "cpp",	&xilinx3_base },
		{ "xlx3",	&xilinx3_base },
		{ "rom",	&eeprom_base  },
		{ NULL,		NULL          }
	};
	struct magic *mp;

	if (argc < 2)
		goto report;
	nbase = strtoul(argv[1], &ap, 16);
	if ((ap == argv[1]) || (*ap != '\0')) {
		/*
		 * Must be a magic keyword instead of an integer
		 */
		for (mp = magic ; mp->name != NULL ; mp++)
			if (!strncmp(argv[1], mp->name, strlen(argv[1])))
				break;
		if (mp->name == NULL)
			printf("base unchanged\n");
		else
			base = *mp->valuep;
	} else {
		base = nbase;
	}
report:
	printf("base=0x%.8x, baselimit=0x%.8x\n", base, BASELIMIT);
}

static void
cmd_read(int argc, char *argv[])
{
	char *ap;
	unsigned int addr, value, repeat;
	int i;
	char adump[16];
	char spaces[3*9] = "                           ";

	if (!has_arm) {
		printf("read: No ARM present\n");
		return;
	}
	if (argc < 2) {
		printf("read: missing argument\n");
		return;
	}
	addr = strtoul(argv[1], &ap, 16);
	if (ap == argv[1]) {
		printf("read: %s: garbage address specification\n", argv[1]);
		return;
	}
	if (addr < BASELIMIT)
		addr += base;

	if (argc < 3) {
		repeat = 1;
	} else {
		repeat = strtoul(argv[2], &ap, radix);
		if (ap == argv[2]) {
			printf("read: %s: garbage repeat specification\n", argv[2]);
			return;
		}
	}

	ap = adump;
	for (i = 0 ; repeat--; i++) {
		if ((i%4)==0)
			printf("%.8x: ", addr);

		value = dagpeek(addr);
		printf("%.8x ", (*dagswap)(value));

		*ap = ((char *)&value)[0]; if (!isprint(*ap)) *ap = '.'; ap++;
		*ap = ((char *)&value)[1]; if (!isprint(*ap)) *ap = '.'; ap++;
		*ap = ((char *)&value)[2]; if (!isprint(*ap)) *ap = '.'; ap++;
		*ap = ((char *)&value)[3]; if (!isprint(*ap)) *ap = '.'; ap++;

		addr += sizeof(unsigned int);

		if ((i%4)==3) {
			printf("%.16s\n", adump);
			ap = adump;
		}
	}
	if ((i%4)!=0)
		printf("%.*s%.*s\n", 9*(4-(i%4)), spaces, (int)(ap-adump), adump);
}

static void
cmd_read4(int argc, char *argv[])
{
	char *ap;
	unsigned int addr, value, repeat;
	int i;
	char adump[16];
	char spaces[3*9] = "                           ";

	if (!has_arm) {
		printf("read4: No ARM present\n");
		return;
	}
	if (argc < 2) {
		printf("read4: missing argument\n");
		return;
	}
	addr = strtoul(argv[1], &ap, 16);
	if (ap == argv[1]) {
		printf("read4: %s: garbage address specification\n", argv[1]);
		return;
	}
	if (addr < BASELIMIT)
		addr += base;

	if (argc < 3) {
		repeat = 1;
	} else {
		repeat = strtoul(argv[2], &ap, radix);
		if (ap == argv[2]) {
			printf("read4: %s: garbage repeat specification\n", argv[2]);
			return;
		}
	}

	ap = adump;
	for (i = 0 ; repeat--; i++) {
		if ((i%4)==0)
			printf("%.8x: ", addr);

		value = ((dagpeek(addr+0) & 0xff)      ) |
			((dagpeek(addr+4) & 0xff) <<  8) |
			((dagpeek(addr+8) & 0xff) << 16) |
			((dagpeek(addr+12)& 0xff) << 24) ;
			
		printf("%.8x ", (*dagswap)(value));

		*ap = ((char *)&value)[0]; if (!isprint(*ap)) *ap = '.'; ap++;
		*ap = ((char *)&value)[1]; if (!isprint(*ap)) *ap = '.'; ap++;
		*ap = ((char *)&value)[2]; if (!isprint(*ap)) *ap = '.'; ap++;
		*ap = ((char *)&value)[3]; if (!isprint(*ap)) *ap = '.'; ap++;

		addr += 4*sizeof(unsigned int);

		if ((i%4)==3) {
			printf("%.16s\n", adump);
			ap = adump;
		}
	}
	if ((i%4)!=0)
		printf("%.*s%.*s\n", 9*(4-(i%4)), spaces, (int)(ap-adump), adump);
}

static void
cmd_iomem(int argc, char *argv[])
{
	char *ap;
	unsigned int addr, value, repeat;
	int i;
	char adump[16];
	char spaces[3*9] = "                           ";

	if (argc < 2) {
		printf("iomem: missing argument\n");
		return;
	}
	addr = strtoul(argv[1], &ap, 16);
	if (ap == argv[1]) {
		printf("iomem: %s: garbage offset specification\n", argv[1]);
		return;
	}
	if (argc < 3) {
		repeat = 1;
	} else {
		repeat = strtoul(argv[2], &ap, radix);
		if (ap == argv[2]) {
			printf("iomem: %s: garbage repeat specification\n", argv[2]);
			return;
		}
	}
	ap = adump;
	for (i = 0 ; repeat--; i++) {
		if ((i%4)==0)
			printf("%.8x: ", addr);
		value = dagrbox(addr);
		printf("%.8x ", value);

		*ap = ((char *)&value)[0]; if (!isprint(*ap)) *ap = '.'; ap++;
		*ap = ((char *)&value)[1]; if (!isprint(*ap)) *ap = '.'; ap++;
		*ap = ((char *)&value)[2]; if (!isprint(*ap)) *ap = '.'; ap++;
		*ap = ((char *)&value)[3]; if (!isprint(*ap)) *ap = '.'; ap++;

		addr += sizeof(unsigned int);

		if ((i%4)==3) {
			printf("%.16s\n", adump);
			ap = adump;
		}
	}
	if ((i%4)!=0)
		printf("%.*s%.*s\n", 9*(4-(i%4)), spaces, (int)(ap-adump), adump);
}

static void
cmd_write(int argc, char *argv[])
{
	char *ap;
	unsigned int addr, value, repeat;

	if (!has_arm) {
		printf("write: No ARM present\n");
		return;
	}
	if (argc < 2) {
		printf("write: missing argument\n");
		return;
	}
	addr = strtoul(argv[1], &ap, 16);
	if (ap == argv[1]) {
		printf("write: %s: garbage address specification\n", argv[1]);
		return;
	}
	if (addr < BASELIMIT)
		addr += base;

	if (argc < 3) {
		value = 0;
	} else {
		value = strtoul(argv[2], &ap, radix);
		if (ap == argv[2]) {
			printf("write: %s: garbage value specification\n", argv[2]);
			return;
		}
	}

	if (argc < 4) {
		repeat = 1;
	} else {
		repeat = strtoul(argv[3], &ap, radix);
		if (ap == argv[3]) {
			printf("write: %s: garbage repeat specification\n", argv[3]);
			return;
		}
	}

	for (; repeat-- ; addr += 4)
		dagpoke(addr, (*dagswap)(value));
}

static void
cmd_write4(int argc, char *argv[])
{
	char * ap;
	unsigned int addr, value, repeat;

	if (!has_arm) {
		printf("write4: No ARM present\n");
		return;
	}
	if (argc < 2) {
		printf("write4: missing argument\n");
		return;
	}
	addr = strtoul(argv[1], &ap, 16);
	if (ap == argv[1]) {
		printf("write4: %s: garbage address specification\n", argv[1]);
		return;
	}
	if (addr < BASELIMIT)
		addr += base;

	if (argc < 3) {
		value = 0;
	} else {
		value = strtoul(argv[2], &ap, radix);
		if (ap == argv[2]) {
			printf("write4: %s: garbage value specification\n", argv[2]);
			return;
		}
	}

	if (argc < 4) {
		repeat = 1;
	} else {
		repeat = strtoul(argv[3], &ap, radix);
		if (ap == argv[3]) {
			printf("write4: %s: garbage repeat specification\n", argv[3]);
			return;
		}
	}

	while (repeat--) {
		dagpoke(addr, ((*dagswap)(value)      ) & 0xff); addr +=4;
		dagpoke(addr, ((*dagswap)(value) >>  8) & 0xff); addr +=4;
		dagpoke(addr, ((*dagswap)(value) >> 16) & 0xff); addr +=4;
		dagpoke(addr, ((*dagswap)(value) >> 24) & 0xff); addr +=4;
	}
}

static void
cmd_iowrite(int argc, char *argv[])
{
	char * ap;
	unsigned int addr, value, repeat;

	if (argc < 2) {
		printf("iowrite: missing argument\n");
		return;
	}
	addr = strtoul(argv[1], &ap, 16);
	if (ap == argv[1]) {
		printf("iowrite: %s: garbage offset specification\n", argv[1]);
		return;
	}

	if (argc < 3) {
		value = 0;
	} else {
		value = strtoul(argv[2], &ap, radix);
		if (ap == argv[2]) {
			printf("iowrite: %s: garbage value specification\n", argv[2]);
			return;
		}
	}

	if (argc < 4) {
		repeat = 1;
	} else {
		repeat = strtoul(argv[3], &ap, radix);
		if (ap == argv[3]) {
			printf("iowrite: %s: garbage repeat specification\n", argv[3]);
			return;
		}
	}

	for (; repeat-- ; addr += 4)
		dagwbox(addr, value);
}

static void
cmd_swap(int argc, char *argv[])
{
	if (argc < 2)
		goto report;
	if (!strncmp(argv[1], "on", strlen(argv[1])))
		dagswap = d__byte_swap_long;
	else if (!strncmp(argv[1], "off", strlen(argv[1])))
		dagswap = d__byte_long;
	else
		printf("swap: garbage argument %s\n", argv[1]);
report:
	printf("swap is now %s\n", (dagswap == d__byte_long) ? "off" : "on");
}

static void
cmd_scramble(int argc, char *argv[])
{
	if (argc < 2)
		goto report;
	if (!strncmp(argv[1], "on", strlen(argv[1])))
		scramble = 1;
	else if (!strncmp(argv[1], "off", strlen(argv[1])))
		scramble = 0;
	else
		printf("scramble: garbage argument %s\n", argv[1]);
report:
	printf("scramble is now %s\n", scramble ? "on" : "off");
}

enum {
	Data_Block = 0x3000,    /* params from PC. */
	Data_Hole_Base = 0x0,   /* pci hole base */
	Data_Hole_Top = 0x04,   /* pci hole top */
	Data_Device_Code= 0x08, /* device code (16bits) */
	Data_Xilinx1 = 0x800,   /* location of first xilinx header */
	Data_Xilinx2 = 0x880,   /* location of second xilinx header */
	Data_Xilinx3 = 0x900    /* location of third xilinx header */
};

static void
cmd_xrev(int argc, char *argv[])
{
# define XBUFSIZE 128
	char *ap;
	unsigned int saddr, addr;
	unsigned char xbuf[XBUFSIZE]; /* hopefully always sufficient */
	int i;

	if (!has_arm) {
		printf("xrev: No ARM present\n");
		return;
	}
	if (argc < 2) {
		/*
		 * XXX these constants should not be hardwired in here.
		 */
		char abuf[16];
		unsigned int curbase;

		curbase = base; /* temporarily disable base addressing */
		base = 0;

		addr = DAG2(daginfo->device_code) ? 0x14000 : 0x0B000;
		addr += eeprom_base;
		(void)sprintf(abuf, "%x", addr);
		argv[1] = abuf;
		argv[2] = NULL;
		argc++;
		cmd_xrev(argc, argv);

		if (DAG35(daginfo->device_code)) {
			addr = 0x34000;
			addr += eeprom_base;
			(void)sprintf(abuf, "%x", addr);
			argv[1] = abuf;
			cmd_xrev(argc, argv);
		}

		(void)sprintf(abuf, "%x", Data_Block + Data_Xilinx1);
		argv[1] = abuf;
		cmd_xrev(argc, argv);

		if (DAG35(daginfo->device_code) || (xilinx2_base)) {
			(void)sprintf(abuf, "%x", Data_Block + Data_Xilinx2);
			argv[1] = abuf;
			cmd_xrev(argc, argv);
		}

		if (xilinx3_base) {
			(void)sprintf(abuf, "%x", Data_Block + Data_Xilinx3);
			argv[1] = abuf;
			cmd_xrev(argc, argv);
		}

		base = curbase; /* reenable base addressing */

		return;
	} else {
		addr = strtoul(argv[1], &ap, 16);
		if (ap == argv[1]) {
			printf("xrev: bad address %s\n", argv[1]);
			return;
		}
		if (addr < BASELIMIT)
			addr += base;
	}
	saddr = addr;
	for (i = 0 ; i < (XBUFSIZE/sizeof(unsigned int)) ; i++, addr += sizeof(unsigned int))
		((unsigned int *)xbuf)[i] = dagpeek(addr);
	printf("%.8x:\t%s\n", saddr, dag_xrev_name(xbuf, XBUFSIZE));
}


static void
cmd_report(int argc, char *argv[])
{
	dagreport();
}


static void
cmd_quit(int argc, char *argv[])
{
	dag_write_history(history_filename_buf);
	exit(EXIT_SUCCESS);
}


static void
cmd_history(int argc, char *argv[])
{
#if HAVE_EDITLINE

	HIST_ENTRY *hep;
	int i;

	/*
	 * Maybe we should set a limit here.
	 */
	for (i = 1 ; i <= uHistoryCount ; i++) {
		if ((hep = history_get(i)) == NULL)
			printf("%4d - NULL!\n", i);
		else
			printf("%4d - %s\n", i, hep->line);
	}

#else

	printf("history not available (requires libedit)\n");
	
#endif /* HAVE_EDITLINE */
}


static void
cmd_radix(int argc, char *argv[])
{
	char *ap;

	if (argc < 2) {
		/* do nothing */
	} else {
		radix = strtoul(argv[1], &ap, 0);
		if (ap == argv[1]) {
			printf("radix: %s: garbage base specification\n", argv[1]);
			return;
		}
	}
	printf("radix=%d", radix);
	if (radix == 0)
		printf(" (C-style: prefix 0x for base 16, prefix 0 for base 8, default base 10)");
	printf("\n");
}

enum {
	ToHM0 = 0x00,
	ToHM1 = 0x04,
	ToHM2 = 0x08,
	ToHM3 = 0x0c,
	ToHM4 = 0x10,
	ToHM5 = 0x14,
	ToHM6 = 0x18,
	ToHM7 = 0x1c,
	ToHM8 = 0x20,
	ToHM9 = 0x24,
	ToHM10 = 0x28,
	ToHM11 = 0x2c,
	ToHM12 = 0x30,
	ToHM13 = 0x34,
	ToHM14 = 0x38,
	ToHM15 = 0x3c
};

enum {
	ToAM0 = 0x40,
	ToAM1 = 0x44,
	ToAM2 = 0x48,
	ToAM3 = 0x4c
};

static void
cmd_mbox(int argc, char *argv[])
{
	/*
	 * Need to add command line argument processing soon
	 * XXX need document that swapping is never applied here.
	 */
	if (!has_arm) {
		printf("mbox: No ARM present\n");
		return;
	}
	switch(DAGN(daginfo->device_code)) {
	case 2:
		printf("HBOX0 %.8x\n", dagrbox(0x50+ToHM0));
		printf("HBOX1 %.8x\n", dagrbox(0x50+ToHM1));
		printf("HBOX2 %.8x\n", dagrbox(0x50+ToHM2));
		printf("HBOX3 %.8x\n", dagrbox(0x50+ToHM3));

		printf("ABOX0 %.8x\n", dagrbox(ToAM0));
		printf("ABOX1 %.8x\n", dagrbox(ToAM1));
		printf("ABOX2 %.8x\n", dagrbox(ToAM2));
		printf("ABOX3 %.8x\n", dagrbox(ToAM3));
		break;
	case 3:
	case 4: /* unless things change */
		printf("HBOX0 %.8x\n", dagrbox(ToHM0));
		printf("HBOX1 %.8x\n", dagrbox(ToHM1));
		printf("HBOX2 %.8x\n", dagrbox(ToHM2));
		printf("HBOX3 %.8x\n", dagrbox(ToHM3));

		printf("HBOX4 %.8x\n", dagrbox(ToHM4));
		printf("HBOX5 %.8x\n", dagrbox(ToHM5));
		printf("HBOX6 %.8x\n", dagrbox(ToHM6));
		printf("HBOX7 %.8x\n", dagrbox(ToHM7));

		printf("HBOX8 %.8x\n", dagrbox(ToHM8));
		printf("HBOX9 %.8x\n", dagrbox(ToHM9));
		printf("HBOXA %.8x\n", dagrbox(ToHM10));
		printf("HBOXB %.8x\n", dagrbox(ToHM11));

		printf("HBOXC %.8x\n", dagrbox(ToHM12));
		printf("HBOXD %.8x\n", dagrbox(ToHM13));
		printf("HBOXE %.8x\n", dagrbox(ToHM14));
		printf("HBOXF %.8x\n", dagrbox(ToHM15));

		printf("ABOXN not implemented yet\n");
		/* via monitor, perhaps local bus DMA really is a better choice here */
		break;
	case 5:
		printf("JBOX0 %.8x\n", dagrbox(ToHM0));
		printf("JBOX1 %.8x\n", dagrbox(ToHM1));
		printf("JBOX2 %.8x\n", dagrbox(ToHM2));
		printf("JBOX3 %.8x\n", dagrbox(ToHM3));
		break;
	default:
		printf("mbox: unsupported DAG%d\n", DAGN(daginfo->device_code));
		break;
	}
}

static void
cmd_bugs(int argc, char *argv[])
{
	printf(
		"To report a bug, please take a screendump of the report command\n"
		"and/or the dagbug startup screen and send it along with your\n"
		"details to:\n"
		"\n"
		"            <support@endace.com>\n"
		"\n"
		"Thanks for your time!\n"
		);
}

typedef struct cmd {
	char * name;
	void	(*func)(int argc, char *argv[]);
} cmd_t;

cmd_t cmdtab[] = {
	{ "help",	cmd_help,	},
	{ "?",		cmd_help,	},
	{ "memory",	cmd_read,	},
	{ "m4",		cmd_read4,	},
	{ "read",	cmd_read,	},
	{ "r4",		cmd_read4,	},
	{ "write",	cmd_write,	},
	{ "w4",		cmd_write4,	},
	{ "base",	cmd_base,	},
	{ "swap",	cmd_swap	},
	{ "scramble",	cmd_scramble	},
	{ "xrev",	cmd_xrev	},
	{ "quit",	cmd_quit	},
	{ "report",	cmd_report	},
	{ "history",	cmd_history	},
	{ "radix",	cmd_radix	},
	{ "mbox",	cmd_mbox	},
	{ "iomem",	cmd_iomem,	},
	{ "ioread",	cmd_iomem,	},
	{ "iowrite",	cmd_iowrite,	},
	{ "bugs",	cmd_bugs	},
	{ NULL,		0 		}
};

static void
dagbug(void)
{
	char prompt[64];
	char* remain_state;
	char* tok_state;
	char * line;
	char * tok, *remain;
	cmd_t *cmdp;
	char **ap, *argv[MAXARGV];
	int temp;

	for (;;)
	{
		(void) sprintf(prompt, "dagbug:%d> ", uHistoryCount + 1);

		line = dgetline(prompt);
		if (line == NULL)
			break;
		/*
		 * It's a bit of a mess here, since we would like to
		 * parse the line only once for spaces and the first
		 * token but we need the line in complete form for
		 * history and shell commands.
		 */
		remain = line + strspn(line, white);

		/* Truncate the line at any comment. */
		tok = strchr(remain, '#');
		if (tok != NULL)
		{
			*tok = '\0';
		}

		if (*remain != '\0')
		{
#if HAVE_EDITLINE
			add_history(remain);
#endif /* HAVE_EDITLINE */
			uHistoryCount++;

			tok = strtok_r(remain, ";", &remain_state);
			while (tok != NULL)
			{
				char first_char;

				tok += strspn(tok, white);
				first_char = (*tok);

				if ('!' == first_char)
				{
					/* Shell command. */
					temp =  system(tok+1);
				}
				else if ('\0' == first_char)
				{
					/* Do nothing. */
				}
				else
				{
					/* Regular command. */
					ap = argv;
					*ap = strtok_r(tok, white, &tok_state);
					while (*ap != NULL)
					{
						if (**ap != '\0')
						{
							if (++ap >= &argv[MAXARGV])
							{
								*--ap = NULL;
								dagutil_warning("too many arguments for command %s\n", argv[0]);
								break;
							}
						}
						*ap = strtok_r(NULL, white, &tok_state);
					}
#if 0
					printf("%s: %d args\n", argv[0], ap-argv);
#endif /* 0 */

					/* Find command in command table. */
					for (cmdp = cmdtab; cmdp->name != NULL; cmdp++)
					{
						if (0 == strncmp(argv[0], cmdp->name, strlen(argv[0])))
						{
							(*cmdp->func)(ap-argv, argv);
							break;
						}
					}
					if (cmdp->name == NULL)
						printf("%s: unknown command\n", argv[0]);
				}

				tok = strtok_r(NULL, ";", &remain_state);
			}
		}

		dagutil_free(line);
	}
}

static void
dagbaseupdate(void)
{
	dag_reg_t	result[DAG_REG_MAX_ENTRIES];
	unsigned int	regn;

	has_arm = 0;

	/* Has ARM? */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ARM, result, &regn)) && (regn))
		has_arm = 1;

	ram_base = 0;

	/* Find ARM RAM */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ARM_RAM, result, &regn)) && (regn))
		ram_base = DAG_REG_ADDR(*result);

	phy_base = 0;

	/* Find Phy */
	/* 3.2x */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ARM_PHY, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);

	/* 3.5/8 */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_SONIC_3, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);

	/* 3.6D */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_SONIC_D, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);

	/* 3.5E */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ICS1893, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);

	/* 4.1x */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ARM_PHY, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);

	/* 4.2s */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_VSC9112, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);

	/* 4.2ge */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_MINIMAC, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);

	/* 4.3s */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_PM5381, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);

	/* 4.3ge */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_PM3386, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);
	
	/* 6.x */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_S19205, result, &regn)) && (regn))
		phy_base = DAG_REG_ADDR(*result);
	
	pci_base = 0;

	/* Find ARM PCI */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ARM_PCI, result, &regn)) && (regn))
		pci_base = DAG_REG_ADDR(*result);

	xilinx1_base = 0;

	/* Find X1 */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ARM_X1, result, &regn)) && (regn))
		xilinx1_base = DAG_REG_ADDR(*result);

	xilinx2_base = 0;

	/* Find X2 */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ARM_X2, result, &regn)) && (regn))
		xilinx2_base = DAG_REG_ADDR(*result);

	xilinx3_base = 0;

	/* Find X3 */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ARM_X3, result, &regn)) && (regn))
		xilinx3_base = DAG_REG_ADDR(*result);

	eeprom_base = 0;

	/* Find EEPROM */
	regn=0;
	if (!(dag_reg_table_find(regs, 0, DAG_REG_ARM_EEP, result, &regn)) && (regn))
		eeprom_base = DAG_REG_ADDR(*result);
}


/*
 * This is a wrapper around readline which cannot handle changing
 * stdin in any way.
 */
static char *
dgetline(char *prompt)
{
	static char *buf;

	if (1 == uReadFromKeyboard)
	{
		if (0 < strlen(command_buf))
		{
			/* Use the -c parameter from the command line */
			if (0 == uHaveReadCommandLine)
			{
				uHaveReadCommandLine = 1;
				
				/* Return a malloc()d string so all strings returned by dgetline are malloc()d. */
				return (char*) dagutil_strdup((const char*) command_buf);
			}
			else
			{
				/* Already read the command line option */
				return NULL;
			}
		}
		else
		{
#if HAVE_EDITLINE
			/* Get the next line from readline(). */
			return readline(prompt);
#else
			/* Get the next line via standard I/O. */
			printf("%s", prompt);
#endif /* HAVE_EDITLINE */
		}
	}

	/* Note: do _not_ prompt or echo here. */
	buf = dagutil_malloc(BUFSIZ);
	if (buf == NULL)
		dagutil_panic("dgetline: out of memory\n");

	return fgets(buf, BUFSIZ, stdin);
}


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