/*
 * Dag SAR stub program
 * Runs on the host and tests SAR related APIs
 * $Id: dagsar_stub.c 11428 2009-06-23 06:55:26Z vladimir $
 */

/*****************************************************************************
 * Includes
 *****************************************************************************/
#include <dagapi.h>
#include <dagutil.h>
#include <dagema.h>
#include <dagsarapi.h>
#include "dag37t_api.h"

#ifndef _WIN32
#include <unistd.h>
#endif
#include <signal.h>  

#include <dagclarg.h>
#include <dag_config_api.h>


/*****************************************************************************
 * Constants
 *****************************************************************************/
#define DAGNAME_STR_SIZE	30
#define DEVICE_STR_SIZE		30
#define FILTER_STR_SIZE		100
#define AALMODE_STR_SIZE	10

#define FILTER_ACCEPT	0
#define FILTER_REJECT	1

enum {
	CLA_DEVICE,
	CLA_HELP,
	CLA_ACTIVATE,
	CLA_DEACTIVATE,
	CLA_CID_ACTIVATE,
	CLA_CID_DEACTIVATE,
	CLA_GETSARMODE,
	CLA_SETSARMODE_AAL0,
	CLA_SETSARMODE_AAL2,
	CLA_SETSARMODE_AAL5,
	CLA_SETSARMODE_AAL0_TX,
	CLA_SETSARMODE_AAL2_TX,
	CLA_SETSARMODE_AAL5_TX,
	CLA_SETNETMODE_UNI,
	CLA_SETNETMODE_NNI,
	CLA_SETFILTER,
	CLA_CLEARFILTER,
	CLA_PORT,
	CLA_CHANNEL,
	CLA_VPI,
	CLA_VCI,
	CLA_CID,
	CLA_RESET,
	CLA_GETSTATS,
	CLA_RESETSTATS,
	CLA_SCAN,
	CLA_VERBOSE,
	CLA_NO_ERFMUX,
	CLA_STRIP_AAL5_TRAILER,
	CLA_NO_IXP,
	CLA_IXP_TXRX,
	CLA_IXP_RX,
	CLA_DONT_STRIP_AAL5_TRAILER,
	CLA_ENABLE_ATM_FORWARDING,
	CLA_DISABLE_ATM_FORWARDING
};

#define DEFAULT_DAGNAME "dag0"


/* cvs/svn header */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagsar_stub.c 11428 2009-06-23 06:55:26Z vladimir $";
static const char* const kRevisionString = "$Revision: 11428 $";


/*****************************************************************************
 * Data types
 *****************************************************************************/
typedef struct {
	/* commands to execute */
	uint32_t is_activation;
	uint32_t is_deactivation;
	uint32_t is_setto_aal0;
	uint32_t is_setto_aal2;
	uint32_t is_setto_aal5;	
	uint32_t is_setto_aal0tx;
	uint32_t is_setto_aal2tx;
	uint32_t is_setto_aal5tx;	
	uint32_t is_reset_proc;
	uint32_t is_get_stats;
	uint32_t is_reset_stats;
	uint32_t is_set_net_mode_uni;
	uint32_t is_set_net_mode_nni;
	uint32_t is_get_sar_mode;
	uint32_t is_apply_filter;
	uint32_t is_clear_filter;
	uint32_t is_scan_connections;
	uint32_t is_cid_activation;
	uint32_t is_cid_deactivation;
	uint32_t is_set_trailer_mode;
	uint32_t is_set_forwarding_mode;

	/* circuit data */
	uint32_t iface;
	uint32_t channel;
	uint32_t vpi;
	uint32_t vci;
	uint32_t cid;

	/* filter data */
	uint32_t bitmask;
	uint32_t match;
	uint32_t action;

	/* strings from command line */
	char dagname_str[DAGNAME_STR_SIZE];
	char device_str[DEVICE_STR_SIZE];
	char filter_str[FILTER_STR_SIZE];

	/* misc stuff */
	uint32_t scan_seconds;
	int32_t  dagstream;
	uint32_t setup_erf_mux;
	uint32_t setup_erfmux_no_ixp;
	uint32_t setup_erfmux_ixp_txrx;
	uint32_t setup_erfmux_ixp_rx;
	trailer_strip_mode_t strip_aal5_trailer;
	atm_forwarding_mode_t atm_forwarding_mode;
	
} global_options_t;


/*****************************************************************************
 * Global variables
 *****************************************************************************/
global_options_t global_options;


/*****************************************************************************
 * Functions
 *****************************************************************************/

/*****************************************************************************/
void init_global_options () {
	global_options.is_activation = 0;
	global_options.is_deactivation = 0;
	global_options.is_setto_aal0 = 0;
	global_options.is_setto_aal2 = 0;
	global_options.is_setto_aal5 = 0;	
	global_options.is_setto_aal0tx = 0;
	global_options.is_setto_aal2tx = 0;
	global_options.is_setto_aal5tx = 0;	
	global_options.is_reset_proc = 0;
	global_options.is_get_stats = 0;
	global_options.is_reset_stats = 0;
	global_options.is_set_net_mode_uni = 0;
	global_options.is_set_net_mode_nni = 0;
	global_options.is_get_sar_mode = 0;
	global_options.is_apply_filter = 0;
	global_options.is_clear_filter = 0;
	global_options.is_scan_connections = 0;
	global_options.is_cid_activation = 0;
	global_options.is_cid_deactivation = 0;
	global_options.is_set_trailer_mode = 0;
	global_options.is_set_forwarding_mode = 0;

	/* circuit data */
	global_options.iface = 0;
	global_options.channel = 0;
	global_options.vpi = 0;
	global_options.vci = 0;
	global_options.cid = 0;

	/* filter data */
	global_options.bitmask = 0x00000000;
	global_options.match   = 0x00000000;
	global_options.action = FILTER_ACCEPT;	

	/* strings from command line */
	memset (global_options.dagname_str, 0, DAGNAME_STR_SIZE);
	memset (global_options.device_str, 0, DEVICE_STR_SIZE);
	memset (global_options.filter_str, 0 ,FILTER_STR_SIZE);

	/* misc stuff */
	global_options.scan_seconds = 1;
	global_options.dagstream = 0;
	global_options.setup_erf_mux = 1;
	global_options.setup_erfmux_no_ixp = 0;
	global_options.setup_erfmux_ixp_txrx = 0;
	global_options.setup_erfmux_ixp_rx = 0;
	global_options.strip_aal5_trailer = trailer_off;
	global_options.atm_forwarding_mode = forwarding_off;
}

