/*
 * 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: dagsar.c 13453 2010-12-02 23:30:08Z jomi.gregory $
 */

/* Endace headers. */
#include "dagapi.h"
#include "dagnew.h"
#include "dagreg.h"
#include "dagtoken.h"
#include "dagutil.h"
#include "dagpci.h"
#include "dagclarg.h"
#include "dag_smbus.h"


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


/* Internal routines. */
static void sar_reset(void);
static void cam_init(int uni);

static void fill_steer_array(char *);
static int sar_config(int argc, char *argv[]);
static int sar_status(void);
static int sar_stats(int interval);

static void write_csr( unsigned rd_cnt, unsigned wr_cnt, unsigned go );
static void write_reg( unsigned addr, unsigned long long value );

extern void dagopt_scan_string(const char *);
extern int dagoptlex(void);
extern char *dagopttext;
extern int dagoptlval;

#define OPTBUFSIZE 1024
#define BUFSIZE 256

static char steer_buf[BUFSIZE];

enum
{
	FUNC_NONE	= 0x00,
	FUNC_FIRST	= 0x01,
	FUNC_CONFIG	= 0x01,
	FUNC_STATUS	= 0x02,
	FUNC_STATS	= 0x04,
	FUNC_DEFAULT	= FUNC_STATUS,
	FUNC_LAST	= 0x08
};

enum
{
	CHAN_A 		= 0x01,
	CHAN_B 		= 0x02,
	CHAN_BOTH 	= 0x03
};

static int CAM_MAINT = 0;	/* 0x2100 */
static int SAR_CTRL = 0;	/* 0x2184 */
static int COPRO_CNT = 0;	/* 0x23c4 */
static int CP_ARMOUR = 0;	/* 0x1ff8 */
static int smb_base = 0;	/* 0x2198 */

static int SAR_UNI = 0;

static int SAR_OAM = 0; 

enum
{ /* from CAM_MAINT */
	CAM_CSR			= 0x80
};

enum
{ /* from SAR_CTRL */
	SAR_Global_SM_Ctrl	= 0x00,
	SAR_Buf_Cnt_Size	= 0x04
};

enum
{ /* From COPRO_CNT */
	STAT_CSR		= 0x00,
	STAT_Status_0		= 0x04,
	STAT_Status_1		= 0x08,
	STAT_Status_2		= 0x0c,
	
	STATS_CSR_0		= 0x10, /* Steerable */
	STATS_CSR_1		= 0x14,
	STATS_CSR_2		= 0x18,
	STATS_CSR_3		= 0x1c,
	
	STATM_CAM_Ent_Max	= 0x20, /* Motley / Miscellaneous */
	STATM_BP_Ent_Max	= 0x24,
	STATM_Cells_IM_Max	= 0x28,
	STATM_CAM_Ent_InUse	= 0x2c,
	STATM_BP_outof_AQ	= 0x30,
	STATM_BP_in_DQ		= 0x34,
	STATM_Cells_InMem	= 0x38,
	/* nothing at 0x233c */
	STATM_chanA_base	= 0x40, /* Motley - Channel A */
	STATM_chanB_base	= 0x50, /* Motley - Channel B */

	STATP_WRSide_wr_BW	= 0x60, /* Performance (DDR) */
	STATP_WRSide_rd_BW	= 0x64,
	STATP_RDSide_wr_BW	= 0x68,
	STATP_RDSide_rd_BW	= 0x6c,
	STATP_WRSide_af_wr	= 0x70,
	STATP_WRSide_af_hwr	= 0x74,
	STATP_RDSide_af		= 0x78,
	/* nothing at 0x237c */

	STATS_Cntr_base		= 0x80, /* not going to list all of these! */
	
	STATD_chanA_base	= 0xc0, /* Dedicated - Channel A */
	STATD_chanB_base	= 0xe0 /* Dedicated - Channel B */
};

enum
{ /* from STATM_chanA_base or STATM_chanB_base */
	SOFFM_Discard_AQ	= 0x00,
	SOFFM_Discard_Aged	= 0x04,
	SOFFM_Discard_Fifo	= 0x08,
	SOFFM_DF_CellOK		= 0x0c
};

enum
{ /* from STATD_chanA_base or STATD_chanB_base */
	SOFFD_cell_arrive	= 0x00,
	SOFFD_pti_cell_arrive	= 0x04,
	SOFFD_oam_cell_arrive	= 0x08,
	SOFFD_srch_ffull 	= 0x0c,
	SOFFD_decide_bypass 	= 0x10,
	SOFFD_decide_discard 	= 0x14,
	SOFFD_crc_err 		= 0x18,
	SOFFD_frame_into_GPP 	= 0x1c
};

enum
{ /* Event numbers - bits within the large vectors */
	EV_aq_push = 0,
	EV_BOTH_LOW = EV_aq_push,
	EV_aq_pop,
	EV_dq_push,
	EV_dq_pop,
	EV_dq_pp,
	EV_wr_wr,
	EV_wr_rd,
	EV_rd_wr,
	EV_rd_rd,
	EV_wr_af_hwr,
	EV_wr_af_wr,
	EV_wr_af,
	EV_nfad,
	EV_gpp_frame_ok,
	EV_cam_full,
	EV_req_ffull,
	EV_dq_ffull,
	EV_dq_overflow,
	EV_aq_overflow,
	EV_dq_underrun,
	EV_aqdq_busy,
	EV_cam_busy,
	EV_sam_busy,
	EV_srch_can_pause,
	EV_srch_is_paused_rh,
	EV_srch_is_paused_lh,
	EV_srch_is_idle_lh,
	EV_sam_is_paused,
	EV_sam_is_idle,
	EV_ma_can_pause,
	EV_ma_is_paused,
	EV_ma_is_idle,
	EV_ddrwr_can_pause,
	EV_ddrwr_is_paused,
	EV_ddrwr_is_idle,
	EV_ddrrd_can_pause,
	EV_ddrrd_is_paused,
	EV_ddrrd_is_idle,
	EV_df_can_pause,
	EV_df_is_paused,
	EV_df_is_idle,
	EV_BOTH_HIGH = EV_df_is_idle,
	
	EV_any_cell_A,
	EV_A_LOW = EV_any_cell_A,
	EV_pti_cell_A,
	EV_oam_cell_A,
	EV_dotted_ffull_A,
	EV_bypass_A,
	EV_discard_A,
	EV_lrnd_A,
	EV_hit_A,
	EV_deld_A,
	EV_lrnd_srch_A,
	EV_sam_frame_ok_A,
	EV_sam_cell_ok_A,
	EV_sam_age_ev_A,
	EV_sam_aq_empty_A,
	EV_sam_cell_discard_A,
	EV_ma_cell_ok_mem_A,
	EV_ma_cell_disc_A,
	EV_ma_frame_ok_dq_A,
	EV_ma_frame_disc_A,
	EV_oversized_frame_A,
	EV_df_frame_ok_A,
	EV_srch_busy_err_A,
	EV_srch_fsm_err_A,
	EV_srch_rerr_A,
	EV_sam_overwrote_vld_A,
	EV_crc_err_A,
	EV_A_HIGH = EV_crc_err_A,
	
