/*
* Copyright (c) 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: dagptb.c 3172 2006-02-24 04:23:30Z koryn $
*/

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

/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagptb.c 3172 2006-02-24 04:23:30Z koryn $";
static const char* const kRevisionString = "$Revision: 3172 $";


#define PORT_A_BASE 0x0400
#define PORT_B_BASE 0x0c00
#define PORT_A_J1_OFFSET (PORT_A_BASE + 0x100)
#define PORT_B_J1_OFFSET (PORT_B_BASE + 0x100)
#define PATH_TRACE_BYTE_COUNT 64


#if (defined(__SVR4) && defined(__sun)) || defined(_WIN32)
extern char *dag_device_name(uint32_t device, uint32_t flag);
#endif

static char dagname_buf[DAGNAME_BUFSIZE] = "dag0";
static char dagname[DAGNAME_BUFSIZE];

static daginf_t* daginf;
static dag_reg_t* regs;
static int dagfd;
static int interval;
static volatile uint8_t* iom;


/* Internal routines. */
static void print_version(void);
static void print_usage(ClArgPtr clarg);
static void dag_get_pt_bytes(volatile char* iom, int port, char* buffer, unsigned int buflen);
static void dag_display_pt_bytes(char* buffer, unsigned int buflen);


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


static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagptb - Endace DAG path trace byte (aka SONET J1 byte) utility.\n");
	printf("Usage: dagptb [options]\n");
	dagclarg_display_usage(clarg, stdout);
}


static void
dag_get_pt_bytes(volatile char* iom, int port, char* buffer, unsigned int buflen)
{
	int offset;
	int i;

	assert(NULL != iom);
	assert(NULL != buffer);
	assert(PATH_TRACE_BYTE_COUNT == buflen);
	assert((0 <= port) && (port <= 1));
	
	if (0 == port)
	{
		offset = PORT_A_J1_OFFSET;
	}
	else
	{
		offset = PORT_B_J1_OFFSET;
	}
	
	/* Read path trace bytes in 32-bit chunks and put them in the buffer. */
	for (i = 0; i < (PATH_TRACE_BYTE_COUNT / 4); i++)
	{
		unsigned int value = *(volatile unsigned int*)(iom + offset + 4*i);

		buffer[3 + 4*i] = (char) ((value & 0xff000000) >> 24);
		buffer[2 + 4*i] = (char) ((value & 0x00ff0000) >> 16);
		buffer[1 + 4*i] = (char) ((value & 0x0000ff00) >> 8);
		buffer[0 + 4*i] = (char)  (value & 0x000000ff);
	}
}


static void
dag_display_pt_bytes(char* buffer, unsigned int buflen)
{
	int i;
	
	assert(NULL != buffer);
	assert(PATH_TRACE_BYTE_COUNT == buflen);

	for (i = 0; i < PATH_TRACE_BYTE_COUNT; i++)
	{
		if (0 == i % 16)
			printf("Bytes %02d - %02d: ", i, i + 15);
		else if (0 == i % 4)
			printf(" ");
		
		printf("%02x", (unsigned char) buffer[i]);
		
		if (15 == i % 16)
			printf("\n");
	}
	printf("\n");
}


/* Commandline argument codes. */
enum
{
	CLA_DEVICE,
	CLA_HELP,
	CLA_INTERVAL,
	CLA_VERBOSE,
	CLA_VERSION
};

int
dagptb_main(int argc, const char * const * argv)
{
	ClArgPtr clarg;
	FILE* errorfile = NULL;
	int code;
	int result;
	int argindex;
	int dagstream;
	uint32_t reg_count;
	dag_reg_t reg_array[DAG_REG_MAX_ENTRIES];

	dagutil_set_progname("dagptb");


	/* 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, "DAG device to use.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add_int(clarg, "Interval to repeat in seconds.", "--interval", 'i', "seconds", &interval, CLA_INTERVAL);
	dagclarg_add(clarg, "Increase verbosity.", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add(clarg, "Display version information.", "--version", 'V', CLA_VERSION);

	dagclarg_add(clarg, "This page.", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option(clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option(clarg, CLA_HELP, '?');


	/* Parse the command line options. */
	result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	

	while (1 == 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_HELP:
				print_usage(clarg);
				return EXIT_SUCCESS;
				break;

			case CLA_INTERVAL:
				if (interval <= 0)
				{
					interval = 1;
				}
				dagutil_verbose("interval=%d\n", interval);
				break;

			case CLA_VERBOSE:
				dagutil_inc_verbosity();
				break;

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

			default:
				/* 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 (CLA_INTERVAL == code)
		{
			/* No argument found for -i.
			* As a special case, we permit -i to have no argument and assume a default of 1.
			*/
			interval = 1;
		}
		else
		{
			if (argindex < argc)
			{
				dagutil_error("while processing option %s\n", argv[argindex]);
			}
			
			dagclarg_display_usage(clarg, stderr);
			return EXIT_FAILURE;
		}
	}

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


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

	/* Verify that the card is the correct type. */
	if (0 == (daginf->device_code & 0x3800))
	{
		dagutil_panic("Not a DAG 3.8S card (dagptb only works with DAG 3.8S cards).\n");
	}
	
	/* Verify that the card has the phymon firmware loaded (SONIC_3 version 2). */
	reg_count = 0;
	if ((dag_reg_table_find(regs, 0, DAG_REG_SONIC_3, reg_array, &reg_count)) || (0 == reg_count))
	{
		dagutil_panic("%s: SONIC OC3/12 PHY not found.\n", dagname);
	}

	if (0x02 != DAG_REG_VER(*reg_array))
	{
		dagutil_panic("%s: Phymon image not found.\n", dagname);
	}
	
	for (;;)
	{
		char buffer_a[PATH_TRACE_BYTE_COUNT];
		char buffer_b[PATH_TRACE_BYTE_COUNT];
	
		memset(buffer_a, 0, sizeof(buffer_a));
		memset(buffer_b, 0, sizeof(buffer_b));

		dag_get_pt_bytes((char*) iom, 0, buffer_a, PATH_TRACE_BYTE_COUNT);
		dag_get_pt_bytes((char*) iom, 1, buffer_b, PATH_TRACE_BYTE_COUNT);
		
		/* Display buffer for port A. */
		printf("Port A J1 bytes\n");
		dag_display_pt_bytes(buffer_a, PATH_TRACE_BYTE_COUNT);
		
		/* Display buffer for port B. */
		printf("Port B J1 bytes\n");
		dag_display_pt_bytes(buffer_b, PATH_TRACE_BYTE_COUNT);
		printf("\n");
	
		if (0 == interval)
		{
			break;
		}
		sleep(interval);
	}
	
	dag_close(dagfd);

	return EXIT_SUCCESS;
}


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