/*
 * Copyright (c) 2003-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: daginf.c 13826 2011-01-25 04:37:57Z karthik.sharma $
 */

/* Endace headers. */
#include "dagapi.h"
#include "dag_platform.h"
#include "dagutil.h"
#include "dagtoken.h"
#include "dagclarg.h"
#include "dagpci.h"
#include "dagerf.h"

/* CVS Header. */
static const char* const kCvsHeader __attribute__((unused)) = "$Id: daginf.c 13826 2011-01-25 04:37:57Z karthik.sharma $";
static const char* const kRevisionString = "$Revision: 13826 $";


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

static int print_all_dags = 1;

static int print_dag(int dagfd);
static void print_reg(dag_reg_t reg, int indent);

/* Internal routines. */
static void print_version(void);
static void print_usage(ClArgPtr clarg);

enum
{
	CLA_DEVICE,
	CLA_HELP,
	CLA_VERBOSE,
	CLA_VERSION
};


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


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


int
daginf_main(int argc, const char *argv[])
{
	int retval = 0;
	int found = 0;
	int dagn;
	int dagfd;
	int dagstream;
	ClArgPtr clarg;
	int result;
	FILE* errorfile = NULL;
	int argindex;
    int code;

	dagutil_set_progname("daginf");

	/* 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(clarg, "This page.", "--help", 'h', CLA_HELP);
	dagclarg_add_string(clarg, "DAG device to use.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add(clarg, "Display version information.", "--version", 'V', CLA_VERSION);
	dagclarg_add(clarg, "Increase verbosity.", "--verbose", 'v', CLA_VERBOSE);

	/* 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);
				print_all_dags = 0;
				break;
	
			case CLA_HELP:
				print_usage(clarg);
				return EXIT_SUCCESS;
				break;
	
			case CLA_VERBOSE:
				dagutil_inc_verbosity();
				errorfile = stderr;
				break;
	
			case CLA_VERSION:
				print_version();
				return EXIT_SUCCESS;
				break;

			default:
				if ((0 == strcmp(argv[argindex], "--usage")) || (0 == strcmp(argv[argindex], "-?")))
				{
					print_usage(clarg);
					return EXIT_SUCCESS;
				}
				else if (argv[argindex][0] == '-')
				{
					/* Unknown option. */
					dagutil_error("unknown option %s\n", argv[argindex]); 
					dagclarg_display_usage(clarg, stdout);
					return EXIT_FAILURE;
				}
				break;
		}
		result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}

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

	if (print_all_dags)
	{
		for (dagn = 0; dagn < 32; dagn++)
		{
			snprintf(dagname_buf, sizeof(dagname_buf), "dag%d", dagn);
			if (-1 == dag_parse_name(dagname_buf, dagname, sizeof(dagname), &dagstream))
			{
				dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
			}

			dagfd = dag_open(dagname);
			if (dagfd < 0)
				continue;

			found++;
			retval |= print_dag(dagfd);
		}

		if (0 == found)
			dagutil_panic("No DAG cards found! Is dag driver loaded?\n");

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

static int
print_dag(int dagfd)
{
	uint8_t* iom;
	daginf_t* daginf;
	dag_reg_t regs[DAG_REG_MAX_ENTRIES];
	int count = 0;
	int indent = 0;
	int c;
	int32_t devnid;
	dag_meminfo_t mem_info;
	int d;
	uint8_t linktype;
	int types;
	uint8_t erfs[TYPE_MAX];
	int found=0, numstreams=0;
	int rxstreams, txstreams;
	
	daginf = dag_info(dagfd);
	if(daginf == NULL)
	{
		printf("dag has not been opened\n");
		return EXIT_SUCCESS;
	}
	printf("card      dag%d\n",daginf->id);
	printf("id        %d\n", daginf->id);
	printf("model     %s\n", dag_device_name(daginf->device_code, 1));
	printf("device    0x%04x\n", daginf->device_code);
	printf("board rev %x\n", daginf->brd_rev);
	printf("phy addr  0x%08x\n", daginf->phy_addr);
	printf("buf size  %u (%uMB)\n", daginf->buf_size, daginf->buf_size>>20);
	printf("iom size  %u (%ukB)\n", daginf->iom_size, daginf->iom_size>>10);

#if defined(__linux__)
	printf("bus addr  %s\n", daginf->bus_id);

	if (!ioctl(dagfd, DAGIOCDEVNID, &devnid))
	{
		if(devnid >= 0)
	    		printf("Device NUMA node: %u \n",devnid);

	}

	if (!ioctl(dagfd, DAGIOCMEMINFO, &mem_info)) 
	{
	    for (count = 0; count < DAG_MAX_NODES; count++)
	    {
		if (mem_info.nid_pages[count] != 0)
		    printf("Memory at node %u: %u pages (%uMB)\n", count, mem_info.nid_pages[count],
			    (mem_info.page_size * mem_info.nid_pages[count])>>20);
	    }
	}
#endif /* __linux__ */
	iom = dag_iom(dagfd);
	
	count = dag_reg_find((char*) iom, 0, regs);
	if (count < 0)
		dagutil_panic("dag_reg_find failed: %s\n", strerror(count));

	printf("copro     ");
	
	printf("%s\n", dag_copro_name(dagutil_detect_coprocessor(iom), 1));
	
	rxstreams = dag_rx_get_stream_count(dagfd);
	txstreams = dag_tx_get_stream_count(dagfd);
	printf("rxstreams %d\n", rxstreams);
	printf("txstreams %d\n", txstreams);

	/* dag_linktype is a legacy interface */
	if(dagutil_get_verbosity() > 1) {
		linktype = dag_linktype(dagfd);
		if(linktype)
			printf("linktype  %s\n", dagerf_type_to_string(linktype, 0));
	}

	/* dag_get_erftypes is a legacy interface */
	if(dagutil_get_verbosity() > 1) {
		
		memset(erfs, 0, TYPE_MAX);
		types = dag_get_erf_types(dagfd, erfs, TYPE_MAX);
		if(types > 0) {
			for (c=0; c < types; c++) {
				printf("erf type  %s\n", dagerf_type_to_string(erfs[c], 0));
			}
		}
	}

	if(dagutil_get_verbosity())
	{
		numstreams = dag_rx_get_stream_count(dagfd);
		
		if(rxstreams > txstreams)
			numstreams = rxstreams * 2 - 1;
		else
			numstreams = txstreams * 2;
		
		for(d=0; d < DAG_STREAM_MAX; d++) {
			if ( ((d&1) && (d<=txstreams*2)) || (!(d&1) && (d<rxstreams*2)) ) { 
				
				types = dag_get_stream_erf_types(dagfd, d, erfs, TYPE_MAX);
				if(types > 0) {
						printf("stream %3d",d);
					for (c=0; c < types; c++) {
						if(c > 0)
							printf(", ");
						else
							printf(":\t");
						printf("%s",dagerf_type_to_string(erfs[c], 0) );
					}
					printf("\n");
					found++;
				}
				if (found >= numstreams)
					break;
			}
		}
	}
	
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
	munmap(iom, daginf->iom_size);
#endif

	dag_close(dagfd);

	if(dagutil_get_verbosity())
	{
		printf("\nRegister enumeration found %d entries\n", count);
		
		for (c = 0; c < count; c++)
		{
			switch(regs[c].module)
			{
				case DAG_REG_START:
					print_reg(regs[c], indent);
					indent++;
					break;
					
				case DAG_REG_END:
					indent--;
					/* Fallthrough. */
					
				default:
					print_reg(regs[c], indent);
					break;
			}
		}
	}
	printf("\n");

	return EXIT_SUCCESS;
}


static void
print_reg(dag_reg_t reg, int indent)
{
	int c;

	if (indent < 0)
		indent = 0;

	if (reg.flags & DAG_REG_FLAG_ARM)
	{
		printf("0x%08x: ", reg.addr << 16);
	}
	else
	{
		printf("0x%04x:     ", reg.addr);
	}
	
	for (c = 0; c < indent; c++)
	{
		printf("    ");
	}
	
	switch(reg.module) {
	case DAG_REG_START:	printf("Start "); break;
	case DAG_REG_GENERAL:	printf("General "); break;
	case DAG_REG_DUCK:	printf("DUCK ");
		switch(reg.version) {
		case 0: printf("100MHz ");break;
		case 1: printf("100MHz Indirect ");break;
		case 2: printf("50MHz ");break;
		case 3: printf("33MHz ");break;
		case 4: printf("62.5MHz ");break;
		case 5: printf("200MHz ");break;
		case 6: printf("125MHz ");break;
		}
		break;
	case DAG_REG_PBM: printf("PBM "); break;
	case DAG_REG_GPP: printf("GPP "); break;
	case DAG_REG_SONET: printf("SONET "); break;
	case DAG_REG_ROM: printf("ROM "); break;
	case DAG_REG_PTX: printf("PTX "); break;
	case DAG_REG_FPGAP: printf("FPGA Prog "); break;
	case DAG_REG_UART: printf("UART "); break;
	case DAG_REG_BTX: printf("BTX "); break;
	case DAG_REG_ARM: printf("ARM "); break;
	case DAG_REG_PM5355: printf("PM5355 "); break;
	case DAG_REG_VSC9112: printf("VSC9112 "); break;
	case DAG_REG_ICS1893: printf("ICS1893 "); break;
	case DAG_REG_SONIC_3: printf("SONIC OC3/12 "); break;
	case DAG_REG_SONIC_D: printf("SONIC DS3 "); break;
	case DAG_REG_MINIMAC: printf("Minimac "); break;
	case DAG_REG_S19205: printf("S19205 "); break;
	case DAG_REG_PM5381: printf("PM5381 "); break;
	case DAG_REG_MAR: printf("MAR "); break;
	case DAG_REG_HDIM: printf("HDIM "); break;
	case DAG_REG_MIPF: printf("MIPF "); break;
	case DAG_REG_COPRO: printf("Co-Pro "); break;
	case DAG_REG_ARM_RAM: printf("ARM RAM "); break;
	case DAG_REG_ARM_PCI: printf("ARM PCI "); break;
	case DAG_REG_ARM_X1: printf("ARM Xilinx 1 "); break;
	case DAG_REG_ARM_X2: printf("ARM Xilinx 2 "); break;
	case DAG_REG_ARM_X3: printf("ARM Xilinx 3 "); break;
	case DAG_REG_ARM_PHY: printf("ARM PHY "); break;
	case DAG_REG_ARM_EEP: printf("ARM EEPROM "); break;
	case DAG_REG_PM3386: printf("PM3386 "); break;
	case DAG_REG_CAM_MAINT: printf("CAM Maint "); break;
	case DAG_REG_CAM_SEARCH: printf("CAM Search "); break;
	case DAG_REG_COPRO_BIST: printf("CoPro BIST "); break;
	case DAG_REG_BIST_DG: printf("BIST DG "); break;
	case DAG_REG_COPRO_COUNTERS: printf("CoPro Counters "); break;
	case DAG_REG_SAR_CTRL: printf("SAR "); break;
	case DAG_REG_MSA300_RX: printf("MSA300 Rx "); break;
	case DAG_REG_MSA300_TX: printf("MSA300 Tx "); break;
	case DAG_REG_PPF: printf("PPF "); break;
	case DAG_REG_ATX: printf("ATX "); break;
	case DAG_REG_QDR: printf("QDR Detect "); break;
	case DAG_REG_ATM_ARMOUR: printf("ATM Checker "); break;
	case DAG_REG_BOPT_GEN: printf("BOPT Generator "); break;
	case DAG_REG_BOPT_RESET: printf("BOPT Reset "); break;
	case DAG_REG_SONIC_E1T1: printf("SONIC E1/T1 "); break;
	case DAG_REG_E1T1_CTRL: printf("Exar 83L38 "); break;
	case DAG_REG_TERF64: 
		switch(reg.version)
		{
			case 0: printf("TERF64 "); break;
			case 1: case 2: case 3: printf("TERF2 "); break;
		}
		break;
	case DAG_REG_PP64: printf("Packet Processor 64 "); break;
	case DAG_REG_DISP64: printf("Dispatcher 64 "); break;
	case DAG_REG_DP83865: printf("DP83865 "); break;
	case DAG_REG_TAP: printf("Ethernet TAP Detect "); break;
	case DAG_REG_SMB: printf("SMBus controller "); break;
	case DAG_REG_E1T1_HDLC_DEMAP: printf("E1/T1 HDLC Demap "); break;
	case DAG_REG_E1T1_HDLC_MAP: printf("E1/T1 HDLC Map "); break;
	case DAG_REG_E1T1_ATM_DEMAP: printf("E1/T1 ATM Demap "); break;
	case DAG_REG_E1T1_ATM_MAP: printf("E1/T1 ATM Map "); break;
	case DAG_REG_THRESHMODE: printf("Sampling Ctrl "); break;
	case DAG_REG_PL3_ARMOUR: printf("PL3 Armour "); break;
	case DAG_REG_RC: printf("Raw Capture "); break;
	case DAG_REG_RAWCAPTYPE: printf("Raw Capture type "); break;
	case DAG_REG_RAW_TX: printf("Raw TX "); break;
	case DAG_REG_SAM_MAINT: printf("SAM Maint "); break;
	case DAG_REG_MV88E1111: printf("Marvell 88E1111 "); break;
	case DAG_REG_AD2D_BRIDGE: printf("Asynchronous DRB to DRB Bridge "); break;
	case DAG_REG_BIST: printf("General BIST "); break;
	case DAG_REG_FAILSAFE: printf("Failsafe "); break;
	case DAG_REG_BPFI: printf("Inline BPF "); break;
	case DAG_REG_IDT_TCAM: printf("IDT TCAM Interface (75P52100) "); break;
	case DAG_REG_AMCC: printf("AMCC Quad OC3/12 Ser/Des "); break;
	case DAG_REG_XRT79L72: printf("Exar Dual Channel DS3/E3 Framer "); break;
	case DAG_REG_RAW_SMBBUS: printf("RAW SMBus "); break;
	case DAG_REG_RAW_MAXDS3182: printf("Maxim DS3/E3 Framer "); break;
	case DAG_REG_STEERING: printf("ERF Steering ");
		switch (reg.version)
		{
			case 0: printf("(IP Filter) "); break;
			case 1: printf("(Hash Load Balancing) "); break;
			default: printf("(Unknown) "); break;
		}
		break;
	case DAG_REG_BITHDLC_DEMAP: printf("Bit HDLC Demap "); break;
	case DAG_REG_SRGPP: printf("Size-reduced GPP "); break;
	case DAG_REG_IXP: printf("IXP "); 
		switch (reg.version)
		{
			case 0: printf("EXP Register Bridge "); break;
			case 1: printf("EXP FIFO Bridge "); break;
			default: printf("Unknown "); break;
		}
		break;
	case DAG_REG_HMM: printf("Simple rule-based packet filter "); break;
	case DAG_REG_STATS: printf("SC256 statistics ");
		switch (reg.version)
		{
			case 0: printf("Basic "); break;
			case 1: printf("Flow analysis "); break;
			default: printf("Unknown "); break;
		}
		break;
	case DAG_REG_DROP: printf("Drop module "); break;
	case DAG_REG_RANDOM_CAPTURE: printf("SC256 random packet and flow capture "); break;
	case DAG_REG_VSC8476: printf("VSC8476 PHY"); 
		switch (reg.version)
		{
			case 0: printf("MDIO interface "); break;
			case 1: printf("control registers "); break;
			default: printf("Unknown "); break;
		}
		break;
	case DAG_REG_XGMII: printf("XGMII module "); break;
	case DAG_REG_HLB: printf("HLB association table "); break;
// Notsused and by sonic means sonet the same 
	case DAG_REG_OC48_DEFRAMER: printf("SONIC OC48 Deframer "); break;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	  /* 0x56 ? */
	case DAG_REG_AMCC3485: printf("AMCC3485 "); break;
	case DAG_REG_DSMU: printf("DSMU module "); break;
	case DAG_REG_DPA: printf("DPA "); break;
	  /* 0x5a ? */
	  /* 0x5b ? */
	case DAG_REG_COUNT_INTERF: printf("Universal Counters Statistics Interface "); break;
	  /* 0x5d ? */
	  /* 0x5e ? */
	  /* 0x5f ? */
	  /* 0x60 ? */
	  /* 0x61 ? */
	case DAG_REG_INFINICLASSIFIER: printf("INFINIBAND Classifier "); break;
	  /* 0x63 ? */
	case DAG_REG_XFP: printf("XFP/SFP/SFP+ port "); break;
	case DAG_REG_VSC8479: printf("VSC8479"); break; 
	case DAG_REG_SONET_PP: printf("SONET Packet processor "); break;
	  /* 0x67 ? */
	case DAG_REG_XGE_PCS: printf("XGE Physical Coding Sublayer "); break;			  
	case DAG_REG_CAT: printf ("CAT module "); break;
	case DAG_REG_STREAM_FTR: printf ("Per Stream Features "); break;
	  /* 0x6b ? */
	  /* 0x6c ? */
	case DAG_REG_ROM_SERIAL_NUMBER: printf("ROM serial number "); break;
	case DAG_REG_INFI_FRAMER: printf("INFINIBAND Framer "); break;
	case DAG_REG_INFINICAM: printf("TCAM IDT75K "); break;
	case DAG_REG_43GE_LASER_CONTROL: printf("4.3GE Laser Control "); break;
	case DAG_REG_GTP_PHY: printf("GTP PHY ");break;
	case DAG_REG_MEMTX: printf ("MEMTX Module "); break;
	case DAG_REG_DUCK_READER: printf("DUCK Reader "); break;
	case DAG_REG_IRIGB: printf("IRIG-B module "); break;
	case DAG_REG_VSC8486: printf("VSC8486 "); break;
	case DAG_REG_BFSCAM: printf("BFS CAM "); break;
	case DAG_REG_PATTERN_MATCH: printf("Pattern Match "); break;
	case DAG_REG_SONET_CHANNEL_DETECT: printf("SONET Channel Detect "); break;
	case DAG_REG_SONET_CHANNEL_CONFIG_MEM: printf("SONET Channel Config Memory "); break;
	case DAG_REG_SONET_RAW_SPE_EXTRACT: printf("SONET Raw SPE Extract "); break;
	case DAG_REG_QT2225_FRAMER: printf("QT2225 Framer "); break;
	case DAG_REG_RESET_STRATEGY: printf("Reset Control Register "); break;
	case DAG_REG_PHY_MOD: printf("Phy Mode "); break;
	case DAG_REG_TILE_CTRL: printf("Tile Control ");break;
	case DAG_REG_SCRATCH_PAD: printf ("Scratch Pad Register ");break;
	case DAG_REG_END: printf("End "); break;
        default: printf("unknown module %d (0x%02x) ", reg.module, reg.module);
	}
	
	printf("v%d ", reg.version);

	if (reg.flags & DAG_REG_FLAG_TABLE)
		printf("New table ");

	printf("\n");
}


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