	EV_any_cell_B,
	EV_B_LOW = EV_any_cell_B,
	EV_pti_cell_B,
	EV_oam_cell_B,
	EV_dotted_ffull_B,
	EV_bypass_B,
	EV_discard_B,
	EV_lrnd_B,
	EV_hit_B,
	EV_deld_B,
	EV_lrnd_srch_B,
	EV_sam_frame_ok_B,
	EV_sam_cell_ok_B,
	EV_sam_age_ev_B,
	EV_sam_aq_empty_B,
	EV_sam_cell_discard_B,
	EV_ma_cell_ok_mem_B,
	EV_ma_cell_disc_B,
	EV_ma_frame_ok_dq_B,
	EV_ma_frame_disc_B,
	EV_oversized_frame_B,
	EV_df_frame_ok_B,
	EV_srch_busy_err_B,
	EV_srch_fsm_err_B,
	EV_srch_rerr_B,
	EV_sam_overwrote_vld_B,
	EV_crc_err_B,
	EV_B_HIGH = EV_crc_err_B
};

/* User-visible names for each "event". Also used for Steerable counters
 * counting those events.
 */
static const char *EV_NAME[] =
{
	"aq push",		"aq pop",
	"dq push",		"dq pop", 		// 3
	"dq pp",		"wr wr",
	"wr rd",		"rd wr", 		// 7
	"rd rd",		"wr af hwr",
	"wr af wr",		"wr af",		// 11
	"nfad",			"gpp frame ok",
	"cam full",		"req ffull",		// 15
	"dq ffull",		"dq overflow",
	"aq overflow",		"dq underrun",		// 19
	"aqdq busy",		"cam busy",
	"sam busy",		"srch can pause",	// 23
	"srch is paused rh",	"srch is paused lh",
	"srch is idle lh",	"sam is paused",	// 27
	"sam is idle",		"ma can pause",
	"ma is paused",		"ma is idle",		// 31
	"ddrwr can pause",	"ddrwr is paused",
	"ddrwr is idle",	"ddrrd can pause",	// 35
	"ddrrd is paused",	"ddrrd is idle",
	"df can pause",		"df is paused",		// 39
	"df is idle",
	"any cell A",		"pti cell A",		// 42
	"oam cell A",		"dotted ffull A",
	"bypass A",		"discard A",		// 46
	"lrnd A",		"hit A",
	"deld A",		"lrnd srch A",		// 50
	"sam frame ok A",	"sam cell ok A",
	"sam age ev A",		"sam aq empty A",	// 54
	"sam cell discard A",	"ma cell ok mem A",
	"ma cell disc A",	"ma frame ok dq A",	// 58
	"ma frame disc A",	"oversized frame A",
	"df frame ok A",	"srch busy err A",	// 62
	"srch fsm err A",	"srch rerr A",
	"sam overwrote vld A",	"crc err A",		// 66
	"any cell B",		"pti cell B",
	"oam cell B",		"dotted ffull B",	// 70
	"bypass B",		"discard B",
	"lrnd B",		"hit B",		// 74
	"deld B",		"lrnd srch B",
	"sam frame ok B",	"sam cell ok B",	// 78
	"sam age ev B",		"sam aq empty B",
	"sam cell discard B",	"ma cell ok mem B",	// 82
	"ma cell disc B",	"ma frame ok dq B",
	"ma frame disc B",	"oversized frame B",	// 86
	"df frame ok B",	"srch busy err B",
	"srch fsm err B",	"srch rerr B",		// 90
	"sam overwrote vld B",	"crc err B",

};

/* Each event is classed as one of the following types */
enum
{
  	Ierr	= 0x01, /* Error (always visible)*/
	Ifsm	= 0x02, /* Finite State Machine Status (verbose>1)*/
	Iprf	= 0x04, /* Performance Statistic (verbose>1)*/
	Idbg	= 0x08, /* Debugging (verbose)*/
	Ivrb	= 0x10 /* Verbose (verbose>1)*/
};
	
static unsigned EVIS[] =
{
	Idbg, Idbg, Idbg, Idbg,  Idbg, Iprf, Iprf, Iprf, //  0 -  7
	Iprf, Iprf, Iprf, Iprf,  Idbg, Ivrb, Ierr, Ierr, //  8 - 15
	Ierr, Ierr, Ierr, Ierr,  Ierr, Ierr, Ierr, Ifsm, // 16 - 23
	Ifsm, Ifsm, Ifsm, Ifsm,  Ifsm, Ifsm, Ifsm, Ifsm, // 24 - 31
	Ifsm, Ifsm, Ifsm, Ifsm,  Ifsm, Ifsm, Ifsm, Ifsm, // 32 - 39
	Ifsm,						 // 40
	Ivrb, Ivrb, Ivrb, Ivrb,  Idbg, Idbg, Idbg, // 41 - 47
	Idbg, Idbg, Idbg, Idbg,  Idbg, Idbg, Idbg, Idbg, // 48 - 55
	Idbg, Idbg, Idbg, Idbg,  Idbg, Idbg, Ierr, Ierr, // 56 - 63
	Ierr, Ierr, Ivrb,				 // 64 - 66
	Ivrb, Ivrb, Ivrb, Ivrb,  Idbg, // 67 - 71
	Idbg, Idbg, Idbg, Idbg,  Idbg, Idbg, Idbg, Idbg, // 72 - 79
	Idbg, Idbg, Idbg, Idbg,  Idbg, Idbg, Idbg, Idbg, // 80 - 87
	Ierr, Ierr, Ierr, Ierr,  Ivrb, 0,    0,    0,    // 88 - 95
};

static int util = 0, extend=0, perf=0, steer=0, cwhich = CHAN_BOTH;
static int temp = 0;
static volatile uint8_t* dagiom = NULL;

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

static volatile unsigned *csr;
static volatile unsigned *cam_maint;
static dag_reg_t* regs;
static unsigned cam_busy = 1;
static unsigned TO_MAX = 1000;

#define SCMAX 8
static unsigned steering[SCMAX] = 
{
	EV_lrnd_A, EV_hit_A, EV_deld_A, EV_nfad,
	EV_sam_frame_ok_A, EV_ma_frame_ok_dq_A,
	EV_srch_rerr_A, EV_srch_busy_err_A
};

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


static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagsar - Endace DAG card segmentation and reassembly utility.\n");
	printf("Usage: dagsar [-hvsabeup] [-d dagN] [-c N,M,O,...,U] [-i interval]\n");
	dagclarg_display_usage(clarg, stdout);
	printf("\nThe following configuration options are available:\n");
	printf("   default          Reset SAR\n");
	printf("   reset            Reset SAR\n");
	printf("   uni              Set UNI mode\n");
	printf("   nni              Set NNI mode\n");
	printf("   enablesar        Start SAR\n");
	printf("   disablesar       Pause SAR\n");
	printf("   long=N           Maximum length packet to reassemble is N\n");
	printf("   pathoam          Enable Path OAM capture\n");
	printf("   nopathoam        Disable Path OAM capture\n");

}