/*****************************************************************************/
#if 0
static void print_version(void)
{
	printf("%s (DAG %s) %s\n", dagutil_get_progname(), kDagReleaseVersion, kRevisionString);
}
#endif


/*****************************************************************************/
static void print_usage(ClArgPtr clarg)
{
	//print_version();
	printf("%s - Stub program to test SAR features.\n", dagutil_get_progname());
	printf("Usage: %s [options]\n", dagutil_get_progname());
	dagclarg_display_usage(clarg, stdout);
}

/*****************************************************************************/
static int parse_filter () {
	int result;
	const char * token;

	/*
	 * get bitmask
	 */
	token = strtok (global_options.filter_str, ".");
	
	if ((token == NULL) || (strlen(token) == 0) || (strlen(token) > 8))
		return -1;

	result = sscanf (token, "%x", &global_options.bitmask);
	if (result != 1)
		return -1;

	/*
	 * get match
	 */
	token = strtok (NULL, ".");

	if ((token == NULL) || (strlen(token) == 0) || (strlen(token) > 8))
		return -1;

	result = sscanf (token, "%x", &global_options.match);
	if (result != 1)
		return -1;

	/*
	 * get action
	 */
	token = strtok (NULL, "\n ");

	if ((token == NULL) || (strlen(token) != 1))
		return -1;

	switch (token[0]) {
	case '0':
		global_options.action = 0; // accept;
		break;

	case '1':
		global_options.action = 1; // reject
		break;

	default:
		return -1;
		break;
	}

	if (global_options.action == 0)
		dagutil_verbose ("[filter] bitmask: 0x%08x, match: 0x%08x, action: ACCEPT\n",
		global_options.bitmask, global_options.match);
	else
		dagutil_verbose ("[filter] bitmask: 0x%08x, match: 0x%08x, action: REJECT\n",
		global_options.bitmask, global_options.match);
	return 0;
}