enum
{

	CLA_HELP,
	CLA_VERSION,
	CLA_VERBOSE,
	CLA_DEVICE,
	CLA_STATS,
	CLA_PORTA,
	CLA_PORTB,
	CLA_ESTATS,
	CLA_USTATS,
	CLA_DDRSTATS,
	CLA_TEMP,
	CLA_STEER,
	CLA_INTERVAL
};

int
dagsar_main(int argc, char *argv[])
{
	int help = 0;
	int dagfd = 0;
	int func = FUNC_DEFAULT;
	int mask;
	int interval = 0;
	char sbuf[128] = {0};
	dag_reg_t result[DAG_REG_MAX_ENTRIES];
	unsigned regn;
	uint64_t data;
	coprocessor_t copro_type;
	ClArgPtr clarg = NULL;
	int argindex;
	int clarg_result;
	int code;
	FILE *errorfile = NULL;


	memset(result, 0, sizeof(result));
	dagutil_set_progname("dagsar");

	/* 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, "display help (this page).", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option(clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option(clarg, CLA_HELP, '?');
	dagclarg_add(clarg, "display version information.", "--version", 'V', CLA_VERSION);
	dagclarg_add(clarg, "increase verbosity.", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add_string(clarg, "DAG device to use.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add(clarg, "show statistics counters.", "--stats", 's', CLA_STATS);
	dagclarg_add(clarg, "only show statistics for port A.", "--portA", 'a', CLA_PORTA);
	dagclarg_add(clarg, "only show statistics for port B.", "--portB", 'b', CLA_PORTB);
	dagclarg_add(clarg, "show extended statistics counters.", "--estats", 'e', CLA_ESTATS);
	dagclarg_add(clarg, "show utilisation statistics (as percentages).", "--ustats", 'u', CLA_USTATS);
	dagclarg_add(clarg, "show (DDR) performance statistics counters.", "--ddrstats", 'p', CLA_DDRSTATS);
	dagclarg_add(clarg, "show CoPro temperatures (degrees C).", "--temp", 't', CLA_TEMP);
	dagclarg_add_string(clarg, "load steerable counters (up to 8, depending on Firmware)\n                                     Argument to 'c' is a comma-separated list of numbers","--ldcntr", 'c', "c1, c2 ...., c8", steer_buf, BUFSIZE, CLA_STEER);
	dagclarg_add_int(clarg, "repeat statistics with interval in seconds", "--interval", 'i', "interval", &interval, CLA_INTERVAL);


	/* Parse the command line options. */
	clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
			case CLA_HELP:
                /*
				print_usage(clarg);
				return EXIT_SUCCESS;
                */
                help = 1;
				break;

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

			case CLA_VERSION:
				print_version();
				return EXIT_SUCCESS;
				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));
				}
				break;
				
			case CLA_STATS:
				func = FUNC_STATS;
				break;

			case CLA_PORTA:
				cwhich = CHAN_A;
				break;

			case CLA_PORTB:
				cwhich = CHAN_B;
				break;

			case CLA_ESTATS:
				func |= FUNC_STATS;
				extend++;
				break;

			case CLA_USTATS:
				func |= FUNC_STATS;
				util++;
				break;

			case CLA_DDRSTATS:
				func |= FUNC_STATS;
				perf++;
				break;

			case CLA_TEMP:
				func |= FUNC_STATS;
				temp++;
				break;

			case CLA_STEER:
				strncpy(sbuf, steer_buf, 127);
				sbuf[127] = '\0';
				steer++;
				break;

			case CLA_INTERVAL:
                if(interval == 0)
                {
                    interval = 1;
                }
				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 (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;
        }
	}

	argv = (char**) dagclarg_get_unprocessed_args(clarg, &argc);

	
	/* Display Usage Message */
	if (help)
	{
		printf("dagsar (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
		print_usage(clarg);
		if (dagutil_get_verbosity())
		{
			int i;

			fprintf(stderr, "Steerable Counters available:\n");
			for (i = EV_BOTH_LOW; i<=EV_B_HIGH; i++ )
			{
				int odd = i & 0x01;

				if (odd)
					fprintf(stderr, " | ");
				
				fprintf(stderr, "%3u\t%-30s", i, EV_NAME[i]);

				if (odd)
					fprintf(stderr, "\n");
			}
			fprintf(stderr, "\n");
		}
		exit(EXIT_SUCCESS);
	}

	/* ----------------------------------------------------------------- */
	/* Open DAG card, check for Copro */
	if((dagfd = dag_open(dagname)) < 0)
		dagutil_panic("dag_open %s: %s\n", dagname, strerror(errno));
	dagiom = dag_iom(dagfd);
	if (!dagiom)
		dagutil_panic("%s: dag_iom failed\n", dagname);

	regs = dag_regs(dagfd);

	/* ----------------------------------------------------------------- */
	/* Check for Copro */
	copro_type = dagutil_detect_coprocessor(dagiom);
	if (kCoprocessorNotSupported == copro_type)
	{
		dagutil_panic("%s: CoProcessor support not present!\n", dagname);
	}
	else if (false == dagutil_coprocessor_programmed(dagiom))
	{
		dagutil_panic("%s: CoProcessor not Programmed!\n", dagname);
	}
	
	/* ----------------------------------------------------------------- */
	/* Verbosity, report the type of copro found */
	dagutil_verbose("%s: CoProcessor: %s\n", dagname, dag_copro_name(copro_type, 1));
	
	/* ----------------------------------------------------------------- */
	/* Register bases (Maintenance) and Steerable Counter Parsing */

	/* Find SAR_CTRL */
	regn=0;
	if ((dag_reg_table_find(regs, 0, DAG_REG_SAR_CTRL, result, &regn)) || (!regn))
		dagutil_panic("%s: SAR module not present!\n", dagname);
	else
    {
		SAR_CTRL = DAG_REG_ADDR(*result);
        /* Hidden Register */
        SAR_OAM = SAR_CTRL - 4;
    }

	/* Find COPRO_COUNTERS */
	regn=0;
	if ((dag_reg_table_find(regs, 0, DAG_REG_COPRO_COUNTERS, result, &regn)) || (!regn))
		dagutil_panic("%s: Copro Counters module not present!\n", dagname);
	else
		COPRO_CNT = DAG_REG_ADDR(*result);
	
	/* Find pre-copro ATM Armour (DAG_REG_ATM_ARMOUR) */
	regn=0;
	if ((dag_reg_table_find(regs, 0, DAG_REG_ATM_ARMOUR, result, &regn)) || (!regn))
		dagutil_panic("%s: ATM Checker module not present!\n", dagname);
	else
		CP_ARMOUR = DAG_REG_ADDR(*result);
	
	/* Find CAM_MAINT */
	regn=0;
	if ((dag_reg_table_find(regs, 0, DAG_REG_CAM_MAINT, result, &regn)) || (!regn))
		dagutil_panic("%s: Maintenance module not present!\n", dagname);
	else
		CAM_MAINT = DAG_REG_ADDR(*result);
	
	csr  = (volatile unsigned *)(dagiom + CAM_MAINT + CAM_CSR);
	cam_maint = (volatile unsigned *)(dagiom + CAM_MAINT);

	/* Find SMBus controller */
	regn=0;
	if ((dag_reg_table_find(regs, 0, DAG_REG_SMB, result, &regn)) || (!regn))
		dagutil_panic("%s: smb_base Controller not present!\n", dagname);
	else
	{
		/* Pick the correct SMBus Controller from the match(es). */
		smb_base = dagutil_smb_init(dagiom, result, regn);
	}

	if(steer)
		fill_steer_array(sbuf);
	if( 0==(*(volatile unsigned *)(dagiom+COPRO_CNT+STAT_CSR) & 0x100)) {
		cwhich = CHAN_A; /* only single channel available. */
		dagutil_verbose("Single Channel ATMR\n");
	}

	/* Read current CAM settings */
	cam_busy = (*csr & 0x10)?1:0;

	/* read gmr */
	write_reg(0, 0x000210000LL);
	write_csr(1, 1, 1);
	data = ((uint64_t) (cam_maint[1] & 0x0f) << 32) | cam_maint[0];

	if(data == 0x90ffffff8LL)
		SAR_UNI = 1;
	else if (data == 0x9fffffff8LL)
		SAR_UNI = 0;
	else dagutil_verbose("GMR misread, GMR=%"PRIu64", setting NNI mode\n", data);

	/* ----------------------------------------------------------------- */
	/* Select a function and perform it */
	if(func != FUNC_DEFAULT)
		func &= ~FUNC_DEFAULT;

	if(argc > 0) /* catch config keywords on command-line */
		func = (FUNC_CONFIG|FUNC_STATUS);

	for( mask = FUNC_FIRST ; mask < FUNC_LAST ; mask <<= 1 )
		switch(func&mask)
		{
		case FUNC_NONE:
			continue;
		case FUNC_CONFIG:
			sar_config(argc, argv);
			break;
		case FUNC_STATUS:
			sar_status();
			break;
		case FUNC_STATS:
			sar_stats(interval);
			break;
		default:
			dagutil_panic("internal error %s at line %u\n", __FILE__, __LINE__);
		}
	
	/* ----------------------------------------------------------------- */
	/* End-of-main */
	if (dagfd > -1 )
		dag_close(dagfd);

	return EXIT_SUCCESS;
}


static void
fill_steer_array(char *string)
{
	char 		*p = string;
	int 		i=0;
	unsigned 	val, imax, vmax;
	
	val = *(volatile unsigned *)(dagiom+COPRO_CNT+STAT_CSR);
	if( (val & BIT11)==0)
		dagutil_panic("Steerable counters not available in this image!\n");
	imax = (val & 0xff);
	dagutil_verbose("Found: %u Firmware Steerable Counters (Software supports %u)\n", imax, SCMAX);
	vmax = (val & 0x100) ? EV_B_HIGH : EV_A_HIGH;
	
	while( p )
	{
		val = strtoul(p, NULL, 0);
		if( val > vmax && val <= EV_B_HIGH )
		{
			dagutil_verbose("Skipping counter \"%s\" - single channel firmware\n", EV_NAME[val]);
			goto skip;
		}
		if( val > vmax )
			dagutil_panic("Illegal argument to 'c', value #%u was %d (legal range is 0 to %u inclusive)\n", i, val, vmax );
		if( i >= (signed)imax )
			dagutil_panic("Firmware only supports %u Steerable counters\n", imax);
		if( i >= SCMAX )
			dagutil_panic("Software only supports %u Steerable counters\n", SCMAX);

		dagutil_verbose("Steerable Counter %u will count \"%s\" events\n", i, EV_NAME[val]);
		steering[i++] = val;
		
		/* strchr will return NULL eventually, which terminates the
		 * while loop */
	skip:
		if( NULL != (p = strchr( p, ',')) )
			p++;
	}
	steer = i;
}