/*****************************************************************************/
void process_cmdline (int argc, const char * const * argv) {
	ClArgPtr clarg = NULL;
	FILE *error_file = NULL;
	int arg_index = 0;
	int result;
	int code;

	clarg = dagclarg_init (argc, argv);

	/*
	 * set command line parser
	 */

	/* global commands */
	dagclarg_add_string (clarg,
		"DAG device to use.", "--device", 'd', "device",
		global_options.device_str, DEVICE_STR_SIZE, CLA_DEVICE);

	dagclarg_add (clarg, "Reset and start the IXP.", "--rst", 'x', CLA_RESET);
	dagclarg_add (clarg, "Get statistics.", "--get-stats", 's', CLA_GETSTATS);
	dagclarg_add (clarg, "Reset statistics.", "--reset-stats", 'r', CLA_RESETSTATS);
	dagclarg_add_uint (clarg,
		"Run scanning mode.", "--scan", 'z', "seconds",
		&global_options.scan_seconds, CLA_SCAN);

	dagclarg_add (clarg, "Don't configure ERF-MUX.", "--no-erfmux", 'm', CLA_NO_ERFMUX);
	dagclarg_add (clarg, "No IXP in the data path. Line to the host ", "--no-ixp", '\0', CLA_NO_IXP);
	dagclarg_add (clarg, "IXP in tramit and receive data path. line->IXP->host/line, host->IXP->line/host ", "--ixp-txrx", '\0', CLA_IXP_TXRX);
	dagclarg_add (clarg, "IXP in receive datapath. line->IXP->host/line, host->line  ", "--ixp-rx", '\0', CLA_IXP_RX);
	dagclarg_add (clarg, "Enable AAL5 trailer stripping (default).", "--strip-trailer", 't', CLA_STRIP_AAL5_TRAILER);
	dagclarg_add (clarg, "Disable AAL5 trailer stripping.", "--dont-strip-trailer", 'k', CLA_DONT_STRIP_AAL5_TRAILER);
	dagclarg_add (clarg, "Enable unconfigured ATM circuit forwarding.", "--forward-unconfigured", '\0', CLA_ENABLE_ATM_FORWARDING);
	dagclarg_add (clarg, "Disable unconfigured ATM circuit forwarding (default).", "--dont-forward-unconfigured", '\0', CLA_DISABLE_ATM_FORWARDING);

	dagclarg_add (clarg, "This page.", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option (clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option (clarg, CLA_HELP, '?');
	dagclarg_add(clarg, "Increase verbosity.", "--verbose", 'v', CLA_VERBOSE);

	/* per circuit commands */
	dagclarg_add (clarg, "Activate circuit.", "--activate", 'a', CLA_ACTIVATE);
	dagclarg_add (clarg, "Deactivate circuit.", "--deactivate", 'e', CLA_DEACTIVATE);
	dagclarg_add (clarg, "Activate CID.", "--cid-activate", 'i', CLA_CID_ACTIVATE);
	dagclarg_add (clarg, "Dectivate CID.", "--cid-deactivate", 'j', CLA_CID_DEACTIVATE);
	dagclarg_add (clarg, "Set SAR mode to AAL0.", "--aal0", '0', CLA_SETSARMODE_AAL0);
	dagclarg_add (clarg, "Set SAR mode to AAL2.", "--aal2", '2', CLA_SETSARMODE_AAL2);
	dagclarg_add (clarg, "Set SAR mode to AAL5.", "--aal5", '5', CLA_SETSARMODE_AAL5);
	dagclarg_add (clarg, "Set SAR mode to AAL0 TX.", "--aal0tx", '7', CLA_SETSARMODE_AAL0_TX);
	dagclarg_add (clarg, "Set SAR mode to AAL2 TX.", "--aal2tx", '8', CLA_SETSARMODE_AAL2_TX);
	dagclarg_add (clarg, "Set SAR mode to AAL5 TX.", "--aal5tx", '9', CLA_SETSARMODE_AAL5_TX);
	dagclarg_add (clarg, "Get SAR mode.", "--getsar", 'g', CLA_GETSARMODE);

	/* per interface(port)/channel commands */
	dagclarg_add (clarg, "Set net mode to UNI.", "--uni", 'u', CLA_SETNETMODE_UNI);
	dagclarg_add (clarg, "Set net mode to NNI.", "--nni", 'n', CLA_SETNETMODE_NNI);
	dagclarg_add (clarg, "Clear bitmask filter (accept all).", "--reset-filter", 'b', CLA_CLEARFILTER);
	dagclarg_add_string (clarg,
		"Set bitmask filter.", "--filter", 'f', "bitmask.match.action",
		global_options.filter_str, FILTER_STR_SIZE, CLA_SETFILTER);
	
	/* circuit data */
	dagclarg_add_uint (clarg,
		"Set interface port to process.", "--port", 'o', "port",
		&global_options.iface, CLA_PORT);

	dagclarg_add_uint (clarg,
		"Set channel to process.", "--channel", 'c', "channel",
		&global_options.channel, CLA_CHANNEL);

	dagclarg_add_uint (clarg,
		"Set vpi to process.", "--vpi", 'p', "vpi",
		&global_options.vpi, CLA_VPI);

	dagclarg_add_uint (clarg,
		"Set vci to process.", "--vci", 'q', "vci",
		&global_options.vci, CLA_VCI);

	dagclarg_add_uint (clarg,
		"Set cid to process.", "--cid", 'w', "cid",
		&global_options.cid, CLA_CID);


	/*
	 * Parse the command line
	 */

	result = dagclarg_parse (clarg, error_file, &arg_index, &code);
	while (result == 1) {

		switch (code) {
		case CLA_DEVICE:	
			if (dag_parse_name(global_options.device_str, global_options.dagname_str,
				DAGNAME_STR_SIZE, &global_options.dagstream) == -1) {
				dagutil_panic("dag_parse_name(%s): %s\n", global_options.device_str, strerror(errno));
			}

			dagutil_verbose("device=%s\n", global_options.dagname_str);
			break;

		case CLA_RESET:
			global_options.is_reset_proc = 1;
			break;

		case CLA_GETSTATS:
			global_options.is_get_stats = 1;
			break;

		case CLA_RESETSTATS:
			global_options.is_reset_stats = 1;
			break;

		case CLA_ACTIVATE:
			global_options.is_activation = 1;
			break;

		case CLA_DEACTIVATE:
			global_options.is_deactivation = 1;
			break;

		case CLA_CID_ACTIVATE:
			global_options.is_cid_activation = 1;
			break;

		case CLA_CID_DEACTIVATE:
			global_options.is_cid_deactivation = 1;
			break;

		case CLA_SETSARMODE_AAL0:
			global_options.is_setto_aal0 = 1;
			break;

		case CLA_SETSARMODE_AAL2:
			global_options.is_setto_aal2 = 1;
			break;

		case CLA_SETSARMODE_AAL5:
			global_options.is_setto_aal5 = 1;
			break;

		case CLA_SETSARMODE_AAL0_TX:
			global_options.is_setto_aal0tx = 1;
			break;

		case CLA_SETSARMODE_AAL2_TX:
			global_options.is_setto_aal2tx = 1;
			break;

		case CLA_SETSARMODE_AAL5_TX:
			global_options.is_setto_aal5tx = 1;
			break;

		case CLA_GETSARMODE:
			global_options.is_get_sar_mode = 1;
			break;

		case CLA_SETNETMODE_UNI:
			global_options.is_set_net_mode_uni = 1;
			break;

		case CLA_SETNETMODE_NNI:
			global_options.is_set_net_mode_nni = 1;
			break;

		case CLA_CLEARFILTER:
			global_options.is_clear_filter = 1;
			break;

		case CLA_SETFILTER:
			if (parse_filter() == -1)
				dagutil_panic ("cannot parse filter\n");

			global_options.is_apply_filter = 1;
			break;

		case CLA_PORT:
			if (global_options.iface < 0 || global_options.iface > 3) {
				dagutil_panic ("Wrong interface port number (%d). Values should be between 0 and 3.",
					global_options.iface);
			}
			break;

		case CLA_CHANNEL:
			if (global_options.channel < 0 || global_options.channel > 671) {
				dagutil_panic ("Wrong channel number (%d). Values should be between 0 and 671.",
					global_options.channel);
			}
			break;

		case CLA_VPI:
			if (global_options.vpi < 0 || global_options.vpi > 4095) {
				dagutil_panic ("Wrong vpi number (%d). Values should be between 0 and 4095.",
					global_options.vpi);
			}
			break;

		case CLA_VCI:
			if (global_options.vci < 0 || global_options.vci > 65535) {
				dagutil_panic ("Wrong vci number (%d). Values should be between 0 and 65535.",
					global_options.vci);
			}
			break;

		case CLA_CID:
			if (global_options.cid < 1 || global_options.cid > 255) {
				dagutil_panic ("Wrong cid number (%d). Values should be between 1 and 255.",
					global_options.cid);
			}
			break;

		case CLA_SCAN:
			global_options.is_scan_connections = 1;
			break;

		case CLA_NO_ERFMUX:
			global_options.setup_erf_mux = 0;
			break;

		case CLA_NO_IXP:
			global_options.setup_erfmux_no_ixp = 1;
			break;

		case CLA_IXP_TXRX:
			global_options.setup_erfmux_ixp_txrx = 1;
			break;
		
		case CLA_IXP_RX:
			global_options.setup_erfmux_ixp_rx = 1;
			break;

		case CLA_STRIP_AAL5_TRAILER:
			global_options.is_set_trailer_mode = 1;
			global_options.strip_aal5_trailer = trailer_off;
			break;
			
		case CLA_DONT_STRIP_AAL5_TRAILER:
			global_options.is_set_trailer_mode = 1;
			global_options.strip_aal5_trailer = trailer_on;
			break;
		
		case CLA_ENABLE_ATM_FORWARDING:
			global_options.is_set_forwarding_mode = 1;
			global_options.atm_forwarding_mode = forwarding_on;
			break;

		case CLA_DISABLE_ATM_FORWARDING:
			global_options.is_set_forwarding_mode = 1;
			global_options.atm_forwarding_mode = forwarding_off;
			break;
		
		case CLA_HELP:
			print_usage (clarg);
			exit (EXIT_SUCCESS);
			break;

		case CLA_VERBOSE:
			dagutil_inc_verbosity();
			break;

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

		result = dagclarg_parse(clarg, error_file, &arg_index, &code);
	}

	/*
	 * check combinations of options
	 */


}

/*****************************************************************************/
void CatchInterrupt (int signum) {
    /* Reset the signal handler to catch interrupt again */
    signal(SIGINT, CatchInterrupt);
    printf("\ndagsar_stub cannot be terminated, it will lost communicate with IXP.\n");
    fflush(stdout);
}

/*****************************************************************************/
int main (int argc, const char * const * argv) {
	int             dagfd;
	int             result;
	uint32_t        flags;
	dag_card_ref_t  card_ref;
	dag_component_t root_component;
	dag_component_t erfmux;
	attr_uuid_t     line_steering_attr;
	attr_uuid_t     ixp_steering_attr;
	attr_uuid_t     host_steering_attr;
	daginf_t*       daginf;


	/* program identification stuff */
	dagutil_set_progname("dagsar_stub");

	/* install interrupt handler */
	signal(SIGINT, CatchInterrupt);	  
	
	/* init stuff */
	init_global_options();

	/* Set up default DAG device */
	strncpy (global_options.device_str, DEFAULT_DAGNAME, DAGNAME_STR_SIZE);
	global_options.device_str[DAGNAME_STR_SIZE-1] = 0;

	if (dag_parse_name(global_options.device_str, global_options.dagname_str,
		DAGNAME_STR_SIZE, &global_options.dagstream) == -1) {
		dagutil_panic("dag_parse_name(%s): %s\n", global_options.device_str, strerror(errno));
	}

	/* options */
	process_cmdline (argc, argv);

	/* open dag card */
	dagfd = dag_open (global_options.dagname_str);
	if (dagfd < 0) {
		dagutil_panic ("cannot open card (%s)\n", global_options.dagname_str);
	}

	/* get card information */
	daginf = dag_info(dagfd);
	if (daginf == NULL) {
		dagutil_panic ("cannot get card information (%s)\n", global_options.dagname_str);
	}

	/* sanity check the type of the card */
	if ( daginf->device_code != PCI_DEVICE_ID_DAG7_10 
	     && daginf->device_code != PCI_DEVICE_ID_DAG3_70T 
	     && daginf->device_code != PCI_DEVICE_ID_DAG3_7T4) {
		dagutil_panic ("card not supported, this program will only work with a DAG3.7T or DAG3.7T4 or DAG7.1S card.\n");
	}

	/* open the config API */
	card_ref = dag_config_init (global_options.dagname_str);
	if ( card_ref == NULL ) {
		dagutil_panic ("dag_config_init failed\n");
	}
	
	/* get a reference to the root component */
	root_component = dag_config_get_root_component (card_ref);
	if ( root_component == NULL ) {
		dag_config_dispose (card_ref);
		dagutil_panic ("dag_config_get_root_component failed\n");
	}

	if (global_options.is_reset_proc) {
		dagutil_verbose("Resetting embedded processor...\n");
		result = dagema_reset_processor (dagfd, 0);
		if (result != 0) {
                        dagutil_panic("Reseting embedded processor failed.\n");
                }
	
		dagutil_verbose("Embedded software online.\n");
	}

	erfmux = dag_component_get_subcomponent (root_component, kComponentErfMux, 0);
	line_steering_attr = dag_component_get_attribute_uuid (erfmux, kUint32AttributeLineSteeringMode);
	ixp_steering_attr  = dag_component_get_attribute_uuid (erfmux, kUint32AttributeIXPSteeringMode);
	host_steering_attr = dag_component_get_attribute_uuid (erfmux, kUint32AttributeHostSteeringMode);
	
	if (global_options.setup_erfmux_no_ixp ) {
		global_options.setup_erf_mux = 0;
	
		/* Steer the packets from the line to the host */
		dag_config_set_uint32_attribute (card_ref, line_steering_attr, kSteerHost);
		assert (kSteerHost == dag_config_get_uint32_attribute (card_ref, line_steering_attr));
		
		/* Steer the packets from the ixp to ixp */
		dag_config_set_uint32_attribute (card_ref, ixp_steering_attr, kSteerIXP);
		assert (kSteerIXP == dag_config_get_uint32_attribute (card_ref, ixp_steering_attr));
	
		/* Steer packets from the host to the line */
		dag_config_set_uint32_attribute (card_ref, host_steering_attr, kSteerLine);
		assert (kSteerLine == dag_config_get_uint32_attribute (card_ref, host_steering_attr));
		
		dag_config_dispose (card_ref);
	}

	if (global_options.setup_erfmux_ixp_txrx ) {
		global_options.setup_erf_mux = 0;
		
		/* Steer the packets from the line to the ixp */
		dag_config_set_uint32_attribute (card_ref, line_steering_attr, kSteerIXP);
		assert (kSteerIXP == dag_config_get_uint32_attribute (card_ref, line_steering_attr));
		
		/* Use the direction bits in the packet record to determine the steering of the packet */
		dag_config_set_uint32_attribute (card_ref, ixp_steering_attr, kSteerDirectionBit);
		assert (kSteerDirectionBit == dag_config_get_uint32_attribute (card_ref, ixp_steering_attr));
	
		/* Steer packets from the host to the ixp */
		dag_config_set_uint32_attribute (card_ref, host_steering_attr, kSteerIXP);
		assert (kSteerIXP == dag_config_get_uint32_attribute (card_ref, host_steering_attr));
		
		dag_config_dispose (card_ref);
	}

	if (global_options.setup_erfmux_ixp_rx ) {
		global_options.setup_erf_mux = 0;
		
		/* Steer the packets from the line to the ixp */
		dag_config_set_uint32_attribute (card_ref, line_steering_attr, kSteerIXP);
		assert (kSteerIXP == dag_config_get_uint32_attribute (card_ref, line_steering_attr));
		
		/* Use the direction bits in the packet record to determine the steering of the packet */
		dag_config_set_uint32_attribute (card_ref, ixp_steering_attr, kSteerDirectionBit);
		assert (kSteerDirectionBit == dag_config_get_uint32_attribute (card_ref, ixp_steering_attr));
	
		/* Steer packets from the host to the line */
		dag_config_set_uint32_attribute (card_ref, host_steering_attr, kSteerLine);
		assert (kSteerLine == dag_config_get_uint32_attribute (card_ref, host_steering_attr));
		
		dag_config_dispose (card_ref);
	}

	/* Configure the ERF MUX to route packet records for dag7.1s:
	*   line -> IXP
	*   IXP  -> host or line (based on the direction bit)
	*   host -> IXP
	*
	*  Configure the ERF MUX to route packet records for dag3.7T:
	*   line -> IXP
	*   IXP -> host
	*   host -> line
	*/
	if ( global_options.setup_erf_mux ) {
		if (daginf->device_code == PCI_DEVICE_ID_DAG7_10) {
			/* Steer the packets from the line to the IXP */
			dag_config_set_uint32_attribute (card_ref, line_steering_attr, kSteerIXP);
			assert (kSteerIXP == dag_config_get_uint32_attribute (card_ref, line_steering_attr));
		
			/* Use the direction bits in the packet record to determine the steering of the packet */
			dag_config_set_uint32_attribute (card_ref, ixp_steering_attr, kSteerDirectionBit);
			assert (kSteerDirectionBit == dag_config_get_uint32_attribute (card_ref, ixp_steering_attr));
	
			/* Steer packets from the host to the IXP */
			dag_config_set_uint32_attribute (card_ref, host_steering_attr, kSteerIXP);
			assert (kSteerIXP == dag_config_get_uint32_attribute (card_ref, host_steering_attr));
		}
		else if (daginf->device_code == PCI_DEVICE_ID_DAG3_70T || daginf->device_code == PCI_DEVICE_ID_DAG3_7T4) {
		         /* Steer the packets from the line to the IXP */
                        dag_config_set_uint32_attribute (card_ref, line_steering_attr, kSteerIXP);
                        assert (kSteerIXP == dag_config_get_uint32_attribute (card_ref, line_steering_attr));

                        /* Steer the packets from the ixp to the host */
                        dag_config_set_uint32_attribute (card_ref, ixp_steering_attr, kSteerHost);
                        assert (kSteerHost == dag_config_get_uint32_attribute (card_ref, ixp_steering_attr));

                        /* Steer packets from the host to the line */
                        dag_config_set_uint32_attribute (card_ref, host_steering_attr, kSteerLine);
                        assert (kSteerLine == dag_config_get_uint32_attribute (card_ref, host_steering_attr));
		}

		dag_config_dispose (card_ref);
	}


	/* set up communication with embedded processor on the card.
	 * Before initializing EMA the embedded processor has to be running
	 * the EMA counterpart.
	 */
	result = dagema_open_conn (dagfd);
	if (result < 0) {
		int ema_error;
	
		ema_error = dagema_get_last_error ();
		switch (ema_error) {
		case ETIMEDOUT:
			dagutil_panic ("[dagema_open_conn] Connection timed out\n");
		
		case ELOCKED:
			dagutil_panic ("[dagema_open_conn] Card is locked\n");

		case ECARDNOTSUPPORTED:
			dagutil_panic ("[dagema_open_conn] Request not supported by card\n");

		case ECARDMEMERROR:
			dagutil_panic ("[dagema_open_conn] Request not supported by card\n");

		case ERESP:
			/* this could happen when the embedded processor is not running
			 * the EMA counterpart
			 */
			dagutil_panic ("[dagema_open_conn] No response or invalid response from card\n");

		default:
			dagutil_panic ("[dagema_open_conn] Unknown error code (0x%04x)\n", ema_error);
		}
	}

	/* Now try to call some SAR API functions */
	
	/* execute commands in global options */
	{
		int32_t  res;
		char aal_mode[AALMODE_STR_SIZE];

	
		if (global_options.is_set_trailer_mode) {
			res = dagsar_set_trailer_strip_mode(dagfd, global_options.strip_aal5_trailer);
			printf ("[dagsar_set_trailer_strip_mode] res: %d\n", res);
		}

		if (global_options.is_set_forwarding_mode) {
			res = dagsar_set_atm_forwarding_mode(dagfd, global_options.atm_forwarding_mode);
			printf ("[dagsar_set_atm_forwarding_mode] res: %d\n", res);
		}

		if (global_options.is_activation) {
			res = dagsar_vci_activate(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci);
			printf ("[dagsar_vci_activate] res: %d\n", res);
		}

		if (global_options.is_deactivation) {
			res = dagsar_vci_deactivate(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci);
			printf ("[dagsar_vci_deactivate] res: %d\n", res);
		}

		if (global_options.is_cid_activation) {
			res = dagsar_cid_activate(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci,
				global_options.cid);
			printf ("[dagsar_cid_activate] res: %d\n", res);
		}

		if (global_options.is_cid_deactivation) {
			res = dagsar_cid_deactivate(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci,
				global_options.cid);
			printf ("[dagsar_cid_deactivate] res: %d\n", res);
		}

		if (global_options.is_setto_aal0) {
			res = dagsar_vci_set_sar_mode(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci, sar_aal0);

			printf ("[dagsar_vci_set_sar_mode:aal0] (%d.%d.%d.%d) res: %d\n",
				global_options.iface, global_options.channel, global_options.vpi,
				global_options.vci, res);
		}

		if (global_options.is_setto_aal2) {
			res = dagsar_vci_set_sar_mode(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci, sar_aal2);

			printf ("[dagsar_vci_set_sar_mode:aal2] (%d.%d.%d.%d) res: %d\n",
				global_options.iface, global_options.channel, global_options.vpi,
				global_options.vci, res);
		}

		if (global_options.is_setto_aal5) {
			res = dagsar_vci_set_sar_mode(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci, sar_aal5);

			printf ("[dagsar_vci_set_sar_mode:aal5] (%d.%d.%d.%d) res: %d\n",
				global_options.iface, global_options.channel, global_options.vpi,
				global_options.vci, res);
		}

		if (global_options.is_setto_aal0tx) {
			res = dagsar_vci_set_sar_mode(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci, sar_aal0_tx);

			printf ("[dagsar_vci_set_sar_mode:aal0_tx] (%d.%d.%d.%d) res: %d\n",
				global_options.iface, global_options.channel, global_options.vpi,
				global_options.vci, res);
		}

		if (global_options.is_setto_aal2tx) {
			res = dagsar_vci_set_sar_mode(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci, sar_aal2_tx);

			printf ("[dagsar_vci_set_sar_mode:aal2_tx] (%d.%d.%d.%d) res: %d\n",
				global_options.iface, global_options.channel, global_options.vpi,
				global_options.vci, res);
		}

		if (global_options.is_setto_aal5tx) {
			res = dagsar_vci_set_sar_mode(dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci, sar_aal5_tx);

			printf ("[dagsar_vci_set_sar_mode:aal5_tx] (%d.%d.%d.%d) res: %d\n",
				global_options.iface, global_options.channel, global_options.vpi,
				global_options.vci, res);
		}

		if (global_options.is_get_sar_mode) {
			res = dagsar_vci_get_sar_mode (dagfd, global_options.iface,
				global_options.channel, global_options.vpi, global_options.vci);
			if(res == 0) strcpy(aal_mode, "AAL0");
			if(res == 1) strcpy(aal_mode, "AAL2");
			if(res == 2) strcpy(aal_mode, "AAL5");
			printf ("[dagsar_vci_get_sar_mode] sar_mode: %s\n", aal_mode);
		}

		if (global_options.is_get_stats && daginf->device_code == PCI_DEVICE_ID_DAG7_10) {
			/* statistic display for the 7.1s */
			uint32_t value[4];

			value[0] = dagsar_get_stats(dagfd, dag71s_stats_rx_pkt_0);
			value[1] = dagsar_get_stats(dagfd, dag71s_stats_rx_pkt_1);
			value[2] = dagsar_get_stats(dagfd, dag71s_stats_rx_pkt_2);
			value[3] = dagsar_get_stats(dagfd, dag71s_stats_rx_pkt_3);

			printf ("[dagsar_get_stats] RX packets(line) : 0x%08x 0x%08x 0x%08x 0x%08x\n",
				value[0], value[1], value[2], value[3]);

			value[0] = dagsar_get_stats(dagfd, dag71s_stats_tx_line_0);
			value[1] = dagsar_get_stats(dagfd, dag71s_stats_tx_line_1);
			value[2] = dagsar_get_stats(dagfd, dag71s_stats_tx_line_2);
			value[3] = dagsar_get_stats(dagfd, dag71s_stats_tx_line_3);

			printf ("[dagsar_get_stats] TX packets(line) : 0x%08x 0x%08x 0x%08x 0x%08x\n",
				value[0], value[1], value[2], value[3]);

			value[0] = dagsar_get_stats(dagfd, dag71s_stats_rx_host_0);
			value[1] = dagsar_get_stats(dagfd, dag71s_stats_rx_host_1);
			value[2] = dagsar_get_stats(dagfd, dag71s_stats_rx_host_2);
			value[3] = dagsar_get_stats(dagfd, dag71s_stats_rx_host_3);

			printf ("[dagsar_get_stats] RX packets(host) : 0x%08x 0x%08x 0x%08x 0x%08x\n",
				value[0], value[1], value[2], value[3]);

			value[0] = dagsar_get_stats(dagfd, dag71s_stats_to_host_0);
			value[1] = dagsar_get_stats(dagfd, dag71s_stats_to_host_1);
			value[2] = dagsar_get_stats(dagfd, dag71s_stats_to_host_2);
			value[3] = dagsar_get_stats(dagfd, dag71s_stats_to_host_3);

			printf ("[dagsar_get_stats] TX packets(host) : 0x%08x 0x%08x 0x%08x 0x%08x\n",
				value[0], value[1], value[2], value[3]);

			value[0] = dagsar_get_stats(dagfd, dag71s_stats_filter_drop_0);
			value[1] = dagsar_get_stats(dagfd, dag71s_stats_filter_drop_1);
			value[2] = dagsar_get_stats(dagfd, dag71s_stats_filter_drop_2);
			value[3] = dagsar_get_stats(dagfd, dag71s_stats_filter_drop_3);

			printf ("[dagsar_get_stats] Filter drop:       0x%08x 0x%08x 0x%08x 0x%08x\n",
				value[0], value[1], value[2], value[3]);

			value[0] = dagsar_get_stats(dagfd, dag71s_stats_crc_error_0);
			value[1] = dagsar_get_stats(dagfd, dag71s_stats_crc_error_1);
			value[2] = dagsar_get_stats(dagfd, dag71s_stats_crc_error_2);
			value[3] = dagsar_get_stats(dagfd, dag71s_stats_crc_error_3);

			printf ("[dagsar_get_stats] CRC error:         0x%08x 0x%08x 0x%08x 0x%08x\n",
				value[0], value[1], value[2], value[3]);

			value[0] = dagsar_get_stats(dagfd, dag71s_stats_tx_drop_0);
			value[1] = dagsar_get_stats(dagfd, dag71s_stats_tx_drop_1);
			value[2] = dagsar_get_stats(dagfd, dag71s_stats_tx_drop_2);
			value[3] = dagsar_get_stats(dagfd, dag71s_stats_tx_drop_3);

			printf ("[dagsar_get_stats] TX drop:           0x%08x 0x%08x 0x%08x 0x%08x\n",
				value[0], value[1], value[2], value[3]);

			value[0] = dagsar_get_stats(dagfd, dag71s_stats_hash_collisions);
			value[1] = dagsar_get_stats(dagfd, dag71s_stats_datapath_resets);
			value[2] = dagsar_get_stats(dagfd, dag71s_stats_cmds_exec);
			value[3] = dagsar_get_stats(dagfd, dag71s_stats_loss_counter);

			printf ("[dagsar_get_stats] Hash collisions:   0x%08x,     Datapath resets: 0x%08x\n",
				value[0], value[1]);
			printf ("[dagsar_get_stats] Commands executed: 0x%08x,        Loss counter: 0x%08x\n",
				value[2], value[3]);


			if ( dagutil_get_verbosity() > 0 )
			{
				value[0] = dagsar_get_stats(dagfd, dag71s_stats_erf_type_error);
				value[1] = dagsar_get_stats(dagfd, dag71s_stats_erf_unaligned);
	
				printf ("[dagsar_get_stats] ERF type error:    0x%08x,     ERF unaligned64: 0x%08x\n",
					value[0], value[1]);
	
				value[0] = dagsar_get_stats(dagfd, dag71s_stats_rlen_error);
				value[1] = dagsar_get_stats(dagfd, dag71s_stats_erf_snapped);
	
				printf ("[dagsar_get_stats] ERF WLEN error:    0x%08x,         ERF Snapped: 0x%08x\n",
					value[0], value[1]);
	
				value[0] = dagsar_get_stats(dagfd, dag71s_stats_oversized_record);
	
				printf ("[dagsar_get_stats] ERF to big error:  0x%08x\n",
					value[0]);
	
				value[0] = dagsar_get_stats(dagfd, dag71s_stats_msf_error_bop);
				value[1] = dagsar_get_stats(dagfd, dag71s_stats_msf_error_sop);
				value[2] = dagsar_get_stats(dagfd, dag71s_stats_msf_error_eop);
				value[3] = dagsar_get_stats(dagfd, dag71s_stats_msf_error_state);
	
				printf ("[dagsar_get_stats] MSF Error (SOP):   0x%08x,     MSF Error (EOP): 0x%08x\n"
					"[dagsar_get_stats] MSF Error (BOP):   0x%08x,   MSF Error (state): 0x%08x\n",
					value[0], value[1], value[2], value[3]);
	
#if !defined(NDEBUG)
				value[0] = dagsar_get_stats(dagfd, (dag71s_stats_rx_pkt_0 + 0x40));
				value[1] = dagsar_get_stats(dagfd, (dag71s_stats_rx_pkt_0 + 0x41));
				value[2] = dagsar_get_stats(dagfd, (dag71s_stats_rx_pkt_0 + 0x42));
				value[3] = dagsar_get_stats(dagfd, (dag71s_stats_rx_pkt_0 + 0x43));
	
				printf ("[dagsar_get_stats] Debugging:         0x%08x 0x%08x 0x%08x 0x%08x\n",
					value[0], value[1], value[2], value[3]);
#endif
			}
		}
		else if ( (global_options.is_get_stats && daginf->device_code == PCI_DEVICE_ID_DAG3_70T) || 
			  (global_options.is_get_stats && daginf->device_code == PCI_DEVICE_ID_DAG3_7T4) ) {

			/* statistic display for the 3.7t and 3.7t4*/
			uint32_t value[2];
		
			value[0] = dagsar_get_stats(dagfd, dropped_cells);
			value[1] = dagsar_get_stats(dagfd, filtered_cells);

			printf ("[dagsar_get_stats] Dropped cells  : 0x%08x\n"
			        "[dagsar_get_stats] Filtered cells : 0x%08x\n",
			        value[0], value[1]);
		}


		if (global_options.is_reset_stats) {
			printf ("[dagsar_reset_stats]\n");

			if ( daginf->device_code == PCI_DEVICE_ID_DAG7_10 ) {

				dagsar_reset_stats(dagfd, dag71s_stats_rx_pkt_0);
				dagsar_reset_stats(dagfd, dag71s_stats_rx_pkt_1);
				dagsar_reset_stats(dagfd, dag71s_stats_rx_pkt_2);
				dagsar_reset_stats(dagfd, dag71s_stats_rx_pkt_3);
				dagsar_reset_stats(dagfd, dag71s_stats_filter_drop_0);
				dagsar_reset_stats(dagfd, dag71s_stats_filter_drop_1);
				dagsar_reset_stats(dagfd, dag71s_stats_filter_drop_2);
				dagsar_reset_stats(dagfd, dag71s_stats_filter_drop_3);
				dagsar_reset_stats(dagfd, dag71s_stats_to_host_0);
				dagsar_reset_stats(dagfd, dag71s_stats_to_host_1);
				dagsar_reset_stats(dagfd, dag71s_stats_to_host_2);
				dagsar_reset_stats(dagfd, dag71s_stats_to_host_3);
				dagsar_reset_stats(dagfd, dag71s_stats_hash_collisions);
				dagsar_reset_stats(dagfd, dag71s_stats_datapath_resets);
				dagsar_reset_stats(dagfd, dag71s_stats_cmds_exec);
				dagsar_reset_stats(dagfd, dag71s_stats_crc_error_0);
				dagsar_reset_stats(dagfd, dag71s_stats_crc_error_1);
				dagsar_reset_stats(dagfd, dag71s_stats_crc_error_2);
				dagsar_reset_stats(dagfd, dag71s_stats_crc_error_3);
				dagsar_reset_stats(dagfd, dag71s_stats_loss_counter);
	
				dagsar_reset_stats(dagfd, dag71s_stats_erf_type_error);
				dagsar_reset_stats(dagfd, dag71s_stats_msf_error_bop);
				dagsar_reset_stats(dagfd, dag71s_stats_msf_error_sop);
				dagsar_reset_stats(dagfd, dag71s_stats_msf_error_eop);
				dagsar_reset_stats(dagfd, dag71s_stats_rlen_error);
				dagsar_reset_stats(dagfd, dag71s_stats_oversized_record);
				dagsar_reset_stats(dagfd, dag71s_stats_msf_error_state);
				dagsar_reset_stats(dagfd, dag71s_stats_erf_unaligned);
				dagsar_reset_stats(dagfd, dag71s_stats_erf_snapped);

				dagsar_reset_stats(dagfd, dag71s_stats_rx_host_0);
				dagsar_reset_stats(dagfd, dag71s_stats_rx_host_1);
				dagsar_reset_stats(dagfd, dag71s_stats_rx_host_2);
				dagsar_reset_stats(dagfd, dag71s_stats_rx_host_3);
				dagsar_reset_stats(dagfd, dag71s_stats_tx_drop_0);
				dagsar_reset_stats(dagfd, dag71s_stats_tx_drop_1);
				dagsar_reset_stats(dagfd, dag71s_stats_tx_drop_2);
				dagsar_reset_stats(dagfd, dag71s_stats_tx_drop_3);
				dagsar_reset_stats(dagfd, dag71s_stats_tx_line_0);
				dagsar_reset_stats(dagfd, dag71s_stats_tx_line_1);
				dagsar_reset_stats(dagfd, dag71s_stats_tx_line_2);
				dagsar_reset_stats(dagfd, dag71s_stats_tx_line_3);
	
#if !defined(NDEBUG)
				dagsar_reset_stats(dagfd, (dag71s_stats_rx_pkt_0 + 0x40));
				dagsar_reset_stats(dagfd, (dag71s_stats_rx_pkt_0 + 0x41));
				dagsar_reset_stats(dagfd, (dag71s_stats_rx_pkt_0 + 0x42));
				dagsar_reset_stats(dagfd, (dag71s_stats_rx_pkt_0 + 0x43));
#endif
			} else {
			
				dagsar_reset_stats(dagfd, dropped_cells);
				dagsar_reset_stats(dagfd, filtered_cells);
			}
			
			//dagsar_reset_stats_all(dagfd);
		}

		if (global_options.is_set_net_mode_uni) {
			dagsar_iface_set_net_mode(dagfd, global_options.iface, uni);
		}

		if (global_options.is_set_net_mode_nni) {
			dagsar_iface_set_net_mode(dagfd, global_options.iface, nni);
		}

		if (global_options.is_apply_filter) {
			dagsar_set_filter_bitmask (dagfd, global_options.iface, global_options.bitmask,
				global_options.match, global_options.action);
		}

		if (global_options.is_clear_filter) {
			dagsar_reset_filter_bitmask (dagfd, global_options.iface);
		}

		if (global_options.is_scan_connections) {
			connection_info_t conn_info;
			uint32_t          conn;
			uint32_t          num_connections;

			res = dagsar_init_scanning_mode (dagfd);
			printf ("dagsar_init_scanning_mode: %d\n", res);
			if (res < 0)
				dagutil_panic ("cannot init scanning mode\n");


			res = dagsar_set_scanning_mode (dagfd, scan_on);
			printf ("dagsar_set_scanning_mode (on): %d\n", res);
			if (res < 0)
				dagutil_panic ("cannot set scanning mode\n");


			sleep(global_options.scan_seconds);


			res = dagsar_set_scanning_mode (dagfd, scan_off);
			printf ("dagsar_set_scanning_mode (off): %d\n", res);
			if (res < 0)
				dagutil_panic ("cannot set scanning mode\n");


			res = dagsar_get_scanned_connections_number (dagfd);
			printf ("dagsar_get_scanned_connections_number: %d\n", res);
			if (res < 0)
				dagutil_panic ("cannot get scanned connections number\n");
			
			num_connections = res;

			/* Iterate over the connections */
			conn = 0;
			while (conn < num_connections) {
				res = dagsar_get_scanned_connection (dagfd, conn, &conn_info);
				if (res < 0)
					dagutil_panic ("failed to read connection number %d\n", conn);
				else
					printf ("%-6d  [%02d.%02d.%02d.%02d]\n", conn, conn_info.iface, conn_info.channel, conn_info.vpi, conn_info.vci);
				
				conn++;
			}
			printf ("\n\n");
		}

	}
	
	/* close ema */
	flags = 0;
	result = dagema_close_conn (dagfd, flags);
	if (result < 0) {
		dagutil_panic ("cannot close dagema\n");
	}

	/* close dag card */
	dag_close (dagfd);

	return 0;
}