static int
sar_stats(int interval)
{
	unsigned sdbase;
	unsigned smbase;
	struct timeval st_time;
	struct timeval cr_time;
	struct timeval en_time;
	double usec;
	int count = 0;
	int dual_channel = 0;
	int i = 0;
	int j = 0;
	unsigned int shift;
	unsigned int val;
	unsigned int max = 0;
	unsigned int cam_max = 65535;
	unsigned int bps_max = 32767;
	unsigned int cim_max = 2097088; /* sensible defaults */
	uint8_t rtemp = 0;

	if( util )
	{
		cam_max = *(volatile unsigned *)(dagiom+COPRO_CNT+STATM_CAM_Ent_Max);
		bps_max = *(volatile unsigned *)(dagiom+COPRO_CNT+STATM_BP_Ent_Max);
		cim_max = *(volatile unsigned *)(dagiom+COPRO_CNT+STATM_Cells_IM_Max);
	}
	
	if( steer )
	{
		val = *(volatile unsigned *)(dagiom+COPRO_CNT+STAT_CSR);
		if( (val & BIT11)==0)
			dagutil_panic("Steerable counters not available in this image!\n");
		max = steer;
		if( (val & 0xff) < max )
			max = val & 0xff;
		
		/* Load steerable counters */
		for( i=0; i<(signed)max; i++ )
		{
			val = steering[i];
			if( val < 32 )
				shift = (1<<val);
			else
				shift = 0x0;
			*(volatile unsigned *)(dagiom+COPRO_CNT+STATS_CSR_1) = shift;
			if( val >= 32 && val < 63 )
				shift = (1<<(val-32));
			else
				shift = 0x0;
			*(volatile unsigned *)(dagiom+COPRO_CNT+STATS_CSR_2) = shift;
			if( val >= 64 )
				shift = (1<<(val-64));
			else
				shift = 0x0;
			*(volatile unsigned *)(dagiom+COPRO_CNT+STATS_CSR_3) = shift;
			*(volatile unsigned *)(dagiom+COPRO_CNT+STATS_CSR_0) = i;
		}
	}
	dual_channel = (*(volatile unsigned *)(dagiom+COPRO_CNT+STAT_CSR) & 0x100) ? 1 : 0;
again:
	gettimeofday(&st_time, 0);
	/* Latch and Clear all counters */
	*(volatile unsigned *)(dagiom+COPRO_CNT+STAT_CSR) = 0;
	usleep(1000);
	if((count++ % 20) == 0)
	{
		for(j=CHAN_A; j<CHAN_BOTH; j<<=1)
		{
			if( (cwhich & j) == 0 )
				continue;
			if( dual_channel && j==CHAN_A )
				printf(" A:");
			if( dual_channel && j==CHAN_B )
				printf(" B:");
			printf(" TotCells  Lcells OAMcell  Frames CRCFail");
			if(extend) {
				printf(" FifoFull");
				if(dagutil_get_verbosity()) {
					printf("  Bypass Discard");
				}
				printf(" disc_AQ discAged discFifo DF_Cells");
			}
		}
		if( dual_channel && (dagutil_get_verbosity() > 1 || perf || steer) )
			printf(" |");

		if(util)
			printf("  %%CAM %%AQ %%DQ %%Cells_IM");
		if(perf)
			printf("     RD_AF     WR_WH     WR_WI  WW (k)  WR (k)  RW (k)  RR (k)");
		if(steer)
		{
			for( i = 0; i<(signed)max; i++ )
			{
				printf("  %12.12s", EV_NAME[steering[i]] );
			}
		}
		if(temp)
		{
			printf(" Tamb Txil");
		}
		printf("\n");
	}
	
	for(j=CHAN_A; j<CHAN_BOTH; j<<=1) {
		smbase = STATM_chanA_base+COPRO_CNT;
		sdbase = STATD_chanA_base+COPRO_CNT;
		if( (cwhich & j) == 0 )
			continue;
		if( dual_channel && j==CHAN_A )
		{
			smbase = STATM_chanA_base+COPRO_CNT;
			sdbase = STATD_chanA_base+COPRO_CNT;
			printf(" A:");
		}
		if( dual_channel && j==CHAN_B )
		{
			smbase = STATM_chanB_base+COPRO_CNT;
			sdbase = STATD_chanB_base+COPRO_CNT;
			printf(" B:");
		}
		
		printf(" %8u", *(volatile unsigned *)(dagiom + sdbase+SOFFD_cell_arrive));
		printf(" %7u", *(volatile unsigned *)(dagiom + sdbase+SOFFD_pti_cell_arrive));
		printf(" %7u", *(volatile unsigned *)(dagiom + sdbase+SOFFD_oam_cell_arrive));
		printf(" %7u", *(volatile unsigned *)(dagiom + sdbase+SOFFD_frame_into_GPP));
		printf(" %7u", *(volatile unsigned *)(dagiom + sdbase+SOFFD_crc_err));
		if(extend)
		{
			printf("  %7u", *(volatile unsigned *)(dagiom + sdbase+SOFFD_srch_ffull));
			if(dagutil_get_verbosity())
			{
				printf(" %7u", *(volatile unsigned *)(dagiom + sdbase+SOFFD_decide_bypass));
				printf(" %7u", *(volatile unsigned *)(dagiom + sdbase+SOFFD_decide_discard));
			}
			printf(" %7u", *(volatile unsigned *)(dagiom + smbase+SOFFM_Discard_AQ));
			printf("  %7u", *(volatile unsigned *)(dagiom + smbase+SOFFM_Discard_Aged));
			printf("  %7u", *(volatile unsigned *)(dagiom + smbase+SOFFM_Discard_Fifo));
			printf("  %7u", *(volatile unsigned *)(dagiom + smbase+SOFFM_DF_CellOK));
		}
	}
	if( dual_channel && (dagutil_get_verbosity()>1 || perf || steer) )
		printf(" |");

	if(util)
	{
		printf("   %3u", (*(volatile unsigned *)(dagiom+COPRO_CNT+STATM_CAM_Ent_InUse) * 100) / cam_max );
		printf(" %3u", (*(volatile unsigned *)(dagiom+COPRO_CNT+STATM_BP_outof_AQ) * 100) / bps_max );
		printf(" %3u", (*(volatile unsigned *)(dagiom+COPRO_CNT+STATM_BP_in_DQ) * 100) / bps_max );
		printf("       %3u", (*(volatile unsigned *)(dagiom+COPRO_CNT+STATM_Cells_InMem) * 100) / cim_max );
	}

	if(perf)
	{
		printf(" %9u", *(volatile unsigned *)(dagiom+COPRO_CNT+STATP_RDSide_af));
		printf(" %9u", *(volatile unsigned *)(dagiom+COPRO_CNT+STATP_WRSide_af_hwr));
		printf(" %9u", *(volatile unsigned *)(dagiom+COPRO_CNT+STATP_WRSide_af_wr));
		printf(" %7u", (*(volatile unsigned *)(dagiom+COPRO_CNT+STATP_WRSide_wr_BW)*4)/1000);
		printf(" %7u", (*(volatile unsigned *)(dagiom+COPRO_CNT+STATP_WRSide_rd_BW)*16)/1000);
		printf(" %7u", (*(volatile unsigned *)(dagiom+COPRO_CNT+STATP_RDSide_wr_BW)*16)/1000);
		printf(" %7u", (*(volatile unsigned *)(dagiom+COPRO_CNT+STATP_RDSide_rd_BW)*4)/1000);
	}

	if(steer)
	{
		for( i = 0; i < (signed)max; i++ )
		{
			printf("     %9u", *(volatile unsigned *)(dagiom+COPRO_CNT+STATS_Cntr_base + 4*i));
		}
	}

	if(temp)
	{
		if (0 == dagutil_smb_read(dagiom, smb_base, LM63, LM63_TEMP_LOC, &rtemp))
		{
			dagutil_panic("SMB read TEMP LOC failed\n");
		}
		printf(" %4d", rtemp);
		usleep(500);	
		if (0 == dagutil_smb_read(dagiom, smb_base, LM63, LM63_TEMP_REM_H, &rtemp))
		{
			dagutil_panic("SMB read TEMP REM failed\n");
		}
		
		printf(" %4d", rtemp);
	}
	printf("\n");
	if(interval)
	{
		/* better than sleep(1), but there must be a better way yet */
		/* probably using select */
		en_time.tv_sec = st_time.tv_sec + interval;
		en_time.tv_usec = st_time.tv_usec;
		gettimeofday(&cr_time, 0);
		usec = (en_time.tv_sec - cr_time.tv_sec)*1000000 +
			(en_time.tv_usec - cr_time.tv_usec);
		do {
#if !defined(_WIN32)
			usleep(usec*0.9);
#else /* _WIN32 */
			usleep((DWORD)(usec*0.9));
#endif /* _WIN32 */
			gettimeofday(&cr_time, 0);
			usec = (en_time.tv_sec - cr_time.tv_sec)*1000000 +
				(en_time.tv_usec - cr_time.tv_usec);
		} while( usec > 100000 );
		if( usec > 100 )
			dagutil_microsleep((uint32_t)usec);
		goto again;
	}
	return 0;
}


static void
pretty_sr( char *msg, unsigned mask,
		   unsigned long long ev_both,
		   unsigned long long ev_chanA,
		   unsigned long long ev_chanB,
		   unsigned dual_channel )
{
	const int M = 4;
	int i, any;
	printf("%s", msg);
	
	for(any=0,i=0;i<=EV_BOTH_HIGH;i++)
	{
		if( (EVIS[i]&mask)!=0 && (ev_both & (1ULL<<i)) != 0 )
		{
			printf("%s%s%s", (any==0)?"":", ",
			       ((any%M)==0&&any>0)?"\n\t":"", EV_NAME[i]);
			any++;
		}
	}
	
	for(i=EV_A_LOW;i<=EV_A_HIGH;i++)
	{
		if((EVIS[i]&mask)!=0 && (ev_chanA&(1ULL<<(i-EV_A_LOW))) != 0)
		{
			printf("%s%s%s", (any==0)?"":", ",
			       ((any%M)==0&&any>0)?"\n\t":"", EV_NAME[i]);
			any++;
		}
	}
	
	if( !dual_channel )
	{
		printf("%s\n", (any)?"":"(none)");
		return;
	}
	for(i=EV_B_LOW;i<=EV_B_HIGH;i++) {
		if((EVIS[i]&mask)!=0 && (ev_chanB&(1ULL<<(i-EV_B_LOW))) != 0)
		{
			printf("%s%s%s", (any==0)?"":", ",
			       ((any%M)==0&&any>0)?"\n\t":"", EV_NAME[i]);
			any++;
		}
	}
	printf("%s\n", (any)?"":"(none)");
}

static int
sar_status(void)
{
	unsigned long long ev1, ev2, ev3;
	unsigned verb1, verb2, verb3;
	unsigned val;
	int i;
	uint64_t data;
	char *armour_ev[] =
		{ "good_sop", "good_eop", "idle_err", "idle_eop",
		  "dump_sop", "dump_eop", "idle_cell_sop", "idle_cell_eop",
		  "cell_err", "cell_len", "cell_sop", "UNUSED_11" };
	unsigned vb_lvl[] = {2,2,0,1,
						 0,0,2,2,
						 0,0,0,2};

	printf("SAR\t");
	val = *(volatile unsigned *)(dagiom+SAR_CTRL+SAR_Global_SM_Ctrl);
	if (dagutil_get_verbosity()>1)
	{
		if (val & BIT23)
			printf("Running ");
		if (val & BIT22)
			printf("Priming ");
		if (val & BIT21)
			printf("Pausing ");
		if (val & BIT20)
			printf("Paused ");
	}
	printf("%sablesar ", (val & BIT15)?"dis":"en");
	printf("SAM %s ", (val & BIT2)?"Busy":"Ready");
	printf("CAM %s ", (val & BIT1)?"Busy":"Ready");
	printf("AQDQ %s ", (val & BIT0)?"Busy":"Ready");

    val = *(volatile unsigned *)(dagiom+SAR_OAM);
    printf("%spathoam ", (val & BIT0)?"":"no");

	/* read gmr */
	write_reg(0, 0x000210000LL);
	write_csr(1, 1, 1);
	data = ((uint64_t) (cam_maint[1] & 0x0f) << 32) | cam_maint[0];

	if(data == 0x90ffffff8LL)
		printf("UNI ");
	else if (data == 0x9fffffff8LL)
		printf("NNI ");
	else printf("GMR=%"PRIu64, data);

	printf("\n");

	printf("buffers\t");
	val = *(volatile unsigned *)(dagiom+SAR_CTRL+SAR_Buf_Cnt_Size);
	printf("number=%u ", ((unsigned)(val >> 16)) & 0xffff );
	printf("long=%u ", ((val >> 8) & 0xff) * 768 - 8 );
	printf("\n");

	val = *(volatile unsigned *)(dagiom+CP_ARMOUR);
	if( (val & 0xf000) != 0xf000 )
	{
		printf("atmchkA\t");
		if( (val & 0xf3c) == 0x0 )
		{
			printf( "(no errors) ");
		}
		for( i = 0; i < 12; i++ )
		{
			if( (val & 1) && (dagutil_get_verbosity() >= (signed)vb_lvl[i]) )
				printf( "%s ", armour_ev[i] );
			val >>=1;
		}
		val>>=4;
		printf("\n");
		if( (val & 0xf000) != 0xf000 )
		{
			printf("atmchkB\t");
			if( (val & 0xf3c) == 0x0 )
			{
				printf( "(no errors) ");
			}
			for( i = 0; i < 12; i++ )
			{
				if( (val & 1) && (dagutil_get_verbosity() >= (signed)vb_lvl[i]) )
					printf( "%s ", armour_ev[i] );
				val >>=1;
			}
			val>>=4;
			printf("\n");
		}
		*(volatile unsigned *)(dagiom+CP_ARMOUR) = 0;
	}
	
	if (dagutil_get_verbosity())
	{
		printf("STATS\t");
		val = *(volatile unsigned *)(dagiom+COPRO_CNT+STAT_CSR);
		printf("%s", (val & BIT9)?"Dedicated_A ":"");
		printf("%s", (val & BIT10)&&(val&BIT8)?"Dedicated_B ":"");
		if( (val & BIT11)!= 0 )
		{
			printf("Steerable x%u ", val & 0xff);
		}
		printf("%s", (val & BIT12)?"Miscellaneous ":"");
		printf("%s", (val & BIT13)?"Performance ":"");
		printf("\n");

#if 0
		if( val & BIT12 )
		{
			printf("Capacity\t");
			printf("%d CAM Entries, ", *(volatile unsigned *)(dagiom+COPRO_CNT+STATM_CAM_Ent_Max));
			printf("%d BufPtrs, ", *(volatile unsigned *)(dagiom+COPRO_CNT+STATM_BP_Ent_Max));
			printf("%d Cells in Memory", *(volatile unsigned *)(dagiom+COPRO_CNT+STATM_Cells_IM_Max));
			printf("\n");
		}
#endif
	}
	verb1 = *(volatile unsigned *)(dagiom+COPRO_CNT+STAT_Status_0);
	verb2 = *(volatile unsigned *)(dagiom+COPRO_CNT+STAT_Status_1);
	verb3 = *(volatile unsigned *)(dagiom+COPRO_CNT+STAT_Status_2);
		
	printf("Events\t");
#if 0
	if (dagutil_get_verbosity())
	{
		printf("both: 0x%.3x%.8x ", (verb2&0x1ff), verb1 );
		printf("A: 0x%.1x%.5x ", verb3&0x7, (verb2>>9) );
		if((val & BIT8)!=0) 
			printf("B: 0x%.6x ", (verb3>>3) );
	}
#endif
	printf("\n");
	ev1 = ((unsigned long long)(verb2&0x1ff) << 32) | (unsigned long long)verb1;
	ev2 = ((unsigned long long)(verb3&0x7) << 23) | (unsigned long long)(verb2>>9);
	ev3 =  (unsigned long long)(verb3>>3);
	pretty_sr("  Err\t",Ierr, ev1,ev2,ev3, (val & BIT8));
	if( dagutil_get_verbosity()>1)
	{
		pretty_sr("  FSM\t",Ifsm, ev1,ev2,ev3, (val & BIT8));
		pretty_sr("  Prf\t",Iprf, ev1,ev2,ev3, (val & BIT8));
		pretty_sr("  Verb\t",Ivrb, ev1,ev2,ev3, (val & BIT8));
	}
	if( dagutil_get_verbosity() )
		pretty_sr("  Dbg\t",Idbg, ev1,ev2,ev3, (val & BIT8));
	
	
	/* ltch-and-clr stat counters and stat event SRs */
	*(volatile unsigned *)(dagiom+COPRO_CNT+STAT_CSR) = 0;

	return 0;
}


static int
sar_config(int argc, char *argv[])
{
	int val, opt, no, tok, c;
	char buffer[OPTBUFSIZE];

	memset(buffer, 0, OPTBUFSIZE);
	for( opt = 0 ; opt < argc; opt++ )
	{
		if (strlen(buffer)+strlen(argv[opt]) > OPTBUFSIZE)
			dagutil_panic("Too many options!\n");
		strcat(buffer, argv[opt]);
		strcat(buffer, " ");
	}

	dagopt_scan_string(buffer);
	while((tok = dagoptlex()) != 0)
	{
		no = !dagoptlval;
		switch(tok)
		{
		case T_ERROR:
			dagutil_panic("Configuration option '%s' not recognized\n", dagopttext);
			break;
			
		case T_LINK_RESET:
			dagutil_start_copro_fan(dagiom, smb_base);
			sar_reset();
			break;
			
		case T_DEFAULT:
            /*Set PathOAM to no*/
            *(volatile unsigned *)(dagiom+SAR_OAM) &= ~BIT0;
		case T_SAR_NNI:
			SAR_UNI = 0;
			dagutil_start_copro_fan(dagiom, smb_base);
			sar_reset();
			break;
			
		case T_SAR_UNI:
			SAR_UNI = 1;
			dagutil_start_copro_fan(dagiom, smb_base);
			sar_reset();
			break;
			
		case T_SAR_ENABLE:
			if(no)
			{
				*(volatile unsigned *)(dagiom+SAR_CTRL+SAR_Global_SM_Ctrl) = BIT12;
			}
			else
			{
				*(volatile unsigned *)(dagiom+SAR_CTRL+SAR_Global_SM_Ctrl) = BIT13;
			}
			break;
			
		case  T_POS_MAXLEN: /* overloaded for maximum packet reassembly size */
			val = (int)((dagoptlval+8+767)/768)*1024;
			val = (val-1) >> 11;
			for(c=0; val>0; c++)
				val = val >> 1;
			if (c>5) c=5;
			*(volatile unsigned *)(dagiom+SAR_CTRL+SAR_Buf_Cnt_Size) = c;
			break;
        case T_SAR_PATH_OAM:
            if(no)
            {
                *(volatile unsigned *)(dagiom+SAR_OAM) &= ~BIT0;
            }
            else
            {
                *(volatile unsigned *)(dagiom+SAR_OAM) |= BIT0;
            }
            break;
			
		default:
			dagutil_panic("Configuration option '%s' is not valid for this card type\n", dagopttext);
		}
	}
	return 0;
}

static void
sar_reset(void)
{
	int		cnt = 1000000;

	/* Set CAM Busy first */
	*csr = (BIT31 | BIT4);
	
	/* Do Pause */
	*(volatile unsigned *)(dagiom+SAR_CTRL+SAR_Global_SM_Ctrl) = BIT12;

	/* Global SM DP reset */
	*(volatile unsigned *)(dagiom+SAR_CTRL+SAR_Global_SM_Ctrl) = BIT31;

	/* CAM Init */
	cam_init(SAR_UNI);
	/*printf( "%.8x ", *(volatile unsigned *)(dagiom+SAR_Global_SM_Ctrl) );*/
	/* busy wait until the code is actually paused. */
	while( ((*(volatile unsigned *)(dagiom+SAR_CTRL+SAR_Global_SM_Ctrl)) & 0x40f0a007) ^ 0x40108000 )
	{
		dagutil_nanosleep(200);
		cnt--;
		if( !cnt ) {
			printf(" Timeout! " );
			break;
		}
	}

	/* Do UnPause */
	/*printf( "%.8x ", *(volatile unsigned *)(dagiom+SAR_Global_SM_Ctrl) );*/
	*(volatile unsigned *)(dagiom+SAR_CTRL+SAR_Global_SM_Ctrl) = BIT13;
			
	/* clear armour SR-flops */
	*(volatile unsigned *)(dagiom+CP_ARMOUR) = 0;
}

static void
cam_init(int uni)
{
	*csr = (BIT31 | BIT4);	/* RST and BUSY */
	cam_busy = 1;

#if !defined(_WIN32)
	/* load config */
	write_reg(0, 0x008000030LL);
	write_reg(1, 0x000000010LL);
	write_csr( 0, 2, 1 );
	dagutil_nanosleep(6500); /* paranoia */
	
	/* learn config */
	write_reg(0, 0x000020000LL);
	write_reg(1, 0x0000000e0LL);
	write_reg(2, 0x000000000LL);
	write_reg(3, 0x00000ffffLL);
	write_csr( 0, 4, 1 );

	/* thread config */
	write_reg(0, 0x000040000LL);
	write_reg(1, 0x000000000LL);
	write_csr( 0, 2, 1 );

	/* init nfa-bist */
	write_reg(0, 0x000f00000LL);
	write_reg(1, 0x000000808LL);
	write_reg(2, 0x10000000bLL);
	write_csr( 0, 3, 1 );
	dagutil_nanosleep(6500); /* paranoia */

	/* clear nfa-bist */
	write_reg(0, 0x000f00000LL);
	write_reg(1, 0x000000000LL);
	write_reg(2, 0x000000000LL);
	write_csr( 0, 3, 1 );

	/* core reset to invalidate entire table */
	write_reg(0, 0x000400000LL);
	write_csr( 0, 1, 1);

	/* load EMR[0] to support ATM searches */
	write_reg(0, 0x000220000LL);
	if (uni)
		write_reg(1, 0x90ffffff8LL); /* UNI, usr/mgmnt bit */
	else
		write_reg(1, 0x9fffffff8LL); /* NNI, usr/mgmnt bit */
	write_csr( 0, 2, 1 );
	
	/* load EMR[1] to support ATM NFA Updates */
	write_reg(0, 0x000220001LL);
	write_reg(1, 0x800000000LL); /* Match any entry */
	write_csr( 0, 2, 1 );
	dagutil_nanosleep(2000);
	
	/* load GMR[0] to support ATM searches
	 * (GMR[0] is used by firmware) */
	write_reg(0, 0x000200000LL);
	if (uni)
		write_reg(1, 0x90ffffff8LL); /* UNI, usr/mgmnt bit */
	else
		write_reg(1, 0x9fffffff8LL); /* NNI, usr/mgmnt bit */
	write_csr( 0, 2, 1 );
	
 	/* load GMR[1] to support ATM NFA Updates
	 * (GMR[1] is used by firmware for this purpose) */
	write_reg(0, 0x000200001LL);
	write_reg(1, 0x800000000LL); /* Match any entry */
	write_csr( 0, 2, 1 );
	dagutil_nanosleep(2000);
	
	/* load NFA to support learning for ATM */
	write_reg(0, 0x000280000LL);
	write_reg(1, 0x000000000LL);
	write_csr( 0, 2, 1 );

	/* Load BDR with the bit pattern needed for NFA update to succeed */
	write_reg(0, 0x000240000LL);
	write_reg(1, 0x800000000LL); 
	write_csr( 0, 2, 1 );
	dagutil_nanosleep(2000);
	
	/* burst-write BDR to all memory entries */
	write_reg(0, 0x000890001LL); /* invalid, EMR[1] */
	write_reg(1, 0x000000000LL); /* saddr = 0 */
	write_reg(2, 0x000010000LL); /* count = 65536 */
	write_csr( 0, 3, 1 );
	dagutil_nanosleep(2000);

	/*
	 * CAM should now be all set to run searches on ATM cells
	 */

	/* blank the array */
	write_reg(0, 0x000000000LL);
	write_reg(1, 0x000000000LL);
	write_reg(2, 0x000000000LL);
	write_reg(3, 0x000000000LL);

#else /* _WIN32 */

	/* load config */
	write_reg(0, 0x008000030i64);
	write_reg(1, 0x000000010i64);
	write_csr( 0, 2, 1 );
	dagutil_nanosleep(6500); /* paranoia */
	
	/* learn config */
	write_reg(0, 0x000020000i64);
	write_reg(1, 0x0000000e0i64);
	write_reg(2, 0x000000000i64);
	write_reg(3, 0x00000ffffi64);
	write_csr( 0, 4, 1 );

	/* thread config */
	write_reg(0, 0x000040000i64);
	write_reg(1, 0x000000000i64);
	write_csr( 0, 2, 1 );

	/* init nfa-bist */
	write_reg(0, 0x000f00000i64);
	write_reg(1, 0x000000808i64);
	write_reg(2, 0x10000000bi64);
	write_csr( 0, 3, 1 );
	dagutil_nanosleep(6500); /* paranoia */

	/* clear nfa-bist */
	write_reg(0, 0x000f00000i64);
	write_reg(1, 0x000000000i64);
	write_reg(2, 0x000000000i64);
	write_csr( 0, 3, 1 );

	/* core reset to invalidate entire table */
	write_reg(0, 0x000400000i64);
	write_csr( 0, 1, 1);

	/* load EMR[0] to support ATM searches */
	write_reg(0, 0x000220000i64);
	if (uni)
		write_reg(1, 0x90ffffff8i64); /* UNI, usr/mgmnt bit */
	else
		write_reg(1, 0x9fffffff8i64); /* NNI, usr/mgmnt bit */
	write_csr( 0, 2, 1 );
	
	/* load EMR[1] to support ATM NFA Updates */
	write_reg(0, 0x000220001i64);
	write_reg(1, 0x800000000i64); /* Match any entry */
	write_csr( 0, 2, 1 );
	dagutil_nanosleep(2000);
	
	/* load GMR[0] to support ATM searches
	 * (GMR[0] is used by firmware) */
	write_reg(0, 0x000200000i64);
	if (uni)
		write_reg(1, 0x90ffffff8i64); /* UNI, usr/mgmnt bit */
	else
		write_reg(1, 0x9fffffff8i64); /* NNI, usr/mgmnt bit */
	write_csr( 0, 2, 1 );
	
 	/* load GMR[1] to support ATM NFA Updates
	 * (GMR[1] is used by firmware for this purpose) */
	write_reg(0, 0x000200001i64);
	write_reg(1, 0x800000000i64); /* Match any entry */
	write_csr( 0, 2, 1 );
	dagutil_nanosleep(2000);
	
	/* load NFA to support learning for ATM */
	write_reg(0, 0x000280000i64);
	write_reg(1, 0x000000000i64);
	write_csr( 0, 2, 1 );

	/* Load BDR with the bit pattern needed for NFA update to succeed */
	write_reg(0, 0x000240000i64);
	write_reg(1, 0x800000000i64); 
	write_csr( 0, 2, 1 );
	dagutil_nanosleep(2000);
	
	/* burst-write BDR to ai64 memory entries */
	write_reg(0, 0x000890001i64); /* invalid, EMR[1] */
	write_reg(1, 0x000000000i64); /* saddr = 0 */
	write_reg(2, 0x000010000i64); /* count = 65536 */
	write_csr( 0, 3, 1 );
	dagutil_nanosleep(2000);

	/*
	 * CAM should now be all set to run searches on ATM cells
	 */

	/* blank the array */
	write_reg(0, 0x000000000i64);
	write_reg(1, 0x000000000i64);
	write_reg(2, 0x000000000i64);
	write_reg(3, 0x000000000i64);
#endif /* _WIN32 */

	/* update busy but don't perform any operation */
	cam_busy = 0;
	write_csr( 0, 0, 0 );
	dagutil_nanosleep(2000);
}


static void
write_csr( unsigned rd_cnt, unsigned wr_cnt, unsigned go )
{
	unsigned timeout = 1000;
	unsigned data;
	
	data = ((rd_cnt & 0x0f) << 24) | ((wr_cnt & 0x0f) << 20) |
		((cam_busy & 1) << 4) | (go & 1);
	
	timeout = TO_MAX;
	while( !((*csr >> 13) & 1) && timeout > 0 )
	{
		timeout--;
		dagutil_nanosleep(2000);
	}
	if( timeout < 1 )
		fprintf(stderr, "*** CSR: Pre Timeout waiting for rdy ***\n");
		
	*csr = data;
	if( go & 1 ) {
		timeout = TO_MAX;
		while( !((*csr >> 13) & 1) && timeout > 0 )
		{
			timeout--;
			dagutil_nanosleep(2000);
		}
		if( timeout < 1 )
			fprintf(stderr, "*** CSR: Post Timeout waiting for rdy ***\n");

		dagutil_nanosleep(2000);
		if( dagutil_get_verbosity()>1 && (*csr)& 0x08 )
		{
			if( (((*csr) >> 16) & 0x0f) != (((*csr) >> 24) & 0x0f) )
				fprintf(stderr, "*** CSR: nread %.1x != aread %.1x ***\n",
						(((*csr) >> 24) & 0x0f), (((*csr) >> 16) & 0x0f) );
			printf("CSR_FSM: %c%c%c%c%c TO:%c RDY:%c BV: %c\n",
			       ((*csr)&0x02)?'i':'_', ((*csr)&0x04)?'b':'_',
			       ((*csr)&0x20)?'W':'_', ((*csr)&0x40)?'R':'_',
			       ((*csr)&0x80)?'w':'_',
			       ((*csr)&0x8000)?'1':'0',
			       ((*csr)&0x2000)?'1':'0',
			       ((*csr)&0x1000)?'1':'0');
		}
	}

}

static void
write_reg( unsigned addr, unsigned long long value )
{
	unsigned lowbits, highbits;
	
	addr = (addr & 0x07) * 2;
	
	lowbits = (unsigned)(value & 0xffffffffL);
	highbits = (unsigned)((value >> 32) & 0x0f);

	*(cam_maint + addr) = lowbits;
	*(cam_maint + addr + 1) = highbits;
}


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